Even more stuff
This commit is contained in:
parent
3189b57a46
commit
d076f92764
6 changed files with 205 additions and 73 deletions
119
Cargo.lock
generated
119
Cargo.lock
generated
|
@ -393,9 +393,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cxx"
|
name = "cxx"
|
||||||
version = "1.0.93"
|
version = "1.0.94"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a9c00419335c41018365ddf7e4d5f1c12ee3659ddcf3e01974650ba1de73d038"
|
checksum = "f61f1b6389c3fe1c316bf8a4dccc90a38208354b330925bce1f74a6c4756eb93"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
"cxxbridge-flags",
|
"cxxbridge-flags",
|
||||||
|
@ -405,9 +405,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cxx-build"
|
name = "cxx-build"
|
||||||
version = "1.0.93"
|
version = "1.0.94"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fb8307ad413a98fff033c8545ecf133e3257747b3bae935e7602aab8aa92d4ca"
|
checksum = "12cee708e8962df2aeb38f594aae5d827c022b6460ac71a7a3e2c3c2aae5a07b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
"codespan-reporting",
|
"codespan-reporting",
|
||||||
|
@ -420,15 +420,15 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cxxbridge-flags"
|
name = "cxxbridge-flags"
|
||||||
version = "1.0.93"
|
version = "1.0.94"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "edc52e2eb08915cb12596d29d55f0b5384f00d697a646dbd269b6ecb0fbd9d31"
|
checksum = "7944172ae7e4068c533afbb984114a56c46e9ccddda550499caa222902c7f7bb"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cxxbridge-macro"
|
name = "cxxbridge-macro"
|
||||||
version = "1.0.93"
|
version = "1.0.94"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "631569015d0d8d54e6c241733f944042623ab6df7bc3be7466874b05fcdb1c5f"
|
checksum = "2345488264226bf682893e25de0769f3360aac9957980ec49361b083ddaa5bc5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -486,6 +486,27 @@ dependencies = [
|
||||||
"crypto-common",
|
"crypto-common",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "errno"
|
||||||
|
version = "0.2.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1"
|
||||||
|
dependencies = [
|
||||||
|
"errno-dragonfly",
|
||||||
|
"libc",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "errno-dragonfly"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "eyre"
|
name = "eyre"
|
||||||
version = "0.6.8"
|
version = "0.6.8"
|
||||||
|
@ -699,6 +720,18 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hermit-abi"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hex"
|
||||||
|
version = "0.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "http"
|
name = "http"
|
||||||
version = "0.2.9"
|
version = "0.2.9"
|
||||||
|
@ -844,6 +877,17 @@ dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "io-lifetimes"
|
||||||
|
version = "1.0.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "09270fd4fa1111bc614ed2246c7ef56239a3063d5be0d1ec3b589c505d400aeb"
|
||||||
|
dependencies = [
|
||||||
|
"hermit-abi 0.3.1",
|
||||||
|
"libc",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "iri-string"
|
name = "iri-string"
|
||||||
version = "0.7.0"
|
version = "0.7.0"
|
||||||
|
@ -905,6 +949,12 @@ dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "linux-raw-sys"
|
||||||
|
version = "0.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lock_api"
|
name = "lock_api"
|
||||||
version = "0.4.9"
|
version = "0.4.9"
|
||||||
|
@ -1008,7 +1058,7 @@ version = "1.15.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
|
checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hermit-abi",
|
"hermit-abi 0.2.6",
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -1213,6 +1263,42 @@ dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "procfs"
|
||||||
|
version = "0.14.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b1de8dacb0873f77e6aefc6d71e044761fcc68060290f5b1089fcdf84626bb69"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"byteorder",
|
||||||
|
"hex",
|
||||||
|
"lazy_static",
|
||||||
|
"rustix",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "prometheus"
|
||||||
|
version = "0.13.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "449811d15fbdf5ceb5c1144416066429cf82316e2ec8ce0c1f6f8a02e7bbcf8c"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"fnv",
|
||||||
|
"lazy_static",
|
||||||
|
"libc",
|
||||||
|
"memchr",
|
||||||
|
"parking_lot",
|
||||||
|
"procfs",
|
||||||
|
"protobuf",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "protobuf"
|
||||||
|
version = "2.28.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pulldown-cmark"
|
name = "pulldown-cmark"
|
||||||
version = "0.9.2"
|
version = "0.9.2"
|
||||||
|
@ -1296,6 +1382,20 @@ version = "0.1.22"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d4a36c42d1873f9a77c53bde094f9664d9891bc604a45b4798fd2c389ed12e5b"
|
checksum = "d4a36c42d1873f9a77c53bde094f9664d9891bc604a45b4798fd2c389ed12e5b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustix"
|
||||||
|
version = "0.36.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "db4165c9963ab29e422d6c26fbc1d37f15bace6b2810221f9d925023480fcf0e"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"errno",
|
||||||
|
"io-lifetimes",
|
||||||
|
"libc",
|
||||||
|
"linux-raw-sys",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustversion"
|
name = "rustversion"
|
||||||
version = "1.0.12"
|
version = "1.0.12"
|
||||||
|
@ -1993,6 +2093,7 @@ dependencies = [
|
||||||
"glob",
|
"glob",
|
||||||
"hyper",
|
"hyper",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
|
"prometheus",
|
||||||
"pulldown-cmark",
|
"pulldown-cmark",
|
||||||
"regex",
|
"regex",
|
||||||
"serde",
|
"serde",
|
||||||
|
|
|
@ -15,6 +15,7 @@ glob = "0.3.0"
|
||||||
#grass = { version = "0.12.3", features = ["random"] } # not really needed yet
|
#grass = { version = "0.12.3", features = ["random"] } # not really needed yet
|
||||||
hyper = { version = "0.14.19", features = ["full"] }
|
hyper = { version = "0.14.19", features = ["full"] }
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
|
prometheus = { version = "0.13.3", features = ["process"] }
|
||||||
pulldown-cmark = "0.9.2"
|
pulldown-cmark = "0.9.2"
|
||||||
regex = "1.7.2"
|
regex = "1.7.2"
|
||||||
serde = "1.0.144"
|
serde = "1.0.144"
|
||||||
|
|
|
@ -1,21 +1,32 @@
|
||||||
use hyper::StatusCode;
|
|
||||||
use std::sync::Arc;
|
|
||||||
use tracing::{instrument, log::*};
|
|
||||||
|
|
||||||
use axum::{
|
use axum::{
|
||||||
response::{Html, IntoResponse, Response},
|
response::{Html, IntoResponse, Response},
|
||||||
Extension,
|
Extension,
|
||||||
};
|
};
|
||||||
|
use hyper::StatusCode;
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
use prometheus::{opts, IntCounterVec};
|
||||||
|
use std::sync::Arc;
|
||||||
|
use tracing::{instrument, log::*};
|
||||||
|
|
||||||
use crate::{State, WebsiteError};
|
use crate::{State, WebsiteError};
|
||||||
|
|
||||||
|
pub mod posts;
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
pub static ref HIT_COUNTER: IntCounterVec =
|
||||||
|
prometheus::register_int_counter_vec!(opts!("hits", "page hits"), &["page"]).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
#[instrument(skip(state))]
|
#[instrument(skip(state))]
|
||||||
pub async fn index(Extension(state): Extension<Arc<State>>) -> std::result::Result<Html<Vec<u8>>, WebsiteError> {
|
pub async fn index(
|
||||||
|
Extension(state): Extension<Arc<State>>,
|
||||||
|
) -> std::result::Result<Html<Vec<u8>>, WebsiteError> {
|
||||||
let ctx = tera::Context::new();
|
let ctx = tera::Context::new();
|
||||||
let res = state.tera.render("index.html", &ctx).map_err(|e| {
|
let res = state.tera.render("index.html", &ctx).map_err(|e| {
|
||||||
error!("Failed rendering index: {}", e);
|
error!("Failed rendering index: {}", e);
|
||||||
WebsiteError::NotFound
|
WebsiteError::NotFound
|
||||||
})?;
|
})?;
|
||||||
|
HIT_COUNTER.with_label_values(&["/"]);
|
||||||
Ok(Html(res.into()))
|
Ok(Html(res.into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
66
src/handlers/posts.rs
Normal file
66
src/handlers/posts.rs
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use axum::{extract::Path, response::Html, routing::get, Extension, Router};
|
||||||
|
use serde_derive::Serialize;
|
||||||
|
use tracing::{instrument, log::*};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
post::{render_post, Post},
|
||||||
|
State, WebsiteError,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::HIT_COUNTER;
|
||||||
|
|
||||||
|
pub fn router() -> Router {
|
||||||
|
Router::new()
|
||||||
|
.route("/", get(index))
|
||||||
|
.route("/:slug/", get(view))
|
||||||
|
.fallback_service(tower_http::services::ServeDir::new("./posts"))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[instrument(skip(state))]
|
||||||
|
pub async fn view(
|
||||||
|
Path(slug): Path<String>,
|
||||||
|
Extension(state): Extension<Arc<State>>,
|
||||||
|
) -> Result<Html<String>, WebsiteError> {
|
||||||
|
debug!("viewing post: {slug}");
|
||||||
|
let post = state.posts.get(&slug).ok_or(WebsiteError::NotFound)?;
|
||||||
|
|
||||||
|
let res = render_post(&state.tera, post).await?;
|
||||||
|
|
||||||
|
HIT_COUNTER.with_label_values(&[&format!("/posts/{}/", slug)]);
|
||||||
|
Ok(Html(res))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct IndexContext<'a> {
|
||||||
|
title: &'a str,
|
||||||
|
posts: Vec<&'a Post>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[instrument(skip(state))]
|
||||||
|
pub async fn index(Extension(state): Extension<Arc<State>>) -> Result<Html<String>, WebsiteError> {
|
||||||
|
let mut posts = state.posts.values().collect::<Vec<&Post>>();
|
||||||
|
|
||||||
|
posts.sort_by_key(|p| &p.date);
|
||||||
|
posts.reverse();
|
||||||
|
|
||||||
|
let ctx = IndexContext {
|
||||||
|
title: "Posts",
|
||||||
|
posts,
|
||||||
|
};
|
||||||
|
|
||||||
|
let res = match state
|
||||||
|
.tera
|
||||||
|
.render("posts_index.html", &tera::Context::from_serialize(ctx)?)
|
||||||
|
{
|
||||||
|
Ok(r) => r,
|
||||||
|
Err(e) => {
|
||||||
|
error!("failed to render posts index: {}", e);
|
||||||
|
return Err(e.into());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
HIT_COUNTER.with_label_values(&["/posts/"]);
|
||||||
|
Ok(Html(res))
|
||||||
|
}
|
|
@ -46,10 +46,7 @@ pub async fn init_app() -> Result<Router> {
|
||||||
|
|
||||||
let app = Router::new()
|
let app = Router::new()
|
||||||
.route("/", get(handlers::index))
|
.route("/", get(handlers::index))
|
||||||
.nest(
|
.nest("/posts", handlers::posts::router())
|
||||||
"/posts",
|
|
||||||
post::router(),
|
|
||||||
)
|
|
||||||
.nest_service("/static", tower_http::services::ServeDir::new("./static"))
|
.nest_service("/static", tower_http::services::ServeDir::new("./static"))
|
||||||
.route("/.healthcheck", get(healthcheck))
|
.route("/.healthcheck", get(healthcheck))
|
||||||
.layer(middleware);
|
.layer(middleware);
|
||||||
|
|
66
src/post.rs
66
src/post.rs
|
@ -1,6 +1,5 @@
|
||||||
use std::{collections::HashMap, path::Path, sync::Arc};
|
use std::{collections::HashMap, path::Path};
|
||||||
|
|
||||||
use axum::{response::Html, routing::get, Extension, Router, extract};
|
|
||||||
use chrono::{DateTime, FixedOffset};
|
use chrono::{DateTime, FixedOffset};
|
||||||
use glob::glob;
|
use glob::glob;
|
||||||
|
|
||||||
|
@ -12,7 +11,7 @@ use tokio::fs;
|
||||||
|
|
||||||
use tracing::{instrument, log::*};
|
use tracing::{instrument, log::*};
|
||||||
|
|
||||||
use crate::{State, WebsiteError};
|
use crate::WebsiteError;
|
||||||
|
|
||||||
#[derive(Deserialize, Debug, Default)]
|
#[derive(Deserialize, Debug, Default)]
|
||||||
pub struct TomlFrontMatter {
|
pub struct TomlFrontMatter {
|
||||||
|
@ -41,7 +40,9 @@ impl Post {
|
||||||
slug,
|
slug,
|
||||||
content,
|
content,
|
||||||
title: fm.title,
|
title: fm.title,
|
||||||
date: fm.date.map(|d| DateTime::parse_from_rfc3339(&d.to_string()).expect("bad toml datetime")),
|
date: fm
|
||||||
|
.date
|
||||||
|
.map(|d| DateTime::parse_from_rfc3339(&d.to_string()).expect("bad toml datetime")),
|
||||||
aliases: fm.aliases.unwrap_or_default(),
|
aliases: fm.aliases.unwrap_or_default(),
|
||||||
tags: fm.tags.unwrap_or_default(),
|
tags: fm.tags.unwrap_or_default(),
|
||||||
}
|
}
|
||||||
|
@ -96,7 +97,11 @@ pub async fn load_post(slug: &str) -> color_eyre::eyre::Result<Post> {
|
||||||
content_html
|
content_html
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(Post::new(slug.to_string(), content.unwrap_or_default(), tomlfm))
|
Ok(Post::new(
|
||||||
|
slug.to_string(),
|
||||||
|
content.unwrap_or_default(),
|
||||||
|
tomlfm,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument]
|
#[instrument]
|
||||||
|
@ -121,62 +126,13 @@ fn parse_frontmatter(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(skip(tera, post))]
|
#[instrument(skip(tera, post))]
|
||||||
async fn render_post(tera: &tera::Tera, post: &Post) -> Result<String, WebsiteError> {
|
pub async fn render_post(tera: &tera::Tera, post: &Post) -> Result<String, WebsiteError> {
|
||||||
let mut ctx = tera::Context::new();
|
let mut ctx = tera::Context::new();
|
||||||
ctx.insert("page", &post);
|
ctx.insert("page", &post);
|
||||||
|
|
||||||
tera.render("post.html", &ctx).map_err(|e| e.into())
|
tera.render("post.html", &ctx).map_err(|e| e.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn router() -> Router
|
|
||||||
{
|
|
||||||
Router::new()
|
|
||||||
.route("/", get(index))
|
|
||||||
.route("/:slug/", get(view))
|
|
||||||
.fallback_service(tower_http::services::ServeDir::new("./posts"))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[instrument(skip(state))]
|
|
||||||
pub async fn view(
|
|
||||||
extract::Path(slug): extract::Path<String>,
|
|
||||||
Extension(state): Extension<Arc<State>>,
|
|
||||||
) -> Result<Html<String>, WebsiteError> {
|
|
||||||
debug!("viewing post: {slug}");
|
|
||||||
let post = state
|
|
||||||
.posts
|
|
||||||
.get(&slug)
|
|
||||||
.ok_or(WebsiteError::NotFound)?;
|
|
||||||
|
|
||||||
let res = render_post(&state.tera, post).await?;
|
|
||||||
|
|
||||||
Ok(Html(res))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[instrument(skip(state))]
|
|
||||||
pub async fn index(
|
|
||||||
Extension(state): Extension<Arc<State>>,
|
|
||||||
) -> Result<Html<String>, WebsiteError> {
|
|
||||||
let mut ctx = tera::Context::new();
|
|
||||||
|
|
||||||
let mut posts = state.posts.values().collect::<Vec<&Post>>();
|
|
||||||
|
|
||||||
posts.sort_by_key(|p| &p.date);
|
|
||||||
posts.reverse();
|
|
||||||
|
|
||||||
ctx.insert("page.title", "Posts");
|
|
||||||
ctx.insert("posts", &posts);
|
|
||||||
|
|
||||||
let res = match state.tera.render("posts_index.html", &ctx) {
|
|
||||||
Ok(r) => r,
|
|
||||||
Err(e) => {
|
|
||||||
error!("failed to render posts index: {}", e);
|
|
||||||
return Err(e.into());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Html(res))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use tera::Tera;
|
use tera::Tera;
|
||||||
|
|
Loading…
Reference in a new issue