107 lines
2.8 KiB
Rust
107 lines
2.8 KiB
Rust
use std::sync::Arc;
|
|
|
|
use axum::{
|
|
Json,
|
|
extract::{Path, State},
|
|
http::HeaderMap,
|
|
};
|
|
use axum_extra::extract::cookie::CookieJar;
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
use crate::{
|
|
db::{Table, row_string},
|
|
error::{AppError, AppResult},
|
|
routes,
|
|
state::AppState,
|
|
};
|
|
|
|
#[derive(Debug, Serialize)]
|
|
pub struct Preference {
|
|
pub key: String,
|
|
pub value: String,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
pub struct PreferenceInput {
|
|
pub value: String,
|
|
}
|
|
|
|
pub async fn list(
|
|
State(state): State<Arc<AppState>>,
|
|
jar: CookieJar,
|
|
) -> AppResult<Json<Vec<Preference>>> {
|
|
let session = routes::session(&state, &jar)?;
|
|
session.require_admin()?;
|
|
if !state.db.has_table(Table::AdminPreferences).await {
|
|
return Ok(Json(Vec::new()));
|
|
}
|
|
let table = state.db.table(Table::AdminPreferences);
|
|
let sql = state.db.q(&format!(
|
|
"SELECT pref_key,pref_value FROM {table} WHERE username=? ORDER BY pref_key"
|
|
));
|
|
let rows = sqlx::query(&sql)
|
|
.bind(&session.username)
|
|
.fetch_all(state.db.pool())
|
|
.await?;
|
|
Ok(Json(
|
|
rows.iter()
|
|
.map(|row| Preference {
|
|
key: row_string(row, "pref_key"),
|
|
value: row_string(row, "pref_value"),
|
|
})
|
|
.collect(),
|
|
))
|
|
}
|
|
|
|
pub async fn set(
|
|
State(state): State<Arc<AppState>>,
|
|
Path(key): Path<String>,
|
|
jar: CookieJar,
|
|
headers: HeaderMap,
|
|
Json(input): Json<PreferenceInput>,
|
|
) -> AppResult<Json<serde_json::Value>> {
|
|
let session = routes::mutation_session(&state, &jar, &headers)?;
|
|
session.require_admin()?;
|
|
validate_key(&key)?;
|
|
if input.value.len() > 2048 {
|
|
return Err(AppError::bad_request("Preference value is too long"));
|
|
}
|
|
if !state.db.has_table(Table::AdminPreferences).await {
|
|
return Err(AppError::conflict(
|
|
"admin_preferences table is not available",
|
|
));
|
|
}
|
|
state
|
|
.db
|
|
.set_admin_pref(&session.username, &key, &input.value)
|
|
.await?;
|
|
Ok(Json(serde_json::json!({"ok": true})))
|
|
}
|
|
|
|
pub async fn remove(
|
|
State(state): State<Arc<AppState>>,
|
|
Path(key): Path<String>,
|
|
jar: CookieJar,
|
|
headers: HeaderMap,
|
|
) -> AppResult<Json<serde_json::Value>> {
|
|
let session = routes::mutation_session(&state, &jar, &headers)?;
|
|
session.require_admin()?;
|
|
validate_key(&key)?;
|
|
if state.db.has_table(Table::AdminPreferences).await {
|
|
state.db.delete_admin_pref(&session.username, &key).await?;
|
|
}
|
|
Ok(Json(serde_json::json!({"ok": true})))
|
|
}
|
|
|
|
fn validate_key(key: &str) -> AppResult<()> {
|
|
if key.is_empty()
|
|
|| key.len() > 64
|
|
|| !key
|
|
.bytes()
|
|
.all(|b| b.is_ascii_alphanumeric() || matches!(b, b'_' | b'-' | b'.'))
|
|
{
|
|
return Err(AppError::bad_request("Invalid preference key"));
|
|
}
|
|
Ok(())
|
|
}
|