hot reload memcache

This commit is contained in:
cbax 2024-10-24 00:10:35 -04:00
parent 0b293d0550
commit 0e20b1d53c
5 changed files with 638 additions and 35 deletions

292
Cargo.lock generated
View file

@ -17,6 +17,18 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "ahash"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
dependencies = [
"cfg-if",
"once_cell",
"version_check",
"zerocopy",
]
[[package]]
name = "aho-corasick"
version = "1.1.2"
@ -41,6 +53,18 @@ dependencies = [
"alloc-no-stdlib",
]
[[package]]
name = "allocator-api2"
version = "0.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f"
[[package]]
name = "arraydeque"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d902e3d592a523def97af8f317b08ce16b7ab854c1985a0c671e6f15cebc236"
[[package]]
name = "askama"
version = "0.12.1"
@ -225,6 +249,15 @@ dependencies = [
"serde",
]
[[package]]
name = "bincode"
version = "1.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
dependencies = [
"serde",
]
[[package]]
name = "bitflags"
version = "1.3.2"
@ -272,13 +305,17 @@ dependencies = [
"askama_axum",
"axum",
"axum-server-timing",
"gray_matter",
"markdown",
"notify",
"serde",
"syntect",
"tokio",
"tower 0.5.1",
"tower-http",
"tracing",
"tracing-subscriber",
"walkdir",
]
[[package]]
@ -321,6 +358,30 @@ version = "0.8.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
[[package]]
name = "deranged"
version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
dependencies = [
"powerfmt",
]
[[package]]
name = "encoding_rs"
version = "0.8.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59"
dependencies = [
"cfg-if",
]
[[package]]
name = "equivalent"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "filetime"
version = "0.2.25"
@ -424,6 +485,43 @@ version = "0.28.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
[[package]]
name = "gray_matter"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31ee6a6070bad7c953b0c8be9367e9372181fed69f3e026c4eb5160d8b3c0222"
dependencies = [
"serde",
"serde_json",
"toml",
"yaml-rust2",
]
[[package]]
name = "hashbrown"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
dependencies = [
"ahash",
"allocator-api2",
]
[[package]]
name = "hashbrown"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb"
[[package]]
name = "hashlink"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7"
dependencies = [
"hashbrown 0.14.5",
]
[[package]]
name = "hermit-abi"
version = "0.3.9"
@ -527,6 +625,16 @@ dependencies = [
"tower-service",
]
[[package]]
name = "indexmap"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da"
dependencies = [
"equivalent",
"hashbrown 0.15.0",
]
[[package]]
name = "inotify"
version = "0.9.6"
@ -621,6 +729,12 @@ dependencies = [
"redox_syscall 0.5.7",
]
[[package]]
name = "linked-hash-map"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
[[package]]
name = "lock_api"
version = "0.4.11"
@ -761,6 +875,12 @@ dependencies = [
"winapi",
]
[[package]]
name = "num-conv"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
[[package]]
name = "num-traits"
version = "0.2.17"
@ -785,6 +905,28 @@ version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "onig"
version = "6.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c4b31c8722ad9171c6d77d3557db078cab2bd50afcc9d09c8b315c59df8ca4f"
dependencies = [
"bitflags 1.3.2",
"libc",
"once_cell",
"onig_sys",
]
[[package]]
name = "onig_sys"
version = "69.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b829e3d7e9cc74c7e315ee8edb185bf4190da5acde74afd7fc59c35b1f086e7"
dependencies = [
"cc",
"pkg-config",
]
[[package]]
name = "overload"
version = "0.1.1"
@ -858,6 +1000,25 @@ version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a"
[[package]]
name = "plist"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42cf17e9a1800f5f396bc67d193dc9411b59012a5876445ef450d449881e1016"
dependencies = [
"base64",
"indexmap",
"quick-xml",
"serde",
"time",
]
[[package]]
name = "powerfmt"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
[[package]]
name = "proc-macro2"
version = "1.0.89"
@ -867,6 +1028,15 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "quick-xml"
version = "0.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d3a6e5838b60e0e8fa7a43f22ade549a37d61f8bdbe636d0d7816191de969c2"
dependencies = [
"memchr",
]
[[package]]
name = "quote"
version = "1.0.35"
@ -1090,6 +1260,48 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394"
[[package]]
name = "syntect"
version = "5.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "874dcfa363995604333cf947ae9f751ca3af4522c60886774c4963943b4746b1"
dependencies = [
"bincode",
"bitflags 1.3.2",
"flate2",
"fnv",
"once_cell",
"onig",
"plist",
"regex-syntax 0.8.2",
"serde",
"serde_derive",
"serde_json",
"thiserror",
"walkdir",
"yaml-rust",
]
[[package]]
name = "thiserror"
version = "1.0.65"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d11abd9594d9b38965ef50805c5e469ca9cc6f197f883f717e0269a3057b3d5"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.65"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae71770322cbd277e69d762a16c444af02aa0575ac0d174f0b9562d3b37f8602"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "thread_local"
version = "1.1.7"
@ -1100,6 +1312,37 @@ dependencies = [
"once_cell",
]
[[package]]
name = "time"
version = "0.3.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
dependencies = [
"deranged",
"itoa",
"num-conv",
"powerfmt",
"serde",
"time-core",
"time-macros",
]
[[package]]
name = "time-core"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
[[package]]
name = "time-macros"
version = "0.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf"
dependencies = [
"num-conv",
"time-core",
]
[[package]]
name = "tokio"
version = "1.41.0"
@ -1142,6 +1385,15 @@ dependencies = [
"tokio",
]
[[package]]
name = "toml"
version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
dependencies = [
"serde",
]
[[package]]
name = "tower"
version = "0.4.13"
@ -1516,6 +1768,46 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "yaml-rust"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
dependencies = [
"linked-hash-map",
]
[[package]]
name = "yaml-rust2"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8902160c4e6f2fb145dbe9d6760a75e3c9522d8bf796ed7047c85919ac7115f8"
dependencies = [
"arraydeque",
"encoding_rs",
"hashlink",
]
[[package]]
name = "zerocopy"
version = "0.7.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.7.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "zstd"
version = "0.13.0"

