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

@@ -27,10 +27,10 @@ use lru::LruCache;
use std::num::NonZeroUsize;
use stripstream_core::config::ApiConfig;
use sqlx::postgres::PgPoolOptions;
use tokio::sync::{Mutex, Semaphore};
use tokio::sync::{Mutex, RwLock, Semaphore};
use tracing::info;
use crate::state::{load_concurrent_renders, AppState, Metrics, ReadRateLimit};
use crate::state::{load_concurrent_renders, load_dynamic_settings, AppState, Metrics, ReadRateLimit};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
@@ -50,6 +50,18 @@ async fn main() -> anyhow::Result<()> {
let concurrent_renders = load_concurrent_renders(&pool).await;
info!("Using concurrent_renders limit: {}", concurrent_renders);
let dynamic_settings = load_dynamic_settings(&pool).await;
info!(
"Dynamic settings: rate_limit={}, timeout={}s, format={}, quality={}, filter={}, max_width={}, cache_dir={}",
dynamic_settings.rate_limit_per_second,
dynamic_settings.timeout_seconds,
dynamic_settings.image_format,
dynamic_settings.image_quality,
dynamic_settings.image_filter,
dynamic_settings.image_max_width,
dynamic_settings.cache_directory,
);
let state = AppState {
pool,
bootstrap_token: Arc::from(config.api_bootstrap_token),
@@ -62,6 +74,7 @@ async fn main() -> anyhow::Result<()> {
window_started_at: Instant::now(),
requests_in_window: 0,
})),
settings: Arc::new(RwLock::new(dynamic_settings)),
};
let admin_routes = Router::new()