1use std::sync::Arc;
10
11use axum::Router;
12use axum::extract::State;
13use axum::http::{StatusCode, header};
14use axum::middleware::from_fn_with_state;
15use axum::response::{IntoResponse, Response};
16use axum::routing::get;
17use kernel::AxError;
18
19use crate::assets::{AssetReply, ClientAssets};
20use crate::reception::PageHeaders;
21
22use super::config::refusal_text;
23use super::guard::{Stamp, Stored, stamp};
24
25pub fn bundle_routes<S>(
39 client: Arc<ClientAssets>,
40 headers: &PageHeaders,
41) -> Result<Router<S>, AxError>
42where
43 S: Clone + Send + Sync + 'static,
44{
45 let stamped = Arc::new(Stamp::of(headers, Stored::AsPage)?);
46 Ok(Router::new()
47 .route("/", get(serve_index))
48 .route("/{*asset}", get(serve_asset))
49 .with_state(client)
50 .layer(from_fn_with_state(stamped, stamp)))
51}
52
53async fn serve_index(State(client): State<Arc<ClientAssets>>) -> Response {
54 asset_response(client.lookup("index.html"))
55}
56
57async fn serve_asset(
58 State(client): State<Arc<ClientAssets>>,
59 axum::extract::Path(asset): axum::extract::Path<String>,
60) -> Response {
61 asset_response(client.lookup(&asset))
62}
63
64fn asset_response(reply: AssetReply) -> Response {
66 match reply {
67 AssetReply::Found {
68 bytes,
69 content_type,
70 gzipped: true,
71 } => (
72 StatusCode::OK,
73 [
74 (header::CONTENT_TYPE, content_type),
75 (header::CONTENT_ENCODING, "gzip"),
76 ],
77 bytes,
78 )
79 .into_response(),
80 AssetReply::Found {
81 bytes,
82 content_type,
83 gzipped: false,
84 } => (
85 StatusCode::OK,
86 [(header::CONTENT_TYPE, content_type)],
87 bytes,
88 )
89 .into_response(),
90 AssetReply::Miss(err) => (StatusCode::NOT_FOUND, refusal_text(&err)).into_response(),
91 }
92}