Generate backlinks
Some checks failed
CI / Check typos (push) Successful in 8s
CI / Clippy (push) Successful in 1m8s
CI / Build and test (push) Successful in 1m5s
CI / Check links (push) Failing after 1m16s
CI / Build container (push) Successful in 46s
CI / Deploy on waypoint (push) Successful in 45s

This commit is contained in:
2025-11-06 08:28:23 -08:00
parent 5554aafc44
commit b0f0038b24
12 changed files with 55 additions and 40 deletions

View File

@@ -15,7 +15,7 @@ pub struct PageMetadata {
pub author: Option<String>,
pub description: Option<String>,
pub image: Option<String>,
pub slug: Option<String>,
pub backlinks: Option<bool>,
}
impl Default for PageMetadata {
@@ -25,7 +25,7 @@ impl Default for PageMetadata {
author: None,
description: None,
image: None,
slug: None,
backlinks: None,
}
}
}

View File

@@ -3,6 +3,7 @@ use axum::http::HeaderMap;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct RequestContext {
pub client_info: ClientInfo,
pub route: String,
}
//

View File

@@ -170,7 +170,10 @@ impl PageServer {
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();

View File

@@ -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<String>, default_image: Option<String>)
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<String>, default_image: Option<String>)
}
}
pub fn backlinks(page: &Page, ctx: &RequestContext) -> Option<Markup> {
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::<Vec<_>>();
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
//

View File

@@ -15,20 +15,3 @@ impl<T: Render> 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) }
}
}
}
}

View File

@@ -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

View File

@@ -1,7 +1,7 @@
+++
title = "Mark's Handouts"
author = "Mark"
slug = "handouts"
backlinks = true
+++
# Mark's Handouts

View File

@@ -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)

View File

@@ -1,9 +1,7 @@
+++
title = "HtWaH: Typesetting"
author = "Mark"
# TODO: many slugs, htwah/typesetting
slug = "handouts"
backlinks = true
+++
## Table of Contents

View File

@@ -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, _| {

View File

@@ -1,7 +1,7 @@
+++
title = "Links"
author = "Mark"
slug = "links"
backlinks = true
+++

View File

@@ -26,7 +26,7 @@ fn build_server() -> Arc<PageServer> {
.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
}