# Portainer Stack Definition for School Compare
#
# Portainer environment variables (set in Portainer UI -> Stack -> Environment):
#   DB_USERNAME            — PostgreSQL username
#   DB_PASSWORD            — PostgreSQL password
#   DB_DATABASE_NAME       — PostgreSQL database name
#   ADMIN_API_KEY          — Backend admin API key
#   TYPESENSE_API_KEY      — Typesense admin API key
#   TYPESENSE_SEARCH_KEY   — Typesense search-only key (exposed to frontend)
#   AIRFLOW_ADMIN_USER     — Airflow admin username (password auto-generated, see api-server logs)

services:

  # ── PostgreSQL ────────────────────────────────────────────────────────
  sc_database:
    container_name: sc_postgres
    image: postgis/postgis:18-3.6-alpine
    environment:
      POSTGRES_PASSWORD: ${DB_PASSWORD}
      POSTGRES_USER: ${DB_USERNAME}
      POSTGRES_DB: ${DB_DATABASE_NAME}
    volumes:
      - postgres_data:/var/lib/postgresql
    shm_size: 128mb
    networks:
      backend: {}
      macvlan:
        ipv4_address: 10.0.1.189
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 10s
    restart: unless-stopped

  # ── FastAPI Backend ───────────────────────────────────────────────────
  backend:
    image: privaterepo.sitaru.org/tudor/school_compare-backend:latest
    container_name: schoolcompare_backend
    environment:
      DATABASE_URL: postgresql://${DB_USERNAME}:${DB_PASSWORD}@sc_database:5432/${DB_DATABASE_NAME}
      PYTHONUNBUFFERED: 1
      ADMIN_API_KEY: ${ADMIN_API_KEY:-changeme}
      TYPESENSE_URL: http://typesense:8108
      TYPESENSE_API_KEY: ${TYPESENSE_API_KEY:-changeme}
    depends_on:
      sc_database:
        condition: service_healthy
    networks:
      - backend
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:80/api/data-info"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 30s

  # ── Next.js Frontend ──────────────────────────────────────────────────
  frontend:
    image: privaterepo.sitaru.org/tudor/school_compare-frontend:latest
    container_name: schoolcompare_nextjs
    environment:
      - NODE_ENV=production
      - NEXT_PUBLIC_API_URL=http://localhost:8000/api
      - FASTAPI_URL=http://backend:80/api
      - TYPESENSE_URL=http://typesense:8108
      - TYPESENSE_API_KEY=${TYPESENSE_SEARCH_KEY:-changeme}
    depends_on:
      backend:
        condition: service_healthy
    networks:
      backend: {}
      macvlan:
        ipv4_address: 10.0.1.150
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "node", "-e", "require('http').get('http://localhost:3000/', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

  # ── Typesense Search Engine ───────────────────────────────────────────
  typesense:
    image: typesense/typesense:30.1
    container_name: schoolcompare_typesense
    environment:
      TYPESENSE_API_KEY: ${TYPESENSE_API_KEY:-changeme}
      TYPESENSE_DATA_DIR: /data
    volumes:
      - typesense_data:/data
    networks:
      - backend
    restart: unless-stopped
    healthcheck:
      test: ["CMD-SHELL", "cat < /dev/tcp/localhost/8108"]
      interval: 15s
      timeout: 5s
      retries: 5
      start_period: 10s

  # ── Airflow API Server + UI ───────────────────────────────────────────
  airflow-api-server:
    image: privaterepo.sitaru.org/tudor/school_compare-pipeline:latest
    container_name: schoolcompare_airflow_api
    command: airflow api-server --port 8080
    ports:
      - "8080:8080"
    environment:
      AIRFLOW__CORE__EXECUTOR: LocalExecutor
      AIRFLOW__DATABASE__SQL_ALCHEMY_CONN: postgresql+psycopg2://${DB_USERNAME}:${DB_PASSWORD}@sc_database:5432/${DB_DATABASE_NAME}
      AIRFLOW__CORE__DAGS_FOLDER: /opt/pipeline/dags
      AIRFLOW__CORE__LOAD_EXAMPLES: "false"
      AIRFLOW__CORE__EXECUTION_API_SERVER_URL: http://airflow-api-server:8080/execution/
      AIRFLOW__API_AUTH__JWT_SECRET: "school-compare-airflow-jwt-secret-key-long-enough-for-sha512"
      AIRFLOW__API_AUTH__JWT_ISSUER: airflow
      AIRFLOW__CORE__SIMPLE_AUTH_MANAGER_USERS: "${AIRFLOW_ADMIN_USER:-admin}:admin"
      AIRFLOW__LOGGING__BASE_LOG_FOLDER: /opt/airflow/logs
      PG_HOST: sc_database
      PG_PORT: "5432"
      PG_USER: ${DB_USERNAME}
      PG_PASSWORD: ${DB_PASSWORD}
      PG_DATABASE: ${DB_DATABASE_NAME}
      TYPESENSE_URL: http://typesense:8108
      TYPESENSE_API_KEY: ${TYPESENSE_API_KEY:-changeme}
    volumes:
      - airflow_logs:/opt/airflow/logs
    depends_on:
      sc_database:
        condition: service_healthy
    networks:
      - backend
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/api/v2/monitor/health"]
      interval: 30s
      timeout: 10s
      retries: 5
      start_period: 60s

  # ── Airflow Scheduler ──────────────────────────────────────────────
  airflow-scheduler:
    image: privaterepo.sitaru.org/tudor/school_compare-pipeline:latest
    container_name: schoolcompare_airflow_scheduler
    command: airflow scheduler
    environment:
      AIRFLOW__CORE__EXECUTOR: LocalExecutor
      AIRFLOW__DATABASE__SQL_ALCHEMY_CONN: postgresql+psycopg2://${DB_USERNAME}:${DB_PASSWORD}@sc_database:5432/${DB_DATABASE_NAME}
      AIRFLOW__CORE__DAGS_FOLDER: /opt/pipeline/dags
      AIRFLOW__CORE__LOAD_EXAMPLES: "false"
      AIRFLOW__CORE__EXECUTION_API_SERVER_URL: http://airflow-api-server:8080/execution/
      AIRFLOW__API_AUTH__JWT_SECRET: "school-compare-airflow-jwt-secret-key-long-enough-for-sha512"
      AIRFLOW__API_AUTH__JWT_ISSUER: airflow
      AIRFLOW__LOGGING__BASE_LOG_FOLDER: /opt/airflow/logs
      PG_HOST: sc_database
      PG_PORT: "5432"
      PG_USER: ${DB_USERNAME}
      PG_PASSWORD: ${DB_PASSWORD}
      PG_DATABASE: ${DB_DATABASE_NAME}
      TYPESENSE_URL: http://typesense:8108
      TYPESENSE_API_KEY: ${TYPESENSE_API_KEY:-changeme}
    volumes:
      - airflow_logs:/opt/airflow/logs
    depends_on:
      sc_database:
        condition: service_healthy
    networks:
      - backend
    restart: unless-stopped

  # ── Airflow DB Init (one-shot) ───────────────────────────────────────
  airflow-init:
    image: privaterepo.sitaru.org/tudor/school_compare-pipeline:latest
    container_name: schoolcompare_airflow_init
    command: bash -c "airflow db migrate && airflow dags delete school_data_daily -y 2>/dev/null; airflow dags delete school_data_monthly_ofsted -y 2>/dev/null; airflow dags delete school_data_annual_ees -y 2>/dev/null; airflow dags reserialize"
    environment:
      AIRFLOW__CORE__EXECUTOR: LocalExecutor
      AIRFLOW__DATABASE__SQL_ALCHEMY_CONN: postgresql+psycopg2://${DB_USERNAME}:${DB_PASSWORD}@sc_database:5432/${DB_DATABASE_NAME}
      AIRFLOW__CORE__DAGS_FOLDER: /opt/pipeline/dags
      AIRFLOW__CORE__LOAD_EXAMPLES: "false"
      AIRFLOW__CORE__EXECUTION_API_SERVER_URL: http://airflow-api-server:8080/execution/
      AIRFLOW__API_AUTH__JWT_SECRET: "school-compare-airflow-jwt-secret-key-long-enough-for-sha512"
      AIRFLOW__API_AUTH__JWT_ISSUER: airflow
    depends_on:
      sc_database:
        condition: service_healthy
    networks:
      - backend
    restart: "no"

networks:
  backend:
    driver: bridge
  macvlan:
    external:
      name: macvlan

volumes:
  postgres_data:
  typesense_data:
  airflow_logs:
