Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,12 @@ Generates static content into the `build/` directory.

The site builds in one of two modes, controlled by `DEPLOY_ENV`:

- `DEPLOY_ENV=production` — emits `https://docs.trakrf.id` / `https://app.trakrf.id` URLs in SSR HTML, sitemap, canonical tags, and the `<EnvBaseURL/>` family of components. The Redoc-rendered `/api` page fetches the spec from `https://app.trakrf.id/api/v1/openapi.yaml` at build time.
- `DEPLOY_ENV=preview` (default for local dev) — emits the `*.preview.trakrf.id` equivalents and fetches the spec from `https://app.preview.trakrf.id/api/v1/openapi.yaml`.
- `DEPLOY_ENV=production` — emits `https://docs.trakrf.id` / `https://app.trakrf.id` URLs in SSR HTML, sitemap, canonical tags, and the `<EnvBaseURL/>` family of components. The Redoc-rendered `/api` page fetches the spec from `https://app.trakrf.id/api/openapi.yaml` at build time.
- `DEPLOY_ENV=preview` (default for local dev) — emits the `*.preview.trakrf.id` equivalents and fetches the spec from `https://app.preview.trakrf.id/api/openapi.yaml`.

On Cloudflare Pages, set `DEPLOY_ENV=production` on the production environment and `DEPLOY_ENV=preview` on the preview environment. If unset, the build auto-detects from `CF_PAGES_BRANCH` (`main` → production, anything else → preview).

The OpenAPI spec is single-source on the platform; this site never stores a mirrored copy. `/api/openapi.{json,yaml}` 302s to `https://app.{env}.trakrf.id/api/v1/openapi.{json,yaml}` via `functions/_middleware.js`.
The OpenAPI spec is single-source on the platform; this site never stores a mirrored copy. `/api/openapi.{json,yaml}` 302s to `https://app.{env}.trakrf.id/api/openapi.{json,yaml}` via `functions/_middleware.js`.

### Serve

Expand Down
4 changes: 2 additions & 2 deletions docs/api/README.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ export const SpecLinks = () => {
const { appHost } = useDeployEnv();
return (
<>
<code>{appHost}/api/v1/openapi.json</code> /{" "}
<code>{appHost}/api/v1/openapi.yaml</code>
<code>{appHost}/api/openapi.json</code> /{" "}
<code>{appHost}/api/openapi.yaml</code>
</>
);
};
Expand Down
4 changes: 2 additions & 2 deletions docs/api/postman.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ export const SpecUrls = () => {
return (
<ul>
<li>
<code>{appHost}/api/v1/openapi.yaml</code>
<code>{appHost}/api/openapi.yaml</code>
</li>
<li>
<code>{appHost}/api/v1/openapi.json</code>
<code>{appHost}/api/openapi.json</code>
</li>
</ul>
);
Expand Down
6 changes: 3 additions & 3 deletions docs/api/quickstart.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ Strict-typed codegen (Pydantic, Java with generated POJOs, Go with generated str

Prefer a GUI? Postman (and Insomnia, Bruno, Hoppscotch) imports the OpenAPI spec from a URL and generates a request collection automatically:

1. In Postman, **File → Import → Link**, paste <EnvBaseURL />/api/v1/openapi.yaml.
1. In Postman, **File → Import → Link**, paste <EnvBaseURL />/api/openapi.yaml.
2. Set the collection variables:
- `baseUrl` → `https://app.trakrf.id` (or `https://app.preview.trakrf.id` for preview accounts) — bare host, no `/api/v1` suffix; paths in the collection already include the version prefix
- `bearerToken` → the JWT from step 2
Expand All @@ -170,9 +170,9 @@ Full detail: [API clients](./postman).
If you'd rather generate a typed client, the OpenAPI spec is served from the platform in both formats:

- <EnvBaseURL />
/api/v1/openapi.json (JSON)
/api/openapi.json (JSON)
- <EnvBaseURL />
/api/v1/openapi.yaml (YAML)
/api/openapi.yaml (YAML)

Feed either into `openapi-generator-cli`, NSwag, `oapi-codegen`, etc. to scaffold client code in your language. The spec is regenerated from the Go handlers on every platform release, so the generated client stays in sync with the running service.

Expand Down
2 changes: 1 addition & 1 deletion docusaurus.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const appHost =
deployEnv === "production"
? "https://app.trakrf.id"
: "https://app.preview.trakrf.id";
const specUrl = `${appHost}/api/v1/openapi.yaml`;
const specUrl = `${appHost}/api/openapi.yaml`;

const config: Config = {
title: "TrakRF Docs",
Expand Down
37 changes: 35 additions & 2 deletions functions/_middleware.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Cloudflare Pages Functions middleware.
//
// Two jobs:
// Three jobs:
//
// 1. Spec asset redirects. /api/openapi.{json,yaml} 302 to the platform's
// canonical spec URL on app.{env}.trakrf.id. Single source of truth lives
Expand All @@ -12,6 +12,13 @@
// (see REWRITE_MAP below). All collapse to /api/openapi.{json,yaml},
// which then redirects out via job (1).
//
// 3. Explicit 410 Gone for retired mirror artifacts. `platform-meta.json`
// and the bundled Postman collection were deleted in TRA-743 but
// Cloudflare's edge cache may keep serving the last-deploy 200 for the
// TTL window. Returning an authoritative 410 with `Cache-Control:
// no-store` displaces the stale entry on next request and prevents any
// BB cycle from treating the stale body as ground truth.
//
// History note: redirects used to live in static/_redirects, but Cloudflare
// Pages on this project does not appear to honor _redirects entries — the
// original /redocusaurus/trakrf-api.yaml rule (TRA-598) was never actually
Expand All @@ -35,6 +42,13 @@ const REWRITE_MAP = {
"/redocusaurus/trakrf-api.yaml": "/api/openapi.yaml",
};

/** Retired mirror artifacts — return 410 Gone with no-store to displace
* any stale CF-edge-cached 200 responses from the pre-TRA-743 deploy. */
const RETIRED_PATHS = new Set([
"/api/platform-meta.json",
"/api/trakrf-api.postman_collection.json",
]);

/** @param {URL} url */
function resolveAppOrigin(url) {
const host = url.hostname;
Expand All @@ -49,6 +63,21 @@ function resolveAppOrigin(url) {
export const onRequest = async ({ request, next }) => {
const url = new URL(request.url);

if (RETIRED_PATHS.has(url.pathname)) {
return new Response(
`Gone. This artifact was retired in TRA-743 (single-source OpenAPI ` +
`spec on platform). See https://docs.trakrf.id/docs/api/postman for ` +
`URL-import guidance.\n`,
{
status: 410,
headers: {
"Content-Type": "text/plain; charset=utf-8",
"Cache-Control": "no-store",
},
},
);
}

const alias = REWRITE_MAP[url.pathname];
if (alias) {
const dest = new URL(alias, url.origin);
Expand All @@ -58,7 +87,11 @@ export const onRequest = async ({ request, next }) => {
const specFormat = SPEC_TARGETS[url.pathname];
if (specFormat) {
const appOrigin = resolveAppOrigin(url);
const dest = `${appOrigin}/api/v1/openapi.${specFormat}`;
// Target the platform's canonical path (`/api/openapi.*`); the
// `/api/v1/openapi.*` form on platform is itself a 301 to the canonical,
// so naming the intermediate hop would add an extra redirect to every
// client.
const dest = `${appOrigin}/api/openapi.${specFormat}`;
return Response.redirect(dest, 302);
}

Expand Down
7 changes: 3 additions & 4 deletions tests/blackbox/BB.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ After your exploratory evaluation, run a mechanical pass against the published O

The OpenAPI spec is published at `$API_TEST_DOCS_URL/api/openapi.yaml` (JSON variant: `$API_TEST_DOCS_URL/api/openapi.json`). If that path 404s, that is itself a finding worth reporting. If the docs don't link to it from a discoverable location, that's also a finding.

The docs origin 302-redirects these paths to the platform's canonical spec on `app.{env}.trakrf.id/api/v1/openapi.{yaml,json}` — single source of truth, no mirror. Standard tooling follows the redirect transparently (curl with `-L`, every OpenAPI codegen client, every API-explorer import-by-URL flow). The spec at this URL is the contract you're testing against.
The docs origin 302-redirects these paths to the platform's canonical spec on `app.{env}.trakrf.id/api/openapi.{yaml,json}` — single source of truth, no mirror. Standard tooling follows the redirect transparently (curl with `-L`, every OpenAPI codegen client, every API-explorer import-by-URL flow). The spec at this URL is the contract you're testing against.

### 2. Walk every path

Expand Down Expand Up @@ -173,11 +173,10 @@ For each generator, run a CRUD lifecycle through the generated client against th
- Authentication setup friction — does the generated client know how to attach the API key from the spec alone, or did you have to wire it manually?
- Generated identifier names — are model class names, method names, and field names clean and conventional in the target language? Flag double-prefixed names (e.g., `AssetPublicAssetView` where the spec schema is `asset.PublicAssetView`), unusual casing (`assets_create` where `createAsset` is conventional), or names that leak the backend's package/namespace structure into the client.

### 11. Multi-format spec consistency
### 11. Multi-format spec sanity

The spec may be served in multiple formats (YAML, JSON, Redoc-served copy). Fetch each variant and compare:
`/api/openapi.yaml` and `/api/openapi.json` resolve to the same platform binary via redirect — divergence between formats would mean the platform is encoding the spec twice and the two encoders disagree, which is a platform bug, not a docs-mirror drift class. So this check is light:

- Are the artifacts byte-equivalent after canonical sort?
- Do numeric literals match across formats? YAML scientific notation (`2.147483647e+09`) parses as float in standard YAML loaders, even when paired with `type: integer` — flag any divergence between YAML and JSON variants for the same field.
- Do any artifacts contain hardcoded URLs that differ from the environment the spec is served from (preview spec hardcoding production docs URL, etc.)?
- Are all variants linked from at least one discoverable docs page? An undiscoverable artifact is a finding.
Expand Down
Loading