Something went wrong. Try again.
Our Personal Data Server from scratch!
Something went wrong. Try again.
6.3 kB · 202 lines
Rust
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203use axum::{Json, extract::State};use serde::{Deserialize, Serialize};use tranquil_pds::api::error::{ApiError, DbResultExt};use tranquil_pds::auth::{Admin, Auth};use tranquil_pds::state::AppState;
#[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 { 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 }))}