From 56035fab3090bda944f63ae1b8920381fb0e9f88 Mon Sep 17 00:00:00 2001 From: Alphonse Paix Date: Tue, 16 Sep 2025 01:47:18 +0200 Subject: [PATCH] Askama + htmx for frontend Server-side rendering with htmx and Tailwind CSS for the styling --- Cargo.lock | 52 +++++++++++ Cargo.toml | 1 + configuration/local.yaml | 2 +- src/routes/home.rs | 12 ++- src/routes/home/home.html | 15 --- src/routes/login.rs | 12 ++- src/routes/login/login.html | 16 ---- templates/base.html | 81 ++++++++++++++++ templates/home.html | 180 ++++++++++++++++++++++++++++++++++++ templates/login.html | 69 ++++++++++++++ 10 files changed, 403 insertions(+), 37 deletions(-) delete mode 100644 src/routes/home/home.html delete mode 100644 src/routes/login/login.html create mode 100644 templates/base.html create mode 100644 templates/home.html create mode 100644 templates/login.html diff --git a/Cargo.lock b/Cargo.lock index 1422a73..b134ba5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -90,6 +90,48 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d902e3d592a523def97af8f317b08ce16b7ab854c1985a0c671e6f15cebc236" +[[package]] +name = "askama" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f75363874b771be265f4ffe307ca705ef6f3baa19011c149da8674a87f1b75c4" +dependencies = [ + "askama_derive", + "itoa", + "percent-encoding", + "serde", + "serde_json", +] + +[[package]] +name = "askama_derive" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "129397200fe83088e8a68407a8e2b1f826cf0086b21ccdb866a722c8bcd3a94f" +dependencies = [ + "askama_parser", + "basic-toml", + "memchr", + "proc-macro2", + "quote", + "rustc-hash", + "serde", + "serde_derive", + "syn", +] + +[[package]] +name = "askama_parser" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6ab5630b3d5eaf232620167977f95eb51f3432fc76852328774afbd242d4358" +dependencies = [ + "memchr", + "serde", + "serde_derive", + "winnow", +] + [[package]] name = "assert-json-diff" version = "2.0.2" @@ -295,6 +337,15 @@ version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" +[[package]] +name = "basic-toml" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba62675e8242a4c4e806d12f11d136e626e6c8361d6b829310732241652a178a" +dependencies = [ + "serde", +] + [[package]] name = "bitflags" version = "2.9.2" @@ -3840,6 +3891,7 @@ version = "0.1.0" dependencies = [ "anyhow", "argon2", + "askama", "axum", "axum-extra", "axum-messages", diff --git a/Cargo.toml b/Cargo.toml index 5590256..757d35f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ name = "zero2prod" [dependencies] anyhow = "1.0.99" argon2 = { version = "0.5.3", features = ["std"] } +askama = "0.14.0" axum = { version = "0.8.4", features = ["macros"] } axum-extra = { version = "0.10.1", features = ["query", "cookie"] } axum-messages = "0.8.0" diff --git a/configuration/local.yaml b/configuration/local.yaml index 77730bb..77ba79e 100644 --- a/configuration/local.yaml +++ b/configuration/local.yaml @@ -1,5 +1,5 @@ application: - port: 8000 + port: 8001 host: "127.0.0.1" base_url: "http://127.0.0.1:8000" database: diff --git a/src/routes/home.rs b/src/routes/home.rs index 4610d1e..8de5fb4 100644 --- a/src/routes/home.rs +++ b/src/routes/home.rs @@ -1,5 +1,11 @@ -use axum::response::{Html, IntoResponse}; +use askama::Template; +use axum::response::Html; -pub async fn home() -> impl IntoResponse { - Html(include_str!("home/home.html")) +#[derive(Template)] +#[template(path = "../templates/home.html")] +struct HomeTemplate; + +pub async fn home() -> Html { + let template = HomeTemplate; + Html(template.render().unwrap()) } diff --git a/src/routes/home/home.html b/src/routes/home/home.html deleted file mode 100644 index bc14f35..0000000 --- a/src/routes/home/home.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - Home - - -

Welcome to our newsletter!

-
    -
  1. Admin login
  2. -
  3. Register
  4. -
- - diff --git a/src/routes/login.rs b/src/routes/login.rs index 05f7c5b..ab16eb8 100644 --- a/src/routes/login.rs +++ b/src/routes/login.rs @@ -4,6 +4,7 @@ use crate::{ session_state::TypedSession, startup::AppState, }; +use askama::Template; use axum::{ Form, Json, extract::State, @@ -50,18 +51,25 @@ impl IntoResponse for LoginError { } } +#[derive(Template)] +#[template(path = "../templates/login.html")] +struct LoginTemplate { + error_html: String, +} + #[derive(serde::Deserialize)] pub struct LoginFormData { username: String, password: SecretString, } -pub async fn get_login(messages: Messages) -> impl IntoResponse { +pub async fn get_login(messages: Messages) -> Html { let mut error_html = String::new(); for message in messages { writeln!(error_html, "

{}

", message).unwrap(); } - Html(format!(include_str!("login/login.html"), error_html)) + let template = LoginTemplate { error_html }; + Html(template.render().unwrap()) } pub async fn post_login( diff --git a/src/routes/login/login.html b/src/routes/login/login.html deleted file mode 100644 index 80e94e0..0000000 --- a/src/routes/login/login.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - Login - - -
- - - -
- {} - - diff --git a/templates/base.html b/templates/base.html new file mode 100644 index 0000000..f9eded3 --- /dev/null +++ b/templates/base.html @@ -0,0 +1,81 @@ + + + + + + {% block title %}zero2prod{% endblock %} + + + + +
+ +
+ +
+
+
+ {% block content %}{% endblock %} +
+
+
+ + + + diff --git a/templates/home.html b/templates/home.html new file mode 100644 index 0000000..38dba5f --- /dev/null +++ b/templates/home.html @@ -0,0 +1,180 @@ +{% extends "base.html" %} {% block title %}Home - zero2prod{% endblock %} {% +block content %} +
+
+
+

zero2prod

+

+ Welcome to my newsletter! Stay updated on my latest projects and + thoughts. Unsubscribe at any time. +

+ +
+
+ + +
+
+
+ + + +
+

Idempotent

+

+ Smart duplicate prevention ensures you'll never receive the same email + twice. +

+
+ +
+
+ + + +
+

Privacy first

+

+ Zero spam, zero tracking, zero data sharing. Your email stays private + and secure. Unsubscribe at any time. +

+
+ +
+
+ + + +
+

Quality content

+

+ Curated insights on Rust backend development, performance tips, and + production war stories. +

+
+
+ +
+
+

Stay updated

+

+ Subscribe to my newsletter to get the latest updates. +

+ +
+
+ + +
+
+ +
+
+
+ +
+

Stats

+
+
+
+ 2 +
+
subscribers
+
+
+
+
23
+
emails sent
+
+
+
+
0
+
email opened
+
+
+
~1ms
+
response time
+
+
+
+ + + {% endblock %} +
diff --git a/templates/login.html b/templates/login.html new file mode 100644 index 0000000..be1d13f --- /dev/null +++ b/templates/login.html @@ -0,0 +1,69 @@ +{% extends "base.html" %} {% block title %}Login - zero2prod{% endblock %} {% +block content %} +
+
+
+

Login

+

+ Sign in to access the admin dashboard +

+
+ +
+
+
+ + +
+ +
+ + +
+ +
+ +
+
+ +
{{ error_html }}
+
+ + +
+
+{% endblock %}