Deployment: - custom-app.compose.yaml: self-contained Compose fuer TrueNAS "Custom App" (absolute Host-Bind-Pfade, postgres:18, pull_policy always, Port 8090) - scripts/truenas-deploy.sh: Host-Skript create/redeploy via midclt (App bleibt unter Apps sichtbar) inkl. Image-Pull + Health-Check - ci.yml Deploy-Job: laeuft auf ubuntu-latest-Runner, kopiert Deploy-Dateien per SSH auf den NAS-Host und triggert truenas-deploy.sh (statt runs-on goldeye) - compose.yaml/.env.example: postgres:18 (Locale-Match zur Quell-DB), Port 8090 - .gitignore: .agents/, tools/rag/, deploy/truenas/.env (Secrets/Scratch) Aufgelaufene Feature-Arbeit (verified/Freeze, Migrationen, Import-Triage): - GerbilOverride/VerifiedGerbil-Endpoints + GerbilSnapshotService + Tests - EF-Migrationen (ShowInChronicle, Stillborn, BirthOrder, ManualFlag, DSGVO) - Frontend VerifizierteTierePage + verified-API + e2e-Spec - diverse Import-/Triage-Skripte und -Tests Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
152 lines
5.2 KiB
YAML
152 lines
5.2 KiB
YAML
# GerbilManager — TrueNAS Custom Application
|
|
# ==============================================
|
|
# Vor dem ersten Start:
|
|
# 1. Kopiere deploy/truenas/.env.example -> deploy/truenas/.env und setze die Werte.
|
|
# 2. Lege die Dataset-Pfade auf der NAS an (pgdata, photos, backups).
|
|
# 3. docker compose -f deploy/truenas/compose.yaml up -d
|
|
#
|
|
# Zugriff: http://<NAS-IP>:${PORT:-80}
|
|
# API-Doku: http://<NAS-IP>:${PORT:-80}/scalar
|
|
|
|
services:
|
|
|
|
# --- PostgreSQL-Datenbank ---
|
|
# postgres:18 (Debian): Locale en_US.utf8 == Quell-DB (Aspire) -> sauberer Dump-Restore.
|
|
db:
|
|
image: postgres:18
|
|
restart: unless-stopped
|
|
environment:
|
|
POSTGRES_PASSWORD: "${POSTGRES_PASSWORD}"
|
|
POSTGRES_DB: gerbilmanager
|
|
POSTGRES_USER: postgres
|
|
volumes:
|
|
- pgdata:/var/lib/postgresql/data
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "pg_isready -U postgres -d gerbilmanager"]
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 5
|
|
start_period: 30s
|
|
|
|
# --- .NET API (GerbilManagerWebAPI) ---
|
|
api:
|
|
image: "${REGISTRY:-git.rismer.de/gulum}/gerbilmanager-api:${TAG:-latest}"
|
|
build:
|
|
context: ../..
|
|
dockerfile: GerbilManagerWebAPI/Dockerfile
|
|
restart: unless-stopped
|
|
environment:
|
|
ASPNETCORE_ENVIRONMENT: Production
|
|
# Verbindung zur Postgres-DB im selben Compose-Netz
|
|
ConnectionStrings__gerbilmanager: "Host=db;Port=5432;Database=gerbilmanager;Username=postgres;Password=${POSTGRES_PASSWORD}"
|
|
# Speicherort der hochgeladenen Fotos (NAS-Dataset gemounted unter /data/photos)
|
|
Photos__RootPath: /data/photos
|
|
# AR-3: Data Protection Key-Ring (persistiert Gmail-App-Passwort-Verschlüsselung über Redeployments)
|
|
DataProtection__KeyRingPath: /data/keys
|
|
# AR-4: KI-Funktionen (Verkaufstext + Posteingang-Entwurf, Sektion AI; beliebiger OpenAI-kompatibler Anbieter)
|
|
# Anbieter-Optionen und Schlüssel-Beispiele: docs/ai-provider.md
|
|
# Leer lassen = KI deaktiviert (503 AiKeyMissing statt Fehler)
|
|
AI__BaseUrl: "${AI__BaseUrl:-}"
|
|
AI__ApiKey: "${AI__ApiKey:-}"
|
|
AI__Model: "${AI__Model:-gemini-flash-latest}"
|
|
# WEB-2: Pfad wo POST /api/publish die oeffentliche Seite hinschreibt
|
|
PublicSite__RootPath: /data/publicsite
|
|
volumes:
|
|
- photos:/data/photos
|
|
- keys:/data/keys
|
|
- publicsite:/data/publicsite
|
|
depends_on:
|
|
db:
|
|
condition: service_healthy
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "curl -fsS http://localhost:8080/health || exit 1"]
|
|
interval: 30s
|
|
timeout: 10s
|
|
retries: 3
|
|
start_period: 90s
|
|
|
|
# --- nginx Frontend (React SPA + API-Proxy) ---
|
|
frontend:
|
|
image: "${REGISTRY:-git.rismer.de/gulum}/gerbilmanager-frontend:${TAG:-latest}"
|
|
build:
|
|
context: ../..
|
|
dockerfile: gerbil-manager-web/Dockerfile
|
|
restart: unless-stopped
|
|
ports:
|
|
# NAS-Port 80 ist durch den nginx-Reverse-Proxy belegt -> Default 8090.
|
|
- "${PORT:-8090}:80"
|
|
depends_on:
|
|
api:
|
|
condition: service_healthy
|
|
|
|
# --- nginx Public Site (WEB-2) ---
|
|
# Serviert NUR die statische oeffentliche Seite (live/ aus dem publicsite-Volume).
|
|
# SICHERHEIT: Kein Proxy auf api/frontend — nur statisches HTML nach aussen.
|
|
# Julian's externer nginx-Proxy leitet <DOMAIN> auf Port 8081 weiter.
|
|
publicsite:
|
|
image: nginx:alpine
|
|
restart: unless-stopped
|
|
ports:
|
|
- "${PUBLICSITE_PORT:-8081}:80"
|
|
volumes:
|
|
- publicsite:/usr/share/nginx/html:ro
|
|
- ./nginx/publicsite.conf:/etc/nginx/conf.d/default.conf:ro
|
|
depends_on:
|
|
- api
|
|
|
|
# --- Backup-Sidecar (taeglicher pg_dump + Foto-Archiv + Rotation) ---
|
|
backup:
|
|
image: postgres:18
|
|
restart: unless-stopped
|
|
environment:
|
|
PGPASSWORD: "${POSTGRES_PASSWORD}"
|
|
POSTGRES_HOST: db
|
|
POSTGRES_USER: postgres
|
|
POSTGRES_DB: gerbilmanager
|
|
BACKUP_KEEP_DAYS: "${BACKUP_KEEP_DAYS:-7}"
|
|
volumes:
|
|
- photos:/data/photos:ro
|
|
- backups:/backups
|
|
- ./scripts:/scripts:ro
|
|
depends_on:
|
|
db:
|
|
condition: service_healthy
|
|
entrypoint: ["/bin/sh", "/scripts/entrypoint.sh"]
|
|
|
|
volumes:
|
|
# NAS-Datasets als Bind-Mounts (Pfade in .env konfigurieren).
|
|
# TrueNAS Goldeye: /mnt/JailStorage/DockerVolumes/gerbilmanager/<name>
|
|
pgdata:
|
|
driver: local
|
|
driver_opts:
|
|
type: none
|
|
o: bind
|
|
device: "${PGDATA_PATH:-/mnt/JailStorage/DockerVolumes/gerbilmanager/pgdata}"
|
|
photos:
|
|
driver: local
|
|
driver_opts:
|
|
type: none
|
|
o: bind
|
|
device: "${PHOTOS_PATH:-/mnt/JailStorage/DockerVolumes/gerbilmanager/photos}"
|
|
# AR-3: Data Protection key ring — persistiert Gmail-App-Passwort-Verschlüsselung.
|
|
# Muss ein persistentes NAS-Dataset sein (nicht dasselbe wie photos).
|
|
keys:
|
|
driver: local
|
|
driver_opts:
|
|
type: none
|
|
o: bind
|
|
device: "${KEYS_PATH:-/mnt/JailStorage/DockerVolumes/gerbilmanager/keys}"
|
|
backups:
|
|
driver: local
|
|
driver_opts:
|
|
type: none
|
|
o: bind
|
|
device: "${BACKUPS_PATH:-/mnt/JailStorage/DockerVolumes/gerbilmanager/backups}"
|
|
# WEB-2: gemeinsames Volume fuer api (rw) und publicsite-nginx (ro).
|
|
publicsite:
|
|
driver: local
|
|
driver_opts:
|
|
type: none
|
|
o: bind
|
|
device: "${PUBLICSITE_PATH:-/mnt/JailStorage/DockerVolumes/gerbilmanager/publicsite}"
|