From 3189b57a46d07523667be05c42a1446d1a928fb1 Mon Sep 17 00:00:00 2001 From: Adrian Hedqvist Date: Sat, 25 Mar 2023 21:38:16 +0100 Subject: [PATCH] So much stuff --- .dockerignore | 3 + .vscode/settings.json | 3 +- Cargo.toml | 2 +- Containerfile | 11 -- Dockerfile | 65 +++++++++ posts/dungeon/index.md | 49 +++++++ posts/dungeon/screenshot.png | Bin 0 -> 28308 bytes posts/foldertest/index.md | 2 - posts/foldertest/nestedpost.md | 9 -- posts/foldertest/nestedpost/evendeeper.md | 7 - posts/hello-world.md | 11 ++ posts/status-update-2020-05-16/index.md | 24 ++++ posts/test.md | 7 - src/handlers/mod.rs | 29 +--- src/main.rs | 24 +++- src/post.rs | 155 +++++++++++----------- templates/base.html | 8 +- templates/index.html | 4 +- templates/partials/head.html | 4 +- templates/post.html | 14 +- templates/posts_index.html | 16 +-- 21 files changed, 289 insertions(+), 158 deletions(-) create mode 100644 .dockerignore delete mode 100644 Containerfile create mode 100644 Dockerfile create mode 100644 posts/dungeon/index.md create mode 100644 posts/dungeon/screenshot.png delete mode 100644 posts/foldertest/nestedpost.md delete mode 100644 posts/foldertest/nestedpost/evendeeper.md create mode 100644 posts/hello-world.md create mode 100644 posts/status-update-2020-05-16/index.md delete mode 100644 posts/test.md diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..eb205fe --- /dev/null +++ b/.dockerignore @@ -0,0 +1,3 @@ +.git +.vscode +target diff --git a/.vscode/settings.json b/.vscode/settings.json index 718e72c..96e527c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,7 +3,6 @@ "sv" ], "spellright.documentTypes": [ - "latex", - "plaintext" + "latex" ] } \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index fa02fd6..2c9346b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ regex = "1.7.2" serde = "1.0.144" serde_derive = "1.0.144" serde_json = "1.0.85" -tera = "1.17.0" +tera = { version = "1.17.0", features = ["builtins"] } tokio = { version = "1.19.2", features = ["full"] } toml = "0.7.3" tower = { version = "0.4.12", features = ["full"] } diff --git a/Containerfile b/Containerfile deleted file mode 100644 index 085e7fd..0000000 --- a/Containerfile +++ /dev/null @@ -1,11 +0,0 @@ -FROM rust:slim as build-env -WORKDIR /app -COPY . /app -RUN cargo build --release - -FROM gcr.io/distroless/cc -COPY --from=build-env /app/target/release/website / -COPY --from=build-env /templates / -COPY --from=build-env /posts / -COPY --from=build-env /static / -CMD ["./website"] \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..654e314 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,65 @@ +FROM rust:slim AS chef +RUN cargo install cargo-chef +WORKDIR app + +#################################################################################################### +## Planner +#################################################################################################### +FROM chef AS planner +COPY . . +RUN cargo chef prepare --recipe-path recipe.json + +#################################################################################################### +## Builder +#################################################################################################### +FROM chef AS builder + +RUN rustup target add x86_64-unknown-linux-musl +RUN apt update && apt install -y musl-tools musl-dev +RUN update-ca-certificates + +# Create appuser +ENV USER=website +ENV UID=10001 + +RUN adduser \ + --disabled-password \ + --gecos "" \ + --home "/nonexistent" \ + --shell "/sbin/nologin" \ + --no-create-home \ + --uid "${UID}" \ + "${USER}" + +WORKDIR /app + +COPY --from=planner /app/recipe.json . +RUN cargo chef cook --release --recipe-path recipe.json + +COPY . . + +RUN cargo build --target x86_64-unknown-linux-musl --release + +#################################################################################################### +## Final image +#################################################################################################### +FROM scratch + +# Import from builder. +COPY --from=builder /etc/passwd /etc/passwd +COPY --from=builder /etc/group /etc/group + +WORKDIR /app + +# Copy our build +COPY --from=builder /app/target/x86_64-unknown-linux-musl/release/website ./ +COPY --from=builder /app/posts ./posts +COPY --from=builder /app/static ./static +COPY --from=builder /app/templates ./templates + +EXPOSE 8180 + +# Use an unprivileged user. +USER website:website + +CMD ["./website"] \ No newline at end of file diff --git a/posts/dungeon/index.md b/posts/dungeon/index.md new file mode 100644 index 0000000..1454952 --- /dev/null +++ b/posts/dungeon/index.md @@ -0,0 +1,49 @@ ++++ +date = 2018-01-10T17:50:00+01:00 +draft = false +title = "Yet another (traditional) roguelike written in c++" +aliases = ["/blog/dungeon/", "/blog/dungeon.html"] + +[taxonomies] +tags = ["cpp", "roguelike"] ++++ + +![work-in-progress screenshot](screenshot.png) + +My current go-to pet project is [dungeon](https://github.com/tollyx/dungeon), a + roguelike which is currently pretty incomplete and basic, but I think I've + gotten far enough to start showing it off a bit more. + +It's a roguelike with your usual fantasy-setting (for now, anyway) + and mechanically it's going to be pretty typical as well. Eventually though, + I'm planning on implementing some kind of bodypart system where potions, food, + traps, enemy attacks and so on can transform your character, giving you both + positive and negative effects. + +Also, Lua scripting. I want as much as possible outside the main game engine be + scriptable in Lua. Which means items, status effects, skills, traps, actors and + whatever else that might be fun to have easily modifiable. I'm also using Lua + for basic data loading but that's just because I'm lazy and why the hell not + when Lua is already implemented. + +The main things that are currently missing are: + +- Menus +- In-game UI (including a log) +- Items (with lua-scripted effects) +- Loading enemies from data + +After those and a bit more content I'll be implementing whatever cool ideas that + I come up with. + +I'm mainly making it for learning purposes (and for fun), trying out new things + as I go. That lack of planning combined with not actually spending too much + time on it means that progress is slow. The fact that I find myself rewriting + things quite often due to the mentioned lack of planning doesn't help much, + either. + +I'll try to write blog posts about whatever in the game that might be + interesting for others, but it will probably take a while before they pop up as + the game is currently pretty basic and will stay so for a while longer. + +[You can find the source on github](https://github.com/tollyx/dungeon). diff --git a/posts/dungeon/screenshot.png b/posts/dungeon/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..1067b9a3d62c9819bd9acdc48cdd95ab9c048b22 GIT binary patch literal 28308 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 Q4H;s-@*Cq=!!1Yt58n~vV*mgE literal 0 HcmV?d00001 diff --git a/posts/foldertest/index.md b/posts/foldertest/index.md index 4409aa0..45721db 100644 --- a/posts/foldertest/index.md +++ b/posts/foldertest/index.md @@ -3,8 +3,6 @@ title="TOML metadata test" date=2023-03-25T14:50:25+01:00 +++ -# Testing post as index within folder - hope it works yay here have a squid miku to test relative paths: diff --git a/posts/foldertest/nestedpost.md b/posts/foldertest/nestedpost.md deleted file mode 100644 index 77a8a06..0000000 --- a/posts/foldertest/nestedpost.md +++ /dev/null @@ -1,9 +0,0 @@ -+++ -title='nested post test does work!' -date=2022-03-25T14:50:25+01:00 -+++ -# yet again a nested post test - -will it work this time, at least with the slug?? - -how about we go [even deeper!!](evendeeper/) diff --git a/posts/foldertest/nestedpost/evendeeper.md b/posts/foldertest/nestedpost/evendeeper.md deleted file mode 100644 index 3449497..0000000 --- a/posts/foldertest/nestedpost/evendeeper.md +++ /dev/null @@ -1,7 +0,0 @@ -+++ -title='how about even deeper??' -date=2024-03-25T14:50:25+01:00 -+++ -# WOWOAOWOFAODWAOWOAWAOWA - -SOOO DEEP BRO diff --git a/posts/hello-world.md b/posts/hello-world.md new file mode 100644 index 0000000..0c488bc --- /dev/null +++ b/posts/hello-world.md @@ -0,0 +1,11 @@ ++++ +date = 2017-06-25T22:40:00+01:00 +draft = false +title = "Hello world, again" +aliases = ["/blog/hello-world/", "/blog/hello-world.html"] +tags = ["hugo", "jekyll"] ++++ + +So I've yet again remade this website. This time I've moved from [jekyll](https://jekyllrb.com/) to [hugo](https://gohugo.io/), because I've had to reinstall ruby far too many times and there's always some issue with it preventing me from just getting it up and running so I can update this site. Hugo is just a binary, no need to install anything to get it to run. It's far less of a hassle. + +If you for whatever reason want to see the old posts (which were basically just dev logs required for some of my school courses), you can find them [here.](@/old/_index.md) diff --git a/posts/status-update-2020-05-16/index.md b/posts/status-update-2020-05-16/index.md new file mode 100644 index 0000000..ce61a06 --- /dev/null +++ b/posts/status-update-2020-05-16/index.md @@ -0,0 +1,24 @@ ++++ +date = 2020-05-16T13:43:00+02:00 +draft = false +title = 'Status update: 2020-05-16' +tags = ["hugo", "zola", "roguelike", "spork"] ++++ + +Oh boy, it's been a while since I wrote anything on this website, over two years ago! + +But I've been thinking of starting to write posts more often, try this proggramming-blogging thing out that seems to have had some sort of resurgance. I don't know if I have anything others will find interesting to read, but I've got a few drafts lying around that I'm gonna try finishing up. We'll see how it goes. Just don't expect me to post things too often - maybe once or twice a month, at most. + +And with that, I've moved the website to yet another static website generator, this time I'm using [zola]. Why? It's written in rust. That's pretty much all I got. It's actually very similar to [hugo], which I used previously, but I don't know - zola kinda feels nicer to work with when it comes to templates/themes? + +My latest free-time project I've been working on is yet another roguelike - this time, I'm following a tutorial so that I won't get stuck for too long figuring out architecture stuff. It's being written in rust and the tutorial I'm following is [this one][roguelike-tutorial]. It's pretty great - it's using [specs] which I've tried out multiple times but I never really got a good hang of figuring out how to do stuff with an ECS, so learning more about that has been nice. But I did the stupid mistake of not writing down some small errors in the tutorial, so I'll probably go back and try to find them again and open a few PR's for them. I'll try to write a post or two about the roguelike here as well. But for now, [you can find the source code for it over here][roguelike-src]. + +In other news, I bought myself an apartment. I'm moving at the start of June, so that's pretty exciting. + +Also, in case someone actually got my rss feed in their reader - sorry, it broke again when I switched to zola, and from what I've read it'll break again in version 0.11 due to a change I agree with. You'll have to fix the feed link, twice. Again, sorry about that. + +[zola]: https://getzola.org +[hugo]: https://gohugo.io +[roguelike-tutorial]: https://bfnightly.bracketproductions.com/rustbook/chapter_0.html +[specs]: https://crates.io/crates/specs +[roguelike-src]: https://gitlab.com/tollyx/roguelike-tutorial-rs diff --git a/posts/test.md b/posts/test.md deleted file mode 100644 index 1fbf5a9..0000000 --- a/posts/test.md +++ /dev/null @@ -1,7 +0,0 @@ -+++ -title="test page please ignore" -date=2023-03-25T15:16:10+01:00 -+++ -# Test page please ignore - -Hello world! diff --git a/src/handlers/mod.rs b/src/handlers/mod.rs index 17cbcd8..095dd29 100644 --- a/src/handlers/mod.rs +++ b/src/handlers/mod.rs @@ -7,43 +7,26 @@ use axum::{ Extension, }; -use crate::State; - -#[derive(Debug)] -pub enum Error { - NotFound, - InternalError(anyhow::Error), -} - -impl From for Error -where - E: Into, -{ - fn from(value: E) -> Self { - Error::InternalError(value.into()) - } -} - -pub type Result>> = std::result::Result; +use crate::{State, WebsiteError}; #[instrument(skip(state))] -pub async fn index(Extension(state): Extension>) -> Result { +pub async fn index(Extension(state): Extension>) -> std::result::Result>, WebsiteError> { let ctx = tera::Context::new(); let res = state.tera.render("index.html", &ctx).map_err(|e| { error!("Failed rendering index: {}", e); - Error::NotFound + WebsiteError::NotFound })?; Ok(Html(res.into())) } -impl IntoResponse for Error { +impl IntoResponse for WebsiteError { fn into_response(self) -> Response { match self { - Error::NotFound => { + WebsiteError::NotFound => { info!("not found"); (StatusCode::NOT_FOUND, ()).into_response() } - Error::InternalError(e) => { + WebsiteError::InternalError(e) => { error!("internal error: {e}"); (StatusCode::INTERNAL_SERVER_ERROR, ()).into_response() } diff --git a/src/main.rs b/src/main.rs index d6e4a9f..426ef26 100644 --- a/src/main.rs +++ b/src/main.rs @@ -37,8 +37,6 @@ pub async fn init_app() -> Result { let tera = Tera::new("templates/**/*")?; let posts = post::load_all().await?; - let posts_router = post::build_router(posts.values()); - let state = Arc::new(State { tera, posts }); let middleware = tower::ServiceBuilder::new() @@ -50,10 +48,30 @@ pub async fn init_app() -> Result { .route("/", get(handlers::index)) .nest( "/posts", - posts_router.fallback_service(tower_http::services::ServeDir::new("./posts")), + post::router(), ) .nest_service("/static", tower_http::services::ServeDir::new("./static")) + .route("/.healthcheck", get(healthcheck)) .layer(middleware); Ok(app) } + +async fn healthcheck() -> &'static str { + "OK" +} + +#[derive(Debug)] +pub enum WebsiteError { + NotFound, + InternalError(anyhow::Error), +} + +impl From for WebsiteError +where + E: Into, +{ + fn from(value: E) -> Self { + WebsiteError::InternalError(value.into()) + } +} diff --git a/src/post.rs b/src/post.rs index 1222015..7b9dbf4 100644 --- a/src/post.rs +++ b/src/post.rs @@ -1,10 +1,9 @@ use std::{collections::HashMap, path::Path, sync::Arc}; -use axum::{response::Html, routing::get, Extension, Router}; +use axum::{response::Html, routing::get, Extension, Router, extract}; use chrono::{DateTime, FixedOffset}; use glob::glob; -use hyper::Uri; use lazy_static::lazy_static; use pulldown_cmark::{html, Options, Parser}; use regex::Regex; @@ -13,26 +12,40 @@ use tokio::fs; use tracing::{instrument, log::*}; -use crate::{handlers, State}; +use crate::{State, WebsiteError}; -#[derive(Serialize, Deserialize, Clone, Debug)] -pub struct FrontMatter { - pub title: String, - pub date: DateTime, -} - -#[derive(Deserialize, Debug)] +#[derive(Deserialize, Debug, Default)] pub struct TomlFrontMatter { pub title: String, - pub date: toml::value::Datetime, + pub date: Option, + pub draft: Option, + pub aliases: Option>, + pub tags: Option>, } #[derive(Serialize, Clone, Debug)] pub struct Post { + pub title: String, + pub date: Option>, + pub aliases: Vec, + pub tags: Vec, pub content: String, pub slug: String, pub absolute_path: String, - pub frontmatter: FrontMatter, +} + +impl Post { + pub fn new(slug: String, content: String, fm: TomlFrontMatter) -> Post { + Post { + absolute_path: format!("/posts/{}/", slug), + slug, + content, + title: fm.title, + date: fm.date.map(|d| DateTime::parse_from_rfc3339(&d.to_string()).expect("bad toml datetime")), + aliases: fm.aliases.unwrap_or_default(), + tags: fm.tags.unwrap_or_default(), + } + } } #[instrument] @@ -42,26 +55,26 @@ pub async fn load_all() -> color_eyre::eyre::Result> { let path = path.unwrap(); debug!("found page: {}", path.display()); - let post = load_post(&path.to_string_lossy()).await?; + let path = path.to_string_lossy().replace('\\', "/"); + let slug = path + .trim_start_matches("posts") + .trim_start_matches('/') + .trim_start_matches('\\') + .trim_end_matches(".html") + .trim_end_matches(".md") + .trim_end_matches("index") + .trim_end_matches('\\') + .trim_end_matches('/'); - res.insert(post.slug.clone(), post); + let post = load_post(slug).await?; + + res.insert(slug.to_string(), post); } Ok(res) } #[instrument] -pub async fn load_post(path: &str) -> color_eyre::eyre::Result { - let path = path.replace('\\', "/"); - let slug = path - .trim_start_matches("posts") - .trim_start_matches('/') - .trim_start_matches('\\') - .trim_end_matches(".html") - .trim_end_matches(".md") - .trim_end_matches("index") - .trim_end_matches('\\') - .trim_end_matches('/'); - +pub async fn load_post(slug: &str) -> color_eyre::eyre::Result { debug!("loading post: {slug}"); let file_path = Path::new("posts").join(slug); @@ -83,25 +96,7 @@ pub async fn load_post(path: &str) -> color_eyre::eyre::Result { content_html }); - let date = toml_date_to_chrono(tomlfm.date)?; - - let frontmatter = FrontMatter { - title: tomlfm.title, - date, - }; - - Ok(Post { - absolute_path: format!("/posts/{}/", slug), - slug: slug.to_string(), - content: content.unwrap_or_default(), - frontmatter, - }) -} - -fn toml_date_to_chrono( - toml: toml::value::Datetime, -) -> color_eyre::eyre::Result> { - Ok(DateTime::parse_from_rfc3339(&toml.to_string())?) + Ok(Post::new(slug.to_string(), content.unwrap_or_default(), tomlfm)) } #[instrument] @@ -125,44 +120,34 @@ fn parse_frontmatter( }) } -#[instrument(skip(state, post))] -async fn render_post(state: &State, post: &Post) -> Result { +#[instrument(skip(tera, post))] +async fn render_post(tera: &tera::Tera, post: &Post) -> Result { let mut ctx = tera::Context::new(); - ctx.insert("title", &post.frontmatter.title); - ctx.insert("content", &post.content); + ctx.insert("page", &post); - state.tera.render("post.html", &ctx).map_err(|e| e.into()) + tera.render("post.html", &ctx).map_err(|e| e.into()) } -#[instrument(skip(posts))] -pub fn build_router<'a, I>(posts: I) -> Router -where - I: Iterator, +pub fn router() -> Router { - let mut router = Router::new().route("/", get(index)); - - for post in posts { - let slug = &post.slug; - let path = format!("/{slug}/"); - info!("adding post route: {path}"); - router = router.route(&path, get(view)); - } - - 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( - uri: Uri, + extract::Path(slug): extract::Path, Extension(state): Extension>, -) -> Result, handlers::Error> { - debug!("viewing post: {uri}"); +) -> Result, WebsiteError> { + debug!("viewing post: {slug}"); let post = state .posts - .get(uri.path().trim_matches('/')) - .ok_or(handlers::Error::NotFound)?; + .get(&slug) + .ok_or(WebsiteError::NotFound)?; - let res = render_post(&state, post).await?; + let res = render_post(&state.tera, post).await?; Ok(Html(res)) } @@ -170,17 +155,39 @@ pub async fn view( #[instrument(skip(state))] pub async fn index( Extension(state): Extension>, -) -> Result, handlers::Error> { +) -> Result, WebsiteError> { let mut ctx = tera::Context::new(); let mut posts = state.posts.values().collect::>(); - posts.sort_by_key(|p| &p.frontmatter.date); + posts.sort_by_key(|p| &p.date); + posts.reverse(); - ctx.insert("title", "Posts"); + ctx.insert("page.title", "Posts"); ctx.insert("posts", &posts); - let res = state.tera.render("posts_index.html", &ctx)?; + 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)] +mod tests { + use tera::Tera; + + #[tokio::test] + async fn render_all() { + let tera = Tera::new("templates/**/*").unwrap(); + let posts = super::load_all().await.unwrap(); + + for (_slug, post) in posts { + super::render_post(&tera, &post).await.unwrap(); + } + } +} diff --git a/templates/base.html b/templates/base.html index 0c1c572..fd00488 100644 --- a/templates/base.html +++ b/templates/base.html @@ -1,17 +1,17 @@ -{% include "partials/head.html" %} +{% include "partials/head.html" -%}
- {% include "partials/header.html" %} + {% include "partials/header.html" -%}

- {% block main %}{% endblock main %} + {% block main %}{% endblock main -%}

- {% include "partials/footer.html" %} + {% include "partials/footer.html" -%}
\ No newline at end of file diff --git a/templates/index.html b/templates/index.html index 0a68a36..f63c2b8 100644 --- a/templates/index.html +++ b/templates/index.html @@ -7,9 +7,9 @@
  • static content ✅
  • sass compilation
  • -
  • post metadata (frontmatter)
  • +
  • post metadata (frontmatter) ✅
  • rss/atom/jsonfeed
  • -
  • proper error handling
  • +
  • proper error handling ✅ (i guess??)
  • other pages???
{% endblock main %} \ No newline at end of file diff --git a/templates/partials/head.html b/templates/partials/head.html index a6df569..e81ee41 100644 --- a/templates/partials/head.html +++ b/templates/partials/head.html @@ -4,8 +4,8 @@ - {% if title -%} - {{ title }} | tollyx.net + {% if page.title -%} + {{ page.title }} | tollyx.net {% else -%} tollyx.net {% endif -%} diff --git a/templates/post.html b/templates/post.html index 2574d4e..80c3a41 100644 --- a/templates/post.html +++ b/templates/post.html @@ -1,5 +1,13 @@ {% extends "base.html" %} -{% block main %} - {{ content | safe }} -{% endblock main %} \ No newline at end of file +{% block main -%} +
+ {% if page.title -%} +

{{ page.title }}

+ {% endif -%} + {% if page.date -%} + Posted on + {% endif -%} + {{ page.content | safe -}} +
+{% endblock main -%} \ No newline at end of file diff --git a/templates/posts_index.html b/templates/posts_index.html index 6f099f0..110793d 100644 --- a/templates/posts_index.html +++ b/templates/posts_index.html @@ -1,15 +1,15 @@ {% extends "base.html" %} {% block main -%} -

posts

-

i occasionally write some stuff i guess

+

Posts

+

I occasionally write some stuff, it's quite rare but it does happen believe it or not.

{% endblock main -%} \ No newline at end of file