phantom_frame/
control.rs

1use crate::cache::RefreshTrigger;
2use axum::{
3    body::Body,
4    extract::State,
5    http::{header, Request, StatusCode},
6    response::IntoResponse,
7    routing::post,
8    Router,
9};
10use std::sync::Arc;
11
12#[derive(Clone)]
13pub struct ControlState {
14    refresh_trigger: RefreshTrigger,
15    auth_token: Option<String>,
16}
17
18impl ControlState {
19    pub fn new(refresh_trigger: RefreshTrigger, auth_token: Option<String>) -> Self {
20        Self {
21            refresh_trigger,
22            auth_token,
23        }
24    }
25}
26
27/// Handler for POST /refresh-cache endpoint
28async fn refresh_cache_handler(
29    State(state): State<Arc<ControlState>>,
30    req: Request<Body>,
31) -> Result<impl IntoResponse, StatusCode> {
32    // Check authorization if auth_token is set
33    if let Some(required_token) = &state.auth_token {
34        let auth_header = req
35            .headers()
36            .get(header::AUTHORIZATION)
37            .and_then(|h| h.to_str().ok());
38
39        let expected = format!("Bearer {}", required_token);
40
41        if auth_header != Some(expected.as_str()) {
42            tracing::warn!("Unauthorized refresh-cache attempt");
43            return Err(StatusCode::UNAUTHORIZED);
44        }
45    }
46
47    // Trigger cache refresh
48    state.refresh_trigger.trigger();
49    tracing::info!("Cache refresh triggered via control endpoint");
50
51    Ok((StatusCode::OK, "Cache refresh triggered"))
52}
53
54/// Create the control server router
55pub fn create_control_router(
56    refresh_trigger: RefreshTrigger,
57    auth_token: Option<String>,
58) -> Router {
59    let state = Arc::new(ControlState::new(refresh_trigger, auth_token));
60
61    Router::new()
62        .route("/refresh-cache", post(refresh_cache_handler))
63        .with_state(state)
64}