mirror of
https://github.com/IliesAllali/parenthese.git
synced 2026-11-10 07:52:13 +00:00
OIDC_ISSUER, OIDC_CLIENT_ID, OIDC_CLIENT_SECRET et OIDC_REDIRECT_URI ajoutent « Se connecter avec … » à l'écran de connexion. Tout le protocole passe par openid-client 6 : découverte, PKCE S256, state et nonce, échange du code, vérification de l'ID token (signature, émetteur, audience, dates). State, nonce et vérificateur PKCE vivent dans un cookie signé, httpOnly, SameSite=Lax, qui lie le retour au navigateur qui a commencé la connexion. Le compte est trouvé par (émetteur, sub), sinon relié par email vérifié, sinon créé sans mot de passe si ALLOW_REGISTRATION le permet (instance vide comprise). Retour vers l'app par un jeton de passage de 60 s, à usage unique, dans le fragment, échangé contre une session. PASSWORD_LOGIN=false retire les mots de passe Parenthèse. Un compte sans mot de passe se supprime en retapant son email, dans les 15 minutes qui suivent une connexion. Code OIDC masqué dans les journaux. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2177 lines
58 KiB
YAML
2177 lines
58 KiB
YAML
openapi: 3.0.3
|
|
info:
|
|
title: Arbre Genealogique API
|
|
version: 1.0.0
|
|
description: Contrats V1 prioritaires (auth, trees, partage, graph read).
|
|
servers:
|
|
- url: http://localhost:4000
|
|
|
|
tags:
|
|
- name: Auth
|
|
- name: Trees
|
|
- name: Graph
|
|
- name: Contributions
|
|
- name: Public
|
|
|
|
components:
|
|
securitySchemes:
|
|
BearerAuth:
|
|
type: http
|
|
scheme: bearer
|
|
bearerFormat: JWT
|
|
schemas:
|
|
ErrorResponse:
|
|
type: object
|
|
properties:
|
|
error:
|
|
type: string
|
|
required: [error]
|
|
Credentials:
|
|
type: object
|
|
properties:
|
|
email:
|
|
type: string
|
|
format: email
|
|
password:
|
|
type: string
|
|
minLength: 8
|
|
maxLength: 128
|
|
required: [email, password]
|
|
User:
|
|
type: object
|
|
properties:
|
|
id:
|
|
type: string
|
|
email:
|
|
type: string
|
|
format: email
|
|
required: [id, email]
|
|
AuthResponse:
|
|
type: object
|
|
properties:
|
|
token:
|
|
type: string
|
|
user:
|
|
$ref: '#/components/schemas/User'
|
|
required: [token, user]
|
|
Tree:
|
|
type: object
|
|
properties:
|
|
id:
|
|
type: string
|
|
slug:
|
|
type: string
|
|
name:
|
|
type: string
|
|
description:
|
|
type: string
|
|
nullable: true
|
|
rootPersonId:
|
|
type: string
|
|
nullable: true
|
|
required: [id, slug, name]
|
|
CreateTreeRequest:
|
|
type: object
|
|
properties:
|
|
name:
|
|
type: string
|
|
slug:
|
|
type: string
|
|
pattern: '^[a-z0-9-]{3,64}$'
|
|
description:
|
|
type: string
|
|
nullable: true
|
|
visitorPassword:
|
|
type: string
|
|
minLength: 8
|
|
contributorPassword:
|
|
type: string
|
|
minLength: 8
|
|
required: [name, slug, visitorPassword, contributorPassword]
|
|
PatchTreeRequest:
|
|
type: object
|
|
properties:
|
|
name:
|
|
type: string
|
|
slug:
|
|
type: string
|
|
pattern: '^[a-z0-9-]{3,64}$'
|
|
description:
|
|
type: string
|
|
nullable: true
|
|
rootPersonId:
|
|
type: string
|
|
nullable: true
|
|
UnlockRequest:
|
|
type: object
|
|
properties:
|
|
password:
|
|
type: string
|
|
required: [password]
|
|
UnlockResponse:
|
|
type: object
|
|
properties:
|
|
token:
|
|
type: string
|
|
treeId:
|
|
type: string
|
|
role:
|
|
type: string
|
|
enum: [visitor, contributor]
|
|
required: [token, treeId, role]
|
|
PatchPasswordsRequest:
|
|
type: object
|
|
properties:
|
|
visitorPassword:
|
|
type: string
|
|
minLength: 8
|
|
contributorPassword:
|
|
type: string
|
|
minLength: 8
|
|
PatchTreeSettingsRequest:
|
|
type: object
|
|
properties:
|
|
memberContributionPolicy:
|
|
type: string
|
|
enum: [direct, pending]
|
|
contributorPolicy:
|
|
type: string
|
|
enum: [direct, pending]
|
|
TreeSettings:
|
|
type: object
|
|
properties:
|
|
treeId:
|
|
type: string
|
|
memberContributionPolicy:
|
|
type: string
|
|
enum: [direct, pending]
|
|
visitorPolicy:
|
|
type: string
|
|
enum: [read_only]
|
|
contributorPolicy:
|
|
type: string
|
|
enum: [direct, pending]
|
|
updatedAt:
|
|
type: string
|
|
format: date-time
|
|
required: [treeId, memberContributionPolicy, visitorPolicy, contributorPolicy]
|
|
TreeStats:
|
|
type: object
|
|
properties:
|
|
persons:
|
|
type: integer
|
|
unions:
|
|
type: integer
|
|
filiations:
|
|
type: integer
|
|
medias:
|
|
type: integer
|
|
pendingContributions:
|
|
type: integer
|
|
totalContributions:
|
|
type: integer
|
|
required: [persons, unions, filiations, medias, pendingContributions, totalContributions]
|
|
AuditLogEntry:
|
|
type: object
|
|
properties:
|
|
id:
|
|
type: string
|
|
treeId:
|
|
type: string
|
|
actorType:
|
|
type: string
|
|
actorId:
|
|
type: string
|
|
nullable: true
|
|
action:
|
|
type: string
|
|
entityType:
|
|
type: string
|
|
entityId:
|
|
type: string
|
|
payloadJson:
|
|
nullable: true
|
|
createdAt:
|
|
type: string
|
|
format: date-time
|
|
required: [id, treeId, actorType, action, entityType, entityId, createdAt]
|
|
CreatePersonRequest:
|
|
type: object
|
|
properties:
|
|
firstName:
|
|
type: string
|
|
lastName:
|
|
type: string
|
|
birthDate:
|
|
type: string
|
|
nullable: true
|
|
deathDate:
|
|
type: string
|
|
nullable: true
|
|
sex:
|
|
type: string
|
|
nullable: true
|
|
notes:
|
|
type: string
|
|
nullable: true
|
|
required: [firstName, lastName]
|
|
PatchPersonRequest:
|
|
type: object
|
|
properties:
|
|
firstName:
|
|
type: string
|
|
lastName:
|
|
type: string
|
|
birthDate:
|
|
type: string
|
|
nullable: true
|
|
deathDate:
|
|
type: string
|
|
nullable: true
|
|
sex:
|
|
type: string
|
|
nullable: true
|
|
notes:
|
|
type: string
|
|
nullable: true
|
|
CreateUnionRequest:
|
|
type: object
|
|
properties:
|
|
partner1PersonId:
|
|
type: string
|
|
partner2PersonId:
|
|
type: string
|
|
nullable: true
|
|
unionType:
|
|
type: string
|
|
nullable: true
|
|
startDate:
|
|
type: string
|
|
nullable: true
|
|
endDate:
|
|
type: string
|
|
nullable: true
|
|
displayOrder:
|
|
type: integer
|
|
required: [partner1PersonId]
|
|
PatchUnionRequest:
|
|
type: object
|
|
properties:
|
|
partner1PersonId:
|
|
type: string
|
|
partner2PersonId:
|
|
type: string
|
|
nullable: true
|
|
unionType:
|
|
type: string
|
|
nullable: true
|
|
startDate:
|
|
type: string
|
|
nullable: true
|
|
endDate:
|
|
type: string
|
|
nullable: true
|
|
displayOrder:
|
|
type: integer
|
|
CreateParentChildLinkRequest:
|
|
type: object
|
|
properties:
|
|
parentPersonId:
|
|
type: string
|
|
childPersonId:
|
|
type: string
|
|
viaUnionId:
|
|
type: string
|
|
nullable: true
|
|
parentageType:
|
|
type: string
|
|
nullable: true
|
|
displayOrder:
|
|
type: integer
|
|
required: [parentPersonId, childPersonId]
|
|
ChildrenOrderRequest:
|
|
type: object
|
|
required: [order]
|
|
properties:
|
|
order:
|
|
type: array
|
|
minItems: 2
|
|
maxItems: 200
|
|
description: Children in the wanted order, each with the links that attach it to its parents
|
|
items:
|
|
type: object
|
|
required: [childId, linkIds]
|
|
properties:
|
|
childId:
|
|
type: string
|
|
linkIds:
|
|
type: array
|
|
minItems: 1
|
|
maxItems: 4
|
|
items:
|
|
type: string
|
|
PatchParentChildLinkRequest:
|
|
type: object
|
|
properties:
|
|
parentPersonId:
|
|
type: string
|
|
childPersonId:
|
|
type: string
|
|
viaUnionId:
|
|
type: string
|
|
nullable: true
|
|
parentageType:
|
|
type: string
|
|
nullable: true
|
|
displayOrder:
|
|
type: integer
|
|
UploadMediaRequest:
|
|
type: object
|
|
properties:
|
|
type:
|
|
type: string
|
|
enum: [photo, video, audio, document, citation]
|
|
fileName:
|
|
type: string
|
|
mimeType:
|
|
type: string
|
|
sizeBytes:
|
|
type: integer
|
|
dataBase64:
|
|
type: string
|
|
caption:
|
|
type: string
|
|
nullable: true
|
|
required: [type, fileName, mimeType, sizeBytes, dataBase64]
|
|
ReorderMediaRequest:
|
|
type: object
|
|
properties:
|
|
orderedMediaIds:
|
|
type: array
|
|
items:
|
|
type: string
|
|
required: [orderedMediaIds]
|
|
GraphPerson:
|
|
type: object
|
|
properties:
|
|
id:
|
|
type: string
|
|
firstName:
|
|
type: string
|
|
lastName:
|
|
type: string
|
|
birthDate:
|
|
type: string
|
|
nullable: true
|
|
deathDate:
|
|
type: string
|
|
nullable: true
|
|
sex:
|
|
type: string
|
|
nullable: true
|
|
notes:
|
|
type: string
|
|
nullable: true
|
|
required: [id, firstName, lastName]
|
|
GraphUnion:
|
|
type: object
|
|
properties:
|
|
id:
|
|
type: string
|
|
partner1PersonId:
|
|
type: string
|
|
partner2PersonId:
|
|
type: string
|
|
nullable: true
|
|
unionType:
|
|
type: string
|
|
nullable: true
|
|
startDate:
|
|
type: string
|
|
nullable: true
|
|
endDate:
|
|
type: string
|
|
nullable: true
|
|
displayOrder:
|
|
type: integer
|
|
required: [id, partner1PersonId, displayOrder]
|
|
GraphFiliation:
|
|
type: object
|
|
properties:
|
|
id:
|
|
type: string
|
|
parentPersonId:
|
|
type: string
|
|
childPersonId:
|
|
type: string
|
|
viaUnionId:
|
|
type: string
|
|
nullable: true
|
|
parentageType:
|
|
type: string
|
|
nullable: true
|
|
displayOrder:
|
|
type: integer
|
|
required: [id, parentPersonId, childPersonId, displayOrder]
|
|
GraphMedia:
|
|
type: object
|
|
properties:
|
|
id:
|
|
type: string
|
|
personId:
|
|
type: string
|
|
type:
|
|
type: string
|
|
mimeType:
|
|
type: string
|
|
urlPath:
|
|
type: string
|
|
caption:
|
|
type: string
|
|
nullable: true
|
|
displayOrder:
|
|
type: integer
|
|
isFeatured:
|
|
type: boolean
|
|
required: [id, personId, type, mimeType, urlPath, displayOrder, isFeatured]
|
|
ContributionChangeInput:
|
|
type: object
|
|
properties:
|
|
entityType:
|
|
type: string
|
|
enum: [person, union, parent_child_link]
|
|
action:
|
|
type: string
|
|
enum: [create, update, delete, reorder]
|
|
entityId:
|
|
type: string
|
|
nullable: true
|
|
before:
|
|
nullable: true
|
|
after:
|
|
nullable: true
|
|
conflictState:
|
|
type: string
|
|
enum: [none, needs_review]
|
|
required: [entityType, action]
|
|
ContributionSessionCreateRequest:
|
|
type: object
|
|
properties:
|
|
title:
|
|
type: string
|
|
submittedByLabel:
|
|
type: string
|
|
changes:
|
|
type: array
|
|
minItems: 1
|
|
items:
|
|
$ref: '#/components/schemas/ContributionChangeInput'
|
|
required: [title, changes]
|
|
ContributionSession:
|
|
type: object
|
|
properties:
|
|
id:
|
|
type: string
|
|
treeId:
|
|
type: string
|
|
submittedByUserId:
|
|
type: string
|
|
nullable: true
|
|
submittedByLabel:
|
|
type: string
|
|
mode:
|
|
type: string
|
|
enum: [user, tree_access]
|
|
status:
|
|
type: string
|
|
enum: [pending, approved, rejected]
|
|
title:
|
|
type: string
|
|
changes:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/ContributionSessionChange'
|
|
required: [id, treeId, submittedByLabel, mode, status, title]
|
|
ContributionSessionChange:
|
|
type: object
|
|
properties:
|
|
id:
|
|
type: string
|
|
entityType:
|
|
type: string
|
|
enum: [person, union, parent_child_link]
|
|
entityId:
|
|
type: string
|
|
nullable: true
|
|
action:
|
|
type: string
|
|
enum: [create, update, delete, reorder]
|
|
beforeJson:
|
|
nullable: true
|
|
afterJson:
|
|
nullable: true
|
|
conflictState:
|
|
type: string
|
|
enum: [none, needs_review]
|
|
decision:
|
|
type: string
|
|
enum: [approved, rejected]
|
|
nullable: true
|
|
required: [id, entityType, action, conflictState]
|
|
ContributionReviewRequest:
|
|
type: object
|
|
properties:
|
|
decision:
|
|
type: string
|
|
enum: [approved, rejected]
|
|
changeDecisions:
|
|
type: array
|
|
items:
|
|
type: object
|
|
properties:
|
|
changeId:
|
|
type: string
|
|
decision:
|
|
type: string
|
|
enum: [approved, rejected]
|
|
required: [changeId, decision]
|
|
required: [decision]
|
|
|
|
paths:
|
|
/auth/options:
|
|
get:
|
|
tags: [Auth]
|
|
summary: What the sign-in screen can offer on this instance
|
|
responses:
|
|
'200':
|
|
description: Instance options
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
registration:
|
|
type: boolean
|
|
description: false when ALLOW_REGISTRATION=false and the instance already has an account
|
|
passwordLogin:
|
|
type: boolean
|
|
description: false when PASSWORD_LOGIN=false (sign-in through the OIDC provider only)
|
|
oidc:
|
|
type: object
|
|
nullable: true
|
|
description: OIDC provider shown on the sign-in screen, null when OIDC_ISSUER is not set
|
|
properties:
|
|
name:
|
|
type: string
|
|
required: [registration, passwordLogin, oidc]
|
|
|
|
/auth/oidc/start:
|
|
get:
|
|
tags: [Auth]
|
|
summary: Start an OIDC sign-in (browser navigation)
|
|
description: Sets a signed, httpOnly cookie holding state, nonce and PKCE verifier, then redirects to the provider. Only exists when OIDC_ISSUER is set.
|
|
parameters:
|
|
- name: returnTo
|
|
in: query
|
|
required: false
|
|
schema:
|
|
type: string
|
|
description: Either / or /arbre/<slug>, anything else falls back to /
|
|
responses:
|
|
'302':
|
|
description: Redirect to the provider, or back to the app with "#oidc_error=oidc_unavailable"
|
|
|
|
/auth/oidc/callback:
|
|
get:
|
|
tags: [Auth]
|
|
summary: Provider redirect target (browser navigation)
|
|
description: Checks the cookie, then lets openid-client exchange the code and verify the ID token (signature, issuer, audience, expiry, nonce, state, PKCE). Finds the account by provider subject, links a verified email, or creates an account when registration allows it.
|
|
responses:
|
|
'302':
|
|
description: Back to the app with "#oidc=<one-time handoff>" or "#oidc_error=oidc_failed|oidc_email_unverified|oidc_email_missing|registration_closed"
|
|
|
|
/auth/oidc/exchange:
|
|
post:
|
|
tags: [Auth]
|
|
summary: Trade the one-time handoff for a session
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
code:
|
|
type: string
|
|
required: [code]
|
|
responses:
|
|
'200':
|
|
description: Session
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/AuthResponse'
|
|
'401':
|
|
description: Handoff invalid, expired or already used (invalid_handoff)
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
|
|
/auth/register:
|
|
post:
|
|
tags: [Auth]
|
|
summary: Register user account
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/Credentials'
|
|
responses:
|
|
'201':
|
|
description: Registered
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/AuthResponse'
|
|
'400':
|
|
description: Invalid payload
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'403':
|
|
description: Registration closed on this instance (registration_closed), or PASSWORD_LOGIN=false (password_login_disabled)
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'409':
|
|
description: Email already exists
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'429':
|
|
description: Too many requests
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
|
|
/auth/login:
|
|
post:
|
|
tags: [Auth]
|
|
summary: Login user
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/Credentials'
|
|
responses:
|
|
'200':
|
|
description: Logged in
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/AuthResponse'
|
|
'400':
|
|
description: Invalid payload
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'403':
|
|
description: PASSWORD_LOGIN=false (password_login_disabled)
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'401':
|
|
description: Invalid credentials
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'429':
|
|
description: Too many requests
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
|
|
/auth/logout:
|
|
post:
|
|
tags: [Auth]
|
|
summary: Logout current session
|
|
security:
|
|
- BearerAuth: []
|
|
responses:
|
|
'200':
|
|
description: Logged out
|
|
'401':
|
|
description: Authentication required
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
|
|
/auth/me:
|
|
delete:
|
|
tags: [Auth]
|
|
summary: Permanently delete the current account
|
|
description: >
|
|
Requires the current password. Permanently deletes every tree owned by the user
|
|
(including trees already marked as deleted) with their media files, then the account.
|
|
Trees where the user is only a member or has shared access are kept.
|
|
The token used for the request is revoked.
|
|
security:
|
|
- BearerAuth: []
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [password]
|
|
properties:
|
|
password:
|
|
type: string
|
|
minLength: 1
|
|
maxLength: 128
|
|
responses:
|
|
'200':
|
|
description: Account deleted
|
|
'400':
|
|
description: Invalid payload
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'401':
|
|
description: Authentication required
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'403':
|
|
description: Invalid password
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'429':
|
|
description: Too many requests
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
|
|
/trees:
|
|
get:
|
|
tags: [Trees]
|
|
summary: List trees for current user
|
|
security:
|
|
- BearerAuth: []
|
|
responses:
|
|
'200':
|
|
description: Trees list
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
trees:
|
|
type: array
|
|
items:
|
|
allOf:
|
|
- $ref: '#/components/schemas/Tree'
|
|
- type: object
|
|
properties:
|
|
role:
|
|
type: string
|
|
enum: [owner, admin, member]
|
|
'401':
|
|
description: Authentication required
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
post:
|
|
tags: [Trees]
|
|
summary: Create tree
|
|
security:
|
|
- BearerAuth: []
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/CreateTreeRequest'
|
|
responses:
|
|
'201':
|
|
description: Tree created
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
tree:
|
|
$ref: '#/components/schemas/Tree'
|
|
'400':
|
|
description: Invalid payload
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'401':
|
|
description: Authentication required
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'409':
|
|
description: Slug already exists
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
|
|
/trees/{id}:
|
|
get:
|
|
tags: [Trees]
|
|
summary: Get tree metadata
|
|
security:
|
|
- BearerAuth: []
|
|
parameters:
|
|
- in: path
|
|
name: id
|
|
required: true
|
|
schema:
|
|
type: string
|
|
responses:
|
|
'200':
|
|
description: Tree metadata
|
|
'400':
|
|
description: Invalid tree id
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'401':
|
|
description: Authentication required
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'403':
|
|
description: Forbidden
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'404':
|
|
description: Tree not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
patch:
|
|
tags: [Trees]
|
|
summary: Update tree metadata
|
|
security:
|
|
- BearerAuth: []
|
|
parameters:
|
|
- in: path
|
|
name: id
|
|
required: true
|
|
schema:
|
|
type: string
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/PatchTreeRequest'
|
|
responses:
|
|
'200':
|
|
description: Tree updated
|
|
'400':
|
|
description: Invalid payload or tree id
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'401':
|
|
description: Authentication required
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'403':
|
|
description: Forbidden
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'404':
|
|
description: Tree not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'409':
|
|
description: Slug already exists
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
delete:
|
|
tags: [Trees]
|
|
summary: Permanently delete a tree
|
|
description: >
|
|
Owner only. Deletes the tree and all its data (persons, links, media, annotations,
|
|
contributions, audit log) and removes its media directory.
|
|
security:
|
|
- BearerAuth: []
|
|
parameters:
|
|
- in: path
|
|
name: id
|
|
required: true
|
|
schema:
|
|
type: string
|
|
responses:
|
|
'200':
|
|
description: Tree deleted
|
|
'400':
|
|
description: Invalid tree id
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'401':
|
|
description: Authentication required
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'403':
|
|
description: Forbidden
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'404':
|
|
description: Tree not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
|
|
/trees/{id}/access/unlock:
|
|
post:
|
|
tags: [Trees]
|
|
summary: Unlock tree access with visitor/contributor password
|
|
parameters:
|
|
- in: path
|
|
name: id
|
|
required: true
|
|
schema:
|
|
type: string
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/UnlockRequest'
|
|
responses:
|
|
'200':
|
|
description: Access granted
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/UnlockResponse'
|
|
'400':
|
|
description: Invalid payload or tree id
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'401':
|
|
description: Invalid password
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'404':
|
|
description: Tree not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'429':
|
|
description: Too many requests
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
|
|
/trees/{id}/access/passwords:
|
|
patch:
|
|
tags: [Trees]
|
|
summary: Rotate visitor/contributor passwords
|
|
security:
|
|
- BearerAuth: []
|
|
parameters:
|
|
- in: path
|
|
name: id
|
|
required: true
|
|
schema:
|
|
type: string
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/PatchPasswordsRequest'
|
|
responses:
|
|
'200':
|
|
description: Passwords updated
|
|
'400':
|
|
description: Invalid payload or tree id
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'401':
|
|
description: Authentication required
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'403':
|
|
description: Forbidden
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'404':
|
|
description: Tree not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
|
|
/trees/{id}/stats:
|
|
get:
|
|
tags: [Trees]
|
|
summary: Get admin statistics for a tree
|
|
security:
|
|
- BearerAuth: []
|
|
parameters:
|
|
- in: path
|
|
name: id
|
|
required: true
|
|
schema:
|
|
type: string
|
|
responses:
|
|
'200':
|
|
description: Tree statistics
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
stats:
|
|
$ref: '#/components/schemas/TreeStats'
|
|
required: [stats]
|
|
'400':
|
|
description: Invalid tree id
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'401':
|
|
description: Authentication required
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'403':
|
|
description: Forbidden
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
|
|
/trees/{id}/settings:
|
|
get:
|
|
tags: [Trees]
|
|
summary: Get tree contribution settings (admin only)
|
|
security:
|
|
- BearerAuth: []
|
|
parameters:
|
|
- in: path
|
|
name: id
|
|
required: true
|
|
schema:
|
|
type: string
|
|
responses:
|
|
'200':
|
|
description: Tree settings
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
settings:
|
|
$ref: '#/components/schemas/TreeSettings'
|
|
required: [settings]
|
|
'400':
|
|
description: Invalid tree id
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'401':
|
|
description: Authentication required
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'403':
|
|
description: Forbidden
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
patch:
|
|
tags: [Trees]
|
|
summary: Update tree contribution settings (admin only)
|
|
security:
|
|
- BearerAuth: []
|
|
parameters:
|
|
- in: path
|
|
name: id
|
|
required: true
|
|
schema:
|
|
type: string
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/PatchTreeSettingsRequest'
|
|
responses:
|
|
'200':
|
|
description: Tree settings updated
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
settings:
|
|
$ref: '#/components/schemas/TreeSettings'
|
|
required: [settings]
|
|
'400':
|
|
description: Invalid payload or tree id
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'401':
|
|
description: Authentication required
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'403':
|
|
description: Forbidden
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
|
|
/trees/{id}/audit:
|
|
get:
|
|
tags: [Trees]
|
|
summary: List admin audit logs for a tree
|
|
security:
|
|
- BearerAuth: []
|
|
parameters:
|
|
- in: path
|
|
name: id
|
|
required: true
|
|
schema:
|
|
type: string
|
|
- in: query
|
|
name: limit
|
|
required: false
|
|
schema:
|
|
type: integer
|
|
minimum: 1
|
|
maximum: 100
|
|
- in: query
|
|
name: offset
|
|
required: false
|
|
schema:
|
|
type: integer
|
|
minimum: 0
|
|
- in: query
|
|
name: action
|
|
required: false
|
|
schema:
|
|
type: string
|
|
responses:
|
|
'200':
|
|
description: Audit log entries
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
logs:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/AuditLogEntry'
|
|
nextOffset:
|
|
type: integer
|
|
nullable: true
|
|
required: [logs, nextOffset]
|
|
'400':
|
|
description: Invalid query or tree id
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'401':
|
|
description: Authentication required
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'403':
|
|
description: Forbidden
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
|
|
/trees/{id}/graph:
|
|
get:
|
|
tags: [Graph]
|
|
summary: Read tree graph payload
|
|
security:
|
|
- BearerAuth: []
|
|
parameters:
|
|
- in: path
|
|
name: id
|
|
required: true
|
|
schema:
|
|
type: string
|
|
responses:
|
|
'200':
|
|
description: Graph payload
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
treeId:
|
|
type: string
|
|
rootPersonId:
|
|
type: string
|
|
nullable: true
|
|
graph:
|
|
type: object
|
|
properties:
|
|
persons:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/GraphPerson'
|
|
unions:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/GraphUnion'
|
|
filiations:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/GraphFiliation'
|
|
medias:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/GraphMedia'
|
|
'400':
|
|
description: Invalid tree id
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'401':
|
|
description: Authentication required
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'403':
|
|
description: Forbidden
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'404':
|
|
description: Tree not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
|
|
/trees/{id}/persons:
|
|
post:
|
|
tags: [Trees]
|
|
summary: Create a person in the tree
|
|
security:
|
|
- BearerAuth: []
|
|
parameters:
|
|
- in: path
|
|
name: id
|
|
required: true
|
|
schema:
|
|
type: string
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/CreatePersonRequest'
|
|
responses:
|
|
'201':
|
|
description: Person created
|
|
'400':
|
|
description: Invalid payload
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'401':
|
|
description: Authentication required
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'403':
|
|
description: Forbidden
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'404':
|
|
description: Tree not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
|
|
/trees/{id}/persons/{personId}:
|
|
patch:
|
|
tags: [Trees]
|
|
summary: Update a person in the tree
|
|
security:
|
|
- BearerAuth: []
|
|
parameters:
|
|
- in: path
|
|
name: id
|
|
required: true
|
|
schema:
|
|
type: string
|
|
- in: path
|
|
name: personId
|
|
required: true
|
|
schema:
|
|
type: string
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/PatchPersonRequest'
|
|
responses:
|
|
'200':
|
|
description: Person updated
|
|
'400':
|
|
description: Invalid payload or params
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'401':
|
|
description: Authentication required
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'403':
|
|
description: Forbidden
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'404':
|
|
description: Person not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
delete:
|
|
tags: [Trees]
|
|
summary: Soft-delete a person in the tree
|
|
security:
|
|
- BearerAuth: []
|
|
parameters:
|
|
- in: path
|
|
name: id
|
|
required: true
|
|
schema:
|
|
type: string
|
|
- in: path
|
|
name: personId
|
|
required: true
|
|
schema:
|
|
type: string
|
|
responses:
|
|
'200':
|
|
description: Person soft-deleted
|
|
'400':
|
|
description: Invalid params
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'401':
|
|
description: Authentication required
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'403':
|
|
description: Forbidden
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'404':
|
|
description: Person not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
|
|
/trees/{id}/unions:
|
|
post:
|
|
tags: [Trees]
|
|
summary: Create a union in the tree
|
|
security:
|
|
- BearerAuth: []
|
|
parameters:
|
|
- in: path
|
|
name: id
|
|
required: true
|
|
schema:
|
|
type: string
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/CreateUnionRequest'
|
|
responses:
|
|
'201':
|
|
description: Union created
|
|
'400':
|
|
description: Invalid payload or reference
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'401':
|
|
description: Authentication required
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'403':
|
|
description: Forbidden
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'404':
|
|
description: Tree not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
|
|
/trees/{id}/unions/{unionId}:
|
|
patch:
|
|
tags: [Trees]
|
|
summary: Update a union in the tree
|
|
security:
|
|
- BearerAuth: []
|
|
parameters:
|
|
- in: path
|
|
name: id
|
|
required: true
|
|
schema:
|
|
type: string
|
|
- in: path
|
|
name: unionId
|
|
required: true
|
|
schema:
|
|
type: string
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/PatchUnionRequest'
|
|
responses:
|
|
'200':
|
|
description: Union updated
|
|
'400':
|
|
description: Invalid payload, params or person reference
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'401':
|
|
description: Authentication required
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'403':
|
|
description: Forbidden
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'404':
|
|
description: Union not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
delete:
|
|
tags: [Trees]
|
|
summary: Soft-delete a union in the tree
|
|
security:
|
|
- BearerAuth: []
|
|
parameters:
|
|
- in: path
|
|
name: id
|
|
required: true
|
|
schema:
|
|
type: string
|
|
- in: path
|
|
name: unionId
|
|
required: true
|
|
schema:
|
|
type: string
|
|
responses:
|
|
'200':
|
|
description: Union soft-deleted
|
|
'400':
|
|
description: Invalid params
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'401':
|
|
description: Authentication required
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'403':
|
|
description: Forbidden
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'404':
|
|
description: Union not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
|
|
/trees/{id}/links:
|
|
post:
|
|
tags: [Trees]
|
|
summary: Create a parent-child link in the tree
|
|
security:
|
|
- BearerAuth: []
|
|
parameters:
|
|
- in: path
|
|
name: id
|
|
required: true
|
|
schema:
|
|
type: string
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/CreateParentChildLinkRequest'
|
|
responses:
|
|
'201':
|
|
description: Link created
|
|
'400':
|
|
description: Invalid payload or reference
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'401':
|
|
description: Authentication required
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'403':
|
|
description: Forbidden
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'404':
|
|
description: Tree not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
|
|
/trees/{id}/links/order:
|
|
put:
|
|
tags: [Trees]
|
|
summary: Reorder siblings (rewrites displayOrder of their parent-child links)
|
|
security:
|
|
- BearerAuth: []
|
|
parameters:
|
|
- in: path
|
|
name: id
|
|
required: true
|
|
schema:
|
|
type: string
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ChildrenOrderRequest'
|
|
responses:
|
|
'200':
|
|
description: Order saved
|
|
'400':
|
|
description: Invalid payload, links that do not belong to their child, or children without a shared parent
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'401':
|
|
description: Authentication required
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'403':
|
|
description: Forbidden
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'404':
|
|
description: Link not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
/trees/{id}/links/{linkId}:
|
|
patch:
|
|
tags: [Trees]
|
|
summary: Update a parent-child link in the tree
|
|
security:
|
|
- BearerAuth: []
|
|
parameters:
|
|
- in: path
|
|
name: id
|
|
required: true
|
|
schema:
|
|
type: string
|
|
- in: path
|
|
name: linkId
|
|
required: true
|
|
schema:
|
|
type: string
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/PatchParentChildLinkRequest'
|
|
responses:
|
|
'200':
|
|
description: Link updated
|
|
'400':
|
|
description: Invalid payload, params or references
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'401':
|
|
description: Authentication required
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'403':
|
|
description: Forbidden
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'404':
|
|
description: Link not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
delete:
|
|
tags: [Trees]
|
|
summary: Soft-delete a parent-child link in the tree
|
|
security:
|
|
- BearerAuth: []
|
|
parameters:
|
|
- in: path
|
|
name: id
|
|
required: true
|
|
schema:
|
|
type: string
|
|
- in: path
|
|
name: linkId
|
|
required: true
|
|
schema:
|
|
type: string
|
|
responses:
|
|
'200':
|
|
description: Link soft-deleted
|
|
'400':
|
|
description: Invalid params
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'401':
|
|
description: Authentication required
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'403':
|
|
description: Forbidden
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'404':
|
|
description: Link not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
|
|
/trees/{id}/persons/{personId}/media:
|
|
post:
|
|
tags: [Trees]
|
|
summary: Upload media for a person
|
|
security:
|
|
- BearerAuth: []
|
|
parameters:
|
|
- in: path
|
|
name: id
|
|
required: true
|
|
schema:
|
|
type: string
|
|
- in: path
|
|
name: personId
|
|
required: true
|
|
schema:
|
|
type: string
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/UploadMediaRequest'
|
|
responses:
|
|
'201':
|
|
description: Media uploaded
|
|
'400':
|
|
description: Invalid payload
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'401':
|
|
description: Authentication required
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'403':
|
|
description: Forbidden
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'404':
|
|
description: Tree or person not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
|
|
/trees/{id}/persons/{personId}/media/reorder:
|
|
patch:
|
|
tags: [Trees]
|
|
summary: Reorder person media
|
|
security:
|
|
- BearerAuth: []
|
|
parameters:
|
|
- in: path
|
|
name: id
|
|
required: true
|
|
schema:
|
|
type: string
|
|
- in: path
|
|
name: personId
|
|
required: true
|
|
schema:
|
|
type: string
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ReorderMediaRequest'
|
|
responses:
|
|
'200':
|
|
description: Media reordered
|
|
'400':
|
|
description: Invalid payload
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'401':
|
|
description: Authentication required
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'403':
|
|
description: Forbidden
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
|
|
/trees/{id}/persons/{personId}/media/{mediaId}:
|
|
delete:
|
|
tags: [Trees]
|
|
summary: Soft-delete person media
|
|
security:
|
|
- BearerAuth: []
|
|
parameters:
|
|
- in: path
|
|
name: id
|
|
required: true
|
|
schema:
|
|
type: string
|
|
- in: path
|
|
name: personId
|
|
required: true
|
|
schema:
|
|
type: string
|
|
- in: path
|
|
name: mediaId
|
|
required: true
|
|
schema:
|
|
type: string
|
|
responses:
|
|
'200':
|
|
description: Media deleted
|
|
'401':
|
|
description: Authentication required
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'403':
|
|
description: Forbidden
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'404':
|
|
description: Media not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
|
|
/trees/{id}/media/{mediaId}:
|
|
get:
|
|
tags: [Graph]
|
|
summary: Download media file by tree and media id
|
|
parameters:
|
|
- in: path
|
|
name: id
|
|
required: true
|
|
schema:
|
|
type: string
|
|
- in: path
|
|
name: mediaId
|
|
required: true
|
|
schema:
|
|
type: string
|
|
- in: query
|
|
name: token
|
|
required: false
|
|
schema:
|
|
type: string
|
|
responses:
|
|
'200':
|
|
description: Media binary content
|
|
'401':
|
|
description: Authentication required
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'404':
|
|
description: Media not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
|
|
/trees/{id}/contributions/sessions:
|
|
post:
|
|
tags: [Contributions]
|
|
summary: Submit a contribution session
|
|
security:
|
|
- BearerAuth: []
|
|
parameters:
|
|
- in: path
|
|
name: id
|
|
required: true
|
|
schema:
|
|
type: string
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ContributionSessionCreateRequest'
|
|
responses:
|
|
'201':
|
|
description: Contribution session submitted
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
session:
|
|
$ref: '#/components/schemas/ContributionSession'
|
|
changesCount:
|
|
type: integer
|
|
required: [session, changesCount]
|
|
'400':
|
|
description: Invalid payload or tree id
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'401':
|
|
description: Authentication required
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'403':
|
|
description: Forbidden
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'404':
|
|
description: Tree not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
get:
|
|
tags: [Contributions]
|
|
summary: List contribution sessions (admin only)
|
|
security:
|
|
- BearerAuth: []
|
|
parameters:
|
|
- in: path
|
|
name: id
|
|
required: true
|
|
schema:
|
|
type: string
|
|
- in: query
|
|
name: status
|
|
required: false
|
|
schema:
|
|
type: string
|
|
enum: [pending, approved, rejected]
|
|
responses:
|
|
'200':
|
|
description: Sessions list
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
sessions:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/ContributionSession'
|
|
required: [sessions]
|
|
'400':
|
|
description: Invalid query or tree id
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'401':
|
|
description: Authentication required
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'403':
|
|
description: Forbidden
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
|
|
/trees/{id}/contributions/sessions/{sessionId}/review:
|
|
post:
|
|
tags: [Contributions]
|
|
summary: Review contribution session (admin only)
|
|
security:
|
|
- BearerAuth: []
|
|
parameters:
|
|
- in: path
|
|
name: id
|
|
required: true
|
|
schema:
|
|
type: string
|
|
- in: path
|
|
name: sessionId
|
|
required: true
|
|
schema:
|
|
type: string
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ContributionReviewRequest'
|
|
responses:
|
|
'200':
|
|
description: Session reviewed
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
session:
|
|
$ref: '#/components/schemas/ContributionSession'
|
|
approvedCount:
|
|
type: integer
|
|
rejectedCount:
|
|
type: integer
|
|
required: [session, approvedCount, rejectedCount]
|
|
'400':
|
|
description: Invalid payload or params
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'401':
|
|
description: Authentication required
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'403':
|
|
description: Forbidden
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'404':
|
|
description: Contribution session or tree not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'409':
|
|
description: Contribution session already reviewed
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
|
|
/arbre/{slug}:
|
|
get:
|
|
tags: [Public]
|
|
summary: Resolve slug to tree metadata
|
|
parameters:
|
|
- in: path
|
|
name: slug
|
|
required: true
|
|
schema:
|
|
type: string
|
|
responses:
|
|
'200':
|
|
description: Tree found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
tree:
|
|
$ref: '#/components/schemas/Tree'
|
|
'400':
|
|
description: Invalid slug
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'404':
|
|
description: Tree not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|