Compare commits
2 Commits
2603c835c6
...
b0f0038b24
| Author | SHA1 | Date | |
|---|---|---|---|
| b0f0038b24 | |||
| 5554aafc44 |
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ use axum::http::HeaderMap;
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct RequestContext {
|
||||
pub client_info: ClientInfo,
|
||||
pub route: String,
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
@@ -130,6 +130,38 @@ impl PageServer {
|
||||
.and_then(|x| x.to_str().ok())
|
||||
.unwrap_or("");
|
||||
|
||||
// Normalize url with redirect
|
||||
if route.ends_with('/') || route.contains("//") || route.starts_with('/') {
|
||||
let mut new_route = route.clone();
|
||||
while new_route.contains("//") {
|
||||
new_route = new_route.replace("//", "/");
|
||||
}
|
||||
let new_route = new_route.trim_matches('/');
|
||||
|
||||
trace!(
|
||||
message = "Redirecting route",
|
||||
route,
|
||||
new_route,
|
||||
addr = ?addr.addr,
|
||||
user_agent = ua,
|
||||
device_type = ?client_info.device_type
|
||||
);
|
||||
|
||||
let mut headers = HeaderMap::with_capacity(2);
|
||||
|
||||
let new_route = match HeaderValue::from_str(&format!("/{new_route}")) {
|
||||
Ok(x) => x,
|
||||
Err(_) => {
|
||||
// Be extra careful, this is user-provided data
|
||||
return StatusCode::BAD_REQUEST.into_response();
|
||||
}
|
||||
};
|
||||
|
||||
headers.append(header::LOCATION, new_route);
|
||||
headers.append("Accept-CH", HeaderValue::from_static("Sec-CH-UA-Mobile"));
|
||||
return (StatusCode::PERMANENT_REDIRECT, headers).into_response();
|
||||
}
|
||||
|
||||
trace!(
|
||||
message = "Serving route",
|
||||
route,
|
||||
@@ -138,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();
|
||||
|
||||
@@ -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
|
||||
//
|
||||
|
||||
@@ -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?\""
|
||||
author = "Mark"
|
||||
slug = "whats-a-betalupi"
|
||||
backlinks = true
|
||||
+++
|
||||
|
||||
[es]: https://github.com/endless-sky/endless-sky
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
+++
|
||||
title = "Mark's Handouts"
|
||||
author = "Mark"
|
||||
slug = "handouts"
|
||||
backlinks = true
|
||||
+++
|
||||
|
||||
# Mark's Handouts
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
+++
|
||||
title = "HtWaH: Typesetting"
|
||||
author = "Mark"
|
||||
|
||||
# TODO: many slugs, htwah/typesetting
|
||||
slug = "handouts"
|
||||
backlinks = true
|
||||
+++
|
||||
|
||||
## Table of Contents
|
||||
|
||||
@@ -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, _| {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
+++
|
||||
title = "Links"
|
||||
author = "Mark"
|
||||
slug = "links"
|
||||
backlinks = true
|
||||
+++
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user