feat(api): relier les settings DB au comportement runtime

- Ajout de DynamicSettings dans AppState (Arc<RwLock>) chargé depuis la DB
- rate_limit_per_second, timeout_seconds : plus hardcodés, lus depuis settings
- image_processing (format, quality, filter, max_width) : appliqués comme
  valeurs par défaut sur les requêtes de pages (overridables via query params)
- cache.directory : lu depuis settings au lieu de la variable d'env
- update_setting recharge immédiatement le DynamicSettings en mémoire
  pour les clés limits, image_processing et cache (sans redémarrage)
- parse_filter() : mapping lanczos3/triangle/nearest → FilterType

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-09 23:27:09 +01:00
parent 137e8ce11c
commit c81f7ce1b7
6 changed files with 142 additions and 31 deletions

View File

@@ -8,7 +8,7 @@ use serde_json::Value;
use sqlx::Row;
use utoipa::ToSchema;
use crate::{error::ApiError, state::AppState};
use crate::{error::ApiError, state::{AppState, load_dynamic_settings}};
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct UpdateSettingRequest {
@@ -134,6 +134,13 @@ pub async fn update_setting(
.await?;
let value: Value = row.get("value");
// Rechargement des settings dynamiques si la clé affecte le comportement runtime
if key == "limits" || key == "image_processing" || key == "cache" {
let new_settings = load_dynamic_settings(&state.pool).await;
*state.settings.write().await = new_settings;
}
Ok(Json(value))
}
@@ -148,9 +155,8 @@ pub async fn update_setting(
),
security(("Bearer" = []))
)]
pub async fn clear_cache(State(_state): State<AppState>) -> Result<Json<ClearCacheResponse>, ApiError> {
let cache_dir = std::env::var("IMAGE_CACHE_DIR")
.unwrap_or_else(|_| "/tmp/stripstream-image-cache".to_string());
pub async fn clear_cache(State(state): State<AppState>) -> Result<Json<ClearCacheResponse>, ApiError> {
let cache_dir = state.settings.read().await.cache_directory.clone();
let result = tokio::task::spawn_blocking(move || {
if std::path::Path::new(&cache_dir).exists() {
@@ -188,9 +194,8 @@ pub async fn clear_cache(State(_state): State<AppState>) -> Result<Json<ClearCac
),
security(("Bearer" = []))
)]
pub async fn get_cache_stats(State(_state): State<AppState>) -> Result<Json<CacheStats>, ApiError> {
let cache_dir = std::env::var("IMAGE_CACHE_DIR")
.unwrap_or_else(|_| "/tmp/stripstream-image-cache".to_string());
pub async fn get_cache_stats(State(state): State<AppState>) -> Result<Json<CacheStats>, ApiError> {
let cache_dir = state.settings.read().await.cache_directory.clone();
let cache_dir_clone = cache_dir.clone();
let stats = tokio::task::spawn_blocking(move || {