Skip to content

feat(ui): render the API reference in-app instead of a CDN#266

Merged
PhenX merged 7 commits into
mainfrom
claude/api-docs-offline-support-lql5nd
Jul 17, 2026
Merged

feat(ui): render the API reference in-app instead of a CDN#266
PhenX merged 7 commits into
mainfrom
claude/api-docs-offline-support-lql5nd

Conversation

@PhenX

@PhenX PhenX commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator

The /docs page loaded Scalar from cdn.jsdelivr.net, so it rendered blank on
any air-gapped / restricted network (a core self-hosting audience) and quietly
contradicted the "zero telemetry, no phone-home" promise on every visit.

Replace it with a small self-contained OpenAPI renderer (a Nuxt page plus
app/components/docs/*) that renders the auto-generated /_openapi.json entirely
in-app: no third-party CDN, works offline, and makes no outbound calls. The
page now lives in the standard dashboard shell, is theme-aware and responsive,
groups endpoints by tag with a filter, and shows the required "Windows users"
curl tip at the top. If the spec itself is unreachable it falls back to a link
to the raw JSON. Works the same in the static demo (client-side fetch).

Co-Authored-By: Claude Opus 4.8 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01RJWPM2eGX9PE2cjecfv3MX

claude added 4 commits July 16, 2026 20:16
The /docs page loaded Scalar from cdn.jsdelivr.net, so it rendered blank on
any air-gapped / restricted network (a core self-hosting audience) and quietly
contradicted the "zero telemetry, no phone-home" promise on every visit.

Replace it with a small self-contained OpenAPI renderer (a Nuxt page plus
app/components/docs/*) that renders the auto-generated /_openapi.json entirely
in-app: no third-party CDN, works offline, and makes no outbound calls. The
page now lives in the standard dashboard shell, is theme-aware and responsive,
groups endpoints by tag with a filter, and shows the required "Windows users"
curl tip at the top. If the spec itself is unreachable it falls back to a link
to the raw JSON. Works the same in the static demo (client-side fetch).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RJWPM2eGX9PE2cjecfv3MX
Enrich the self-contained /docs reference with the interactivity Scalar
provided, while keeping it CDN-free and zero-outbound-by-default.

Each expanded operation now has a "Try it out" console: fill path/query
params, a bearer token, and a JSON body (prefilled from the schema), then
Send a real same-origin request and see the status, timing, and pretty
response body. Every operation also shows copy-paste cURL and fetch code
samples that reflect the current inputs. Requests only fire on click and
only to the same origin the dashboard is served from (the demo's service
worker handles them in the static build), so nothing phones home. Pure
helpers live in openapi-console.ts (unit-tested); the shared bearer token
in useApiConsole.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RJWPM2eGX9PE2cjecfv3MX
Every expanded endpoint now shows an "Access" line (Public / Any signed-in
user / Administrator / Administrator or Reporter), and admin- or reporter-only
routes get a compact role chip in the list header so they stand out.

Routes declare their allowed roles once as REQUIRED_ROLES (used by requireAuth),
but Nitro's OpenAPI extractor can't fold those enum references into the spec, so
they never reached the docs. A generator mirrors them into a committed
route-roles map keyed by "{method} {path}" that the reference reads; the
file→operation mapping was validated against the live spec (all 96 entries
match). A unit test regenerates and fails on drift, and the extraction/mapping
is shared between the generator and the test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RJWPM2eGX9PE2cjecfv3MX
…ne-support-lql5nd

# Conflicts:
#	application/package.json
@github-actions

github-actions Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Coverage Report for Reporter (./reporter)

Status Category Percentage Covered / Total
🔵 Lines 64.01% 1450 / 2265
🔵 Statements 62.39% 1649 / 2643
🔵 Functions 70.43% 243 / 345
🔵 Branches 58.86% 1195 / 2030
File CoverageNo changed files found.
Generated in workflow #914 for commit d0cd029 by the Vitest Coverage Report Action

@github-actions

github-actions Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Coverage Report for Application (./application)

Status Category Percentage Covered / Total
🔵 Lines 24.79% 2017 / 8134
🔵 Statements 24.72% 2345 / 9486
🔵 Functions 24.31% 364 / 1497
🔵 Branches 22.04% 1652 / 7495
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
application/app/composables/useApiConsole.ts 0% 100% 0% 0% 10-11
application/app/utils/openapi-console.ts 93.22% 82.35% 90% 97.87% 37, 42, 57, 66
application/app/utils/openapi.ts 94.91% 83.82% 100% 100% 152, 198, 247
application/server/utils/auth.ts 2.88% 0% 0% 2.88% 16-163, 178-312
application/server/utils/route-required-roles.ts 0% 100% 0% 0% 20-21
application/server/utils/route-roles-match.ts 88.88% 71.42% 100% 100% 25
application/shared/nitro-virtual.d.ts 0% 0% 0% 0%
Generated in workflow #914 for commit d0cd029 by the Vitest Coverage Report Action

claude added 3 commits July 17, 2026 06:35
The /docs role display previously read a generated side map because Nitro's
OpenAPI extractor dropped the routes' enum/variable role references. Fix it at
the source instead: each route's defineRouteMeta now declares its roles as a
string-literal x-required-roles array, which the extractor folds into the spec,
so the reference reads them straight from /_openapi.json (and any other spec
consumer sees them too).

Enforcement is untouched — requireAuth still gates on the typed Role[]; only the
doc metadata changed, and the now-orphaned REQUIRED_ROLES consts + Role imports
(documentation-only in routes that just require sign-in) were removed. Drops the
generated route-roles map, its generator/extractor, the drift test, and the
app:gen:route-roles script.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RJWPM2eGX9PE2cjecfv3MX
Make each route's x-required-roles OpenAPI meta the single source of truth for
authorization. requireAuth now reads the current route's roles from the compiled
route metas (matched with rou3, the same router Nitro dispatches with) and 403s
a user who lacks them, so routes no longer repeat the roles: the REQUIRED_ROLES
consts and the explicit requireAuth/requireProjectAccess role arguments are gone.
The argument stays as an override for the rare handler that computes its own
authorization (e.g. users self-or-admin edit).

Also corrects two metas whose old REQUIRED_ROLES const was documented but never
enforced (users list, search) to match their real any-signed-in-user access, so
switching to meta enforcement changes no behavior.

Verified end to end against an auth-enabled server: a reporter is 403'd on
admin-only routes and allowed on any-auth ones, an admin is allowed, and anon is
401'd; the matcher is unit-tested and a role-enforcement case was added to the
auth E2E spec.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RJWPM2eGX9PE2cjecfv3MX
Meta-based enforcement fixed the under-enforcement this test documented: a
"user"-role caller (not in the route's ['administrator', 'reporter']
x-required-roles) is now rejected with 403 before reaching the AI-config check,
instead of falling through to a 503. Update the assertion and its notes to the
corrected behavior.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RJWPM2eGX9PE2cjecfv3MX
@PhenX
PhenX merged commit 9814de6 into main Jul 17, 2026
7 checks passed
@PhenX
PhenX deleted the claude/api-docs-offline-support-lql5nd branch July 17, 2026 14:02
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.

2 participants