Something went wrong. Try again.
A downstream fork of Tranquil PDS for use in margin.cafe
Something went wrong. Try again.
7.8 kB · 244 lines
Rust
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245use axum::{Json, extract::State};use serde::{Deserialize, Serialize};use tracing::{error, warn};use tranquil_pds::api::error::{ApiError, DbResultExt};use tranquil_pds::auth::{Admin, Auth};use tranquil_pds::state::AppState;use tranquil_types::CidLink;
#[derive(Serialize)]#[serde(rename_all = "camelCase")]pub struct ServerConfigOutput { pub server_name: String, pub primary_color: Option<String>, pub primary_color_dark: Option<String>, pub secondary_color: Option<String>, pub secondary_color_dark: Option<String>, pub logo_cid: Option<String>,}
#[derive(Deserialize)]#[serde(rename_all = "camelCase")]pub struct UpdateServerConfigRequest { pub server_name: Option<String>, pub primary_color: Option<String>, pub primary_color_dark: Option<String>, pub secondary_color: Option<String>, pub secondary_color_dark: Option<String>, pub logo_cid: Option<String>,}
#[derive(Serialize)]pub struct UpdateServerConfigOutput { pub success: bool,}
fn is_valid_hex_color(s: &str) -> bool { if s.len() != 7 || !s.starts_with('#') { return false; } s[1..].chars().all(|c| c.is_ascii_hexdigit())}
pub async fn get_server_config( State(state): State<AppState>,) -> Result<Json<ServerConfigOutput>, ApiError> { let keys = &[ "server_name", "primary_color", "primary_color_dark", "secondary_color", "secondary_color_dark", "logo_cid", ];
let rows = state .repos .infra .get_server_configs(keys) .await .log_db_err("fetching server config")?;
let config_map: std::collections::HashMap<String, String> = rows.into_iter().collect();
Ok(Json(ServerConfigOutput { server_name: config_map .get("server_name") .cloned() .unwrap_or_else(|| "Tranquil PDS".to_string()), primary_color: config_map.get("primary_color").cloned(), primary_color_dark: config_map.get("primary_color_dark").cloned(), secondary_color: config_map.get("secondary_color").cloned(), secondary_color_dark: config_map.get("secondary_color_dark").cloned(), logo_cid: config_map.get("logo_cid").cloned(), }))}
pub async fn update_server_config( State(state): State<AppState>, _auth: Auth<Admin>, Json(req): Json<UpdateServerConfigRequest>,) -> Result<Json<UpdateServerConfigOutput>, ApiError> { if let Some(server_name) = req.server_name { let trimmed = server_name.trim(); if trimmed.is_empty() || trimmed.len() > 100 { return Err(ApiError::InvalidRequest( "Server name must be 1-100 characters".into(), )); } state .repos .infra .upsert_server_config("server_name", trimmed) .await .log_db_err("upserting server_name")?; }
if let Some(ref color) = req.primary_color { if color.is_empty() { state .repos .infra .delete_server_config("primary_color") .await .log_db_err("deleting primary_color")?; } else if is_valid_hex_color(color) { state .repos .infra .upsert_server_config("primary_color", color) .await .log_db_err("upserting primary_color")?; } else { return Err(ApiError::InvalidRequest( "Invalid primary color format (expected #RRGGBB)".into(), )); } }
if let Some(ref color) = req.primary_color_dark { if color.is_empty() { state .repos .infra .delete_server_config("primary_color_dark") .await .log_db_err("deleting primary_color_dark")?; } else if is_valid_hex_color(color) { state .repos .infra .upsert_server_config("primary_color_dark", color) .await .log_db_err("upserting primary_color_dark")?; } else { return Err(ApiError::InvalidRequest( "Invalid primary dark color format (expected #RRGGBB)".into(), )); } }
if let Some(ref color) = req.secondary_color { if color.is_empty() { state .repos .infra .delete_server_config("secondary_color") .await .log_db_err("deleting secondary_color")?; } else if is_valid_hex_color(color) { state .repos .infra .upsert_server_config("secondary_color", color) .await .log_db_err("upserting secondary_color")?; } else { return Err(ApiError::InvalidRequest( "Invalid secondary color format (expected #RRGGBB)".into(), )); } }
if let Some(ref color) = req.secondary_color_dark { if color.is_empty() { state .repos .infra .delete_server_config("secondary_color_dark") .await .log_db_err("deleting secondary_color_dark")?; } else if is_valid_hex_color(color) { state .repos .infra .upsert_server_config("secondary_color_dark", color) .await .log_db_err("upserting secondary_color_dark")?; } else { return Err(ApiError::InvalidRequest( "Invalid secondary dark color format (expected #RRGGBB)".into(), )); } }
if let Some(ref logo_cid) = req.logo_cid { let old_logo_cid = state .repos .infra .get_server_config("logo_cid") .await .ok() .flatten();
let should_delete_old = match (&old_logo_cid, logo_cid.is_empty()) { (Some(old), true) => Some(old.clone()), (Some(old), false) if old != logo_cid => Some(old.clone()), _ => None, };
if let Some(old_cid_str) = should_delete_old { match CidLink::new(old_cid_str) { Ok(old_cid) => { if let Ok(Some(storage_key)) = state .repos .infra .get_blob_storage_key_by_cid(&old_cid) .await { if let Err(e) = state.blob_store.delete(&storage_key).await { error!("Failed to delete old logo blob from storage: {:?}", e); } if let Err(e) = state.repos.infra.delete_blob_by_cid(&old_cid).await { error!("Failed to delete old logo blob record: {:?}", e); } } } Err(e) => { warn!( "Old logo CID in database is invalid, skipping cleanup: {:?}", e ); } } }
if logo_cid.is_empty() { state .repos .infra .delete_server_config("logo_cid") .await .log_db_err("deleting logo_cid")?; } else { state .repos .infra .upsert_server_config("logo_cid", logo_cid) .await .log_db_err("upserting logo_cid")?; } }
Ok(Json(UpdateServerConfigOutput { success: true }))}