use axum::http::HeaderMap;
use axum::extract::State;
use axum::http::StatusCode;
use axum::Json;
use serde::Deserialize;
use tracing::warn;
use crate::message::ServerMessage;
use crate::state::AppState;
#[derive(Debug, Deserialize)]
pub struct BroadcastRequest {
pub room: String,
pub text: String,
#[serde(default)]
pub from: String,
}
pub async fn broadcast_handler(
State(state): State<AppState>,
headers: HeaderMap,
Json(payload): Json<BroadcastRequest>,
) -> (StatusCode, Json<serde_json::Value>) {
if !state.config.broadcast.api_key.is_empty() {
let provided_key = headers
.get("X-Api-Key")
.and_then(|v| v.to_str().ok())
.unwrap_or("");
if provided_key != state.config.broadcast.api_key {
warn!("broadcast API: invalid API key");
return (
StatusCode::UNAUTHORIZED,
Json(serde_json::json!({ "error": "unauthorized" })),
);
}
} else {
warn!("broadcast API: no API key configured, endpoint disabled");
return (
StatusCode::FORBIDDEN,
Json(serde_json::json!({ "error": "broadcast endpoint disabled" })),
);
}
{
let rooms = state.rooms.read().await;
if !rooms.contains_key(&payload.room) {
return (
StatusCode::NOT_FOUND,
Json(serde_json::json!({ "error": "room not found" })),
);
}
}
let from = if payload.from.is_empty() {
"system".into()
} else {
payload.from
};
let at = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs();
let msg = ServerMessage::Message {
room: payload.room.clone(),
from,
text: payload.text,
at,
};
state.broadcast(&payload.room, &msg, None).await;
(
StatusCode::OK,
Json(serde_json::json!({ "status": "ok" })),
)
}