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
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:
@@ -15,7 +15,7 @@ pub struct PageMetadata {
|
|||||||
pub author: Option<String>,
|
pub author: Option<String>,
|
||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
||||||
pub image: Option<String>,
|
pub image: Option<String>,
|
||||||
pub slug: Option<String>,
|
pub backlinks: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for PageMetadata {
|
impl Default for PageMetadata {
|
||||||
@@ -25,7 +25,7 @@ impl Default for PageMetadata {
|
|||||||
author: None,
|
author: None,
|
||||||
description: None,
|
description: None,
|
||||||
image: None,
|
image: None,
|
||||||
slug: None,
|
backlinks: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ use axum::http::HeaderMap;
|
|||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
pub struct RequestContext {
|
pub struct RequestContext {
|
||||||
pub client_info: ClientInfo,
|
pub client_info: ClientInfo,
|
||||||
|
pub route: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -170,7 +170,10 @@ impl PageServer {
|
|||||||
device_type = ?client_info.device_type
|
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 cache_key = (route.clone(), req_ctx.clone());
|
||||||
let now = Utc::now();
|
let now = Utc::now();
|
||||||
|
|||||||
@@ -5,12 +5,11 @@ use markdown_it::parser::core::Root;
|
|||||||
use markdown_it::parser::inline::{InlineRule, InlineState};
|
use markdown_it::parser::inline::{InlineRule, InlineState};
|
||||||
use markdown_it::{MarkdownIt, Node, NodeValue, Renderer};
|
use markdown_it::{MarkdownIt, Node, NodeValue, Renderer};
|
||||||
use maud::{Markup, PreEscaped, Render, html};
|
use maud::{Markup, PreEscaped, Render, html};
|
||||||
use page::{Page, PageMetadata};
|
use page::{Page, PageMetadata, RequestContext};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use crate::components::fa::FAIcon;
|
use crate::components::fa::FAIcon;
|
||||||
use crate::components::mangle::{MangledBetaEmail, MangledGoogleEmail};
|
use crate::components::mangle::{MangledBetaEmail, MangledGoogleEmail};
|
||||||
use crate::components::misc::Backlinks;
|
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref MdParser: MarkdownIt = {
|
static ref MdParser: MarkdownIt = {
|
||||||
@@ -115,12 +114,13 @@ pub fn page_from_markdown(md: impl Into<String>, default_image: Option<String>)
|
|||||||
|
|
||||||
Page {
|
Page {
|
||||||
meta,
|
meta,
|
||||||
generate_html: Box::new(move |page, _| {
|
generate_html: Box::new(move |page, ctx| {
|
||||||
let html = html.clone();
|
let html = html.clone();
|
||||||
|
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
html! {
|
html! {
|
||||||
@if let Some(slug) = &page.meta.slug {
|
@if let Some(backlinks) = backlinks(page, ctx) {
|
||||||
(Backlinks(&[("/", "home")], slug))
|
(backlinks)
|
||||||
}
|
}
|
||||||
|
|
||||||
(html)
|
(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
|
// MARK: extensions
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -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) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
+++
|
+++
|
||||||
title = "What's a \"betalupi?\""
|
title = "What's a \"betalupi?\""
|
||||||
author = "Mark"
|
author = "Mark"
|
||||||
slug = "whats-a-betalupi"
|
backlinks = true
|
||||||
+++
|
+++
|
||||||
|
|
||||||
[es]: https://github.com/endless-sky/endless-sky
|
[es]: https://github.com/endless-sky/endless-sky
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
+++
|
+++
|
||||||
title = "Mark's Handouts"
|
title = "Mark's Handouts"
|
||||||
author = "Mark"
|
author = "Mark"
|
||||||
slug = "handouts"
|
backlinks = true
|
||||||
+++
|
+++
|
||||||
|
|
||||||
# Mark's Handouts
|
# Mark's Handouts
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ use tracing::{debug, warn};
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
components::{
|
components::{
|
||||||
md::{Markdown, meta_from_markdown},
|
md::{Markdown, backlinks, meta_from_markdown},
|
||||||
misc::{Backlinks, FarLink},
|
misc::FarLink,
|
||||||
},
|
},
|
||||||
routes::assets::Image_Icon,
|
routes::assets::Image_Icon,
|
||||||
};
|
};
|
||||||
@@ -202,7 +202,7 @@ pub fn handouts() -> Page {
|
|||||||
meta,
|
meta,
|
||||||
html_ttl: Some(TimeDelta::seconds(300)),
|
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 html = html.clone(); // TODO: find a way to not clone here
|
||||||
let index = index.clone();
|
let index = index.clone();
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
@@ -222,7 +222,7 @@ pub fn handouts() -> Page {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let warmups = match &*handouts {
|
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) => {
|
Err(error) => {
|
||||||
warn!("Could not load handout index: {error:?}");
|
warn!("Could not load handout index: {error:?}");
|
||||||
fallback.clone()
|
fallback.clone()
|
||||||
@@ -230,13 +230,13 @@ pub fn handouts() -> Page {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let advanced = match &*handouts {
|
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,
|
Err(_) => fallback,
|
||||||
};
|
};
|
||||||
|
|
||||||
html! {
|
html! {
|
||||||
@if let Some(slug) = &page.meta.slug {
|
@if let Some(backlinks) = backlinks(page, ctx) {
|
||||||
(Backlinks(&[("/", "home")], slug))
|
(backlinks)
|
||||||
}
|
}
|
||||||
|
|
||||||
(html)
|
(html)
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
+++
|
+++
|
||||||
title = "HtWaH: Typesetting"
|
title = "HtWaH: Typesetting"
|
||||||
author = "Mark"
|
author = "Mark"
|
||||||
|
backlinks = true
|
||||||
# TODO: many slugs, htwah/typesetting
|
|
||||||
slug = "handouts"
|
|
||||||
+++
|
+++
|
||||||
|
|
||||||
## Table of Contents
|
## Table of Contents
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ pub fn index() -> Page {
|
|||||||
author: Some("Mark".into()),
|
author: Some("Mark".into()),
|
||||||
description: Some("Description".into()),
|
description: Some("Description".into()),
|
||||||
image: Some(Image_Icon::URL.into()),
|
image: Some(Image_Icon::URL.into()),
|
||||||
slug: None,
|
backlinks: Some(false),
|
||||||
},
|
},
|
||||||
|
|
||||||
generate_html: Box::new(move |_page, _| {
|
generate_html: Box::new(move |_page, _| {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
+++
|
+++
|
||||||
title = "Links"
|
title = "Links"
|
||||||
author = "Mark"
|
author = "Mark"
|
||||||
slug = "links"
|
backlinks = true
|
||||||
+++
|
+++
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ fn build_server() -> Arc<PageServer> {
|
|||||||
.add_page("/links", pages::links())
|
.add_page("/links", pages::links())
|
||||||
.add_page("/whats-a-betalupi", pages::betalupi())
|
.add_page("/whats-a-betalupi", pages::betalupi())
|
||||||
.add_page("/handouts", pages::handouts())
|
.add_page("/handouts", pages::handouts())
|
||||||
.add_page("/htwah", pages::htwah_typesetting());
|
.add_page("/htwah/typesetting", pages::htwah_typesetting());
|
||||||
server
|
server
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user