diff --git a/Cargo.lock b/Cargo.lock index f733ef9..c7975db 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,61 @@ version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" +[[package]] +name = "askama" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b79091df18a97caea757e28cd2d5fda49c6cd4bd01ddffd7ff01ace0c0ad2c28" +dependencies = [ + "askama_derive", + "askama_escape", + "humansize", + "num-traits", + "percent-encoding", +] + +[[package]] +name = "askama_axum" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a41603f7cdbf5ac4af60760f17253eb6adf6ec5b6f14a7ed830cf687d375f163" +dependencies = [ + "askama", + "axum-core", + "http", +] + +[[package]] +name = "askama_derive" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19fe8d6cb13c4714962c072ea496f3392015f0989b1a2847bb4b2d9effd71d83" +dependencies = [ + "askama_parser", + "basic-toml", + "mime", + "mime_guess", + "proc-macro2", + "quote", + "serde", + "syn", +] + +[[package]] +name = "askama_escape" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341" + +[[package]] +name = "askama_parser" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acb1161c6b64d1c3d83108213c2a2533a342ac225aabd0bda218278c2ddb00c0" +dependencies = [ + "nom", +] + [[package]] name = "async-trait" version = "0.1.89" @@ -25,6 +80,12 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + [[package]] name = "axum" version = "0.7.9" @@ -80,6 +141,15 @@ dependencies = [ "tracing", ] +[[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.11.0" @@ -132,6 +202,12 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + [[package]] name = "futures-task" version = "0.3.32" @@ -183,6 +259,12 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "http-range-header" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9171a2ea8a68358193d15dd5d70c1c10a2afc3e7e4c5bc92bc9f025cebd7359c" + [[package]] name = "httparse" version = "1.10.1" @@ -195,6 +277,15 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +[[package]] +name = "humansize" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cb51c9a029ddc91b07a787f1d86b53ccfa49b0e86688c946ebe8d3555685dd7" +dependencies = [ + "libm", +] + [[package]] name = "hyper" version = "1.9.0" @@ -242,6 +333,12 @@ version = "0.2.184" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48f5d2a454e16a5ea0f4ced81bd44e4cfc7bd3a507b61887c99fd3538b28e4af" +[[package]] +name = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + [[package]] name = "lock_api" version = "0.4.14" @@ -275,6 +372,22 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "mio" version = "1.2.0" @@ -286,6 +399,25 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + [[package]] name = "once_cell" version = "1.21.4" @@ -371,8 +503,11 @@ name = "sarmentine" version = "0.1.0" dependencies = [ "anyhow", + "askama", + "askama_axum", "axum", "tokio", + "tower-http", ] [[package]] @@ -388,6 +523,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" dependencies = [ "serde_core", + "serde_derive", ] [[package]] @@ -523,6 +659,19 @@ dependencies = [ "syn", ] +[[package]] +name = "tokio-util" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + [[package]] name = "tower" version = "0.5.3" @@ -539,6 +688,31 @@ dependencies = [ "tracing", ] +[[package]] +name = "tower-http" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" +dependencies = [ + "bitflags", + "bytes", + "futures-util", + "http", + "http-body", + "http-body-util", + "http-range-header", + "httpdate", + "mime", + "mime_guess", + "percent-encoding", + "pin-project-lite", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "tower-layer" version = "0.3.3" @@ -571,6 +745,12 @@ dependencies = [ "once_cell", ] +[[package]] +name = "unicase" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" + [[package]] name = "unicode-ident" version = "1.0.24" diff --git a/Cargo.toml b/Cargo.toml index d536762..8f5f657 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,9 @@ edition = "2024" [dependencies] axum = "0.7" tokio = { version = "1", features = ["full"] } +askama = { version = "0.12", features = ["with-axum"] } +askama_axum = "0.4" +tower-http = { version = "0.5", features = ["fs"] } # Error handling anyhow = "1" diff --git a/src/main.rs b/src/main.rs index 39df257..6f2abe0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,33 @@ +use askama::Template; +use askama_axum::IntoResponse; use axum::{routing::get, Router}; +use tower_http::services::ServeDir; + +// Represents the currently logged-in user, passed into every template. +// None = logged out. +pub struct CurrentUser { + pub username: String, +} + +#[derive(Template)] +#[template(path = "index.html")] +struct IndexTemplate { + title: String, + current_user: Option, +} + +async fn index() -> impl IntoResponse { + IndexTemplate { + title: "home".into(), + current_user: None, // no auth yet + } +} #[tokio::main] async fn main() { let app = Router::new() - .route("/", get(|| async { "sarmentine" })); + .route("/", get(index)) + .nest_service("/static", ServeDir::new("static")); let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") .await diff --git a/static/css/style.css b/static/css/style.css new file mode 100644 index 0000000..ecc851d --- /dev/null +++ b/static/css/style.css @@ -0,0 +1,295 @@ +* { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +body { + background: #4a4060; +} + +pre { + overflow-x: auto; +} + +.site { + background: #fdf6e3; + color: #4a4060; + font-family: 'VT323', monospace; + font-size: 18px; + min-height: 100vh; + border: 3px ridge #d4879c; + position: relative; + overflow: hidden; + max-width: 960px; + margin: 0 auto; +} + +.tiled-bg { + position: absolute; + inset: 0; + background-image: radial-gradient(circle, #d4879c22 1px, transparent 1px); + background-size: 20px 20px; + pointer-events: none; +} + +.nav { + background: #4a4060; + display: flex; + border-bottom: 3px solid #d4879c; + font-family: 'Silkscreen', monospace; + font-size: 11px; + position: relative; + z-index: 1; +} + +.nav a { + color: #fdf6e3; + padding: 6px 14px; + text-decoration: none; + border-right: 1px solid #6b5b8a; + display: block; +} + +.nav a:hover { + background: #d4879c; +} + +.layout { + display: flex; + position: relative; + z-index: 1; +} + +.sidebar { + width: 120px; + flex-shrink: 0; + border-right: 2px solid #d4879c; + padding: 12px 8px; + background: #fdf6e3cc; + display: flex; + flex-direction: column; + gap: 12px; +} +.sidebar-box { + border: 2px solid #b48ead; + background: #fff8f0; + padding: 8px; + font-size: 14px; + line-height: 1.4; +} + +.sidebar-box-title { + font-family: 'Silkscreen', monospace; + font-size: 10px; + color: #d4879c; + border-bottom: 1px solid #d4879c; + margin-bottom: 6px; + padding-bottom: 3px; + text-transform: uppercase; +} + +.sidebar-text { + font-size: 14px; + color: #4a4060; + line-height: 1.5; +} + +.sidebar-meta { + font-size: 12px; + color: #b48ead; + margin-bottom: 3px; +} + +.visitor-counter { + font-family: 'Silkscreen', monospace; + font-size: 10px; + color: #4a4060; + background: #ebcb8b; + border: 2px inset #4a4060; + padding: 2px 6px; + display: inline-block; + letter-spacing: 2px; + margin-top: 4px; +} + +.now-playing { + font-size: 13px; + color: #4a4060; + line-height: 1.5; +} + +.marquee-wrap { + overflow: hidden; + border: 1px solid #d4879c; + background: #4a4060; + padding: 2px 0; + margin-top: 4px; +} + +.marquee-inner { + display: inline-block; + color: #ebcb8b; + font-family: 'Silkscreen', monospace; + font-size: 9px; + white-space: nowrap; + animation: marquee 12s linear infinite; +} + +@keyframes marquee { + from { transform: translateX(160px); } + to { transform: translateX(-100%); } +} + +.blinkie { + display: inline-block; + background: #d4879c; + color: #fdf6e3; + font-family: 'Silkscreen', monospace; + font-size: 9px; + padding: 2px 4px; + animation: blink 1s step-end infinite; + width: 100%; + text-align: center; +} + +@keyframes blink { + 50% { background: #b48ead; } +} + +.main { + flex: 1; + max-width: 100%; + min-width: 0; + height: auto; + padding: 16px; + background: #fdf6e3ee; +} +.main img { + max-width: 100%; + height: auto; +} + +.site-title { + font-family: 'VT323', monospace; + font-size: 42px; + color: #d4879c; + text-shadow: 2px 2px 0 #b48ead, 4px 4px 0 #ebcb8b; + line-height: 1; + margin-bottom: 4px; +} + +.site-subtitle { + font-family: 'Silkscreen', monospace; + font-size: 10px; + color: #b48ead; + margin-bottom: 16px; + letter-spacing: 1px; +} + +.divider { + border: none; + border-top: 2px dashed #d4879c; + margin: 12px 0; +} + +.post { + border: 2px solid #b48ead; + background: #fff8f0; + padding: 10px 12px; + margin-bottom: 10px; +} + +.post-title { + font-family: 'Silkscreen', monospace; + font-size: 12px; + color: #d4879c; + margin-bottom: 4px; + text-decoration: none; + display: block; +} + +.post-title:hover { + color: #b48ead; +} + +.post-meta { + font-size: 13px; + color: #b48ead; + margin-bottom: 6px; +} + +.post-body { + font-size: 16px; + color: #4a4060; + line-height: 1.3; +} + +.tag { + display: inline-block; + background: #b48ead; + color: #fdf6e3; + font-family: 'Silkscreen', monospace; + font-size: 9px; + padding: 2px 5px; + margin-right: 3px; +} + +.status-bar { + background: #4a4060; + color: #ebcb8b; + font-family: 'Silkscreen', monospace; + font-size: 9px; + padding: 3px 8px; + display: flex; + justify-content: space-between; + border-top: 2px solid #d4879c; + position: relative; + z-index: 1; +} + +.button-large { + image-rendering: pixelated; + width: calc(88px * 2); + height: calc(31px * 2); +} + +/* nav right (login/logout) */ +.nav { + justify-content: flex-start; +} + +.nav-right { + margin-left: auto; + display: flex; +} + +.nav-right a, +.nav-logout { + color: #fdf6e3; + padding: 6px 14px; + text-decoration: none; + border-left: 1px solid #6b5b8a; + display: block; + font-family: 'Silkscreen', monospace; + font-size: 11px; + background: none; + border-top: none; + border-right: none; + border-bottom: none; + cursor: pointer; +} + +.nav-right a:hover, +.nav-logout:hover { + background: #d4879c; +} + +/* layout: no sidebar */ +.layout { + display: block; +} + +.main { + max-width: 100%; +} diff --git a/templates/base.html b/templates/base.html new file mode 100644 index 0000000..62d6752 --- /dev/null +++ b/templates/base.html @@ -0,0 +1,43 @@ + + + + + + {{ title }} :: sarmentine + + + + + +
+
+ + + +
+
+ {% block content %}{% endblock %} +
+
+ +
+ rhizomatic queer ascendance + toasterdragon.com +
+
+ + diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..32f6a0f --- /dev/null +++ b/templates/index.html @@ -0,0 +1,15 @@ +{% extends "base.html" %} + +{% block content %} +
sarmentine
+
:: who was that? ::
+ +
+ +
recent threads
+ +
+
nothing here yet...
+
be the first to post :3
+
+{% endblock %}