feat: add settings API with key-value store
GET/PUT endpoints for app settings backed by a settings table.
This commit is contained in:
96
src/api/settings.rs
Normal file
96
src/api/settings.rs
Normal file
@@ -0,0 +1,96 @@
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use axum::{
|
||||
extract::{Path, State},
|
||||
http::StatusCode,
|
||||
response::{IntoResponse, Response},
|
||||
routing::get,
|
||||
Json, Router,
|
||||
};
|
||||
use serde::Deserialize;
|
||||
use crate::AppState;
|
||||
use super::{ApiResult, db_err};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct PutSetting {
|
||||
pub value: String,
|
||||
}
|
||||
|
||||
pub fn router(state: Arc<AppState>) -> Router {
|
||||
Router::new()
|
||||
.route("/settings", get(list_settings))
|
||||
.route("/settings/{key}", get(get_setting).put(put_setting).delete(delete_setting))
|
||||
.with_state(state)
|
||||
}
|
||||
|
||||
async fn list_settings(
|
||||
State(state): State<Arc<AppState>>,
|
||||
) -> ApiResult<HashMap<String, String>> {
|
||||
let rows: Vec<(String, String)> = sqlx::query_as(
|
||||
"SELECT key, value FROM settings ORDER BY key"
|
||||
)
|
||||
.fetch_all(&state.db.pool)
|
||||
.await
|
||||
.map_err(db_err)?;
|
||||
|
||||
let map: HashMap<String, String> = rows.into_iter().collect();
|
||||
Ok(Json(map))
|
||||
}
|
||||
|
||||
async fn get_setting(
|
||||
State(state): State<Arc<AppState>>,
|
||||
Path(key): Path<String>,
|
||||
) -> ApiResult<HashMap<String, String>> {
|
||||
let row: Option<(String,)> = sqlx::query_as(
|
||||
"SELECT value FROM settings WHERE key = ?"
|
||||
)
|
||||
.bind(&key)
|
||||
.fetch_optional(&state.db.pool)
|
||||
.await
|
||||
.map_err(db_err)?;
|
||||
|
||||
match row {
|
||||
Some((value,)) => {
|
||||
let mut map = HashMap::new();
|
||||
map.insert(key, value);
|
||||
Ok(Json(map))
|
||||
}
|
||||
None => Err((StatusCode::NOT_FOUND, "Setting not found").into_response()),
|
||||
}
|
||||
}
|
||||
|
||||
async fn put_setting(
|
||||
State(state): State<Arc<AppState>>,
|
||||
Path(key): Path<String>,
|
||||
Json(input): Json<PutSetting>,
|
||||
) -> ApiResult<HashMap<String, String>> {
|
||||
sqlx::query(
|
||||
"INSERT INTO settings (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value"
|
||||
)
|
||||
.bind(&key)
|
||||
.bind(&input.value)
|
||||
.execute(&state.db.pool)
|
||||
.await
|
||||
.map_err(db_err)?;
|
||||
|
||||
let mut map = HashMap::new();
|
||||
map.insert(key, input.value);
|
||||
Ok(Json(map))
|
||||
}
|
||||
|
||||
async fn delete_setting(
|
||||
State(state): State<Arc<AppState>>,
|
||||
Path(key): Path<String>,
|
||||
) -> Result<StatusCode, Response> {
|
||||
let result = sqlx::query("DELETE FROM settings WHERE key = ?")
|
||||
.bind(&key)
|
||||
.execute(&state.db.pool)
|
||||
.await
|
||||
.map_err(db_err)?;
|
||||
|
||||
if result.rows_affected() > 0 {
|
||||
Ok(StatusCode::NO_CONTENT)
|
||||
} else {
|
||||
Err((StatusCode::NOT_FOUND, "Setting not found").into_response())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user