1
0
Fork 0

static files for posts

This commit is contained in:
Adrian Hedqvist 2023-03-22 22:39:18 +01:00
parent 982a642d17
commit 3895837b7a
6 changed files with 658 additions and 350 deletions

945
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -6,7 +6,8 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
axum = { version = "0.5.7", features = ["http2"] } axum = { version = "0.6.12", features = ["http2"] }
cached = "0.42.0"
color-eyre = "0.6.1" color-eyre = "0.6.1"
glob = "0.3.0" glob = "0.3.0"
hyper = { version = "0.14.19", features = ["full"] } hyper = { version = "0.14.19", features = ["full"] }
@ -17,6 +18,6 @@ serde_json = "1.0.85"
tera = "1.17.0" tera = "1.17.0"
tokio = { version = "1.19.2", features = ["full"] } tokio = { version = "1.19.2", features = ["full"] }
tower = { version = "0.4.12", features = ["full"] } tower = { version = "0.4.12", features = ["full"] }
tower-http = { version = "0.3.4", features = ["full"] } tower-http = { version = "0.4.0", features = ["full"] }
tracing = "0.1.35" tracing = "0.1.35"
tracing-subscriber = { version = "0.3.11", features = ["fmt"] } tracing-subscriber = { version = "0.3.11", features = ["fmt"] }

View file

@ -1,20 +1,23 @@
use std::sync::Arc; use std::sync::Arc;
use axum::{extract::Path, response::Html, Extension}; use axum::{extract::Path, response::Html, Extension};
use cached::proc_macro::cached;
use pulldown_cmark::{html, Options, Parser}; use pulldown_cmark::{html, Options, Parser};
use tracing::log::*; use tracing::log::*;
use super::{Error, Result}; use super::{Error, Result};
use crate::{handlers::PageContext, State}; use crate::{handlers::PageContext, Post, State};
pub async fn view(Path(slug): Path<String>, Extension(state): Extension<Arc<State>>) -> Result { pub async fn view(Path(path): Path<String>, Extension(state): Extension<Arc<State>>) -> Result {
let post = state let post = path.trim_end_matches('/');
.posts info!("Requested post: {}", post);
.iter() let res = render_post(&state, post).await.ok_or(Error::NotFound)?;
.find(|p| p.slug == slug) Ok(Html(res.into()))
.ok_or(Error::NotFound)?; }
info!("Requested post: {}", slug); #[cached(time = 60, key = "String", convert = r"{ path.to_owned() }")]
async fn render_post(state: &State, path: &str) -> Option<String> {
let post = state.posts.iter().find(|p| p.slug == path)?;
let options = Options::all(); let options = Options::all();
let parser = Parser::new_ext(&post.content, options); let parser = Parser::new_ext(&post.content, options);
@ -22,22 +25,23 @@ pub async fn view(Path(slug): Path<String>, Extension(state): Extension<Arc<Stat
let mut out = String::new(); let mut out = String::new();
html::push_html(&mut out, parser); html::push_html(&mut out, parser);
let ctx = let ctx = tera::Context::from_serialize(PageContext { content: out }).ok()?;
tera::Context::from_serialize(PageContext { content: out }).map_err(|_| Error::NotFound)?;
let res = state.tera.render("post.html", &ctx).map_err(|e| { let res = match state.tera.render("post.html", &ctx) {
Ok(res) => res,
Err(e) => {
error!("Failed rendering post: {}", e); error!("Failed rendering post: {}", e);
Error::NotFound return None;
})?; }
};
Ok(Html(res.into())) Some(res)
} }
pub async fn index(Extension(state): Extension<Arc<State>>) -> Result { pub async fn index(Extension(state): Extension<Arc<State>>) -> Result {
let mut ctx = tera::Context::new(); let mut ctx = tera::Context::new();
ctx.insert("posts", &state.posts); ctx.insert("posts", &state.posts);
let res = state.tera.render("postsindex.html", &ctx).map_err(|e| { let res = state.tera.render("postsindex.html", &ctx).map_err(|e| {
error!("Failed rendering posts index: {}", e); error!("Failed rendering posts index: {:?}", e);
Error::NotFound Error::NotFound
})?; })?;
Ok(Html(res.into())) Ok(Html(res.into()))

View file

@ -4,7 +4,7 @@ use axum::{
handler::Handler, handler::Handler,
http::StatusCode, http::StatusCode,
routing::{get, get_service}, routing::{get, get_service},
Extension, Router, Extension, Router, ServiceExt,
}; };
use color_eyre::eyre::Result; use color_eyre::eyre::Result;
use glob::glob; use glob::glob;
@ -22,6 +22,7 @@ pub struct State {
#[derive(Serialize)] #[derive(Serialize)]
pub struct Post { pub struct Post {
pub name: String,
pub slug: String, pub slug: String,
pub content: String, pub content: String,
} }
@ -36,6 +37,7 @@ async fn main() -> Result<()> {
let posts = glob("posts/**/*.md")? let posts = glob("posts/**/*.md")?
.map(|p| { .map(|p| {
let path = p.unwrap(); let path = p.unwrap();
info!("found page: {}", path.display());
let filename = path.file_name().unwrap().to_string_lossy(); let filename = path.file_name().unwrap().to_string_lossy();
@ -51,8 +53,8 @@ async fn main() -> Result<()> {
} else { } else {
filename.to_owned() filename.to_owned()
}; };
Post { Post {
name: slug.clone(),
slug, slug,
content: std::fs::read_to_string(&path).unwrap(), content: std::fs::read_to_string(&path).unwrap(),
} }
@ -68,13 +70,9 @@ async fn main() -> Result<()> {
let app = Router::new() let app = Router::new()
.route("/", get(handlers::index)) .route("/", get(handlers::index))
.route("/posts/", get(handlers::posts::index)) .route("/posts/", get(handlers::posts::index))
.route("/posts/:slug/", get(handlers::posts::view)) .route("/posts/:path/", get(handlers::posts::view))
.nest( .nest_service("/static", tower_http::services::ServeDir::new("./static"))
"/static", .fallback_service(tower_http::services::ServeDir::new("./"))
get_service(tower_http::services::ServeDir::new("./static"))
.handle_error(|_e| async { (StatusCode::NOT_FOUND, "not found") }),
)
.fallback((|| async { (StatusCode::NOT_FOUND, "not found") }).into_service())
.layer(middleware); .layer(middleware);
info!("Now listening at http://localhost:8180"); info!("Now listening at http://localhost:8180");

View file

@ -5,7 +5,7 @@
<p>hi hello welcome to my website it's pretty wip right now yeah ok bye</p> <p>hi hello welcome to my website it's pretty wip right now yeah ok bye</p>
<h2>todo</h2> <h2>todo</h2>
<ul> <ul>
<li>static content</li> <li>static content</li>
<li>sass compilation</li> <li>sass compilation</li>
<li>post metadata (frontmatter)</li> <li>post metadata (frontmatter)</li>
<li>rss/atom/jsonfeed</li> <li>rss/atom/jsonfeed</li>

View file

@ -5,7 +5,7 @@
<p>i occasionally write some stuff i guess</p> <p>i occasionally write some stuff i guess</p>
<ul> <ul>
{% for post in posts %} {% for post in posts %}
<li><a href="/posts/{{post.name}}">{{post.name}}</a></li> <li><a href="/posts/{{post.name}}/">{{post.name}}</a></li>
{% endfor %} {% endfor %}
</ul> </ul>
{% endblock main %} {% endblock main %}