name: ai-drama-platform # Deployment boundary for a commercial private installation. # The current application runtime still uses SQLite through AI_DRAMA_DB_PATH. # PostgreSQL, Redis and S3-compatible storage are provisioned here as the # target provider contract; switching business runtime adapters is a separate # implementation step and must not be inferred from this compose file. services: postgres: image: postgres:16-alpine restart: unless-stopped environment: POSTGRES_DB: ${POSTGRES_DB:-ai_drama} POSTGRES_USER: ${POSTGRES_USER:-ai_drama} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD} volumes: - postgres-data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"] interval: 10s timeout: 5s retries: 10 redis: image: redis:7-alpine restart: unless-stopped command: ["redis-server", "--appendonly", "yes", "--requirepass", "${REDIS_PASSWORD:?set REDIS_PASSWORD}"] volumes: - redis-data:/data healthcheck: test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"] interval: 10s timeout: 5s retries: 10 object-storage: image: minio/minio:latest restart: unless-stopped command: server /data --console-address ":9001" environment: MINIO_ROOT_USER: ${MINIO_ROOT_USER:?set MINIO_ROOT_USER} MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:?set MINIO_ROOT_PASSWORD} volumes: - object-storage-data:/data ports: - "${MINIO_API_PORT:-9000}:9000" - "${MINIO_CONSOLE_PORT:-9001}:9001" healthcheck: test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"] interval: 10s timeout: 5s retries: 10 api: build: context: .. dockerfile: deploy/api.Dockerfile restart: unless-stopped environment: AI_DRAMA_API_PORT: 8787 AI_DRAMA_API_ORIGIN: ${PUBLIC_ORIGIN:-http://localhost} AI_DRAMA_FRONTEND_ORIGIN: ${PUBLIC_ORIGIN:-http://localhost} AI_DRAMA_DB_PATH: /app/data/platform.sqlite AI_DRAMA_ALLOW_DEV_CONTEXT: "0" AI_DRAMA_SESSION_SECRET: ${AI_DRAMA_SESSION_SECRET:?set AI_DRAMA_SESSION_SECRET} AI_DRAMA_OIDC_STORAGE_KEY: ${AI_DRAMA_OIDC_STORAGE_KEY:?set AI_DRAMA_OIDC_STORAGE_KEY} AI_DRAMA_MFA_ENCRYPTION_KEY: ${AI_DRAMA_MFA_ENCRYPTION_KEY:?set AI_DRAMA_MFA_ENCRYPTION_KEY} AI_DRAMA_SAML_IDP_CERT: ${AI_DRAMA_SAML_IDP_CERT:-} PLATFORM_POSTGRES_URL: postgresql://${POSTGRES_USER:-ai_drama}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:-ai_drama} PLATFORM_REDIS_URL: redis://:${REDIS_PASSWORD}@redis:6379/0 PLATFORM_OBJECT_STORAGE_ENDPOINT: http://object-storage:9000 PLATFORM_OBJECT_STORAGE_BUCKET: ${MINIO_BUCKET:-ai-drama} PLATFORM_OBJECT_STORAGE_ACCESS_KEY: ${MINIO_ROOT_USER} PLATFORM_OBJECT_STORAGE_SECRET_KEY: ${MINIO_ROOT_PASSWORD} volumes: - platform-data:/app/data - platform-storage:/app/storage - platform-exports:/app/exports depends_on: postgres: condition: service_healthy redis: condition: service_healthy object-storage: condition: service_healthy expose: - "8787" healthcheck: test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:8787/api/health').then(r => { if (!r.ok) process.exit(1) }).catch(() => process.exit(1))"] interval: 10s timeout: 5s retries: 12 worker: build: context: .. dockerfile: deploy/api.Dockerfile restart: unless-stopped command: ["npm", "run", "worker"] environment: AI_DRAMA_DB_PATH: /app/data/platform.sqlite AI_DRAMA_SESSION_SECRET: ${AI_DRAMA_SESSION_SECRET:?set AI_DRAMA_SESSION_SECRET} PLATFORM_REDIS_URL: redis://:${REDIS_PASSWORD}@redis:6379/0 volumes: - platform-data:/app/data - platform-storage:/app/storage - platform-exports:/app/exports depends_on: api: condition: service_healthy frontend: build: context: .. dockerfile: deploy/frontend.Dockerfile args: VITE_API_BASE: ${VITE_API_BASE:-} restart: unless-stopped ports: - "${HTTP_PORT:-80}:80" depends_on: api: condition: service_healthy volumes: postgres-data: redis-data: object-storage-data: platform-data: platform-storage: platform-exports: