//! Deliberately small anonymous surface. Operational templates/assets are private. use crate::{auth::Session, runtime::{ApiError, App}, settings::PanelCompression}; use axum::{extract::{rejection::JsonRejection, State}, response::{Html, IntoResponse, Response}, Extension}; use http::{HeaderMap, StatusCode}; use std::sync::{Arc, OnceLock}; use super::panel_content::PanelContent; static ADMIN_INDEX: OnceLock = OnceLock::new(); fn panel_response(headers: &HeaderMap, compression: &PanelCompression) -> Response { let content = ADMIN_INDEX.get_or_init(|| { let html = include_str!("web/index.html") .replace("__RUSTPROXY_ASSET_ID__", super::asset_build_id()) .replace("__RUSTPROXY_VERSION__", env!("CARGO_PKG_VERSION")); // Exactly one static panel document shared between all administrator accounts. PanelContent::new(Box::leak(html.into_boxed_str())) }); content.response(headers, compression, "text/html; charset=utf-8") } pub(super) async fn index( State(state): State>, session: Option>, headers: HeaderMap, ) -> Response { if session.is_none() { let html = include_str!("web/login.html") .replace("__RUSTPROXY_ASSET_ID__", super::asset_build_id()); return Html(html).into_response(); } let snapshot = state.config.load(); panel_response(&headers, &snapshot.envelope.config.administration.panel_compression) } pub(super) fn invalid_json(error: JsonRejection) -> ApiError { // Keep useful protocol statuses without returning serde's input excerpts. let status = match error.status() { StatusCode::PAYLOAD_TOO_LARGE => StatusCode::PAYLOAD_TOO_LARGE, StatusCode::UNSUPPORTED_MEDIA_TYPE => StatusCode::UNSUPPORTED_MEDIA_TYPE, _ => StatusCode::BAD_REQUEST, }; ApiError(status, "auth.invalid_request".into()) } #[cfg(test)] mod tests;