Refactor error handling in IAM and CMS crates

- Introduced `RepositoryError` and `ServiceError` enums in both IAM and CMS domains for better error management.
- Updated domain traits and services to return specific error types instead of `anyhow::Result`.
- Enhanced session and OAuth repository implementations to handle errors more explicitly.
- Refactored session service methods to return `Result<T, ServiceError>` for improved error handling.
- Updated HTTP handlers to utilize the new error types.
- Modified password hashing functions to run in a blocking context using `tokio::task::spawn_blocking`.
- Added tests for new error handling mechanisms and async password functions.
This commit is contained in:
asepharyana
2026-07-20 06:14:23 +07:00
parent 5aaedbf787
commit ab1a54b72e
47 changed files with 630 additions and 347 deletions
@@ -15,11 +15,11 @@
use std::path::Path;
use anyhow::{Context, Result};
use serde::{Deserialize, Serialize};
use zesdex_utils::write_json_atomic;
use crate::domain::app_config::{AppConfig, ModelRole, ProviderConfig};
use crate::domain::error::RepositoryError;
use crate::domain::repository::AppConfigRepository;
/// File-based `AppConfigRepository` that reads/writes `app_config.json`.
@@ -95,19 +95,18 @@ impl AppConfigRepository for JsonAppConfigRepository {
/// Flow: read file → parse JSON → merge default providers → auto-detect Claude → return.
///
/// If the file is missing, returns `AppConfig::default()`.
fn load(&self, base_dir: &Path) -> Result<AppConfig> {
fn load(&self, base_dir: &Path) -> Result<AppConfig, RepositoryError> {
tracing::debug!("loading app_config from {base_dir:?}");
let path = base_dir.join("app_config.json");
// Try to read and parse the config file
let mut cfg: AppConfig = match std::fs::read_to_string(&path) {
Ok(s) => serde_json::from_str(&s)
.map_err(|e| anyhow::anyhow!("failed to parse app_config.json: {e}"))?,
Ok(s) => serde_json::from_str(&s)?,
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
tracing::info!("app_config.json not found, using defaults");
AppConfig::default()
}
Err(e) => {
return Err(anyhow::anyhow!("failed to read app_config.json: {e}"));
return Err(RepositoryError::Io(e));
}
};
@@ -153,13 +152,12 @@ impl AppConfigRepository for JsonAppConfigRepository {
/// Persist `AppConfig` to `<base_dir>/app_config.json`.
///
/// Flow: create base dir → atomic JSON write → log success.
fn save(&self, base_dir: &Path, config: &AppConfig) -> Result<()> {
fn save(&self, base_dir: &Path, config: &AppConfig) -> Result<(), RepositoryError> {
tracing::debug!("saving app_config to {base_dir:?}");
std::fs::create_dir_all(base_dir)
.with_context(|| format!("failed to create base dir '{}'", base_dir.display()))?;
std::fs::create_dir_all(base_dir)?;
let path = base_dir.join("app_config.json");
write_json_atomic(&path, config, None)
.with_context(|| "failed to save app_config")?;
.map_err(RepositoryError::from_anyhow)?;
tracing::debug!("app_config saved to '{}'", path.display());
Ok(())
}