feat: Sites feature backend

This commit is contained in:
Florian Sylvain committed 2026-10-14 03:56:50 +02:00
1 parent 45fcbd890d
commit a11338094e
98 files changed
+5365 -214

No files matched your search

+78 -6
View File
@@ -126,6 +126,12 @@ const ARTICLE_LIST_QUERY: ApiField[] = [
required: false,
rule: 'field:direction, with field title, status, created_at or updated_at and direction asc or desc. Default updated_at:desc.',
},
{
name: 'site',
type: 'string',
required: false,
rule: 'The key of a site. Only its articles are listed. An unknown key answers an empty page.',
},
]
const MEDIA_QUERY: ApiField[] = [
@@ -168,6 +174,7 @@ const article = (overrides: Record<string, unknown> = {}) => ({
body: 'A bathroom renovation is priced by room size, plumbing changes and finish level.',
body_format: 'markdown',
is_online: true,
site_id: 1,
created_at: '2026-03-12T09:14:00Z',
updated_at: '2026-09-10T07:00:00Z',
...overrides,
@@ -189,6 +196,19 @@ const mediaItem = (overrides: Record<string, unknown> = {}) => ({
...overrides,
})
const site = (overrides: Record<string, unknown> = {}) => ({
id: 1,
name: 'Renew blog',
key: 'renew-blog',
base_url: 'https://blog.renew.example',
default_social_image_id: 4,
default_robots: 'index',
locale: 'en',
is_default: true,
article_count: 30,
...overrides,
})
const me = {
id: 1,
username: 'marta',
@@ -374,9 +394,15 @@ export const API_GROUPS: ApiGroup[] = [
'art-update',
'PATCH',
'/v1/article/{id}',
'Update the body of an article.',
'Update the body of an article, or move it to another site. Send body, site_id or both.',
'Verified user',
verified(articleErrors(e(400, BODY_MISMATCH), ...articleIdErrors)),
verified(
articleErrors(
e(400, BODY_MISMATCH),
...articleIdErrors,
e(404, 'site_not_found'),
),
),
json(article({ body: 'Updated text.', is_online: false })),
{
query: [FORMAT_QUERY],
@@ -385,11 +411,17 @@ export const API_GROUPS: ApiGroup[] = [
{
name: 'body',
type: 'string',
required: true,
rule: 'Up to 10 000 characters.',
required: false,
rule: 'Up to 10 000 characters. An empty string empties the body; null is refused.',
},
{
name: 'site_id',
type: 'number',
required: false,
rule: 'The id of an existing site, 1 or more. null is refused. At least one of body and site_id is required.',
},
],
bodySample: { body: 'Updated text.' },
bodySample: { body: 'Updated text.', site_id: 2 },
},
),
endpoint(
@@ -523,7 +555,7 @@ export const API_GROUPS: ApiGroup[] = [
e(401, UNAUTHORIZED),
e(409, 'Some of the images to merge changed. Refresh and try again.'),
]),
json({ articles_updated: 5, merged: 2 }),
json({ articles_updated: 5, sites_updated: 1, merged: 2 }),
{
bodyKind: 'json',
body: [
@@ -629,6 +661,7 @@ export const API_GROUPS: ApiGroup[] = [
groups_purged: 1,
merged: 2,
articles_updated: 5,
sites_updated: 1,
failed_groups: 0,
}),
{
@@ -784,6 +817,36 @@ export const API_GROUPS: ApiGroup[] = [
),
],
},
{
key: 'sites',
title: 'Sites',
description:
'The sites an article can belong to. Any verified user can read them; changes are made in the browser by the owner.',
endpoints: [
endpoint(
'site-list',
'GET',
'/v1/site/',
'List the sites, the default first and then by name, each with its article count.',
'Verified user',
verified([e(401, UNAUTHORIZED)]),
json([
site(),
site({
id: 2,
name: 'Renew shop',
key: 'renew-shop',
base_url: 'https://shop.renew.example',
default_social_image_id: null,
default_robots: 'noindex',
locale: null,
is_default: false,
article_count: 4,
}),
]),
),
],
},
]
const row = (
@@ -819,6 +882,15 @@ export const SESSION_GROUPS: SessionGroup[] = [
row('POST', '/v1/auth/password-reset', 'Reset password', false),
],
},
{
title: 'Sites · owner only',
rows: [
row('POST', '/v1/site/', 'Create a site'),
row('PATCH', '/v1/site/{id}', 'Edit a site'),
row('PATCH', '/v1/site/{id}/default', 'Make a site the default'),
row('DELETE', '/v1/site/{id}', 'Delete a site, moving its articles with reassign_to'),
],
},
{
title: 'Settings · owner only',
rows: [row('PATCH', '/v1/settings', 'Change the activity log retention')],