mockforge_ui/handlers/
assets.rs

1//! Static asset serving handlers
2//!
3//! This module handles serving static assets like HTML, CSS, and JavaScript
4//! files for the admin UI.
5
6use axum::{
7    http::{self},
8    response::{Html, IntoResponse, Redirect},
9};
10
11/// Serve the main admin HTML page
12pub async fn serve_admin_html() -> Html<&'static str> {
13    Html(include_str!("../../ui/dist/index.html"))
14}
15
16/// Serve the admin CSS with proper content type
17pub async fn serve_admin_css() -> ([(http::HeaderName, &'static str); 1], &'static str) {
18    (
19        [(http::header::CONTENT_TYPE, "text/css")],
20        include_str!("../../ui/dist/assets/index.css"),
21    )
22}
23
24/// Serve the admin JavaScript with proper content type
25pub async fn serve_admin_js() -> ([(http::HeaderName, &'static str); 1], &'static str) {
26    (
27        [(http::header::CONTENT_TYPE, "application/javascript")],
28        include_str!("../../ui/dist/assets/index.js"),
29    )
30}
31
32/// Serve icon files
33pub async fn serve_icon() -> impl IntoResponse {
34    // Return a simple SVG icon or placeholder
35    let icon_svg = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 32 32\"><rect width=\"32\" height=\"32\" fill=\"#4f46e5\"/><text x=\"16\" y=\"20\" text-anchor=\"middle\" fill=\"white\" font-family=\"Arial\" font-size=\"14\">MF</text></svg>";
36    ([(http::header::CONTENT_TYPE, "image/svg+xml")], icon_svg)
37}
38
39/// Serve 32x32 icon
40pub async fn serve_icon_32() -> impl IntoResponse {
41    serve_icon().await
42}
43
44/// Serve 48x48 icon
45pub async fn serve_icon_48() -> impl IntoResponse {
46    serve_icon().await
47}
48
49/// Serve logo files
50pub async fn serve_logo() -> impl IntoResponse {
51    serve_icon().await
52}
53
54/// Serve 40x40 logo
55pub async fn serve_logo_40() -> impl IntoResponse {
56    serve_icon().await
57}
58
59/// Serve 80x80 logo
60pub async fn serve_logo_80() -> impl IntoResponse {
61    serve_icon().await
62}
63
64/// Serve the API documentation - redirects to the book
65pub async fn serve_api_docs() -> impl IntoResponse {
66    // Redirect to the comprehensive documentation in the book
67    Redirect::permanent("https://docs.mockforge.dev/api/admin-ui-rest.html")
68}
69
70#[cfg(test)]
71mod tests {
72    use super::*;
73
74    #[tokio::test]
75    async fn test_serve_admin_html() {
76        let html = serve_admin_html().await;
77        let html_str = html.0;
78        assert!(!html_str.is_empty());
79        assert!(html_str.contains("<!DOCTYPE html>") || html_str.contains("<html"));
80    }
81
82    #[tokio::test]
83    async fn test_serve_admin_css() {
84        let (headers, css) = serve_admin_css().await;
85        assert_eq!(headers[0].0, http::header::CONTENT_TYPE);
86        assert_eq!(headers[0].1, "text/css");
87        assert!(!css.is_empty());
88    }
89
90    #[tokio::test]
91    async fn test_serve_admin_js() {
92        let (headers, js) = serve_admin_js().await;
93        assert_eq!(headers[0].0, http::header::CONTENT_TYPE);
94        assert_eq!(headers[0].1, "application/javascript");
95        assert!(!js.is_empty());
96    }
97
98    #[tokio::test]
99    async fn test_serve_icon() {
100        let response = serve_icon().await;
101        // Icon returns SVG content - we can't easily check headers in impl IntoResponse
102        // but we can verify it returns successfully
103        let _ = response;
104    }
105
106    #[tokio::test]
107    async fn test_serve_icon_32() {
108        let _ = serve_icon_32().await;
109    }
110
111    #[tokio::test]
112    async fn test_serve_icon_48() {
113        let _ = serve_icon_48().await;
114    }
115
116    #[tokio::test]
117    async fn test_serve_logo() {
118        let _ = serve_logo().await;
119    }
120
121    #[tokio::test]
122    async fn test_serve_logo_40() {
123        let _ = serve_logo_40().await;
124    }
125
126    #[tokio::test]
127    async fn test_serve_logo_80() {
128        let _ = serve_logo_80().await;
129    }
130
131    #[tokio::test]
132    async fn test_serve_api_docs() {
133        let _ = serve_api_docs().await;
134        // Redirect can't be easily tested without request context
135        // but we verify it compiles and runs
136    }
137}