diff --git a/nix/modules/reports.nix b/nix/modules/reports.nix index e734bfc..775a88f 100644 --- a/nix/modules/reports.nix +++ b/nix/modules/reports.nix @@ -3,10 +3,49 @@ # no secrets — just static files behind traefik + the wildcard TLS (same pattern as dashboard.nix, # but a plain nginx:alpine since there's nothing to render server-side). Content is updated by writing # files into /var/lib/cc-ci-reports; nginx serves them live (no redeploy needed). +# +# It ALSO serves a same-origin realtime PR-status proxy at /pr//: the report's STATUS +# column fetches it client-side to show each PR's live state (open vs. ✓). Same-origin means no +# dependency on the Gitea CORS allow-list; the recipe mirrors are public so no token is needed. The +# proxy is pinned to recipe-maintainers + a safe recipe-name charset and is read-only (GET/HEAD). { pkgs, ... }: let reportsDir = "/var/lib/cc-ci-reports"; + # Custom nginx server: static report files + the /pr// → Gitea-API proxy. Replaces the + # stock /etc/nginx/conf.d/default.conf (which the image's nginx.conf includes inside http{}). + nginxConf = pkgs.writeText "cc-ci-reports-default.conf" '' + server { + listen 80; + server_name _; + root /usr/share/nginx/html; + index index.html; + + # Realtime PR-status proxy for the Recipe Report STATUS column. + # GET /pr// -> the PUBLIC Gitea PR JSON ({state, merged, ...}). Same-origin from + # the browser's view, so no CORS dependency; unauthenticated, since the recipe mirrors are + # public. The repo owner is hard-pinned to recipe-maintainers and the recipe name to a + # slashless charset, so the proxied path can only ever address recipe-maintainers//pulls + # (it cannot be coerced to another org or path). Only safe read methods are allowed. + location ~ ^/pr/([a-z0-9._-]+)/([0-9]+)$ { + limit_except GET HEAD { deny all; } + resolver 127.0.0.11 ipv6=off valid=30s; # docker embedded DNS (forwards external names) + proxy_ssl_server_name on; + proxy_set_header Host git.autonomic.zone; + proxy_set_header Accept "application/json"; + proxy_pass https://git.autonomic.zone/api/v1/repos/recipe-maintainers/$1/pulls/$2; + proxy_intercept_errors off; + proxy_connect_timeout 5s; + proxy_read_timeout 10s; + add_header Cache-Control "no-store" always; # always fetch live state, never cache in the browser + } + + location / { + try_files $uri $uri/ =404; + } + } + ''; + stack = pkgs.writeText "cc-ci-reports-stack.yml" '' version: "3.8" services: @@ -17,6 +56,10 @@ let source: ${reportsDir} target: /usr/share/nginx/html read_only: true + - type: bind + source: ${nginxConf} + target: /etc/nginx/conf.d/default.conf + read_only: true networks: - proxy deploy: