Cladèse documentation

API HTTP

JSON sur HTTP, connexion par email et mot de passe, jeton porté par un en-tête ou un cookie. Le schéma OpenAPI complet est servi en mode debug.

Authentification#

Toute route est fermée par défaut. Seules POST /auth/login, POST /auth/logout, /, /health, /ready et /metrics répondent sans connexion.

POST /auth/login reçoit l'email dans le champ username et le mot de passe, au format formulaire (flux OAuth2 password). Il renvoie un jeton JWT, valable APP_ACCESS_TOKEN_EXPIRE_MINUTES (7 jours par défaut, sans rafraîchissement). Le jeton s'envoie de deux façons :

TransportClientsDétail
En-tête Authorization: Bearer <jeton>Mobile, scripts, SwaggerPrioritaire s'il est présent
Cookie access_tokenFront webPosé par /auth/login : HttpOnly, SameSite=Lax, Secure hors localhost. Le navigateur l'envoie aussi pour <video> et les liens de téléchargement

Avec le cookie, les écritures (POST, PUT, PATCH, DELETE) exigent en plus l'en-tête X-Requested-With, de valeur libre : c'est la protection contre les requêtes forgées depuis un autre site. Sans lui, la réponse est 403.

TOKEN=$(curl -s localhost:8000/auth/login \
  -d username=admin@example.com --data-urlencode 'password=votre-mot-de-passe' \
  | jq -r .access_token)
curl -s localhost:8000/progress/formations -H "Authorization: Bearer $TOKEN"

Rôles#

RôleDroits
Apprenant (is_admin=false)Catalogue, vidéos, documents, assistant, ses notes, sa progression et sa consommation
Admin (is_admin=true)Tout, plus le studio (toutes les écritures du catalogue) et la gestion des comptes

Le rôle n'est pas lu dans le jeton : le compte est relu en base à chaque requête. Un retrait du rôle admin ou une désactivation prend donc effet à la requête suivante. Notes et progression restent propres à chaque compte, admin compris.

CodeSignification
401Pas de jeton, jeton invalide ou expiré, compte désactivé
403Connecté, mais route réservée aux admins (ou en-tête anti-CSRF manquant)
429Trop d'échecs de connexion : 5 par email ou par adresse IP sur 15 minutes. En-tête Retry-After

Avec APP_DEBUG=true, l'interface OpenAPI est servie sur /api-docs, /api-redoc et /openapi.json. Son bouton Authorize se connecte via /auth/login : saisir l'email dans username. Le mode debug ne contourne pas l'authentification.

Les listes ne sont pas paginées, sauf les comptes : GET /formations renvoie le catalogue complet, chapitres et leçons compris.

Santé et session#

MéthodeRouteAccèsEffet
GET/healthPublic{"status": "ok"}
GET/readyPublicVérifie PostgreSQL
POST/auth/loginPublicFormulaire username + password → {"access_token", "token_type", "expires_in"} et cookie. 401 au message unique, que l'email ou le mot de passe soit faux
POST/auth/logoutPublic204, efface le cookie
GET/auth/meApprenant{"id", "email", "full_name", "is_admin"}
PATCH/auth/me/passwordApprenantCorps {"current_password", "new_password"} ; 400 si l'actuel est faux, 422 hors 8–128 caractères

Comptes (admin)#

MéthodeRouteEffet
GET/admin/users?offset=0&limit=50 (limite ≤ 200) → {"items", "total"}
POST/admin/usersCorps {"email", "password", "full_name"?, "is_admin"?} ; email déjà pris → 409
PATCH/admin/users/{id}Corps {"full_name"?, "is_admin"?, "is_active"?, "password"?} ; champ absent = inchangé
DELETE/admin/users/{id}Supprime le compte, ses notes, sa progression et sa consommation

Un compte renvoyé contient id, email, full_name, is_admin, is_active et created_at, jamais le mot de passe ni son empreinte. Un admin ne peut ni se retirer le rôle admin, ni se désactiver, ni se supprimer : 409.

Formations (apprenant)#

MéthodeRouteEffet
GET/formationsCatalogue complet : chapitres, vidéos, documents, statuts, active_jobs
GET/formations/{id}Une formation
POST/formations/{id}/askCorps {"question"} → {"answer", "citations"}

Studio (admin)#

Toutes les écritures du catalogue, y compris celles des sections Vidéos et Documents marquées « Admin ». Un apprenant reçoit 403.

MéthodeRouteEffet
POST/formations/{id}/index202, job rag_index_formation
POST/formationsCorps {"name"}
PATCH/formations/{id}Renommer
DELETE/formations/{id}Supprimer
PUT/formations/{id}/chapters/orderCorps {"chapter_ids": [...]}
POST/formations/{id}/chaptersCorps {"name"}
PATCH/chapters/{id}Renommer
DELETE/chapters/{id}Supprimer
POST/chapters/{id}/videosMultipart title + file ; conversion en job si nécessaire
PUT/chapters/{id}/videos/orderCorps {"video_ids": [...]}
PATCH/chapters/{source}/{target}/{video_id}Déplacer ; position ou after_video_id facultatifs
PATCH/videos/{id}JSON {"title"}, ou multipart title? + file?
DELETE/videos/{id}Supprimer
POST/chapters/{id}/docsMultipart title + file + video_id? ; 422 si extension refusée

Vidéos#

MéthodeRouteAccèsEffet
GET/videos/{id}/streamApprenantFlux avec support Range
GET/videos/{id}/fileApprenantFichier complet
GET/videos/{id}/transcriptionApprenant{"content"}
POST/videos/{id}/transcriptionAdmin202, job transcription
GET/videos/{id}/summaryApprenant{"summary"}
PUT/videos/{id}/summaryAdminCorps {"summary"} ; remplace le texte
POST/videos/{id}/summary/generateAdmin202, job summary
POST/videos/{id}/conversionAdmin202, relance ffmpeg

Champs d'état d'une vidéo#

ChampValeurs
kindvideo | audio
processing_statusready | processing | failed
transcription_statusnone | processing | ready | failed
summary_statusnone | processing | ready | failed
active_jobs[]id, kind, status (queued | running), progress 0–100, message

Progression et notes (apprenant)#

MéthodeRouteEffet
GET/progress/formationsProgression de toutes les formations
GET/progress/formation/{id}Détail par chapitre et par vidéo
GET/progress/{video_id}Position d'une vidéo
POST/progress/{video_id}Corps {"last_position"} en secondes
GET/notes/{video_id}Notes du compte connecté sur la vidéo
POST/notes/{video_id}Corps {"timecode", "content"}
PUT/notes/{note_id}Corps {"content"}
DELETE/notes/{note_id}Supprimer

Documents#

MéthodeRouteAccèsEffet
GET/docs/chapters/{chapter_id}ApprenantDocuments du chapitre
GET/docs/{id}/fileApprenantTéléchargement ; ?download=true force l'enregistrement
PATCH/docs/{id}AdminCorps {"title"?, "video_id"?} ; video_id: null détache
DELETE/docs/{id}AdminSupprimer

Consommation IA (apprenant)#

MéthodeRouteEffet
GET/usage?days=30Tokens du compte connecté (1 à 365 jours) : period, all_time, by_kind, by_model, daily (jours UTC)
Suivre un traitement Après un 202, interrogez GET /formations/{id} toutes les quelques secondes tant qu'un statut vaut processing, et affichez active_jobs[].progress.