# 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://:${PORT:-80} # API-Doku: http://:${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: # postgres:18: Volume auf /var/lib/postgresql (PGDATA landet in .../18/docker). # Mount auf .../data laesst v18 nicht starten -> db unhealthy. - pgdata:/var/lib/postgresql 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 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/ 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}"