From 4091d8b486e3d8734e6fa99a12e03fd9e822edac Mon Sep 17 00:00:00 2001 From: rm-dr <96270320+rm-dr@users.noreply.github.com> Date: Thu, 6 Nov 2025 08:56:58 -0800 Subject: [PATCH] Generate backlinks --- crates/lib/page/src/page.rs | 4 +- crates/lib/page/src/requestcontext.rs | 1 + crates/lib/page/src/server.rs | 49 ++++++++++++------- .../service-webpage/src/components/md.rs | 40 +++++++++++++-- .../service-webpage/src/components/misc.rs | 17 ------- .../service-webpage/src/pages/betalupi.md | 2 +- .../service-webpage/src/pages/handouts.md | 2 +- .../service-webpage/src/pages/handouts.rs | 14 +++--- .../src/pages/htwah-typesetting.md | 4 +- .../service-webpage/src/pages/index.rs | 2 +- .../service-webpage/src/pages/links.md | 2 +- .../service/service-webpage/src/routes/mod.rs | 2 +- 12 files changed, 83 insertions(+), 56 deletions(-) diff --git a/crates/lib/page/src/page.rs b/crates/lib/page/src/page.rs index 82aff76..df6c20c 100644 --- a/crates/lib/page/src/page.rs +++ b/crates/lib/page/src/page.rs @@ -15,7 +15,7 @@ pub struct PageMetadata { pub author: Option, pub description: Option, pub image: Option, - pub slug: Option, + pub backlinks: Option, } impl Default for PageMetadata { @@ -25,7 +25,7 @@ impl Default for PageMetadata { author: None, description: None, image: None, - slug: None, + backlinks: None, } } } diff --git a/crates/lib/page/src/requestcontext.rs b/crates/lib/page/src/requestcontext.rs index d1ee3d8..565a848 100644 --- a/crates/lib/page/src/requestcontext.rs +++ b/crates/lib/page/src/requestcontext.rs @@ -3,6 +3,7 @@ use axum::http::HeaderMap; #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct RequestContext { pub client_info: ClientInfo, + pub route: String, } // diff --git a/crates/lib/page/src/server.rs b/crates/lib/page/src/server.rs index 5e73dbb..425912f 100644 --- a/crates/lib/page/src/server.rs +++ b/crates/lib/page/src/server.rs @@ -93,16 +93,18 @@ impl PageServer { ) -> Option<(String, Option>)> { let now = Utc::now(); let start = Instant::now(); - trace!(message = "Rendering page", route, reason); - let page = match self.pages.lock().get(route) { Some(x) => x.clone(), - None => { - warn!(message = "Not rerendering, no such route", route, reason); - return None; - } + None => return None, }; + trace!( + message = "Rendering page", + route, + reason, + lock_time_ms = start.elapsed().as_millis() + ); + let html = (self.render_page)(&page, &req_ctx).await.0; let mut expires = None; @@ -130,6 +132,14 @@ impl PageServer { .and_then(|x| x.to_str().ok()) .unwrap_or(""); + trace!( + message = "Serving route", + route, + addr = ?addr.addr, + user_agent = ua, + device_type = ?client_info.device_type + ); + // Normalize url with redirect if route.ends_with('/') || route.contains("//") || route.starts_with('/') { let mut new_route = route.clone(); @@ -139,7 +149,7 @@ impl PageServer { let new_route = new_route.trim_matches('/'); trace!( - message = "Redirecting route", + message = "Redirecting", route, new_route, addr = ?addr.addr, @@ -162,15 +172,10 @@ impl PageServer { return (StatusCode::PERMANENT_REDIRECT, headers).into_response(); } - trace!( - message = "Serving route", - route, - addr = ?addr.addr, - user_agent = ua, - device_type = ?client_info.device_type - ); - - let req_ctx = RequestContext { client_info }; + let req_ctx = RequestContext { + client_info, + route: format!("/{route}"), + }; let cache_key = (route.clone(), req_ctx.clone()); let now = Utc::now(); @@ -186,7 +191,17 @@ impl PageServer { if html_expires.is_none() { html_expires = match state.render_page("request", &route, req_ctx).await { Some(x) => Some(x.clone()), - None => return (StatusCode::NOT_FOUND, "page doesn't exist").into_response(), + None => { + trace!( + message = "Not found", + route, + addr = ?addr.addr, + user_agent = ua, + device_type = ?client_info.device_type + ); + + return (StatusCode::NOT_FOUND, "page doesn't exist").into_response(); + } }; } diff --git a/crates/service/service-webpage/src/components/md.rs b/crates/service/service-webpage/src/components/md.rs index f57ef5f..3adb068 100644 --- a/crates/service/service-webpage/src/components/md.rs +++ b/crates/service/service-webpage/src/components/md.rs @@ -5,12 +5,11 @@ use markdown_it::parser::core::Root; use markdown_it::parser::inline::{InlineRule, InlineState}; use markdown_it::{MarkdownIt, Node, NodeValue, Renderer}; use maud::{Markup, PreEscaped, Render, html}; -use page::{Page, PageMetadata}; +use page::{Page, PageMetadata, RequestContext}; use std::str::FromStr; use crate::components::fa::FAIcon; use crate::components::mangle::{MangledBetaEmail, MangledGoogleEmail}; -use crate::components::misc::Backlinks; lazy_static! { static ref MdParser: MarkdownIt = { @@ -115,12 +114,13 @@ pub fn page_from_markdown(md: impl Into, default_image: Option) Page { meta, - generate_html: Box::new(move |page, _| { + generate_html: Box::new(move |page, ctx| { let html = html.clone(); + Box::pin(async move { html! { - @if let Some(slug) = &page.meta.slug { - (Backlinks(&[("/", "home")], slug)) + @if let Some(backlinks) = backlinks(page, ctx) { + (backlinks) } (html) @@ -132,6 +132,36 @@ pub fn page_from_markdown(md: impl Into, default_image: Option) } } +pub fn backlinks(page: &Page, ctx: &RequestContext) -> Option { + let mut last = None; + let mut backlinks = vec![("/", "home")]; + + if page.meta.backlinks.unwrap_or(false) { + let mut segments = ctx.route.split("/").skip(1).collect::>(); + last = segments.pop(); + + let mut end = 0; + for s in segments { + end += s.len(); + backlinks.push((&ctx.route[0..=end], s)); + end += 1; // trailing slash + } + } + + last.map(|last| { + html! { + div { + @for (url, text) in backlinks { + a href=(url) style="padding-left:5pt;padding-right:5pt;" { (text) } + "/" + } + + span style="color:var(--metaColor);padding-left:5pt;padding-right:5pt;" { (last) } + } + } + }) +} + // // MARK: extensions // diff --git a/crates/service/service-webpage/src/components/misc.rs b/crates/service/service-webpage/src/components/misc.rs index f6c0c8d..53b73c0 100644 --- a/crates/service/service-webpage/src/components/misc.rs +++ b/crates/service/service-webpage/src/components/misc.rs @@ -15,20 +15,3 @@ impl Render for FarLink<'_, T> { ) } } - -pub struct Backlinks<'a>(pub &'a [(&'a str, &'a str)], pub &'a str); - -impl Render for Backlinks<'_> { - fn render(&self) -> Markup { - html! { - div { - @for (url, text) in self.0 { - a href=(url) style="padding-left:5pt;padding-right:5pt;" { (text) } - "/" - } - - span style="color:var(--metaColor);padding-left:5pt;padding-right:5pt;" { (self.1) } - } - } - } -} diff --git a/crates/service/service-webpage/src/pages/betalupi.md b/crates/service/service-webpage/src/pages/betalupi.md index c93abaf..88e6ef3 100644 --- a/crates/service/service-webpage/src/pages/betalupi.md +++ b/crates/service/service-webpage/src/pages/betalupi.md @@ -1,7 +1,7 @@ +++ title = "What's a \"betalupi?\"" author = "Mark" -slug = "whats-a-betalupi" +backlinks = true +++ [es]: https://github.com/endless-sky/endless-sky diff --git a/crates/service/service-webpage/src/pages/handouts.md b/crates/service/service-webpage/src/pages/handouts.md index b44b10d..ceda1c3 100644 --- a/crates/service/service-webpage/src/pages/handouts.md +++ b/crates/service/service-webpage/src/pages/handouts.md @@ -1,7 +1,7 @@ +++ title = "Mark's Handouts" author = "Mark" -slug = "handouts" +backlinks = true +++ # Mark's Handouts diff --git a/crates/service/service-webpage/src/pages/handouts.rs b/crates/service/service-webpage/src/pages/handouts.rs index 90e289b..cf27b36 100644 --- a/crates/service/service-webpage/src/pages/handouts.rs +++ b/crates/service/service-webpage/src/pages/handouts.rs @@ -14,8 +14,8 @@ use tracing::{debug, warn}; use crate::{ components::{ - md::{Markdown, meta_from_markdown}, - misc::{Backlinks, FarLink}, + md::{Markdown, backlinks, meta_from_markdown}, + misc::FarLink, }, routes::assets::Image_Icon, }; @@ -202,7 +202,7 @@ pub fn handouts() -> Page { meta, html_ttl: Some(TimeDelta::seconds(300)), - generate_html: Box::new(move |page, req_ctx| { + generate_html: Box::new(move |page, ctx| { let html = html.clone(); // TODO: find a way to not clone here let index = index.clone(); Box::pin(async move { @@ -222,7 +222,7 @@ pub fn handouts() -> Page { }; let warmups = match &*handouts { - Ok(handouts) => build_list_for_group(handouts, "Warm-Ups", req_ctx), + Ok(handouts) => build_list_for_group(handouts, "Warm-Ups", ctx), Err(error) => { warn!("Could not load handout index: {error:?}"); fallback.clone() @@ -230,13 +230,13 @@ pub fn handouts() -> Page { }; let advanced = match &*handouts { - Ok(handouts) => build_list_for_group(handouts, "Advanced", req_ctx), + Ok(handouts) => build_list_for_group(handouts, "Advanced", ctx), Err(_) => fallback, }; html! { - @if let Some(slug) = &page.meta.slug { - (Backlinks(&[("/", "home")], slug)) + @if let Some(backlinks) = backlinks(page, ctx) { + (backlinks) } (html) diff --git a/crates/service/service-webpage/src/pages/htwah-typesetting.md b/crates/service/service-webpage/src/pages/htwah-typesetting.md index 4cd94ee..eb75c5c 100644 --- a/crates/service/service-webpage/src/pages/htwah-typesetting.md +++ b/crates/service/service-webpage/src/pages/htwah-typesetting.md @@ -1,9 +1,7 @@ +++ title = "HtWaH: Typesetting" author = "Mark" - -# TODO: many slugs, htwah/typesetting -slug = "handouts" +backlinks = true +++ ## Table of Contents diff --git a/crates/service/service-webpage/src/pages/index.rs b/crates/service/service-webpage/src/pages/index.rs index 1c60a3e..1ebe8d0 100644 --- a/crates/service/service-webpage/src/pages/index.rs +++ b/crates/service/service-webpage/src/pages/index.rs @@ -19,7 +19,7 @@ pub fn index() -> Page { author: Some("Mark".into()), description: Some("Description".into()), image: Some(Image_Icon::URL.into()), - slug: None, + backlinks: Some(false), }, generate_html: Box::new(move |_page, _| { diff --git a/crates/service/service-webpage/src/pages/links.md b/crates/service/service-webpage/src/pages/links.md index 96d2233..3f62bb3 100644 --- a/crates/service/service-webpage/src/pages/links.md +++ b/crates/service/service-webpage/src/pages/links.md @@ -1,7 +1,7 @@ +++ title = "Links" author = "Mark" -slug = "links" +backlinks = true +++ diff --git a/crates/service/service-webpage/src/routes/mod.rs b/crates/service/service-webpage/src/routes/mod.rs index 12c235c..9eb7e47 100644 --- a/crates/service/service-webpage/src/routes/mod.rs +++ b/crates/service/service-webpage/src/routes/mod.rs @@ -26,7 +26,7 @@ fn build_server() -> Arc { .add_page("/links", pages::links()) .add_page("/whats-a-betalupi", pages::betalupi()) .add_page("/handouts", pages::handouts()) - .add_page("/htwah", pages::htwah_typesetting()); + .add_page("/htwah/typesetting", pages::htwah_typesetting()); server }