Regroupe l'état de travail en cours réalisé dans un même worktree sur plusieurs tickets/sprints (#85, #136, #145, #155-160, #162-164), mélangeant des tickets QA et inProgress. Ne constitue pas une feature terminée : commit de sauvegarde avant triage/split par ticket en branches feature/* dédiées. Exclut les dossiers d'environnement de build locaux et le heap dump parasite (.gitignore mis à jour). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
633 lines
16 KiB
YAML
633 lines
16 KiB
YAML
openapi: 3.0.3
|
|
info:
|
|
title: GameTime Server API
|
|
version: 0.1.0
|
|
description: Headless API for GameTime authentication, incremental sync and targeted sharing.
|
|
servers:
|
|
- url: http://localhost:8080
|
|
description: Local Docker or development server
|
|
tags:
|
|
- name: Health
|
|
- name: Auth
|
|
- name: Sync
|
|
- name: Shares
|
|
paths:
|
|
/health:
|
|
get:
|
|
tags: [Health]
|
|
summary: Healthcheck
|
|
responses:
|
|
'200':
|
|
description: Server is reachable.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/HealthResponse'
|
|
|
|
/auth/register:
|
|
post:
|
|
tags: [Auth]
|
|
summary: Register a user account
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/RegisterRequest'
|
|
responses:
|
|
'201':
|
|
description: User created.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/RegisterResponse'
|
|
'400':
|
|
$ref: '#/components/responses/BadRequest'
|
|
'409':
|
|
description: Email already taken.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
|
|
/auth/login:
|
|
post:
|
|
tags: [Auth]
|
|
summary: Login and create an API token session
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/LoginRequest'
|
|
responses:
|
|
'200':
|
|
description: Login accepted. The clear token is returned once.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/LoginResponse'
|
|
'400':
|
|
$ref: '#/components/responses/BadRequest'
|
|
'401':
|
|
$ref: '#/components/responses/Unauthorized'
|
|
|
|
/auth/logout:
|
|
post:
|
|
tags: [Auth]
|
|
summary: Revoke the current API token session
|
|
security:
|
|
- bearerAuth: []
|
|
responses:
|
|
'204':
|
|
description: Session revoked.
|
|
'401':
|
|
$ref: '#/components/responses/Unauthorized'
|
|
|
|
/sync/push:
|
|
post:
|
|
tags: [Sync]
|
|
summary: Push client-side resource mutations
|
|
security:
|
|
- bearerAuth: []
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/SyncPushRequest'
|
|
responses:
|
|
'200':
|
|
description: Batch processed. Item-level validation errors are returned in results.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/SyncPushResponse'
|
|
'400':
|
|
$ref: '#/components/responses/BadRequest'
|
|
'401':
|
|
$ref: '#/components/responses/Unauthorized'
|
|
|
|
/sync/pull:
|
|
get:
|
|
tags: [Sync]
|
|
summary: Pull resources updated after a server cursor
|
|
security:
|
|
- bearerAuth: []
|
|
parameters:
|
|
- name: since
|
|
in: query
|
|
required: false
|
|
schema:
|
|
type: string
|
|
format: date-time
|
|
description: ISO8601 UTC server cursor. If omitted, returns all resources.
|
|
responses:
|
|
'200':
|
|
description: Resources for the authenticated user.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/SyncPullResponse'
|
|
'400':
|
|
$ref: '#/components/responses/BadRequest'
|
|
'401':
|
|
$ref: '#/components/responses/Unauthorized'
|
|
|
|
/sync/exchange:
|
|
post:
|
|
tags: [Sync]
|
|
summary: Push then pull in one request
|
|
security:
|
|
- bearerAuth: []
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/SyncExchangeRequest'
|
|
responses:
|
|
'200':
|
|
description: Pull response after applying the push batch.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/SyncExchangeResponse'
|
|
'400':
|
|
$ref: '#/components/responses/BadRequest'
|
|
'401':
|
|
$ref: '#/components/responses/Unauthorized'
|
|
|
|
/shares:
|
|
post:
|
|
tags: [Shares]
|
|
summary: Create a targeted share
|
|
security:
|
|
- bearerAuth: []
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/CreateShareRequest'
|
|
responses:
|
|
'201':
|
|
description: Share created. Unknown recipient emails are reported but do not fail the request.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/CreateShareResponse'
|
|
'400':
|
|
$ref: '#/components/responses/BadRequest'
|
|
'401':
|
|
$ref: '#/components/responses/Unauthorized'
|
|
|
|
/shares/inbox:
|
|
get:
|
|
tags: [Shares]
|
|
summary: List shares received by the authenticated user
|
|
security:
|
|
- bearerAuth: []
|
|
responses:
|
|
'200':
|
|
description: Inbox sorted from newest to oldest.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ShareInboxResponse'
|
|
'401':
|
|
$ref: '#/components/responses/Unauthorized'
|
|
|
|
/shares/{id}/accept:
|
|
post:
|
|
tags: [Shares]
|
|
summary: Accept a received share
|
|
security:
|
|
- bearerAuth: []
|
|
parameters:
|
|
- $ref: '#/components/parameters/ShareId'
|
|
responses:
|
|
'200':
|
|
description: Share accepted and copied into synced resources.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/AcceptShareResponse'
|
|
'401':
|
|
$ref: '#/components/responses/Unauthorized'
|
|
'404':
|
|
$ref: '#/components/responses/NotFound'
|
|
'409':
|
|
$ref: '#/components/responses/Conflict'
|
|
|
|
/shares/{id}/decline:
|
|
post:
|
|
tags: [Shares]
|
|
summary: Decline a received share
|
|
security:
|
|
- bearerAuth: []
|
|
parameters:
|
|
- $ref: '#/components/parameters/ShareId'
|
|
responses:
|
|
'204':
|
|
description: Share declined.
|
|
'401':
|
|
$ref: '#/components/responses/Unauthorized'
|
|
'404':
|
|
$ref: '#/components/responses/NotFound'
|
|
'409':
|
|
$ref: '#/components/responses/Conflict'
|
|
|
|
/shares/{id}/revoke:
|
|
post:
|
|
tags: [Shares]
|
|
summary: Revoke a share sent by the authenticated user
|
|
security:
|
|
- bearerAuth: []
|
|
parameters:
|
|
- $ref: '#/components/parameters/ShareId'
|
|
responses:
|
|
'204':
|
|
description: Share revoked.
|
|
'401':
|
|
$ref: '#/components/responses/Unauthorized'
|
|
'404':
|
|
$ref: '#/components/responses/NotFound'
|
|
|
|
components:
|
|
securitySchemes:
|
|
bearerAuth:
|
|
type: http
|
|
scheme: bearer
|
|
bearerFormat: opaque
|
|
|
|
parameters:
|
|
ShareId:
|
|
name: id
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
description: Share identifier.
|
|
|
|
responses:
|
|
BadRequest:
|
|
description: Invalid JSON body, invalid parameter, or validation failure.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
Unauthorized:
|
|
description: Missing, malformed, expired, revoked or unknown bearer token.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
NotFound:
|
|
description: Resource not found or not accessible by the authenticated user.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
Conflict:
|
|
description: Current resource state rejects the requested transition.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
|
|
schemas:
|
|
ErrorResponse:
|
|
type: object
|
|
required: [error]
|
|
properties:
|
|
error:
|
|
type: string
|
|
|
|
HealthResponse:
|
|
type: object
|
|
required: [status]
|
|
properties:
|
|
status:
|
|
type: string
|
|
enum: [ok]
|
|
|
|
RegisterRequest:
|
|
type: object
|
|
required: [email, password]
|
|
properties:
|
|
email:
|
|
type: string
|
|
format: email
|
|
password:
|
|
type: string
|
|
minLength: 8
|
|
displayName:
|
|
type: string
|
|
nullable: true
|
|
|
|
RegisterResponse:
|
|
type: object
|
|
required: [userId, email]
|
|
properties:
|
|
userId:
|
|
type: string
|
|
email:
|
|
type: string
|
|
format: email
|
|
|
|
LoginRequest:
|
|
type: object
|
|
required: [email, password]
|
|
properties:
|
|
email:
|
|
type: string
|
|
format: email
|
|
password:
|
|
type: string
|
|
deviceLabel:
|
|
type: string
|
|
nullable: true
|
|
|
|
LoginResponse:
|
|
type: object
|
|
required: [token, expiresAt]
|
|
properties:
|
|
token:
|
|
type: string
|
|
expiresAt:
|
|
type: string
|
|
format: date-time
|
|
|
|
ResourceType:
|
|
type: string
|
|
enum: [exercise, program, workoutTemplate, workoutHistory, mediaAsset]
|
|
|
|
ShareResourceType:
|
|
type: string
|
|
enum: [program, workoutTemplate]
|
|
|
|
ShareKind:
|
|
type: string
|
|
enum: [single, pack]
|
|
|
|
ShareRecipientStatus:
|
|
type: string
|
|
enum: [pending, accepted, declined, revoked]
|
|
|
|
JsonObject:
|
|
type: object
|
|
additionalProperties: true
|
|
|
|
SyncPushRequest:
|
|
type: object
|
|
required: [items]
|
|
properties:
|
|
deviceId:
|
|
type: string
|
|
nullable: true
|
|
description: Required by the application use case for each valid item.
|
|
items:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/SyncPushItem'
|
|
|
|
SyncPushItem:
|
|
type: object
|
|
required: [resourceType, clientId, schemaVersion, clientUpdatedAt, payload]
|
|
properties:
|
|
resourceType:
|
|
$ref: '#/components/schemas/ResourceType'
|
|
clientId:
|
|
type: string
|
|
schemaVersion:
|
|
type: integer
|
|
minimum: 1
|
|
clientUpdatedAt:
|
|
type: string
|
|
format: date-time
|
|
deletedAt:
|
|
type: string
|
|
format: date-time
|
|
nullable: true
|
|
payload:
|
|
$ref: '#/components/schemas/JsonObject'
|
|
|
|
SyncPushResponse:
|
|
type: object
|
|
required: [serverCursor, results]
|
|
properties:
|
|
serverCursor:
|
|
type: string
|
|
format: date-time
|
|
results:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/SyncPushResultItem'
|
|
|
|
SyncPushResultItem:
|
|
type: object
|
|
required: [status]
|
|
properties:
|
|
resourceType:
|
|
type: string
|
|
nullable: true
|
|
clientId:
|
|
type: string
|
|
nullable: true
|
|
serverId:
|
|
type: string
|
|
nullable: true
|
|
status:
|
|
type: string
|
|
enum: [accepted, ignoredOlder, error]
|
|
serverUpdatedAt:
|
|
type: string
|
|
format: date-time
|
|
nullable: true
|
|
message:
|
|
type: string
|
|
|
|
SyncPullResponse:
|
|
type: object
|
|
required: [serverCursor, items]
|
|
properties:
|
|
serverCursor:
|
|
type: string
|
|
format: date-time
|
|
items:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/SyncedResourceItem'
|
|
|
|
SyncExchangeRequest:
|
|
allOf:
|
|
- $ref: '#/components/schemas/SyncPushRequest'
|
|
- type: object
|
|
properties:
|
|
since:
|
|
type: string
|
|
format: date-time
|
|
nullable: true
|
|
|
|
SyncExchangeResponse:
|
|
type: object
|
|
required: [serverCursor, pushResults, items]
|
|
properties:
|
|
serverCursor:
|
|
type: string
|
|
format: date-time
|
|
pushResults:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/SyncPushResultItem'
|
|
items:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/SyncedResourceItem'
|
|
|
|
SyncedResourceItem:
|
|
type: object
|
|
required:
|
|
- resourceType
|
|
- clientId
|
|
- serverId
|
|
- schemaVersion
|
|
- clientUpdatedAt
|
|
- serverUpdatedAt
|
|
- payload
|
|
properties:
|
|
resourceType:
|
|
$ref: '#/components/schemas/ResourceType'
|
|
clientId:
|
|
type: string
|
|
serverId:
|
|
type: string
|
|
schemaVersion:
|
|
type: integer
|
|
minimum: 1
|
|
clientUpdatedAt:
|
|
type: string
|
|
format: date-time
|
|
serverUpdatedAt:
|
|
type: string
|
|
format: date-time
|
|
deletedAt:
|
|
type: string
|
|
format: date-time
|
|
nullable: true
|
|
payload:
|
|
$ref: '#/components/schemas/JsonObject'
|
|
|
|
CreateShareRequest:
|
|
type: object
|
|
required: [recipientEmails]
|
|
properties:
|
|
shareKind:
|
|
description: Defaults to single when omitted.
|
|
allOf:
|
|
- $ref: '#/components/schemas/ShareKind'
|
|
packName:
|
|
type: string
|
|
nullable: true
|
|
resourceType:
|
|
nullable: true
|
|
allOf:
|
|
- $ref: '#/components/schemas/ShareResourceType'
|
|
payload:
|
|
$ref: '#/components/schemas/JsonObject'
|
|
nullable: true
|
|
items:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/SharePackItem'
|
|
recipientEmails:
|
|
type: array
|
|
minItems: 1
|
|
items:
|
|
type: string
|
|
format: email
|
|
|
|
SharePackItem:
|
|
type: object
|
|
required: [resourceType, payload]
|
|
properties:
|
|
resourceType:
|
|
$ref: '#/components/schemas/ShareResourceType'
|
|
payload:
|
|
$ref: '#/components/schemas/JsonObject'
|
|
|
|
CreateShareResponse:
|
|
type: object
|
|
required: [shareId, shareKind, recipientUserIds, unresolvedEmails]
|
|
properties:
|
|
shareId:
|
|
type: string
|
|
shareKind:
|
|
$ref: '#/components/schemas/ShareKind'
|
|
recipientUserIds:
|
|
type: array
|
|
items:
|
|
type: string
|
|
unresolvedEmails:
|
|
type: array
|
|
items:
|
|
type: string
|
|
format: email
|
|
|
|
ShareInboxResponse:
|
|
type: object
|
|
required: [items]
|
|
properties:
|
|
items:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/ShareInboxItem'
|
|
|
|
ShareInboxItem:
|
|
type: object
|
|
required:
|
|
- shareId
|
|
- senderUserId
|
|
- shareKind
|
|
- payload
|
|
- status
|
|
- createdAt
|
|
properties:
|
|
shareId:
|
|
type: string
|
|
senderUserId:
|
|
type: string
|
|
shareKind:
|
|
$ref: '#/components/schemas/ShareKind'
|
|
packName:
|
|
type: string
|
|
nullable: true
|
|
resourceType:
|
|
nullable: true
|
|
allOf:
|
|
- $ref: '#/components/schemas/ShareResourceType'
|
|
payload:
|
|
$ref: '#/components/schemas/JsonObject'
|
|
status:
|
|
$ref: '#/components/schemas/ShareRecipientStatus'
|
|
createdAt:
|
|
type: string
|
|
format: date-time
|
|
respondedAt:
|
|
type: string
|
|
format: date-time
|
|
nullable: true
|
|
|
|
AcceptShareResponse:
|
|
type: object
|
|
required: [createdResources]
|
|
properties:
|
|
createdResource:
|
|
$ref: '#/components/schemas/SyncedResourceItem'
|
|
description: Present only for single-resource shares.
|
|
createdResources:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/SyncedResourceItem'
|