View file

@ -10,10 +10,14 @@ askama = { version = "0.12.1", features = ["with-axum"] }
askama_axum = "0.4.0"
axum = "0.7.7"
axum-server-timing = "1.0.1"
gray_matter = "0.2.8"
markdown = "1.0.0-alpha.21"
notify = "6.1.1"
serde = { version = "1.0.213", features = ["derive"] }
syntect = "5.2.0"
tokio = { version = "1.41.0", features = ["full"] }
tower = "0.5.1"
tower-http = { version = "0.6.1", features = ["full"] }
tracing = "0.1.40"
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
walkdir = "2.5.0"

View file

@ -1,6 +1,6 @@
- name: cbax dev deploy
hosts:
containers:
'{{ lookup("env", "ANSIBLE_HOST") }}':
ansible_user: '{{ lookup("env", "ANSIBLE_USER") }}'
ansible_password: '{{ lookup("env", "ANSIBLE_PASSWORD") }}'
tasks:

17
posts/test_post.md Normal file
View file

@ -0,0 +1,17 @@
---
title: "Test markdown post"
author: "cbax"
description: "Only a test post"
img_path: "/res/img/posts/testing.png"
canonical_url: "//cbax.dev/blog/testing"
---
# This is a test
## h2
testing testing testing
> quote me!
```bash
cat lol | ripgrep "test"
```

View file

