feat: wrapup env vars + render concurrency semaphore
Some checks failed
CI / Check / Test (push) Failing after 43s

This commit is contained in:
2026-06-02 23:41:08 +02:00
parent efd1214a4c
commit f160adcd1c
4 changed files with 46 additions and 4 deletions

View File

@@ -3,6 +3,15 @@ pub struct AppConfig {
pub allow_registration: bool, pub allow_registration: bool,
pub base_url: String, pub base_url: String,
pub rate_limit: u64, pub rate_limit: u64,
pub wrapup: WrapUpConfig,
}
#[derive(Clone)]
pub struct WrapUpConfig {
pub font_path: Option<String>,
pub logo_path: Option<String>,
pub ffmpeg_path: String,
pub max_concurrent_renders: usize,
} }
impl AppConfig { impl AppConfig {
@@ -20,6 +29,21 @@ impl AppConfig {
allow_registration, allow_registration,
base_url, base_url,
rate_limit, rate_limit,
wrapup: WrapUpConfig::from_env(),
}
}
}
impl WrapUpConfig {
pub fn from_env() -> Self {
Self {
font_path: std::env::var("WRAPUP_FONT_PATH").ok(),
logo_path: std::env::var("WRAPUP_LOGO_PATH").ok(),
ffmpeg_path: std::env::var("FFMPEG_PATH").unwrap_or_else(|_| "ffmpeg".to_string()),
max_concurrent_renders: std::env::var("WRAPUP_MAX_CONCURRENT")
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or(2),
} }
} }
} }

View File

@@ -89,6 +89,12 @@ impl TestContextBuilder {
allow_registration: true, allow_registration: true,
base_url: "http://localhost:3000".into(), base_url: "http://localhost:3000".into(),
rate_limit: 20, rate_limit: 20,
wrapup: crate::config::WrapUpConfig {
font_path: None,
logo_path: None,
ffmpeg_path: "ffmpeg".into(),
max_concurrent_renders: 2,
},
}, },
} }
} }

View File

@@ -1,17 +1,25 @@
use std::sync::Arc;
use async_trait::async_trait; use async_trait::async_trait;
use domain::errors::DomainError; use domain::errors::DomainError;
use domain::events::DomainEvent; use domain::events::DomainEvent;
use domain::ports::EventHandler; use domain::ports::EventHandler;
use tokio::sync::Semaphore;
use crate::context::AppContext; use crate::context::AppContext;
pub struct WrapUpEventHandler { pub struct WrapUpEventHandler {
ctx: AppContext, ctx: AppContext,
semaphore: Arc<Semaphore>,
} }
impl WrapUpEventHandler { impl WrapUpEventHandler {
pub fn new(ctx: AppContext) -> Self { pub fn new(ctx: AppContext) -> Self {
Self { ctx } let max = ctx.config.wrapup.max_concurrent_renders;
Self {
ctx,
semaphore: Arc::new(Semaphore::new(max)),
}
} }
} }
@@ -25,6 +33,9 @@ impl EventHandler for WrapUpEventHandler {
start_date, start_date,
end_date, end_date,
} => { } => {
let _permit = self.semaphore.acquire().await.map_err(|_| {
DomainError::InfrastructureError("render semaphore closed".into())
})?;
super::handle_requested::execute( super::handle_requested::execute(
&self.ctx, &self.ctx,
wrapup_id.clone(), wrapup_id.clone(),

View File

@@ -39,13 +39,14 @@ pub async fn execute(
// Optionally render video (non-fatal) // Optionally render video (non-fatal)
if let Some(ref renderer) = ctx.services.video_renderer { if let Some(ref renderer) = ctx.services.video_renderer {
let poster_images = resolve_poster_images(ctx, &report).await; let poster_images = resolve_poster_images(ctx, &report).await;
let wc = &ctx.config.wrapup;
let config = VideoRenderConfig { let config = VideoRenderConfig {
slide_duration_secs: 4, slide_duration_secs: 4,
transition_duration_secs: 0.8, transition_duration_secs: 0.8,
resolution: (1080, 1920), resolution: (1080, 1920),
ffmpeg_path: "ffmpeg".to_string(), ffmpeg_path: wc.ffmpeg_path.clone(),
font_path: None, font_path: wc.font_path.clone(),
logo_path: None, logo_path: wc.logo_path.clone(),
}; };
match renderer.render(&report, poster_images, &config).await { match renderer.render(&report, poster_images, &config).await {
Ok(video_bytes) => { Ok(video_bytes) => {