Merge asset and page server
This commit is contained in:
44
crates/lib/page/src/servable/asset.rs
Normal file
44
crates/lib/page/src/servable/asset.rs
Normal file
@@ -0,0 +1,44 @@
|
||||
use axum::http::{
|
||||
HeaderMap, HeaderValue, StatusCode,
|
||||
header::{self},
|
||||
};
|
||||
use std::pin::Pin;
|
||||
use toolbox::mime::MimeType;
|
||||
|
||||
use crate::{Rendered, RenderedBody, RequestContext, Servable};
|
||||
|
||||
pub struct StaticAsset {
|
||||
pub bytes: &'static [u8],
|
||||
pub mime: MimeType,
|
||||
}
|
||||
|
||||
impl Servable for StaticAsset {
|
||||
fn render<'a>(
|
||||
&'a self,
|
||||
_ctx: &'a RequestContext,
|
||||
) -> Pin<Box<dyn Future<Output = crate::Rendered> + 'a + Send + Sync>> {
|
||||
Box::pin(async {
|
||||
let mut headers = HeaderMap::with_capacity(3);
|
||||
|
||||
#[expect(clippy::unwrap_used)]
|
||||
headers.insert(
|
||||
header::CONTENT_TYPE,
|
||||
HeaderValue::from_str(&self.mime.to_string()).unwrap(),
|
||||
);
|
||||
|
||||
headers.insert(
|
||||
header::CACHE_CONTROL,
|
||||
HeaderValue::from_str(&format!("immutable, public, max-age={}", 60 * 60 * 24 * 30))
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
return Rendered {
|
||||
code: StatusCode::OK,
|
||||
headers,
|
||||
body: RenderedBody::Static(self.bytes),
|
||||
ttl: None,
|
||||
immutable: true,
|
||||
};
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,2 +1,3 @@
|
||||
pub mod asset;
|
||||
pub mod page;
|
||||
pub mod redirect;
|
||||
|
||||
@@ -7,7 +7,7 @@ use maud::{Markup, Render, html};
|
||||
use serde::Deserialize;
|
||||
use std::pin::Pin;
|
||||
|
||||
use crate::{Rendered, RequestContext, Servable};
|
||||
use crate::{Rendered, RenderedBody, RequestContext, Servable};
|
||||
|
||||
//
|
||||
// MARK: metadata
|
||||
@@ -67,6 +67,7 @@ impl Render for PageMetadata {
|
||||
// Some HTML
|
||||
pub struct Page {
|
||||
pub meta: PageMetadata,
|
||||
pub immutable: bool,
|
||||
|
||||
/// How long this page's html may be cached.
|
||||
/// This controls the maximum age of a page shown to the user.
|
||||
@@ -94,10 +95,11 @@ impl Default for Page {
|
||||
fn default() -> Self {
|
||||
Page {
|
||||
meta: Default::default(),
|
||||
html_ttl: Some(TimeDelta::seconds(60 * 24 * 30)),
|
||||
//css_ttl: Duration::from_secs(60 * 24 * 30),
|
||||
html_ttl: Some(TimeDelta::seconds(60 * 60 * 24 * 30)),
|
||||
//css_ttl: Duration::from_secs(60 * 60 * 24 * 30),
|
||||
//generate_css: None,
|
||||
generate_html: Box::new(|_, _| Box::pin(async { html!() })),
|
||||
immutable: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -125,8 +127,9 @@ impl Servable for Page {
|
||||
return Rendered {
|
||||
code: StatusCode::OK,
|
||||
headers,
|
||||
body: html.0.into_bytes(),
|
||||
body: RenderedBody::Markup(html),
|
||||
ttl: self.html_ttl,
|
||||
immutable: self.immutable,
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ use axum::http::{
|
||||
header::{self, InvalidHeaderValue},
|
||||
};
|
||||
|
||||
use crate::{Rendered, RequestContext, Servable};
|
||||
use crate::{Rendered, RenderedBody, RequestContext, Servable};
|
||||
|
||||
pub struct Redirect {
|
||||
to: HeaderValue,
|
||||
@@ -31,8 +31,9 @@ impl Servable for Redirect {
|
||||
return Rendered {
|
||||
code: StatusCode::PERMANENT_REDIRECT,
|
||||
headers,
|
||||
body: Vec::new(),
|
||||
body: RenderedBody::Empty,
|
||||
ttl: None,
|
||||
immutable: true,
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ use axum::{
|
||||
use chrono::{DateTime, TimeDelta, Utc};
|
||||
use libservice::ServiceConnectInfo;
|
||||
use lru::LruCache;
|
||||
use maud::Markup;
|
||||
use parking_lot::Mutex;
|
||||
use std::{collections::HashMap, num::NonZero, pin::Pin, sync::Arc, time::Instant};
|
||||
use tower_http::compression::{CompressionLayer, DefaultPredicate};
|
||||
@@ -15,13 +16,21 @@ use tracing::trace;
|
||||
|
||||
use crate::{ClientInfo, RequestContext};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum RenderedBody {
|
||||
Markup(Markup),
|
||||
Static(&'static [u8]),
|
||||
Empty,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Rendered {
|
||||
pub code: StatusCode,
|
||||
pub headers: HeaderMap,
|
||||
pub body: Vec<u8>,
|
||||
pub body: RenderedBody,
|
||||
|
||||
pub ttl: Option<TimeDelta>,
|
||||
pub immutable: bool,
|
||||
}
|
||||
|
||||
pub trait Servable: Send + Sync {
|
||||
@@ -209,20 +218,31 @@ impl PageServer {
|
||||
#[expect(clippy::unwrap_used)]
|
||||
let (mut html, expires) = html_expires.unwrap();
|
||||
|
||||
let max_age = match expires {
|
||||
Some(expires) => (expires - now).num_seconds().max(1),
|
||||
None => 1,
|
||||
};
|
||||
if !html.headers.contains_key(header::CACHE_CONTROL) {
|
||||
let max_age = match expires {
|
||||
Some(expires) => (expires - now).num_seconds().max(1),
|
||||
None => 1,
|
||||
};
|
||||
|
||||
#[expect(clippy::unwrap_used)]
|
||||
html.headers.insert(
|
||||
header::CACHE_CONTROL,
|
||||
// immutable; public/private
|
||||
HeaderValue::from_str(&format!("immutable, public, max-age={}", max_age)).unwrap(),
|
||||
);
|
||||
#[expect(clippy::unwrap_used)]
|
||||
let mut value = String::new();
|
||||
if html.immutable {
|
||||
value.push_str("immutable, ");
|
||||
}
|
||||
|
||||
html.headers
|
||||
.insert("Accept-CH", HeaderValue::from_static("Sec-CH-UA-Mobile"));
|
||||
value.push_str("public, ");
|
||||
value.push_str(&format!("max-age={}, ", max_age));
|
||||
|
||||
html.headers.insert(
|
||||
header::CACHE_CONTROL,
|
||||
HeaderValue::from_str(&value.trim().trim_end_matches(',')).unwrap(),
|
||||
);
|
||||
}
|
||||
|
||||
if !html.headers.contains_key("Accept-CH") {
|
||||
html.headers
|
||||
.insert("Accept-CH", HeaderValue::from_static("Sec-CH-UA-Mobile"));
|
||||
}
|
||||
|
||||
trace!(
|
||||
message = "Served route",
|
||||
@@ -233,7 +253,11 @@ impl PageServer {
|
||||
time_ns = start.elapsed().as_nanos()
|
||||
);
|
||||
|
||||
return (html.code, html.headers, html.body).into_response();
|
||||
return match html.body {
|
||||
RenderedBody::Markup(markup) => (html.code, html.headers, markup.0).into_response(),
|
||||
RenderedBody::Static(data) => (html.code, html.headers, data).into_response(),
|
||||
RenderedBody::Empty => (html.code, html.headers).into_response(),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn into_router(self: Arc<Self>) -> Router<()> {
|
||||
|
||||
Reference in New Issue
Block a user