- manifest + icones, service worker (precache, network-first, Background Sync)
- module client FlowOffline (IndexedDB, queue, delta, flush) + hook editeur
- endpoints /api/v2/sync/{delta,batch,status} + moteur de sync (conflits LWW/orpheline/copie offline)
- migrations offline_sync_queue + sync_version (triggers)
- UI offline (banner, badge sync, toasts, icone dirty) + doc /help
- tests pytest (sync, migrations, SW, offline) + E2E Playwright; bump 6.0.0
36 KiB
Guide des API FlowDeck — Référence d'implémentation v6.0.0
Statut : référence de conception pour la mise en place de l'API publique complète (v6.0.0). Dernière mise à jour : 2026-09-04 Portée : inventaire de l'API existante, conventions cibles, design CRUD par ressource, webhooks, sécurité, checklist d'implémentation.
1. Vision & architecture
FlowDeck est API-first (FastAPI). Deux couches d'API coexistent :
| Couche | Préfixe | Auth | Usage |
|---|---|---|---|
| API interne (existant) | /api, /workspace, /db, /board… |
Session cookie flowdeck_session + CSRF |
Le frontend HTMX/Alpine |
| API publique v1 (existant, lecture seule) | /api/v1 |
Bearer token | Intégrations externes minimales |
| API publique v2 (cible v6.0.0) | /api/v2 |
Bearer token + scopes | CRUD complet, usage externe (clone Notion API) |
Principe : l'API v2 expose les mêmes opérations que l'API interne, mais avec une surcouche de contrôle (auth par token, scopes, pagination, filtres, erreurs normalisées). Elle est implémentée comme des wrappers sur les mêmes services/logique métier — jamais de duplication.
Règles d'or
- Un endpoint public = un wrapper autour de la logique existante (un seul chemin de code).
- L'API publique ne renvoie jamais de HTML, de secrets, ni de colonnes internes (
password_hash, tokens OAuth). - Toute mutation passe par les mêmes validations que l'UI (CSRF concerne l'UI seule ; les tokens remplacent CSRF).
- Versionnage par préfixe d'URL (
/api/v1,/api/v2) — jamais de breaking change sur une version publiée.
2. État actuel — inventaire de l'API
2.1 Schéma global des routeurs (app/main.py)
| Routeur | Fichier | Préfixe | Rôle |
|---|---|---|---|
auth |
routers/auth.py |
/auth |
Login OAuth/local, register, logout, sessions |
dashboard |
routers/dashboard.py |
— | Pages HTML (landing, workspace, settings…) |
board |
routers/board.py |
/board |
Kanban Gitea (legacy, compatible) |
notes |
routers/notes.py |
/notes |
Notes Markdown par projet |
api |
routers/api.py |
/api |
Pages, workspaces, tags, trash, favoris, settings |
webhooks |
routers/webhooks.py |
/api/webhook |
Réception webhooks Gitea (issues/PR/repo) |
collections |
routers/collections.py |
/db |
Databases Notion-style (CRUD) |
my_tasks |
routers/my_tasks.py |
/my-tasks |
Agrégation tâches |
workspace |
routers/workspace.py |
/workspace |
Local workspace, arborescence, webhooks sortants |
library |
routers/library.py |
/api/library |
Bibliothèque (sources local/Gitea/GitHub) |
admin |
routers/admin.py |
/api/admin |
Admin utilisateurs + audit |
gitea |
routers/gitea.py |
/api/gitea |
Projets, fichiers, commits, labels |
github |
routers/github_routes.py |
/api/github |
Intégration GitHub |
public_api |
routers/public_api.py |
/api/v1 |
API publique existante (lecture seule) |
sharing |
routers/sharing.py |
/api |
Share/publish pages |
sidebar_config |
routers/sidebar_config.py |
/api/sidebar |
Customisation sidebar |
export |
routers/export.py |
/api/export |
Export MD/PDF/HTML/CSV |
notifications |
routers/notifications.py |
/api/notifications |
Notifications in-app |
collaboration |
routers/collaboration.py |
/api |
Commentaires inline, mentions, historique |
2.2 Récapitulatif des endpoints existants (par domaine)
Liste non exhaustive côté pages HTML ; les endpoints API sont tous listés.
Auth & users
GET /auth/login, /auth/register, /auth/callback, /auth/logout, /auth/user
POST /auth/local-login, /auth/register, /auth/register/{owner}/{repo}
GET /api/users, /api/users/me, /api/users/search
PUT /api/users/me, /api/user/profile, /api/user/password
POST /api/user/token, /api/settings/avatar, /api/settings/avatar-color, /api/settings/tags
GET /api/settings/account, /api/settings/avatar/{filename}
PUT /api/settings/tags/{tag_id}, /api/settings/tags/all (GET)
DELETE /api/settings/tags/{tag_id}, /api/user/forge/{provider}
Workspaces & membres
GET /api/workspaces, POST /api/workspaces
GET /api/workspace/{ws_id}/members, POST /api/workspace/{ws_id}/members
PUT /api/workspace/{ws_id}/members/{user_id}, DELETE /api/workspace/{ws_id}/members/{user_id}
PUT /api/workspaces/{ws_id}, DELETE /api/workspaces/{ws_id}
POST /api/workspaces/{ws_id}/select
Pages (éditeur blocs)
GET /api/pages/{page_id}, PUT /api/pages/{page_id}, DELETE /api/pages/{page_id}
POST /api/pages (création), /api/pages/{page_id}/blocks, /api/pages/{page_id}/content
PUT /api/pages/{page_id}/rename, /api/pages/{page_id}/move
POST /api/pages/{page_id}/trash, /api/trash/{page_id}/restore
GET /api/trash, DELETE /api/trash/{page_id}
POST /api/pages/{page_id:int}/convert-to-database
GET /api/local-workspace/tree, /api/sidebar/workspace-tree, /api/nav/menu
GET /api/local-workspace/page-content/{page_id}, /children/{page_id}
Collections (databases)
GET /db/{collection_id}/api (détail), DELETE /db/{collection_id}
POST /db/inline/api (créer inline)
POST /db/{id}/linked/api, /db/{id}/toggle-inline/api, PUT /db/{id}/toggle-task/api
GET /db/{id}/sources/api, POST /db/{id}/sources/api, DELETE /db/{id}/sources/{sid}/api
GET /db/{id}/properties/api, POST /db/{id}/properties/api
PUT /properties/{prop_id}/api, DELETE /properties/{prop_id}/api
POST /db/{id}/properties/relation, /db/{id}/properties/relation/link
GET /db/{id}/views/api, POST /db/{id}/views/save-as, PUT /views/{view_id}/config
GET /property-types/api
Collection pages & tâches
GET /db/{id}/pages/api, POST /db/{id}/pages/api, PUT /pages/{page_id}/api, DELETE /pages/{page_id}/api
POST /db/{id}/pages/{pid}/sub-items, GET /db/{id}/pages/{pid}/sub-items
GET /db/{id}/pages/{pid}/dependencies/api, POST /db/{id}/pages/{pid}/dependencies/api
DELETE /db/{id}/pages/{pid}/dependencies/{dep_id}/api
POST /db/{id}/pages/{pid}/auto-shift/api, /db/{id}/pages/{pid}/check-deps
GET /db/{id}/pages/{pid}/status-aggregate
POST /formula/evaluate, /rollup/compute
Sprints, dashboards, templates
GET/POST /workspace/collections/{id}/sprints, PUT/DELETE /workspace/collections/{id}/sprints/{sid}
POST /workspace/collections/{id}/sprints/{sid}/assign, DELETE .../assign/{page_id}
GET /workspace/collections/{id}/sprints/burndown/{sid}
GET/POST/PUT/DELETE /workspace/collections/{id}/dashboards[/{did}]
GET/POST /workspace/collections/{id}/templates/page, PUT/DELETE .../templates/page/{tid}
POST .../templates/page/{tid}/apply
GET/POST /templates/database, POST /templates/database/{tid}/apply
Favoris, tags, recents
GET/POST /api/favorites, DELETE /api/favorites/{page_id}
GET/POST /db/{id}/pages/api… (tags par page via /api/local-workspace/items/{id}/tags)
GET /api/local-workspace/tags, /api/local-workspace/tags/search
POST /api/local-workspace/items/{id}/tags, DELETE .../tags/{tag_id}
GET /api/recents, POST /api/recents/track
Partage & publication
POST /api/share/{page_id}, GET /api/pages/{page_id}/shares, DELETE /api/pages/{page_id}/share/{share_id}
POST /pages/{page_id}/share, POST /pages/{page_id}/publish, DELETE /pages/{page_id}/publish
GET /public/{collection_id}, /published, /shared, /private, /p/{slug}
Commentaires & notifications (v4.9.0)
GET/POST /pages/{page_id}/comments, PUT/DELETE /comments/{comment_id}
POST /pages/{page_id}/mentions
GET /api/notifications, /api/notifications/unread-count
POST /api/notifications/read, /api/notifications/read-all
Historique
GET/POST /pages/{page_id}/history
Import / Export
GET /api/collections/{id}/export/csv, POST /api/collections/{id}/import/csv
GET /api/export/markdown|pdf|html (via /export et /markdown/{page_id}, /pdf/{page_id}, /html/{page_id})
Forge Gitea / GitHub
GET /api/workspace/projects, POST /api/workspace/projects
GET /projects/{owner}/{repo}/tree|labels|commits|private-pages|file
PUT /projects/{owner}/{repo}/file, DELETE /projects/{owner}/{repo}/file
POST /projects/{owner}/{repo}/upload, /sync-labels, /private-pages
GET/POST /issues/{owner}/{repo}, PATCH /issues/{owner}/{repo}/{issue_id}
POST /api/sync/{owner}/{repo}, /api/ai-keywords/{owner}/{repo}/extract
Admin
GET /api/admin/users, PUT /api/admin/users/{user_id}, DELETE /api/admin/users/{user_id}
GET /audit, /api/frontend-errors, POST /api/frontend-error
Santé & divers
GET /api/health, /api/stats, /api/config, PUT /api/config, /api/favorites…
POST /api/move, /col-mapping (GET/DELETE/POST), /board-config/{owner}/{repo} (GET/POST)
2.3 API publique v1 existante (app/routers/public_api.py)
| Méthode | Route | Description |
|---|---|---|
| POST | /api/v1/token |
Génère un token fd_xxx (inséré dans user_tokens) |
| GET | /api/v1/collections |
Liste des collections (id, name, description, icon, created_at) |
| GET | /api/v1/collections/{id} |
Collection + ses pages |
| GET | /api/v1/collections/{id}/pages |
Pages racine de la collection |
| GET | /api/v1/pages/{page_id} |
Page détaillée |
| GET | /api/v1/my-tasks |
Tâches (limit 50) |
Auth v1 : header Authorization: Bearer <token>. Token par défaut : fd-public-key (fallback dev). Vérification dans la table user_tokens (colonne gitea_token).
Limites de v1 (ce que v2 doit corriger) :
- Lecture seule — aucun POST/PUT/DELETE sur les ressources.
- Un seul token par utilisateur (
UNIQUE(gitea_user_id)) et stocké dans une table prévue pour les tokens Gitea. - Pas de scopes, pas d'expiration, pas de nom d'affichage, pas de révocation individuelle.
- Pas de pagination, filtres, tri.
- Pas de documentation OpenAPI en production (
docs_urln'est actif qu'en DEBUG).
3. Conventions cibles pour l'API v2
3.1 Auth & tokens
Nouvelle table api_tokens (remplace l'usage détourné de user_tokens) :
CREATE TABLE api_tokens (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
name TEXT NOT NULL DEFAULT 'default', -- ex: "CI production"
token_hash TEXT NOT NULL UNIQUE, -- sha256 du token, jamais en clair
scopes TEXT NOT NULL DEFAULT 'read', -- 'read' | 'read,write' | 'read,write,admin'
expires_at TIMESTAMP, -- NULL = jamais
last_used_at TIMESTAMP,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
revoked INTEGER NOT NULL DEFAULT 0
);
- Format token :
fd_{user_id}_{urlsafe(32)}— l'utilisateur ne le voit qu'une fois. Stockage ensha256. - Crud tokens :
POST /api/v2/tokens,GET /api/v2/tokens,DELETE /api/v2/tokens/{id}(révocation),POST /api/v2/tokens/rotate. - Scopes :
read(GET),write(POST/PUT/PATCH),admin(gestion users/admin). Dépendance FastAPIrequire_scope("write"). - Compat v1 : garder le fallback
fd-public-keyuniquement sisettings.public_api_insecure_ok=true(défaut : false en prod).
3.2 Format des réponses
- JSON brut (pas d'enveloppe
{data: …}) — style Notion API. - Timestamps ISO-8601 UTC (
2026-09-04T14:30:00Z). SQLite stockeYYYY-MM-DD HH:MM:SS→ convertir dans le convertisseur de sortie. - IDs : entiers SQLite (
int). Exposés tels quels (pas de UUID) —ponytail: garder int, passer en UUID seulement si exposition publique nécessaire. - Champs
*_jsonstockés en SQLite (ex:property_values_json,config_json) → déjà parsés en objets JSON dans les réponses API.
3.3 Erreurs (RFC 7807 application/problem+json)
{
"type": "https://flowdeck/api/errors/not-found",
"title": "Resource not found",
"status": 404,
"detail": "Collection 42 n'existe pas",
"instance": "/api/v2/collections/42"
}
Codes : 400 validation (détail = liste des champs), 401 token manquant/invalide, 403 scope insuffisant / permissions, 404 inexistant, 409 conflit (nom dupliqué, dépendance bloquante), 422 erreur de schéma (FastAPI), 429 rate limit, 500 interne.
Handler global : @app.exception_handler(HTTPException) + adaptation des erreurs internes → problème JSON pour tout préfixe /api/v2.
3.4 Pagination, filtres, tri
Pagination offset (simple, SQLite) :
GET /api/v2/collections?limit=30&offset=0
→ response headers: X-Total-Count: 142
Requête : limit (défaut 30, max 100), offset (défaut 0).
Utilise SELECT COUNT(*) + LIMIT ? OFFSET ? — ponytail: offset OK jusqu'à ~10k lignes, passer à keyset (cursor) si besoin.
Filtres (par propriété de la ressource) :
GET /api/v2/collections/{id}/pages?filter[status]=Done&filter[assignee]=bruno
GET /api/v2/pages?query=mot&workspace_id=3
Convention : filter[<property>]=<value> (AND implicite), sort=<property>, sort=-<property> (desc), fields=a,b,c (projection).
Recherche FTS : réserver ?query= sur les listes → recherche plein texte (table FTS5 à créer, indexant pages + collection_pages).
3.5 Idempotence & mutations
POSTde création accepte un header optionnelIdempotency-Key— sur conflit, renvoyer la ressource existante créée avec cette clé (tableidempotency_keys).- Les
PATCHsont partiels (seuls les champs présents sont modifiés).
3.6 Rate limiting par token
Étendre RateLimitMiddleware (actuellement 100 req/min/IP sur /api/, /board/api/, /auth/) :
/api/v2/*: quota par token (défaut 300 req/min, configurable par scope), en plus du quota IP.- Remplacer le store
defaultdictin-memory par une clé composite(ip|token_id)—ponytail: in-memory OK mono-instance, Redis si multi-workers. - Header de réponse :
X-RateLimit-Limit,X-RateLimit-Remaining,X-RateLimit-Reset. - Exemptés :
/api/health,/api/v2/tokens(auth).
4. Design des endpoints v2 — CRUD par ressource
Pour chaque ressource : table(s) SQLite, endpoints internes existants (réutilisables), endpoints publics v2 à créer.
GET /collections→ wrapperpublic_list_collectionsexistant à enrichir ; les mutations s'appuient sur les routeurscollections.py(préfixe/db).
4.1 Users
| Table | users |
|---|---|
| Colonnes clés | id, login, full_name, email, avatar_url, avatar_color, is_admin, is_active, auth_method, sidebar_config, notification_prefs |
| Interne | GET/PUT /api/users/me, GET /api/users, PUT/DELETE /api/users/{id} (admin) |
Endpoints publics v2 :
| Méthode | Route | Description |
|---|---|---|
| GET | /api/v2/users/me |
Profil du user courant (sans password_hash) |
| PATCH | /api/v2/users/me |
Mettre à jour profil (full_name, email, avatar_color, notification_prefs) |
| GET | /api/v2/users |
admin — lister (pagination) |
| GET | /api/v2/users/search?q= |
Recherche par login/email (pour @mentions) |
4.2 Workspaces & membres
| Tables | workspaces, workspace_members (role: owner/admin/editor/viewer) |
|---|---|
| Interne | GET/POST /api/workspaces, PUT/DELETE /api/workspaces/{ws_id}, GET/POST /api/workspace/{ws_id}/members, PUT/DELETE …/members/{user_id} |
Endpoints publics v2 :
| Méthode | Route | Description |
|---|---|---|
| GET | /api/v2/workspaces |
Workspaces de l'utilisateur (avec rôle) |
| POST | /api/v2/workspaces |
Créer (body: name, settings_json) |
| GET | /api/v2/workspaces/{id} |
Détail + membres |
| PATCH | /api/v2/workspaces/{id} |
Renommer / settings |
| DELETE | /api/v2/workspaces/{id} |
Supprimer (owner only) |
| GET | /api/v2/workspaces/{id}/members |
Lister membres |
| POST | /api/v2/workspaces/{id}/members |
Inviter (user_id ou email) |
| PATCH | /api/v2/workspaces/{id}/members/{uid} |
Changer le rôle |
| DELETE | /api/v2/workspaces/{id}/members/{uid} |
Retirer un membre |
⚠️ Dépendance v6 : créer
app/services/permission_manager.py(la roadmap y fait référence mais le fichier n'existe pas). Centraliser :require_workspace_role(ws_id, "editor"),can_read(workspace_id|collection_id|page_id, user).
4.3 Collections (databases)
| Tables | collections (+ workspace_id, created_by, is_inline, parent_page_id, is_task) |
|---|---|
| Interne | POST /db/inline/api, GET/DELETE /db/{id}/api, POST /db/{id}/linked/api, POST /db/{id}/toggle-inline/api, PUT /db/{id}/toggle-task/api, sources (/db/{id}/sources/api) |
Endpoints publics v2 :
| Méthode | Route | Description |
|---|---|---|
| GET | /api/v2/collections?workspace_id=&query= |
Liste (paginée) — wrapper de v1 enrichi |
| POST | /api/v2/collections |
Créer (name, description, icon, workspace_id, schema_json) |
| GET | /api/v2/collections/{id} |
Détail + pages (existant v1) |
| PATCH | /api/v2/collections/{id} |
Renommer, icon, description, schema |
| DELETE | /api/v2/collections/{id} |
Supprimer (cascade pages/vues/props) |
| POST | /api/v2/collections/{id}/linked |
Créer une DB liée |
| POST | /api/v2/collections/{id}/task |
toggler is_task |
| GET | /api/v2/collections/{id}/sources |
Sources / DB liées — wrapper v1 |
| POST | /api/v2/collections/{id}/sources |
Ajouter une source |
| DELETE | /api/v2/collections/{id}/sources/{sid} |
Retirer une source |
4.4 Pages de collection (collection_pages)
| Tables | collection_pages (property_values_json, parent_id, position, gitea_issue_*) |
|---|---|
| Interne | GET/POST /db/{id}/pages/api, GET/PUT/DELETE /pages/{page_id}/api, sub-items, dependencies, auto-shift, status-aggregate, move |
Endpoints publics v2 :
| Méthode | Route | Description |
|---|---|---|
| GET | /api/v2/collections/{id}/pages?filter[status]=…&sort=… |
Lister (pagination, filtres, tri, fields) |
| POST | /api/v2/collections/{id}/pages |
Créer (title, icon, property_values, parent_id) |
| GET | /api/v2/pages/{id} |
Détail (propriétés parsées) |
| PATCH | /api/v2/pages/{id} |
Titre, icon, position, property_values (merge) |
| DELETE | /api/v2/pages/{id} |
Corbeille (soft delete via deleted_at) |
| POST | /api/v2/pages/{id}/restore |
Restaurer |
| POST | /api/v2/pages/{id}/move |
Reordonner (position / parent_id) |
| GET | /api/v2/pages/{id}/sub-items |
Sous-tâches |
| POST | /api/v2/pages/{id}/sub-items |
Créer sous-item |
| GET/POST/DELETE | /api/v2/pages/{id}/dependencies[/{dep_id}] |
Dépendances |
Détail
property_values: objet JSON{"<property_id>": <valeur typée>}— utiliserproperty_types.py(validation/formatage) déjà en place.
4.5 Propriétés (collection_properties)
| Colonnes clés | name, prop_type (21 types), options_json, number_format, related_collection_id, reverse_name, relation_property_id, target_property_id, rollup_function, formula_expression, required, visible_in_views |
|---|---|
| Interne | GET/POST /db/{id}/properties/api, PUT/DELETE /properties/{prop_id}/api, POST …/properties/relation, …/relation/link, POST /formula/evaluate, POST /rollup/compute, GET /property-types/api |
Endpoints publics v2 :
| Méthode | Route | Description |
|---|---|---|
| GET | /api/v2/collections/{id}/properties |
Types + configuration (wrapper) |
| POST | /api/v2/collections/{id}/properties |
Créer (name, prop_type, options…) |
| PATCH | /api/v2/properties/{id} |
Modifier (options, required, formula, rollup…) |
| DELETE | /api/v2/properties/{id} |
Supprimer |
| POST | /api/v2/properties/{id}/relation |
Créer relation bidirectionnelle |
| POST | /api/v2/properties/evaluate-formula |
Moteur formula (debug) |
| POST | /api/v2/properties/compute-rollup |
Moteur rollup (debug) |
4.6 Vues (collection_views) & Dashboards (collection_dashboards)
| Interne | GET /db/{id}/views/api, POST /db/{id}/views/save-as, PUT /views/{view_id}/config, GET/POST/PUT/DELETE /workspace/collections/{id}/dashboards[/{did}] |
|---|
| Méthode | Route | Description |
|---|---|---|
| GET | /api/v2/collections/{id}/views |
Lister les vues (config_json parsé) |
| POST | /api/v2/collections/{id}/views |
Créer (name, view_type, config) |
| PATCH | /api/v2/views/{id} |
Modifier config (filtres, tri, group_by, layout) |
| DELETE | /api/v2/views/{id} |
Supprimer |
| POST | /api/v2/views/{id}/save-as |
Dupliquer sous un autre nom |
| GET/POST/PUT/DELETE | /api/v2/collections/{id}/dashboards[/{did}] |
Dashboards (layout_json) |
4.7 Commentaires (comments, v4.9.0)
| Colonnes clés | target_type ('page'/'collection_page'), target_id, user_id, body, parent_id, resolved, anchor_block_id, anchor_start, anchor_end |
|---|---|
| Interne | GET/POST /pages/{page_id}/comments, PUT/DELETE /comments/{comment_id}, POST /pages/{page_id}/mentions |
| Méthode | Route | Description |
|---|---|---|
| GET | /api/v2/pages/{id}/comments |
Lister (avec tri chronologique) |
| POST | /api/v2/pages/{id}/comments |
Ajouter (body, anchor_block_id/start/end optionnels) |
| PATCH | /api/v2/comments/{id} |
Éditer / résoudre (resolved: true) |
| DELETE | /api/v2/comments/{id} |
Supprimer |
| POST | /api/v2/pages/{id}/mentions |
Mentionner des users (déclenche notification + email) |
4.8 Notifications (notifications)
| ntype | 'mention' / 'comment' / 'page' — resource_type, resource_id, url, is_read |
|---|---|
| Interne | GET /api/notifications, GET /unread-count, POST /read, POST /read-all |
| Méthode | Route | Description |
|---|---|---|
| GET | /api/v2/notifications?unread=1 |
Lister (pagination) |
| POST | /api/v2/notifications/{id}/read |
Marquer lue |
| POST | /api/v2/notifications/read-all |
Tout marquer lu |
| GET | /api/v2/notifications/unread-count |
Compteur (badge) |
| PATCH | /api/v2/users/me/preferences |
prefs email (comments/mentions) |
4.9 Favoris & Tags & Recents
| Tables | favorites, tags (per-user), page_tags, recents |
|---|---|
| Interne | GET/POST /api/favorites, DELETE /api/favorites/{page_id}, /api/settings/tags*, /api/local-workspace/items/{id}/tags*, /api/recents* |
| Méthode | Route | Description |
|---|---|---|
| GET | /api/v2/favorites |
Lister |
| POST | /api/v2/favorites |
Ajouter {page_id} |
| DELETE | /api/v2/favorites/{page_id} |
Retirer |
| GET | /api/v2/tags?q= |
Tags de l'utilisateur |
| POST | /api/v2/tags |
Créer (name, color) |
| PUT/DELETE | /api/v2/tags/{id} |
Modifier / supprimer |
| POST | /api/v2/pages/{id}/tags |
Attacher {tag_id} |
| DELETE | /api/v2/pages/{id}/tags/{tag_id} |
Détacher |
| GET | /api/v2/recents?source_type= |
Récents (limit 20) |
4.10 Partage & publication (page_shares)
| Colonnes clés | page_id, shared_with_user_id, shared_with_email, permission ('view'/'edit'), created_by + colonnes pages (is_published, publish_slug, is_shared, share_mode) |
|---|---|
| Interne | POST /api/share/{page_id}, GET /api/pages/{id}/shares, DELETE /api/pages/{id}/share/{share_id}, POST /pages/{id}/publish, DELETE /pages/{id}/publish |
| Méthode | Route | Description |
|---|---|---|
| GET | /api/v2/pages/{id}/shares |
Liste des accès |
| POST | /api/v2/pages/{id}/shares |
Partager `{email |
| PATCH | /api/v2/shares/{share_id} |
Changer permission |
| DELETE | /api/v2/shares/{share_id} |
Révoquer |
| POST | /api/v2/pages/{id}/publish |
Publier ({slug}) → page publique /p/{slug} |
| DELETE | /api/v2/pages/{id}/publish |
Dé-publier |
4.11 Historique (page_history)
| Colonnes clés | page_id, user_id, change_type, snapshot_json, created_at |
|---|---|
| Interne | GET/POST /pages/{page_id}/history |
| Méthode | Route | Description |
|---|---|---|
| GET | /api/v2/pages/{id}/history |
Liste des versions (change_type, created_at, user) |
| POST | /api/v2/pages/{id}/history/restore |
Restaurer {history_id} |
4.12 Sprints (sprints, sprint_pages)
| Interne | /workspace/collections/{id}/sprints[/{sid}], assign, burndown |
|---|
| Méthode | Route | Description |
|---|---|---|
| GET/POST | /api/v2/collections/{id}/sprints |
Lister / créer |
| PATCH/DELETE | /api/v2/sprints/{sid} |
Mettre à jour / supprimer |
| POST | /api/v2/sprints/{sid}/assign |
Assigner {page_id, velocity_points} |
| DELETE | /api/v2/sprints/{sid}/assign/{page_id} |
Retirer |
| GET | /api/v2/sprints/{sid}/burndown |
Points (total/completed/remaining/ideal) |
4.13 Templates (page_templates, database_templates)
| Interne | /workspace/collections/{id}/templates/page*, /templates/database*, apply |
|---|
| Méthode | Route | Description |
|---|---|---|
| GET/POST | /api/v2/collections/{id}/templates |
Lister / créer un template de page |
| PATCH/DELETE | /api/v2/templates/{tid} |
Modifier / supprimer |
| POST | /api/v2/templates/{tid}/apply |
Instancier (crée une page depuis le template) |
| GET | /api/v2/templates/database |
Templates de DB (Project tracker, CRM…) |
| POST | /api/v2/templates/database/{tid}/apply |
Créer une collection depuis le template |
4.14 Export & Import
| Interne | GET /api/collections/{id}/export/csv, POST /api/collections/{id}/import/csv, /export, /markdown/{page_id}, /pdf/{page_id}, /html/{page_id} (routers/export.py) |
|---|
| Méthode | Route | Description |
|---|---|---|
| GET | /api/v2/pages/{id}/export |
`?format=markdown |
| GET | /api/v2/collections/{id}/export/csv |
CSV |
| POST | /api/v2/collections/{id}/import/csv |
Import CSV (multipart) |
Les exports PDF/HTML/MD existent déjà (
export.py) — v2 ne fait que les exposer avec auth token. Générer le fichier puis le renvoyer (FileResponse) ou une URL signée temporaire.
4.15 Forges (Gitea / GitHub)
| Interne | routers/gitea.py + services/gitea_client.py (adapter), routers/github_routes.py + services/github_adapter.py |
|---|
| Méthode | Route | Description |
|---|---|---|
| GET | /api/v2/projects |
Projets forges liés (workspace) |
| GET | /api/v2/projects/{owner}/{repo}/tree |
Arborescence |
| GET | /api/v2/projects/{owner}/{repo}/file?path= |
Lire un fichier |
| PUT | /api/v2/projects/{owner}/{repo}/file |
Écrire {path, content, message} (commit) |
| DELETE | /api/v2/projects/{owner}/{repo}/file |
Supprimer (commit) |
| GET | /api/v2/projects/{owner}/{repo}/commits?path= |
Historique |
| GET | /api/v2/projects/{owner}/{repo}/issues |
Issues (provider-agnostic via ForgeAdapter) |
| PATCH | /api/v2/issues/{provider}/{owner}/{repo}/{id} |
Mettre à jour |
Cible v6 (roadmap) : interface
ForgeAdaptercommune (Gitea + GitHub) — les endpoints v2 consomment l'adapter, pas les clients bruts.
4.16 Admin & Audit
| Interne | routers/admin.py (/api/admin), /audit |
|---|
| Méthode | Route | Description |
|---|---|---|
| GET | /api/v2/admin/users |
scope admin — lister (pagination) |
| PATCH | /api/v2/admin/users/{id} |
Activer/désactiver, rôle admin |
| DELETE | /api/v2/admin/users/{id} |
Supprimer |
| GET | /api/v2/admin/audit-logs |
Dernières actions (login, exports, changements rôle) |
4.17 Recherche
| Métier | FTS5 sur pages + collection_pages (+ tags) — table d'index à créer à la migration v6 |
|---|
| Méthode | Route | Description |
|---|---|---|
| GET | `/api/v2/search?query=&workspace_id=&type=page | collection |
4.18 Synchronisation offline (PWA)
| Interne | routers/sync.py + services/sync_engine.py — auth session (flowdeck_session), CSRF-exempt (/api/v2) |
|---|
| Méthode | Route | Description |
|---|---|---|
| GET | /api/v2/sync/delta?since=<epoch>&workspace_id=<id> |
Changements serveur depuis since (pages créées/màj/supprimées, collections) → {changes, server_time, has_more} |
| POST | /api/v2/sync/batch |
Rejoue un lot de mutations offline → {results, conflicts, server_time} |
| GET | /api/v2/sync/status?workspace_id=<id> |
{pending_count, last_sync, is_syncing, server_time} |
Format batch : {device_id, mutations:[{id, type, payload, client_timestamp}]} — type ∈ page_create|page_update|page_delete|page_move|collection_create|collection_update|collection_delete. page_update accepte base_version (colonne sync_version) pour la détection de conflit.
Conflits : edit_edit (last-write-wins + rapport), edit_delete (page orpheline recréée), create_create (renommage « … (copie offline) »). Chaque mutation est tracée dans offline_sync_queue.
5. Webhooks sortants (à étendre en v2)
Mécanisme existant (app/services/webhook_outbound.py) :
- Table
webhook_subscriptions: id, url, event, secret, active, created_at. - Événements actuels (8) :
EVENTS = [
"page.created", "page.updated", "page.deleted",
"collection.created", "collection.updated", "collection.deleted",
"comment.added", "page.moved",
]
- Dispatch
fire_event(event, payload)→ POST JSON avec headersX-FlowDeck-Event+X-FlowDeck-Secret.
Endpoints v2 webhooks (wrapper du CRUD sortant existant + gestion des abonnements) :
| Méthode | Route | Description |
|---|---|---|
| GET | /api/v2/webhooks |
Abonnements de l'utilisateur |
| POST | /api/v2/webhooks |
Créer {url, event, secret} |
| PATCH | /api/v2/webhooks/{id} |
Activer/désactiver, changer url/secret |
| DELETE | /api/v2/webhooks/{id} |
Supprimer |
| POST | /api/v2/webhooks/{id}/test |
Événement de test ping |
| GET | /api/v2/webhooks/{id}/deliveries |
Journal des livraisons (à ajouter : table webhook_deliveries) |
Événements à ajouter (parité Notion + besoins v6) :
"page.created", "page.updated", "page.deleted", "page.restored",
"collection.created", "collection.updated", "collection.deleted",
"collection_page.created", "collection_page.updated", "collection_page.deleted",
"comment.added", "comment.resolved",
"mention.created", "notification.created",
"sprint.created", "sprint.updated", "sprint.completed",
"share.created", "share.revoked", "page.published", "page.unpublished",
"agent.run.completed",
Payload type :
{
"event": "page.updated",
"data": { "id": 42, "workspace_id": 3, "updated_at": "2026-09-04T14:30:00Z" },
"actor": { "id": 1, "login": "bruno" },
"timestamp": "2026-09-04T14:30:00Z"
}
- Signature : HMAC-SHA256 du body avec
secret, headerX-FlowDeck-Signature. - Retry : 3 tentatives (2s, 10s, 60s) + journal
webhook_deliveries(status, http_code, error, duration_ms).
6. Schéma de données — résumé par ressource
Schéma complet dans
app/db.py(init_db()+ migrations). Tables principales :
| Domaine | Tables |
|---|---|
| Auth | users, login_history, user_tokens (Gitea), user_oauth_tokens, api_tokens (à créer) |
| Kanban legacy | boards, cards, col_mapping, checklists, checklist_items, notes |
| Pages | pages (content_format='markdown'/'blocks', deleted_at, share_mode, is_published, publish_slug, is_shared, collection_id) |
| Databases | collections, collection_pages, collection_views, collection_properties, collection_data_sources, collection_dashboards |
| Multi-utilisateur | workspaces, workspace_members, comments (target_type/target_id/anchors), page_history, favorites, recents, page_shares |
| Templates | database_templates, page_templates (is_recurring, recurrence_rule) |
| Tags | tags (per-user), page_tags |
| Tâches | page_dependencies (blocks/related, auto_shift), sprints, sprint_pages |
| Notifications | notifications (+ users.notification_prefs) |
| Forges | gitea_private_pages, (projets via adapters) |
| Webhooks | webhook_subscriptions, webhook_deliveries (à créer) |
| Agent | (v4.10.0 — à créer : agents, agent_conversations, agent_messages, agent_actions, agent_skills, agent_triggers) |
Règles :
- FKs avec
ON DELETE CASCADElà où la suppression parent doit purger (pages, vues, props, membres, dépendances). property_values_json,schema_json,config_json,layout_json,options_json: JSON textuel, parse/validate côté service (property_types.py).- WAL mode +
PRAGMA foreign_keys=ON(déjà en place dansget_conn()).
7. Sécurité (v2)
| Menace | Contre-mesure |
|---|---|
| Token volé | Hash sha256 en DB, révocation, rotation, expires_at, scopes minimaux |
| Rejeu CSRF | N/A côté token (Bearer) ; l'UI garde son CSRF middleware |
| Brute force / abus | Rate limit par token + par IP (middleware existant étendu), 429 |
| Injection SQL | SQL paramétré (? seul, jamais de f-string dans les requêtes) |
| XSS (contenu) | Sanitisation à la sortie HTML (UI) ; l'API renvoie du JSON brut (aucun rendu) |
| Exfiltration de secrets | Projection stricte : jamais password_hash, *_token, clés OAuth dans les réponses |
| CORS | Configurer allow_origins pour les clients externes (settings v2) |
| Fichiers | validate_upload() existant : extensions whitelist + 10 MB max |
| Audit | Journaliser chaque mutation v2 (user, token_id, action, resource, ts) — table api_audit_log |
8. OpenAPI & documentation
- Activer
docs_url="/docs"etredoc_url="/redoc"en production sur le routeur v2 uniquement (ou protéger par token admin). - Générer
openapi.jsonà la release et le versionner dansdocs/openapi-v2.json. - Tags OpenAPI par domaine (workspaces, collections, pages, properties, views, comments…).
- Exemples request/response réels dans les docstrings (FastAPI le génère automatiquement en OpenAPI).
- Garder
docs_url=Nonetant que v1 est seule (comportement actuel), l'activer lors du merge v2.
9. Checklist d'implémentation v6.0.0 (ordre recommandé)
- Migration DB : table
api_tokens(+ index sur token_hash),webhook_deliveries,api_audit_log, FTS5 index de recherche. PermissionManager(app/services/permission_manager.py) — prerequisite de toute la v2 :can_read/can_write/can_adminpar workspace + collection + page.- Conventions : handler d'erreurs RFC 7807, convertisseur ISO-8601, helper pagination (
paginate(query, limit, offset)), dépendancesrequire_scope. - Wrappers v2 read : reprendre
public_api.py→/api/v2avec pagination/filtres (collections, pages, my-tasks). - Wrappers v2 write : collections, pages, properties, views (mutation) — les plus demandés par les intégrations.
- Webhooks v2 : CRUD abonnements + déliveries + retry + signature HMAC + événements étendus.
- Reste des ressources : sprints, templates, dashboards, favoris, tags, partage, notifications, admin.
- Recherche FTS (
/api/v2/search). - OpenAPI : activer /docs + générer openapi-v2.json + exemples.
- Tests (
tests/test_public_api_v2.py) : auth token + scopes, CRUD complet par ressource, pagination, erreurs, rate limit, webhook delivery (mock httpx). Cible : couverture ≥ 80 % sur le routeur v2. - Documentation utilisateur : page
/help+ ce guide référencé depuis le README.
10. Références
- Schéma DB :
app/db.py - Routeurs existants :
app/routers/*.py - Frontend :
app/templates/*.html,static/js/app.js - Webhooks sortants :
app/services/webhook_outbound.py - Types de propriétés :
app/services/property_types.py - Roadmap :
ROADMAP.md(v6.0.0 — API publique, realtime, synced blocks, web clipper, permissions granulaires)
11. Documents de conception détaillée v6.0.0
Chaque feature v6.0.0 dispose d'un document de conception détaillé dans /docs/ :
| Feature | Document | Description |
|---|---|---|
| PWA | V6_PWA_Progressive_Web_App.md |
Offline support, service worker, IndexedDB, sync batch |
| SSO/SAML | V6_SSO_SAML_Enterprise_Auth.md |
SAML 2.0, OIDC, auto-provisioning, group mapping |
| Permissions granulaires | V6_Granular_Permissions.md |
Page-level, property-level, collection-level, groupes |
| Web Clipper | V6_Web_Clipper.md |
Extension navigateur, capture d'articles, OAuth |
Note
: Le présent guide couvre la couche API v2 commune à toutes les features. Chaque document de conception ci-dessus détaille les endpoints, tables, services et UI spécifiques à sa feature.