Files
OPENBUREAU/cms/docker-compose.yml
T
karim 8404165f5c perf/ops: Auth-Latenz, Zähl-View, DB-Backup, Schreib-Limit, Asset-Cache
- auth: Supabase-JWT lokal verifizieren (hono/jwt, HS256) statt GoTrue-
  Roundtrip pro Request; JWT_SECRET in cms-env, Remote-Fallback wenn ungesetzt
- dialog: comment_stats-View (group by thread) ersetzt Full-Table-Scan +
  JS-Aggregation bei jedem Forum-Aufruf
- ops: scripts/backup-db.sh (pg_dump, rotiert) + täglicher Cron im Proxmox-
  Script — Dialog-Daten liegen nur in Postgres, nicht in Git
- security: Rate-Limit auf Schreib-Endpunkte (/api non-GET, 60/min je Nutzer)
- perf: Cache-Control (1 Woche) auf statische Assets, HTML bleibt frisch

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01 23:01:12 +02:00

183 lines
8.8 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# OPENBUREAU — Komplettstack für Self-Hosting (Muster gespiegelt von RAPPORT-SERVER).
# ALLES in einem Stack / einem LXC: Supabase-Kern + CMS.
#
# Vor `docker compose up`:
# 1. cp .env.example .env
# 2. JWT_SECRET + POSTGRES_PASSWORD setzen (openssl rand -hex 32)
# 3. node scripts/generate-keys.mjs → ANON_KEY + SERVICE_ROLE_KEY in .env
# 4. SITE_URL + API_EXTERNAL_URL auf die LAN-/Domain-Adresse setzen
# 5. kong.yml: __CORS_ORIGIN__ durch SITE_URL ersetzen (Browser-Origin)
# 6. BIND_ADDR: 127.0.0.1 hinter Reverse-Proxy, 0.0.0.0 für LAN-Direkt
#
# (Das Proxmox-Script erledigt 16 automatisch.)
# Dann: docker compose up -d --build
#
# Abweichung von RAPPORT: realtime + storage weggelassen (nutzt das CMS nicht).
# Nachrüsten = die beiden Service-Blöcke aus RAPPORT-SERVER hier einfügen.
services:
# ════════════════════════════════════════════════════════════════════════
# Postgres — Datenbank mit Supabase-Extensions
# ════════════════════════════════════════════════════════════════════════
db:
image: supabase/postgres:15.8.1.020
container_name: openbureau-db
restart: unless-stopped
environment:
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: postgres
JWT_SECRET: ${JWT_SECRET}
JWT_EXP: 3600
volumes:
- postgres-data:/var/lib/postgresql/data
# OPENBUREAU-Schema (posts-Tabelle) wird vom Post-Init eingespielt.
- ./db/schema.sql:/openbureau-schema.sql:ro
# Läuft als LETZTES Init-Script (zz-) — nach den supabase-internen.
- ./volumes/db/init/zz-openbureau-post-init.sh:/docker-entrypoint-initdb.d/zz-openbureau-post-init.sh:ro
ports:
# Nur localhost — Zugriff über Kong / interne Container-Kommunikation.
- "127.0.0.1:${DB_PORT:-5432}:5432"
healthcheck:
test: ["CMD", "pg_isready", "-U", "supabase_admin", "-d", "postgres"]
interval: 5s
timeout: 5s
retries: 20
start_period: 30s
# ════════════════════════════════════════════════════════════════════════
# Migrate — spielt das (idempotente) Schema bei jedem `up` nach, damit neue
# Tabellen/Spalten auch in eine BESTEHENDE DB kommen (Init-Scripts laufen nur
# beim allerersten Start). Läuft einmal und beendet sich. supabase_admin =
# Superuser → keine Owner-Konflikte. schema.sql ist idempotent.
# ════════════════════════════════════════════════════════════════════════
migrate:
image: supabase/postgres:15.8.1.020
container_name: openbureau-migrate
restart: "no"
depends_on:
db:
condition: service_healthy
environment:
PGPASSWORD: ${POSTGRES_PASSWORD}
volumes:
- ./db/schema.sql:/openbureau-schema.sql:ro
entrypoint: ["bash", "-c"]
command:
- >
psql -h db -U supabase_admin -d postgres -v ON_ERROR_STOP=1 -f /openbureau-schema.sql &&
psql -h db -U supabase_admin -d postgres -c "notify pgrst, 'reload schema';" &&
echo '✓ Schema migriert.'
# ════════════════════════════════════════════════════════════════════════
# GoTrue — Auth (Login für das CMS)
# ════════════════════════════════════════════════════════════════════════
auth:
image: supabase/gotrue:v2.158.1
container_name: openbureau-auth
restart: unless-stopped
depends_on:
db:
condition: service_healthy
environment:
GOTRUE_API_HOST: 0.0.0.0
GOTRUE_API_PORT: 9999
API_EXTERNAL_URL: ${API_EXTERNAL_URL}
GOTRUE_DB_DRIVER: postgres
GOTRUE_DB_DATABASE_URL: postgres://supabase_auth_admin:${POSTGRES_PASSWORD}@db:5432/postgres
GOTRUE_SITE_URL: ${SITE_URL}
GOTRUE_URI_ALLOW_LIST: ${SITE_URL},${SITE_URL}/
# Single-Author: Self-Signup aus. User wird per Admin-API angelegt
# (Kommando steht im README / LXC-Output).
GOTRUE_DISABLE_SIGNUP: "true"
GOTRUE_JWT_ADMIN_ROLES: service_role
GOTRUE_JWT_AUD: authenticated
GOTRUE_JWT_DEFAULT_GROUP_NAME: authenticated
GOTRUE_JWT_EXP: 3600
GOTRUE_JWT_SECRET: ${JWT_SECRET}
GOTRUE_EXTERNAL_EMAIL_ENABLED: "true"
GOTRUE_MAILER_AUTOCONFIRM: "true"
# ════════════════════════════════════════════════════════════════════════
# PostgREST — REST-API auf der DB (supabase-js spricht hierüber mit posts)
# ════════════════════════════════════════════════════════════════════════
rest:
image: postgrest/postgrest:v12.2.0
container_name: openbureau-rest
restart: unless-stopped
depends_on:
db:
condition: service_healthy
environment:
PGRST_DB_URI: postgres://authenticator:${POSTGRES_PASSWORD}@db:5432/postgres
PGRST_DB_SCHEMAS: public
PGRST_DB_ANON_ROLE: anon
PGRST_JWT_SECRET: ${JWT_SECRET}
PGRST_DB_USE_LEGACY_GUCS: "false"
# ════════════════════════════════════════════════════════════════════════
# Kong — API-Gateway: bündelt /auth/v1 + /rest/v1 unter einer URL
# ════════════════════════════════════════════════════════════════════════
kong:
image: kong:2.8.1
container_name: openbureau-kong
restart: unless-stopped
depends_on:
- auth
- rest
environment:
KONG_DATABASE: "off"
KONG_DECLARATIVE_CONFIG: /var/lib/kong/kong.yml
KONG_DNS_ORDER: LAST,A,CNAME
KONG_PLUGINS: request-transformer,cors,key-auth,acl,basic-auth
volumes:
- ./kong.yml:/var/lib/kong/kong.yml:ro
ports:
# Standard 127.0.0.1: nur lokal/Reverse-Proxy erreichbar. Für LAN-Direkt-
# zugriff ohne Proxy BIND_ADDR=0.0.0.0 in .env setzen.
- "${BIND_ADDR:-127.0.0.1}:${KONG_HTTP_PORT:-8000}:8000"
- "${BIND_ADDR:-127.0.0.1}:${KONG_HTTPS_PORT:-8443}:8443"
# ════════════════════════════════════════════════════════════════════════
# CMS — Node-API + Hugo-Binary + Admin-SPA, serviert die Site
# ════════════════════════════════════════════════════════════════════════
cms:
build:
context: .
dockerfile: api/Dockerfile
args:
# Browser-seitig (Admin-SPA, zur Build-Zeit): öffentliche Supabase-URL.
VITE_SUPABASE_URL: ${API_EXTERNAL_URL}
VITE_SUPABASE_ANON_KEY: ${ANON_KEY}
container_name: openbureau-cms
restart: unless-stopped
depends_on:
db:
condition: service_healthy
migrate:
condition: service_completed_successfully
kong:
condition: service_started
environment:
# Server-seitig: intern über Kong, mit Service-Key.
SUPABASE_URL: http://kong:8000
SUPABASE_SERVICE_KEY: ${SERVICE_ROLE_KEY}
# Für lokale JWT-Verifikation (kein GoTrue-Roundtrip pro Request).
JWT_SECRET: ${JWT_SECRET}
ADMIN_EMAILS: ${ADMIN_EMAILS:-}
SITE_DIR: /site
PORT: 3000
GIT_PUBLISH: ${GIT_PUBLISH:-false}
GIT_REMOTE: ${GIT_REMOTE:-origin}
GIT_BRANCH: ${GIT_BRANCH:-main}
GIT_AUTHOR_NAME: ${GIT_AUTHOR_NAME:-OPENBUREAU CMS}
GIT_AUTHOR_EMAIL: ${GIT_AUTHOR_EMAIL:-cms@openbureau.ch}
volumes:
# Repo-Root: api schreibt content/ und baut public/ + preview/.
- ..:/site
ports:
# Wie Kong: standardmäßig nur 127.0.0.1 (hinter Reverse-Proxy).
- "${BIND_ADDR:-127.0.0.1}:${APP_PORT:-8080}:3000"
volumes:
postgres-data: