Skip to content

feat(reports-ui): Caddy static SPA over the reports API (RFC-0002) - #182

Merged
vovinacci merged 2 commits into
mainfrom
feat/reports-ui-service
Jul 24, 2026
Merged

feat(reports-ui): Caddy static SPA over the reports API (RFC-0002)#182
vovinacci merged 2 commits into
mainfrom
feat/reports-ui-service

Conversation

@vovinacci

@vovinacci vovinacci commented Jul 24, 2026

Copy link
Copy Markdown
Owner

A small, dependency-free browser UI for the reports service, served by Caddy as a separate reports-ui service (RFC-0002, ADR-0013). No new runtime - an integration exhibit.

  • Vanilla HTML/CSS/JS SPA (no build step): submit a report, watch it reach SUCCEEDED, download the artifact, list recent jobs. All calls go same-origin under /api/*; rendering is textContent/createElement only (no innerHTML).
  • Caddy on :8084: handle_path /api/* strips the prefix so reports sees /reports/... (a bare reverse_proxy would 404); an explicit metrics handler serves Caddy's native Prometheus metrics on the service listener (they live on the admin API by default, which stays private/unpublished); /healthz + /readyz complete the D6 contract that the frontend's nginx does not meet.
  • Active upstream health check (health_uri /healthz) so caddy_reverse_proxy_upstreams_healthy tracks reality -- the dashboard shows a dead reports backend as down, not green (verified 1->0->1 across a stop/start). With reports absent the page still serves and the client shows an unavailable banner (D10); proxied calls return 503.
  • reports-ui compose profile at :8084 (admin :2019 never published), into up-full/up-workshop and the nightly smoke-full stack with an e2e-smoke health assertion; Prometheus scrape job + a provisioned Caddy dashboard; reports-ui.yml CI (caddy fmt/validate + image build) + Trivy matrix entry; non-root digest-pinned Dockerfile. Also refreshes the stale e2e.yml comment.

Summary by CodeRabbit

  • New Features

    • Added a Reports UI for submitting reports, tracking job status, downloading results, and viewing recent jobs.
    • Added same-origin API access, health/readiness endpoints, and Prometheus metrics.
    • Added optional local deployment on port 8084 with graceful handling when the Reports service is unavailable.
    • Added monitoring dashboards and service health checks.
  • Documentation

    • Documented the Reports UI, local access URL, architecture, CI checks, and observability.
  • Bug Fixes

    • Improved smoke-test coverage for Reports UI health and metrics availability.

A small, dependency-free browser UI for the reports service, served by Caddy
as a separate `reports-ui` service (RFC-0002, ADR-0013). No new runtime -- an
integration exhibit.

- Vanilla HTML/CSS/JS SPA (no build step): submit a report, watch it reach
  SUCCEEDED, download the artifact, list recent jobs. All calls go same-origin
  under /api/*; rendering is textContent/createElement only (no innerHTML).
- Caddy on :8084: `handle_path /api/*` strips the prefix so reports sees
  /reports/... (a bare reverse_proxy would 404); an explicit `metrics` handler
  serves Caddy's native Prometheus metrics on the service listener (they live
  on the admin API by default, which stays private/unpublished); /healthz +
  /readyz complete the D6 contract that the frontend's nginx does not meet.
- Active upstream health check (health_uri /healthz) so
  caddy_reverse_proxy_upstreams_healthy tracks reality -- the dashboard shows a
  dead reports backend as down, not green (verified 1->0->1 across a stop/start).
  With reports absent the page still serves and the client shows an
  unavailable banner (D10); proxied calls return 503.
- `reports-ui` compose profile at :8084 (admin :2019 never published), into
  up-full/up-workshop and the nightly smoke-full stack with an e2e-smoke
  health assertion; Prometheus scrape job + a provisioned Caddy dashboard;
  reports-ui.yml CI (caddy fmt/validate + image build) + Trivy matrix entry;
  non-root digest-pinned Dockerfile. Also refreshes the stale e2e.yml comment.
@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@vovinacci, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 40 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 7fe59f21-c4e4-496e-a309-8479c950c952

📥 Commits

Reviewing files that changed from the base of the PR and between 6a07801 and bb8df61.

📒 Files selected for processing (2)
  • .github/workflows/reports-ui.yml
  • services/reports-ui/site/app.js

Walkthrough

Adds a Caddy-served Reports UI SPA with same-origin API proxying, compose integration, containerised validation, CI coverage, nightly checks, Prometheus scraping, and Grafana monitoring.

Changes

Reports UI

Layer / File(s) Summary
UI experience and Caddy routing
services/reports-ui/site/*, services/reports-ui/Caddyfile
Adds report submission, polling, downloads, recent-job rendering, responsive styling, health and metrics endpoints, API proxying, and static SPA hosting.
Container packaging and local validation
services/reports-ui/Dockerfile, services/reports-ui/Makefile, services/reports-ui/README.md
Adds a non-root Caddy image and containerised build, test, lint, format, run, and documentation workflows.
Compose and project integration
deploy/compose/docker-compose.yml, Makefile, README.md, docs/architecture.md
Adds the opt-in service profile and includes Reports UI in full-stack startup, smoke, test, lint, image, and architecture documentation paths.
CI and nightly validation
.github/workflows/*, scripts/e2e-smoke.sh, docs/ci.md
Adds path-scoped formatting, validation, asset checks, image building, image scanning, and nightly health and metrics assertions.
Prometheus and Grafana observability
observability/prometheus.yml, observability/grafana/dashboards/reports-ui.json, docs/observability.md
Adds the Reports UI scrape job, dashboard panels, and monitoring documentation.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Browser
  participant Caddy
  participant ReportsAPI
  Browser->>Caddy: Submit report through /api/reports
  Caddy->>ReportsAPI: Proxy request to reports:8083
  ReportsAPI-->>Caddy: Return job status
  Caddy-->>Browser: Return API response
  Browser->>Caddy: Poll job status
  Caddy->>ReportsAPI: Proxy status request
  ReportsAPI-->>Browser: Return status or download link
Loading

Possibly related PRs

  • vovinacci/devops-demo#179: Extends the same nightly E2E smoke path with Reports service checks that this change complements with Reports UI assertions.

Poem

A bunny hops through Caddy’s door,
Reports bloom on port eight-oh-four.
Jobs hop from pending to done,
Metrics sparkle in Prometheus sun.
“Healthz!” cries the rabbit with glee—
A dashboard grows beside the tree.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 27.27% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: a new reports-ui Caddy-served static SPA over the reports API.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/reports-ui-service

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@vovinacci

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/reports-ui.yml:
- Around line 35-43: Update the caddy fmt step so the formatter command used to
generate Caddyfile.fmt cannot terminate the shell before diff runs; append the
failure suppression only to the docker run command that writes the comparison
output. Preserve the existing diff check and explicit failure message so
unformatted Caddyfiles still fail the job.

In `@services/reports-ui/site/app.js`:
- Around line 73-76: Update the polling flow around pollJob and submission
handling to track a submission generation or abort signal. Invalidate the
previous generation when a new job is submitted, and ensure stale poll
responses, errors, and timer scheduling are ignored so only the active job can
update status/download state or schedule another poll; retain clearing pollTimer
as cleanup.
- Around line 122-131: Update the polling error handling around pollJob so
recoverable transport and HTTP 5xx failures schedule another poll with
setTimeout until POLL_TIMEOUT_MS is reached, instead of returning permanently.
Preserve the existing showBanner behavior and active-job guard, while continuing
to stop polling immediately for non-recoverable errors and after the timeout.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 9c746e20-fba0-471b-9750-f809598b0aed

📥 Commits

Reviewing files that changed from the base of the PR and between 329740e and 6a07801.

📒 Files selected for processing (19)
  • .github/workflows/e2e.yml
  • .github/workflows/images.yml
  • .github/workflows/reports-ui.yml
  • Makefile
  • README.md
  • deploy/compose/docker-compose.yml
  • docs/architecture.md
  • docs/ci.md
  • docs/observability.md
  • observability/grafana/dashboards/reports-ui.json
  • observability/prometheus.yml
  • scripts/e2e-smoke.sh
  • services/reports-ui/Caddyfile
  • services/reports-ui/Dockerfile
  • services/reports-ui/Makefile
  • services/reports-ui/README.md
  • services/reports-ui/site/app.js
  • services/reports-ui/site/index.html
  • services/reports-ui/site/styles.css

Comment thread .github/workflows/reports-ui.yml
Comment thread services/reports-ui/site/app.js
Comment thread services/reports-ui/site/app.js Outdated
…fmt diag

Address PR #182 review findings:

- app.js: a resubmission could leave an earlier in-flight poll running, which
  would overwrite the new job's status/download and schedule a second poller.
  A submission-generation token now tags each poll; a poll whose generation is
  no longer current bails after every await and schedules nothing.
- app.js: a transient network error or 5xx during polling permanently stopped
  monitoring before the 60s deadline (Refresh only reloads the table). Such
  recoverable failures now reschedule until the deadline; a 4xx (job gone) or
  the deadline still stops with the existing banner.
- reports-ui.yml: append `|| true` to the `caddy fmt` capture so a non-zero
  exit no longer aborts the step under `set -e` before the diff prints the
  "run make ... format" helper. The diff remains the gate -- an unformatted
  Caddyfile still fails.
@vovinacci
vovinacci merged commit e4bbafc into main Jul 24, 2026
14 checks passed
@vovinacci
vovinacci deleted the feat/reports-ui-service branch July 24, 2026 13:14
vovinacci added a commit that referenced this pull request Jul 27, 2026
The phase tags were never pushed. origin carries only v1.1.0-v1.5.0, so
`git diff phase-2..phase-3` fails for everyone who clones this repo -- the
course promised a history workflow no reader could run, in eleven places.

Pushing them was the alternative, and it is worse. Every tag predates the
loopback port binding and the dashboard uid fix: all eight publish ports on
every interface (8 mappings at phase-0, rising to 18 at rfc-0002) and none
pins a dashboard uid. Publishing eight trees that put Postgres on the wire,
to an audience that will check them out and run them, is not worth a diff
convenience. Better to not ship the footgun than to document it.

The construction history is unaffected: every phase is a run of PRs on main,
RFC-0001 Section 9 lists what each delivered, and the ADRs froze the
decisions. The Tag column, the checkout instruction, the read-only-checkpoint
paragraph, and the two "the rfc-000N tag will mark it" promises are gone.

Tags deleted locally, recreatable from these commits if ever wanted:

  phase-0   8775267  Phase 0: repo restructure, CI, baseline hardening (#143)
  phase-1   99d458a  Phase 1: three-layer monitoring, canary v1 (#159)
  phase-2   23170c3  Phase 2: contract-first gRPC + OpenTelemetry (#162)
  phase-3   a8ccbc0  Phase 3: Go analytics, pipeline-lag canary v2 (#167)
  phase-4   ec4810b  Phase 4: load profile, Alertmanager, e2e gate (#172)
  phase-5   9cb1b1f  Phase 5: historical seeder, workshop mode (#175)
  phase-6   393989a  Phase 6: Kotlin reports, report load, canary v3 (#180)
  rfc-0002  e4bbafc  RFC-0002: reports-ui Caddy static SPA (#182)

All eight are ancestors of main, so the commits stay reachable regardless.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant