# FrankenPHP Caddyfile for the api container.
# Serves Slim from public/ on :8081.
{
    frankenphp
    order php_server before file_server
    auto_https off
    admin off
    servers {
        trusted_proxies static private_ranges
    }
}

:8081 {
    root * /app/public
    encode zstd gzip

    # ── Security headers (M14) ──────────────────────────────────────────
    # Applied to every response. The api serves JSON + the OpenAPI YAML +
    # the /api/docs viewer; everything else is locked down.
    header {
        # Identify ourselves as little as possible.
        -Server
        -X-Powered-By
        X-Content-Type-Options "nosniff"
        # The api doesn't render its own pages except /api/docs which is a
        # single embedded viewer; SAMEORIGIN is the conservative default
        # that still allows future same-origin embedding.
        X-Frame-Options "SAMEORIGIN"
        Referrer-Policy "strict-origin-when-cross-origin"
        Permissions-Policy "geolocation=(), microphone=(), camera=()"
    }

    # HSTS: prod-only. Setting it in dev would lock you out of plain-HTTP
    # localhost on the same hostname (sticky for 1 year). Gate strictly.
    @prod expression `{env.APP_ENV} == "production"`
    header @prod Strict-Transport-Security "max-age=31536000; includeSubDomains"

    # CSP: docs viewer needs RapiDoc from jsDelivr + inline styles + the
    # try-it-now feature posting to /api/v1/*. Everything else is JSON.
    @docs path /api/docs /api/v1/openapi.yaml
    header @docs Content-Security-Policy "default-src 'self'; script-src 'self' https://cdn.jsdelivr.net 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data: https://cdn.jsdelivr.net; connect-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'"

    @not_docs not path /api/docs /api/v1/openapi.yaml
    header @not_docs Content-Security-Policy "default-src 'none'; frame-ancestors 'none'; base-uri 'none'"

    # Internal jobs API: only callable from loopback / RFC1918.
    # The PHP layer also enforces this (InternalNetworkMiddleware) — Caddy
    # is the first line of defence for production deployments where the
    # api is reachable from the public internet.
    @internal {
        path /internal/*
        remote_ip 127.0.0.1/32 ::1/128 172.16.0.0/12 10.0.0.0/8 192.168.0.0/16
    }
    handle @internal {
        php_server
    }

    @external_internal_blocked {
        path /internal/*
        not remote_ip 127.0.0.1/32 ::1/128 172.16.0.0/12 10.0.0.0/8 192.168.0.0/16
    }
    respond @external_internal_blocked 404

    php_server
}
