services:
  nginx:
    image: nginx:alpine
    restart: unless-stopped
    ports:
      - "80:80"
    volumes:
      - ./nginx/default.conf.template:/etc/nginx/templates/default.conf.template:ro
    environment:
      DOMAIN: ${DOMAIN:-localhost}
      MAIL_FRONTEND_UPSTREAM: ${MAIL_FRONTEND_UPSTREAM:-host.docker.internal:3000}
    env_file: ../.env.resolved
    extra_hosts:
      - "host.docker.internal:host-gateway"
    networks:
      - ulti-net
    depends_on:
      ultid:
        condition: service_started
      authentik-server:
        condition: service_started

  ultid:
    build:
      context: ..
      dockerfile: Dockerfile
    restart: unless-stopped
    env_file: ../.env.resolved
    networks:
      - ulti-net
    healthcheck:
      test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:8080/healthz >/dev/null 2>&1 || exit 1"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 30s
    depends_on:
      postgres:
        condition: service_healthy
      keydb:
        condition: service_healthy
      rustfs:
        condition: service_started
      authentik-server:
        condition: service_healthy

  postgres:
    image: postgres:16-alpine
    restart: unless-stopped
    env_file: ../.env.resolved
    volumes:
      - postgres_data:/var/lib/postgresql/data
      - ./init-db.sh:/docker-entrypoint-initdb.d/init-db.sh:ro
    networks:
      - ulti-net
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
      interval: 5s
      timeout: 5s
      retries: 5
      start_period: 10s

  keydb:
    image: eqalpha/keydb:latest
    restart: unless-stopped
    command: keydb-server --appendonly yes
    volumes:
      - keydb_data:/data
    networks:
      - ulti-net
    healthcheck:
      test: ["CMD", "keydb-cli", "-h", "127.0.0.1", "ping"]
      interval: 10s
      timeout: 3s
      retries: 5
      start_period: 10s

  rustfs:
    image: rustfs/rustfs:latest
    restart: unless-stopped
    command: server /data
    ports:
      - "9002:9000"
      - "9003:9001"
    environment:
      RUSTFS_ACCESS_KEY: ${RUSTFS_ACCESS_KEY}
      RUSTFS_SECRET_KEY: ${RUSTFS_SECRET_KEY}
    volumes:
      - rustfs_data:/data
    networks:
      - ulti-net

  authentik-server:
    image: ghcr.io/goauthentik/server:latest
    restart: unless-stopped
    command: server
    environment:
      # Required at compose parse time — empty ${VAR} would override env_file with "".
      AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY:?Set AUTHENTIK_SECRET_KEY in .env and use ./deploy/compose-up.sh}
      AUTHENTIK_POSTGRESQL__HOST: ${AUTHENTIK_POSTGRESQL__HOST:-postgres}
      AUTHENTIK_POSTGRESQL__USER: ${AUTHENTIK_POSTGRESQL__USER:?Set AUTHENTIK_POSTGRESQL__USER in .env}
      AUTHENTIK_POSTGRESQL__PASSWORD: ${AUTHENTIK_POSTGRESQL__PASSWORD:?Set AUTHENTIK_POSTGRESQL__PASSWORD in .env}
      AUTHENTIK_POSTGRESQL__NAME: ${AUTHENTIK_POSTGRESQL__NAME:-authentik}
      AUTHENTIK_REDIS__HOST: ${AUTHENTIK_REDIS__HOST:-keydb}
      AUTHENTIK_WEB__PATH: /auth/
      AUTHENTIK_HOST: http://${DOMAIN:-localhost}
    env_file: ../.env.resolved
    volumes:
      - ./authentik/blueprints:/blueprints/custom:ro
      - ./authentik/branding/ultimail-logo-light.png:/web/dist/assets/branding/ultimail-logo-light.png:ro
      - ./authentik/branding/ultimail-logo-dark.png:/web/dist/assets/branding/ultimail-logo-dark.png:ro
      - ./authentik/branding/ultimail-favicon.png:/web/dist/assets/branding/ultimail-favicon.png:ro
      - ./authentik/branding/ultimail-favicon-light.png:/web/dist/assets/branding/ultimail-favicon-light.png:ro
      - ./authentik/branding/ultimail-favicon-dark.png:/web/dist/assets/branding/ultimail-favicon-dark.png:ro
    networks:
      - ulti-net
    healthcheck:
      test: ["CMD", "ak", "healthcheck"]
      interval: 15s
      timeout: 10s
      retries: 10
      start_period: 90s
    depends_on:
      postgres:
        condition: service_healthy
      keydb:
        condition: service_healthy

  authentik-worker:
    image: ghcr.io/goauthentik/server:latest
    restart: unless-stopped
    command: worker
    environment:
      AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY:?Set AUTHENTIK_SECRET_KEY in .env and use ./deploy/compose-up.sh}
      AUTHENTIK_POSTGRESQL__HOST: ${AUTHENTIK_POSTGRESQL__HOST:-postgres}
      AUTHENTIK_POSTGRESQL__USER: ${AUTHENTIK_POSTGRESQL__USER:?Set AUTHENTIK_POSTGRESQL__USER in .env}
      AUTHENTIK_POSTGRESQL__PASSWORD: ${AUTHENTIK_POSTGRESQL__PASSWORD:?Set AUTHENTIK_POSTGRESQL__PASSWORD in .env}
      AUTHENTIK_POSTGRESQL__NAME: ${AUTHENTIK_POSTGRESQL__NAME:-authentik}
      AUTHENTIK_REDIS__HOST: ${AUTHENTIK_REDIS__HOST:-keydb}
      AUTHENTIK_WEB__PATH: /auth/
      AUTHENTIK_HOST: http://${DOMAIN:-localhost}
    env_file: ../.env.resolved
    volumes:
      - ./authentik/blueprints:/blueprints/custom:ro
      - ./authentik/branding/ultimail-logo-light.png:/web/dist/assets/branding/ultimail-logo-light.png:ro
      - ./authentik/branding/ultimail-logo-dark.png:/web/dist/assets/branding/ultimail-logo-dark.png:ro
      - ./authentik/branding/ultimail-favicon.png:/web/dist/assets/branding/ultimail-favicon.png:ro
      - ./authentik/branding/ultimail-favicon-light.png:/web/dist/assets/branding/ultimail-favicon-light.png:ro
      - ./authentik/branding/ultimail-favicon-dark.png:/web/dist/assets/branding/ultimail-favicon-dark.png:ro
    networks:
      - ulti-net
    depends_on:
      postgres:
        condition: service_healthy
      keydb:
        condition: service_healthy
      authentik-server:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "ak", "healthcheck"]
      interval: 30s
      timeout: 10s
      retries: 5
      start_period: 60s

  prometheus:
    image: prom/prometheus:v2.54.1
    restart: unless-stopped
    command:
      - --config.file=/etc/prometheus/prometheus.yml
      - --storage.tsdb.path=/prometheus
      - --web.enable-lifecycle
    volumes:
      - ./observability/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
      - ./observability/prometheus/alerts.yml:/etc/prometheus/rules/alerts.yml:ro
      - prometheus_data:/prometheus
    ports:
      - "9090:9090"
    networks:
      - ulti-net
    depends_on:
      ultid:
        condition: service_started

  grafana:
    image: grafana/grafana:11.3.0
    restart: unless-stopped
    environment:
      GF_SECURITY_ADMIN_USER: ${GRAFANA_ADMIN_USER:-admin}
      GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_ADMIN_PASSWORD:-admin}
      GF_USERS_ALLOW_SIGN_UP: "false"
    volumes:
      - ./observability/grafana/provisioning:/etc/grafana/provisioning:ro
      - ./observability/grafana/ultid-baseline.json:/etc/grafana/dashboards/ultid-baseline.json:ro
      - grafana_data:/var/lib/grafana
    ports:
      - "3002:3000"
    networks:
      - ulti-net
    depends_on:
      prometheus:
        condition: service_started

  suite-frontend:
    build:
      context: ../../gmail-interface-clone
      dockerfile: Dockerfile
    restart: unless-stopped
    environment:
      - ULTI_PROXY_ORIGIN=http://nginx
      - NEXT_PUBLIC_API_URL=/api/v1
      - NEXT_PUBLIC_APP_URL=http://${DOMAIN:-localhost}
      - OIDC_CLIENT_SECRET=${ULTID_OIDC_CLIENT_SECRET:-changeme}
      - NEXT_PUBLIC_OIDC_ISSUER=http://${DOMAIN:-localhost}/auth/application/o/ulti/
      - NEXT_PUBLIC_OIDC_CLIENT_ID=${ULTID_OIDC_CLIENT_ID:-ulti-backend}
      - NEXT_PUBLIC_ONLYOFFICE_URL=http://${DOMAIN:-localhost}/office
    networks:
      - ulti-net
    depends_on:
      - ultid

networks:
  ulti-net:
    driver: bridge

volumes:
  postgres_data:
  keydb_data:
  rustfs_data:
  prometheus_data:
  grafana_data:
