services:
  traefik:
    image: traefik:v3.2
    command:
      - --configFile=/etc/traefik/traefik.yml
    ports:
      - "0.0.0.0:8288:80"
      - "0.0.0.0:2288:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./traefik/traefik.yml:/etc/traefik/traefik.yml:ro
      - ./traefik/dynamic:/etc/traefik/dynamic
      - ./traefik/letsencrypt:/letsencrypt
    networks:
      - portal_net
    restart: unless-stopped

  db:
    image: postgres:16
    environment:
      POSTGRES_DB: ${POSTGRES_DB}
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    volumes:
      - pg_data:/var/lib/postgresql/data
    networks:
      - portal_net
    restart: unless-stopped

  api:
    build:
      context: ./app
    command: ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "${UVICORN_WORKERS:-6}"]
    environment:
      DATABASE_URL: postgresql+psycopg2://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}
      SIGNING_KEY: ${SIGNING_KEY}
      PUBLIC_HOST: ${PUBLIC_HOST}
      ADMIN_USERNAME: ${ADMIN_USERNAME}
      ADMIN_PASSWORD: ${ADMIN_PASSWORD}
      SESSION_IDLE_SECONDS: ${SESSION_IDLE_SECONDS:-7200}
      PREWARM_POOL_SIZE: ${PREWARM_POOL_SIZE:-2}
      UNIVERSAL_POOL_SIZE: ${UNIVERSAL_POOL_SIZE:-0}
      WEB_POOL_SIZE: ${WEB_POOL_SIZE:-20}
      WEB_POOL_BUFFER: ${WEB_POOL_BUFFER:-2}
      X11VNC_FLAGS: ${X11VNC_FLAGS:--wait 5 -defer 5 -threads}
      MAX_ACTIVE_SERVICES_PER_USER: ${MAX_ACTIVE_SERVICES_PER_USER:-4}
      LOG_LEVEL: ${LOG_LEVEL:-INFO}
      LOG_SLOW_REQUEST_MS: ${LOG_SLOW_REQUEST_MS:-2000}
      GO_USER_LOCK_TIMEOUT_SECONDS: ${GO_USER_LOCK_TIMEOUT_SECONDS:-8}
      GO_POOL_LOCK_TIMEOUT_SECONDS: ${GO_POOL_LOCK_TIMEOUT_SECONDS:-20}
      POOL_DISPATCH_RETRIES: ${POOL_DISPATCH_RETRIES:-6}
      POOL_DISPATCH_REQUEST_TIMEOUT_SECONDS: ${POOL_DISPATCH_REQUEST_TIMEOUT_SECONDS:-2.0}
      POOL_DISPATCH_SLEEP_SECONDS: ${POOL_DISPATCH_SLEEP_SECONDS:-0.3}
      TRAEFIK_INTERNAL_URL: ${TRAEFIK_INTERNAL_URL:-http://traefik}
      WEB_RESOLUTION_MIN_WIDTH: ${WEB_RESOLUTION_MIN_WIDTH:-1024}
      WEB_RESOLUTION_MIN_HEIGHT: ${WEB_RESOLUTION_MIN_HEIGHT:-720}
      WEB_RESOLUTION_MAX_WIDTH: ${WEB_RESOLUTION_MAX_WIDTH:-3840}
      WEB_RESOLUTION_MAX_HEIGHT: ${WEB_RESOLUTION_MAX_HEIGHT:-2160}
      ENABLE_STARTUP_MAINTENANCE: ${ENABLE_STARTUP_MAINTENANCE:-0}
    depends_on:
      - db
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./app/static/service-icons:/app/static/service-icons
    labels:
      - traefik.enable=true
      - traefik.docker.network=portal_net
      - traefik.http.routers.portal.rule=Host(`${PUBLIC_HOST}`)
      - traefik.http.routers.portal.entrypoints=websecure
      - traefik.http.routers.portal.tls=true
      - traefik.http.routers.portal.tls.certresolver=letsencrypt
      - traefik.http.routers.portal.priority=1
      - traefik.http.services.portal.loadbalancer.server.port=8000
      - traefik.http.routers.portal.middlewares=secure-headers@file
    networks:
      - portal_net
    restart: unless-stopped


  maintenance:
    build:
      context: ./app
    command: [python, maintenance_runner.py]
    environment:
      DATABASE_URL: postgresql+psycopg2://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}
      SIGNING_KEY: ${SIGNING_KEY}
      PUBLIC_HOST: ${PUBLIC_HOST}
      ADMIN_USERNAME: ${ADMIN_USERNAME}
      ADMIN_PASSWORD: ${ADMIN_PASSWORD}
      SESSION_IDLE_SECONDS: ${SESSION_IDLE_SECONDS:-7200}
      PREWARM_POOL_SIZE: ${PREWARM_POOL_SIZE:-2}
      UNIVERSAL_POOL_SIZE: ${UNIVERSAL_POOL_SIZE:-0}
      WEB_POOL_SIZE: ${WEB_POOL_SIZE:-20}
      WEB_POOL_BUFFER: ${WEB_POOL_BUFFER:-2}
      X11VNC_FLAGS: ${X11VNC_FLAGS:--wait 5 -defer 5 -threads}
      MAX_ACTIVE_SERVICES_PER_USER: ${MAX_ACTIVE_SERVICES_PER_USER:-4}
      LOG_LEVEL: ${LOG_LEVEL:-INFO}
      LOG_SLOW_REQUEST_MS: ${LOG_SLOW_REQUEST_MS:-2000}
      GO_USER_LOCK_TIMEOUT_SECONDS: ${GO_USER_LOCK_TIMEOUT_SECONDS:-8}
      GO_POOL_LOCK_TIMEOUT_SECONDS: ${GO_POOL_LOCK_TIMEOUT_SECONDS:-20}
      POOL_DISPATCH_RETRIES: ${POOL_DISPATCH_RETRIES:-6}
      POOL_DISPATCH_REQUEST_TIMEOUT_SECONDS: ${POOL_DISPATCH_REQUEST_TIMEOUT_SECONDS:-2.0}
      POOL_DISPATCH_SLEEP_SECONDS: ${POOL_DISPATCH_SLEEP_SECONDS:-0.3}
      TRAEFIK_INTERNAL_URL: ${TRAEFIK_INTERNAL_URL:-http://traefik}
      WEB_RESOLUTION_MIN_WIDTH: ${WEB_RESOLUTION_MIN_WIDTH:-1024}
      WEB_RESOLUTION_MIN_HEIGHT: ${WEB_RESOLUTION_MIN_HEIGHT:-720}
      WEB_RESOLUTION_MAX_WIDTH: ${WEB_RESOLUTION_MAX_WIDTH:-3840}
      WEB_RESOLUTION_MAX_HEIGHT: ${WEB_RESOLUTION_MAX_HEIGHT:-2160}
      ENABLE_STARTUP_MAINTENANCE: ${ENABLE_STARTUP_MAINTENANCE:-0}
    depends_on:
      - db
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./app/static/service-icons:/app/static/service-icons
    networks:
      - portal_net
    restart: unless-stopped

  kiosk-image:
    image: portal-kiosk:latest
    build:
      context: ./kiosk
    profiles: ["build-only"]

  rdp-proxy-image:
    image: portal-rdp-proxy:latest
    build:
      context: ./rdp-proxy
    profiles: ["build-only"]

  universal-runtime-image:
    image: portal-universal-runtime:latest
    build:
      context: ./universal-runtime
    profiles: ["build-only"]

networks:
  portal_net:
    name: portal_net

volumes:
  pg_data:
