OXYGENHUB

Coverage / crates/hub/src/web/statics.rs 89.6%

43 of 48 lines run, 8 of 20 functions · run #536 on main · run missed

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.
145161×
pub fn assets_version() -> &'static str {
15
    static V: std::sync::OnceLock<String> = std::sync::OnceLock::new();
165161×
    V.get_or_init(|| {
17132×
        let mut h = blake3::Hasher::new();
18132×
        h.update(CSS.as_bytes());
192640×
        for a in ASSETS.iter().filter(|a| {
201188×
            matches!(
212640×
                a.path,
222640×
                "github.css"
232508×
                    | "hub.js"
242376×
                    | "early.js"
252244×
                    | "pulse.js"
262112×
                    | "backdrop-boot.js"
271980×
                    | "report-frame.js"
281848×
                    | "report.css"
291716×
                    | "backdrop/backdrop.js"
301584×
                    | "backdrop/backdrop_bg.wasm"
31
            )
322640×
        }) {
331188×
            h.update(a.bytes);
341188×
        }
35132×
        h.finalize().to_hex()[..12].to_string()
36132×
    })
375161×
}
38
39
struct Asset {
40
    path: &'static str,
41
    mime: &'static str,
42
    bytes: &'static [u8],
43
}
44
45
const ASSETS: &[Asset] = &[
46
    Asset { path: "fonts/Barlow-300.woff2", mime: "font/woff2", bytes: include_bytes!("../../../../assets/fonts/Barlow-300.woff2") },
47
    Asset { path: "fonts/Barlow-400.woff2", mime: "font/woff2", bytes: include_bytes!("../../../../assets/fonts/Barlow-400.woff2") },
48
    Asset { path: "fonts/Barlow-500.woff2", mime: "font/woff2", bytes: include_bytes!("../../../../assets/fonts/Barlow-500.woff2") },
49
    Asset { path: "fonts/Barlow-600.woff2", mime: "font/woff2", bytes: include_bytes!("../../../../assets/fonts/Barlow-600.woff2") },
50
    Asset {
51
        path: "fonts/PressStart2P-400.woff2",
52
        mime: "font/woff2",
53
        bytes: include_bytes!("../../../../assets/fonts/PressStart2P-400.woff2"),
54
    },
55
    Asset { path: "github.css", mime: "text/css; charset=utf-8", bytes: include_bytes!("../../../../assets/github.css") },
56
    Asset { path: "hub.js", mime: "text/javascript; charset=utf-8", bytes: include_bytes!("../../../../assets/hub.js") },
57
    // What pages ran inline before the CSP was enforced (#227): the look
58
    // and theme before first paint, the pulse chart, the backdrop's start.
59
    Asset { path: "early.js", mime: "text/javascript; charset=utf-8", bytes: include_bytes!("../../../../assets/early.js") },
60
    Asset { path: "pulse.js", mime: "text/javascript; charset=utf-8", bytes: include_bytes!("../../../../assets/pulse.js") },
61
    Asset {
62
        path: "backdrop-boot.js",
63
        mime: "text/javascript; charset=utf-8",
64
        bytes: include_bytes!("../../../../assets/backdrop-boot.js"),
65
    },
66
    // For report frames (#127): the size and theme bridge the hub adds to
67
    // a report's pages, and the stylesheet a report may link.
68
    Asset { path: "report-frame.js", mime: "text/javascript; charset=utf-8", bytes: include_bytes!("../../../../assets/report-frame.js") },
69
    Asset { path: "report.css", mime: "text/css; charset=utf-8", bytes: include_bytes!("../../../../assets/report.css") },
70
    // Backdrops (docs/backgrounds.md, #157): the engine, Rust compiled to
71
    // WebAssembly (crates/backdrop), and its wasm-bindgen glue, both
72
    // written by `make backdrop`.
73
    Asset {
74
        path: "backdrop/backdrop.js",
75
        mime: "text/javascript; charset=utf-8",
76
        bytes: include_bytes!("../../../../assets/backdrop/backdrop.js"),
77
    },
78
    Asset {
79
        path: "backdrop/backdrop_bg.wasm",
80
        mime: "application/wasm",
81
        bytes: include_bytes!("../../../../assets/backdrop/backdrop_bg.wasm"),
82
    },
83
    Asset {
84
        path: "vendor/editor.js",
85
        mime: "text/javascript; charset=utf-8",
86
        bytes: include_bytes!("../../../../assets/vendor/editor.js"),
87
    },
88
    Asset {
89
        path: "vendor/easymde.min.js",
90
        mime: "text/javascript; charset=utf-8",
91
        bytes: include_bytes!("../../../../assets/vendor/easymde.min.js"),
92
    },
93
    // Mermaid 12.1.0 (MIT, assets/vendor/mermaid.LICENSE), from npm's
94
    // mermaid package, dist/mermaid.min.js; loaded only by pages with a
95
    // diagram.
96
    Asset {
97
        path: "vendor/mermaid.min.js",
98
        mime: "text/javascript; charset=utf-8",
99
        bytes: include_bytes!("../../../../assets/vendor/mermaid.min.js"),
100
    },
101
    Asset { path: "wasi-worker.js", mime: "text/javascript; charset=utf-8", bytes: include_bytes!("../../../../assets/wasi-worker.js") },
102
    Asset { path: "wasm-tab.js", mime: "text/javascript; charset=utf-8", bytes: include_bytes!("../../../../assets/wasm-tab.js") },
103
    Asset {
104
        path: "vendor/easymde.min.css",
105
        mime: "text/css; charset=utf-8",
106
        bytes: include_bytes!("../../../../assets/vendor/easymde.min.css"),
107
    },
108
];
109
110
const CACHE: &str = "public, max-age=86400";
111
1121×
pub async fn style() -> Response {
1131×
    ([(header::CONTENT_TYPE, "text/css; charset=utf-8"), (header::CACHE_CONTROL, CACHE)], CSS).into_response()
1141×
}
115
1160×
pub async fn favicon() -> Response {
1170×
    ([(header::CONTENT_TYPE, "image/svg+xml"), (header::CACHE_CONTROL, CACHE)], FAVICON).into_response()
1180×
}
119
1205×
pub async fn asset(Path(path): Path<String>) -> Response {
12153×
    match ASSETS.iter().find(|a| a.path == path) {
122
        // Readable across origins: a report's sandboxed frame has an
123
        // opaque origin, and loads fonts in CORS mode (#127).
1245×
        Some(a) => (
1255×
            [
1265×
                (header::CONTENT_TYPE, a.mime),
1275×
                (header::CACHE_CONTROL, "public, max-age=31536000, immutable"),
1285×
                (header::ACCESS_CONTROL_ALLOW_ORIGIN, "*"),
1295×
            ],
1305×
            a.bytes,
1315×
        )
1325×
            .into_response(),
1330×
        None => StatusCode::NOT_FOUND.into_response(),
134
    }
1355×
}