name: colectivo-erosi

# Production stack for ambrosio.
# Keycloak: external — URL set via PUBLIC_KEYCLOAK_URL in .env.
#
# Reverse proxy + TLS are owned externally and not described here. The stack
# exposes nothing publicly — `kong` (8000) and `app` (3000) listen on the
# internal `colectivo` Docker network only. Whatever proxy fronts the host is
# responsible for reaching them (e.g. via the Docker network or by mapping
# host ports).
#
# Bring up:    docker compose -f infra/docker-compose.erosi.yml --env-file .env up -d --build
# Logs:        docker compose -f infra/docker-compose.erosi.yml logs -f
# Tear down:   docker compose -f infra/docker-compose.erosi.yml down

services:

  # ── PostgreSQL 15 (Supabase image — internal roles preconfigured) ──────────
  db:
    image: supabase/postgres:15.1.1.78
    restart: always
    healthcheck:
      test: pg_isready -U postgres -h localhost
      interval: 10s
      timeout: 5s
      retries: 10
    environment:
      POSTGRES_HOST: /var/run/postgresql
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      JWT_SECRET: ${SUPABASE_JWT_SECRET}
      JWT_EXP: 3600
    volumes:
      - db-data:/var/lib/postgresql/data
      - ./db-init:/docker-entrypoint-initdb.d:ro
    networks: [colectivo]

  # ── GoTrue (Supabase Auth) ─────────────────────────────────────────────────
  auth:
    image: supabase/gotrue:v2.158.1
    restart: always
    depends_on:
      db: { condition: service_healthy }
    environment:
      GOTRUE_API_HOST: "0.0.0.0"
      GOTRUE_API_PORT: 9999
      API_EXTERNAL_URL: ${PUBLIC_APP_URL}
      GOTRUE_API_EXTERNAL_URL: ${PUBLIC_APP_URL}
      GOTRUE_DB_DRIVER: postgres
      GOTRUE_DB_DATABASE_URL: postgres://supabase_auth_admin:${POSTGRES_PASSWORD}@db:5432/postgres
      GOTRUE_SITE_URL: ${PUBLIC_APP_URL}
      GOTRUE_URI_ALLOW_LIST: "${PUBLIC_APP_URL}/auth/callback,${PUBLIC_APP_URL}/*"
      GOTRUE_JWT_SECRET: ${SUPABASE_JWT_SECRET}
      GOTRUE_JWT_EXP: 3600
      GOTRUE_DISABLE_SIGNUP: "false"
      GOTRUE_EXTERNAL_KEYCLOAK_ENABLED: "true"
      GOTRUE_EXTERNAL_KEYCLOAK_CLIENT_ID: ${PUBLIC_KEYCLOAK_CLIENT_ID}
      GOTRUE_EXTERNAL_KEYCLOAK_SECRET: ${KEYCLOAK_CLIENT_SECRET}
      GOTRUE_EXTERNAL_KEYCLOAK_SCOPES: "openid profile email"
      # Browser-facing Keycloak issuer URL. GoTrue appends /.well-known to it.
      GOTRUE_EXTERNAL_KEYCLOAK_URL: ${PUBLIC_KEYCLOAK_URL}/realms/${PUBLIC_KEYCLOAK_REALM}
      GOTRUE_EXTERNAL_KEYCLOAK_REDIRECT_URI: ${PUBLIC_SUPABASE_URL}/auth/v1/callback
      GOTRUE_MAILER_AUTOCONFIRM: "true"
    networks: [colectivo]

  # ── PostgREST ──────────────────────────────────────────────────────────────
  rest:
    image: postgrest/postgrest:v12.2.0
    restart: always
    depends_on:
      db: { condition: service_healthy }
    environment:
      PGRST_DB_URI: postgres://authenticator:${POSTGRES_PASSWORD}@db:5432/postgres
      PGRST_DB_SCHEMAS: public,storage,graphql_public
      PGRST_DB_ANON_ROLE: anon
      PGRST_JWT_SECRET: ${SUPABASE_JWT_SECRET}
      PGRST_DB_USE_LEGACY_GUCS: "false"
      PGRST_APP_SETTINGS_JWT_SECRET: ${SUPABASE_JWT_SECRET}
      PGRST_APP_SETTINGS_JWT_EXP: 3600
    networks: [colectivo]

  # ── Realtime (pinned to v2.76.5 — see CLAUDE.md note on handle_out/3) ──────
  realtime:
    image: supabase/realtime:v2.76.5
    restart: always
    depends_on:
      db: { condition: service_healthy }
    environment:
      PORT: 4000
      DB_HOST: db
      DB_PORT: 5432
      DB_USER: supabase_admin
      DB_PASSWORD: ${POSTGRES_PASSWORD}
      DB_NAME: postgres
      DB_AFTER_CONNECT_QUERY: SET search_path TO _realtime
      DB_ENC_KEY: ${REALTIME_ENC_KEY}
      API_JWT_SECRET: ${SUPABASE_JWT_SECRET}
      SELF_HOST_TENANT_NAME: realtime
      SEED_SELF_HOST: "true"
      RUN_JANITOR: "true"
      APP_NAME: realtime
      SECRET_KEY_BASE: ${REALTIME_SECRET_KEY_BASE}
      ERL_AFLAGS: -proto_dist inet_tcp
      DNS_NODES: "''"
      RLIMIT_NOFILE: "10000"
      METRICS_JWT_SECRET: ${SUPABASE_JWT_SECRET}
    networks: [colectivo]

  # ── Storage ────────────────────────────────────────────────────────────────
  storage:
    image: supabase/storage-api:v1.11.13
    restart: always
    depends_on:
      db:   { condition: service_healthy }
      rest: { condition: service_started }
    environment:
      ANON_KEY: ${PUBLIC_SUPABASE_ANON_KEY}
      SERVICE_KEY: ${SUPABASE_SERVICE_ROLE_KEY}
      POSTGREST_URL: http://rest:3000
      PGRST_JWT_SECRET: ${SUPABASE_JWT_SECRET}
      DATABASE_URL: postgres://supabase_storage_admin:${POSTGRES_PASSWORD}@db:5432/postgres
      FILE_SIZE_LIMIT: 52428800
      STORAGE_BACKEND: file
      FILE_STORAGE_BACKEND_PATH: /var/lib/storage
      TENANT_ID: colectivo-erosi
      REGION: local
      GLOBAL_S3_BUCKET: stub
      ENABLE_IMAGE_TRANSFORMATION: "true"
      IMGPROXY_URL: http://imgproxy:5001
    volumes:
      - storage-data:/var/lib/storage
    networks: [colectivo]

  # ── ImgProxy ───────────────────────────────────────────────────────────────
  imgproxy:
    image: darthsim/imgproxy:v3.8.0
    restart: always
    environment:
      IMGPROXY_BIND: ":5001"
      IMGPROXY_LOCAL_FILESYSTEM_ROOT: /
      IMGPROXY_USE_ETAG: "true"
      IMGPROXY_ENABLE_WEBP_DETECTION: "true"
    volumes:
      - storage-data:/var/lib/storage:ro
    networks: [colectivo]

  # ── Kong (API Gateway) — exposed on the Traefik network via labels ────────
  kong:
    image: kong:2.8.1
    restart: always
    entrypoint: ["/bin/bash", "/home/kong/kong-start.sh"]
    depends_on:
      db: { condition: service_healthy }
    environment:
      KONG_DATABASE: "off"
      KONG_DECLARATIVE_CONFIG: /home/kong/kong.yml
      KONG_DNS_ORDER: LAST,A,CNAME
      KONG_PLUGINS: request-transformer,cors,key-auth,acl,basic-auth,rate-limiting
      KONG_NGINX_PROXY_PROXY_BUFFER_SIZE: 160k
      KONG_NGINX_PROXY_PROXY_BUFFERS: 64 160k
      SUPABASE_ANON_KEY: ${PUBLIC_SUPABASE_ANON_KEY}
      SUPABASE_SERVICE_KEY: ${SUPABASE_SERVICE_ROLE_KEY}
    volumes:
      - ./kong.yml:/home/kong/temp.yml:ro
      - ./kong-start.sh:/home/kong/kong-start.sh:ro
    networks: [colectivo]

  # ── App (SvelteKit Node server) — built on the host from the repo ──────────
  # Build args bake the PUBLIC_* values into the client bundle (SvelteKit reqs).
  app:
    image: colectivo-web:erosi
    build:
      context: ..
      dockerfile: apps/web/Dockerfile
      args:
        PUBLIC_SUPABASE_URL: ${PUBLIC_SUPABASE_URL}
        PUBLIC_SUPABASE_ANON_KEY: ${PUBLIC_SUPABASE_ANON_KEY}
        PUBLIC_KEYCLOAK_URL: ${PUBLIC_KEYCLOAK_URL}
        PUBLIC_KEYCLOAK_REALM: ${PUBLIC_KEYCLOAK_REALM}
        PUBLIC_KEYCLOAK_CLIENT_ID: ${PUBLIC_KEYCLOAK_CLIENT_ID}
        PUBLIC_APP_URL: ${PUBLIC_APP_URL}
    restart: always
    environment:
      PUBLIC_SUPABASE_URL: ${PUBLIC_SUPABASE_URL}
      PUBLIC_SUPABASE_ANON_KEY: ${PUBLIC_SUPABASE_ANON_KEY}
      PUBLIC_KEYCLOAK_URL: ${PUBLIC_KEYCLOAK_URL}
      PUBLIC_KEYCLOAK_REALM: ${PUBLIC_KEYCLOAK_REALM}
      PUBLIC_KEYCLOAK_CLIENT_ID: ${PUBLIC_KEYCLOAK_CLIENT_ID}
      PUBLIC_APP_URL: ${PUBLIC_APP_URL}
      NODE_ENV: production
    networks: [colectivo]

  # ── Internal edge: path-split kong vs app on a single port ────────────────
  # Plain HTTP. TLS + outermost routing live on whatever external proxy
  # fronts the host; that proxy only needs to know about port 3000.
  caddy:
    image: caddy:2-alpine
    restart: always
    depends_on: [kong, app]
    volumes:
      - ./Caddyfile.erosi:/etc/caddy/Caddyfile:ro
    ports:
      - "3000:3000"
    networks: [colectivo]

networks:
  colectivo:
    driver: bridge

volumes:
  db-data:
  storage-data:
