From ac28f04d4d7e23d01a61459f81dbee7f7c3e7c9f Mon Sep 17 00:00:00 2001 From: ozpv <39195175+ozpv@users.noreply.github.com> Date: Wed, 27 May 2026 01:28:39 -0500 Subject: inject components server-side --- public/components/head.html | 4 +- public/components/nav.html | 4 +- src/main.rs | 92 ++++++++++++++++++++++++++++++++++++--------- 3 files changed, 79 insertions(+), 21 deletions(-) diff --git a/public/components/head.html b/public/components/head.html index d7d43cc..b1dbda8 100644 --- a/public/components/head.html +++ b/public/components/head.html @@ -1,7 +1,7 @@ - + haemolacriaa - \ No newline at end of file + diff --git a/public/components/nav.html b/public/components/nav.html index f9d5adc..447bc12 100644 --- a/public/components/nav.html +++ b/public/components/nav.html @@ -13,5 +13,5 @@ - - \ No newline at end of file + + diff --git a/src/main.rs b/src/main.rs index 1ddddfb..4f03f7f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,13 +1,14 @@ use axum::{ Router, + body::{self, Body}, extract::{Request, State}, handler::HandlerWithoutStateExt, - http::StatusCode, - response::{Html, IntoResponse}, - routing::{get, get_service}, + http::{StatusCode, header}, + response::{Html, IntoResponse, Response}, + routing::get_service, }; use site::{env::Env, state::AppState}; -use std::{path::PathBuf, time::Duration}; +use std::{fs, path::PathBuf, time::Duration}; use tokio::net::TcpListener; use tower::util::ServiceExt; use tower_http::{ @@ -24,14 +25,48 @@ async fn not_found() -> impl IntoResponse { ERR } -async fn static_files(State(state): State, req: Request) -> impl IntoResponse { - ServeDir::new(state.get_dist_dir().as_ref()) - .precompressed_br() - .precompressed_gzip() - .fallback(not_found.into_service()) - .oneshot(req) - .await - .map_err(|_| ERR) +async fn inject( + res: Response, + component_name: &'static str, + after: &'static str, + state: AppState, +) -> impl IntoResponse { + if !res + .headers() + .get(header::CONTENT_TYPE) + .and_then(|value| value.to_str().ok()) + .is_some_and(|value| value.contains("text/html")) + { + return res; + } + + let (parts, body) = res.into_parts(); + + let Ok(body) = body::to_bytes(body, 5_000_000).await else { + return ERR.into_response(); + }; + + let Ok(mut string) = String::from_utf8(body.to_vec()) else { + return ERR.into_response(); + }; + + let Some(index) = string.find(after) else { + return ERR.into_response(); + }; + + let path = state + .get_dist_dir() + .as_ref() + .join("components") + .join(component_name); + + let Ok(component) = fs::read_to_string(path) else { + return ERR.into_response(); + }; + + string.insert_str(index + after.len(), component.as_str()); + + (parts, Body::from(string)).into_response() } async fn html_files(State(state): State, mut req: Request) -> impl IntoResponse { @@ -43,23 +78,47 @@ async fn html_files(State(state): State, mut req: Request) -> impl Int *req.uri_mut() = file_path .to_str() - // if this were to run on a windows machine - .map(|s| s.replace("\\", "/")) + // in case this were to run on a windows machine + .map(|s| s.replace('\\', "/")) .ok_or(ERR)? .parse() .map_err(|_| ERR)?; + println!( + "{}{}", + state.get_dist_dir().as_ref().display(), + req.uri().path() + ); + ServeDir::new(state.get_dist_dir().as_ref()) + .precompressed_br() + .precompressed_gzip() .fallback(not_found.into_service()) .oneshot(req) .await .map_err(|_| ERR) } +// TODO: move all of this to build.rs +async fn html_inject_head_nav_footer( + State(state): State, + req: Request, +) -> impl IntoResponse { + let res = html_files(State(state.clone()), req).await.into_response(); + let res = inject(res, "head.html", "", state.clone()) + .await + .into_response(); + let res = inject(res, "nav.html", "", state.clone()) + .await + .into_response(); + + inject(res, "footer.html", "", state.clone()).await +} + #[tokio::main] async fn main() -> Result<(), Box> { tracing_subscriber::fmt() - .with_max_level(tracing::Level::DEBUG) + .with_max_level(tracing::Level::TRACE) .init(); let Env { @@ -76,8 +135,7 @@ async fn main() -> Result<(), Box> { "/", get_service(ServeFile::new(dist_dir.join("index.html"))), ) - .route("/assets/{*any}", get(static_files)) - .fallback(html_files) + .fallback(html_inject_head_nav_footer) .layer(CompressionLayer::new().gzip(true)) .layer(TimeoutLayer::with_status_code( StatusCode::REQUEST_TIMEOUT, -- cgit v1.2.3