17e5ee15794729001a9c9877fec84d72b97c4965
rate-my-shots
A photo-rating webapp that pairs assets from an Immich album for ELO-style head-to-head voting.
Local development
bun install
cp .env.example .env # fill in IMMICH_URL, IMMICH_API_KEY, IMMICH_ALBUM_ID, ADMIN_PASSWORD_HASH
bun run scripts/migrate.ts
bun dev
Open http://localhost:3000.
Required env
| Var | Description |
|---|---|
IMMICH_URL |
Base URL of the Immich instance |
IMMICH_API_KEY |
Immich API key with read + rating-write scope |
IMMICH_ALBUM_ID |
UUID of the album to pull assets from |
ADMIN_PASSWORD_HASH |
scrypt hash, format <salt-hex>:<hash-hex> (see below) |
DATABASE_PATH |
SQLite file path (default data/rate-my-shots.db) |
Generate the admin password hash:
node -e "const c=require('crypto');const s=c.randomBytes(16);process.stdout.write(s.toString('hex')+':'+c.scryptSync(process.argv[1],s,64).toString('hex')+'\n')" 'your-password'
Deployment
The Dockerfile produces a standalone Next.js bundle that runs as a non-root bun user. Mount ./data on the host for SQLite persistence; the host directory must be writable by UID 1000.
The app trusts X-Forwarded-For for client IP (rate limiting, vote IP-binding). Run it behind a reverse proxy that strips/sets that header from untrusted clients — Caddy with reverse_proxy does this by default. Do not expose the container directly to untrusted networks; clients can otherwise spoof XFF and bypass rate limits or hijack pairings.
docker compose up -d --build
Security model
- Admin auth is a hashed password (scrypt) verified at login; sessions are random opaque tokens stored in-memory with a 7-day TTL (server restart logs admins out).
- Login attempts are rate-limited per IP (5 / 5 min, then exponential backoff).
- The
/img/[id]proxy only serves assets present in the local DB (album members or configured lens portraits). Other Immich assets are 404. - All
/api/**POST/PUT/DELETE requests requireOriginto matchHost. - CSP,
X-Frame-Options: DENY,X-Content-Type-Options: nosniff, andReferrer-Policy: same-originare set on all responses.
Description
Languages
TypeScript
99.3%
Dockerfile
0.6%