From e036304b78f8c821e324f9f3cd8b91c7c6cc4d36 Mon Sep 17 00:00:00 2001 From: Butter Date: Mon, 4 May 2026 13:26:33 -0400 Subject: [PATCH] added docker, ci/cd, threads, posts, profiles --- .dockerignore | 7 + .gitea/workflows/ci.yml | 93 ++ ...0d260d8bb01fb891509bf95561d72c3376f98.json | 50 + ...a372b07872cd763a48f3a035e7e5c5a70c879.json | 50 + ...322a76e5f77c663cdbb433d7985fb5528af67.json | 50 + ...382de37f2b57bcfccc5f05b4a59ace6a60872.json | 50 + Cargo.lock | 1475 +---------------- Cargo.toml | 3 - Dockerfile | 27 + migrations/0002_threads.sql | 8 + migrations/0003_posts.sql | 8 + src/db.rs | 2 +- src/handlers/auth.rs | 14 +- src/handlers/mod.rs | 1 + src/handlers/thread.rs | 301 ++++ src/main.rs | 49 +- src/models/mod.rs | 2 + src/models/post.rs | 42 + src/models/thread.rs | 63 + src/models/user.rs | 18 +- templates/profile.html | 27 + templates/threads/list.html | 23 + templates/threads/new.html | 23 + templates/threads/view.html | 44 + 24 files changed, 924 insertions(+), 1506 deletions(-) create mode 100644 .dockerignore create mode 100644 .gitea/workflows/ci.yml create mode 100644 .sqlx/query-4a1b2d9c1c66741913ff3a9ddf50d260d8bb01fb891509bf95561d72c3376f98.json create mode 100644 .sqlx/query-6549001e2a522da8788d77d915ca372b07872cd763a48f3a035e7e5c5a70c879.json create mode 100644 .sqlx/query-c40c4444269dd206d0dc0618d8a322a76e5f77c663cdbb433d7985fb5528af67.json create mode 100644 .sqlx/query-ce4cb40fa25111c00d2f4575ce0382de37f2b57bcfccc5f05b4a59ace6a60872.json create mode 100644 Dockerfile create mode 100644 migrations/0002_threads.sql create mode 100644 migrations/0003_posts.sql create mode 100644 src/handlers/thread.rs create mode 100644 src/models/post.rs create mode 100644 src/models/thread.rs create mode 100644 templates/profile.html create mode 100644 templates/threads/list.html create mode 100644 templates/threads/new.html create mode 100644 templates/threads/view.html diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..feccaa1 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,7 @@ +/target +.git +*.db +*.db-* +.env +.env.* +.dockerignore diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml new file mode 100644 index 0000000..b5f35e1 --- /dev/null +++ b/.gitea/workflows/ci.yml @@ -0,0 +1,93 @@ +name: CI + +on: + push: + branches: [master] + pull_request: + branches: [master] + +env: + CARGO_TERM_COLOR: always + +jobs: + check: + name: Check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable + + - name: Cache dependencies + uses: Swatinem/rust-cache@v2 + + - name: Check formatting + run: cargo fmt --check + + - name: Clippy + run: cargo clippy -- -D warnings + + - name: Build + run: cargo build + + - name: Test + run: cargo test + + docker: + name: Build & Push Docker Image + runs-on: ubuntu-latest + needs: check + if: github.event_name == 'push' && github.ref == 'refs/heads/master' + steps: + - uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to Gitea Registry + uses: docker/login-action@v3 + with: + registry: git.toasterdragon.com + username: ${{ secrets.REGISTRY_USERNAME }} + password: ${{ secrets.REGISTRY_TOKEN }} + + - name: Extract metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: git.toasterdragon.com/butter/sarmentine + tags: | + type=sha + type=raw,value=latest + + - name: Build and push + uses: docker/build-push-action@v6 + with: + context: . + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + + deploy: + name: Deploy to Rocky + runs-on: ubuntu-latest + needs: docker + if: github.event_name == 'push' && github.ref == 'refs/heads/master' + steps: + - uses: actions/checkout@v4 + + - name: Deploy via SSH + uses: appleboy/ssh-action@v1.2.0 + with: + host: ${{ secrets.DEPLOY_HOST }} + port: ${{ secrets.DEPLOY_PORT }} + username: butter + key: ${{ secrets.DEPLOY_KEY }} + script: | + cd /opt/sarmentine + docker compose pull + docker compose up -d --force-recreate + docker image prune -f diff --git a/.sqlx/query-4a1b2d9c1c66741913ff3a9ddf50d260d8bb01fb891509bf95561d72c3376f98.json b/.sqlx/query-4a1b2d9c1c66741913ff3a9ddf50d260d8bb01fb891509bf95561d72c3376f98.json new file mode 100644 index 0000000..3c612aa --- /dev/null +++ b/.sqlx/query-4a1b2d9c1c66741913ff3a9ddf50d260d8bb01fb891509bf95561d72c3376f98.json @@ -0,0 +1,50 @@ +{ + "db_name": "SQLite", + "query": "SELECT id, body, author_id, thread_id, created_at, updated_at\n FROM posts WHERE thread_id = ? ORDER BY created_at ASC", + "describe": { + "columns": [ + { + "name": "id", + "ordinal": 0, + "type_info": "Int64" + }, + { + "name": "body", + "ordinal": 1, + "type_info": "Text" + }, + { + "name": "author_id", + "ordinal": 2, + "type_info": "Int64" + }, + { + "name": "thread_id", + "ordinal": 3, + "type_info": "Int64" + }, + { + "name": "created_at", + "ordinal": 4, + "type_info": "Datetime" + }, + { + "name": "updated_at", + "ordinal": 5, + "type_info": "Datetime" + } + ], + "parameters": { + "Right": 1 + }, + "nullable": [ + false, + false, + false, + false, + false, + false + ] + }, + "hash": "4a1b2d9c1c66741913ff3a9ddf50d260d8bb01fb891509bf95561d72c3376f98" +} diff --git a/.sqlx/query-6549001e2a522da8788d77d915ca372b07872cd763a48f3a035e7e5c5a70c879.json b/.sqlx/query-6549001e2a522da8788d77d915ca372b07872cd763a48f3a035e7e5c5a70c879.json new file mode 100644 index 0000000..07234d8 --- /dev/null +++ b/.sqlx/query-6549001e2a522da8788d77d915ca372b07872cd763a48f3a035e7e5c5a70c879.json @@ -0,0 +1,50 @@ +{ + "db_name": "SQLite", + "query": "SELECT id, title, body, author_id, created_at, updated_at\n FROM threads WHERE id = ?", + "describe": { + "columns": [ + { + "name": "id", + "ordinal": 0, + "type_info": "Int64" + }, + { + "name": "title", + "ordinal": 1, + "type_info": "Text" + }, + { + "name": "body", + "ordinal": 2, + "type_info": "Text" + }, + { + "name": "author_id", + "ordinal": 3, + "type_info": "Int64" + }, + { + "name": "created_at", + "ordinal": 4, + "type_info": "Datetime" + }, + { + "name": "updated_at", + "ordinal": 5, + "type_info": "Datetime" + } + ], + "parameters": { + "Right": 1 + }, + "nullable": [ + false, + false, + false, + false, + false, + false + ] + }, + "hash": "6549001e2a522da8788d77d915ca372b07872cd763a48f3a035e7e5c5a70c879" +} diff --git a/.sqlx/query-c40c4444269dd206d0dc0618d8a322a76e5f77c663cdbb433d7985fb5528af67.json b/.sqlx/query-c40c4444269dd206d0dc0618d8a322a76e5f77c663cdbb433d7985fb5528af67.json new file mode 100644 index 0000000..c294a11 --- /dev/null +++ b/.sqlx/query-c40c4444269dd206d0dc0618d8a322a76e5f77c663cdbb433d7985fb5528af67.json @@ -0,0 +1,50 @@ +{ + "db_name": "SQLite", + "query": "SELECT id, title, body, author_id, created_at, updated_at\n FROM threads ORDER BY updated_at DESC", + "describe": { + "columns": [ + { + "name": "id", + "ordinal": 0, + "type_info": "Int64" + }, + { + "name": "title", + "ordinal": 1, + "type_info": "Text" + }, + { + "name": "body", + "ordinal": 2, + "type_info": "Text" + }, + { + "name": "author_id", + "ordinal": 3, + "type_info": "Int64" + }, + { + "name": "created_at", + "ordinal": 4, + "type_info": "Datetime" + }, + { + "name": "updated_at", + "ordinal": 5, + "type_info": "Datetime" + } + ], + "parameters": { + "Right": 0 + }, + "nullable": [ + false, + false, + false, + false, + false, + false + ] + }, + "hash": "c40c4444269dd206d0dc0618d8a322a76e5f77c663cdbb433d7985fb5528af67" +} diff --git a/.sqlx/query-ce4cb40fa25111c00d2f4575ce0382de37f2b57bcfccc5f05b4a59ace6a60872.json b/.sqlx/query-ce4cb40fa25111c00d2f4575ce0382de37f2b57bcfccc5f05b4a59ace6a60872.json new file mode 100644 index 0000000..4e15a08 --- /dev/null +++ b/.sqlx/query-ce4cb40fa25111c00d2f4575ce0382de37f2b57bcfccc5f05b4a59ace6a60872.json @@ -0,0 +1,50 @@ +{ + "db_name": "SQLite", + "query": "SELECT id, title, body, author_id, created_at, updated_at\n FROM threads WHERE author_id = ? ORDER BY updated_at DESC", + "describe": { + "columns": [ + { + "name": "id", + "ordinal": 0, + "type_info": "Int64" + }, + { + "name": "title", + "ordinal": 1, + "type_info": "Text" + }, + { + "name": "body", + "ordinal": 2, + "type_info": "Text" + }, + { + "name": "author_id", + "ordinal": 3, + "type_info": "Int64" + }, + { + "name": "created_at", + "ordinal": 4, + "type_info": "Datetime" + }, + { + "name": "updated_at", + "ordinal": 5, + "type_info": "Datetime" + } + ], + "parameters": { + "Right": 1 + }, + "nullable": [ + false, + false, + false, + false, + false, + false + ] + }, + "hash": "ce4cb40fa25111c00d2f4575ce0382de37f2b57bcfccc5f05b4a59ace6a60872" +} diff --git a/Cargo.lock b/Cargo.lock index cd8e55f..007d520 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,57 +2,6 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "adler2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" - -[[package]] -name = "aead" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" -dependencies = [ - "bytes", - "crypto-common", - "generic-array", -] - -[[package]] -name = "aes" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" -dependencies = [ - "cfg-if", - "cipher", - "cpufeatures", -] - -[[package]] -name = "aes-gcm" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" -dependencies = [ - "aead", - "aes", - "cipher", - "ctr", - "ghash", - "subtle", -] - -[[package]] -name = "aes-kw" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69fa2b352dcefb5f7f3a5fb840e02665d311d878955380515e4fd50095dd3d8c" -dependencies = [ - "aes", -] - [[package]] name = "ahash" version = "0.8.12" @@ -66,15 +15,6 @@ dependencies = [ "zerocopy", ] -[[package]] -name = "aho-corasick" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" -dependencies = [ - "memchr", -] - [[package]] name = "allocator-api2" version = "0.2.21" @@ -90,56 +30,6 @@ dependencies = [ "libc", ] -[[package]] -name = "anstream" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "is_terminal_polyfill", - "utf8parse", -] - -[[package]] -name = "anstyle" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" - -[[package]] -name = "anstyle-parse" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" -dependencies = [ - "anstyle", - "once_cell_polyfill", - "windows-sys 0.61.2", -] - [[package]] name = "anyhow" version = "1.0.102" @@ -156,7 +46,6 @@ dependencies = [ "blake2", "cpufeatures", "password-hash", - "zeroize", ] [[package]] @@ -211,7 +100,7 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acb1161c6b64d1c3d83108213c2a2533a342ac225aabd0bda218278c2ddb00c0" dependencies = [ - "nom 7.1.3", + "nom", ] [[package]] @@ -301,12 +190,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "base16ct" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" - [[package]] name = "base64" version = "0.21.7" @@ -334,27 +217,6 @@ dependencies = [ "serde", ] -[[package]] -name = "bitfields" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef6e59298da389bc0649c7463856b34c6e17fe542f88939426ede4436c6b1195" -dependencies = [ - "bitfields-impl", -] - -[[package]] -name = "bitfields-impl" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2c044f98f86f15414668d6c8187c7e4fadab1ad2b31680f648703e0fe07c555" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", - "thiserror 2.0.18", -] - [[package]] name = "bitflags" version = "2.11.0" @@ -364,18 +226,6 @@ dependencies = [ "serde_core", ] -[[package]] -name = "bitvec" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" -dependencies = [ - "funty", - "radium", - "tap", - "wyz", -] - [[package]] name = "blake2" version = "0.10.6" @@ -394,34 +244,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "block-padding" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" -dependencies = [ - "generic-array", -] - -[[package]] -name = "blowfish" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e412e2cd0f2b2d93e02543ceae7917b3c70331573df19ee046bcbc35e45e87d7" -dependencies = [ - "byteorder", - "cipher", -] - -[[package]] -name = "buffer-redux" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "431a9cc8d7efa49bc326729264537f5e60affce816c66edf434350778c9f4f54" -dependencies = [ - "memchr", -] - [[package]] name = "bumpalo" version = "3.20.2" @@ -440,60 +262,6 @@ version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" -[[package]] -name = "bzip2" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3a53fac24f34a81bc9954b5d6cfce0c21e18ec6959f44f56e8e90e4bb7c346c" -dependencies = [ - "libbz2-rs-sys", -] - -[[package]] -name = "camellia" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3264e2574e9ef2b53ce6f536dea83a69ac0bc600b762d1523ff83fe07230ce30" -dependencies = [ - "byteorder", - "cipher", -] - -[[package]] -name = "cargo-generate-rpm" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3005027bdce292635d4a3779942159f0f71085118469c2930f7901b3b2bca397" -dependencies = [ - "cargo_toml", - "clap", - "color-print", - "elf", - "glob", - "rpm", - "thiserror 2.0.18", - "toml", -] - -[[package]] -name = "cargo_toml" -version = "0.22.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "374b7c592d9c00c1f4972ea58390ac6b18cbb6ab79011f3bdc90a0b82ca06b77" -dependencies = [ - "serde", - "toml", -] - -[[package]] -name = "cast5" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b07d673db1ccf000e90f54b819db9e75a8348d6eb056e9b8ab53231b7a9911" -dependencies = [ - "cipher", -] - [[package]] name = "cc" version = "1.2.59" @@ -501,20 +269,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7a4d3ec6524d28a329fc53654bbadc9bdd7b0431f5d65f1a56ffb28a1ee5283" dependencies = [ "find-msvc-tools", - "jobserver", - "libc", "shlex", ] -[[package]] -name = "cfb-mode" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "738b8d467867f80a71351933f70461f5b56f24d5c93e0cf216e59229c968d330" -dependencies = [ - "cipher", -] - [[package]] name = "cfg-if" version = "1.0.4" @@ -535,109 +292,12 @@ dependencies = [ "windows-link", ] -[[package]] -name = "cipher" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" -dependencies = [ - "crypto-common", - "inout", -] - -[[package]] -name = "clap" -version = "4.5.61" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52fa72306bb30daf11bc97773431628e5b4916e97aaa74b7d3f625d4d495da02" -dependencies = [ - "clap_builder", - "clap_derive", -] - -[[package]] -name = "clap_builder" -version = "4.5.61" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2071365c5c56eae7d77414029dde2f4f4ba151cf68d5a3261c9a40de428ace93" -dependencies = [ - "anstream", - "anstyle", - "clap_lex", - "strsim", -] - -[[package]] -name = "clap_derive" -version = "4.5.61" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dec5be1eea072311774b7b84ded287adbd9f293f9d23456817605c6042f4f5e0" -dependencies = [ - "heck 0.5.0", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "clap_lex" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" - -[[package]] -name = "cmac" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8543454e3c3f5126effff9cd44d562af4e31fb8ce1cc0d3dcd8f084515dbc1aa" -dependencies = [ - "cipher", - "dbl", - "digest", -] - -[[package]] -name = "color-print" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3aa954171903797d5623e047d9ab69d91b493657917bdfb8c2c80ecaf9cdb6f4" -dependencies = [ - "color-print-proc-macro", -] - -[[package]] -name = "color-print-proc-macro" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "692186b5ebe54007e45a59aea47ece9eb4108e141326c304cdc91699a7118a22" -dependencies = [ - "nom 7.1.3", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "colorchoice" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" - [[package]] name = "const-oid" version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" -[[package]] -name = "convert_case" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" -dependencies = [ - "unicode-segmentation", -] - [[package]] name = "cookie" version = "0.18.1" @@ -684,12 +344,6 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" -[[package]] -name = "crc24" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd121741cf3eb82c08dd3023eb55bf2665e5f60ec20f89760cf836ae4562e6a0" - [[package]] name = "crossbeam-queue" version = "0.3.12" @@ -705,18 +359,6 @@ version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" -[[package]] -name = "crypto-bigint" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" -dependencies = [ - "generic-array", - "rand_core", - "subtle", - "zeroize", -] - [[package]] name = "crypto-common" version = "0.1.7" @@ -724,107 +366,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" dependencies = [ "generic-array", - "rand_core", "typenum", ] -[[package]] -name = "ctr" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" -dependencies = [ - "cipher", -] - -[[package]] -name = "curve25519-dalek" -version = "4.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" -dependencies = [ - "cfg-if", - "cpufeatures", - "curve25519-dalek-derive", - "digest", - "fiat-crypto", - "rustc_version", - "subtle", - "zeroize", -] - -[[package]] -name = "curve25519-dalek-derive" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "cx448" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4c0cf476284b03eb6c10e78787b21c7abb7d7d43cb2f02532ba6b831ed892fa" -dependencies = [ - "crypto-bigint", - "elliptic-curve", - "pkcs8", - "rand_core", - "serdect 0.3.0", - "sha3", - "signature", - "subtle", - "zeroize", -] - -[[package]] -name = "darling" -version = "0.20.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" -dependencies = [ - "darling_core", - "darling_macro", -] - -[[package]] -name = "darling_core" -version = "0.20.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn 2.0.117", -] - -[[package]] -name = "darling_macro" -version = "0.20.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" -dependencies = [ - "darling_core", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "dbl" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd2735a791158376708f9347fe8faba9667589d82427ef3aed6794a8981de3d9" -dependencies = [ - "generic-array", -] - [[package]] name = "der" version = "0.7.10" @@ -846,69 +390,6 @@ dependencies = [ "serde_core", ] -[[package]] -name = "derive_builder" -version = "0.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947" -dependencies = [ - "derive_builder_macro", -] - -[[package]] -name = "derive_builder_core" -version = "0.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" -dependencies = [ - "darling", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "derive_builder_macro" -version = "0.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" -dependencies = [ - "derive_builder_core", - "syn 2.0.117", -] - -[[package]] -name = "derive_more" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" -dependencies = [ - "derive_more-impl", -] - -[[package]] -name = "derive_more-impl" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" -dependencies = [ - "convert_case", - "proc-macro2", - "quote", - "rustc_version", - "syn 2.0.117", - "unicode-xid", -] - -[[package]] -name = "des" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffdd80ce8ce993de27e9f063a444a4d53ce8e8db4c1f00cc03af5ad5a9867a1e" -dependencies = [ - "cipher", -] - [[package]] name = "digest" version = "0.10.7" @@ -938,74 +419,6 @@ version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" -[[package]] -name = "dsa" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48bc224a9084ad760195584ce5abb3c2c34a225fa312a128ad245a6b412b7689" -dependencies = [ - "digest", - "num-bigint-dig", - "num-traits", - "pkcs8", - "rfc6979", - "sha2", - "signature", - "zeroize", -] - -[[package]] -name = "eax" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9954fabd903b82b9d7a68f65f97dc96dd9ad368e40ccc907a7c19d53e6bfac28" -dependencies = [ - "aead", - "cipher", - "cmac", - "ctr", - "subtle", -] - -[[package]] -name = "ecdsa" -version = "0.16.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" -dependencies = [ - "der", - "digest", - "elliptic-curve", - "rfc6979", - "signature", - "spki", -] - -[[package]] -name = "ed25519" -version = "2.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" -dependencies = [ - "pkcs8", - "signature", -] - -[[package]] -name = "ed25519-dalek" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" -dependencies = [ - "curve25519-dalek", - "ed25519", - "rand_core", - "serde", - "sha2", - "subtle", - "zeroize", -] - [[package]] name = "either" version = "1.15.0" @@ -1015,59 +428,6 @@ dependencies = [ "serde", ] -[[package]] -name = "elf" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55dd888a213fc57e957abf2aa305ee3e8a28dbe05687a251f33b637cd46b0070" - -[[package]] -name = "elliptic-curve" -version = "0.13.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" -dependencies = [ - "base16ct", - "base64ct", - "crypto-bigint", - "digest", - "ff", - "generic-array", - "group", - "hkdf", - "pem-rfc7468", - "pkcs8", - "rand_core", - "sec1", - "serde_json", - "serdect 0.2.0", - "subtle", - "tap", - "zeroize", -] - -[[package]] -name = "enum-display-derive" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f16ef37b2a9b242295d61a154ee91ae884afff6b8b933b486b12481cc58310ca" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "enum-primitive-derive" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba7795da175654fe16979af73f81f26a8ea27638d8d9823d317016888a63dc4c" -dependencies = [ - "num-traits", - "quote", - "syn 2.0.117", -] - [[package]] name = "equivalent" version = "1.0.2" @@ -1107,39 +467,12 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" -[[package]] -name = "ff" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" -dependencies = [ - "bitvec", - "rand_core", - "subtle", -] - -[[package]] -name = "fiat-crypto" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" - [[package]] name = "find-msvc-tools" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" -[[package]] -name = "flate2" -version = "1.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" -dependencies = [ - "miniz_oxide", - "zlib-rs", -] - [[package]] name = "flume" version = "0.11.1" @@ -1151,12 +484,6 @@ dependencies = [ "spin", ] -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - [[package]] name = "foldhash" version = "0.1.5" @@ -1172,12 +499,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "funty" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" - [[package]] name = "futures" version = "0.3.32" @@ -1283,7 +604,6 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", - "zeroize", ] [[package]] @@ -1322,33 +642,6 @@ dependencies = [ "wasip3", ] -[[package]] -name = "ghash" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" -dependencies = [ - "opaque-debug", - "polyval", -] - -[[package]] -name = "glob" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" - -[[package]] -name = "group" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" -dependencies = [ - "ff", - "rand_core", - "subtle", -] - [[package]] name = "hashbrown" version = "0.14.5" @@ -1638,21 +931,6 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" -[[package]] -name = "idea" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "075557004419d7f2031b8bb7f44bb43e55a83ca7b63076a8fb8fe75753836477" -dependencies = [ - "cipher", -] - -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - [[package]] name = "idna" version = "1.1.0" @@ -1686,46 +964,12 @@ dependencies = [ "serde_core", ] -[[package]] -name = "inout" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" -dependencies = [ - "generic-array", -] - -[[package]] -name = "is_terminal_polyfill" -version = "1.70.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" - -[[package]] -name = "itertools" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" -dependencies = [ - "either", -] - [[package]] name = "itoa" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" -[[package]] -name = "jobserver" -version = "0.1.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" -dependencies = [ - "getrandom 0.3.4", - "libc", -] - [[package]] name = "js-sys" version = "0.3.94" @@ -1736,29 +980,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "k256" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" -dependencies = [ - "cfg-if", - "ecdsa", - "elliptic-curve", - "once_cell", - "sha2", - "signature", -] - -[[package]] -name = "keccak" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653" -dependencies = [ - "cpufeatures", -] - [[package]] name = "lazy_static" version = "1.5.0" @@ -1774,38 +995,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" -[[package]] -name = "libbz2-rs-sys" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3a6a8c165077efc8f3a971534c50ea6a1a18b329ef4a66e897a7e3a1494565f" - [[package]] name = "libc" version = "0.2.184" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48f5d2a454e16a5ea0f4ced81bd44e4cfc7bd3a507b61887c99fd3538b28e4af" -[[package]] -name = "liblzma" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6033b77c21d1f56deeae8014eb9fbe7bdf1765185a6c508b5ca82eeaed7f899" -dependencies = [ - "liblzma-sys", -] - -[[package]] -name = "liblzma-sys" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a60851d15cd8c5346eca4ab8babff585be2ae4bc8097c067291d3ffe2add3b6" -dependencies = [ - "cc", - "libc", - "pkg-config", -] - [[package]] name = "libm" version = "0.2.16" @@ -1907,16 +1102,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" -[[package]] -name = "miniz_oxide" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" -dependencies = [ - "adler2", - "simd-adler32", -] - [[package]] name = "mio" version = "1.2.0" @@ -1938,39 +1123,6 @@ dependencies = [ "minimal-lexical", ] -[[package]] -name = "nom" -version = "8.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" -dependencies = [ - "memchr", -] - -[[package]] -name = "num" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" -dependencies = [ - "num-bigint", - "num-complex", - "num-integer", - "num-iter", - "num-rational", - "num-traits", -] - -[[package]] -name = "num-bigint" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" -dependencies = [ - "num-integer", - "num-traits", -] - [[package]] name = "num-bigint-dig" version = "0.8.6" @@ -1983,37 +1135,16 @@ dependencies = [ "num-iter", "num-traits", "rand", - "serde", "smallvec", "zeroize", ] -[[package]] -name = "num-complex" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" -dependencies = [ - "num-traits", -] - [[package]] name = "num-conv" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967" -[[package]] -name = "num-derive" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - [[package]] name = "num-integer" version = "0.1.46" @@ -2034,17 +1165,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "num-rational" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" -dependencies = [ - "num-bigint", - "num-integer", - "num-traits", -] - [[package]] name = "num-traits" version = "0.2.19" @@ -2055,96 +1175,12 @@ dependencies = [ "libm", ] -[[package]] -name = "num_enum" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d0bca838442ec211fa11de3a8b0e0e8f3a4522575b5c4c06ed722e005036f26" -dependencies = [ - "num_enum_derive", - "rustversion", -] - -[[package]] -name = "num_enum_derive" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "ocb3" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c196e0276c471c843dd5777e7543a36a298a4be942a2a688d8111cd43390dedb" -dependencies = [ - "aead", - "cipher", - "ctr", - "subtle", -] - [[package]] name = "once_cell" version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" -[[package]] -name = "once_cell_polyfill" -version = "1.70.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" - -[[package]] -name = "opaque-debug" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" - -[[package]] -name = "p256" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" -dependencies = [ - "ecdsa", - "elliptic-curve", - "primeorder", - "sha2", -] - -[[package]] -name = "p384" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe42f1670a52a47d448f14b6a5c61dd78fce51856e68edaa38f7ae3a46b8d6b6" -dependencies = [ - "ecdsa", - "elliptic-curve", - "primeorder", - "sha2", -] - -[[package]] -name = "p521" -version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fc9e2161f1f215afdfce23677034ae137bbd45016a880c2eb3ba8eb95f085b2" -dependencies = [ - "base16ct", - "ecdsa", - "elliptic-curve", - "primeorder", - "rand_core", - "sha2", -] - [[package]] name = "parking_lot" version = "0.12.5" @@ -2200,76 +1236,6 @@ version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" -[[package]] -name = "pgp" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d918d5da2ce943e4c6088d7694f33f47c19374d6f0f2080a0c5e8010afdfd29" -dependencies = [ - "aead", - "aes", - "aes-gcm", - "aes-kw", - "argon2", - "base64 0.22.1", - "bitfields", - "block-padding", - "blowfish", - "buffer-redux", - "byteorder", - "bytes", - "bzip2", - "camellia", - "cast5", - "cfb-mode", - "chrono", - "cipher", - "const-oid", - "crc24", - "curve25519-dalek", - "cx448", - "derive_builder", - "derive_more", - "des", - "digest", - "dsa", - "eax", - "ecdsa", - "ed25519-dalek", - "elliptic-curve", - "flate2", - "generic-array", - "hex", - "hkdf", - "idea", - "k256", - "log", - "md-5", - "nom 8.0.0", - "num-bigint-dig", - "num-traits", - "num_enum", - "ocb3", - "p256", - "p384", - "p521", - "rand", - "regex", - "replace_with", - "ripemd", - "rsa", - "sha1", - "sha1-checked", - "sha2", - "sha3", - "signature", - "smallvec", - "snafu", - "twofish", - "x25519-dalek", - "zeroize", -] - [[package]] name = "pin-project-lite" version = "0.2.17" @@ -2309,18 +1275,6 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" -[[package]] -name = "polyval" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" -dependencies = [ - "cfg-if", - "cpufeatures", - "opaque-debug", - "universal-hash", -] - [[package]] name = "potential_utf" version = "0.1.5" @@ -2355,24 +1309,6 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "primeorder" -version = "0.13.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" -dependencies = [ - "elliptic-curve", -] - -[[package]] -name = "proc-macro-crate" -version = "3.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" -dependencies = [ - "toml_edit", -] - [[package]] name = "proc-macro2" version = "1.0.106" @@ -2403,12 +1339,6 @@ version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" -[[package]] -name = "radium" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" - [[package]] name = "rand" version = "0.8.5" @@ -2457,60 +1387,6 @@ dependencies = [ "bitflags", ] -[[package]] -name = "regex" -version = "1.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" - -[[package]] -name = "replace_with" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51743d3e274e2b18df81c4dc6caf8a5b8e15dbe799e0dca05c7617380094e884" - -[[package]] -name = "rfc6979" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" -dependencies = [ - "hmac", - "subtle", -] - -[[package]] -name = "ripemd" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" -dependencies = [ - "digest", -] - [[package]] name = "rmp" version = "0.8.15" @@ -2530,38 +1406,6 @@ dependencies = [ "serde", ] -[[package]] -name = "rpm" -version = "0.18.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "834393db3afd30c31d6ecde41d8dad7f081ac692f82c5a7a05fc0a0915343e6b" -dependencies = [ - "base64 0.22.1", - "bitflags", - "bzip2", - "chrono", - "digest", - "enum-display-derive", - "enum-primitive-derive", - "flate2", - "hex", - "itertools", - "liblzma", - "log", - "md-5", - "nom 8.0.0", - "num", - "num-derive", - "num-traits", - "pgp", - "sha1", - "sha2", - "sha3", - "thiserror 2.0.18", - "zeroize", - "zstd", -] - [[package]] name = "rsa" version = "0.9.10" @@ -2582,15 +1426,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "rustc_version" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" -dependencies = [ - "semver", -] - [[package]] name = "rustix" version = "1.1.4" @@ -2625,7 +1460,6 @@ dependencies = [ "askama", "askama_axum", "axum", - "cargo-generate-rpm", "chrono", "dotenvy", "rand_core", @@ -2643,21 +1477,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" -[[package]] -name = "sec1" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" -dependencies = [ - "base16ct", - "der", - "generic-array", - "pkcs8", - "serdect 0.2.0", - "subtle", - "zeroize", -] - [[package]] name = "semver" version = "1.0.28" @@ -2718,15 +1537,6 @@ dependencies = [ "serde_core", ] -[[package]] -name = "serde_spanned" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" -dependencies = [ - "serde_core", -] - [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -2739,26 +1549,6 @@ dependencies = [ "serde", ] -[[package]] -name = "serdect" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a84f14a19e9a014bb9f4512488d9829a68e04ecabffb0f9904cd1ace94598177" -dependencies = [ - "base16ct", - "serde", -] - -[[package]] -name = "serdect" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f42f67da2385b51a5f9652db9c93d78aeaf7610bf5ec366080b6de810604af53" -dependencies = [ - "base16ct", - "serde", -] - [[package]] name = "sha1" version = "0.10.6" @@ -2770,17 +1560,6 @@ dependencies = [ "digest", ] -[[package]] -name = "sha1-checked" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89f599ac0c323ebb1c6082821a54962b839832b03984598375bff3975b804423" -dependencies = [ - "digest", - "sha1", - "zeroize", -] - [[package]] name = "sha2" version = "0.10.9" @@ -2792,16 +1571,6 @@ dependencies = [ "digest", ] -[[package]] -name = "sha3" -version = "0.10.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77fd7028345d415a4034cf8777cd4f8ab1851274233b45f84e3d955502d93874" -dependencies = [ - "digest", - "keccak", -] - [[package]] name = "shlex" version = "1.3.0" @@ -2828,12 +1597,6 @@ dependencies = [ "rand_core", ] -[[package]] -name = "simd-adler32" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" - [[package]] name = "slab" version = "0.4.12" @@ -2846,27 +1609,6 @@ version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" -[[package]] -name = "snafu" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e84b3f4eacbf3a1ce05eac6763b4d629d60cbc94d632e4092c54ade71f1e1a2" -dependencies = [ - "snafu-derive", -] - -[[package]] -name = "snafu-derive" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1c97747dbf44bb1ca44a561ece23508e99cb592e862f22222dcf42f51d1e451" -dependencies = [ - "heck 0.5.0", - "proc-macro2", - "quote", - "syn 2.0.117", -] - [[package]] name = "socket2" version = "0.6.3" @@ -2902,7 +1644,7 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7bba3a93db0cc4f7bdece8bb09e77e2e785c20bfebf79eb8340ed80708048790" dependencies = [ - "nom 7.1.3", + "nom", "unicode_categories", ] @@ -2952,7 +1694,7 @@ dependencies = [ "sha2", "smallvec", "sqlformat", - "thiserror 1.0.69", + "thiserror", "time", "tokio", "tokio-stream", @@ -3037,7 +1779,7 @@ dependencies = [ "smallvec", "sqlx-core", "stringprep", - "thiserror 1.0.69", + "thiserror", "time", "tracing", "whoami", @@ -3077,7 +1819,7 @@ dependencies = [ "smallvec", "sqlx-core", "stringprep", - "thiserror 1.0.69", + "thiserror", "time", "tracing", "whoami", @@ -3125,12 +1867,6 @@ dependencies = [ "unicode-properties", ] -[[package]] -name = "strsim" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" - [[package]] name = "subtle" version = "2.6.1" @@ -3176,12 +1912,6 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "tap" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" - [[package]] name = "tempfile" version = "3.27.0" @@ -3201,16 +1931,7 @@ version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "thiserror-impl 1.0.69", -] - -[[package]] -name = "thiserror" -version = "2.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" -dependencies = [ - "thiserror-impl 2.0.18", + "thiserror-impl", ] [[package]] @@ -3224,17 +1945,6 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "thiserror-impl" -version = "2.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - [[package]] name = "time" version = "0.3.47" @@ -3343,66 +2053,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "toml" -version = "0.9.12+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863" -dependencies = [ - "indexmap", - "serde_core", - "serde_spanned", - "toml_datetime 0.7.5+spec-1.1.0", - "toml_parser", - "toml_writer", - "winnow 0.7.15", -] - -[[package]] -name = "toml_datetime" -version = "0.7.5+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" -dependencies = [ - "serde_core", -] - -[[package]] -name = "toml_datetime" -version = "1.1.1+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" -dependencies = [ - "serde_core", -] - -[[package]] -name = "toml_edit" -version = "0.25.11+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b59c4d22ed448339746c59b905d24568fcbb3ab65a500494f7b8c3e97739f2b" -dependencies = [ - "indexmap", - "toml_datetime 1.1.1+spec-1.1.0", - "toml_parser", - "winnow 1.0.2", -] - -[[package]] -name = "toml_parser" -version = "1.1.2+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" -dependencies = [ - "winnow 1.0.2", -] - -[[package]] -name = "toml_writer" -version = "1.1.1+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" - [[package]] name = "tower" version = "0.5.3" @@ -3506,7 +2156,7 @@ dependencies = [ "rand", "serde", "serde_json", - "thiserror 1.0.69", + "thiserror", "time", "tokio", "tracing", @@ -3533,7 +2183,7 @@ dependencies = [ "async-trait", "rmp-serde", "sqlx", - "thiserror 1.0.69", + "thiserror", "time", "tower-sessions-core", ] @@ -3570,15 +2220,6 @@ dependencies = [ "once_cell", ] -[[package]] -name = "twofish" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a78e83a30223c757c3947cd144a31014ff04298d8719ae10d03c31c0448c8013" -dependencies = [ - "cipher", -] - [[package]] name = "typenum" version = "1.19.0" @@ -3636,16 +2277,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" -[[package]] -name = "universal-hash" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" -dependencies = [ - "crypto-common", - "subtle", -] - [[package]] name = "url" version = "2.5.8" @@ -3669,12 +2300,6 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" -[[package]] -name = "utf8parse" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" - [[package]] name = "vcpkg" version = "0.2.15" @@ -3940,21 +2565,6 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" -[[package]] -name = "winnow" -version = "0.7.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" - -[[package]] -name = "winnow" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ee1708bef14716a11bae175f579062d4554d95be2c6829f518df847b7b3fdd0" -dependencies = [ - "memchr", -] - [[package]] name = "wit-bindgen" version = "0.51.0" @@ -4049,27 +2659,6 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" -[[package]] -name = "wyz" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" -dependencies = [ - "tap", -] - -[[package]] -name = "x25519-dalek" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" -dependencies = [ - "curve25519-dalek", - "rand_core", - "serde", - "zeroize", -] - [[package]] name = "yoke" version = "0.8.2" @@ -4139,20 +2728,6 @@ name = "zeroize" version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" -dependencies = [ - "zeroize_derive", -] - -[[package]] -name = "zeroize_derive" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] [[package]] name = "zerotrie" @@ -4187,42 +2762,8 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "zlib-rs" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be3d40e40a133f9c916ee3f9f4fa2d9d63435b5fbe1bfc6d9dae0aa0ada1513" - [[package]] name = "zmij" version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" - -[[package]] -name = "zstd" -version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" -dependencies = [ - "zstd-safe", -] - -[[package]] -name = "zstd-safe" -version = "7.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" -dependencies = [ - "zstd-sys", -] - -[[package]] -name = "zstd-sys" -version = "2.0.16+zstd.1.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748" -dependencies = [ - "cc", - "pkg-config", -] diff --git a/Cargo.toml b/Cargo.toml index 20e56c4..e47c07e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,3 @@ serde = { version = "1", features = ["derive"] } chrono = { version = "0.4", features = ["serde"] } # Error handling anyhow = "1" - -[dev-dependencies] -cargo-generate-rpm = "0.20" diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..6f3ac39 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,27 @@ +FROM rust:1.85 AS builder + +RUN apt-get update && apt-get install -y --no-install-recommends musl-tools \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /app +COPY . . + +ENV SQLX_OFFLINE=true +RUN cargo build --release --target x86_64-unknown-linux-musl + +FROM alpine:3.21 + +RUN apk add --no-cache ca-certificates + +COPY --from=builder /app/target/x86_64-unknown-linux-musl/release/sarmentine /usr/local/bin/sarmentine +COPY --from=builder /app/templates /app/templates +COPY --from=builder /app/static /app/static + +WORKDIR /app + +EXPOSE 3000 + +ENV SARMENTINE_BIND_ADDR=0.0.0.0:3000 +ENV SARMENTINE_STATIC_DIR=/app/static + +CMD ["sarmentine"] diff --git a/migrations/0002_threads.sql b/migrations/0002_threads.sql new file mode 100644 index 0000000..6caae7b --- /dev/null +++ b/migrations/0002_threads.sql @@ -0,0 +1,8 @@ +CREATE TABLE IF NOT EXISTS threads ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + title TEXT NOT NULL, + body TEXT NOT NULL, + author_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, + created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP +); diff --git a/migrations/0003_posts.sql b/migrations/0003_posts.sql new file mode 100644 index 0000000..1952992 --- /dev/null +++ b/migrations/0003_posts.sql @@ -0,0 +1,8 @@ +CREATE TABLE IF NOT EXISTS posts ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + body TEXT NOT NULL, + author_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, + thread_id INTEGER NOT NULL REFERENCES threads(id) ON DELETE CASCADE, + created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP +); diff --git a/src/db.rs b/src/db.rs index 5160efb..b8e6493 100644 --- a/src/db.rs +++ b/src/db.rs @@ -1,4 +1,4 @@ -use sqlx::{sqlite::SqlitePoolOptions, SqlitePool}; +use sqlx::{SqlitePool, sqlite::SqlitePoolOptions}; pub async fn connect(database_url: &str) -> SqlitePool { let pool = SqlitePoolOptions::new() diff --git a/src/handlers/auth.rs b/src/handlers/auth.rs index 2dca072..ba3bbd0 100644 --- a/src/handlers/auth.rs +++ b/src/handlers/auth.rs @@ -1,6 +1,6 @@ use argon2::{ - password_hash::{rand_core::OsRng, PasswordHash, PasswordHasher, PasswordVerifier, SaltString}, Argon2, + password_hash::{PasswordHash, PasswordHasher, PasswordVerifier, SaltString, rand_core::OsRng}, }; use askama::Template; use axum::{ @@ -33,14 +33,18 @@ pub struct LoginTemplate { pub error: Option, } -fn render(t: T) -> Response { +pub fn render(t: T) -> Response { match t.render() { Ok(html) => Html(html).into_response(), - Err(_) => (axum::http::StatusCode::INTERNAL_SERVER_ERROR, "render error").into_response(), + Err(_) => ( + axum::http::StatusCode::INTERNAL_SERVER_ERROR, + "render error", + ) + .into_response(), } } -// ~~ Form types +// ~~ Form types #[derive(Deserialize)] pub struct RegisterForm { @@ -55,7 +59,7 @@ pub struct LoginForm { pub password: String, } -// ~~ Handlers +// ~~ Handlers pub async fn register_page() -> Response { render(RegisterTemplate { diff --git a/src/handlers/mod.rs b/src/handlers/mod.rs index 0e4a05d..545f050 100644 --- a/src/handlers/mod.rs +++ b/src/handlers/mod.rs @@ -1 +1,2 @@ pub mod auth; +pub mod thread; diff --git a/src/handlers/thread.rs b/src/handlers/thread.rs new file mode 100644 index 0000000..556c6a0 --- /dev/null +++ b/src/handlers/thread.rs @@ -0,0 +1,301 @@ +use askama::Template; +use axum::{ + extract::{Form, Path, State}, + response::{IntoResponse, Redirect, Response}, +}; +use serde::Deserialize; +use sqlx::SqlitePool; +use tower_sessions::Session; + +use crate::CurrentUser; +use crate::handlers::auth::{get_current_user, render}; +use crate::models::{post::Post, thread::Thread, user::User}; + +// ~~ Templates + +#[derive(Template)] +#[template(path = "threads/list.html")] +pub struct ThreadListTemplate { + pub title: String, + pub current_user: Option, + pub threads: Vec, +} + +#[derive(Template)] +#[template(path = "threads/new.html")] +pub struct NewThreadTemplate { + pub title: String, + pub current_user: Option, + pub error: Option, +} + +#[derive(Template)] +#[template(path = "threads/view.html")] +pub struct ThreadViewTemplate { + pub title: String, + pub current_user: Option, + pub thread: ThreadWithAuthor, + pub posts: Vec, + pub error: Option, +} + +#[derive(Template)] +#[template(path = "profile.html")] +pub struct ProfileTemplate { + pub title: String, + pub current_user: Option, + pub profile_user: User, + pub threads: Vec, +} + +// ~~ Data wrappers + +#[derive(Debug, Clone)] +pub struct ThreadWithAuthor { + pub thread: Thread, + pub author: User, +} + +#[derive(Debug, Clone)] +pub struct PostWithAuthor { + pub post: Post, + pub author: User, +} + +// ~~ Form types + +#[derive(Deserialize)] +pub struct NewThreadForm { + pub title: String, + pub body: String, +} + +#[derive(Deserialize)] +pub struct NewPostForm { + pub body: String, +} + +// ~~ Handlers + +pub async fn list_threads(State(pool): State, session: Session) -> Response { + let user: Option = get_current_user(&session, &pool).await; + let current_user = user.map(|u| CurrentUser { + username: u.username, + }); + + let threads = Thread::list_all(&pool).await.unwrap_or_default(); + + let mut threads_with_authors = Vec::new(); + for thread in threads { + let author = User::find_by_id(&pool, thread.author_id) + .await + .ok() + .flatten(); + if let Some(a) = author { + threads_with_authors.push(ThreadWithAuthor { thread, author: a }); + } + } + + render(ThreadListTemplate { + title: "threads".into(), + current_user, + threads: threads_with_authors, + }) +} + +pub async fn new_thread_page(session: Session, State(pool): State) -> Response { + let user: Option = get_current_user(&session, &pool).await; + if user.is_none() { + return Redirect::to("/auth/login").into_response(); + } + + let current_user = user.map(|u| CurrentUser { + username: u.username, + }); + + render(NewThreadTemplate { + title: "new thread".into(), + current_user, + error: None, + }) +} + +pub async fn create_thread( + session: Session, + State(pool): State, + Form(form): Form, +) -> Response { + let user: Option = get_current_user(&session, &pool).await; + let current_user = user.clone().map(|u| CurrentUser { + username: u.username, + }); + + let user = match user { + Some(u) => u, + None => return Redirect::to("/auth/login").into_response(), + }; + + if form.title.trim().is_empty() || form.body.trim().is_empty() { + return render(NewThreadTemplate { + title: "new thread".into(), + current_user, + error: Some("title and body cannot be empty".into()), + }); + } + + let thread_id = Thread::insert(&pool, &form.title, &form.body, user.id) + .await + .expect("failed to create thread"); + + Redirect::to(&format!("/threads/{}", thread_id)).into_response() +} + +pub async fn view_thread( + Path(id): Path, + session: Session, + State(pool): State, +) -> Response { + let user: Option = get_current_user(&session, &pool).await; + let current_user = user.map(|u| CurrentUser { + username: u.username, + }); + + let thread = match Thread::find_by_id(&pool, id).await.ok().flatten() { + Some(t) => t, + None => return (axum::http::StatusCode::NOT_FOUND, "thread not found").into_response(), + }; + + let author = match User::find_by_id(&pool, thread.author_id) + .await + .ok() + .flatten() + { + Some(a) => a, + None => { + return ( + axum::http::StatusCode::INTERNAL_SERVER_ERROR, + "author not found", + ) + .into_response(); + } + }; + + let posts = Post::by_thread(&pool, id).await.unwrap_or_default(); + + let mut posts_with_authors = Vec::new(); + for post in posts { + let author = User::find_by_id(&pool, post.author_id).await.ok().flatten(); + if let Some(a) = author { + posts_with_authors.push(PostWithAuthor { post, author: a }); + } + } + + render(ThreadViewTemplate { + title: thread.title.clone(), + current_user, + thread: ThreadWithAuthor { thread, author }, + posts: posts_with_authors, + error: None, + }) +} + +pub async fn create_post( + Path(id): Path, + session: Session, + State(pool): State, + Form(form): Form, +) -> Response { + let user: Option = get_current_user(&session, &pool).await; + let current_user = user.clone().map(|u| CurrentUser { + username: u.username, + }); + + let user = match user { + Some(u) => u, + None => return Redirect::to("/auth/login").into_response(), + }; + + let thread = match Thread::find_by_id(&pool, id).await.ok().flatten() { + Some(t) => t, + None => return Redirect::to("/threads").into_response(), + }; + + if form.body.trim().is_empty() { + let author = User::find_by_id(&pool, thread.author_id) + .await + .ok() + .flatten() + .unwrap_or_else(|| User { + id: 0, + username: "unknown".into(), + email: "".into(), + password: "".into(), + bio: None, + avatar_url: None, + role: "member".into(), + created_at: chrono::NaiveDateTime::MIN, + }); + + let posts = Post::by_thread(&pool, id).await.unwrap_or_default(); + let mut posts_with_authors = Vec::new(); + for post in posts { + let author = User::find_by_id(&pool, post.author_id).await.ok().flatten(); + if let Some(a) = author { + posts_with_authors.push(PostWithAuthor { post, author: a }); + } + } + + return render(ThreadViewTemplate { + title: thread.title.clone(), + current_user, + thread: ThreadWithAuthor { thread, author }, + posts: posts_with_authors, + error: Some("reply cannot be empty".into()), + }); + } + + Post::insert(&pool, &form.body, user.id, thread.id) + .await + .expect("failed to create post"); + + Redirect::to(&format!("/threads/{}", thread.id)).into_response() +} + +pub async fn view_profile( + Path(username): Path, + session: Session, + State(pool): State, +) -> Response { + let user: Option = get_current_user(&session, &pool).await; + let current_user = user.map(|u| CurrentUser { + username: u.username, + }); + + let profile_user = match User::find_by_username(&pool, &username) + .await + .ok() + .flatten() + { + Some(u) => u, + None => return (axum::http::StatusCode::NOT_FOUND, "user not found").into_response(), + }; + + let threads = Thread::by_author(&pool, profile_user.id) + .await + .unwrap_or_default(); + + let mut threads_with_authors = Vec::new(); + for thread in threads { + threads_with_authors.push(ThreadWithAuthor { + thread, + author: profile_user.clone(), + }); + } + + render(ProfileTemplate { + title: format!("profile: {}", profile_user.username), + current_user, + profile_user, + threads: threads_with_authors, + }) +} diff --git a/src/main.rs b/src/main.rs index eeffae4..f121a50 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,19 +4,22 @@ mod models; use askama::Template; use axum::{ + Router, extract::State, response::{Html, IntoResponse, Response}, routing::{get, post}, - Router, }; use sqlx::SqlitePool; use tower_http::services::ServeDir; -use tower_sessions::{cookie::SameSite, SessionManagerLayer}; +use tower_sessions::{SessionManagerLayer, cookie::SameSite}; use tower_sessions_sqlx_store::SqliteStore; use handlers::auth::{ get_current_user, login_page, login_submit, logout, register_page, register_submit, }; +use handlers::thread::{ + create_post, create_thread, list_threads, new_thread_page, view_profile, view_thread, +}; pub struct CurrentUser { pub username: String, @@ -29,12 +32,11 @@ struct IndexTemplate { current_user: Option, } -async fn index( - State(pool): State, - session: tower_sessions::Session, -) -> Response { +async fn index(State(pool): State, session: tower_sessions::Session) -> Response { let user: Option = get_current_user(&session, &pool).await; - let current_user = user.map(|u| CurrentUser { username: u.username }); + let current_user = user.map(|u| CurrentUser { + username: u.username, + }); let tmpl = IndexTemplate { title: "home".into(), @@ -43,7 +45,11 @@ async fn index( match tmpl.render() { Ok(html) => Html(html).into_response(), - Err(_) => (axum::http::StatusCode::INTERNAL_SERVER_ERROR, "render error").into_response(), + Err(_) => ( + axum::http::StatusCode::INTERNAL_SERVER_ERROR, + "render error", + ) + .into_response(), } } @@ -57,7 +63,10 @@ async fn main() { let pool: SqlitePool = db::connect(&database_url).await; let session_store = SqliteStore::new(pool.clone()); - session_store.migrate().await.expect("session store migration failed"); + session_store + .migrate() + .await + .expect("session store migration failed"); let session_secret = std::env::var("SARMENTINE_SESSION_SECRET") .unwrap_or_else(|_| "change-me-in-production-use-a-long-random-string!!".into()); @@ -65,15 +74,21 @@ async fn main() { let session_layer = SessionManagerLayer::new(session_store) .with_secure(false) .with_same_site(SameSite::Lax) - .with_signed(tower_sessions::cookie::Key::from( - session_secret.as_bytes(), - )); + .with_signed(tower_sessions::cookie::Key::from(session_secret.as_bytes())); - let static_dir = std::env::var("SARMENTINE_STATIC_DIR") - .unwrap_or_else(|_| "static".into()); + let static_dir = std::env::var("SARMENTINE_STATIC_DIR").unwrap_or_else(|_| "static".into()); + + let bind_addr = + std::env::var("SARMENTINE_BIND_ADDR").unwrap_or_else(|_| "127.0.0.1:3000".into()); let app = Router::new() .route("/", get(index)) + .route("/threads", get(list_threads)) + .route("/threads/new", get(new_thread_page)) + .route("/threads", post(create_thread)) + .route("/threads/:id", get(view_thread)) + .route("/threads/:id/posts", post(create_post)) + .route("/profile/:username", get(view_profile)) .route("/auth/register", get(register_page)) .route("/auth/register", post(register_submit)) .route("/auth/login", get(login_page)) @@ -83,10 +98,8 @@ async fn main() { .layer(session_layer) .with_state(pool); - let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") - .await - .unwrap(); + let listener = tokio::net::TcpListener::bind(&bind_addr).await.unwrap(); - println!("listening on http://127.0.0.1:3000"); + println!("listening on http://{}", bind_addr); axum::serve(listener, app).await.unwrap(); } diff --git a/src/models/mod.rs b/src/models/mod.rs index 22d12a3..691100f 100644 --- a/src/models/mod.rs +++ b/src/models/mod.rs @@ -1 +1,3 @@ +pub mod post; +pub mod thread; pub mod user; diff --git a/src/models/post.rs b/src/models/post.rs new file mode 100644 index 0000000..77508aa --- /dev/null +++ b/src/models/post.rs @@ -0,0 +1,42 @@ +use chrono::NaiveDateTime; +use serde::{Deserialize, Serialize}; +use sqlx::SqlitePool; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Post { + pub id: i64, + pub body: String, + pub author_id: i64, + pub thread_id: i64, + pub created_at: NaiveDateTime, + pub updated_at: NaiveDateTime, +} + +impl Post { + pub async fn by_thread(pool: &SqlitePool, thread_id: i64) -> sqlx::Result> { + sqlx::query_as!( + Post, + r#"SELECT id, body, author_id, thread_id, created_at, updated_at + FROM posts WHERE thread_id = ? ORDER BY created_at ASC"#, + thread_id + ) + .fetch_all(pool) + .await + } + + pub async fn insert( + pool: &SqlitePool, + body: &str, + author_id: i64, + thread_id: i64, + ) -> sqlx::Result { + let result = sqlx::query("INSERT INTO posts (body, author_id, thread_id) VALUES (?, ?, ?)") + .bind(body) + .bind(author_id) + .bind(thread_id) + .execute(pool) + .await?; + + Ok(result.last_insert_rowid()) + } +} diff --git a/src/models/thread.rs b/src/models/thread.rs new file mode 100644 index 0000000..fa7c579 --- /dev/null +++ b/src/models/thread.rs @@ -0,0 +1,63 @@ +use chrono::NaiveDateTime; +use serde::{Deserialize, Serialize}; +use sqlx::SqlitePool; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Thread { + pub id: i64, + pub title: String, + pub body: String, + pub author_id: i64, + pub created_at: NaiveDateTime, + pub updated_at: NaiveDateTime, +} + +impl Thread { + pub async fn list_all(pool: &SqlitePool) -> sqlx::Result> { + sqlx::query_as!( + Thread, + r#"SELECT id, title, body, author_id, created_at, updated_at + FROM threads ORDER BY updated_at DESC"# + ) + .fetch_all(pool) + .await + } + + pub async fn find_by_id(pool: &SqlitePool, id: i64) -> sqlx::Result> { + sqlx::query_as!( + Thread, + r#"SELECT id, title, body, author_id, created_at, updated_at + FROM threads WHERE id = ?"#, + id + ) + .fetch_optional(pool) + .await + } + + pub async fn by_author(pool: &SqlitePool, author_id: i64) -> sqlx::Result> { + sqlx::query_as!( + Thread, + r#"SELECT id, title, body, author_id, created_at, updated_at + FROM threads WHERE author_id = ? ORDER BY updated_at DESC"#, + author_id + ) + .fetch_all(pool) + .await + } + + pub async fn insert( + pool: &SqlitePool, + title: &str, + body: &str, + author_id: i64, + ) -> sqlx::Result { + let result = sqlx::query("INSERT INTO threads (title, body, author_id) VALUES (?, ?, ?)") + .bind(title) + .bind(body) + .bind(author_id) + .execute(pool) + .await?; + + Ok(result.last_insert_rowid()) + } +} diff --git a/src/models/user.rs b/src/models/user.rs index a093928..35f873a 100644 --- a/src/models/user.rs +++ b/src/models/user.rs @@ -55,19 +55,13 @@ impl User { email: &str, password_hash: &str, ) -> sqlx::Result { - let result = sqlx::query( - "INSERT INTO users (username, email, password) VALUES (?, ?, ?)" - ) - .bind(username) - .bind(email) - .bind(password_hash) - .execute(pool) - .await?; + let result = sqlx::query("INSERT INTO users (username, email, password) VALUES (?, ?, ?)") + .bind(username) + .bind(email) + .bind(password_hash) + .execute(pool) + .await?; Ok(result.last_insert_rowid()) } - - pub fn is_admin(&self) -> bool { - self.role == "admin" - } } diff --git a/templates/profile.html b/templates/profile.html new file mode 100644 index 0000000..2bc8775 --- /dev/null +++ b/templates/profile.html @@ -0,0 +1,27 @@ +{% extends "base.html" %} + +{% block content %} +
profile: {{ profile_user.username }}
+ +
+
{{ profile_user.username }}
+ {% if let Some(bio) = profile_user.bio %} +
{{ bio }}
+ {% endif %} +
joined {{ profile_user.created_at }} · role: {{ profile_user.role }}
+
+ +{% if !threads.is_empty() %} +
threads by {{ profile_user.username }}
+{% for twa in threads %} +
+
{{ twa.thread.title }}
+
{{ twa.thread.created_at }}
+
+{% endfor %} +{% else %} +
+
no threads yet
+
+{% endif %} +{% endblock %} diff --git a/templates/threads/list.html b/templates/threads/list.html new file mode 100644 index 0000000..2a08373 --- /dev/null +++ b/templates/threads/list.html @@ -0,0 +1,23 @@ +{% extends "base.html" %} + +{% block content %} +
threads
+ +{% if let Some(user) = current_user %} ++ new thread +{% endif %} + +{% if threads.is_empty() %} +
+
no threads yet
+
be the first to start a thread!
+
+{% else %} +{% for twa in threads %} +
+
{{ twa.thread.title }}
+
by {{ twa.author.username }} · {{ twa.thread.created_at }}
+
+{% endfor %} +{% endif %} +{% endblock %} diff --git a/templates/threads/new.html b/templates/threads/new.html new file mode 100644 index 0000000..e3397b5 --- /dev/null +++ b/templates/threads/new.html @@ -0,0 +1,23 @@ +{% extends "base.html" %} + +{% block content %} +
new thread
+ +{% if let Some(error) = error %} +
+
{{ error }}
+
+{% endif %} + +
+
+ + +
+
+ + +
+ +
+{% endblock %} diff --git a/templates/threads/view.html b/templates/threads/view.html new file mode 100644 index 0000000..17d0e59 --- /dev/null +++ b/templates/threads/view.html @@ -0,0 +1,44 @@ +{% extends "base.html" %} + +{% block content %} +
+ threads / {{ thread.thread.title }} +
+ +
+
{{ thread.thread.title }}
+
by {{ thread.author.username }} · {{ thread.thread.created_at }}
+
{{ thread.thread.body }}
+
+ +{% if let Some(error) = error %} +
+
{{ error }}
+
+{% endif %} + +{% if !posts.is_empty() %} +
replies
+{% for pwa in posts %} +
+
by {{ pwa.author.username }} · {{ pwa.post.created_at }}
+
{{ pwa.post.body }}
+
+{% endfor %} +{% endif %} + +{% if let Some(user) = current_user %} +
+
+
+ +
+ +
+
+{% else %} +
+ login to reply +
+{% endif %} +{% endblock %}