All checks were successful
CD / Build and push images (push) Successful in 2m2s
CI / Lint, typecheck, test (push) Successful in 1m43s
CI / Auth e2e pack (push) Successful in 1m48s
CI / Build container images (push) Has been skipped
CD / Deploy to Test (push) Successful in 8s
CD / Smoke tests against Test (push) Successful in 1m10s
CD / Promote to Int (push) Successful in 10s
Implements the FileStorage abstraction (uploads/<pondId>/<fileId> on the mounted volume), the attachments model, and POST /ponds/:id/files, GET /media/:fileId, DELETE /files/:id. Uploads are validated by sniffing magic bytes rather than trusting the client's Content-Type/filename (catches a renamed .html-as-.png), checked against the max_file_bytes and storage_bytes quotas, and served with nosniff + immutable caching. Closes #27
94 lines
2.6 KiB
YAML
94 lines
2.6 KiB
YAML
# Production Compose stack — one file for every stage and for self-hosters.
|
|
# Configuration comes from .env (see .env.example); the host reverse proxy
|
|
# routes to the two published localhost ports (deployment.md §Compose).
|
|
#
|
|
# Networks: `frontend` is what the reverse proxy reaches (via published
|
|
# ports); `internal` connects api/collab to db and (later) the converter
|
|
# sidecars, which are never exposed.
|
|
|
|
name: ${COMPOSE_PROJECT_NAME:-dorfteich}
|
|
|
|
x-logging: &logging
|
|
logging:
|
|
driver: json-file
|
|
options:
|
|
max-size: '10m'
|
|
max-file: '5'
|
|
|
|
services:
|
|
web:
|
|
image: ${IMAGE_PREFIX:-dorfteich}-web:${TAG:-latest}
|
|
build:
|
|
context: ../..
|
|
dockerfile: apps/web/Dockerfile
|
|
args:
|
|
APP_VERSION: ${TAG:-latest}
|
|
restart: unless-stopped
|
|
ports:
|
|
- '127.0.0.1:${WEB_PORT:-8100}:8080'
|
|
networks: [frontend]
|
|
depends_on:
|
|
api:
|
|
condition: service_started
|
|
<<: *logging
|
|
|
|
api:
|
|
image: ${IMAGE_PREFIX:-dorfteich}-api:${TAG:-latest}
|
|
build:
|
|
context: ../..
|
|
dockerfile: apps/api/Dockerfile
|
|
args:
|
|
APP_VERSION: ${TAG:-latest}
|
|
restart: unless-stopped
|
|
environment:
|
|
NODE_ENV: production
|
|
PORT: '3000'
|
|
LOG_LEVEL: ${LOG_LEVEL:-info}
|
|
DATABASE_URL: postgresql://dorfteich:${POSTGRES_PASSWORD:?set in .env}@db:5432/dorfteich
|
|
# Public URL of this stage — e-mail links and the CSRF origin check
|
|
# depend on it matching what browsers actually use.
|
|
APP_BASE_URL: ${APP_BASE_URL:-http://localhost:5173}
|
|
# SMTP relay; defaults are only useful with the dev Mailpit overlay.
|
|
SMTP_HOST: ${SMTP_HOST:-localhost}
|
|
SMTP_PORT: ${SMTP_PORT:-1025}
|
|
SMTP_SECURE: ${SMTP_SECURE:-false}
|
|
SMTP_USER: ${SMTP_USER:-}
|
|
SMTP_PASS: ${SMTP_PASS:-}
|
|
SMTP_FROM: ${SMTP_FROM:-Dorfteich <no-reply@localhost>}
|
|
# Matches the `uploads` volume mount below (ADR 0011).
|
|
UPLOADS_DIR: /data/uploads
|
|
ports:
|
|
- '127.0.0.1:${API_PORT:-8101}:3000'
|
|
networks: [frontend, internal]
|
|
volumes:
|
|
- uploads:/data/uploads
|
|
depends_on:
|
|
db:
|
|
condition: service_healthy
|
|
<<: *logging
|
|
|
|
db:
|
|
image: postgres:17.5-alpine
|
|
restart: unless-stopped
|
|
environment:
|
|
POSTGRES_USER: dorfteich
|
|
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set in .env}
|
|
POSTGRES_DB: dorfteich
|
|
networks: [internal]
|
|
volumes:
|
|
- db-data:/var/lib/postgresql/data
|
|
healthcheck:
|
|
test: ['CMD-SHELL', 'pg_isready -U dorfteich -d dorfteich']
|
|
interval: 10s
|
|
timeout: 3s
|
|
retries: 12
|
|
<<: *logging
|
|
|
|
networks:
|
|
frontend:
|
|
internal:
|
|
|
|
volumes:
|
|
db-data:
|
|
uploads:
|