Adds subcommands to the binary for admin tasks without needing the web UI: list users, set passwords, promote/demote roles, and lock/unlock accounts. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
3.7 KiB
3.7 KiB
Favoritter — Project Guide
Quick Reference
go build ./cmd/favoritter # Build
go test ./... # Test
FAVORITTER_DEV_MODE=true \
FAVORITTER_ADMIN_USERNAME=admin \
FAVORITTER_ADMIN_PASSWORD=dev \
go run ./cmd/favoritter # Run (dev mode)
make build # Build with version info
make test # Run tests
make container # Build container image
CLI Admin Commands
favoritter user list # List all users
favoritter user set-password <user> # Change a user's password
favoritter user promote <user> # Promote user to admin
favoritter user demote <user> # Remove admin role
favoritter user lock <user> # Disable user account
favoritter user unlock <user> # Enable user account
Architecture
Single Go binary serving HTML (server-rendered templates + HTMX) and a JSON API. SQLite for storage, filesystem for uploaded images. All templates and static assets are embedded via go:embed.
Directory Layout
cmd/favoritter/— Entry point, wiring, graceful shutdown, CLI admin commands (cli_*.go)internal/config/— Environment variable configurationinternal/database/— SQLite connection, PRAGMAs, migration runnerinternal/model/— Domain types (no logic, no DB)internal/store/— Data access layer (one file per entity, plain SQL)internal/handler/— HTTP handlers for web UIinternal/handler/api/— JSON REST API handlersinternal/middleware/— HTTP middleware (auth, CSRF, rate limit, etc.)internal/render/— Template rendering with layout supportinternal/image/— Image upload processing (validate, resize, strip EXIF)web/templates/— HTML templates (layouts, pages, partials)web/static/— CSS, JS, vendored Pico CSS + HTMXdist/— Packaging artifacts (systemd, env file, install scripts)
Key Design Decisions
- Go 1.22+ stdlib router — no framework,
http.ServeMuxwith method routing - Minimal dependencies — modernc.org/sqlite (pure Go), golang.org/x/crypto (Argon2id), gorilla/feeds, google/uuid, golang.org/x/term
SetMaxOpenConns(1)— SQLite works best with a single writer; PRAGMAs are set once on the single connection- Templates embedded in binary —
//go:embedfor single-binary deployment; dev mode reads from disk for live reload - Middleware chain order matters — Recovery → SecurityHeaders → BasePath → RealIP → Logger → SessionLoader → CSRF → MustResetGuard
Database
SQLite with WAL mode. Migrations are embedded SQL files in internal/database/migrations/, applied sequentially on startup. Forward-only — no down migrations.
Conventions
- Norwegian Bokmål for all user-facing text (templates, flash messages, error text)
- SPDX license headers on all source files
- Argon2id for password hashing (never bcrypt)
- UUID filenames for all uploads — never use user-provided filenames
- Errors: log with
slog.Errorat the handler level, return generic messages to users — never leak internal errors - Tests: use real in-memory SQLite (
:memory:), set fast Argon2 params (Memory=1024, Time=1) in tests - Session cookie name: use
middleware.SessionCookieNameconstant, never hardcode"session" - CSRF: auto-included in HTMX requests via
htmx:configRequestJS hook
Hosting
- Hosted on Forgejo at
kode.naiv.no— usefjCLI, notgh - Supports deployment as: standalone binary, .deb package, .rpm package, or container
- Reverse proxy (Caddy) may be on a different machine — always use
EXTERNAL_URLandTRUSTED_PROXIES