OXYGENHUB
# 9a777fec4ed9 oxygenhub / crates / hub / src / web / statics.rs
4.9 KiB · Rust · last changed feat(web): Backdrops, the engine in Rust and WASM, and the dashboard's Your sky (refs #157) 22 days ago source blame history raw
1
//! Compiled-in static assets: the stylesheet, fonts and icons.
2
3
use axum::extract::Path;
4
use axum::http::{header, StatusCode};
5
use axum::response::{IntoResponse, Response};
6
7
const CSS: &str = include_str!("../../../../templates/style.css");
8
const FAVICON: &str = include_str!("../../../../assets/icons/oxygenhub.svg");
9
10
/// A short hash of the stylesheets and script the pages link, put in their
11
/// URLs (`?v=…`): they are served for a long time and cached by any proxy
12
/// in front of the hub (Cloudflare held a day-old stylesheet), so a new
13
/// version must have a new URL.
14
pub fn assets_version() -> &'static str {
15
    static V: std::sync::OnceLock<String> = std::sync::OnceLock::new();
16
    V.get_or_init(|| {
17
        let mut h = blake3::Hasher::new();
18
        h.update(CSS.as_bytes());
19
        for a in ASSETS.iter().filter(|a| {
20
            matches!(
21
                a.path,
22
                "github.css" | "hub.js" | "report-frame.js" | "report.css" | "backdrop/backdrop.js" | "backdrop/backdrop_bg.wasm"
23
            )
24
        }) {
25
            h.update(a.bytes);
26
        }
27
        h.finalize().to_hex()[..12].to_string()
28
    })
29
}
30
31
struct Asset {
32
    path: &'static str,
33
    mime: &'static str,
34
    bytes: &'static [u8],
35
}
36
37
const ASSETS: &[Asset] = &[
38
    Asset { path: "fonts/Barlow-300.woff2", mime: "font/woff2", bytes: include_bytes!("../../../../assets/fonts/Barlow-300.woff2") },
39
    Asset { path: "fonts/Barlow-400.woff2", mime: "font/woff2", bytes: include_bytes!("../../../../assets/fonts/Barlow-400.woff2") },
40
    Asset { path: "fonts/Barlow-500.woff2", mime: "font/woff2", bytes: include_bytes!("../../../../assets/fonts/Barlow-500.woff2") },
41
    Asset { path: "fonts/Barlow-600.woff2", mime: "font/woff2", bytes: include_bytes!("../../../../assets/fonts/Barlow-600.woff2") },
42
    Asset {
43
        path: "fonts/PressStart2P-400.woff2",
44
        mime: "font/woff2",
45
        bytes: include_bytes!("../../../../assets/fonts/PressStart2P-400.woff2"),
46
    },
47
    Asset { path: "github.css", mime: "text/css; charset=utf-8", bytes: include_bytes!("../../../../assets/github.css") },
48
    Asset { path: "hub.js", mime: "text/javascript; charset=utf-8", bytes: include_bytes!("../../../../assets/hub.js") },
49
    // For report frames (#127): the size and theme bridge the hub adds to
50
    // a report's pages, and the stylesheet a report may link.
51
    Asset { path: "report-frame.js", mime: "text/javascript; charset=utf-8", bytes: include_bytes!("../../../../assets/report-frame.js") },
52
    Asset { path: "report.css", mime: "text/css; charset=utf-8", bytes: include_bytes!("../../../../assets/report.css") },
53
    // Backdrops (docs/backgrounds.md, #157): the engine, Rust compiled to
54
    // WebAssembly (crates/backdrop), and its wasm-bindgen glue, both
55
    // written by `make backdrop`.
56
    Asset {
57
        path: "backdrop/backdrop.js",
58
        mime: "text/javascript; charset=utf-8",
59
        bytes: include_bytes!("../../../../assets/backdrop/backdrop.js"),
60
    },
61
    Asset {
62
        path: "backdrop/backdrop_bg.wasm",
63
        mime: "application/wasm",
64
        bytes: include_bytes!("../../../../assets/backdrop/backdrop_bg.wasm"),
65
    },
66
    Asset {
67
        path: "vendor/editor.js",
68
        mime: "text/javascript; charset=utf-8",
69
        bytes: include_bytes!("../../../../assets/vendor/editor.js"),
70
    },
71
    Asset {
72
        path: "vendor/easymde.min.js",
73
        mime: "text/javascript; charset=utf-8",
74
        bytes: include_bytes!("../../../../assets/vendor/easymde.min.js"),
75
    },
76
    // Mermaid 12.1.0 (MIT, assets/vendor/mermaid.LICENSE), from npm's
77
    // mermaid package, dist/mermaid.min.js; loaded only by pages with a
78
    // diagram.
79
    Asset {
80
        path: "vendor/mermaid.min.js",
81
        mime: "text/javascript; charset=utf-8",
82
        bytes: include_bytes!("../../../../assets/vendor/mermaid.min.js"),
83
    },
84
    Asset {
85
        path: "vendor/easymde.min.css",
86
        mime: "text/css; charset=utf-8",
87
        bytes: include_bytes!("../../../../assets/vendor/easymde.min.css"),
88
    },
89
];
90
91
const CACHE: &str = "public, max-age=86400";
92
93
pub async fn style() -> Response {
94
    ([(header::CONTENT_TYPE, "text/css; charset=utf-8"), (header::CACHE_CONTROL, CACHE)], CSS).into_response()
95
}
96
97
pub async fn favicon() -> Response {
98
    ([(header::CONTENT_TYPE, "image/svg+xml"), (header::CACHE_CONTROL, CACHE)], FAVICON).into_response()
99
}
100
101
pub async fn asset(Path(path): Path<String>) -> Response {
102
    match ASSETS.iter().find(|a| a.path == path) {
103
        // Readable across origins: a report's sandboxed frame has an
104
        // opaque origin, and loads fonts in CORS mode (#127).
105
        Some(a) => (
106
            [
107
                (header::CONTENT_TYPE, a.mime),
108
                (header::CACHE_CONTROL, "public, max-age=31536000, immutable"),
109
                (header::ACCESS_CONTROL_ALLOW_ORIGIN, "*"),
110
            ],
111
            a.bytes,
112
        )
113
            .into_response(),
114
        None => StatusCode::NOT_FOUND.into_response(),
115
    }
116
}