Files
RecipesCam/docker/docker-compose.yml
T
3dtours ffdefd2c9c feat(photos): community film strip uploads + admin moderation
Backend
- photos table + upload storage under DATA_DIR/uploads (magic-byte sniffing,
  no multipart dep, SVG rejected, wx exclusive writes)
- POST/GET /api/photos, GET /api/photos/:id/file with nosniff + sandboxed CSP
- admin routes (ADMIN_EMAILS allowlist): list, delete one, clear all
- identity-keyed rate limits (login 20/15m, signup 5/h, upload 60/h)
- cookie gains Secure when the request is https (via trustProxy)
- /api/auth/me now 200 {user:null} instead of 401 when signed out

Frontend
- landing strip section: signed-in users upload straight from the reel,
  guests get a /app?auth=1 link
- /admin page: grid of uploads with delete + clear all
- nginx: nosniff / X-Frame-Options / Referrer-Policy, forward
  X-Forwarded-Proto so the API can mark cookies Secure behind TLS

Tests: docker/backend test/security.mjs (45 checks)
2026-09-17 22:35:12 +07:00

30 lines
929 B
YAML

# RecipesCam web — the whole stack. Copy this folder anywhere, then:
# cp .env.example .env
# docker compose up -d --build
name: recipescam-web
services:
# Accounts + saved recipes. Never sees a photo: every pixel of the render
# pipeline runs in the visitor's browser.
api:
build: ./backend
restart: unless-stopped
environment:
DATA_DIR: /data
# Who may moderate the contributed strip: a comma-separated email
# allowlist. Empty means nobody is an admin, which is the safe default.
ADMIN_EMAILS: ${ADMIN_EMAILS:-}
volumes:
# SQLite (WAL) lives on the host so a rebuild never loses accounts.
- ./data:/data
# Static SPA + /api reverse proxy. "api" is the DNS name nginx.conf proxies to,
# so this service name is load-bearing.
frontend:
build: ./frontend
restart: unless-stopped
depends_on:
- api
ports:
- "${WEB_PORT:-8090}:80"