6.3 KiB
Routes
| Route | Screen | Query | API |
|---|---|---|---|
| /settings | Settings (Version, Media card, Media cleanup, Retention) | none | GET/PATCH /v1/media/purge-settings, GET/PATCH /v1/settings |
| /settings/media/variants | Image variants (admin) | none | GET/PATCH/DELETE /v1/image-variants, POST /v1/image-variants/{name}/purge |
| /settings/media/cache | Cache (admin) | none | GET /v1/image-variants/cache, POST /v1/image-variants/cache/purge {scope: all |
| /media | Media library | page, size, q, filter, sort, view | GET /v1/media/?page=&size=&q=&filter=&variants=&sort= (page, size; total in the X-Total-Count header) |
| /media (detail) | Variants tab | none | GET /v1/media/{id}/variants, POST and DELETE /v1/media/{id}/variants/{name} |
| /articles | Articles list | page, size, tab, q | GET /v1/article/?page=&size=&q=&status= (page, size; total in the X-Total-Count header; array body unchanged) |
| /activity | Activity | q, actor, category, outcome, security, range, from, to, page, size | GET /v1/activity (page, size), GET /v1/activity/export (shipped, CSV) |
| /integration | Integration | none | none |
| /settings/sites | Sites (owner only; other roles get a 403 page and no sidebar entry): list, add and edit slide-over, delete with reassignment. The key is read-only after creation | none | GET/POST /v1/site/, PATCH/DELETE /v1/site/{id} (DELETE takes reassign_to) |
| /authors | Authors, all roles. Owner and admin: list, search, add and edit slide-over, delete with reassignment. Other roles: read-only list and detail | q, page, size | GET/POST /v1/author/, PATCH/DELETE /v1/author/{id}, POST /v1/author/me |
| /articles | Articles list gains Authors column; Site column and filter when more than one site | site | GET /v1/article/?site={key} |
| /article/:id/edit | Editor gains the "Site and authors" section (Site select and authors, any role that can edit the article) | none | PATCH /v1/article/{id} {site_id, author_ids[]} |
| /profile | Profile gains the "Author profile" card, all roles | none | POST /v1/author/me |
| /integration/api-keys | API keys | none | GET/POST /v1/api-key, DELETE /v1/api-key/{id} |
Pagination in the URL (Articles, Media)
/articles?page=2&size=50 and /media?page=7&size=50. Parameter names follow the repo (activity already uses page and size). Allowed sizes 5, 25, 50, 100. Defaults page=1, size=25, omitted from the URL when default. Changing search, tab, filter or size resets page to 1. page above the last page redirects (replace) to the last page and shows "That page doesn't exist. Showing the last page." An invalid size falls back to 25.
API: GET /v1/article/ accepts page (default 1) and size (5, 25, 50, 100; default 25), as GET /v1/activity does. The body stays a JSON array so existing consumers keep working; the total comes back in X-Total-Count (the response shape is to be confirmed with the specifier). Anonymous callers only see online articles, and the total counts only those. Invalid page or size answers 400, the same as Activity.
Media API: GET /v1/media/ takes the same page and size, with the same 400 and the total in X-Total-Count; the array body stays. The earlier cursor and limit parameters are replaced by them.
Page size memory: the size chosen on Articles, Activity or Media is remembered for the user and used when the URL has no size; an explicit size in the URL wins.
SPA: never shows that 400. A size outside 5, 25, 50, 100 or a page that is not a positive integer is replaced in the URL by the default (history.replace), silently. A page past the last page is replaced by the last page and the notice "That page doesn't exist. Showing the last page." appears.
Activity pagination
page and size, allowed sizes 5, 25, 50, 100, default 25 (as implemented).
Activity export
GET /v1/activity/export: implemented. CSV only, synchronous (one streamed download, no job, no progress, no later notification). Accepts the list filters q, actor, category, outcome, security, range, from, to. Owner and admin only; others get 403. "Export current view" calls it with the active filters. "Export all activity" calls it with no filters and an optional from/to range. JSON (format=json) is proposed: new backend feature.
Public image request
/media/photo.png?v=<variant-name>. Unknown or disabled variant: 400 { "status": 400, "error": "unknown_variant", "message": "Unknown variant", "detail": "<name>" }. Any other query parameter: 400 { "status": 400, "error": "unsupported_parameter", "message": "Unsupported parameter", "detail": "<parameter>" }. Missing source image: 404.
Sites and authors in the URL
/articles?site=journal filters by site key; it combines with page, size, tab and q, and resets page to 1 when it changes. An unknown key is dropped from the URL with no error. /authors?q=camille&page=2&size=25 follows the same pagination rules as Articles. Deep links: /authors?edit={slug} and /settings/sites?edit={key} open the slide-over for owner and admin (authors) and the owner (sites); an unknown value shows the info toast "That author no longer exists" or "That site no longer exists". For other roles /authors?edit={slug} opens the read-only detail, and /settings/sites and its ?edit= link show the 403 page "You don't have access to Sites".
Sites pagination: /settings/sites?q=&page=&size= with the same rules as Articles (sizes 5, 25, 50, 100; default 25; defaults omitted; search and size changes reset to page 1; a page past the end falls back to the last page). API: GET /v1/site/?q=&page=&size=, total in X-Total-Count (design assumption). Authors: /authors?q=&page=&size= as above.
SEO
No new page routes. The panel and dialog are overlays on /article/:id/edit: ?panel=seo (optionally &field=seo_title|seo_description|slug|cover|authors|body for the suggestion jump) and ?dialog=seo-api. /articles?seo=suggestions is the Needs attention filter. API: GET /v1/article/{id}/seo, GET /v1/site/{key}/sitemap?page=&size=, PATCH /v1/article/{id} (new fields), PATCH /v1/site/{id} {site_name, title_template, default_description, social_image_id, default_robots}, GET /v1/article/?seo=ready|suggestions|hidden. 401 without a session; 403 owner_only on site SEO fields; 409 slug_taken {suggestion} on PATCH slug; 400 on invalid canonical_url or title_template.