Compare commits
2 Commits
08586f0a7a
...
11d7f8b0d0
| Author | SHA1 | Date | |
|---|---|---|---|
| 11d7f8b0d0 | |||
| 0da489dc1b |
@@ -93,9 +93,7 @@ impl Default for Page {
|
|||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Page {
|
Page {
|
||||||
meta: Default::default(),
|
meta: Default::default(),
|
||||||
html_ttl: Some(TimeDelta::seconds(60 * 60 * 24 * 30)),
|
html_ttl: Some(TimeDelta::days(1)),
|
||||||
//css_ttl: Duration::from_secs(60 * 60 * 24 * 30),
|
|
||||||
//generate_css: None,
|
|
||||||
generate_html: Box::new(|_, _| Box::pin(async { html!() })),
|
generate_html: Box::new(|_, _| Box::pin(async { html!() })),
|
||||||
immutable: true,
|
immutable: true,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,6 +52,29 @@ pub trait Servable: Send + Sync {
|
|||||||
) -> Pin<Box<dyn Future<Output = Rendered> + 'a + Send + Sync>>;
|
) -> Pin<Box<dyn Future<Output = Rendered> + 'a + Send + Sync>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct Default404 {}
|
||||||
|
impl Servable for Default404 {
|
||||||
|
fn render<'a>(
|
||||||
|
&'a self,
|
||||||
|
_ctx: &'a RequestContext,
|
||||||
|
) -> Pin<Box<dyn Future<Output = Rendered> + 'a + Send + Sync>> {
|
||||||
|
Box::pin(async {
|
||||||
|
return Rendered {
|
||||||
|
code: StatusCode::NOT_FOUND,
|
||||||
|
body: RenderedBody::String("page not found".into()),
|
||||||
|
ttl: Some(TimeDelta::days(1)),
|
||||||
|
immutable: true,
|
||||||
|
headers: HeaderMap::new(),
|
||||||
|
mime: Some(MimeType::Html),
|
||||||
|
};
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// MARK: server
|
||||||
|
//
|
||||||
|
|
||||||
pub struct PageServer {
|
pub struct PageServer {
|
||||||
/// If true, expired pages will be rerendered before being sent to the user.
|
/// If true, expired pages will be rerendered before being sent to the user.
|
||||||
/// If false, requests never trigger rerenders. We rely on the rerender task.
|
/// If false, requests never trigger rerenders. We rely on the rerender task.
|
||||||
@@ -61,7 +84,9 @@ pub struct PageServer {
|
|||||||
never_rerender_on_request: bool,
|
never_rerender_on_request: bool,
|
||||||
|
|
||||||
/// Map of `{ route: page }`
|
/// Map of `{ route: page }`
|
||||||
pages: Arc<Mutex<HashMap<String, Arc<dyn Servable>>>>,
|
pages: Mutex<HashMap<String, Arc<dyn Servable>>>,
|
||||||
|
|
||||||
|
notfound: Mutex<Arc<dyn Servable>>,
|
||||||
|
|
||||||
/// Map of `{ route: (page data, expire time) }`
|
/// Map of `{ route: (page data, expire time) }`
|
||||||
///
|
///
|
||||||
@@ -75,12 +100,19 @@ impl PageServer {
|
|||||||
let cache_size = NonZero::new(128).unwrap();
|
let cache_size = NonZero::new(128).unwrap();
|
||||||
|
|
||||||
Arc::new(Self {
|
Arc::new(Self {
|
||||||
pages: Arc::new(Mutex::new(HashMap::new())),
|
pages: Mutex::new(HashMap::new()),
|
||||||
page_cache: Mutex::new(LruCache::new(cache_size)),
|
page_cache: Mutex::new(LruCache::new(cache_size)),
|
||||||
never_rerender_on_request: true,
|
never_rerender_on_request: true,
|
||||||
|
notfound: Mutex::new(Arc::new(Default404 {})),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set this server's "not found" page
|
||||||
|
pub fn with_404<S: Servable + 'static>(&self, page: S) -> &Self {
|
||||||
|
*self.notfound.lock() = Arc::new(page);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub fn add_page<S: Servable + 'static>(&self, route: impl Into<String>, page: S) -> &Self {
|
pub fn add_page<S: Servable + 'static>(&self, route: impl Into<String>, page: S) -> &Self {
|
||||||
#[expect(clippy::expect_used)]
|
#[expect(clippy::expect_used)]
|
||||||
let route = route
|
let route = route
|
||||||
@@ -102,23 +134,23 @@ impl PageServer {
|
|||||||
reason: &'static str,
|
reason: &'static str,
|
||||||
route: &str,
|
route: &str,
|
||||||
ctx: RequestContext,
|
ctx: RequestContext,
|
||||||
) -> Option<(Rendered, Option<DateTime<Utc>>)> {
|
) -> (Rendered, Option<DateTime<Utc>>) {
|
||||||
let now = Utc::now();
|
let now = Utc::now();
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
|
|
||||||
let page = match self.pages.lock().get(route) {
|
let page = match self.pages.lock().get(route) {
|
||||||
Some(x) => x.clone(),
|
Some(x) => x.clone(),
|
||||||
None => return None,
|
None => self.notfound.lock().clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
trace!(
|
trace!(
|
||||||
message = "Rendering page",
|
message = "Rendering page",
|
||||||
route,
|
route = route.to_owned(),
|
||||||
reason,
|
reason,
|
||||||
lock_time_ms = start.elapsed().as_millis()
|
lock_time_ms = start.elapsed().as_millis()
|
||||||
);
|
);
|
||||||
|
|
||||||
let rendered = page.render(&ctx).await;
|
let rendered = page.render(&ctx).await;
|
||||||
//let html = (self.render_page)(&page, &req_ctx).await.0;
|
|
||||||
|
|
||||||
let mut expires = None;
|
let mut expires = None;
|
||||||
if let Some(ttl) = rendered.ttl {
|
if let Some(ttl) = rendered.ttl {
|
||||||
@@ -129,8 +161,13 @@ impl PageServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let elapsed = start.elapsed().as_millis();
|
let elapsed = start.elapsed().as_millis();
|
||||||
trace!(message = "Rendered page", route, reason, time_ms = elapsed);
|
trace!(
|
||||||
return Some((rendered, expires));
|
message = "Rendered page",
|
||||||
|
route = route.to_owned(),
|
||||||
|
reason,
|
||||||
|
time_ms = elapsed
|
||||||
|
);
|
||||||
|
return (rendered, expires);
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handler(
|
async fn handler(
|
||||||
@@ -206,30 +243,7 @@ impl PageServer {
|
|||||||
|
|
||||||
if html_expires.is_none() {
|
if html_expires.is_none() {
|
||||||
cached = false;
|
cached = false;
|
||||||
html_expires = match state.render_page("request", &route, ctx).await {
|
html_expires = Some(state.render_page("request", &route, ctx).await);
|
||||||
Some(x) => Some(x.clone()),
|
|
||||||
None => {
|
|
||||||
trace!(
|
|
||||||
message = "Not found",
|
|
||||||
route,
|
|
||||||
addr = ?addr.addr,
|
|
||||||
user_agent = ua,
|
|
||||||
device_type = ?client_info.device_type
|
|
||||||
);
|
|
||||||
|
|
||||||
trace!(
|
|
||||||
message = "Served route",
|
|
||||||
route,
|
|
||||||
addr = ?addr.addr,
|
|
||||||
user_agent = ua,
|
|
||||||
device_type = ?client_info.device_type,
|
|
||||||
cached,
|
|
||||||
time_ns = start.elapsed().as_nanos()
|
|
||||||
);
|
|
||||||
|
|
||||||
return StatusCode::NOT_FOUND.into_response();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[expect(clippy::unwrap_used)]
|
#[expect(clippy::unwrap_used)]
|
||||||
|
|||||||
@@ -81,9 +81,11 @@ body {
|
|||||||
color: var(--fgColor);
|
color: var(--fgColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
main {
|
div.wrapper {
|
||||||
margin-top: 2ex;
|
min-height: 100vh;
|
||||||
overflow-wrap: break-word;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
hr.footline {
|
hr.footline {
|
||||||
@@ -92,18 +94,14 @@ hr.footline {
|
|||||||
|
|
||||||
hr {
|
hr {
|
||||||
border: 1pt dashed;
|
border: 1pt dashed;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
iframe {
|
iframe {
|
||||||
max-width: 90%;
|
max-width: 90%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wrapper {
|
|
||||||
min-height: 100vh;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footContainer {
|
.footContainer {
|
||||||
padding-top: 0;
|
padding-top: 0;
|
||||||
|
|||||||
@@ -262,7 +262,7 @@ pub fn handouts() -> Page {
|
|||||||
br {}
|
br {}
|
||||||
};
|
};
|
||||||
|
|
||||||
page_wrapper(&page.meta, inner).await
|
page_wrapper(&page.meta, inner, true).await
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ pub fn index() -> Page {
|
|||||||
meta: PageMetadata {
|
meta: PageMetadata {
|
||||||
title: "Betalupi: About".into(),
|
title: "Betalupi: About".into(),
|
||||||
author: Some("Mark".into()),
|
author: Some("Mark".into()),
|
||||||
description: Some("Description".into()),
|
description: None,
|
||||||
image: Some("/assets/img/icon.png".to_owned()),
|
image: Some("/assets/img/icon.png".to_owned()),
|
||||||
backlinks: Some(false),
|
backlinks: Some(false),
|
||||||
},
|
},
|
||||||
@@ -74,7 +74,7 @@ pub fn index() -> Page {
|
|||||||
(Markdown(include_str!("index.md")))
|
(Markdown(include_str!("index.md")))
|
||||||
};
|
};
|
||||||
|
|
||||||
page_wrapper(&page.meta, inner).await
|
page_wrapper(&page.meta, inner, true).await
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
|||||||
@@ -10,9 +10,11 @@ use crate::components::{
|
|||||||
|
|
||||||
mod handouts;
|
mod handouts;
|
||||||
mod index;
|
mod index;
|
||||||
|
mod notfound;
|
||||||
|
|
||||||
pub use handouts::handouts;
|
pub use handouts::handouts;
|
||||||
pub use index::index;
|
pub use index::index;
|
||||||
|
pub use notfound::notfound;
|
||||||
|
|
||||||
pub fn links() -> Page {
|
pub fn links() -> Page {
|
||||||
/*
|
/*
|
||||||
@@ -67,7 +69,7 @@ fn page_from_markdown(md: impl Into<String>, default_image: Option<String>) -> P
|
|||||||
meta,
|
meta,
|
||||||
immutable: true,
|
immutable: true,
|
||||||
|
|
||||||
html_ttl: Some(TimeDelta::seconds(60 * 24 * 30)),
|
html_ttl: Some(TimeDelta::days(1)),
|
||||||
generate_html: Box::new(move |page, ctx| {
|
generate_html: Box::new(move |page, ctx| {
|
||||||
let html = html.clone();
|
let html = html.clone();
|
||||||
|
|
||||||
@@ -80,7 +82,7 @@ fn page_from_markdown(md: impl Into<String>, default_image: Option<String>) -> P
|
|||||||
(html)
|
(html)
|
||||||
};
|
};
|
||||||
|
|
||||||
page_wrapper(&page.meta, inner).await
|
page_wrapper(&page.meta, inner, true).await
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
@@ -93,6 +95,7 @@ fn page_from_markdown(md: impl Into<String>, default_image: Option<String>) -> P
|
|||||||
pub fn page_wrapper<'a>(
|
pub fn page_wrapper<'a>(
|
||||||
meta: &'a PageMetadata,
|
meta: &'a PageMetadata,
|
||||||
inner: Markup,
|
inner: Markup,
|
||||||
|
footer: bool,
|
||||||
) -> Pin<Box<dyn Future<Output = Markup> + 'a + Send + Sync>> {
|
) -> Pin<Box<dyn Future<Output = Markup> + 'a + Send + Sync>> {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
html! {
|
html! {
|
||||||
@@ -133,9 +136,17 @@ pub fn page_wrapper<'a>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
div class="wrapper" {
|
main{
|
||||||
main { (inner) }
|
div class="wrapper" style=(
|
||||||
|
// for 404 page. Margin makes it scroll.
|
||||||
|
match footer {
|
||||||
|
true => "margin-top:3ex;",
|
||||||
|
false =>""
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
(inner)
|
||||||
|
|
||||||
|
@if footer {
|
||||||
footer {
|
footer {
|
||||||
hr class = "footline" {}
|
hr class = "footline" {}
|
||||||
div class = "footContainer" {
|
div class = "footContainer" {
|
||||||
@@ -154,6 +165,7 @@ pub fn page_wrapper<'a>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
32
crates/service/service-webpage/src/pages/notfound.rs
Normal file
32
crates/service/service-webpage/src/pages/notfound.rs
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
use maud::html;
|
||||||
|
use page::page::{Page, PageMetadata};
|
||||||
|
|
||||||
|
use crate::pages::page_wrapper;
|
||||||
|
|
||||||
|
pub fn notfound() -> Page {
|
||||||
|
Page {
|
||||||
|
meta: PageMetadata {
|
||||||
|
title: "Betalupi: About".into(),
|
||||||
|
author: None,
|
||||||
|
description: None,
|
||||||
|
image: Some("/assets/img/icon.png".to_owned()),
|
||||||
|
backlinks: Some(false),
|
||||||
|
},
|
||||||
|
|
||||||
|
generate_html: Box::new(move |page, _ctx| {
|
||||||
|
Box::pin(async {
|
||||||
|
let inner = html! {
|
||||||
|
div style="display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:100vh" {
|
||||||
|
p style="font-weight:bold;font-size:50pt;margin:0;" { "404" }
|
||||||
|
p style="font-size:13pt;margin:0;color:var(--grey);" { "(page not found)" }
|
||||||
|
a style="font-size:12pt;margin:10pt;padding:5px;" href="/" {"<- Back to site"}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
page_wrapper(&page.meta, inner, false).await
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,6 +15,7 @@ fn build_server() -> Arc<PageServer> {
|
|||||||
|
|
||||||
#[expect(clippy::unwrap_used)]
|
#[expect(clippy::unwrap_used)]
|
||||||
server
|
server
|
||||||
|
.with_404(pages::notfound())
|
||||||
.add_page("/", pages::index())
|
.add_page("/", pages::index())
|
||||||
.add_page("/links", pages::links())
|
.add_page("/links", pages::links())
|
||||||
.add_page("/whats-a-betalupi", pages::betalupi())
|
.add_page("/whats-a-betalupi", pages::betalupi())
|
||||||
|
|||||||
Reference in New Issue
Block a user