AR-3 (P1): PersistKeysToFileSystem + persistentes Volume
Program.cs: AddDataProtection().PersistKeysToFileSystem(keyRingPath).SetApplicationName(GerbilManager)
Pfad konfigurierbar via DataProtection:KeyRingPath (env DataProtection__KeyRingPath);
Fallback = ContentRoot/.data-protection-keys (Aspire-Dev-ephemeral, ok).
compose.yaml: DataProtection__KeyRingPath: /data/keys + Volume-Mount keys:/data/keys.
Volumes: neues 'keys' Volume (Bind-Mount auf NAS-Dataset KEYS_PATH=/mnt/SSD/gerbil/keys).
.gitignore: .data-protection-keys/ ignoriert (Dev-only ephemeral keys).
Verhindert: Gmail-App-Passwort wird nach Image-Redeploy unlesbar (bisher stilles inbox-fail).
AR-4 (P1): compose.yaml + .env.example: AI__* statt ANTHROPIC_API_KEY
compose.yaml: ANTHROPIC_API_KEY entfernt (Code liest es nicht). Korrekte Vars:
AI__BaseUrl: ${AI__BaseUrl:-}
AI__ApiKey: ${AI__ApiKey:-}
AI__Model: ${AI__Model:-gemini-2.0-flash}
.env.example: AI__BaseUrl/ApiKey/Model + KEYS_PATH hinzugefuegt; ANTHROPIC_API_KEY entfernt.
Quelle: docs/ai-provider.md (war korrekt, compose war falsch).
Verhindert: alle 4 KI-Features (Verkaufstext, Inbox-Entwurf) blieben in prod stumm.
GATE: 139/139 C#-Tests, build gruen (using Microsoft.AspNetCore.DataProtection; framework-included).
125 lines
3.9 KiB
YAML
125 lines
3.9 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 ---
|
|
db:
|
|
image: postgres:17-alpine
|
|
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:-truenas:13000/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-2.0-flash}"
|
|
volumes:
|
|
- photos:/data/photos
|
|
- keys:/data/keys
|
|
depends_on:
|
|
db:
|
|
condition: service_healthy
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "wget -qO- http://localhost:8080/health || exit 1"]
|
|
interval: 30s
|
|
timeout: 10s
|
|
retries: 3
|
|
start_period: 90s
|
|
|
|
# --- nginx Frontend (React SPA + API-Proxy) ---
|
|
frontend:
|
|
image: "${REGISTRY:-truenas:13000/gulum}/gerbilmanager-frontend:${TAG:-latest}"
|
|
build:
|
|
context: ../..
|
|
dockerfile: gerbil-manager-web/Dockerfile
|
|
restart: unless-stopped
|
|
ports:
|
|
- "${PORT:-80}:80"
|
|
depends_on:
|
|
api:
|
|
condition: service_healthy
|
|
|
|
# --- Backup-Sidecar (taeglicher pg_dump + Foto-Archiv + Rotation) ---
|
|
backup:
|
|
image: postgres:17-alpine
|
|
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: Dataset-Pfad z.B. /mnt/SSD/gerbil/pgdata
|
|
pgdata:
|
|
driver: local
|
|
driver_opts:
|
|
type: none
|
|
o: bind
|
|
device: "${PGDATA_PATH:-/mnt/gerbil/pgdata}"
|
|
photos:
|
|
driver: local
|
|
driver_opts:
|
|
type: none
|
|
o: bind
|
|
device: "${PHOTOS_PATH:-/mnt/gerbil/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/gerbil/keys}"
|
|
backups:
|
|
driver: local
|
|
driver_opts:
|
|
type: none
|
|
o: bind
|
|
device: "${BACKUPS_PATH:-/mnt/gerbil/backups}"
|