From 7c0ee219068b82b2d397786eaa6adca6cd33b4ec Mon Sep 17 00:00:00 2001 From: Florian Sylvain Date: Mon, 26 Oct 2026 02:18:35 +0100 Subject: [PATCH] feat: updated integration page with missing API routes --- docs/design/screens/integration.md | 44 +- docs/design/source/IntegrationPage.dc.html | 96 ++- docs/design/source/IntegrationSearch.dc.html | 51 +- .../RenewCMS Screens Back-office.dc.html | 16 +- features/integration.feature | 19 +- features/integration.qa.md | 2 +- features/integration_api_key_routes.feature | 169 +++++ features/integration_api_key_routes.qa.md | 47 ++ features/integration_search.feature | 16 +- features/integration_search.qa.md | 10 +- web/src/components/EndpointCard.vue | 12 +- web/src/data/__tests__/apiCatalogue.spec.ts | 272 +++++-- web/src/data/apiCatalogue.ts | 678 +++++++++++++++--- web/src/i18n/messages.ts | 98 ++- web/src/i18n/metaMessages.spec.ts | 8 +- web/src/lib/__tests__/apiSearch.spec.ts | 70 +- web/src/lib/apiReference.ts | 12 +- web/src/lib/apiSearch.ts | 68 +- web/src/views/IntegrationView.vue | 228 ++++-- .../views/__tests__/IntegrationImages.spec.ts | 4 +- .../views/__tests__/IntegrationSearch.spec.ts | 109 ++- .../views/__tests__/IntegrationView.spec.ts | 588 ++++++++++++++- .../views/__tests__/SeoIntegration.spec.ts | 18 +- web/visual/check.ts | 10 + web/visual/states.ts | 107 ++- 25 files changed, 2402 insertions(+), 350 deletions(-) create mode 100644 features/integration_api_key_routes.feature create mode 100644 features/integration_api_key_routes.qa.md diff --git a/docs/design/screens/integration.md b/docs/design/screens/integration.md index 31eadc9..0bba967 100644 --- a/docs/design/screens/integration.md +++ b/docs/design/screens/integration.md @@ -15,7 +15,7 @@ Sticky section index on the right (desktop), "Jump to" select (mobile). A refere - Card "API key": "Send `Authorization: Bearer rcms_…`. Requests act as the user who created the key, with that user's role (owner or admin). A key never has more rights than its creator." CodeSnippet tabs curl, fetch (GET /v1/auth/me). "Keys start with `rcms_` and are shown in full once, when you create them." Link "Manage API keys". - Note 1: "A key can't create, list or revoke API keys, log in, register, verify or reset a password, or call anything under `/v1/system`. Those routes answer 403 "This action needs a real session." An invalid, unknown, expired or revoked key answers 401, even on public routes, and so does a key whose user was deleted or unverified." - Note 2: "Each key can make 120 requests per minute. Beyond that the API answers 429 with a `Retry-After` header." -- Groups: Articles (7), Media (6), Users and invites (6), Settings (1), Session only (12 routes, 3 sub-groups; every `/v1/system` route is in this group). Session-only banner: "These routes need a signed-in browser session. With an API key they answer 403 "This action needs a real session."" +- Groups, in index order: Authentication, Articles (7), Search and sharing (2), Content types (5), Entries (5), Built-in types (2), Media (10), Sites and authors (2), Images and variants, Users and invites (6), Settings (1), Session only (35 routes in 6 sub-groups: System, Sign in and account, API key management, Sites and authors writes, Image variants, Activity; every `/v1/system` route is in this group), Errors, Notes. Session-only banner: "These routes need a signed-in browser session. With an API key they answer 403 "This action needs a real session."" - Errors: "Every error is a plain-text body with a single sentence. The status code is in the HTTP status line, and the Content-Type is text/plain." Table 400, 401, 403, 404, 429, 500 with the real messages below. The 403 row has a note line under it: "Site SEO defaults are owner only. Article SEO fields need only the right to edit the article." (12/18, `--ui-text-muted`, in the cause column, 2 px under the cause text). It is the only row with a note; rows below it move down by the wrapped note height (two 18 px lines on desktop, three on mobile, plus 2 px). Sample: a status line plus the text body (`HTTP/1.1 403 Forbidden`, `Content-Type: text/plain`, blank line, `This action needs a real session.`). - Notes: image URL prefix, CORS (existing copy), and "Article routes accept `?format=markdown`, `?format=html` or `?format=json` to choose how `body` is returned. With `json`, `body` is the stored JSON structure. Articles have `body_format: markdown` by default." @@ -139,10 +139,10 @@ Board: group "Integration", states 8a to 8h, desktop 1440×900 and mobile 390×8 |---|---|---| | 8a | Trigger focused | Placeholder "Search the API reference…"; key hint "/"; aria-label "Search the API reference" | | 8b | Open, empty | Input placeholder "Search the API reference…"; group "Suggested": Authentication ("Public access and API keys"), POST /v1/media/ ("Upload an image (multipart)." Verified user), Errors ("Status codes and messages"), GET /v1/article/ ("List articles, one page at a time." Public), PATCH /v1/user/me ("Update my profile." Verified user); footer "↑↓ Move", "↵ Open", "Esc Close", count "5 suggestions"; live "5 suggestions" | -| 8c | Typing "media" | Groups "Endpoints" (6 media routes) and "Sections" (Media, "6 routes"); count and live "7 results" | -| 8d | "404", third row selected | Endpoints list the six first 404 routes with an "Error" line, for example "404 Article not found"; Sections: Errors ("Status codes and messages"); "6 more endpoints. Keep typing to narrow the list."; count "13 results"; live "13 results, showing 7" | +| 8c | Typing "media" | Groups "Endpoints" (6 media routes) and "Sections" (Media, "10 routes"); count and live "17 results, showing 7" | +| 8d | "404", third row selected | Endpoints list the six first 404 routes with an "Error" line, for example "404 Article not found"; Sections: Errors ("Status codes and messages"); "N more endpoints. Keep typing to narrow the list."; count "22 results"; live "22 results, showing 7" | | 8e | "keep_id" | POST /v1/media/merge, "Merge identical copies into one image.", Verified user, line "Body field keep_id"; "1 result" | -| 8f | "login" | POST /v1/auth/login, "Log in", chip "Session only" (lock); Section "Session only", "12 routes, not available to API keys"; "2 results" | +| 8f | "login" | POST /v1/auth/login, "Log in", chip "Session only" (lock); Section "Session only", "35 routes, not available to API keys"; "2 results" | | 8g | No results | No match for "xyz". Try a path, a method or a status code like 404. Count "0 results"; live "No results" | | 8h | After selecting | Page only: POST /v1/media/merge expanded and highlighted; index and Jump to read "Media" | @@ -162,3 +162,39 @@ Board: group "Integration", states 8a to 8h, desktop 1440×900 and mobile 390×8 - The search trigger scrolls away with the header on desktop. Open question: keep it sticky next to the section index? - Highlight uses --rc-sand on --rc-on-sand. On the selected row (--ui-bg-elevated) the two tones are close; the dark text keeps contrast. No new token is needed. - The 1.5 s flash duration and the #ep- hash are proposals. + + +## Custom content types round +Board: group "Integration", states 9a to 9j. Source: `IntegrationPage.dc.html`, `IntegrationSearch.dc.html`. Every route an API key can call is now documented. + +### New groups +- Content types (`ctypes`): description "Define your own kind of content, like events or recipes. An owner or admin creates a type made of 1 to 50 ordered fields; the type then has its own entry routes." A "Field types" table (Type, Value in an entry, Rules) and a sample field object sit above the routes (anchor `ct-fields`). + - `GET /v1/content-type/` and `GET /v1/content-type/{key}`: Verified user. 404 `content_type_not_found`. + - `POST /v1/content-type/`: Owner or admin. Body `key` (2 to 40, lowercase letters, digits, hyphens), `name` (max 100), `icon` (optional), `fields` (1 to 50). 409 `content_type_key_taken`. + - `PUT /v1/content-type/{key}`: Owner or admin. Body `name`, `icon`, `fields`. 409 `schema_incompatible`. + - `DELETE /v1/content-type/{key}`: Owner or admin. 204. 409 `content_type_has_entries`, 409 `content_type_referenced`. +- Entries (`entries`): `GET` and `POST /v1/content-type/{key}/entry`, `GET`, `PUT` and `DELETE /v1/content-type/{key}/entry/{id}`. Verified user. List query `page`, `size` (5, 25, 50, 100), `sort` (`id`, `created_at`, `updated_at`, then `:asc` or `:desc`); the response is `{ items, total, page, size }`, not a bare array. Body `data` (object keyed by field key). `media` in a response is keyed by field key. 409 and 422 bodies are both `invalid_entry: `; 409 only when a unique field clashes. Id 400: "The server expects the ID to be in the format of an unsigned 32-bit integer (uint32)." +- Built-in types (`builtin`): `GET /v1/builtin-type/` and `/{key}`, Owner or admin. 404 `builtin_type_not_found`. +- Sites and authors (`sites`): `GET /v1/site/` and `GET /v1/author/` (paged, `{ items, total, page, size }`), Verified user. +- Media adds `GET` and `PATCH /v1/media/purge-settings` (`auto_purge_enabled`, `auto_purge_interval_days` 1 to 365; response also `last_purge_at`, `last_purge_merged`), `GET /v1/media/purge-preview` and `POST /v1/media/purge` (optional `fingerprint` from the preview). Owner or admin. +- PUT uses the PATCH badge colours. + +### Access and errors +- 403 rows: "Only owners and admins can do this." (owner-or-admin routes) and "Only the owner can do this.", both listed in the 403 row; the existing note stays. +- 404 adds `content_type_not_found`, `entry_not_found`, `builtin_type_not_found`. 409 and 422 are real rows now (earlier text said there is no such row). Code-style text is the literal body. +- Authentication copy says a key can also manage content types and entries, and owner or admin routes answer 403 to a key whose creator is neither. + +### Search +The index covers the new endpoints, body fields (`data`, and the events sample keys such as `capacity`) and error codes. "entry" also matches "entries". "N more endpoints" is singular for one. + +### States 9a to 9j +9a create a content type, curl tab · 9b entries list, Response tab · 9c entry replace with the 409 and 422 errors · 9d Errors table · 9e Session only, 6 sub-groups · 9f search "entry" · 9g search "capacity" (Body field) · 9h search "schema_incompatible" (Error) · 9i search "content type" (Sections) · 9j navigation with scroll-spy on Content types. 8c now announces "17 results, showing 7" and 8d "22 results, showing 7" (the copy table above predates this; the board is right). + +### Final round +- `POST /v1/media/purge` and the built-in type samples now match the API (no `icon`; keys `article`, `author`, `site`, `media`, `user`). Built-in types are read-only through the API. +- `GET /v1/media/` takes `page` (default 1) and `size` (5, 25, 50, 100; default 25), returns a JSON array and sends the total in `X-Total-Count`; 400 "Invalid page or size." +- Counts: Articles 7, Search and sharing 2, Content types 5, Entries 5, Built-in types 2, Media 10, Sites and authors 2, Users and invites 6, Settings 1, Session only 35. Search index: 75 endpoints (40 documented, 35 session-only). +- Announced: 8c "17 results, showing 7", 8d "22 results, showing 7", 8f "2 results", 9f "8 results, showing 7", 9g "2 results", 9h "1 result", 9i "12 results, showing 8". +- Illustrative only: built-in sample fields, purge-preview sample, the reason after `invalid_entry:`, session-only row labels, entry `created_by`/`updated_by`, `cover` media data. +- Not confirmed by the design tool: dark mode, the search palette on mobile and the Session only list on mobile. `task visual:check` is the gate. +- `POST /v1/content-type/` and `POST …/entry` answer 201; POST cards show only the JSON body. diff --git a/docs/design/source/IntegrationPage.dc.html b/docs/design/source/IntegrationPage.dc.html index 484d06e..5c65253 100644 --- a/docs/design/source/IntegrationPage.dc.html +++ b/docs/design/source/IntegrationPage.dc.html @@ -28,7 +28,7 @@ -

RenewCMS is headless: it stores your content and your own code displays it. There are two ways in. Public read-only access lets a website read online articles. API keys let a script or service manage articles, media and users without the back-office.

+

RenewCMS is headless: it stores your content and your own code displays it. There are two ways in. Public read-only access lets a website read online articles. API keys let a script or service manage articles, media and users, and manage content types and entries, without the back-office.

Base URL @@ -52,7 +52,7 @@
-
A key can't create, list or revoke API keys, log in, register, verify or reset a password, or call anything under /v1/system. Those routes answer 403 "This action needs a real session." An invalid, unknown, expired or revoked key answers 401, even on public routes, and so does a key whose user was deleted or unverified.
+
A key can't create, list or revoke API keys, log in, register, verify or reset a password, or call anything under /v1/system. Those routes answer 403 "This action needs a real session." An invalid, unknown, expired or revoked key answers 401, even on public routes, and so does a key whose user was deleted or unverified. Owner and admin routes answer 403 to a key whose creator is neither.
Each key can make 120 requests per minute. Beyond that the API answers 429 with a Retry-After header.
@@ -61,6 +61,11 @@

{{ g.title }}

{{ g.count }}
{{ g.desc }} +
{{ p.t }}{{ p.t }}
+
Field types
+
TypeValue in an entryRules
+
{{ r.t }}{{ p.t }}{{ p.t }}{{ p.t }}{{ p.t }}
+
@@ -77,9 +82,9 @@
NameTypeRequiredRule
{{ f.n }}{{ f.t }}{{ f.req }}{{ f.rule }}
No request body.
- +
Errors -
{{ e.c }}{{ e.cap }}{{ e.cap }}
+
{{ e.c }}{{ e.cap }}{{ e.cap }}{{ e.tail }}
@@ -89,7 +94,7 @@
-

Session only

12 routes, not available to API keys
+

Session only

{{ sessCount }}
These routes need a signed-in browser session. With an API key they answer 403 "This action needs a real session."
@@ -108,7 +113,7 @@ Every error is a plain-text body with a single sentence. The status code is in the HTTP status line, and the Content-Type is text/plain.
StatusMeaningTypical cause
-
{{ r.c }}{{ r.m }}{{ r.w }}{{ r.n }}
+
{{ r.c }}{{ r.m }}{{ p.t }}{{ p.t }}{{ r.n }}
@@ -134,11 +139,16 @@