services:
  minio:
    image: quay.io/minio/minio:latest          
    container_name: minio
    # Host 9000/9001 are often taken; map to free ports on your machine (S3 API / web UI).
    ports:
      - "19000:9000"     # API / S3 endpoint → http://localhost:19000
      - "19001:9001"     # Web console → http://localhost:19001
    environment:
      MINIO_ROOT_USER:     minio_user
      MINIO_ROOT_PASSWORD: minio_password  # Use strong password in real projects!
      # Community MinIO has no per-bucket PutBucketCors (AiStor-only). Browsers need global API CORS for presigned GETs.
      MINIO_API_CORS_ALLOW_ORIGIN: ${MINIO_API_CORS_ALLOW_ORIGIN:-*}
    volumes:
      - ./assets/minio-data:/data
    command: server /data --console-address ":9001"
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
      interval: 30s
      timeout: 20s
      retries: 3
    networks:
      profyt-net:
        ipv4_address: "10.5.0.3"

  # One-shot: ensure buckets. Browser CORS is MINIO_API_CORS_ALLOW_ORIGIN on the minio service (not mc cors set).
  minio-cors:
    image: quay.io/minio/mc:latest
    container_name: minio-cors
    depends_on:
      minio:
        condition: service_healthy
    entrypoint: ["/bin/sh", "-c"]
    command:
      - |
        mc alias set local http://minio:9000 minio_user minio_password
        for b in initiative-attachments initiative-exports initiative-quarantine imagehub-blobs; do
          mc mb -p "local/$$b" 2>/dev/null || true
        done
        echo "MinIO buckets ensured."
    networks:
      profyt-net:
        ipv4_address: "10.5.0.5"
    restart: "no"
  postgres:
    image: postgres:16-alpine
    container_name: initiative-postgres
    # Host 5432 is often taken by a local Postgres; map a different port for host access.
    ports:
      - "15432:5432"
    environment:
      POSTGRES_USER: initiative
      POSTGRES_PASSWORD: initiative_secret
      POSTGRES_DB: initiatives
    volumes:
      - initiative_pg_data:/var/lib/postgresql/data
      # Schema lives under be0 (dyd/0backend/migrations is not in this repo).
      - ./be0/migrations/001_initiative_schema.sql:/docker-entrypoint-initdb.d/01_initiative_schema.sql:ro
      - ./be0/migrations/002_application_storage_extensions.sql:/docker-entrypoint-initdb.d/02_application_storage_extensions.sql:ro
      - ./be0/migrations/003_review_documents.sql:/docker-entrypoint-initdb.d/03_review_documents.sql:ro
      - ./be0/migrations/004_evidence_artifact_review.sql:/docker-entrypoint-initdb.d/04_evidence_artifact_review.sql:ro
      - ./be0/migrations/004_application_admin_results.sql:/docker-entrypoint-initdb.d/05_application_admin_results.sql:ro
      - ./be0/migrations/006_user_notifications.sql:/docker-entrypoint-initdb.d/06_user_notifications.sql:ro
      - ./be0/migrations/007_user_roles_email_policy_admin.sql:/docker-entrypoint-initdb.d/07_user_roles_email_policy_admin.sql:ro
      - ./be0/migrations/008_audit_events.sql:/docker-entrypoint-initdb.d/08_audit_events.sql:ro
      - ./be0/migrations/009_backup_artifact_roles_storage_kind.sql:/docker-entrypoint-initdb.d/09_backup_artifact_roles_storage_kind.sql:ro
      - ./be0/migrations/010_user_staff_profiles.sql:/docker-entrypoint-initdb.d/10_user_staff_profiles.sql:ro
      - ./be0/migrations/011_academic_titles_vn.sql:/docker-entrypoint-initdb.d/11_academic_titles_vn.sql:ro
      - ./be0/migrations/012_password_reset.sql:/docker-entrypoint-initdb.d/12_password_reset.sql:ro
      - ./be0/migrations/013_email_verification.sql:/docker-entrypoint-initdb.d/13_email_verification.sql:ro
      - ./be0/migrations/014_registration_otp.sql:/docker-entrypoint-initdb.d/14_registration_otp.sql:ro
      - ./be0/migrations/015_document_templates.sql:/docker-entrypoint-initdb.d/15_document_templates.sql:ro
      - ./be0/migrations/016_research_projects.sql:/docker-entrypoint-initdb.d/16_research_projects.sql:ro
      - ./be0/migrations/017_imagehub_datasets.sql:/docker-entrypoint-initdb.d/17_imagehub_datasets.sql:ro
      - ./be0/migrations/018_imagehub_segmentation_links.sql:/docker-entrypoint-initdb.d/18_imagehub_segmentation_links.sql:ro
      - ./be0/migrations/019_imagehub_cloud_import.sql:/docker-entrypoint-initdb.d/19_imagehub_cloud_import.sql:ro
      - ./be0/migrations/020_imagehub_dataset_stages.sql:/docker-entrypoint-initdb.d/20_imagehub_dataset_stages.sql:ro
      - ./be0/migrations/021_imagehub_task_pipeline.sql:/docker-entrypoint-initdb.d/21_imagehub_task_pipeline.sql:ro
      - ./be0/migrations/022_imagehub_task_annotations.sql:/docker-entrypoint-initdb.d/22_imagehub_task_annotations.sql:ro
      - ./be0/migrations/023_imagehub_dataset_members.sql:/docker-entrypoint-initdb.d/23_imagehub_dataset_members.sql:ro
      - ./be0/migrations/024_imagehub_dataset_project_link.sql:/docker-entrypoint-initdb.d/24_imagehub_dataset_project_link.sql:ro
      - ./be0/migrations/025_imagehub_task_review_events.sql:/docker-entrypoint-initdb.d/25_imagehub_task_review_events.sql:ro
      - ./be0/migrations/026_imagehub_file_folder_path.sql:/docker-entrypoint-initdb.d/26_imagehub_file_folder_path.sql:ro
      - ./be0/migrations/027_imagehub_dataset_label_map.sql:/docker-entrypoint-initdb.d/27_imagehub_dataset_label_map.sql:ro
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U initiative -d initiatives"]
      interval: 3s
      timeout: 5s
      retries: 20
      start_period: 10s
    restart: unless-stopped
    networks:
      profyt-net:
        ipv4_address: "10.5.0.10"
  
  # ── Frontends ───────────────────────────────────────────────────────────────
  # Two SPAs built from the npm workspace (shared kernel + each app). The browser
  # calls same-origin /api/*; Vite proxies to be0 (localhost:4402 is wrong inside the
  # container). Build context is the repo ROOT — the workspace — not the app dir, so
  # `@ump/shared` (../shared/src) resolves. Dev mode: bind-mount the workspace + reinstall
  # on start so new deps land in the isolated node_modules volume.
  frontend_user:
    build:
      context: .
      dockerfile: frontend_user/Dockerfile
    container_name: frontend_user
    ipc: host
    ports:
      - 8081:5173
    environment:
      - GENERIC_TIMEZONE=UTC
      - VITE_DEV_PROXY_TARGET=http://be0:4402
      # When unset, Vite allows all hosts. Set e.g. YOUR_IP,localhost for cloud/LAN dev.
      - VITE_ALLOWED_HOSTS=${VITE_ALLOWED_HOSTS:-}
    volumes:
      - ./package.json:/app/package.json
      - ./package-lock.json:/app/package-lock.json
      - ./shared:/app/shared
      - ./frontend_user:/app/frontend_user
      - ./frontend_admin:/app/frontend_admin
      - ./frontend_investigator:/app/frontend_investigator
      - ./frontend_publisher:/app/frontend_publisher
      # Isolated workspace-hoisted node_modules (not shadowed by the host).
      - /app/node_modules
    command: ["sh", "-c", "npm install && npm run dev -w frontend_user -- --host 0.0.0.0 --port 5173"]
    depends_on:
      be0:
        condition: service_started
    networks:
      profyt-net:
        ipv4_address: "10.5.0.4"

  frontend_admin:
    build:
      context: .
      dockerfile: frontend_admin/Dockerfile
    container_name: frontend_admin
    ipc: host
    ports:
      - 8082:5174
    environment:
      - GENERIC_TIMEZONE=UTC
      - VITE_DEV_PROXY_TARGET=http://be0:4402
      - VITE_ALLOWED_HOSTS=${VITE_ALLOWED_HOSTS:-}
    volumes:
      - ./package.json:/app/package.json
      - ./package-lock.json:/app/package-lock.json
      - ./shared:/app/shared
      - ./frontend_user:/app/frontend_user
      - ./frontend_admin:/app/frontend_admin
      - ./frontend_investigator:/app/frontend_investigator
      - ./frontend_publisher:/app/frontend_publisher
      - /app/node_modules
    command: ["sh", "-c", "npm install && npm run dev -w frontend_admin -- --host 0.0.0.0 --port 5174"]
    depends_on:
      be0:
        condition: service_started
    networks:
      profyt-net:
        ipv4_address: "10.5.0.6"

  frontend_investigator:
    build:
      context: .
      dockerfile: frontend_investigator/Dockerfile
    container_name: frontend_investigator
    ipc: host
    ports:
      - 8083:5175
    environment:
      - GENERIC_TIMEZONE=UTC
      - VITE_DEV_PROXY_TARGET=http://be0:4402
      - VITE_ALLOWED_HOSTS=${VITE_ALLOWED_HOSTS:-}
    volumes:
      - ./package.json:/app/package.json
      - ./package-lock.json:/app/package-lock.json
      - ./shared:/app/shared
      - ./frontend_user:/app/frontend_user
      - ./frontend_admin:/app/frontend_admin
      - ./frontend_investigator:/app/frontend_investigator
      - ./frontend_publisher:/app/frontend_publisher
      - /app/node_modules
    command: ["sh", "-c", "npm install && npm run dev -w frontend_investigator -- --host 0.0.0.0 --port 5175"]
    depends_on:
      be0:
        condition: service_started
    networks:
      profyt-net:
        ipv4_address: "10.5.0.7"

  frontend_publisher:
    build:
      context: .
      dockerfile: frontend_publisher/Dockerfile
    container_name: frontend_publisher
    ipc: host
    ports:
      - 8084:5176
    environment:
      - GENERIC_TIMEZONE=UTC
      - VITE_DEV_PROXY_TARGET=http://be0:4402
      - VITE_ALLOWED_HOSTS=${VITE_ALLOWED_HOSTS:-}
    volumes:
      - ./package.json:/app/package.json
      - ./package-lock.json:/app/package-lock.json
      - ./shared:/app/shared
      - ./frontend_user:/app/frontend_user
      - ./frontend_admin:/app/frontend_admin
      - ./frontend_investigator:/app/frontend_investigator
      - ./frontend_publisher:/app/frontend_publisher
      - /app/node_modules
    command: ["sh", "-c", "npm install && npm run dev -w frontend_publisher -- --host 0.0.0.0 --port 5176"]
    depends_on:
      be0:
        condition: service_started
    networks:
      profyt-net:
        ipv4_address: "10.5.0.8"

  be0:
    build:
      context: ./be0
      dockerfile: Dockerfile
    container_name: be0
    ipc: host
    ports:
      - 4402:4402
    environment:
      # Dev stack: hot-reload API when bind-mounting ./be0
      - UVICORN_RELOAD=1
      - GENERIC_TIMEZONE=UTC
      - INITIATIVE_DATABASE_URL=postgresql+asyncpg://initiative:initiative_secret@postgres:5432/initiatives
      - APPLICATION_DRAFT_DIR=/app/assets/application-drafts
      # Shared with fe0 `public/submitted-initiatives` so PDFs written by be0 are served by Vite static.
      - SUBMITTED_INITIATIVES_DIR=/app/submitted-initiatives
      # From inside the be0 container, reach MinIO on the shared Docker network (not localhost:19000).
      - S3_ENDPOINT_URL=http://minio:9000
      - S3_ACCESS_KEY=minio_user
      - S3_SECRET_KEY=minio_password
      - S3_BUCKET_ATTACHMENTS=initiative-attachments
      - S3_BUCKET_EXPORTS=initiative-exports
      - S3_BUCKET_QUARANTINE=initiative-quarantine
      # Presigned « Xem / tải » links in the browser must hit the host-mapped MinIO port, not minio:9000.
      - S3_PUBLIC_ENDPOINT_URL=${S3_PUBLIC_ENDPOINT_URL:-http://localhost:19000}
      # Optional: comma-separated; merged with localhost defaults (e.g. http://YOUR_IP:8081 for LAN deploys).
      - CORS_ORIGINS=${CORS_ORIGINS:-}
      # Optional: comma-separated institutional admin emails. When unset, auth_api uses built-in UMP allow-list.
      - AUTH_ADMIN_EMAILS=${AUTH_ADMIN_EMAILS:-}
      # SMTP (Option A) — OTP + password-reset email. Set SMTP_HOST (and secrets) in repo-root .env; see .env.example.
      - SMTP_HOST=${SMTP_HOST:-}
      - SMTP_PORT=${SMTP_PORT:-587}
      - SMTP_USER=${SMTP_USER:-}
      - SMTP_PASSWORD=${SMTP_PASSWORD:-}
      - AUTH_MAIL_FROM=${AUTH_MAIL_FROM:-}
      - SMTP_USE_TLS=${SMTP_USE_TLS:-1}
      - AUTH_PUBLIC_WEB_ORIGIN=${AUTH_PUBLIC_WEB_ORIGIN:-http://localhost:8081}
      # Dev-only: OTP/link in stdout instead of SMTP — leave unset when using SMTP above.
      - AUTH_MAIL_LOG_ONLY=${AUTH_MAIL_LOG_ONLY:-}
      # DOCX mẫu hồ sơ (Xem lại) — cùng file với fe0/public/…/template_application_form.docx
      - TEMPLATE_APPLICATION_FORM_DOCX=/app/template_application_form.docx
    volumes:
      - ./be0:/app
      - ./assets:/app/assets
      - ./assets/submitted-initiatives:/app/submitted-initiatives
      - ./fe0/public/assets/template_application_form.docx:/app/template_application_form.docx:ro
    depends_on:
      postgres:
        condition: service_healthy
      minio:
        condition: service_healthy
      # One-shot minio-cors must finish first so buckets exist (Compose v2.13+).
      minio-cors:
        condition: service_completed_successfully
    networks:
      profyt-net:
        ipv4_address: "10.5.0.2"

volumes:
  initiative_pg_data:

networks:
  profyt-net:
    driver:  bridge
    ipam:  
      config:
        - subnet: "10.5.0.0/16"