@ -1,6 +1,6 @@
use std::io::prelude::*;
use std::collections::HashMap;
use std::hash::Hash;
use std::sync::{Arc, RwLock};
use std::{collections::HashMap, fs::File};
use askama::Template;
use axum::extract::{Path, State};
@ -10,9 +10,13 @@ use axum::{
routing::get,
Router,
};
use gray_matter::engine::YAML;
use gray_matter::Matter;
use notify::{RecursiveMode, Watcher};
use tower::ServiceBuilder;
use tower_http::services::ServeDir;
use tower_http::trace::TraceLayer;
use tracing::Instrument;
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
#[derive(Clone)]
@ -77,7 +81,8 @@ struct BlogPostTemplate {
#[derive(Clone)]
struct AppState {
posts: Arc<RwLock<HashMap<String, Post>>>,
posts: HashMap<String, Post>,
styles: HashMap<String, String>,
}
struct HtmlTemplate<T>(T);
@ -98,14 +103,52 @@ where
}
}
async fn blog(State(state): State<AppState>) -> Result<impl IntoResponse, StatusCode> {
let mut styles_file = File::open("static/css/styles.css").expect("Failed to open stylesheet.");
let mut styles = String::new();
styles_file
.read_to_string(&mut styles)
.expect("Failed to read stylesheet.");
async fn blog(State(state): State<Arc<RwLock<AppState>>>) -> Result<impl IntoResponse, StatusCode> {
let mut styles: String = state
.read()
.unwrap()
.styles
.get("core.css")
.unwrap()
.clone();
styles.push_str(
state
.read()
.unwrap()
.styles
.get("nord.css")
.unwrap()
.as_str(),
);
styles.push_str(
state
.read()
.unwrap()
.styles
.get("colors.css")
.unwrap()
.as_str(),
);
styles.push_str(
state
.read()
.unwrap()
.styles
.get("components.css")
.unwrap()
.as_str(),
);
styles.push_str(
state
.read()
.unwrap()
.styles
.get("font.css")
.unwrap()
.as_str(),
);
let mut posts: Vec<(String, Meta)> = vec![];
for (slug, post) in state.posts.read().unwrap().clone().iter() {
for (slug, post) in state.read().unwrap().posts.clone().iter() {
posts.push((slug.clone(), post.meta.clone()));
}
let meta = Meta {
@ -128,15 +171,53 @@ async fn blog(State(state): State<AppState>) -> Result<impl IntoResponse, Status
}
async fn blog_post(
State(state): State<AppState>,
State(state): State<Arc<RwLock<AppState>>>,
Path(post_slug): Path<String>,
) -> Result<impl IntoResponse, StatusCode> {
let mut styles_file = File::open("static/css/styles.css").expect("Failed to open stylesheet.");
let mut styles = String::new();
styles_file
.read_to_string(&mut styles)
.expect("Failed to read stylesheet.");
if let Some(post) = state.posts.read().unwrap().clone().get(&post_slug) {
let mut styles: String = state
.read()
.unwrap()
.styles
.get("core.css")
.unwrap()
.clone();
styles.push_str(
state
.read()
.unwrap()
.styles
.get("nord.css")
.unwrap()
.as_str(),
);
styles.push_str(
state
.read()
.unwrap()
.styles
.get("colors.css")
.unwrap()
.as_str(),
);
styles.push_str(
state
.read()
.unwrap()
.styles
.get("components.css")
.unwrap()
.as_str(),
);
styles.push_str(
state
.read()
.unwrap()
.styles
.get("font.css")
.unwrap()
.as_str(),
);
if let Some(post) = state.read().unwrap().posts.clone().get(&post_slug) {
let meta = post.meta.clone();
let template = BlogPostTemplate {
_parent: BaseTemplate {
@ -152,22 +233,102 @@ async fn blog_post(
return Err(StatusCode::NOT_FOUND);
}
async fn homelab() -> Result<impl IntoResponse, StatusCode> {
let mut styles_file = File::open("static/css/styles.css").expect("Failed to open stylesheet.");
let mut styles = String::new();
styles_file
.read_to_string(&mut styles)
.expect("Failed to read stylesheet.");
async fn homelab(
State(state): State<Arc<RwLock<AppState>>>,
) -> Result<impl IntoResponse, StatusCode> {
let mut styles: String = state
.read()
.unwrap()
.styles
.get("core.css")
.unwrap()
.clone();
styles.push_str(
state
.read()
.unwrap()
.styles
.get("nord.css")
.unwrap()
.as_str(),
);
styles.push_str(
state
.read()
.unwrap()
.styles
.get("colors.css")
.unwrap()
.as_str(),
);
styles.push_str(
state
.read()
.unwrap()
.styles
.get("components.css")
.unwrap()
.as_str(),
);
styles.push_str(
state
.read()
.unwrap()
.styles
.get("font.css")
.unwrap()
.as_str(),
);
let template = HomelabTemplate { styles };
return Ok(HtmlTemplate(template));
}
async fn index() -> Result<impl IntoResponse, StatusCode> {
let mut styles_file = File::open("static/css/styles.css").expect("Failed to open stylesheet.");
let mut styles = String::new();
styles_file
.read_to_string(&mut styles)
.expect("Failed to read stylesheet.");
async fn index(
State(state): State<Arc<RwLock<AppState>>>,
) -> Result<impl IntoResponse, StatusCode> {
let mut styles: String = state
.read()
.unwrap()
.styles
.get("core.css")
.unwrap()
.clone();
styles.push_str(
state
.read()
.unwrap()
.styles
.get("nord.css")
.unwrap()
.as_str(),
);
styles.push_str(
state
.read()
.unwrap()
.styles
.get("colors.css")
.unwrap()
.as_str(),
);
styles.push_str(
state
.read()
.unwrap()
.styles
.get("components.css")
.unwrap()
.as_str(),
);
styles.push_str(
state
.read()
.unwrap()
.styles
.get("font.css")
.unwrap()
.as_str(),
);
let template = IndexTemplate { styles };
return Ok(HtmlTemplate(template));
}
@ -185,10 +346,11 @@ async fn main() {
)
.with(tracing_subscriber::fmt::layer())
.init();
let state = AppState {
posts: Arc::new(RwLock::new(HashMap::new())),
};
state.posts.write().unwrap().insert(
let state = Arc::new(RwLock::new(AppState {
posts: HashMap::new(),
styles: HashMap::new(),
}));
state.write().unwrap().posts.insert(
"test_slug".into(),
Post {
meta: Meta {
@ -202,12 +364,140 @@ async fn main() {
body: "this is a test post!".into(),
},
);
for entry in walkdir::WalkDir::new("./posts") {
let entry = entry.unwrap();
if entry.file_type().is_file() {
let path = entry.into_path();
let file: String = std::fs::read_to_string(&path).unwrap();
let matter = Matter::<YAML>::new();
let matter_result = matter.parse(file.as_str());
let meta = Meta {
title: matter_result.data.as_ref().unwrap()["title"]
.as_string()
.unwrap_or_default(),
author: matter_result.data.as_ref().unwrap()["author"]
.as_string()
.unwrap_or_default(),
description: matter_result.data.as_ref().unwrap()["description"]
.as_string()
.unwrap_or_default(),
img_path: matter_result.data.as_ref().unwrap()["img_path"]
.as_string()
.unwrap_or_default(),
canonical_url: matter_result.data.as_ref().unwrap()["canonical_url"]
.as_string()
.unwrap_or_default(),
};
state.write().unwrap().posts.insert(
path.file_name().unwrap().to_string_lossy().to_string(),
Post {
meta,
cover_img_path: String::new(),
body: markdown::to_html_with_options(
matter_result.content.as_str(),
&markdown::Options::gfm(),
)
.unwrap(),
},
);
}
}
for entry in walkdir::WalkDir::new("./static/css") {
let entry = entry.unwrap();
if entry.file_type().is_file() {
let path = entry.into_path();
let file: String = std::fs::read_to_string(&path).unwrap();
state.write().unwrap().styles.insert(
path.file_name().unwrap().to_string_lossy().to_string(),
file,
);
}
}
let notify_state1 = state.clone();
let notify_state2 = state.clone();
let mut css_watcher =
notify::recommended_watcher(move |res: Result<notify::Event, notify::Error>| match res {
Ok(event) => {
println!("event triggered: {:?}", event);
if event.kind.is_create() || event.kind.is_modify() {
for path in event.paths.iter() {
println!("refreshing css: {}", path.to_string_lossy());
let file: String = std::fs::read_to_string(&path).unwrap();
notify_state1.clone().write().unwrap().styles.insert(
path.file_name().unwrap().to_string_lossy().to_string(),
file,
);
}
}
}
Err(e) => eprintln!("watch error: {:?}", e),
})
.unwrap();
let mut post_watcher =
notify::recommended_watcher(move |res: Result<notify::Event, notify::Error>| match res {
Ok(mut event) => {
if event.kind.is_create() || event.kind.is_modify() {
event.paths.dedup();
for path in event.paths.iter().filter(|p| {
if p.extension().is_some() {
p.extension().unwrap() == "md"
} else {
false
}
}) {
println!("refreshing post: {}", path.to_string_lossy().to_string());
std::thread::sleep(std::time::Duration::from_millis(1));
let file: String =
std::fs::read_to_string(path.to_string_lossy().to_string()).unwrap();
let matter = Matter::<YAML>::new();
let matter_result = matter.parse(file.as_str());
let meta = Meta {
title: matter_result.data.as_ref().unwrap()["title"]
.as_string()
.unwrap_or_default(),
author: matter_result.data.as_ref().unwrap()["author"]
.as_string()
.unwrap_or_default(),
description: matter_result.data.as_ref().unwrap()["description"]
.as_string()
.unwrap_or_default(),
img_path: matter_result.data.as_ref().unwrap()["img_path"]
.as_string()
.unwrap_or_default(),
canonical_url: matter_result.data.as_ref().unwrap()["canonical_url"]
.as_string()
.unwrap_or_default(),
};
notify_state2.clone().write().unwrap().posts.insert(
path.file_name().unwrap().to_string_lossy().to_string(),
Post {
meta,
cover_img_path: String::new(),
body: markdown::to_html_with_options(
matter_result.content.as_str(),
&markdown::Options::gfm(),
)
.unwrap(),
},
);
}
}
}
Err(e) => eprintln!("watch error: {:?}", e),
})
.unwrap();
css_watcher
.watch(std::path::Path::new("static/css"), RecursiveMode::Recursive)
.unwrap();
post_watcher
.watch(std::path::Path::new("posts"), RecursiveMode::Recursive)
.unwrap();
let app = Router::new()
.route("/", get(index))
.route("/homelab", get(homelab))
.route("/blog", get(blog))
.route("/blog/:post_slug", get(blog_post))
.with_state(state)
.with_state(state.clone())
.fallback_service(serve_dir())
.layer(axum_server_timing::ServerTimingLayer::new("Axum"))
.layer(ServiceBuilder::new().layer(TraceLayer::new_for_http()));