Compare commits
2 Commits
2603c835c6
...
b0f0038b24
| Author | SHA1 | Date | |
|---|---|---|---|
| b0f0038b24 | |||
| 5554aafc44 |
@@ -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,
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -130,6 +130,38 @@ impl PageServer {
|
|||||||
.and_then(|x| x.to_str().ok())
|
.and_then(|x| x.to_str().ok())
|
||||||
.unwrap_or("");
|
.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!(
|
trace!(
|
||||||
message = "Serving route",
|
message = "Serving route",
|
||||||
route,
|
route,
|
||||||
@@ -138,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