From 5dd9aac68da1f04511ee8b4888168327435167f0 Mon Sep 17 00:00:00 2001 From: Gabriel Kaszewski Date: Mon, 4 May 2026 15:19:29 +0200 Subject: [PATCH] feat: add Dockerfile, .dockerignore, and README; remove common crate Co-authored-by: Copilot --- .dockerignore | 6 +++ Cargo.lock | 8 ---- Cargo.toml | 9 ++-- Dockerfile | 57 ++++++++++++++++++++++++ LICENSE | 21 +++++++++ README.md | 86 +++++++++++++++++++++++++++++++++++++ crates/common/Cargo.toml | 7 --- crates/common/src/errors.rs | 0 crates/common/src/lib.rs | 1 - crates/domain/Cargo.toml | 1 - 10 files changed, 175 insertions(+), 21 deletions(-) create mode 100644 .dockerignore create mode 100644 Dockerfile create mode 100644 LICENSE create mode 100644 README.md delete mode 100644 crates/common/Cargo.toml delete mode 100644 crates/common/src/errors.rs delete mode 100644 crates/common/src/lib.rs diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..f5f624d --- /dev/null +++ b/.dockerignore @@ -0,0 +1,6 @@ +target/ +.git/ +.env +*.db +*.db-shm +*.db-wal diff --git a/Cargo.lock b/Cargo.lock index d51a349..37ef1bc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -389,13 +389,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "common" -version = "0.1.0" -dependencies = [ - "thiserror", -] - [[package]] name = "concurrent-queue" version = "2.5.0" @@ -611,7 +604,6 @@ dependencies = [ "anyhow", "async-trait", "chrono", - "common", "email_address", "thiserror", "uuid", diff --git a/Cargo.toml b/Cargo.toml index 6ccac46..1ea2631 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,14 @@ [workspace] members = [ - "crates/adapters/auth", "crates/adapters/event-publisher", - "crates/adapters/metadata", "crates/adapters/poster-fetcher", "crates/adapters/poster-storage", + "crates/adapters/auth", + "crates/adapters/event-publisher", + "crates/adapters/metadata", + "crates/adapters/poster-fetcher", + "crates/adapters/poster-storage", "crates/adapters/rss", "crates/adapters/sqlite", "crates/adapters/template-askama", "crates/application", - "crates/common", "crates/domain", "crates/presentation", ] @@ -34,7 +36,6 @@ reqwest = { version = "0.13", features = ["json", "query"] } object_store = { version = "0.11", features = ["aws"] } domain = { path = "crates/domain" } -common = { path = "crates/common" } application = { path = "crates/application" } presentation = { path = "crates/presentation" } auth = { path = "crates/adapters/auth" } diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..761dbf8 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,57 @@ +# ----- build ----- +FROM rust:slim-bookworm AS builder + +RUN apt-get update && apt-get install -y --no-install-recommends sqlite3 && rm -rf /var/lib/apt/lists/* + +WORKDIR /build + +# Cache dependency compilation separately from source +COPY Cargo.toml Cargo.lock ./ +COPY crates/adapters/auth/Cargo.toml crates/adapters/auth/Cargo.toml +COPY crates/adapters/event-publisher/Cargo.toml crates/adapters/event-publisher/Cargo.toml +COPY crates/adapters/metadata/Cargo.toml crates/adapters/metadata/Cargo.toml +COPY crates/adapters/poster-fetcher/Cargo.toml crates/adapters/poster-fetcher/Cargo.toml +COPY crates/adapters/poster-storage/Cargo.toml crates/adapters/poster-storage/Cargo.toml +COPY crates/adapters/rss/Cargo.toml crates/adapters/rss/Cargo.toml +COPY crates/adapters/sqlite/Cargo.toml crates/adapters/sqlite/Cargo.toml +COPY crates/adapters/template-askama/Cargo.toml crates/adapters/template-askama/Cargo.toml +COPY crates/application/Cargo.toml crates/application/Cargo.toml +COPY crates/domain/Cargo.toml crates/domain/Cargo.toml +COPY crates/presentation/Cargo.toml crates/presentation/Cargo.toml + +# Stub every crate so cargo can resolve and fetch deps +RUN find crates -name "Cargo.toml" | sed 's|/Cargo.toml||' | \ + xargs -I{} sh -c 'mkdir -p {}/src && echo "fn main(){}" > {}/src/main.rs && echo "" > {}/src/lib.rs' + +RUN cargo fetch + +# Now copy real sources (invalidates cache only on source changes) +COPY crates ./crates + +# sqlx macros verify queries at compile time; create a real DB from migrations +RUN sqlite3 /build/dev.db \ + < crates/adapters/sqlite/migrations/0001_initial.sql && \ + sqlite3 /build/dev.db \ + < crates/adapters/sqlite/migrations/0002_users.sql + +ENV DATABASE_URL=sqlite:///build/dev.db + +RUN cargo build --release -p presentation + +# ----- runtime ----- +FROM debian:bookworm-slim + +RUN apt-get update && apt-get install -y --no-install-recommends \ + ca-certificates \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /app + +COPY --from=builder /build/target/release/presentation ./presentation +COPY static ./static + +EXPOSE 3000 + +ENV RUST_LOG=presentation=info,tower_http=info + +CMD ["./presentation"] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..31bafd2 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Gabriel Kaszewski + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..bc59d03 --- /dev/null +++ b/README.md @@ -0,0 +1,86 @@ +# Movies Diary + +A self-hosted, server-side rendered movie logging system. Built in Rust — no JavaScript, no SPA, just HTML forms and an RSS feed. Designed to run as a lightweight widget embedded on a personal site. + +## Features + +- Log movies with a TMDB/OMDb ID and a 0–5 rating +- Immutable append-only viewing ledger (tracks re-watches) +- Background poster fetching and storage (S3-compatible) +- RSS/Atom feed for public subscription +- JWT authentication via cookie (HTML) or Bearer token (REST API) +- Zero JavaScript + +## Architecture + +Hexagonal (Ports & Adapters) with Domain-Driven Design: + +``` +domain — pure types and trait definitions, no external deps +common — shared error types +application — use cases / business logic orchestration +presentation — Axum HTTP router, wires all adapters together +adapters/ + auth — JWT issuance and validation (Argon2 passwords) + sqlite — SQLite repository via sqlx + metadata — OMDb HTTP client + poster-fetcher — downloads poster images + poster-storage — uploads posters to S3-compatible storage + template-askama — Askama HTML rendering + rss — RSS/Atom feed generation + event-publisher — async event channel for background poster sync +``` + +## Prerequisites + +- Rust (stable, 2024 edition) +- SQLite +- An S3-compatible object store (e.g. MinIO) for poster storage +- An [OMDb API key](https://www.omdbapi.com/apikey.aspx) + +## Environment Variables + +Copy and fill in the following (e.g. in a `.env` file): + +```env +# Database +DATABASE_URL=sqlite://movies.db + +# Authentication +JWT_SECRET=change-me +JWT_TTL_SECONDS=86400 + +# OMDb metadata +OMDB_API_KEY=your-key + +# Poster storage (S3-compatible) +MINIO_ENDPOINT=http://localhost:9000 +MINIO_BUCKET=posters +MINIO_REGION=us-east-1 +MINIO_ACCESS_KEY_ID=minioadmin +MINIO_SECRET_ACCESS_KEY=minioadmin + +# Optional +ALLOW_REGISTRATION=false +POSTER_FETCH_TIMEOUT_SECONDS=10 +EVENT_CHANNEL_BUFFER=32 +RUST_LOG=presentation=debug,tower_http=debug +``` + +## Run + +```bash +cargo run -p presentation +``` + +Server listens on `0.0.0.0:3000`. + +## Test + +```bash +cargo test +``` + +## License + +MIT License. See [LICENSE](LICENSE). diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml deleted file mode 100644 index 2decf7b..0000000 --- a/crates/common/Cargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "common" -version = "0.1.0" -edition = "2024" - -[dependencies] -thiserror = { workspace = true } diff --git a/crates/common/src/errors.rs b/crates/common/src/errors.rs deleted file mode 100644 index e69de29..0000000 diff --git a/crates/common/src/lib.rs b/crates/common/src/lib.rs deleted file mode 100644 index 629e98f..0000000 --- a/crates/common/src/lib.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod errors; diff --git a/crates/domain/Cargo.toml b/crates/domain/Cargo.toml index b581b18..5ccb5cd 100644 --- a/crates/domain/Cargo.toml +++ b/crates/domain/Cargo.toml @@ -10,5 +10,4 @@ async-trait = { workspace = true } anyhow = { workspace = true } thiserror = { workspace = true } -common = { workspace = true } email_address = "0.2.9"