Breakout Status Webhook
Endpoint standalone permettant à des systèmes externes d'interroger l'état du breakout pour un ou plusieurs dictionnaires CSWeb.
Fichier :
breakout-status-webhook.php(déployé à la racine de CSWeb)
Authentification
Cet endpoint utilise un Bearer token statique (indépendant d'OAuth2), configuré via la variable d'environnement BREAKOUT_WEBHOOK_TOKEN.
Authorization: Bearer <token>Configuration du token :
# .env (à la racine du projet)
BREAKOUT_WEBHOOK_TOKEN=<32+ octets aléatoires>Important : la variable
BREAKOUT_WEBHOOK_TOKENest obligatoire. Si elle n'est pas définie, l'endpoint répond500 server_misconfigured. Aucune valeur par défaut n'est codée dans les webhooks.
Pour générer un token solide :
openssl rand -hex 32Endpoint
GET /breakout-status-webhook.phpParamètres de requête
| Paramètre | Type | Description |
|---|---|---|
dictionary | string | Filtrer par nom exact d'un dictionnaire (ex: KATS_DICT) |
dictionaries | string | Filtrer par plusieurs dictionnaires séparés par virgule (limité à 100 entrées) |
cron_enabled | boolean | true = seulement ceux avec cron actif |
breakout_configured | boolean | true = seulement ceux avec breakout configuré |
page | integer | Numéro de page (défaut: 1) |
limit | integer | Résultats par page, max 100 (défaut: 20) |
Exemples de requêtes
Tous les dictionnaires (paginé)
curl -X GET "http://localhost:8080/breakout-status-webhook.php" \
-H "Authorization: Bearer $BREAKOUT_WEBHOOK_TOKEN"Un seul dictionnaire
curl -X GET "http://localhost:8080/breakout-status-webhook.php?dictionary=KATS_DICT" \
-H "Authorization: Bearer $BREAKOUT_WEBHOOK_TOKEN"Plusieurs dictionnaires
curl -X GET "http://localhost:8080/breakout-status-webhook.php?dictionaries=KATS_DICT,EVAL_DICT" \
-H "Authorization: Bearer $BREAKOUT_WEBHOOK_TOKEN"Uniquement ceux avec breakout configuré et cron actif
curl -X GET "http://localhost:8080/breakout-status-webhook.php?breakout_configured=true&cron_enabled=true" \
-H "Authorization: Bearer $BREAKOUT_WEBHOOK_TOKEN"Pagination : page 2, 50 résultats
curl -X GET "http://localhost:8080/breakout-status-webhook.php?page=2&limit=50" \
-H "Authorization: Bearer $BREAKOUT_WEBHOOK_TOKEN"Format de réponse
Toutes les réponses des webhooks suivent le contrat unifié :
{ "success": true, "data": ... | null, "error": null, "meta": {...}? }
{ "success": false, "data": null, "error": { "code": "...", "message": "..." } }Succès (200 OK)
{
"success": true,
"data": [
{
"dictionary": "KATS_DICT",
"label": "Enquête KATS 2025",
"breakout_configured": true,
"target_host": "host.docker.internal:5433",
"target_schema": "kats_breakout",
"db_type": "postgresql",
"cron_enabled": true,
"cron_expression": "*/30 * * * *",
"last_run": "2026-04-19 08:30:00",
"next_run": "2026-04-19 09:00:00",
"last_exit_code": 0,
"total_cases": 250,
"processed_cases": 248,
"cases_pending": 2,
"completion_rate": 99.2,
"is_up_to_date": false,
"last_processed_time": "2026-04-19 08:29:45"
},
{
"dictionary": "EVAL_DICT",
"label": "Évaluation terrain",
"breakout_configured": false,
"target_host": null,
"target_schema": null,
"db_type": null,
"cron_enabled": false,
"cron_expression": null,
"last_run": null,
"next_run": null,
"last_exit_code": null,
"total_cases": 120,
"processed_cases": null,
"cases_pending": null,
"completion_rate": null,
"is_up_to_date": null,
"last_processed_time": null
}
],
"error": null,
"meta": {
"total": 3,
"page": 1,
"pages": 1,
"limit": 20
}
}Description des champs
meta (pagination)
| Champ | Type | Description |
|---|---|---|
total | integer | Nombre total de dictionnaires |
page | integer | Page courante |
pages | integer | Nombre total de pages |
limit | integer | Taille de page utilisée |
Items de data
| Champ | Type | Description |
|---|---|---|
dictionary | string | Nom du dictionnaire (ex: KATS_DICT) |
label | string | Libellé du dictionnaire |
breakout_configured | boolean | true si une DB cible est configurée |
target_host | string | null | Hôte de la DB cible (host:port) |
target_schema | string | null | Nom de la base/schéma cible |
db_type | string | null | Type de DB cible : postgresql, mysql, sqlserver |
cron_enabled | boolean | true si le scheduler automatique est activé |
cron_expression | string | null | Expression cron du scheduler (ex: */30 * * * *) |
last_run | string | null | Dernière exécution du cron (YYYY-MM-DD HH:MM:SS) |
next_run | string | null | Prochaine exécution planifiée |
last_exit_code | integer | null | Code de sortie du dernier cron (0 = succès, -1 = en cours) |
total_cases | integer | Nombre de questionnaires actifs dans CSWeb (source) |
processed_cases | integer | null | Questionnaires présents dans la DB cible (null si inaccessible) |
cases_pending | integer | null | total_cases - processed_cases (questionnaires non encore breakés) |
completion_rate | float | null | Taux de complétion en % (processed_cases / total_cases × 100) |
is_up_to_date | boolean | null | true si cases_pending === 0 |
last_processed_time | string | null | Horodatage du dernier job traité dans la DB cible |
Note : les champs
processed_cases,cases_pending,completion_rate,is_up_to_dateetlast_processed_timesontnullsi le breakout n'est pas configuré ou si la DB cible est inaccessible au moment de la requête.
Codes d'erreur
Toutes les erreurs renvoient success: false, data: null et un objet error :
{
"success": false,
"data": null,
"error": { "code": "<identifiant>", "message": "<description humaine>" }
}| HTTP | error.code | Cause |
|---|---|---|
| 401 | missing_token | Header Authorization: Bearer <token> absent ou mal formé |
| 401 | invalid_token | Token reçu ne correspond pas à BREAKOUT_WEBHOOK_TOKEN |
| 405 | method_not_allowed | Verbe HTTP autre que GET |
| 429 | rate_limited | 60 requêtes / 60 s dépassées (header Retry-After fourni) |
| 500 | server_misconfigured | BREAKOUT_WEBHOOK_TOKEN non défini ou config.php absent |
| 500 | internal_error | Erreur DB ou exception non spécifique |
Exemple 401 (missing_token) :
{
"success": false,
"data": null,
"error": {
"code": "missing_token",
"message": "Missing or invalid Authorization header. Expected: Bearer <token>."
}
}Exemple 429 (rate_limited) :
{
"success": false,
"data": null,
"error": {
"code": "rate_limited",
"message": "Too many requests. Retry after 42 seconds."
}
}Interprétation des résultats
Évaluer si un dictionnaire est à jour
is_up_to_date = true → Tous les questionnaires ont été breakés
is_up_to_date = false → cases_pending > 0, le breakout a du retard
is_up_to_date = null → Breakout non configuré ou DB cible inaccessibleSurveiller la santé du cron
cron_enabled = true + last_exit_code = 0 → Cron actif et dernière exécution OK
cron_enabled = true + last_exit_code = -1 → Breakout en cours d'exécution
cron_enabled = true + last_exit_code ≠ 0 → Erreur lors du dernier run
cron_enabled = false → Breakout manuel uniquementExemple de logique de décision (Python)
import os, requests
token = os.environ["BREAKOUT_WEBHOOK_TOKEN"]
response = requests.get(
"http://csweb.example.com/breakout-status-webhook.php",
headers={"Authorization": f"Bearer {token}"},
params={"breakout_configured": "true", "cron_enabled": "true"},
)
payload = response.json()
if not payload["success"]:
err = payload["error"]
raise SystemExit(f"[{err['code']}] {err['message']}")
print(f"Page {payload['meta']['page']}/{payload['meta']['pages']} "
f"({payload['meta']['total']} dictionnaires)")
for d in payload["data"]:
name = d["dictionary"]
rate = d["completion_rate"]
pending = d["cases_pending"]
exit_code = d["last_exit_code"]
if not d["is_up_to_date"]:
print(f"[ALERTE] {name}: {pending} questionnaires en attente ({rate}% traités)")
if exit_code is not None and exit_code != 0 and exit_code != -1:
print(f"[ERREUR] {name}: dernier cron en échec (exit code {exit_code})")Limites et garde-fous
- Body POST max : non applicable (endpoint GET).
- Rate-limit : 60 requêtes par fenêtre glissante de 60 secondes par couple (token, IP). Au-delà :
429avec headerRetry-After. - Liste
dictionaries: capée à 100 entrées côté serveur pour borner la requête SQL.
Déploiement
Le fichier breakout-status-webhook.php est déployé à la racine de l'application CSWeb (même niveau que index.php). Il se connecte à la base MySQL de CSWeb via le fichier src/AppBundle/config.php.
Variables d'environnement requises
# docker-compose.yml
environment:
BREAKOUT_WEBHOOK_TOKEN: "${BREAKOUT_WEBHOOK_TOKEN}"
CSWEB_ROOT: "/var/www/html"Générer un token sécurisé
# Linux / macOS
openssl rand -hex 32
# PHP
php -r "echo bin2hex(random_bytes(32));"Ressources
- Breakout API : API - Breakout
- OAuth Authentication : API - OAuth2
- Webhooks API : API - Webhooks
CSWeb Community Platform v2.0 - Breakout Status Webhook Architecte : Bouna DRAME | Portfolio (opens in a new tab)