mockforge_ui/handlers/
assets.rs1use axum::{
7 extract::Path,
8 http::{self, StatusCode},
9 response::{Html, IntoResponse, Redirect},
10};
11use std::collections::HashMap;
12
13include!(concat!(env!("OUT_DIR"), "/asset_paths.rs"));
15
16pub async fn serve_admin_html() -> Html<&'static str> {
18 Html(include_str!("../../ui/dist/index.html"))
19}
20
21pub async fn serve_admin_css() -> ([(http::HeaderName, &'static str); 1], &'static str) {
23 (
24 [(http::header::CONTENT_TYPE, "text/css")],
25 include_str!("../../ui/dist/assets/index.css"),
26 )
27}
28
29pub async fn serve_admin_js() -> ([(http::HeaderName, &'static str); 1], &'static str) {
31 (
32 [(http::header::CONTENT_TYPE, "application/javascript")],
33 include_str!("../../ui/dist/assets/index.js"),
34 )
35}
36
37pub async fn serve_vendor_asset(Path(filename): Path<String>) -> impl IntoResponse {
41 let content_type = if filename.ends_with(".js") {
43 "application/javascript"
44 } else if filename.ends_with(".css") {
45 "text/css"
46 } else if filename.ends_with(".png") {
47 "image/png"
48 } else if filename.ends_with(".svg") {
49 "image/svg+xml"
50 } else if filename.ends_with(".woff") || filename.ends_with(".woff2") {
51 "font/woff2"
52 } else {
53 "application/octet-stream"
54 };
55
56 let asset_map = get_asset_map();
58 if let Some(content) = asset_map.get(filename.as_str()) {
59 ([(http::header::CONTENT_TYPE, content_type)], *content).into_response()
60 } else {
61 (
63 StatusCode::NOT_FOUND,
64 [(http::header::CONTENT_TYPE, "text/plain")],
65 "Asset not found",
66 )
67 .into_response()
68 }
69}
70
71pub async fn serve_icon() -> impl IntoResponse {
73 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>";
75 ([(http::header::CONTENT_TYPE, "image/svg+xml")], icon_svg)
76}
77
78pub async fn serve_icon_32() -> impl IntoResponse {
80 serve_icon().await
81}
82
83pub async fn serve_icon_48() -> impl IntoResponse {
85 serve_icon().await
86}
87
88pub async fn serve_logo() -> impl IntoResponse {
90 serve_icon().await
91}
92
93pub async fn serve_logo_40() -> impl IntoResponse {
95 serve_icon().await
96}
97
98pub async fn serve_logo_80() -> impl IntoResponse {
100 serve_icon().await
101}
102
103pub async fn serve_api_docs() -> impl IntoResponse {
105 Redirect::permanent("https://docs.mockforge.dev/api/admin-ui-rest.html")
107}
108
109#[cfg(test)]
110mod tests {
111 use super::*;
112
113 #[tokio::test]
114 async fn test_serve_admin_html() {
115 let html = serve_admin_html().await;
116 let html_str = html.0;
117 assert!(!html_str.is_empty());
118 assert!(html_str.contains("<!DOCTYPE html>") || html_str.contains("<html"));
119 }
120
121 #[tokio::test]
122 async fn test_serve_admin_css() {
123 let (headers, css) = serve_admin_css().await;
124 assert_eq!(headers[0].0, http::header::CONTENT_TYPE);
125 assert_eq!(headers[0].1, "text/css");
126 assert!(!css.is_empty());
127 }
128
129 #[tokio::test]
130 async fn test_serve_admin_js() {
131 let (headers, js) = serve_admin_js().await;
132 assert_eq!(headers[0].0, http::header::CONTENT_TYPE);
133 assert_eq!(headers[0].1, "application/javascript");
134 assert!(!js.is_empty());
135 }
136
137 #[tokio::test]
138 async fn test_serve_icon() {
139 let response = serve_icon().await;
140 let _ = response;
143 }
144
145 #[tokio::test]
146 async fn test_serve_icon_32() {
147 let _ = serve_icon_32().await;
148 }
149
150 #[tokio::test]
151 async fn test_serve_icon_48() {
152 let _ = serve_icon_48().await;
153 }
154
155 #[tokio::test]
156 async fn test_serve_logo() {
157 let _ = serve_logo().await;
158 }
159
160 #[tokio::test]
161 async fn test_serve_logo_40() {
162 let _ = serve_logo_40().await;
163 }
164
165 #[tokio::test]
166 async fn test_serve_logo_80() {
167 let _ = serve_logo_80().await;
168 }
169
170 #[tokio::test]
171 async fn test_serve_api_docs() {
172 let _ = serve_api_docs().await;
173 }
176}