lean_ctx/gateway_server/
admin_ui.rs1use axum::http::header;
15use axum::response::IntoResponse;
16
17const ADMIN_INDEX_HTML: &str = include_str!("static/index.html");
18const ADMIN_CSS: &str = include_str!("static/admin.css");
19const ADMIN_JS: &str = include_str!("static/admin.js");
20
21const FONTS_CSS: &str = include_str!("../dashboard/static/fonts/fonts.css");
23const FONT_INTER_WOFF2: &[u8] = include_bytes!("../dashboard/static/fonts/inter-variable.woff2");
24const FONT_JETBRAINS_WOFF2: &[u8] =
25 include_bytes!("../dashboard/static/fonts/jetbrains-mono-variable.woff2");
26const FONT_SPACE_GROTESK_WOFF2: &[u8] =
27 include_bytes!("../dashboard/static/fonts/space-grotesk-variable.woff2");
28const VENDOR_CHART_JS: &str = include_str!("../dashboard/static/vendor/chart.umd.min.js");
29
30pub fn router() -> axum::Router {
32 axum::Router::new()
33 .route("/", axum::routing::get(index))
34 .route("/static/admin.css", axum::routing::get(css))
35 .route("/static/admin.js", axum::routing::get(js))
36 .route("/static/fonts/fonts.css", axum::routing::get(fonts_css))
37 .route(
38 "/static/vendor/chart.umd.min.js",
39 axum::routing::get(chart_js),
40 )
41 .route("/static/fonts/{file}", axum::routing::get(font_file))
42}
43
44async fn index() -> impl IntoResponse {
45 (
46 [(header::CONTENT_TYPE, "text/html; charset=utf-8")],
47 ADMIN_INDEX_HTML,
48 )
49}
50
51async fn css() -> impl IntoResponse {
52 (
53 [(header::CONTENT_TYPE, "text/css; charset=utf-8")],
54 ADMIN_CSS,
55 )
56}
57
58async fn js() -> impl IntoResponse {
59 (
60 [(
61 header::CONTENT_TYPE,
62 "application/javascript; charset=utf-8",
63 )],
64 ADMIN_JS,
65 )
66}
67
68async fn fonts_css() -> impl IntoResponse {
69 (
70 [(header::CONTENT_TYPE, "text/css; charset=utf-8")],
71 FONTS_CSS,
72 )
73}
74
75async fn chart_js() -> impl IntoResponse {
76 (
77 [(
78 header::CONTENT_TYPE,
79 "application/javascript; charset=utf-8",
80 )],
81 VENDOR_CHART_JS,
82 )
83}
84
85async fn font_file(
86 axum::extract::Path(file): axum::extract::Path<String>,
87) -> axum::response::Response {
88 let bytes: &'static [u8] = match file.as_str() {
89 "inter-variable.woff2" => FONT_INTER_WOFF2,
90 "jetbrains-mono-variable.woff2" => FONT_JETBRAINS_WOFF2,
91 "space-grotesk-variable.woff2" => FONT_SPACE_GROTESK_WOFF2,
92 _ => return axum::http::StatusCode::NOT_FOUND.into_response(),
93 };
94 ([(header::CONTENT_TYPE, "font/woff2")], bytes).into_response()
95}
96
97#[cfg(test)]
98mod tests {
99 use super::*;
100
101 #[test]
102 fn embedded_assets_are_nonempty_and_wired() {
103 assert!(ADMIN_INDEX_HTML.contains("<!doctype html"));
104 assert!(
105 ADMIN_INDEX_HTML.contains("/static/admin.js"),
106 "shell must load the app script"
107 );
108 assert!(ADMIN_CSS.contains(":root"), "design tokens present");
109 assert!(
110 ADMIN_JS.contains("/api/admin/usage"),
111 "app must talk to the guarded API"
112 );
113 assert!(!VENDOR_CHART_JS.is_empty());
114 assert!(!FONT_INTER_WOFF2.is_empty());
115 }
116
117 #[test]
118 fn shell_never_embeds_credentials() {
119 for (idx, _) in ADMIN_INDEX_HTML.match_indices("Bearer ") {
124 let after = &ADMIN_INDEX_HTML[idx + "Bearer ".len()..];
125 assert!(
126 after.starts_with("<"),
127 "index.html embeds 'Bearer ' followed by non-placeholder material: {:?}",
128 &after[..after.len().min(40)]
129 );
130 }
131 assert!(
132 !ADMIN_INDEX_HTML.contains("LEAN_CTX_GATEWAY_ADMIN_TOKEN="),
133 "index.html must not embed the admin token env assignment"
134 );
135 assert!(
136 !ADMIN_JS.contains("localStorage.setItem('leanctx-admin-token'"),
137 "token must live in sessionStorage, not persist in localStorage"
138 );
139 }
140}