services:
  database:
    image: postgres:17-bookworm
    restart: unless-stopped
    environment:
      POSTGRES_DB: ${POSTGRES_DB:?required}
      POSTGRES_USER: ${POSTGRES_USER:?required}
      POSTGRES_PASSWORD_FILE: /run/secrets/postgres_password
    secrets:
      - postgres_password
    volumes:
      - licensing_database:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U \"$$POSTGRES_USER\" -d \"$$POSTGRES_DB\""]
      interval: 10s
      timeout: 5s
      retries: 10
    networks:
      - licensing_backend

  api:
    build:
      context: ${APOLON_SOURCE_ROOT:?required}
      dockerfile: licensing_server/Dockerfile
      args:
        APOLON_UID: ${APOLON_CONTAINER_UID:?required}
        APOLON_GID: ${APOLON_CONTAINER_GID:?required}
    restart: unless-stopped
    depends_on:
      database:
        condition: service_healthy
    environment:
      LICENSING_DATABASE_URL_FILE: /run/secrets/licensing_database_url
      LICENSING_ENVIRONMENT: production
      LICENSING_SIGNING_KEY_ID: ${LICENSING_SIGNING_KEY_ID:?required}
      LICENSING_SIGNING_PRIVATE_KEY_PATH: /run/licensing-signing-key
      LICENSING_SERIAL_PEPPER_BASE64_FILE: /run/secrets/serial_pepper_base64
      LICENSING_FINGERPRINT_PEPPER_BASE64_FILE: /run/secrets/fingerprint_pepper_base64
      LICENSING_OIDC_ISSUER: ${LICENSING_OIDC_ISSUER:?required}
      LICENSING_OIDC_AUDIENCE: ${LICENSING_OIDC_AUDIENCE:?required}
      LICENSING_OIDC_JWKS_URL: ${LICENSING_OIDC_JWKS_URL:?required}
      LICENSING_OIDC_AUTHORIZATION_ENDPOINT: ${LICENSING_OIDC_AUTHORIZATION_ENDPOINT:?required}
      LICENSING_OIDC_TOKEN_ENDPOINT: ${LICENSING_OIDC_TOKEN_ENDPOINT:?required}
      LICENSING_OIDC_NATIVE_CLIENT_ID: ${LICENSING_OIDC_NATIVE_CLIENT_ID:?required}
      LICENSING_ALLOW_PRODUCTION_FILE_SIGNER: "true"
      LICENSING_STRIPE_WEBHOOK_SECRET_FILE: /run/secrets/stripe_webhook_secret
      LICENSING_STRIPE_SECRET_KEY_FILE: /run/secrets/stripe_secret_key
      LICENSING_STRIPE_COMMERCE_ENABLED: ${LICENSING_STRIPE_COMMERCE_ENABLED:-false}
      LICENSING_STRIPE_PRICE_SKU_MAP_JSON: ${LICENSING_STRIPE_PRICE_SKU_MAP_JSON:-{}}
      LICENSING_STRIPE_CHECKOUT_SUCCESS_URL: ${LICENSING_STRIPE_CHECKOUT_SUCCESS_URL:-}
      LICENSING_STRIPE_CHECKOUT_CANCEL_URL: ${LICENSING_STRIPE_CHECKOUT_CANCEL_URL:-}
      LICENSING_STRIPE_BILLING_PORTAL_RETURN_URL: ${LICENSING_STRIPE_BILLING_PORTAL_RETURN_URL:-}
      LICENSING_TRUSTED_PROXY_IPS: ${LICENSING_PROXY_IPV4:?required}/32
    secrets:
      - licensing_database_url
      - serial_pepper_base64
      - fingerprint_pepper_base64
      - stripe_webhook_secret
      - stripe_secret_key
    volumes:
      - type: bind
        source: ${LICENSING_SIGNING_KEY_FILE:?required}
        target: /run/licensing-signing-key
        read_only: true
    expose:
      - "8080"
    read_only: true
    tmpfs:
      - /tmp:size=64m,mode=1700
    security_opt:
      - no-new-privileges:true
    healthcheck:
      test: ["CMD", "python", "-m", "licensing_server.cli", "deployment", "doctor", "--component", "subscription_worker"]
      interval: 30s
      timeout: 5s
      start_period: 20s
      retries: 3
    networks:
      - licensing_backend

  subscription_worker:
    profiles: ["subscriptions"]
    build:
      context: ${APOLON_SOURCE_ROOT:?required}
      dockerfile: licensing_server/Dockerfile
      args:
        APOLON_UID: ${APOLON_CONTAINER_UID:?required}
        APOLON_GID: ${APOLON_CONTAINER_GID:?required}
    restart: unless-stopped
    depends_on:
      database:
        condition: service_healthy
    entrypoint: ["python", "-m", "licensing_server.cli", "outbox", "worker"]
    environment:
      LICENSING_DATABASE_URL_FILE: /run/secrets/licensing_database_url
      LICENSING_ENVIRONMENT: production
      LICENSING_STRIPE_SECRET_KEY_FILE: /run/secrets/stripe_secret_key
      LICENSING_STRIPE_PRICE_SKU_MAP_JSON: ${LICENSING_STRIPE_PRICE_SKU_MAP_JSON:-{}}
    secrets:
      - licensing_database_url
      - stripe_secret_key
    read_only: true
    tmpfs:
      - /tmp:size=32m,mode=1700
    security_opt:
      - no-new-privileges:true
    healthcheck:
      test: ["CMD", "python", "-m", "licensing_server.cli", "deployment", "doctor", "--component", "notification_worker"]
      interval: 30s
      timeout: 5s
      start_period: 20s
      retries: 3
    networks:
      - licensing_backend

  notification_worker:
    profiles: ["notifications"]
    build:
      context: ${APOLON_SOURCE_ROOT:?required}
      dockerfile: licensing_server/Dockerfile
      args:
        APOLON_UID: ${APOLON_CONTAINER_UID:?required}
        APOLON_GID: ${APOLON_CONTAINER_GID:?required}
    restart: unless-stopped
    depends_on:
      database:
        condition: service_healthy
    entrypoint: ["python", "-m", "licensing_server.cli", "notifications", "worker"]
    environment:
      LICENSING_DATABASE_URL_FILE: /run/secrets/licensing_database_url
      LICENSING_ENVIRONMENT: production
      LICENSING_EMAIL_NOTIFICATIONS_ENABLED: "true"
      LICENSING_SMTP_HOST: ${LICENSING_SMTP_HOST:-}
      LICENSING_SMTP_PORT: ${LICENSING_SMTP_PORT:-465}
      LICENSING_SMTP_TLS_MODE: ${LICENSING_SMTP_TLS_MODE:-implicit}
      LICENSING_SMTP_USERNAME: ${LICENSING_SMTP_USERNAME:-}
      LICENSING_SMTP_PASSWORD_FILE: /run/secrets/smtp_password
      LICENSING_NOTIFICATION_PEPPER_BASE64_FILE: /run/secrets/notification_pepper_base64
      LICENSING_EMAIL_FROM_ADDRESS: ${LICENSING_EMAIL_FROM_ADDRESS:-}
      LICENSING_ACCOUNT_PORTAL_URL: ${LICENSING_ACCOUNT_PORTAL_URL:-}
    secrets:
      - licensing_database_url
      - smtp_password
      - notification_pepper_base64
    read_only: true
    tmpfs:
      - /tmp:size=32m,mode=1700
    security_opt:
      - no-new-privileges:true
    networks:
      - licensing_backend

  caddy:
    image: caddy:2.11.4-alpine
    restart: unless-stopped
    depends_on:
      api:
        condition: service_healthy
    environment:
      LICENSING_ACME_EMAIL: ${LICENSING_ACME_EMAIL:?required}
      LICENSING_PUBLIC_HOST: ${LICENSING_PUBLIC_HOST:?required}
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - type: bind
        source: ${BLUEHOST_CADDYFILE_PATH:?required}
        target: /etc/caddy/Caddyfile
        read_only: true
      - caddy_data:/data
      - caddy_config:/config
    healthcheck:
      test: ["CMD", "caddy", "validate", "--config", "/etc/caddy/Caddyfile"]
      interval: 30s
      timeout: 5s
      start_period: 10s
      retries: 3
    read_only: true
    tmpfs:
      - /tmp:size=32m,mode=1700
    cap_drop:
      - ALL
    cap_add:
      - NET_BIND_SERVICE
    security_opt:
      - no-new-privileges:true
    networks:
      licensing_backend:
        ipv4_address: ${LICENSING_PROXY_IPV4:?required}

  backup:
    image: postgres:17-bookworm
    profiles: ["operations"]
    restart: "no"
    depends_on:
      database:
        condition: service_healthy
    user: "0:0"
    environment:
      POSTGRES_DB: ${POSTGRES_DB:?required}
      POSTGRES_USER: ${POSTGRES_USER:?required}
      LICENSING_BACKUP_RETENTION_DAYS: ${LICENSING_BACKUP_RETENTION_DAYS:?required}
    secrets:
      - postgres_password
    volumes:
      - type: bind
        source: ${LICENSING_BACKUP_DIRECTORY:?required}
        target: /backups
    entrypoint: ["/bin/sh", "-ec"]
    command:
      - |
        umask 077
        export PGPASSWORD="$$(cat /run/secrets/postgres_password)"
        stamp="$$(date -u +%Y%m%dT%H%M%SZ)"
        output="/backups/apolon_licensing_$${stamp}.dump"
        partial="$${output}.partial"
        trap 'rm -f "$${partial}"' EXIT HUP INT TERM
        pg_dump --host database --username "$${POSTGRES_USER}" --dbname "$${POSTGRES_DB}" --format custom --file "$${partial}"
        pg_restore --list "$${partial}" >/dev/null
        mv "$${partial}" "$${output}"
        trap - EXIT HUP INT TERM
        unset PGPASSWORD
        find /backups -maxdepth 1 -type f -name 'apolon_licensing_*.dump' -mtime "+$${LICENSING_BACKUP_RETENTION_DAYS}" -delete
        echo "Verified PostgreSQL backup created: $${output}"
    networks:
      - licensing_backend

volumes:
  licensing_database:
  caddy_data:
  caddy_config:

networks:
  licensing_backend:
    ipam:
      config:
        - subnet: ${LICENSING_DOCKER_SUBNET:?required}

secrets:
  postgres_password:
    file: ${POSTGRES_PASSWORD_FILE:?required}
  licensing_database_url:
    file: ${LICENSING_DATABASE_URL_FILE:?required}
  serial_pepper_base64:
    file: ${LICENSING_SERIAL_PEPPER_BASE64_FILE:?required}
  fingerprint_pepper_base64:
    file: ${LICENSING_FINGERPRINT_PEPPER_BASE64_FILE:?required}
  stripe_webhook_secret:
    file: ${LICENSING_STRIPE_WEBHOOK_SECRET_FILE:?required}
  stripe_secret_key:
    file: ${LICENSING_STRIPE_SECRET_KEY_FILE:?required}
  smtp_password:
    file: ${LICENSING_SMTP_PASSWORD_FILE:?required}
  notification_pepper_base64:
    file: ${LICENSING_NOTIFICATION_PEPPER_BASE64_FILE:?required}
