Movies Diary

A self-hosted, server-side rendered movie logging system with a full REST API. Built in Rust — no JavaScript in the HTML interface, just HTML forms and an RSS feed. Designed to run as a lightweight widget embedded on a personal site or as a backend for third-party clients.

Features

  • Log movies with a TMDB/OMDb ID or manual title/year/director, with a 05 rating
  • Immutable append-only viewing ledger (tracks re-watches)
  • Background poster fetching and storage (local filesystem or S3-compatible)
  • RSS/Atom feed for public subscription (global and per-user)
  • JWT authentication via cookie (HTML) or Bearer token (REST API)
  • ActivityPub federation — follow/unfollow remote users on any compatible server, accept/reject/remove followers, pending follow request management
  • CSV and JSON diary export (full round-trip: exported files can be re-imported via the TUI bulk import)
  • REST API v1 (/api/v1/) with full feature parity with the HTML interface
  • OpenAPI documentation at /docs (Swagger UI) and /scalar (Scalar)
  • CSRF protection on all HTML form routes (double-submit cookie, defense-in-depth on top of SameSite=Strict)
  • Per-IP rate limiting via token bucket (production-grade, backed by axum-governor)
  • Terminal UI client (crates/tui) for logging reviews, bulk CSV import, and diary browsing

Architecture

Hexagonal (Ports & Adapters) with Domain-Driven Design:

domain              — pure types and trait definitions, no external deps
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 local filesystem or S3-compatible storage
  template-askama   — Askama HTML rendering
  rss               — RSS/Atom feed generation
  export            — CSV and JSON diary serialization
  sqlite-event-queue   — polling event queue backed by SQLite (DB-queue mode)
  postgres-event-queue — polling event queue backed by PostgreSQL (DB-queue mode)
  nats                 — NATS Core / JetStream event publisher and consumer
  event-publisher      — in-memory event channel (used in tests)
  activitypub          — ActivityPub federation (follow, inbox/outbox, actor)
  activitypub-base     — core ActivityPub types and repository traits
doc                 — OpenAPI spec assembly and Swagger UI / Scalar serving
worker              — standalone worker binary (polls the event queue, syncs posters)
tui                 — terminal UI client (ratatui)

Prerequisites

  • Rust (stable, 2024 edition)
  • SQLite
  • Poster storage: local filesystem (zero deps) or an S3-compatible object store (e.g. MinIO)
  • An OMDb API key

Environment Variables

A .env.example file is provided at the repo root — copy it to .env and fill in your values.

# Database
DATABASE_URL=sqlite://movies.db

# Authentication
JWT_SECRET=change-me

# OMDb metadata
OMDB_API_KEY=your-key

# Public base URL (used for ActivityPub actor URLs and canonical links)
BASE_URL=https://yourdomain.example.com

# Poster storage — pick one backend:

# Option A: local filesystem (zero deps)
POSTER_STORAGE_BACKEND=local
POSTER_STORAGE_PATH=./posters

# Option B: S3-compatible (MinIO, AWS S3, etc.)
# POSTER_STORAGE_BACKEND=s3
# MINIO_ENDPOINT=http://localhost:9000
# MINIO_BUCKET=posters
# MINIO_REGION=minio
# MINIO_ACCESS_KEY_ID=minioadmin
# MINIO_SECRET_ACCESS_KEY=minioadmin

# Optional
HOST=0.0.0.0
PORT=3000
RATE_LIMIT=60           # requests per minute per IP (default: 60)
ALLOW_REGISTRATION=true # set to false to disable new sign-ups
SECURE_COOKIES=true     # set when serving over HTTPS
RUST_LOG=presentation=info,tower_http=info,worker=info,application=info

# Event bus — "db" (default, uses same database) or "nats"
EVENT_BUS_BACKEND=db
# NATS_URL=nats://localhost:4222   # required when EVENT_BUS_BACKEND=nats

The worker binary must run alongside presentation to process events:

cargo run -p worker

Run

cargo run -p presentation   # HTTP server (0.0.0.0:3000)
cargo run -p worker         # event worker (poster sync, in a separate terminal)

The worker polls the event queue and must run alongside the presentation to process background tasks like poster fetching. Both processes share the same database.

API

All REST endpoints are under /api/v1/. Authentication uses Authorization: Bearer <token> obtained from POST /api/v1/auth/login.

Interactive API documentation is available at runtime:

  • Swagger UIhttp://localhost:3000/docs
  • Scalarhttp://localhost:3000/scalar

Terminal UI

cargo run -p tui

Supports review logging, bulk CSV import (column order matches the export format), and diary browsing with review history.

Test

cargo test

Docker

docker build -t movies-diary .
docker run -p 3000:3000 \
  -e DATABASE_URL=sqlite:///data/movies.db \
  -e JWT_SECRET=change-me \
  -e OMDB_API_KEY=your-key \
  -e BASE_URL=https://yourdomain.example.com \
  -v $(pwd)/data:/data \
  movies-diary

License

MIT License. See LICENSE.

Description
Log every film you watch, rate it, and share reviews across the Fediverse via ActivityPub. Federate with other instances, follow remote users, and browse a fully federated activity feed.
https://movies.gabrielkaszewski.dev/ Readme MIT 22 MiB
Languages
Rust 73%
TypeScript 21%
HTML 3.3%
CSS 2.1%
Mermaid 0.3%
Other 0.2%