EC06 attendait une reponse a « comment savez-vous que le modele se degrade ? ». Elle n'existait nulle part : `docs/architecture/00-vue-ensemble.md` et `docs/ML-START.md` le disaient tous les deux. Le calcul vit dans le backend, et `ml/` ne gagne pas une ligne. Trois raisons : `prediction` n'est pas dans le perimetre de lecture que `ML_DATABASE_URL` vise (ADR 0003 et ML-START le bornent a `reading` et `site`) ; l'alignement prevu contre realise existe deja une fois ici, dans `AlertService._detect_anomaly`, et le dupliquer en SQL brut creerait une seconde source de verite, ce que l'ADR 0006 refuse ; et FastAPI continue de ne jamais faire tourner LightGBM. Ce qui est mesure : la jointure `prediction` x `reading` sur `(site_id, target_at)`, avec un `DISTINCT ON` des deux cotes. Les runs de scoring s'empilent volontairement, et `uq_reading_source` autorise deux lectures au meme instant quand la source differe : sans ce dedoublonnage, la meme heure pesait plusieurs fois dans la moyenne. La fenetre est fermee a droite par un delai de grace, sinon la derniere heure, dont le realise n'est pas encore ingere, ferait chuter la couverture a chaque execution. Le verdict a trois valeurs, pas deux : avec trois points on ne declare pas une derive, on dit qu'on ne sait pas. La comparaison se fait entre deux fenetres vives de meme duree, jamais contre la metrique loguee a l'entrainement : celle-ci mesure un backtest a meteo connue, le scoring prevoit une heure dont la meteo ne l'est pas. `drift_report` porte une ligne par site plus une ligne globale, que `site_id` a NULL designe. L'idempotence passe par un index a `coalesce` et non par une contrainte d'unicite, sans quoi deux lignes globales ne seraient jamais egales.
3549 lines
98 KiB
JSON
3549 lines
98 KiB
JSON
{
|
|
"openapi": "3.1.0",
|
|
"info": {
|
|
"title": "EnerVision API",
|
|
"summary": "Collecte, analyse et restitution de séries temporelles énergétiques.",
|
|
"description": "\nToutes les routes sont préfixées par `/api/v1`.\n\n**Authentification.** Le jeton d'accès se présente dans l'en-tête `Authorization: Bearer ...`.\nLe jeton de rafraîchissement est un cookie `HttpOnly` que le code client ne voit jamais : il\nsuffit d'émettre les requêtes avec les identifiants de session. `POST /auth/refresh` rend un\nnouveau jeton d'accès et fait tourner le cookie.\n\n**Rôles.** `lecteur`, puis `operateur`, puis `admin`. Chaque rôle couvre les droits du\nprécédent.\n\n**Erreurs.** Le corps porte toujours une clé `detail`. Un `403` dont le `detail` vaut\n`password_change_required` n'est pas un refus de droits : il exige le changement du mot de passe\nprovisoire avant toute autre action.\n\nLe parcours de session complet est décrit dans\n`docs/architecture/31-contrat-authentification.md`.\n",
|
|
"version": "0.1.0",
|
|
"x-logo": {
|
|
"url": "/static/logo-icon.png",
|
|
"altText": "EnerVision"
|
|
}
|
|
},
|
|
"paths": {
|
|
"/api/v1/health/live": {
|
|
"get": {
|
|
"tags": [
|
|
"health"
|
|
],
|
|
"summary": "Sonde de vivacité",
|
|
"operationId": "liveness_api_v1_health_live_get",
|
|
"responses": {
|
|
"200": {
|
|
"description": "Successful Response",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/LivenessStatus"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Erreur interne. `correlation` identifie la trace côté serveur, qui n'est pas renvoyée au client.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/InternalErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/v1/health/ready": {
|
|
"get": {
|
|
"tags": [
|
|
"health"
|
|
],
|
|
"summary": "Sonde de disponibilité",
|
|
"operationId": "readiness_api_v1_health_ready_get",
|
|
"responses": {
|
|
"200": {
|
|
"description": "Successful Response",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ReadinessStatus"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Erreur interne. `correlation` identifie la trace côté serveur, qui n'est pas renvoyée au client.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/InternalErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"503": {
|
|
"description": "Base injoignable, ou extension TimescaleDB absente de la base.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/v1/auth/login": {
|
|
"post": {
|
|
"tags": [
|
|
"auth"
|
|
],
|
|
"summary": "Ouvre une session",
|
|
"operationId": "login_api_v1_auth_login_post",
|
|
"requestBody": {
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/LoginRequest"
|
|
}
|
|
}
|
|
},
|
|
"required": true
|
|
},
|
|
"responses": {
|
|
"200": {
|
|
"description": "Successful Response",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/TokenResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Erreur interne. `correlation` identifie la trace côté serveur, qui n'est pas renvoyée au client.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/InternalErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"422": {
|
|
"description": "Corps invalide. Le détail nomme le champ fautif et le type d'erreur, jamais la valeur envoyée.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ValidationErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Identifiants faux, compte inconnu ou compte désactivé. Le message est le même dans les trois cas, et n'apprend donc rien sur l'existence du compte.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"429": {
|
|
"description": "Trop de tentatives sur cette fenêtre glissante.",
|
|
"headers": {
|
|
"Retry-After": {
|
|
"description": "Secondes à attendre avant une nouvelle tentative.",
|
|
"schema": {
|
|
"type": "integer"
|
|
}
|
|
}
|
|
},
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/v1/auth/refresh": {
|
|
"post": {
|
|
"tags": [
|
|
"auth"
|
|
],
|
|
"summary": "Fait tourner la session",
|
|
"operationId": "refresh_api_v1_auth_refresh_post",
|
|
"responses": {
|
|
"200": {
|
|
"description": "Successful Response",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/TokenResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Erreur interne. `correlation` identifie la trace côté serveur, qui n'est pas renvoyée au client.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/InternalErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "Origine non autorisée (protection CSRF de `require_trusted_origin`).",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Cookie absent, session expirée, révoquée, ou jeton déjà tourné. Dans ce dernier cas toute la famille de sessions est révoquée et le cookie est effacé avec la réponse.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"Cookie de rafraîchissement": []
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"/api/v1/auth/logout": {
|
|
"post": {
|
|
"tags": [
|
|
"auth"
|
|
],
|
|
"summary": "Ferme la session courante",
|
|
"operationId": "logout_api_v1_auth_logout_post",
|
|
"responses": {
|
|
"204": {
|
|
"description": "Successful Response"
|
|
},
|
|
"500": {
|
|
"description": "Erreur interne. `correlation` identifie la trace côté serveur, qui n'est pas renvoyée au client.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/InternalErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "Origine non autorisée (protection CSRF de `require_trusted_origin`).",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"Cookie de rafraîchissement": []
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"/api/v1/auth/logout-all": {
|
|
"post": {
|
|
"tags": [
|
|
"auth"
|
|
],
|
|
"summary": "Ferme toutes les sessions du compte",
|
|
"operationId": "logout_all_api_v1_auth_logout_all_post",
|
|
"responses": {
|
|
"204": {
|
|
"description": "Successful Response"
|
|
},
|
|
"500": {
|
|
"description": "Erreur interne. `correlation` identifie la trace côté serveur, qui n'est pas renvoyée au client.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/InternalErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Jeton absent, illisible, périmé, ou rendu caduc par un changement de rôle ou une désactivation. L'en-tête `WWW-Authenticate` porte la cause dans `error=`.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "Origine non autorisée (protection CSRF de `require_trusted_origin`).",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"Jeton d'accès": []
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"/api/v1/auth/me": {
|
|
"get": {
|
|
"tags": [
|
|
"auth"
|
|
],
|
|
"summary": "Décrit le compte connecté",
|
|
"operationId": "me_api_v1_auth_me_get",
|
|
"responses": {
|
|
"200": {
|
|
"description": "Successful Response",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/PrincipalResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Erreur interne. `correlation` identifie la trace côté serveur, qui n'est pas renvoyée au client.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/InternalErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Jeton absent, illisible, périmé, ou rendu caduc par un changement de rôle ou une désactivation. L'en-tête `WWW-Authenticate` porte la cause dans `error=`.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"Jeton d'accès": []
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"/api/v1/auth/password": {
|
|
"post": {
|
|
"tags": [
|
|
"auth"
|
|
],
|
|
"summary": "Change son propre mot de passe",
|
|
"operationId": "change_password_api_v1_auth_password_post",
|
|
"requestBody": {
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/PasswordChangeRequest"
|
|
}
|
|
}
|
|
},
|
|
"required": true
|
|
},
|
|
"responses": {
|
|
"200": {
|
|
"description": "Successful Response",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/TokenResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Erreur interne. `correlation` identifie la trace côté serveur, qui n'est pas renvoyée au client.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/InternalErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"422": {
|
|
"description": "Corps invalide. Le détail nomme le champ fautif et le type d'erreur, jamais la valeur envoyée.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ValidationErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "Origine non autorisée (protection CSRF de `require_trusted_origin`).",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Jeton d'accès invalide, ou mot de passe courant faux.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"Jeton d'accès": []
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"/api/v1/auth/forgot-password": {
|
|
"post": {
|
|
"tags": [
|
|
"auth"
|
|
],
|
|
"summary": "Demande un lien de réinitialisation par email",
|
|
"operationId": "forgot_password_api_v1_auth_forgot_password_post",
|
|
"requestBody": {
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ForgotPasswordRequest"
|
|
}
|
|
}
|
|
},
|
|
"required": true
|
|
},
|
|
"responses": {
|
|
"202": {
|
|
"description": "Successful Response",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Erreur interne. `correlation` identifie la trace côté serveur, qui n'est pas renvoyée au client.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/InternalErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"422": {
|
|
"description": "Corps invalide. Le détail nomme le champ fautif et le type d'erreur, jamais la valeur envoyée.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ValidationErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"429": {
|
|
"description": "Trop de demandes sur cette fenêtre glissante.",
|
|
"headers": {
|
|
"Retry-After": {
|
|
"description": "Secondes à attendre avant une nouvelle tentative.",
|
|
"schema": {
|
|
"type": "integer"
|
|
}
|
|
}
|
|
},
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/v1/auth/reset-password/validate": {
|
|
"get": {
|
|
"tags": [
|
|
"auth"
|
|
],
|
|
"summary": "Vérifie sans le consommer si un lien de réinitialisation est encore valide",
|
|
"operationId": "validate_reset_token_api_v1_auth_reset_password_validate_get",
|
|
"parameters": [
|
|
{
|
|
"name": "token",
|
|
"in": "query",
|
|
"required": true,
|
|
"schema": {
|
|
"type": "string",
|
|
"title": "Token"
|
|
}
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "Successful Response",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ResetTokenValidationResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Erreur interne. `correlation` identifie la trace côté serveur, qui n'est pas renvoyée au client.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/InternalErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"422": {
|
|
"description": "Corps invalide. Le détail nomme le champ fautif et le type d'erreur, jamais la valeur envoyée.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ValidationErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/v1/auth/reset-password": {
|
|
"post": {
|
|
"tags": [
|
|
"auth"
|
|
],
|
|
"summary": "Choisit un nouveau mot de passe depuis un lien reçu par email",
|
|
"operationId": "reset_password_api_v1_auth_reset_password_post",
|
|
"requestBody": {
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ResetPasswordRequest"
|
|
}
|
|
}
|
|
},
|
|
"required": true
|
|
},
|
|
"responses": {
|
|
"200": {
|
|
"description": "Successful Response",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/TokenResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Erreur interne. `correlation` identifie la trace côté serveur, qui n'est pas renvoyée au client.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/InternalErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"422": {
|
|
"description": "Corps invalide. Le détail nomme le champ fautif et le type d'erreur, jamais la valeur envoyée.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ValidationErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "Origine non autorisée (protection CSRF de `require_trusted_origin`).",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Lien invalide, déjà utilisé, ou expiré (durée de vie : 15 minutes).",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/v1/users": {
|
|
"get": {
|
|
"tags": [
|
|
"users"
|
|
],
|
|
"summary": "Liste les comptes",
|
|
"operationId": "list_users_api_v1_users_get",
|
|
"responses": {
|
|
"200": {
|
|
"description": "Successful Response",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"items": {
|
|
"$ref": "#/components/schemas/UserResponse"
|
|
},
|
|
"type": "array",
|
|
"title": "Response List Users Api V1 Users Get"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Erreur interne. `correlation` identifie la trace côté serveur, qui n'est pas renvoyée au client.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/InternalErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Jeton absent, illisible, périmé, ou rendu caduc par un changement de rôle ou une désactivation. L'en-tête `WWW-Authenticate` porte la cause dans `error=`.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "Droits insuffisants, ou mot de passe provisoire à changer quand `detail` vaut `password_change_required`.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"Jeton d'accès": []
|
|
}
|
|
]
|
|
},
|
|
"post": {
|
|
"tags": [
|
|
"users"
|
|
],
|
|
"summary": "Crée un compte avec un mot de passe provisoire",
|
|
"operationId": "create_user_api_v1_users_post",
|
|
"requestBody": {
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/UserCreateRequest"
|
|
}
|
|
}
|
|
},
|
|
"required": true
|
|
},
|
|
"responses": {
|
|
"201": {
|
|
"description": "Successful Response",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/TemporaryPasswordResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Erreur interne. `correlation` identifie la trace côté serveur, qui n'est pas renvoyée au client.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/InternalErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Jeton absent, illisible, périmé, ou rendu caduc par un changement de rôle ou une désactivation. L'en-tête `WWW-Authenticate` porte la cause dans `error=`.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "Droits insuffisants, ou mot de passe provisoire à changer quand `detail` vaut `password_change_required`.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"422": {
|
|
"description": "Corps invalide. Le détail nomme le champ fautif et le type d'erreur, jamais la valeur envoyée.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ValidationErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"409": {
|
|
"description": "Adresse déjà portée par un autre compte.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"Jeton d'accès": []
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"/api/v1/users/{user_id}": {
|
|
"patch": {
|
|
"tags": [
|
|
"users"
|
|
],
|
|
"summary": "Change le rôle ou l'activation",
|
|
"operationId": "update_user_api_v1_users__user_id__patch",
|
|
"security": [
|
|
{
|
|
"Jeton d'accès": []
|
|
}
|
|
],
|
|
"parameters": [
|
|
{
|
|
"name": "user_id",
|
|
"in": "path",
|
|
"required": true,
|
|
"schema": {
|
|
"type": "string",
|
|
"format": "uuid",
|
|
"title": "User Id"
|
|
}
|
|
}
|
|
],
|
|
"requestBody": {
|
|
"required": true,
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/UserUpdateRequest"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"responses": {
|
|
"200": {
|
|
"description": "Successful Response",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/UserResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Erreur interne. `correlation` identifie la trace côté serveur, qui n'est pas renvoyée au client.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/InternalErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Jeton absent, illisible, périmé, ou rendu caduc par un changement de rôle ou une désactivation. L'en-tête `WWW-Authenticate` porte la cause dans `error=`.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "Droits insuffisants, ou mot de passe provisoire à changer quand `detail` vaut `password_change_required`.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"422": {
|
|
"description": "Corps invalide. Le détail nomme le champ fautif et le type d'erreur, jamais la valeur envoyée.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ValidationErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"404": {
|
|
"description": "Aucun compte ne porte cet identifiant.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Corps vide, aucune modification demandée.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"409": {
|
|
"description": "L'opération laisserait la plateforme sans administrateur actif, qu'il s'agisse de rétrograder le dernier ou de le désactiver.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/v1/users/{user_id}/password-reset": {
|
|
"post": {
|
|
"tags": [
|
|
"users"
|
|
],
|
|
"summary": "Réinitialise le mot de passe et ferme les sessions",
|
|
"operationId": "reset_password_api_v1_users__user_id__password_reset_post",
|
|
"security": [
|
|
{
|
|
"Jeton d'accès": []
|
|
}
|
|
],
|
|
"parameters": [
|
|
{
|
|
"name": "user_id",
|
|
"in": "path",
|
|
"required": true,
|
|
"schema": {
|
|
"type": "string",
|
|
"format": "uuid",
|
|
"title": "User Id"
|
|
}
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "Successful Response",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/TemporaryPasswordResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Erreur interne. `correlation` identifie la trace côté serveur, qui n'est pas renvoyée au client.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/InternalErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Jeton absent, illisible, périmé, ou rendu caduc par un changement de rôle ou une désactivation. L'en-tête `WWW-Authenticate` porte la cause dans `error=`.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "Droits insuffisants, ou mot de passe provisoire à changer quand `detail` vaut `password_change_required`.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"422": {
|
|
"description": "Corps invalide. Le détail nomme le champ fautif et le type d'erreur, jamais la valeur envoyée.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ValidationErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"404": {
|
|
"description": "Aucun compte ne porte cet identifiant.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/v1/sites": {
|
|
"get": {
|
|
"tags": [
|
|
"sites"
|
|
],
|
|
"summary": "Liste les sites",
|
|
"operationId": "list_sites_api_v1_sites_get",
|
|
"responses": {
|
|
"200": {
|
|
"description": "Successful Response",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"items": {
|
|
"$ref": "#/components/schemas/SiteResponse"
|
|
},
|
|
"type": "array",
|
|
"title": "Response List Sites Api V1 Sites Get"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Erreur interne. `correlation` identifie la trace côté serveur, qui n'est pas renvoyée au client.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/InternalErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Jeton absent, illisible, périmé, ou rendu caduc par un changement de rôle ou une désactivation. L'en-tête `WWW-Authenticate` porte la cause dans `error=`.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "Mot de passe provisoire à changer (`detail` vaut `password_change_required`).",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"Jeton d'accès": []
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"/api/v1/sites/{site_id}": {
|
|
"get": {
|
|
"tags": [
|
|
"sites"
|
|
],
|
|
"summary": "Décrit un site",
|
|
"operationId": "get_site_api_v1_sites__site_id__get",
|
|
"security": [
|
|
{
|
|
"Jeton d'accès": []
|
|
}
|
|
],
|
|
"parameters": [
|
|
{
|
|
"name": "site_id",
|
|
"in": "path",
|
|
"required": true,
|
|
"schema": {
|
|
"type": "string",
|
|
"title": "Site Id"
|
|
}
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "Successful Response",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/SiteResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Erreur interne. `correlation` identifie la trace côté serveur, qui n'est pas renvoyée au client.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/InternalErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Jeton absent, illisible, périmé, ou rendu caduc par un changement de rôle ou une désactivation. L'en-tête `WWW-Authenticate` porte la cause dans `error=`.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "Mot de passe provisoire à changer (`detail` vaut `password_change_required`).",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"422": {
|
|
"description": "Corps invalide. Le détail nomme le champ fautif et le type d'erreur, jamais la valeur envoyée.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ValidationErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"404": {
|
|
"description": "Aucun site ne porte cet identifiant.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/v1/sites/{site_id}/current": {
|
|
"get": {
|
|
"tags": [
|
|
"sites"
|
|
],
|
|
"summary": "Dernière mesure d'un site",
|
|
"operationId": "get_current_api_v1_sites__site_id__current_get",
|
|
"security": [
|
|
{
|
|
"Jeton d'accès": []
|
|
}
|
|
],
|
|
"parameters": [
|
|
{
|
|
"name": "site_id",
|
|
"in": "path",
|
|
"required": true,
|
|
"schema": {
|
|
"type": "string",
|
|
"title": "Site Id"
|
|
}
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "Successful Response",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/SiteCurrentResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Erreur interne. `correlation` identifie la trace côté serveur, qui n'est pas renvoyée au client.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/InternalErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Jeton absent, illisible, périmé, ou rendu caduc par un changement de rôle ou une désactivation. L'en-tête `WWW-Authenticate` porte la cause dans `error=`.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "Mot de passe provisoire à changer (`detail` vaut `password_change_required`).",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"422": {
|
|
"description": "Corps invalide. Le détail nomme le champ fautif et le type d'erreur, jamais la valeur envoyée.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ValidationErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"404": {
|
|
"description": "Aucun site ne porte cet identifiant.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/v1/alerts": {
|
|
"get": {
|
|
"tags": [
|
|
"alerts"
|
|
],
|
|
"summary": "Liste les alertes",
|
|
"operationId": "list_alerts_api_v1_alerts_get",
|
|
"security": [
|
|
{
|
|
"Jeton d'accès": []
|
|
}
|
|
],
|
|
"parameters": [
|
|
{
|
|
"name": "site_id",
|
|
"in": "query",
|
|
"required": false,
|
|
"schema": {
|
|
"anyOf": [
|
|
{
|
|
"type": "string"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
],
|
|
"title": "Site Id"
|
|
}
|
|
},
|
|
{
|
|
"name": "severity",
|
|
"in": "query",
|
|
"required": false,
|
|
"schema": {
|
|
"anyOf": [
|
|
{
|
|
"$ref": "#/components/schemas/AlertSeverity"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
],
|
|
"title": "Severity"
|
|
}
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "Successful Response",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"type": "array",
|
|
"items": {
|
|
"$ref": "#/components/schemas/AlertResponse"
|
|
},
|
|
"title": "Response List Alerts Api V1 Alerts Get"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Erreur interne. `correlation` identifie la trace côté serveur, qui n'est pas renvoyée au client.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/InternalErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Jeton absent, illisible, périmé, ou rendu caduc par un changement de rôle ou une désactivation. L'en-tête `WWW-Authenticate` porte la cause dans `error=`.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "Mot de passe provisoire à changer (`detail` vaut `password_change_required`).",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"422": {
|
|
"description": "Corps invalide. Le détail nomme le champ fautif et le type d'erreur, jamais la valeur envoyée.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ValidationErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/v1/recommendations": {
|
|
"get": {
|
|
"tags": [
|
|
"recommendations"
|
|
],
|
|
"summary": "Liste les recommandations",
|
|
"operationId": "list_recommendations_api_v1_recommendations_get",
|
|
"responses": {
|
|
"200": {
|
|
"description": "Successful Response",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"items": {
|
|
"$ref": "#/components/schemas/RecommendationResponse"
|
|
},
|
|
"type": "array",
|
|
"title": "Response List Recommendations Api V1 Recommendations Get"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Erreur interne. `correlation` identifie la trace côté serveur, qui n'est pas renvoyée au client.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/InternalErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Jeton absent, illisible, périmé, ou rendu caduc par un changement de rôle ou une désactivation. L'en-tête `WWW-Authenticate` porte la cause dans `error=`.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "Mot de passe provisoire à changer (`detail` vaut `password_change_required`).",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"Jeton d'accès": []
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"/api/v1/recommendations/{recommendation_id}": {
|
|
"get": {
|
|
"tags": [
|
|
"recommendations"
|
|
],
|
|
"summary": "Décrit une recommandation",
|
|
"operationId": "get_recommendation_api_v1_recommendations__recommendation_id__get",
|
|
"security": [
|
|
{
|
|
"Jeton d'accès": []
|
|
}
|
|
],
|
|
"parameters": [
|
|
{
|
|
"name": "recommendation_id",
|
|
"in": "path",
|
|
"required": true,
|
|
"schema": {
|
|
"type": "integer",
|
|
"title": "Recommendation Id"
|
|
}
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "Successful Response",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/RecommendationResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Erreur interne. `correlation` identifie la trace côté serveur, qui n'est pas renvoyée au client.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/InternalErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Jeton absent, illisible, périmé, ou rendu caduc par un changement de rôle ou une désactivation. L'en-tête `WWW-Authenticate` porte la cause dans `error=`.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "Mot de passe provisoire à changer (`detail` vaut `password_change_required`).",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"422": {
|
|
"description": "Corps invalide. Le détail nomme le champ fautif et le type d'erreur, jamais la valeur envoyée.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ValidationErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"404": {
|
|
"description": "Aucune recommandation ne porte cet identifiant.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/v1/recommendations/generate": {
|
|
"post": {
|
|
"tags": [
|
|
"recommendations"
|
|
],
|
|
"summary": "Génère les recommandations à partir des alertes",
|
|
"operationId": "generate_recommendations_api_v1_recommendations_generate_post",
|
|
"security": [
|
|
{
|
|
"Jeton d'accès": []
|
|
}
|
|
],
|
|
"parameters": [
|
|
{
|
|
"name": "site_id",
|
|
"in": "query",
|
|
"required": false,
|
|
"schema": {
|
|
"anyOf": [
|
|
{
|
|
"type": "string"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
],
|
|
"title": "Site Id"
|
|
}
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "Successful Response",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/RecommendationGenerationResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Erreur interne. `correlation` identifie la trace côté serveur, qui n'est pas renvoyée au client.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/InternalErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Jeton absent, illisible, périmé, ou rendu caduc par un changement de rôle ou une désactivation. L'en-tête `WWW-Authenticate` porte la cause dans `error=`.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "Droits insuffisants, ou mot de passe provisoire à changer quand `detail` vaut `password_change_required`.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"422": {
|
|
"description": "Corps invalide. Le détail nomme le champ fautif et le type d'erreur, jamais la valeur envoyée.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ValidationErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/v1/stats/summary": {
|
|
"get": {
|
|
"tags": [
|
|
"stats"
|
|
],
|
|
"summary": "Résume la consommation instantanée du parc",
|
|
"operationId": "get_summary_api_v1_stats_summary_get",
|
|
"responses": {
|
|
"200": {
|
|
"description": "Successful Response",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/StatsSummaryResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Erreur interne. `correlation` identifie la trace côté serveur, qui n'est pas renvoyée au client.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/InternalErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Jeton absent, illisible, périmé, ou rendu caduc par un changement de rôle ou une désactivation. L'en-tête `WWW-Authenticate` porte la cause dans `error=`.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "Mot de passe provisoire à changer (`detail` vaut `password_change_required`).",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"Jeton d'accès": []
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"/api/v1/readings": {
|
|
"get": {
|
|
"tags": [
|
|
"readings"
|
|
],
|
|
"summary": "Liste l'historique des lectures",
|
|
"operationId": "list_readings_api_v1_readings_get",
|
|
"security": [
|
|
{
|
|
"Jeton d'accès": []
|
|
}
|
|
],
|
|
"parameters": [
|
|
{
|
|
"name": "site_id",
|
|
"in": "query",
|
|
"required": false,
|
|
"schema": {
|
|
"anyOf": [
|
|
{
|
|
"type": "string"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
],
|
|
"title": "Site Id"
|
|
}
|
|
},
|
|
{
|
|
"name": "start",
|
|
"in": "query",
|
|
"required": false,
|
|
"schema": {
|
|
"anyOf": [
|
|
{
|
|
"type": "string",
|
|
"format": "date-time"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
],
|
|
"title": "Start"
|
|
}
|
|
},
|
|
{
|
|
"name": "end",
|
|
"in": "query",
|
|
"required": false,
|
|
"schema": {
|
|
"anyOf": [
|
|
{
|
|
"type": "string",
|
|
"format": "date-time"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
],
|
|
"title": "End"
|
|
}
|
|
},
|
|
{
|
|
"name": "limit",
|
|
"in": "query",
|
|
"required": false,
|
|
"schema": {
|
|
"type": "integer",
|
|
"maximum": 2000,
|
|
"minimum": 1,
|
|
"default": 500,
|
|
"title": "Limit"
|
|
}
|
|
},
|
|
{
|
|
"name": "offset",
|
|
"in": "query",
|
|
"required": false,
|
|
"schema": {
|
|
"type": "integer",
|
|
"minimum": 0,
|
|
"default": 0,
|
|
"title": "Offset"
|
|
}
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "Successful Response",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"type": "array",
|
|
"items": {
|
|
"$ref": "#/components/schemas/ReadingResponse"
|
|
},
|
|
"title": "Response List Readings Api V1 Readings Get"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Erreur interne. `correlation` identifie la trace côté serveur, qui n'est pas renvoyée au client.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/InternalErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Jeton absent, illisible, périmé, ou rendu caduc par un changement de rôle ou une désactivation. L'en-tête `WWW-Authenticate` porte la cause dans `error=`.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "Mot de passe provisoire à changer (`detail` vaut `password_change_required`).",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"422": {
|
|
"description": "Corps invalide. Le détail nomme le champ fautif et le type d'erreur, jamais la valeur envoyée.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ValidationErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Fenêtre temporelle invalide : `start` postérieur ou égal à `end`, ou écart entre les deux supérieur à 90 jours.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/v1/sensors/status": {
|
|
"get": {
|
|
"tags": [
|
|
"sensors"
|
|
],
|
|
"summary": "État de santé des capteurs par site",
|
|
"operationId": "get_status_api_v1_sensors_status_get",
|
|
"responses": {
|
|
"200": {
|
|
"description": "Successful Response",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/SensorStatusResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Erreur interne. `correlation` identifie la trace côté serveur, qui n'est pas renvoyée au client.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/InternalErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Jeton absent, illisible, périmé, ou rendu caduc par un changement de rôle ou une désactivation. L'en-tête `WWW-Authenticate` porte la cause dans `error=`.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "Droits insuffisants, ou mot de passe provisoire à changer quand `detail` vaut `password_change_required`.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"Jeton d'accès": []
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"/api/v1/predictions": {
|
|
"get": {
|
|
"tags": [
|
|
"predictions"
|
|
],
|
|
"summary": "Dernière prédiction de consommation par site",
|
|
"operationId": "get_predictions_api_v1_predictions_get",
|
|
"responses": {
|
|
"200": {
|
|
"description": "Successful Response",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/PredictionSummaryResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Erreur interne. `correlation` identifie la trace côté serveur, qui n'est pas renvoyée au client.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/InternalErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Jeton absent, illisible, périmé, ou rendu caduc par un changement de rôle ou une désactivation. L'en-tête `WWW-Authenticate` porte la cause dans `error=`.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "Mot de passe provisoire à changer (`detail` vaut `password_change_required`).",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"security": [
|
|
{
|
|
"Jeton d'accès": []
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"/api/v1/monitoring/drift": {
|
|
"get": {
|
|
"tags": [
|
|
"monitoring"
|
|
],
|
|
"summary": "Dernier rapport de dérive par site, plus la ligne globale",
|
|
"operationId": "get_drift_api_v1_monitoring_drift_get",
|
|
"security": [
|
|
{
|
|
"Jeton d'accès": []
|
|
}
|
|
],
|
|
"parameters": [
|
|
{
|
|
"name": "site_id",
|
|
"in": "query",
|
|
"required": false,
|
|
"schema": {
|
|
"anyOf": [
|
|
{
|
|
"type": "string"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
],
|
|
"title": "Site Id"
|
|
}
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "Successful Response",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"type": "array",
|
|
"items": {
|
|
"$ref": "#/components/schemas/DriftReportResponse"
|
|
},
|
|
"title": "Response Get Drift Api V1 Monitoring Drift Get"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Erreur interne. `correlation` identifie la trace côté serveur, qui n'est pas renvoyée au client.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/InternalErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"description": "Jeton absent, illisible, périmé, ou rendu caduc par un changement de rôle ou une désactivation. L'en-tête `WWW-Authenticate` porte la cause dans `error=`.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "Droits insuffisants, ou mot de passe provisoire à changer quand `detail` vaut `password_change_required`.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"422": {
|
|
"description": "Corps invalide. Le détail nomme le champ fautif et le type d'erreur, jamais la valeur envoyée.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ValidationErrorResponse"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"components": {
|
|
"schemas": {
|
|
"AccountKind": {
|
|
"type": "string",
|
|
"enum": [
|
|
"human",
|
|
"service"
|
|
],
|
|
"title": "AccountKind"
|
|
},
|
|
"AlertResponse": {
|
|
"properties": {
|
|
"alert_id": {
|
|
"type": "integer",
|
|
"title": "Alert Id"
|
|
},
|
|
"site_id": {
|
|
"type": "string",
|
|
"title": "Site Id"
|
|
},
|
|
"timestamp": {
|
|
"type": "string",
|
|
"format": "date-time",
|
|
"title": "Timestamp"
|
|
},
|
|
"type": {
|
|
"$ref": "#/components/schemas/AlertType"
|
|
},
|
|
"severity": {
|
|
"$ref": "#/components/schemas/AlertSeverity"
|
|
},
|
|
"message": {
|
|
"type": "string",
|
|
"title": "Message"
|
|
},
|
|
"value": {
|
|
"anyOf": [
|
|
{
|
|
"type": "number"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
],
|
|
"title": "Value"
|
|
},
|
|
"threshold": {
|
|
"anyOf": [
|
|
{
|
|
"type": "number"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
],
|
|
"title": "Threshold"
|
|
},
|
|
"metric": {
|
|
"anyOf": [
|
|
{
|
|
"type": "string"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
],
|
|
"title": "Metric"
|
|
},
|
|
"prediction_id": {
|
|
"anyOf": [
|
|
{
|
|
"type": "integer"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
],
|
|
"title": "Prediction Id"
|
|
}
|
|
},
|
|
"type": "object",
|
|
"required": [
|
|
"alert_id",
|
|
"site_id",
|
|
"timestamp",
|
|
"type",
|
|
"severity",
|
|
"message",
|
|
"value",
|
|
"threshold",
|
|
"metric",
|
|
"prediction_id"
|
|
],
|
|
"title": "AlertResponse"
|
|
},
|
|
"AlertSeverity": {
|
|
"type": "string",
|
|
"enum": [
|
|
"low",
|
|
"medium",
|
|
"high",
|
|
"critical"
|
|
],
|
|
"title": "AlertSeverity"
|
|
},
|
|
"AlertType": {
|
|
"type": "string",
|
|
"enum": [
|
|
"spike",
|
|
"threshold",
|
|
"anomaly",
|
|
"outage",
|
|
"sensor"
|
|
],
|
|
"title": "AlertType"
|
|
},
|
|
"DriftReportResponse": {
|
|
"properties": {
|
|
"site_id": {
|
|
"anyOf": [
|
|
{
|
|
"type": "string"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
],
|
|
"title": "Site Id"
|
|
},
|
|
"computed_at": {
|
|
"type": "string",
|
|
"format": "date-time",
|
|
"title": "Computed At"
|
|
},
|
|
"window_start": {
|
|
"type": "string",
|
|
"format": "date-time",
|
|
"title": "Window Start"
|
|
},
|
|
"window_end": {
|
|
"type": "string",
|
|
"format": "date-time",
|
|
"title": "Window End"
|
|
},
|
|
"reference_start": {
|
|
"anyOf": [
|
|
{
|
|
"type": "string",
|
|
"format": "date-time"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
],
|
|
"title": "Reference Start"
|
|
},
|
|
"reference_end": {
|
|
"anyOf": [
|
|
{
|
|
"type": "string",
|
|
"format": "date-time"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
],
|
|
"title": "Reference End"
|
|
},
|
|
"n_observations": {
|
|
"type": "integer",
|
|
"title": "N Observations"
|
|
},
|
|
"mae": {
|
|
"anyOf": [
|
|
{
|
|
"type": "number"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
],
|
|
"title": "Mae"
|
|
},
|
|
"mape": {
|
|
"anyOf": [
|
|
{
|
|
"type": "number"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
],
|
|
"title": "Mape"
|
|
},
|
|
"bias": {
|
|
"anyOf": [
|
|
{
|
|
"type": "number"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
],
|
|
"title": "Bias"
|
|
},
|
|
"reference_mae": {
|
|
"anyOf": [
|
|
{
|
|
"type": "number"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
],
|
|
"title": "Reference Mae"
|
|
},
|
|
"coverage_ratio": {
|
|
"anyOf": [
|
|
{
|
|
"type": "number"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
],
|
|
"title": "Coverage Ratio"
|
|
},
|
|
"insufficient_data_ratio": {
|
|
"anyOf": [
|
|
{
|
|
"type": "number"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
],
|
|
"title": "Insufficient Data Ratio"
|
|
},
|
|
"model_references": {
|
|
"items": {
|
|
"type": "string"
|
|
},
|
|
"type": "array",
|
|
"title": "Model References"
|
|
},
|
|
"status": {
|
|
"$ref": "#/components/schemas/DriftStatus"
|
|
},
|
|
"reason": {
|
|
"anyOf": [
|
|
{
|
|
"type": "string"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
],
|
|
"title": "Reason"
|
|
}
|
|
},
|
|
"type": "object",
|
|
"required": [
|
|
"site_id",
|
|
"computed_at",
|
|
"window_start",
|
|
"window_end",
|
|
"reference_start",
|
|
"reference_end",
|
|
"n_observations",
|
|
"mae",
|
|
"mape",
|
|
"bias",
|
|
"reference_mae",
|
|
"coverage_ratio",
|
|
"insufficient_data_ratio",
|
|
"model_references",
|
|
"status",
|
|
"reason"
|
|
],
|
|
"title": "DriftReportResponse"
|
|
},
|
|
"DriftStatus": {
|
|
"type": "string",
|
|
"enum": [
|
|
"stable",
|
|
"derive",
|
|
"indetermine"
|
|
],
|
|
"title": "DriftStatus"
|
|
},
|
|
"ErrorResponse": {
|
|
"properties": {
|
|
"detail": {
|
|
"type": "string",
|
|
"title": "Detail"
|
|
}
|
|
},
|
|
"type": "object",
|
|
"required": [
|
|
"detail"
|
|
],
|
|
"title": "ErrorResponse"
|
|
},
|
|
"FieldError": {
|
|
"properties": {
|
|
"champ": {
|
|
"type": "string",
|
|
"title": "Champ"
|
|
},
|
|
"type": {
|
|
"type": "string",
|
|
"title": "Type"
|
|
}
|
|
},
|
|
"type": "object",
|
|
"required": [
|
|
"champ",
|
|
"type"
|
|
],
|
|
"title": "FieldError"
|
|
},
|
|
"ForgotPasswordRequest": {
|
|
"properties": {
|
|
"email": {
|
|
"type": "string",
|
|
"format": "email",
|
|
"title": "Email"
|
|
}
|
|
},
|
|
"type": "object",
|
|
"required": [
|
|
"email"
|
|
],
|
|
"title": "ForgotPasswordRequest"
|
|
},
|
|
"InternalErrorResponse": {
|
|
"properties": {
|
|
"detail": {
|
|
"type": "string",
|
|
"title": "Detail"
|
|
},
|
|
"correlation": {
|
|
"type": "string",
|
|
"title": "Correlation"
|
|
}
|
|
},
|
|
"type": "object",
|
|
"required": [
|
|
"detail",
|
|
"correlation"
|
|
],
|
|
"title": "InternalErrorResponse"
|
|
},
|
|
"LivenessStatus": {
|
|
"properties": {
|
|
"status": {
|
|
"type": "string",
|
|
"const": "ok",
|
|
"title": "Status"
|
|
},
|
|
"service": {
|
|
"type": "string",
|
|
"title": "Service"
|
|
},
|
|
"version": {
|
|
"type": "string",
|
|
"title": "Version"
|
|
},
|
|
"environment": {
|
|
"type": "string",
|
|
"title": "Environment"
|
|
}
|
|
},
|
|
"type": "object",
|
|
"required": [
|
|
"status",
|
|
"service",
|
|
"version",
|
|
"environment"
|
|
],
|
|
"title": "LivenessStatus"
|
|
},
|
|
"LoginRequest": {
|
|
"properties": {
|
|
"email": {
|
|
"type": "string",
|
|
"format": "email",
|
|
"title": "Email"
|
|
},
|
|
"password": {
|
|
"type": "string",
|
|
"maxLength": 128,
|
|
"minLength": 1,
|
|
"title": "Password"
|
|
}
|
|
},
|
|
"type": "object",
|
|
"required": [
|
|
"email",
|
|
"password"
|
|
],
|
|
"title": "LoginRequest"
|
|
},
|
|
"PasswordChangeRequest": {
|
|
"properties": {
|
|
"current_password": {
|
|
"type": "string",
|
|
"maxLength": 128,
|
|
"minLength": 1,
|
|
"title": "Current Password"
|
|
},
|
|
"new_password": {
|
|
"type": "string",
|
|
"maxLength": 128,
|
|
"minLength": 8,
|
|
"title": "New Password"
|
|
}
|
|
},
|
|
"type": "object",
|
|
"required": [
|
|
"current_password",
|
|
"new_password"
|
|
],
|
|
"title": "PasswordChangeRequest"
|
|
},
|
|
"PredictionStatus": {
|
|
"type": "string",
|
|
"enum": [
|
|
"available",
|
|
"insufficient_data",
|
|
"error"
|
|
],
|
|
"title": "PredictionStatus"
|
|
},
|
|
"PredictionSummaryResponse": {
|
|
"properties": {
|
|
"timestamp": {
|
|
"type": "string",
|
|
"format": "date-time",
|
|
"title": "Timestamp"
|
|
},
|
|
"sites": {
|
|
"items": {
|
|
"$ref": "#/components/schemas/SitePredictionSummaryResponse"
|
|
},
|
|
"type": "array",
|
|
"title": "Sites"
|
|
}
|
|
},
|
|
"type": "object",
|
|
"required": [
|
|
"timestamp",
|
|
"sites"
|
|
],
|
|
"title": "PredictionSummaryResponse"
|
|
},
|
|
"PredictionTargetMetric": {
|
|
"type": "string",
|
|
"enum": [
|
|
"consumption_kwh",
|
|
"consumption_kw"
|
|
],
|
|
"title": "PredictionTargetMetric"
|
|
},
|
|
"PrincipalResponse": {
|
|
"properties": {
|
|
"id": {
|
|
"type": "string",
|
|
"format": "uuid",
|
|
"title": "Id"
|
|
},
|
|
"email": {
|
|
"type": "string",
|
|
"title": "Email"
|
|
},
|
|
"role": {
|
|
"$ref": "#/components/schemas/Role"
|
|
},
|
|
"kind": {
|
|
"$ref": "#/components/schemas/AccountKind"
|
|
},
|
|
"must_change_password": {
|
|
"type": "boolean",
|
|
"title": "Must Change Password"
|
|
}
|
|
},
|
|
"type": "object",
|
|
"required": [
|
|
"id",
|
|
"email",
|
|
"role",
|
|
"kind",
|
|
"must_change_password"
|
|
],
|
|
"title": "PrincipalResponse"
|
|
},
|
|
"ReadinessStatus": {
|
|
"properties": {
|
|
"status": {
|
|
"type": "string",
|
|
"const": "ready",
|
|
"title": "Status"
|
|
},
|
|
"database": {
|
|
"type": "string",
|
|
"const": "reachable",
|
|
"title": "Database"
|
|
},
|
|
"timescaledb": {
|
|
"type": "string",
|
|
"const": "loaded",
|
|
"title": "Timescaledb"
|
|
}
|
|
},
|
|
"type": "object",
|
|
"required": [
|
|
"status",
|
|
"database",
|
|
"timescaledb"
|
|
],
|
|
"title": "ReadinessStatus"
|
|
},
|
|
"ReadingDataQuality": {
|
|
"type": "string",
|
|
"enum": [
|
|
"good",
|
|
"partial",
|
|
"degraded",
|
|
"critical"
|
|
],
|
|
"title": "ReadingDataQuality"
|
|
},
|
|
"ReadingResponse": {
|
|
"properties": {
|
|
"reading_id": {
|
|
"type": "integer",
|
|
"title": "Reading Id"
|
|
},
|
|
"site_id": {
|
|
"type": "string",
|
|
"title": "Site Id"
|
|
},
|
|
"timestamp": {
|
|
"type": "string",
|
|
"format": "date-time",
|
|
"title": "Timestamp"
|
|
},
|
|
"source": {
|
|
"$ref": "#/components/schemas/ReadingSource"
|
|
},
|
|
"consumption_kw": {
|
|
"anyOf": [
|
|
{
|
|
"type": "number"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
],
|
|
"title": "Consumption Kw"
|
|
},
|
|
"consumption_kwh": {
|
|
"anyOf": [
|
|
{
|
|
"type": "number"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
],
|
|
"title": "Consumption Kwh"
|
|
},
|
|
"consumption_euros": {
|
|
"anyOf": [
|
|
{
|
|
"type": "string",
|
|
"pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
],
|
|
"title": "Consumption Euros"
|
|
},
|
|
"voltage_v": {
|
|
"anyOf": [
|
|
{
|
|
"type": "number"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
],
|
|
"title": "Voltage V"
|
|
},
|
|
"current_a": {
|
|
"anyOf": [
|
|
{
|
|
"type": "number"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
],
|
|
"title": "Current A"
|
|
},
|
|
"power_factor": {
|
|
"anyOf": [
|
|
{
|
|
"type": "number"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
],
|
|
"title": "Power Factor"
|
|
},
|
|
"temperature_celsius": {
|
|
"anyOf": [
|
|
{
|
|
"type": "number"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
],
|
|
"title": "Temperature Celsius"
|
|
},
|
|
"humidity_percent": {
|
|
"anyOf": [
|
|
{
|
|
"type": "number"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
],
|
|
"title": "Humidity Percent"
|
|
},
|
|
"solar_irradiance_wm2": {
|
|
"anyOf": [
|
|
{
|
|
"type": "number"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
],
|
|
"title": "Solar Irradiance Wm2"
|
|
},
|
|
"is_working_hours": {
|
|
"anyOf": [
|
|
{
|
|
"type": "boolean"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
],
|
|
"title": "Is Working Hours"
|
|
},
|
|
"data_quality": {
|
|
"anyOf": [
|
|
{
|
|
"$ref": "#/components/schemas/ReadingDataQuality"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
]
|
|
},
|
|
"null_reasons": {
|
|
"anyOf": [
|
|
{
|
|
"items": {
|
|
"type": "string"
|
|
},
|
|
"type": "array"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
],
|
|
"title": "Null Reasons"
|
|
},
|
|
"imputed_values": {
|
|
"anyOf": [
|
|
{
|
|
"additionalProperties": true,
|
|
"type": "object"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
],
|
|
"title": "Imputed Values"
|
|
},
|
|
"imputation_method": {
|
|
"anyOf": [
|
|
{
|
|
"type": "string"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
],
|
|
"title": "Imputation Method"
|
|
}
|
|
},
|
|
"type": "object",
|
|
"required": [
|
|
"reading_id",
|
|
"site_id",
|
|
"timestamp",
|
|
"source",
|
|
"consumption_kw",
|
|
"consumption_kwh",
|
|
"consumption_euros",
|
|
"voltage_v",
|
|
"current_a",
|
|
"power_factor",
|
|
"temperature_celsius",
|
|
"humidity_percent",
|
|
"solar_irradiance_wm2",
|
|
"is_working_hours",
|
|
"data_quality",
|
|
"null_reasons",
|
|
"imputed_values",
|
|
"imputation_method"
|
|
],
|
|
"title": "ReadingResponse"
|
|
},
|
|
"ReadingSource": {
|
|
"type": "string",
|
|
"enum": [
|
|
"csv",
|
|
"api_current",
|
|
"api_history"
|
|
],
|
|
"title": "ReadingSource"
|
|
},
|
|
"RecommendationGenerationResponse": {
|
|
"properties": {
|
|
"alerts_examined": {
|
|
"type": "integer",
|
|
"title": "Alerts Examined"
|
|
},
|
|
"recommendations_created": {
|
|
"type": "integer",
|
|
"title": "Recommendations Created"
|
|
},
|
|
"already_present": {
|
|
"type": "integer",
|
|
"title": "Already Present"
|
|
}
|
|
},
|
|
"type": "object",
|
|
"required": [
|
|
"alerts_examined",
|
|
"recommendations_created",
|
|
"already_present"
|
|
],
|
|
"title": "RecommendationGenerationResponse"
|
|
},
|
|
"RecommendationResponse": {
|
|
"properties": {
|
|
"recommendation_id": {
|
|
"type": "integer",
|
|
"title": "Recommendation Id"
|
|
},
|
|
"alert_id": {
|
|
"type": "integer",
|
|
"title": "Alert Id"
|
|
},
|
|
"action": {
|
|
"type": "string",
|
|
"title": "Action"
|
|
},
|
|
"explanation": {
|
|
"type": "string",
|
|
"title": "Explanation"
|
|
},
|
|
"rule_reference": {
|
|
"type": "string",
|
|
"title": "Rule Reference"
|
|
},
|
|
"created_at": {
|
|
"type": "string",
|
|
"format": "date-time",
|
|
"title": "Created At"
|
|
}
|
|
},
|
|
"type": "object",
|
|
"required": [
|
|
"recommendation_id",
|
|
"alert_id",
|
|
"action",
|
|
"explanation",
|
|
"rule_reference",
|
|
"created_at"
|
|
],
|
|
"title": "RecommendationResponse"
|
|
},
|
|
"ResetPasswordRequest": {
|
|
"properties": {
|
|
"token": {
|
|
"type": "string",
|
|
"minLength": 1,
|
|
"title": "Token"
|
|
},
|
|
"new_password": {
|
|
"type": "string",
|
|
"maxLength": 128,
|
|
"minLength": 8,
|
|
"title": "New Password"
|
|
}
|
|
},
|
|
"type": "object",
|
|
"required": [
|
|
"token",
|
|
"new_password"
|
|
],
|
|
"title": "ResetPasswordRequest"
|
|
},
|
|
"ResetTokenValidationResponse": {
|
|
"properties": {
|
|
"valid": {
|
|
"type": "boolean",
|
|
"title": "Valid"
|
|
}
|
|
},
|
|
"type": "object",
|
|
"required": [
|
|
"valid"
|
|
],
|
|
"title": "ResetTokenValidationResponse"
|
|
},
|
|
"Role": {
|
|
"type": "string",
|
|
"enum": [
|
|
"lecteur",
|
|
"operateur",
|
|
"admin"
|
|
],
|
|
"title": "Role"
|
|
},
|
|
"SensorDiagnosticResponse": {
|
|
"properties": {
|
|
"status": {
|
|
"type": "string",
|
|
"enum": [
|
|
"ok",
|
|
"failing"
|
|
],
|
|
"title": "Status"
|
|
},
|
|
"since": {
|
|
"anyOf": [
|
|
{
|
|
"type": "string",
|
|
"format": "date-time"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
],
|
|
"title": "Since",
|
|
"description": "Horodatage de la dernière lecture reçue pour ce site. Ce n'est pas le début de la panne : l'historique ne permet pas de le dater sans requête supplémentaire."
|
|
}
|
|
},
|
|
"type": "object",
|
|
"required": [
|
|
"status",
|
|
"since"
|
|
],
|
|
"title": "SensorDiagnosticResponse"
|
|
},
|
|
"SensorStatusResponse": {
|
|
"properties": {
|
|
"timestamp": {
|
|
"type": "string",
|
|
"format": "date-time",
|
|
"title": "Timestamp"
|
|
},
|
|
"sites": {
|
|
"items": {
|
|
"$ref": "#/components/schemas/SiteSensorStatusResponse"
|
|
},
|
|
"type": "array",
|
|
"title": "Sites"
|
|
}
|
|
},
|
|
"type": "object",
|
|
"required": [
|
|
"timestamp",
|
|
"sites"
|
|
],
|
|
"title": "SensorStatusResponse"
|
|
},
|
|
"SiteCurrentResponse": {
|
|
"properties": {
|
|
"timestamp": {
|
|
"anyOf": [
|
|
{
|
|
"type": "string",
|
|
"format": "date-time"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
],
|
|
"title": "Timestamp"
|
|
},
|
|
"site_id": {
|
|
"type": "string",
|
|
"title": "Site Id"
|
|
},
|
|
"site_type": {
|
|
"type": "string",
|
|
"title": "Site Type"
|
|
},
|
|
"consumption_kw": {
|
|
"anyOf": [
|
|
{
|
|
"type": "number"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
],
|
|
"title": "Consumption Kw"
|
|
},
|
|
"consumption_kwh": {
|
|
"anyOf": [
|
|
{
|
|
"type": "number"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
],
|
|
"title": "Consumption Kwh"
|
|
},
|
|
"voltage_v": {
|
|
"anyOf": [
|
|
{
|
|
"type": "number"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
],
|
|
"title": "Voltage V"
|
|
},
|
|
"current_a": {
|
|
"anyOf": [
|
|
{
|
|
"type": "number"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
],
|
|
"title": "Current A"
|
|
},
|
|
"power_factor": {
|
|
"anyOf": [
|
|
{
|
|
"type": "number"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
],
|
|
"title": "Power Factor"
|
|
},
|
|
"temperature_celsius": {
|
|
"anyOf": [
|
|
{
|
|
"type": "number"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
],
|
|
"title": "Temperature Celsius"
|
|
},
|
|
"humidity_percent": {
|
|
"anyOf": [
|
|
{
|
|
"type": "number"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
],
|
|
"title": "Humidity Percent"
|
|
},
|
|
"null_reasons": {
|
|
"items": {
|
|
"type": "string"
|
|
},
|
|
"type": "array",
|
|
"title": "Null Reasons"
|
|
},
|
|
"data_quality": {
|
|
"type": "string",
|
|
"enum": [
|
|
"good",
|
|
"partial",
|
|
"degraded",
|
|
"critical"
|
|
],
|
|
"title": "Data Quality"
|
|
}
|
|
},
|
|
"type": "object",
|
|
"required": [
|
|
"timestamp",
|
|
"site_id",
|
|
"site_type",
|
|
"consumption_kw",
|
|
"consumption_kwh",
|
|
"voltage_v",
|
|
"current_a",
|
|
"power_factor",
|
|
"temperature_celsius",
|
|
"humidity_percent",
|
|
"null_reasons",
|
|
"data_quality"
|
|
],
|
|
"title": "SiteCurrentResponse"
|
|
},
|
|
"SitePredictionResponse": {
|
|
"properties": {
|
|
"target_at": {
|
|
"type": "string",
|
|
"format": "date-time",
|
|
"title": "Target At"
|
|
},
|
|
"target_metric": {
|
|
"$ref": "#/components/schemas/PredictionTargetMetric"
|
|
},
|
|
"period_minutes": {
|
|
"anyOf": [
|
|
{
|
|
"type": "integer"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
],
|
|
"title": "Period Minutes"
|
|
},
|
|
"predicted_value": {
|
|
"anyOf": [
|
|
{
|
|
"type": "number"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
],
|
|
"title": "Predicted Value"
|
|
},
|
|
"status": {
|
|
"$ref": "#/components/schemas/PredictionStatus"
|
|
},
|
|
"failure_reason": {
|
|
"anyOf": [
|
|
{
|
|
"type": "string"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
],
|
|
"title": "Failure Reason"
|
|
},
|
|
"model_reference": {
|
|
"type": "string",
|
|
"title": "Model Reference"
|
|
},
|
|
"created_at": {
|
|
"type": "string",
|
|
"format": "date-time",
|
|
"title": "Created At"
|
|
}
|
|
},
|
|
"type": "object",
|
|
"required": [
|
|
"target_at",
|
|
"target_metric",
|
|
"period_minutes",
|
|
"predicted_value",
|
|
"status",
|
|
"failure_reason",
|
|
"model_reference",
|
|
"created_at"
|
|
],
|
|
"title": "SitePredictionResponse"
|
|
},
|
|
"SitePredictionSummaryResponse": {
|
|
"properties": {
|
|
"site_id": {
|
|
"type": "string",
|
|
"title": "Site Id"
|
|
},
|
|
"site_name": {
|
|
"type": "string",
|
|
"title": "Site Name"
|
|
},
|
|
"prediction": {
|
|
"anyOf": [
|
|
{
|
|
"$ref": "#/components/schemas/SitePredictionResponse"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"type": "object",
|
|
"required": [
|
|
"site_id",
|
|
"site_name",
|
|
"prediction"
|
|
],
|
|
"title": "SitePredictionSummaryResponse"
|
|
},
|
|
"SiteResponse": {
|
|
"properties": {
|
|
"site_id": {
|
|
"type": "string",
|
|
"title": "Site Id"
|
|
},
|
|
"site_name": {
|
|
"type": "string",
|
|
"title": "Site Name"
|
|
},
|
|
"site_type": {
|
|
"type": "string",
|
|
"title": "Site Type"
|
|
},
|
|
"location": {
|
|
"anyOf": [
|
|
{
|
|
"type": "string"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
],
|
|
"title": "Location"
|
|
},
|
|
"capacity_kw": {
|
|
"anyOf": [
|
|
{
|
|
"type": "number"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
],
|
|
"title": "Capacity Kw"
|
|
},
|
|
"status": {
|
|
"anyOf": [
|
|
{
|
|
"type": "string"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
],
|
|
"title": "Status"
|
|
}
|
|
},
|
|
"type": "object",
|
|
"required": [
|
|
"site_id",
|
|
"site_name",
|
|
"site_type",
|
|
"location",
|
|
"capacity_kw",
|
|
"status"
|
|
],
|
|
"title": "SiteResponse"
|
|
},
|
|
"SiteSensorStatusResponse": {
|
|
"properties": {
|
|
"site_id": {
|
|
"type": "string",
|
|
"title": "Site Id"
|
|
},
|
|
"site_name": {
|
|
"type": "string",
|
|
"title": "Site Name"
|
|
},
|
|
"sensors": {
|
|
"$ref": "#/components/schemas/SiteSensorsResponse"
|
|
},
|
|
"overall": {
|
|
"type": "string",
|
|
"enum": [
|
|
"ok",
|
|
"degraded",
|
|
"critical"
|
|
],
|
|
"title": "Overall"
|
|
}
|
|
},
|
|
"type": "object",
|
|
"required": [
|
|
"site_id",
|
|
"site_name",
|
|
"sensors",
|
|
"overall"
|
|
],
|
|
"title": "SiteSensorStatusResponse"
|
|
},
|
|
"SiteSensorsResponse": {
|
|
"properties": {
|
|
"consumption": {
|
|
"$ref": "#/components/schemas/SensorDiagnosticResponse"
|
|
},
|
|
"electrical": {
|
|
"$ref": "#/components/schemas/SensorDiagnosticResponse"
|
|
},
|
|
"temperature": {
|
|
"$ref": "#/components/schemas/SensorDiagnosticResponse"
|
|
},
|
|
"humidity": {
|
|
"$ref": "#/components/schemas/SensorDiagnosticResponse"
|
|
},
|
|
"network": {
|
|
"$ref": "#/components/schemas/SensorDiagnosticResponse"
|
|
}
|
|
},
|
|
"type": "object",
|
|
"required": [
|
|
"consumption",
|
|
"electrical",
|
|
"temperature",
|
|
"humidity",
|
|
"network"
|
|
],
|
|
"title": "SiteSensorsResponse"
|
|
},
|
|
"SiteSummaryResponse": {
|
|
"properties": {
|
|
"site_id": {
|
|
"type": "string",
|
|
"title": "Site Id"
|
|
},
|
|
"site_name": {
|
|
"type": "string",
|
|
"title": "Site Name"
|
|
},
|
|
"current_consumption_kw": {
|
|
"anyOf": [
|
|
{
|
|
"type": "number"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
],
|
|
"title": "Current Consumption Kw"
|
|
},
|
|
"capacity_kw": {
|
|
"type": "number",
|
|
"title": "Capacity Kw"
|
|
},
|
|
"load_percent": {
|
|
"anyOf": [
|
|
{
|
|
"type": "number"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
],
|
|
"title": "Load Percent"
|
|
},
|
|
"data_quality": {
|
|
"type": "string",
|
|
"enum": [
|
|
"good",
|
|
"partial",
|
|
"degraded",
|
|
"critical"
|
|
],
|
|
"title": "Data Quality"
|
|
}
|
|
},
|
|
"type": "object",
|
|
"required": [
|
|
"site_id",
|
|
"site_name",
|
|
"current_consumption_kw",
|
|
"capacity_kw",
|
|
"load_percent",
|
|
"data_quality"
|
|
],
|
|
"title": "SiteSummaryResponse"
|
|
},
|
|
"StatsSummaryResponse": {
|
|
"properties": {
|
|
"timestamp": {
|
|
"type": "string",
|
|
"format": "date-time",
|
|
"title": "Timestamp"
|
|
},
|
|
"total_sites": {
|
|
"type": "integer",
|
|
"title": "Total Sites"
|
|
},
|
|
"total_consumption_kw": {
|
|
"type": "number",
|
|
"title": "Total Consumption Kw"
|
|
},
|
|
"total_capacity_kw": {
|
|
"type": "number",
|
|
"title": "Total Capacity Kw"
|
|
},
|
|
"average_load_percent": {
|
|
"type": "number",
|
|
"title": "Average Load Percent"
|
|
},
|
|
"sites": {
|
|
"items": {
|
|
"$ref": "#/components/schemas/SiteSummaryResponse"
|
|
},
|
|
"type": "array",
|
|
"title": "Sites"
|
|
}
|
|
},
|
|
"type": "object",
|
|
"required": [
|
|
"timestamp",
|
|
"total_sites",
|
|
"total_consumption_kw",
|
|
"total_capacity_kw",
|
|
"average_load_percent",
|
|
"sites"
|
|
],
|
|
"title": "StatsSummaryResponse"
|
|
},
|
|
"TemporaryPasswordResponse": {
|
|
"properties": {
|
|
"user": {
|
|
"$ref": "#/components/schemas/UserResponse"
|
|
},
|
|
"temporary_password": {
|
|
"type": "string",
|
|
"title": "Temporary Password"
|
|
}
|
|
},
|
|
"type": "object",
|
|
"required": [
|
|
"user",
|
|
"temporary_password"
|
|
],
|
|
"title": "TemporaryPasswordResponse"
|
|
},
|
|
"TokenResponse": {
|
|
"properties": {
|
|
"access_token": {
|
|
"type": "string",
|
|
"title": "Access Token"
|
|
},
|
|
"token_type": {
|
|
"type": "string",
|
|
"const": "bearer",
|
|
"title": "Token Type",
|
|
"default": "bearer"
|
|
},
|
|
"expires_in": {
|
|
"type": "integer",
|
|
"title": "Expires In"
|
|
},
|
|
"principal": {
|
|
"$ref": "#/components/schemas/PrincipalResponse"
|
|
}
|
|
},
|
|
"type": "object",
|
|
"required": [
|
|
"access_token",
|
|
"expires_in",
|
|
"principal"
|
|
],
|
|
"title": "TokenResponse"
|
|
},
|
|
"UserCreateRequest": {
|
|
"properties": {
|
|
"email": {
|
|
"type": "string",
|
|
"format": "email",
|
|
"title": "Email"
|
|
},
|
|
"role": {
|
|
"$ref": "#/components/schemas/Role"
|
|
},
|
|
"full_name": {
|
|
"anyOf": [
|
|
{
|
|
"type": "string",
|
|
"maxLength": 200
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
],
|
|
"title": "Full Name"
|
|
}
|
|
},
|
|
"type": "object",
|
|
"required": [
|
|
"email",
|
|
"role"
|
|
],
|
|
"title": "UserCreateRequest"
|
|
},
|
|
"UserResponse": {
|
|
"properties": {
|
|
"id": {
|
|
"type": "string",
|
|
"format": "uuid",
|
|
"title": "Id"
|
|
},
|
|
"email": {
|
|
"type": "string",
|
|
"title": "Email"
|
|
},
|
|
"role": {
|
|
"$ref": "#/components/schemas/Role"
|
|
},
|
|
"kind": {
|
|
"$ref": "#/components/schemas/AccountKind"
|
|
},
|
|
"is_active": {
|
|
"type": "boolean",
|
|
"title": "Is Active"
|
|
},
|
|
"must_change_password": {
|
|
"type": "boolean",
|
|
"title": "Must Change Password"
|
|
},
|
|
"full_name": {
|
|
"anyOf": [
|
|
{
|
|
"type": "string"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
],
|
|
"title": "Full Name"
|
|
},
|
|
"last_login_at": {
|
|
"anyOf": [
|
|
{
|
|
"type": "string",
|
|
"format": "date-time"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
],
|
|
"title": "Last Login At"
|
|
},
|
|
"created_at": {
|
|
"type": "string",
|
|
"format": "date-time",
|
|
"title": "Created At"
|
|
}
|
|
},
|
|
"type": "object",
|
|
"required": [
|
|
"id",
|
|
"email",
|
|
"role",
|
|
"kind",
|
|
"is_active",
|
|
"must_change_password",
|
|
"full_name",
|
|
"last_login_at",
|
|
"created_at"
|
|
],
|
|
"title": "UserResponse"
|
|
},
|
|
"UserUpdateRequest": {
|
|
"properties": {
|
|
"role": {
|
|
"anyOf": [
|
|
{
|
|
"$ref": "#/components/schemas/Role"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
]
|
|
},
|
|
"is_active": {
|
|
"anyOf": [
|
|
{
|
|
"type": "boolean"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
],
|
|
"title": "Is Active"
|
|
}
|
|
},
|
|
"type": "object",
|
|
"title": "UserUpdateRequest"
|
|
},
|
|
"ValidationErrorResponse": {
|
|
"properties": {
|
|
"detail": {
|
|
"items": {
|
|
"$ref": "#/components/schemas/FieldError"
|
|
},
|
|
"type": "array",
|
|
"title": "Detail"
|
|
}
|
|
},
|
|
"type": "object",
|
|
"required": [
|
|
"detail"
|
|
],
|
|
"title": "ValidationErrorResponse"
|
|
}
|
|
},
|
|
"securitySchemes": {
|
|
"Cookie de rafraîchissement": {
|
|
"type": "apiKey",
|
|
"description": "Cookie `HttpOnly` posé par `/auth/login` et tourné par `/auth/refresh`. Il prend le préfixe `__Secure-` dès que l'API tourne derrière TLS, et n'est émis que vers `/api/v1/auth`.",
|
|
"in": "cookie",
|
|
"name": "ev_refresh"
|
|
},
|
|
"Jeton d'accès": {
|
|
"type": "http",
|
|
"scheme": "bearer"
|
|
}
|
|
}
|
|
},
|
|
"tags": [
|
|
{
|
|
"name": "health",
|
|
"description": "Sondes d'infrastructure, publiques. `live` prouve que le processus répond, `ready` que la base répond et que l'extension TimescaleDB est chargée."
|
|
},
|
|
{
|
|
"name": "auth",
|
|
"description": "Ouverture, rotation et fermeture de session, et changement de son propre mot de passe."
|
|
},
|
|
{
|
|
"name": "users",
|
|
"description": "Administration des comptes. Réservé au rôle `admin`."
|
|
},
|
|
{
|
|
"name": "sites",
|
|
"description": "Consultation du parc de sites. Accessible à partir du rôle `lecteur`."
|
|
},
|
|
{
|
|
"name": "alerts",
|
|
"description": "Consultation des alertes de consommation. Accessible à partir du rôle `lecteur`."
|
|
},
|
|
{
|
|
"name": "recommendations",
|
|
"description": "Consultation des recommandations issues des alertes. Accessible à partir du rôle `lecteur`. Leur génération par le moteur de règles est réservée au rôle `admin`."
|
|
},
|
|
{
|
|
"name": "stats",
|
|
"description": "Statistiques agrégées de consommation. Accessible à partir du rôle `lecteur`."
|
|
},
|
|
{
|
|
"name": "readings",
|
|
"description": "Historique des lectures de consommation. Fenêtre temporelle plafonnée à 90 jours, 24 dernières heures par défaut si `start`/`end` sont omis. Accessible à partir du rôle `lecteur`."
|
|
},
|
|
{
|
|
"name": "sensors",
|
|
"description": "État de santé des capteurs par site. Réservé au rôle `admin`."
|
|
},
|
|
{
|
|
"name": "predictions",
|
|
"description": "Dernière prévision de consommation par site, calculée hors ligne par le pipeline de scoring (`ml/`) et simplement lue ici. Accessible à partir du rôle `lecteur`."
|
|
},
|
|
{
|
|
"name": "monitoring",
|
|
"description": "Surveillance de la dérive du modèle : écart entre les prévisions déjà écrites et les lectures réellement arrivées, par site et tous sites confondus. Réservé à partir du rôle `operateur`, qui agit sur un pipeline dégradé."
|
|
}
|
|
]
|
|
}
|