12 KiB
Documentation de l'API REST (Intégration Présence)
Ce document fournit la documentation technique, autonome, exacte et vérifiable de l'API REST implémentée dans l'application Tableau de bord pro.
1. But et périmètre
L'API REST expose un endpoint unique destiné à recevoir des événements de présence automatisés (par exemple en provenance d'un système domotique comme Home Assistant). Elle permet de :
- Enregistrer l'arrivée d'un collaborateur sur son lieu de travail (création automatique d'une entrée de journée
WorkEntrysi elle n'existe pas et d'un événementWorkplacePresenceEvent). - Enregistrer le départ du lieu de travail (fermeture de l'arrivée ouverte, création d'une plage horaire
TimeSlotet de l'événement de départ associé). - Garantir l'idempotence des requêtes via une clé unique pour éviter les doublons en cas de rejeu réseau.
2. Prérequis de configuration ([home_assistant])
L'activation de l'API dépend de la présence et de la validité de la section [home_assistant] dans le fichier de configuration config.toml. Si cette section est absente, l'API est désactivée et renvoie un code 404.
Exemple de configuration valide dans config.toml :
[home_assistant]
timezone = "Europe/Paris"
default_day_type = "WORK"
default_journey_profile_id = "moteur_seul"
default_motor_vehicle_id = "citadine"
Règles de validation de la configuration :
timezone: Doit être un fuseau horaire valide reconnu parzoneinfo.ZoneInfo(ex:"Europe/Paris").default_day_type: Doit correspondre à un type de jour valide (WORK,TT,GARDE,ASTREINTE,FORMATION,RTT,CONGE,MALADE,FERIE).default_journey_profile_id: Doit être un identifiant de trajet existant dans la section[journeys].default_motor_vehicle_id: Doit être un identifiant de véhicule existant dans la section[vehicles]dont le type est"moteur".
3. Sécurité, Authentification et Transport
Authentification par Jeton (Bearer Token)
- L'API exige un jeton d'authentification transmis via l'en-tête HTTP
Authorizationau format Bearer :Authorization: Bearer <WORKLOG_API_TOKEN> - La variable d'environnement
WORKLOG_API_TOKENdoit être définie sur le serveur d'exécution. Si cette variable n'est pas configurée, l'API refuse toute requête et renvoie le statut503. - La comparaison du jeton fourni avec la valeur configurée est réalisée de manière sécurisée en temps constant via
hmac.compare_digestpour prévenir les attaques temporelles.
Transport et Proxy (HTTPS / HAProxy)
- L'application Flask n'implémente pas nativement le chiffrement TLS ni de mécanisme de rotation de jeton.
- En production, la sécurité du transport (HTTPS) et le routage sont entièrement délégués au serveur amont HAProxy.
4. Spécifications techniques de l'endpoint
- URL :
/api/v1/workplace-presence - Méthode :
POST - Content-Type requis :
application/json - Taille maximale du corps :
4096octets (_MAX_BODY_BYTES). Au-delà, l'API rejette la requête avec le code413.
Limitation de débit (Rate-Limit)
- Chaque jeton d'authentification est soumis à une limitation de débit par processus de 30 requêtes par fenêtre glissante de 60 secondes (
_RATE_LIMIT = 30,_RATE_WINDOW_SECONDS = 60.0). - Note d'architecture : Ce compteur est strictement local au processus d'exécution Flask/Gunicorn. Dans un déploiement multi-workers, une limitation globale nécessite un mécanisme amont (tel que HAProxy) ou un stockage partagé.
5. Structure du Corps de la Requête (JSON)
Le corps de la requête doit être un objet JSON valide contenant les champs suivants :
| Champ | Type | Obligatoire | Description |
|---|---|---|---|
event |
string |
Oui | Type d'événement : "arrival" ou "departure". |
occurred_at |
string |
Oui | Horodatage au format ISO 8601 comportant obligatoirement un offset de fuseau horaire (ex: 2026-08-13T08:00:00+02:00). |
idempotency_key |
string |
Oui | Clé d'idempotence unique (limitée à 255 caractères). Obligatoire : transmise soit dans le corps JSON (idempotency_key), soit via l'en-tête HTTP X-Idempotency-Key. |
Règle sur la clé d'idempotence :
La clé d'idempotence est obligatoire. Elle doit être fournie soit dans le corps JSON (idempotency_key), soit via l'en-tête HTTP X-Idempotency-Key. Si la clé est fournie à la fois dans le corps JSON et dans l'en-tête, leurs valeurs doivent être strictement identiques, sous peine d'un rejet avec le code 422. Ne jamais considérer ce champ comme facultatif.
6. Gestion du Temps et Règles Métier
- Fuseau horaire : Les horodatages
occurred_atsont convertis dans le fuseau horaire configuré dans[home_assistant].timezone(la configuration explicite detimezoneest obligatoire dans la section[home_assistant];"Europe/Paris"n'est qu'un exemple de valeur fourni dans la configuration de référence du projet). - Arrivée (
arrival) :- Crée une entrée de journée (
WorkEntry) à la date locale si elle n'existe pas, en appliquant les valeurs par défaut de la configuration (default_day_type,default_journey_profile_id,default_motor_vehicle_id). - Une seule arrivée peut être ouverte (
time_slot_id IS NULL) simultanément dans toute l'application.
- Crée une entrée de journée (
- Départ (
departure) :- Ferme l'arrivée ouverte correspondante.
- Crée un intervalle de temps (
TimeSlot) rattaché à l'entrée de journée, défini entre l'heure de l'arrivée et l'heure du départ. - L'heure du départ doit être strictement postérieure à celle de l'arrivée locale (
occurred_local > arrival_local), sinon un code422est renvoyé.
7. Réponses de Succès
Codes de statut de succès :
201 Created: Événement d'arrivée créé avec succès (nouvelle ressource).200 OK: Événement de départ enregistré avec succès, ou rejeu (replay) d'un événement existant identique (idempotence).
Format de la réponse JSON :
{
"status": "created",
"event_id": 42,
"entry_id": 12,
"time_slot_id": null,
"event_type": "arrival"
}
Champs retournés :
status:"created"(nouvel enregistrement) ou"replayed"(requête idempotente rejouée).event_id: Identifiant unique de l'événement de présence enregistré.entry_id: Identifiant de la journée de travail associée (WorkEntry).time_slot_id: Identifiant de la plage horaire créée (nullpour une arrivée non fermée, entier pour un départ).event_type: Rappel du type d'événement traité ("arrival"ou"departure").
8. Gestion des Erreurs
L'API renvoie un format d'erreur homogène sous forme d'objet JSON :
{
"error": "Message explicatif de l'erreur"
}
Toutes les réponses de l'API (succès et erreurs) comportent l'en-tête Cache-Control: no-store.
Codes de statut HTTP d'erreur pris en charge :
| Code | Message d'erreur associé | Cause / Description |
|---|---|---|
400 |
"JSON invalide" |
Le corps de la requête n'est pas un JSON syntaxiquement valide. |
401 |
"Authentification requise" |
En-tête Authorization absent, schéma non Bearer, ou jeton invalide. |
404 |
"API indisponible" |
Section [home_assistant] absente de config.toml. |
405 |
"Méthode non autorisée" |
Utilisation d'une méthode HTTP autre que POST sur l'endpoint /api/v1/.... |
409 |
"Conflit métier" |
Conflit logique : tentative d'enregistrement d'un départ sans arrivée ouverte, double arrivée, ou réutilisation d'une clé d'idempotence avec des paramètres différents. |
413 |
"Requête trop volumineuse" |
La taille du corps de la requête dépasse la limite de 4096 octets. |
415 |
"Content-Type invalide" |
L'en-tête Content-Type est absent ou différent de application/json. |
422 |
"Schéma invalide" ou "Événement invalide" |
Données non conformes : clés non autorisées, format occurred_at invalide (absence d'offset ou ISO 8601 incorrect), types incorrects, ou départ antérieur à l'arrivée. |
429 |
"Trop de requêtes" |
Dépassement de la limite de débit (Rate-limit de 30 req / 60s par processus). |
500 |
"Erreur interne" |
Exception inattendue lors du traitement en base de données. |
503 |
"API indisponible" |
Variable d'environnement WORKLOG_API_TOKEN non définie sur le serveur. |
9. Idempotence
- L'API garantit l'idempotence par le biais de la clé d'idempotence obligatoire (
idempotency_keydans le corps ou en-têteX-Idempotency-Key). - Si une requête est rejouée avec une clé d'idempotence déjà enregistrée :
- Si les paramètres (
eventetoccurred_at) sont strictement identiques, l'API renvoie le résultat précédent avec le statut200 OKet"status": "replayed", sans dupliquer les enregistrements. - Si les paramètres diffèrent pour une même clé, l'API rejette la requête avec le code
409("Conflit métier").
- Si les paramètres (
10. Comportement de l'Interface Web après l'API
- Les journées (
WorkEntry) créées automatiquement par l'API suite à une arrivée sont pleinement intégrées dans l'application. - L'utilisateur peut consulter et modifier ces entrées via l'interface Web (formulaire d'édition
/entries/<id>/edit) :- Modification du type de journée (ex: passer de
WORKàFORMATION), du profil de trajet ou du véhicule à moteur. - Modification, ajout ou suppression de plages horaires (
TimeSlot).
- Modification du type de journée (ex: passer de
- Cohérence des événements de présence (comportement validé par les tests d'intégration) : Lorsque les plages horaires d'une entrée sont modifiées ou recréées depuis l'interface Web (formulaire d'édition), les événements de présence associés (
WorkplacePresenceEvent) conservent leur traçabilité (rattachés à l'entréeWorkEntry), mais leurs identifiants de plage (time_slot_id) sont dissociés (NULL) pour refléter la réorganisation manuelle de la journée, conformément aux tests d'intégration existants. Aucune autre promesse au-delà de cette dissociation (NULL) et du maintien des événements n'est garantie.
11. Exemples d'utilisation curl sécurisés
Avertissement de sécurité : N'écrivez jamais de secret en clair dans des scripts ou des documentations. Utilisez toujours une variable shell (ex:
$WORKLOG_API_TOKEN).
1. Enregistrer une arrivée :
curl -i -X POST "http://localhost:5000/api/v1/workplace-presence" \
-H "Authorization: Bearer $WORKLOG_API_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Idempotency-Key: arrival-2026-08-13-01" \
-d '{
"event": "arrival",
"occurred_at": "2026-08-13T08:00:00+02:00"
}'
2. Enregistrer un départ :
curl -i -X POST "http://localhost:5000/api/v1/workplace-presence" \
-H "Authorization: Bearer $WORKLOG_API_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Idempotency-Key: departure-2026-08-13-01" \
-d '{
"event": "departure",
"occurred_at": "2026-08-13T17:00:00+02:00"
}'
12. Périmètre et Limites Connues
- Fonctionnalités non implémentées :
- Pas de support CORS (Cross-Origin Resource Sharing) pour des appels directs depuis un navigateur web.
- Pas de rotation automatique des jetons d'API.
- Pas de limite de débit distribuée (le rate-limit est confiné à chaque processus/worker Gunicorn).
- Pas d'import CSV en masse via l'API REST.
- Exemple Home Assistant : La configuration d'automatisation Home Assistant (YAML) n'est pas documentée dans ce fichier (cette documentation fait l'objet d'une étape ultérieure).