name: app services: # ── Base de datos ──────────────────────────────────────────────────────────── postgres: image: postgres:18-alpine container_name: app-postgres environment: POSTGRES_DB: ${CORE_DB_NAME:-app_core} POSTGRES_USER: ${CORE_DB_USER:-postgres} POSTGRES_PASSWORD: ${POSTGRES_APP_PASSWORD:-postgres} POSTGRES_INITDB_ARGS: "--encoding=UTF8" ports: - "5432:5432" volumes: - postgres_data:/var/lib/postgresql networks: - app-backend restart: unless-stopped healthcheck: test: ["CMD-SHELL", "pg_isready -U ${CORE_DB_USER:-postgres} -d ${CORE_DB_NAME:-app_core} || exit 1"] interval: 5s timeout: 3s retries: 10 start_period: 20s logging: driver: "json-file" options: max-size: "10m" max-file: "3" deploy: resources: limits: memory: 512M reservations: memory: 256M shm_size: 128mb # ── Backend FastAPI ────────────────────────────────────────────────────────── backend: build: context: ./backend dockerfile: Dockerfile args: - BUILDKIT_INLINE_CACHE=1 image: app-backend:latest container_name: app-backend environment: - DEBUG=${DEBUG:-True} - ENVIRONMENT=${ENVIRONMENT:-development} - PYTHONUNBUFFERED=1 - PYTHONDONTWRITEBYTECODE=1 - DEV_LOCAL_AUTH=${DEV_LOCAL_AUTH:-False} - DEV_LOCAL_AUTH_EMAIL=${DEV_LOCAL_AUTH_EMAIL:-dev@local.test} - DEV_LOCAL_AUTH_NAME=${DEV_LOCAL_AUTH_NAME:-Dev User} - DEV_LOCAL_AUTH_TENANT_ID=${DEV_LOCAL_AUTH_TENANT_ID:-1} - DEV_LOCAL_AUTH_COMPANY_ID=${DEV_LOCAL_AUTH_COMPANY_ID:-1} - CORE_DB_HOST=${CORE_DB_HOST:-postgres} - CORE_DB_PORT=${CORE_DB_PORT:-5432} - CORE_DB_NAME=${CORE_DB_NAME:-app_core} - CORE_DB_USER=${CORE_DB_USER:-postgres} - CORE_DB_PASSWORD=${POSTGRES_APP_PASSWORD:-postgres} - SECRET_KEY=${SECRET_KEY:-change-this-secret-key-in-production} - CORS_ORIGINS=${CORS_ORIGINS:-http://localhost:5173,http://localhost:3000} - VALKEY_URL=${VALKEY_URL:-redis://valkey:6379/0} - CENTRAL_SERVER_URL=${CENTRAL_SERVER_URL:-""} - SYNC_SECRET_TOKEN=${SYNC_SECRET_TOKEN:-change-this-sync-token-in-production} - SPOKE_URLS=${SPOKE_URLS:-""} - HUB_URL=${WORKSPACE_URL:-http://host.docker.internal:8001} - HUB_ADMIN_EMAIL=${HUB_ADMIN_EMAIL:-} - HUB_ADMIN_PASSWORD=${HUB_ADMIN_PASSWORD:-} - APP_PUBLIC_URL=${APP_PUBLIC_URL:-http://localhost:5173} - S3_ENDPOINT_URL=${S3_ENDPOINT_URL:-http://minio:9000} - S3_ACCESS_KEY=${S3_ACCESS_KEY:-minioadmin} - S3_SECRET_KEY=${S3_SECRET_KEY:-minioadmin} - S3_BUCKET=${S3_BUCKET:-app} - S3_REGION=${S3_REGION:-us-east-1} - S3_USE_SSL=${S3_USE_SSL:-false} - S3_FILE_STORAGE=${S3_FILE_STORAGE:-true} - S3_PRESIGNED_EXPIRES_SECONDS=${S3_PRESIGNED_EXPIRES_SECONDS:-3600} - CSV_IMPORT_STORAGE=${CSV_IMPORT_STORAGE:-minio} ports: - "8000:8000" depends_on: postgres: condition: service_healthy volumes: - ./backend:/app - backend_cache:/app/__pycache__ - backend_uploads:/app/uploads networks: - app-backend - app-frontend - app-hub restart: unless-stopped command: ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--reload", "--log-level", "info"] healthcheck: test: ["CMD-SHELL", "curl -f http://localhost:8000/api/health || exit 1"] interval: 20s timeout: 10s retries: 20 start_period: 600s logging: driver: "json-file" options: max-size: "10m" max-file: "3" deploy: resources: limits: memory: 1024M reservations: memory: 512M # ── Frontend SvelteKit ─────────────────────────────────────────────────────── frontend: build: context: ./frontend dockerfile: Dockerfile args: - BUILDKIT_INLINE_CACHE=1 image: app-frontend:latest container_name: app-frontend environment: - NODE_ENV=${NODE_ENV:-development} - DEV_LOCAL_AUTH=${DEV_LOCAL_AUTH:-false} - BACKEND_URL=${BACKEND_URL:-http://backend:8000} - VITE_API_URL=${VITE_API_URL:-http://localhost:8000/api/} - INTERNAL_API_URL=${INTERNAL_API_URL:-http://backend:8000/api/} # Hub y Keycloak se derivan de WORKSPACE_URL (una sola fuente de verdad) - VITE_HUB_URL=${WORKSPACE_URL:-http://localhost:8001} - HUB_URL=${WORKSPACE_URL:-http://localhost:8001} - INTERNAL_HUB_URL=${WORKSPACE_URL:-http://localhost:8001} - VITE_KEYCLOAK_URL=${WORKSPACE_URL:-http://localhost:8085}/kcauth - KEYCLOAK_URL=${WORKSPACE_URL:-http://localhost:8085}/kcauth - VITE_KEYCLOAK_REALM=${KEYCLOAK_REALM:-master} - KEYCLOAK_REALM=${KEYCLOAK_REALM:-master} - VITE_KEYCLOAK_CLIENT_ID=${KEYCLOAK_CLIENT_ID:-app-frontend} - KEYCLOAK_CLIENT_ID=${KEYCLOAK_CLIENT_ID:-app-frontend} - CORS_ORIGINS=${CORS_ORIGINS:-http://localhost:5173} - TRUSTED_ORIGINS=${TRUSTED_ORIGINS:-} - ORIGIN=${ORIGIN:-http://localhost:5173} ports: - "5173:5173" depends_on: backend: condition: service_healthy volumes: - ./frontend:/app - frontend_node_modules:/app/node_modules networks: - app-frontend - app-hub restart: unless-stopped command: ["pnpm", "run", "dev", "--", "--host", "0.0.0.0"] healthcheck: test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:5173/ || exit 1"] interval: 15s timeout: 5s retries: 5 start_period: 45s logging: driver: "json-file" options: max-size: "10m" max-file: "3" # ── Celery Worker ──────────────────────────────────────────────────────────── celery_worker: build: ./backend image: app-worker:latest container_name: app-worker command: celery -A core.celery_app worker --loglevel=info environment: - DEBUG=${DEBUG:-True} - ENVIRONMENT=${ENVIRONMENT:-development} - PYTHONUNBUFFERED=1 - CORE_DB_HOST=${CORE_DB_HOST:-postgres} - CORE_DB_PORT=${CORE_DB_PORT:-5432} - CORE_DB_NAME=${CORE_DB_NAME:-app_core} - CORE_DB_USER=${CORE_DB_USER:-postgres} - CORE_DB_PASSWORD=${POSTGRES_APP_PASSWORD:-postgres} - VALKEY_URL=${VALKEY_URL:-redis://valkey:6379/0} - S3_ENDPOINT_URL=${S3_ENDPOINT_URL:-http://minio:9000} - S3_ACCESS_KEY=${S3_ACCESS_KEY:-minioadmin} - S3_SECRET_KEY=${S3_SECRET_KEY:-minioadmin} - S3_BUCKET=${S3_BUCKET:-app} - CSV_IMPORT_STORAGE=${CSV_IMPORT_STORAGE:-minio} depends_on: backend: condition: service_healthy valkey: condition: service_started volumes: - ./backend:/app - backend_cache:/app/__pycache__ - backend_uploads:/app/uploads networks: - app-backend # ── Celery Beat ────────────────────────────────────────────────────────────── celery_beat: build: ./backend image: app-celery-beat:latest container_name: app-celery-beat command: celery -A core.celery_app beat --loglevel=info environment: - CORE_DB_HOST=${CORE_DB_HOST:-postgres} - CORE_DB_PORT=${CORE_DB_PORT:-5432} - CORE_DB_NAME=${CORE_DB_NAME:-app_core} - CORE_DB_USER=${CORE_DB_USER:-postgres} - CORE_DB_PASSWORD=${POSTGRES_APP_PASSWORD:-postgres} - VALKEY_URL=${VALKEY_URL:-redis://valkey:6379/0} depends_on: backend: condition: service_healthy valkey: condition: service_started volumes: - ./backend:/app - backend_cache:/app/__pycache__ networks: - app-backend # ── Valkey (Redis) ─────────────────────────────────────────────────────────── valkey: image: valkey/valkey:7.2 container_name: app-valkey restart: always ports: - "6379:6379" networks: - app-backend # ── MinIO (object storage) ─────────────────────────────────────────────────── minio: image: minio/minio:RELEASE.2025-09-07T16-13-09Z container_name: app-minio command: server /data --console-address ":9001" environment: MINIO_ROOT_USER: ${MINIO_ROOT_USER:-minioadmin} MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:-minioadmin} ports: - "${MINIO_API_PORT:-9100}:9000" - "${MINIO_CONSOLE_PORT:-9101}:9001" volumes: - minio_data:/data networks: - app-backend restart: unless-stopped healthcheck: test: ["CMD-SHELL", "curl -f http://127.0.0.1:9000/minio/health/live || exit 1"] interval: 10s timeout: 5s retries: 5 start_period: 15s logging: driver: "json-file" options: max-size: "10m" max-file: "3" volumes: postgres_data: driver: local frontend_node_modules: driver: local backend_cache: driver: local backend_uploads: driver: local minio_data: driver: local networks: app-backend: driver: bridge app-frontend: driver: bridge app-hub: external: true name: aduanasoft-hub_default