# ============================================================ # Dockhand / Portainer Stack File # Verwendet das vorgebaute Image aus der Gitea-Registry. # Vor dem Deploy: .env Datei im Stack-Verzeichnis ablegen! # ============================================================ services: app: image: gitea.leonkn.de/lxe/workflow-app:DEV2.0-v7 ports: - "3900:5000" volumes: - app-data:/app/data environment: - LDAP_SERVER=${LDAP_SERVER:-} - LDAP_PORT=${LDAP_PORT:-389} - LDAP_SEARCH_BASE=${LDAP_SEARCH_BASE:-} - LDAP_DOMAIN=${LDAP_DOMAIN:-} - LDAP_IGNORE_CERT_ERRORS=${LDAP_IGNORE_CERT_ERRORS:-false} - LDAP_BIND_USER=${LDAP_BIND_USER:-} - LDAP_BIND_PASSWORD=${LDAP_BIND_PASSWORD:-} - LDAP_SYNC_INTERVAL=${LDAP_SYNC_INTERVAL:-300000} - LDAP_FILTER=${LDAP_FILTER:-} - LDAP_ATTRIBUTES=${LDAP_ATTRIBUTES:-mail,displayName,memberOf,distinguishedName,sAMAccountName} - LDAP_CREATE_OU=${LDAP_CREATE_OU:-} - LDAP_UPN_SUFFIX=${LDAP_UPN_SUFFIX:-} - ADMIN_EMAIL=${ADMIN_EMAIL:-admin@workflow.local} - ADMIN_INIT_PASSWORD=${ADMIN_INIT_PASSWORD:-} # M3: Session-Secret für CSRF-Token-Ableitung (Pflicht in Produktion) - SESSION_SECRET=${SESSION_SECRET:-} # M1: Cookie/HSTS-Sicherheit (false für plain HTTP, true hinter TLS-Proxy) - COOKIE_SECURE=${COOKIE_SECURE:-false} - HSTS_ENABLED=${HSTS_ENABLED:-false} # H3: Massenlösch-Schutz beim LDAP-Sync (max % löschbarer AD-User pro Sync) - LDAP_MAX_DELETE_PCT=${LDAP_MAX_DELETE_PCT:-25} # Upload-Größenlimit in MB - UPLOAD_MAX_MB=${UPLOAD_MAX_MB:-10} - CORS_ORIGIN=${CORS_ORIGIN:-http://localhost:3900} - NODE_ENV=${NODE_ENV:-production} # H3: Proxy-Header vertrauen (true hinter HAProxy/Nginx, false bei direktem Zugriff) - TRUST_PROXY=${TRUST_PROXY:-true} - DATABASE_URL=postgresql://${POSTGRES_USER:-workflow}:${POSTGRES_PASSWORD:-workflow}@db:5432/${POSTGRES_DB:-workflow} - SESSION_MAX_PER_USER=${SESSION_MAX_PER_USER:-5} - SESSION_TTL_HOURS=${SESSION_TTL_HOURS:-168} - LOGIN_MAX_ATTEMPTS=${LOGIN_MAX_ATTEMPTS:-5} - LOGIN_LOCKOUT_MINUTES=${LOGIN_LOCKOUT_MINUTES:-15} - BODY_LIMIT=${BODY_LIMIT:-1mb} depends_on: db: condition: service_healthy restart: unless-stopped db: image: postgres:16-alpine environment: - POSTGRES_DB=${POSTGRES_DB:-workflow} - POSTGRES_USER=${POSTGRES_USER:-workflow} - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-workflow} volumes: - pgdata:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-workflow} -d ${POSTGRES_DB:-workflow}"] interval: 10s timeout: 5s retries: 5 restart: unless-stopped db-backup: image: postgres:16-alpine environment: - POSTGRES_USER=${POSTGRES_USER:-workflow} - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-workflow} - POSTGRES_DB=${POSTGRES_DB:-workflow} - BACKUP_INTERVAL_HOURS=${BACKUP_INTERVAL_HOURS:-6} - BACKUP_RETENTION_DAYS=${BACKUP_RETENTION_DAYS:-30} - PGHOST=db - PGUSER=${POSTGRES_USER:-workflow} - PGPASSWORD=${POSTGRES_PASSWORD:-workflow} volumes: - backups:/backups entrypoint: | /bin/sh -c " echo '[Backup] Starting backup service (interval: '$$BACKUP_INTERVAL_HOURS'h, retention: '$$BACKUP_RETENTION_DAYS'd)'; while true; do TIMESTAMP=$$(date +%Y%m%d_%H%M%S); FILENAME=/backups/workflow_$$TIMESTAMP.sql.gz; echo '[Backup] Creating backup: '$$FILENAME; pg_dump -h db -U $$PGUSER $$POSTGRES_DB | gzip > $$FILENAME; echo '[Backup] Done: '$$FILENAME; find /backups -name 'workflow_*.sql.gz' -mtime +$$BACKUP_RETENTION_DAYS -delete; echo '[Backup] Cleaned old backups (older than '$$BACKUP_RETENTION_DAYS' days)'; SLEEP_SECONDS=$$((BACKUP_INTERVAL_HOURS * 3600)); sleep $$SLEEP_SECONDS; done " depends_on: db: condition: service_healthy restart: unless-stopped volumes: pgdata: app-data: backups: