infra: single postgres instance (casdoor DB merged) + Traefik v3 edge

- remove the separate casdoor-postgres container; casdoor uses the main
  postgres (db casdoor, created by sql/02-casdoor-db.sql on fresh volumes);
  data migrated via pg_dump/pg_restore (backups kept outside the repo)
- Traefik v3.6 as the public edge (ports 80/443): docker provider pinned to
  the dedicated traefik_network (fixed-name network), host rules for
  app/api/auth/s3/storage (env-overridable, localhost defaults), frontend
  nginx demoted to internal SPA server; docker-compose.prod.yml adds real
  domains + Let's Encrypt + HTTP→HTTPS redirect (redirect middleware in
  infra/traefik-dynamic.yml)
- v3.6 required (Traefik <=3.3 fails against Docker Engine 29.x); socket
  mounted rw (Docker Desktop rejects :ro)
- start.ps1: app URL now http://localhost, casdoor + db notes
This commit is contained in:
ali_mohaghegh
2026-09-01 17:28:33 +03:30
parent c5997ac243
commit 8825f333a7
5 changed files with 198 additions and 19 deletions
+74
View File
@@ -0,0 +1,74 @@
# ============================================================================
# Production overlay — real domains + HTTPS (Let's Encrypt) + HTTP→HTTPS
# redirect. Usage:
#
# APP_HOST=app.example.com API_HOST=api.example.com AUTH_HOST=auth.example.com \
# S3_HOST=s3.example.com STORAGE_HOST=storage.example.com \
# TRAEFIK_ACME_EMAIL=admin@example.com \
# docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d --build
#
# Requirements: DNS records for every host pointing at the server, ports
# 80 + 443 open (ACME http-challenge uses port 80), and the api's
# PROCUREMENT_CASDOOR_ISSUER must be updated to the public auth host
# (see docs/casdoor-auth.md).
# ============================================================================
services:
traefik:
environment:
# Certificates persist across restarts (acme.json in the traefik_certs volume).
TRAEFIK_ACME_EMAIL: ${TRAEFIK_ACME_EMAIL:?set TRAEFIK_ACME_EMAIL for production}
api:
ports: [] # production: only reachable through Traefik
labels:
- traefik.http.routers.api.rule=Host(`${API_HOST:?set API_HOST}`)
- traefik.http.routers.api.entrypoints=websecure
- traefik.http.routers.api.tls=true
- traefik.http.routers.api.tls.certresolver=letsencrypt
# HTTP → HTTPS
- traefik.http.routers.api-redir.rule=Host(`${API_HOST:?set API_HOST}`)
- traefik.http.routers.api-redir.entrypoints=web
- traefik.http.routers.api-redir.middlewares=redirect-https@file
casdoor:
ports: [] # production: only reachable through Traefik (auth host)
environment:
# Casdoor must know its public origin so tokens carry the right iss.
origin: "https://${AUTH_HOST:?set AUTH_HOST}"
labels:
- traefik.http.routers.casdoor.rule=Host(`${AUTH_HOST:?set AUTH_HOST}`)
- traefik.http.routers.casdoor.entrypoints=websecure
- traefik.http.routers.casdoor.tls=true
- traefik.http.routers.casdoor.tls.certresolver=letsencrypt
- traefik.http.routers.casdoor-redir.rule=Host(`${AUTH_HOST:?set AUTH_HOST}`)
- traefik.http.routers.casdoor-redir.entrypoints=web
- traefik.http.routers.casdoor-redir.middlewares=redirect-https@file
frontend:
labels:
- traefik.http.routers.frontend.rule=Host(`${APP_HOST:?set APP_HOST}`)
- traefik.http.routers.frontend.entrypoints=websecure
- traefik.http.routers.frontend.tls=true
- traefik.http.routers.frontend.tls.certresolver=letsencrypt
- traefik.http.routers.frontend-redir.rule=Host(`${APP_HOST:?set APP_HOST}`)
- traefik.http.routers.frontend-redir.entrypoints=web
- traefik.http.routers.frontend-redir.middlewares=redirect-https@file
minio:
ports: [] # production: only reachable through Traefik
labels:
- traefik.http.routers.minio-api.rule=Host(`${S3_HOST:?set S3_HOST}`)
- traefik.http.routers.minio-api.entrypoints=websecure
- traefik.http.routers.minio-api.tls=true
- traefik.http.routers.minio-api.tls.certresolver=letsencrypt
- traefik.http.routers.minio-api-redir.rule=Host(`${S3_HOST:?set S3_HOST}`)
- traefik.http.routers.minio-api-redir.entrypoints=web
- traefik.http.routers.minio-api-redir.middlewares=redirect-https@file
- traefik.http.routers.minio-console.rule=Host(`${STORAGE_HOST:?set STORAGE_HOST}`)
- traefik.http.routers.minio-console.entrypoints=websecure
- traefik.http.routers.minio-console.tls=true
- traefik.http.routers.minio-console.tls.certresolver=letsencrypt
- traefik.http.routers.minio-console-redir.rule=Host(`${STORAGE_HOST:?set STORAGE_HOST}`)
- traefik.http.routers.minio-console-redir.entrypoints=web
- traefik.http.routers.minio-console-redir.middlewares=redirect-https@file