Files
workflow-app/docker-compose.yml
Kühn 1aec65dc95 Security & UX Release v7
Security:
- H1: Stored-XSS-Fix — Upload-Pfad-Whitelist (Server + Frontend-Guard safeFileUrl)
- H2: Transaktionen repariert — txDb-Contract in db.js (PG + SQLite), Rollback funktioniert
- H3-Vorbereitung: SESSION_SECRET wird in Compose durchgereicht (Fix M3)
- registerLimiter exportiert (Crash-Bug: Route.post ohne Callback)
- LDAP-Sync: PG-Transaktionsabbruch bei UNIQUE-Verstoß behoben (Precheck-Selects)
- LDAP-Filter: nur echte Benutzerkonten (keine Computer/Service-Accounts, Bit 512)
- Rollen app-seitig: Sync ändert nie role/status, neue User immer user+inaktiv
- DB-Cleanup: 82 Computer-/Service-Accounts aus lokaler User-Tabelle entfernt

UX:
- Dashboard: Vorlagen als Table-Liste + Column-Chart (Top 5 in %), 2 gleich große Spalten
- Table-Listen (Dashboard/Vorlageneditor/Aufgaben) scrollbar bis Seitenende
- Pagination 10/Seite im Dashboard, Sidebar-Label Dashboard
2026-09-10 16:57:20 +02:00

93 lines
3.5 KiB
YAML

services:
app:
build: .
ports:
- "3900:5000"
volumes:
- ./backend/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:-}
- CORS_ORIGIN=${CORS_ORIGIN:-http://localhost:5000}
- NODE_ENV=${NODE_ENV:-production}
# Punkt 4: PostgreSQL (auto-started)
- DATABASE_URL=postgresql://${POSTGRES_USER:-workflow}:${POSTGRES_PASSWORD:-workflow}@db:5432/${POSTGRES_DB:-workflow}
# Punkt 9: Session-Limitierung
- SESSION_MAX_PER_USER=${SESSION_MAX_PER_USER:-5}
- SESSION_TTL_HOURS=${SESSION_TTL_HOURS:-168}
# Punkt 12: Account-Lockout
- LOGIN_MAX_ATTEMPTS=${LOGIN_MAX_ATTEMPTS:-5}
- LOGIN_LOCKOUT_MINUTES=${LOGIN_LOCKOUT_MINUTES:-15}
# Punkt 14: Body-Size-Limit
- BODY_LIMIT=${BODY_LIMIT:-1mb}
depends_on:
db:
condition: service_healthy
restart: unless-stopped
# Punkt 4: PostgreSQL Database (auto-started)
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
# Punkt 5: DB-Backup Service (auto-started)
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: