use axum::{ extract::Query, http::StatusCode, response::{IntoResponse, Json}, }; #[derive(Debug, serde::Deserialize)] pub struct EditConfigQuery { pub key: String, pub value: String, } pub async fn get_config_handler() -> impl IntoResponse { let config_path = std::path::Path::new("config/markbase.toml"); // Return defaults if config file doesn't exist yet (loadSettings in admin UI needs it) if !config_path.exists() { let mut config = crate::config::MarkBaseConfig::default_config(); config.merge_env(); return ( StatusCode::OK, Json(serde_json::to_value(&config).unwrap_or_default()), ) .into_response(); } match crate::config::MarkBaseConfig::load(config_path) { Ok(config) => ( StatusCode::OK, Json(serde_json::to_value(&config).unwrap_or_default()), ) .into_response(), Err(e) => ( StatusCode::INTERNAL_SERVER_ERROR, Json(serde_json::json!({"error": e.to_string()})), ) .into_response(), } } pub async fn edit_config_handler(Query(params): Query) -> impl IntoResponse { let config_path = std::path::Path::new("config/markbase.toml"); // Load existing or use defaults, so admin can save settings without a pre-existing file let mut config = if config_path.exists() { match crate::config::MarkBaseConfig::load(config_path) { Ok(c) => c, Err(e) => { return (StatusCode::INTERNAL_SERVER_ERROR, Json(serde_json::json!({"error": e.to_string()}))).into_response(); } } } else { let mut defaults = crate::config::MarkBaseConfig::default_config(); defaults.merge_env(); defaults }; let old_value = config.get(¶ms.key).unwrap_or_default(); match config.set(¶ms.key, ¶ms.value) { Ok(_) => match config.validate() { Ok(_) => match config.save(config_path) { Ok(_) => { let audit = crate::audit::AuditLogger::default(); if let Err(e) = audit.log_config_change( "markbase", ¶ms.key, &old_value, ¶ms.value, "system", None, ) { log::warn!("Failed to write audit log: {}", e); } (StatusCode::OK, Json(serde_json::json!({"ok": true}))).into_response() } Err(e) => ( StatusCode::INTERNAL_SERVER_ERROR, Json(serde_json::json!({"error": e.to_string()})), ) .into_response(), }, Err(e) => ( StatusCode::BAD_REQUEST, Json(serde_json::json!({"error": e.to_string()})), ) .into_response(), }, Err(e) => ( StatusCode::BAD_REQUEST, Json(serde_json::json!({"error": e.to_string()})), ) .into_response(), } } pub async fn validate_config_handler() -> impl IntoResponse { let config_path = std::path::Path::new("config/markbase.toml"); if !config_path.exists() { return ( StatusCode::NOT_FOUND, Json(serde_json::json!({"ok": false, "error": "Config file not found"})), ) .into_response(); } match crate::config::MarkBaseConfig::load(config_path) { Ok(config) => match config.validate() { Ok(_) => (StatusCode::OK, Json(serde_json::json!({"ok": true}))).into_response(), Err(e) => ( StatusCode::BAD_REQUEST, Json(serde_json::json!({"ok": false, "error": e.to_string()})), ) .into_response(), }, Err(e) => ( StatusCode::INTERNAL_SERVER_ERROR, Json(serde_json::json!({"ok": false, "error": e.to_string()})), ) .into_response(), } } pub async fn get_s3_config_handler() -> impl IntoResponse { match crate::s3_config::S3Config::load_default() { Ok(config) => ( StatusCode::OK, Json(serde_json::to_value(&config).unwrap_or_default()), ) .into_response(), Err(e) => ( StatusCode::INTERNAL_SERVER_ERROR, Json(serde_json::json!({"error": e.to_string()})), ) .into_response(), } } pub async fn edit_s3_config_handler(Query(params): Query) -> impl IntoResponse { match crate::s3_config::S3Config::load_default() { Ok(mut config) => { let old_value = config.get(¶ms.key).unwrap_or_default(); match config.set(¶ms.key, ¶ms.value) { Ok(_) => match config.validate() { Ok(_) => match config.save("config/s3.toml") { Ok(_) => { let audit = crate::audit::AuditLogger::default(); if let Err(e) = audit.log_config_change( "s3", ¶ms.key, &old_value, ¶ms.value, "system", None, ) { log::warn!("Failed to write audit log: {}", e); } (StatusCode::OK, Json(serde_json::json!({"ok": true}))).into_response() } Err(e) => ( StatusCode::INTERNAL_SERVER_ERROR, Json(serde_json::json!({"error": e.to_string()})), ) .into_response(), }, Err(e) => ( StatusCode::BAD_REQUEST, Json(serde_json::json!({"error": e.to_string()})), ) .into_response(), }, Err(e) => ( StatusCode::BAD_REQUEST, Json(serde_json::json!({"error": e.to_string()})), ) .into_response(), } } Err(e) => ( StatusCode::INTERNAL_SERVER_ERROR, Json(serde_json::json!({"error": e.to_string()})), ) .into_response(), } } pub async fn validate_s3_config_handler() -> impl IntoResponse { match crate::s3_config::S3Config::load_default() { Ok(config) => match config.validate() { Ok(_) => (StatusCode::OK, Json(serde_json::json!({"ok": true}))).into_response(), Err(e) => ( StatusCode::BAD_REQUEST, Json(serde_json::json!({"ok": false, "error": e.to_string()})), ) .into_response(), }, Err(e) => ( StatusCode::INTERNAL_SERVER_ERROR, Json(serde_json::json!({"ok": false, "error": e.to_string()})), ) .into_response(), } }