Ajoute la page de détail d'un site (fiche, mesure instantanée, jauge de
consommation, historique) en remplacement du placeholder. Les champs
null sont affichés explicitement avec leur raison plutôt que masqués.
Ajoute l'endpoint GET /sites/{id}/current côté backend, qui retourne la
dernière mesure connue d'un site sans filtre temporel, conformément au
contrat de l'issue.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012i5NteMLRZgfTAKB5GD37X
2853 lines
80 KiB
JSON
2853 lines
80 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 connue 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": {
|
|
"anyOf": [
|
|
{
|
|
"$ref": "#/components/schemas/ReadingResponse"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
],
|
|
"title": "Response Get Current Api V1 Sites Site Id Current 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"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"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/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": []
|
|
}
|
|
]
|
|
}
|
|
}
|
|
},
|
|
"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"
|
|
},
|
|
"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"
|
|
},
|
|
"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"
|
|
},
|
|
"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"
|
|
},
|
|
"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`."
|
|
},
|
|
{
|
|
"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`."
|
|
}
|
|
]
|
|
}
|