diff --git a/.gitignore b/.gitignore index 83972fa..b20ca39 100644 --- a/.gitignore +++ b/.gitignore @@ -150,6 +150,9 @@ activemq-data/ # Environments .env .envrc +# Configuration de déploiement : contient la chaîne de connexion et +# la clé JWT. Seul api.env.example (sans valeurs) est versionné. +api.env .venv env/ venv/ diff --git a/api.env.example b/api.env.example new file mode 100644 index 0000000..856641c --- /dev/null +++ b/api.env.example @@ -0,0 +1,51 @@ +# ============================================================ +# Data Sentinel — modèle de configuration de l'API +# +# Copier ce fichier en api.env (à côté du docker-compose.yml) +# et renseigner les valeurs. api.env n'est JAMAIS versionné : +# il est exclu par .gitignore. +# +# cp Api-DataSentinel/api.env.example api.env +# ============================================================ + +# ---- Base de données --------------------------------------- +# Hôte SQL Server. En conteneur, le nom du service Docker +# (ex. dev-mssql) ; en local, localhost ou HOTE\INSTANCE. +DB_SERVER=dev-mssql + +# Port. Laisser VIDE pour une instance nommée (HOTE\INSTANCE). +DB_PORT=1433 + +DB_NAME=DataSentinel + +# Compte applicatif dédié — surtout pas « sa ». +# Création : voir README.md §3.3 (db_datareader + db_datawriter +# sur la seule base DataSentinel). +DB_USER=datasentinel_app +DB_PASSWORD= + +# Pilote ODBC embarqué dans l'image. +DB_DRIVER=ODBC Driver 18 for SQL Server + +# Mettre à yes pour forcer l'authentification Windows +# (poste de développement uniquement, sans DB_USER). +DB_TRUSTED_CONNECTION= + +# ---- Sécurité ---------------------------------------------- +# OBLIGATOIRE en déploiement. Sans cette variable, l'API démarre +# mais tire une clé aléatoire à chaque redémarrage : toutes les +# sessions sont alors invalidées au moindre restart. +# Générer : python -c "import secrets; print(secrets.token_urlsafe(64))" +JWT_SECRET= + +JWT_ALGORITHM=HS256 +JWT_EXPIRE_MINUTES=60 + +# ---- CORS --------------------------------------------------- +# Origines autorisées, séparées par des virgules. Doit contenir +# l'URL exacte du front (schéma + hôte, sans slash final). +CORS_ORIGINS=https://datasentinel.nfteam.ovh + +# ---- Build -------------------------------------------------- +# Injecté par la CI (SHA court du commit) et exposé par GET /health. +APP_BUILD=local diff --git a/config.py b/config.py index 6e979a2..fb29aaa 100644 --- a/config.py +++ b/config.py @@ -81,7 +81,7 @@ class Config: # Paramètres API API_TITLE = "Data Sentinel API" - API_VERSION = "1.0.0" + API_VERSION = "1.1.0" API_DESCRIPTION = "API de monitoring de la qualité des données — XEFI" # Identifiant de build injecté par la CI : permet de vérifier quelle diff --git a/docs/openapi.json b/docs/openapi.json index d9840fd..7118c9e 100644 --- a/docs/openapi.json +++ b/docs/openapi.json @@ -1 +1 @@ -{"openapi":"3.1.0","info":{"title":"Data Sentinel API","description":"API de monitoring de la qualité des données — XEFI","version":"1.0.0"},"paths":{"/health":{"get":{"tags":["Système"],"summary":"Health Check","description":"Ping API + test connexion SQL Server.\n\n`build` porte le SHA du commit dont l'image a été construite (injecté par\nla CI). C'est le seul moyen fiable de vérifier qu'un déploiement a bien\npris : comparer ce champ au dernier commit poussé.","operationId":"health_check_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/auth/login":{"post":{"tags":["Auth"],"summary":"Login","description":"Authentifie un utilisateur et retourne un JWT.","operationId":"login_auth_login_post","requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_login_auth_login_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/me":{"get":{"tags":["Auth"],"summary":"Me","description":"Profil de l'utilisateur connecté.","operationId":"me_auth_me_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"OAuth2PasswordBearer":[]}]}},"/categories":{"get":{"tags":["Référentiels"],"summary":"Get Categories","description":"Toutes les catégories de monitoring.","operationId":"get_categories_categories_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"OAuth2PasswordBearer":[]}]},"post":{"tags":["Référentiels"],"summary":"Create Categorie","operationId":"create_categorie_categories_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CategorieBody"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"OAuth2PasswordBearer":[]}]}},"/categories/{id_categorie}":{"put":{"tags":["Référentiels"],"summary":"Update Categorie","operationId":"update_categorie_categories__id_categorie__put","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"id_categorie","in":"path","required":true,"schema":{"type":"integer","title":"Id Categorie"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CategorieBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Référentiels"],"summary":"Delete Categorie","description":"Supprime une catégorie, sauf si des monitorings s'y rattachent encore :\nla clé étrangère FK_NOM_CATEGORIE l'interdirait, autant renvoyer un\nmessage explicite plutôt que de laisser remonter l'erreur SQL brute.","operationId":"delete_categorie_categories__id_categorie__delete","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"id_categorie","in":"path","required":true,"schema":{"type":"integer","title":"Id Categorie"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/services":{"get":{"tags":["Référentiels"],"summary":"Get Services","description":"Tous les services.","operationId":"get_services_services_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"OAuth2PasswordBearer":[]}]},"post":{"tags":["Référentiels"],"summary":"Create Service","operationId":"create_service_services_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceBody"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"OAuth2PasswordBearer":[]}]}},"/services/{id_service}":{"put":{"tags":["Référentiels"],"summary":"Update Service","operationId":"update_service_services__id_service__put","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"id_service","in":"path","required":true,"schema":{"type":"integer","title":"Id Service"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Référentiels"],"summary":"Delete Service","description":"Refuse la suppression tant que des monitorings ou contacts y sont rattachés.","operationId":"delete_service_services__id_service__delete","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"id_service","in":"path","required":true,"schema":{"type":"integer","title":"Id Service"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/contacts":{"get":{"tags":["Référentiels"],"summary":"Get Contacts","description":"Contacts, filtrables par service.","operationId":"get_contacts_contacts_get","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"id_service","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Filtrer par service","title":"Id Service"},"description":"Filtrer par service"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["Référentiels"],"summary":"Create Contact","operationId":"create_contact_contacts_post","security":[{"OAuth2PasswordBearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactBody"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/contacts/{id_contact}":{"put":{"tags":["Référentiels"],"summary":"Update Contact","operationId":"update_contact_contacts__id_contact__put","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"id_contact","in":"path","required":true,"schema":{"type":"integer","title":"Id Contact"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Référentiels"],"summary":"Delete Contact","description":"Un contact n'est référencé par aucune autre table : suppression directe.","operationId":"delete_contact_contacts__id_contact__delete","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"id_contact","in":"path","required":true,"schema":{"type":"integer","title":"Id Contact"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/monitorings":{"get":{"tags":["Monitorings"],"summary":"Get Monitorings","description":"Liste des monitorings actifs avec table_source et bdd_source.","operationId":"get_monitorings_monitorings_get","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"id_service","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Filtrer par service","title":"Id Service"},"description":"Filtrer par service"},{"name":"id_categorie","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Filtrer par catégorie","title":"Id Categorie"},"description":"Filtrer par catégorie"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["Monitorings"],"summary":"Create Monitoring","description":"Déclare un monitoring dans la nomenclature.\n\nAttention : cela n'ajoute que l'entrée de référentiel. Pour qu'il remonte\ndes données, il faut aussi créer la table MONITO_[NOM] correspondante et\nl'ajouter au mapping MONITO_TABLES — voir l'en-tête de data_sentinel_init.sql.","operationId":"create_monitoring_monitorings_post","security":[{"OAuth2PasswordBearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MonitoringBody"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/monitorings/{id_monito}":{"put":{"tags":["Monitorings"],"summary":"Update Monitoring","operationId":"update_monitoring_monitorings__id_monito__put","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"id_monito","in":"path","required":true,"schema":{"type":"integer","title":"Id Monito"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MonitoringUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Monitorings"],"summary":"Desactiver Monitoring","description":"Désactivation (actif = 0), pas de suppression physique : TABLE_FINAL\nréférence id_monito et l'historique doit rester consultable.","operationId":"desactiver_monitoring_monitorings__id_monito__delete","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"id_monito","in":"path","required":true,"schema":{"type":"integer","title":"Id Monito"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["Monitorings"],"summary":"Get Monitoring By Id","description":"Détail d'un monitoring.","operationId":"get_monitoring_by_id_monitorings__id_monito__get","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"id_monito","in":"path","required":true,"schema":{"type":"integer","title":"Id Monito"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/monitorings/{id_monito}/details":{"get":{"tags":["Monitorings"],"summary":"Get Monitoring Details","description":"Retourne toutes les lignes en erreur depuis la table MONITO_[NOM] dédiée.\nToutes les colonnes métier sont exposées → utilisable pour export CSV côté frontend.","operationId":"get_monitoring_details_monitorings__id_monito__details_get","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"id_monito","in":"path","required":true,"schema":{"type":"integer","title":"Id Monito"}},{"name":"search","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Recherche sur colonnes texte (ct_intitule, agence, STE...)","title":"Search"},"description":"Recherche sur colonnes texte (ct_intitule, agence, STE...)"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":5000,"minimum":1,"description":"Lignes max — mettre 5000 pour export CSV complet","default":500,"title":"Limit"},"description":"Lignes max — mettre 5000 pour export CSV complet"},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Offset pagination","default":0,"title":"Offset"},"description":"Offset pagination"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/monitorings/{id_monito}/count":{"get":{"tags":["Monitorings"],"summary":"Get Monitoring Count","description":"Nombre d'erreurs dans la table dédiée du monitoring.","operationId":"get_monitoring_count_monitorings__id_monito__count_get","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"id_monito","in":"path","required":true,"schema":{"type":"integer","title":"Id Monito"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/monitorings/{id_monito}/columns":{"get":{"tags":["Monitorings"],"summary":"Get Monitoring Columns","description":"Retourne les colonnes de la table dédiée.\nPermet au frontend de générer dynamiquement les en-têtes du tableau.","operationId":"get_monitoring_columns_monitorings__id_monito__columns_get","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"id_monito","in":"path","required":true,"schema":{"type":"integer","title":"Id Monito"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/dashboard":{"get":{"tags":["Dashboard"],"summary":"Get Dashboard","description":"Vue consolidée — source principale du dashboard.","operationId":"get_dashboard_dashboard_get","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"service","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filtrer par service","title":"Service"},"description":"Filtrer par service"},{"name":"categorie","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filtrer par catégorie","title":"Categorie"},"description":"Filtrer par catégorie"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/dashboard/filtres":{"get":{"tags":["Dashboard"],"summary":"Get Dashboard Filtres","description":"Valeurs de filtre réellement présentes dans VUE_CONSO.\n\nLes référentiels SERVICE / CATEGORIE contiennent des entrées auxquelles\naucun monitoring actif n'est rattaché (ex. « Business Intelligence »).\nLes proposer dans les menus déroulants du dashboard mène à un écran vide :\ncet endpoint ne renvoie que les valeurs qui ramènent au moins un monitoring.\n\n`combinaisons` permet au frontend de restreindre les catégories proposées\nau service sélectionné.","operationId":"get_dashboard_filtres_dashboard_filtres_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"OAuth2PasswordBearer":[]}]}},"/dashboard/summary":{"get":{"tags":["Dashboard"],"summary":"Get Dashboard Summary","description":"KPI globaux pour les 4 cartes du dashboard.","operationId":"get_dashboard_summary_dashboard_summary_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"OAuth2PasswordBearer":[]}]}},"/historique":{"get":{"tags":["Historique"],"summary":"Get Historique","description":"Snapshots journaliers (TABLE_FINAL).","operationId":"get_historique_historique_get","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"id_monito","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Id Monito"}},{"name":"date_debut","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"description":"YYYY-MM-DD","title":"Date Debut"},"description":"YYYY-MM-DD"},{"name":"date_fin","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"description":"YYYY-MM-DD","title":"Date Fin"},"description":"YYYY-MM-DD"},{"name":"service","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Service"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/historique/comparaison":{"get":{"tags":["Historique"],"summary":"Get Comparaison","description":"Déprécié — utiliser GET /evolution/par-monitoring à la place.\nConservé pour compatibilité ascendante.","operationId":"get_comparaison_historique_comparaison_get","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"date_debut","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Date Debut"}},{"name":"date_fin","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Date Fin"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/historique/{id_monito}/evolution":{"get":{"tags":["Historique"],"summary":"Get Evolution","description":"Évolution d'un monitoring — format optimisé Recharts.","operationId":"get_evolution_historique__id_monito__evolution_get","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"id_monito","in":"path","required":true,"schema":{"type":"integer","title":"Id Monito"}},{"name":"date_debut","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Date Debut"}},{"name":"date_fin","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Date Fin"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/evolution/global":{"get":{"tags":["Évolution globale"],"summary":"Get Evolution Global","description":"Courbe globale : total des erreurs tous monitorings confondus, par jour.\nSource : VUE_TABLE_FINAL_CONSO WHERE type_agregat = 'GLOBAL'\nFormat optimisé Recharts → [{date, nb_erreurs}]","operationId":"get_evolution_global_evolution_global_get","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"date_debut","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"description":"YYYY-MM-DD","title":"Date Debut"},"description":"YYYY-MM-DD"},{"name":"date_fin","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"description":"YYYY-MM-DD","title":"Date Fin"},"description":"YYYY-MM-DD"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/evolution/par-monitoring":{"get":{"tags":["Évolution globale"],"summary":"Get Evolution Par Monitoring","description":"Évolution par monitoring — toutes les courbes individuelles.\nSource : VUE_TABLE_FINAL_CONSO WHERE type_agregat = 'MONITO'\n\nRetourne un format pivot par monitoring :\n{\n \"monitorings\": [{\"id\": 1, \"nom\": \"...\", \"serie\": [{\"date\",\"nb_erreurs\"}]}]\n}\nUtile pour un graphique multi-lignes Recharts (une ligne par monitoring).","operationId":"get_evolution_par_monitoring_evolution_par_monitoring_get","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"date_debut","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"description":"YYYY-MM-DD","title":"Date Debut"},"description":"YYYY-MM-DD"},{"name":"date_fin","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"description":"YYYY-MM-DD","title":"Date Fin"},"description":"YYYY-MM-DD"},{"name":"id_monito","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Filtrer sur un seul monitoring","title":"Id Monito"},"description":"Filtrer sur un seul monitoring"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/evolution/par-service":{"get":{"tags":["Évolution globale"],"summary":"Get Evolution Par Service","description":"Évolution par service (Contrat / Fournisseur) — 1 courbe par service.\nSource : VUE_TABLE_FINAL_CONSO WHERE type_agregat = 'SERVICE'\n\nRetourne :\n{\n \"services\": [{\"service\": \"Contrat\", \"serie\": [{\"date\",\"nb_erreurs\"}]}]\n}","operationId":"get_evolution_par_service_evolution_par_service_get","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"date_debut","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"description":"YYYY-MM-DD","title":"Date Debut"},"description":"YYYY-MM-DD"},{"name":"date_fin","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"description":"YYYY-MM-DD","title":"Date Fin"},"description":"YYYY-MM-DD"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/users":{"get":{"tags":["Admin"],"summary":"List Users","operationId":"list_users_admin_users_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"OAuth2PasswordBearer":[]}]},"post":{"tags":["Admin"],"summary":"Create User","operationId":"create_user_admin_users_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserCreate"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"OAuth2PasswordBearer":[]}]}},"/admin/users/{id_user}":{"put":{"tags":["Admin"],"summary":"Update User","operationId":"update_user_admin_users__id_user__put","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"id_user","in":"path","required":true,"schema":{"type":"integer","title":"Id User"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Admin"],"summary":"Delete User","description":"Suppression douce (actif = 0).","operationId":"delete_user_admin_users__id_user__delete","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"id_user","in":"path","required":true,"schema":{"type":"integer","title":"Id User"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/users/{id_user}/reset-password":{"post":{"tags":["Admin"],"summary":"Reset Password","operationId":"reset_password_admin_users__id_user__reset_password_post","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"id_user","in":"path","required":true,"schema":{"type":"integer","title":"Id User"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PasswordReset"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/journal":{"get":{"tags":["Admin"],"summary":"Get Journal","operationId":"get_journal_admin_journal_get","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":200,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/me/data-export":{"get":{"tags":["RGPD"],"summary":"Export My Data","description":"Droit à la portabilité (art. 20) : export des données de l'utilisateur.","operationId":"export_my_data_me_data_export_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"OAuth2PasswordBearer":[]}]}},"/me":{"delete":{"tags":["RGPD"],"summary":"Delete My Account","description":"Droit à l'oubli (art. 17) : désactivation + anonymisation.","operationId":"delete_my_account_me_delete","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"OAuth2PasswordBearer":[]}]}}},"components":{"schemas":{"Body_login_auth_login_post":{"properties":{"grant_type":{"anyOf":[{"type":"string","pattern":"^password$"},{"type":"null"}],"title":"Grant Type"},"username":{"type":"string","title":"Username"},"password":{"type":"string","format":"password","title":"Password"},"scope":{"type":"string","title":"Scope","default":""},"client_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Id"},"client_secret":{"anyOf":[{"type":"string"},{"type":"null"}],"format":"password","title":"Client Secret"}},"type":"object","required":["username","password"],"title":"Body_login_auth_login_post"},"CategorieBody":{"properties":{"intitule_categorie":{"type":"string","maxLength":100,"minLength":1,"title":"Intitule Categorie"}},"type":"object","required":["intitule_categorie"],"title":"CategorieBody"},"ContactBody":{"properties":{"id_service":{"type":"integer","title":"Id Service"},"intitule_contact":{"type":"string","maxLength":100,"minLength":1,"title":"Intitule Contact"},"nom":{"type":"string","maxLength":100,"minLength":1,"title":"Nom"},"prenom":{"type":"string","maxLength":100,"minLength":1,"title":"Prenom"},"mail":{"type":"string","maxLength":200,"minLength":1,"title":"Mail"}},"type":"object","required":["id_service","intitule_contact","nom","prenom","mail"],"title":"ContactBody"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"MonitoringBody":{"properties":{"monito_intitule":{"type":"string","maxLength":200,"minLength":1,"title":"Monito Intitule"},"id_service":{"type":"integer","title":"Id Service"},"id_categorie":{"type":"integer","title":"Id Categorie"},"table_source":{"type":"string","maxLength":100,"minLength":1,"title":"Table Source"},"bdd_source":{"type":"string","maxLength":50,"minLength":1,"title":"Bdd Source"}},"type":"object","required":["monito_intitule","id_service","id_categorie","table_source","bdd_source"],"title":"MonitoringBody"},"MonitoringUpdate":{"properties":{"monito_intitule":{"anyOf":[{"type":"string","maxLength":200,"minLength":1},{"type":"null"}],"title":"Monito Intitule"},"id_service":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Id Service"},"id_categorie":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Id Categorie"},"table_source":{"anyOf":[{"type":"string","maxLength":100,"minLength":1},{"type":"null"}],"title":"Table Source"},"bdd_source":{"anyOf":[{"type":"string","maxLength":50,"minLength":1},{"type":"null"}],"title":"Bdd Source"},"actif":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Actif"}},"type":"object","title":"MonitoringUpdate"},"PasswordReset":{"properties":{"password":{"type":"string","title":"Password"}},"type":"object","required":["password"],"title":"PasswordReset"},"ServiceBody":{"properties":{"nom_service":{"type":"string","maxLength":100,"minLength":1,"title":"Nom Service"}},"type":"object","required":["nom_service"],"title":"ServiceBody"},"UserCreate":{"properties":{"username":{"type":"string","title":"Username"},"email":{"type":"string","title":"Email"},"password":{"type":"string","title":"Password"},"role":{"$ref":"#/components/schemas/UserRole"}},"type":"object","required":["username","email","password","role"],"title":"UserCreate"},"UserRole":{"type":"string","enum":["Admin","Superviseur","Consultant"],"title":"UserRole","description":"Rôles applicatifs — miroir de la contrainte CK_USER_ROLE en base."},"UserUpdate":{"properties":{"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"role":{"anyOf":[{"$ref":"#/components/schemas/UserRole"},{"type":"null"}]},"actif":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Actif"}},"type":"object","title":"UserUpdate"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}},"securitySchemes":{"OAuth2PasswordBearer":{"type":"oauth2","flows":{"password":{"scopes":{},"tokenUrl":"auth/login"}}}}}} \ No newline at end of file +{"openapi": "3.1.0", "info": {"title": "Data Sentinel API", "description": "API de monitoring de la qualité des données — XEFI", "version": "1.1.0"}, "paths": {"/health": {"get": {"tags": ["Système"], "summary": "Health Check", "description": "Ping API + test connexion SQL Server.\n\n`build` porte le SHA du commit dont l'image a été construite (injecté par\nla CI). C'est le seul moyen fiable de vérifier qu'un déploiement a bien\npris : comparer ce champ au dernier commit poussé.", "operationId": "health_check_health_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}}}, "/auth/login": {"post": {"tags": ["Auth"], "summary": "Login", "description": "Authentifie un utilisateur et retourne un JWT.", "operationId": "login_auth_login_post", "requestBody": {"content": {"application/x-www-form-urlencoded": {"schema": {"$ref": "#/components/schemas/Body_login_auth_login_post"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/auth/me": {"get": {"tags": ["Auth"], "summary": "Me", "description": "Profil de l'utilisateur connecté.", "operationId": "me_auth_me_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/categories": {"get": {"tags": ["Référentiels"], "summary": "Get Categories", "description": "Toutes les catégories de monitoring.", "operationId": "get_categories_categories_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "post": {"tags": ["Référentiels"], "summary": "Create Categorie", "operationId": "create_categorie_categories_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/CategorieBody"}}}, "required": true}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/categories/{id_categorie}": {"put": {"tags": ["Référentiels"], "summary": "Update Categorie", "operationId": "update_categorie_categories__id_categorie__put", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "id_categorie", "in": "path", "required": true, "schema": {"type": "integer", "title": "Id Categorie"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/CategorieBody"}}}}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "delete": {"tags": ["Référentiels"], "summary": "Delete Categorie", "description": "Supprime une catégorie, sauf si des monitorings s'y rattachent encore :\nla clé étrangère FK_NOM_CATEGORIE l'interdirait, autant renvoyer un\nmessage explicite plutôt que de laisser remonter l'erreur SQL brute.", "operationId": "delete_categorie_categories__id_categorie__delete", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "id_categorie", "in": "path", "required": true, "schema": {"type": "integer", "title": "Id Categorie"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/services": {"get": {"tags": ["Référentiels"], "summary": "Get Services", "description": "Tous les services.", "operationId": "get_services_services_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "post": {"tags": ["Référentiels"], "summary": "Create Service", "operationId": "create_service_services_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/ServiceBody"}}}, "required": true}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/services/{id_service}": {"put": {"tags": ["Référentiels"], "summary": "Update Service", "operationId": "update_service_services__id_service__put", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "id_service", "in": "path", "required": true, "schema": {"type": "integer", "title": "Id Service"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ServiceBody"}}}}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "delete": {"tags": ["Référentiels"], "summary": "Delete Service", "description": "Refuse la suppression tant que des monitorings ou contacts y sont rattachés.", "operationId": "delete_service_services__id_service__delete", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "id_service", "in": "path", "required": true, "schema": {"type": "integer", "title": "Id Service"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/contacts": {"get": {"tags": ["Référentiels"], "summary": "Get Contacts", "description": "Contacts, filtrables par service.", "operationId": "get_contacts_contacts_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "id_service", "in": "query", "required": false, "schema": {"anyOf": [{"type": "integer"}, {"type": "null"}], "description": "Filtrer par service", "title": "Id Service"}, "description": "Filtrer par service"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "post": {"tags": ["Référentiels"], "summary": "Create Contact", "operationId": "create_contact_contacts_post", "security": [{"OAuth2PasswordBearer": []}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ContactBody"}}}}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/contacts/{id_contact}": {"put": {"tags": ["Référentiels"], "summary": "Update Contact", "operationId": "update_contact_contacts__id_contact__put", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "id_contact", "in": "path", "required": true, "schema": {"type": "integer", "title": "Id Contact"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ContactBody"}}}}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "delete": {"tags": ["Référentiels"], "summary": "Delete Contact", "description": "Un contact n'est référencé par aucune autre table : suppression directe.", "operationId": "delete_contact_contacts__id_contact__delete", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "id_contact", "in": "path", "required": true, "schema": {"type": "integer", "title": "Id Contact"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/monitorings": {"get": {"tags": ["Monitorings"], "summary": "Get Monitorings", "description": "Liste des monitorings actifs avec table_source et bdd_source.", "operationId": "get_monitorings_monitorings_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "id_service", "in": "query", "required": false, "schema": {"anyOf": [{"type": "integer"}, {"type": "null"}], "description": "Filtrer par service", "title": "Id Service"}, "description": "Filtrer par service"}, {"name": "id_categorie", "in": "query", "required": false, "schema": {"anyOf": [{"type": "integer"}, {"type": "null"}], "description": "Filtrer par catégorie", "title": "Id Categorie"}, "description": "Filtrer par catégorie"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "post": {"tags": ["Monitorings"], "summary": "Create Monitoring", "description": "Déclare un monitoring dans la nomenclature.\n\nAttention : cela n'ajoute que l'entrée de référentiel. Pour qu'il remonte\ndes données, il faut aussi créer la table MONITO_[NOM] correspondante et\nl'ajouter au mapping MONITO_TABLES — voir l'en-tête de data_sentinel_init.sql.", "operationId": "create_monitoring_monitorings_post", "security": [{"OAuth2PasswordBearer": []}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/MonitoringBody"}}}}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/monitorings/{id_monito}": {"put": {"tags": ["Monitorings"], "summary": "Update Monitoring", "operationId": "update_monitoring_monitorings__id_monito__put", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "id_monito", "in": "path", "required": true, "schema": {"type": "integer", "title": "Id Monito"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/MonitoringUpdate"}}}}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "delete": {"tags": ["Monitorings"], "summary": "Desactiver Monitoring", "description": "Désactivation (actif = 0), pas de suppression physique : TABLE_FINAL\nréférence id_monito et l'historique doit rester consultable.", "operationId": "desactiver_monitoring_monitorings__id_monito__delete", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "id_monito", "in": "path", "required": true, "schema": {"type": "integer", "title": "Id Monito"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "get": {"tags": ["Monitorings"], "summary": "Get Monitoring By Id", "description": "Détail d'un monitoring.", "operationId": "get_monitoring_by_id_monitorings__id_monito__get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "id_monito", "in": "path", "required": true, "schema": {"type": "integer", "title": "Id Monito"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/monitorings/{id_monito}/details": {"get": {"tags": ["Monitorings"], "summary": "Get Monitoring Details", "description": "Retourne toutes les lignes en erreur depuis la table MONITO_[NOM] dédiée.\nToutes les colonnes métier sont exposées → utilisable pour export CSV côté frontend.", "operationId": "get_monitoring_details_monitorings__id_monito__details_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "id_monito", "in": "path", "required": true, "schema": {"type": "integer", "title": "Id Monito"}}, {"name": "search", "in": "query", "required": false, "schema": {"anyOf": [{"type": "string"}, {"type": "null"}], "description": "Recherche sur colonnes texte (ct_intitule, agence, STE...)", "title": "Search"}, "description": "Recherche sur colonnes texte (ct_intitule, agence, STE...)"}, {"name": "limit", "in": "query", "required": false, "schema": {"type": "integer", "maximum": 5000, "minimum": 1, "description": "Lignes max — mettre 5000 pour export CSV complet", "default": 500, "title": "Limit"}, "description": "Lignes max — mettre 5000 pour export CSV complet"}, {"name": "offset", "in": "query", "required": false, "schema": {"type": "integer", "minimum": 0, "description": "Offset pagination", "default": 0, "title": "Offset"}, "description": "Offset pagination"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/monitorings/{id_monito}/count": {"get": {"tags": ["Monitorings"], "summary": "Get Monitoring Count", "description": "Nombre d'erreurs dans la table dédiée du monitoring.", "operationId": "get_monitoring_count_monitorings__id_monito__count_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "id_monito", "in": "path", "required": true, "schema": {"type": "integer", "title": "Id Monito"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/monitorings/{id_monito}/columns": {"get": {"tags": ["Monitorings"], "summary": "Get Monitoring Columns", "description": "Retourne les colonnes de la table dédiée.\nPermet au frontend de générer dynamiquement les en-têtes du tableau.", "operationId": "get_monitoring_columns_monitorings__id_monito__columns_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "id_monito", "in": "path", "required": true, "schema": {"type": "integer", "title": "Id Monito"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/dashboard": {"get": {"tags": ["Dashboard"], "summary": "Get Dashboard", "description": "Vue consolidée — source principale du dashboard.", "operationId": "get_dashboard_dashboard_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "service", "in": "query", "required": false, "schema": {"anyOf": [{"type": "string"}, {"type": "null"}], "description": "Filtrer par service", "title": "Service"}, "description": "Filtrer par service"}, {"name": "categorie", "in": "query", "required": false, "schema": {"anyOf": [{"type": "string"}, {"type": "null"}], "description": "Filtrer par catégorie", "title": "Categorie"}, "description": "Filtrer par catégorie"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/dashboard/filtres": {"get": {"tags": ["Dashboard"], "summary": "Get Dashboard Filtres", "description": "Valeurs de filtre réellement présentes dans VUE_CONSO.\n\nLes référentiels SERVICE / CATEGORIE contiennent des entrées auxquelles\naucun monitoring actif n'est rattaché (ex. « Business Intelligence »).\nLes proposer dans les menus déroulants du dashboard mène à un écran vide :\ncet endpoint ne renvoie que les valeurs qui ramènent au moins un monitoring.\n\n`combinaisons` permet au frontend de restreindre les catégories proposées\nau service sélectionné.", "operationId": "get_dashboard_filtres_dashboard_filtres_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/dashboard/summary": {"get": {"tags": ["Dashboard"], "summary": "Get Dashboard Summary", "description": "KPI globaux pour les 4 cartes du dashboard.", "operationId": "get_dashboard_summary_dashboard_summary_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/historique": {"get": {"tags": ["Historique"], "summary": "Get Historique", "description": "Snapshots journaliers (TABLE_FINAL).", "operationId": "get_historique_historique_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "id_monito", "in": "query", "required": false, "schema": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Id Monito"}}, {"name": "date_debut", "in": "query", "required": false, "schema": {"anyOf": [{"type": "string", "format": "date"}, {"type": "null"}], "description": "YYYY-MM-DD", "title": "Date Debut"}, "description": "YYYY-MM-DD"}, {"name": "date_fin", "in": "query", "required": false, "schema": {"anyOf": [{"type": "string", "format": "date"}, {"type": "null"}], "description": "YYYY-MM-DD", "title": "Date Fin"}, "description": "YYYY-MM-DD"}, {"name": "service", "in": "query", "required": false, "schema": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Service"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/historique/comparaison": {"get": {"tags": ["Historique"], "summary": "Get Comparaison", "description": "Déprécié — utiliser GET /evolution/par-monitoring à la place.\nConservé pour compatibilité ascendante.", "operationId": "get_comparaison_historique_comparaison_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "date_debut", "in": "query", "required": false, "schema": {"anyOf": [{"type": "string", "format": "date"}, {"type": "null"}], "title": "Date Debut"}}, {"name": "date_fin", "in": "query", "required": false, "schema": {"anyOf": [{"type": "string", "format": "date"}, {"type": "null"}], "title": "Date Fin"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/historique/{id_monito}/evolution": {"get": {"tags": ["Historique"], "summary": "Get Evolution", "description": "Évolution d'un monitoring — format optimisé Recharts.", "operationId": "get_evolution_historique__id_monito__evolution_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "id_monito", "in": "path", "required": true, "schema": {"type": "integer", "title": "Id Monito"}}, {"name": "date_debut", "in": "query", "required": false, "schema": {"anyOf": [{"type": "string", "format": "date"}, {"type": "null"}], "title": "Date Debut"}}, {"name": "date_fin", "in": "query", "required": false, "schema": {"anyOf": [{"type": "string", "format": "date"}, {"type": "null"}], "title": "Date Fin"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/evolution/global": {"get": {"tags": ["Évolution globale"], "summary": "Get Evolution Global", "description": "Courbe globale : total des erreurs tous monitorings confondus, par jour.\nSource : VUE_TABLE_FINAL_CONSO WHERE type_agregat = 'GLOBAL'\nFormat optimisé Recharts → [{date, nb_erreurs}]", "operationId": "get_evolution_global_evolution_global_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "date_debut", "in": "query", "required": false, "schema": {"anyOf": [{"type": "string", "format": "date"}, {"type": "null"}], "description": "YYYY-MM-DD", "title": "Date Debut"}, "description": "YYYY-MM-DD"}, {"name": "date_fin", "in": "query", "required": false, "schema": {"anyOf": [{"type": "string", "format": "date"}, {"type": "null"}], "description": "YYYY-MM-DD", "title": "Date Fin"}, "description": "YYYY-MM-DD"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/evolution/par-monitoring": {"get": {"tags": ["Évolution globale"], "summary": "Get Evolution Par Monitoring", "description": "Évolution par monitoring — toutes les courbes individuelles.\nSource : VUE_TABLE_FINAL_CONSO WHERE type_agregat = 'MONITO'\n\nRetourne un format pivot par monitoring :\n{\n \"monitorings\": [{\"id\": 1, \"nom\": \"...\", \"serie\": [{\"date\",\"nb_erreurs\"}]}]\n}\nUtile pour un graphique multi-lignes Recharts (une ligne par monitoring).", "operationId": "get_evolution_par_monitoring_evolution_par_monitoring_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "date_debut", "in": "query", "required": false, "schema": {"anyOf": [{"type": "string", "format": "date"}, {"type": "null"}], "description": "YYYY-MM-DD", "title": "Date Debut"}, "description": "YYYY-MM-DD"}, {"name": "date_fin", "in": "query", "required": false, "schema": {"anyOf": [{"type": "string", "format": "date"}, {"type": "null"}], "description": "YYYY-MM-DD", "title": "Date Fin"}, "description": "YYYY-MM-DD"}, {"name": "id_monito", "in": "query", "required": false, "schema": {"anyOf": [{"type": "integer"}, {"type": "null"}], "description": "Filtrer sur un seul monitoring", "title": "Id Monito"}, "description": "Filtrer sur un seul monitoring"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/evolution/par-service": {"get": {"tags": ["Évolution globale"], "summary": "Get Evolution Par Service", "description": "Évolution par service (Contrat / Fournisseur) — 1 courbe par service.\nSource : VUE_TABLE_FINAL_CONSO WHERE type_agregat = 'SERVICE'\n\nRetourne :\n{\n \"services\": [{\"service\": \"Contrat\", \"serie\": [{\"date\",\"nb_erreurs\"}]}]\n}", "operationId": "get_evolution_par_service_evolution_par_service_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "date_debut", "in": "query", "required": false, "schema": {"anyOf": [{"type": "string", "format": "date"}, {"type": "null"}], "description": "YYYY-MM-DD", "title": "Date Debut"}, "description": "YYYY-MM-DD"}, {"name": "date_fin", "in": "query", "required": false, "schema": {"anyOf": [{"type": "string", "format": "date"}, {"type": "null"}], "description": "YYYY-MM-DD", "title": "Date Fin"}, "description": "YYYY-MM-DD"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/admin/users": {"get": {"tags": ["Admin"], "summary": "List Users", "operationId": "list_users_admin_users_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "post": {"tags": ["Admin"], "summary": "Create User", "operationId": "create_user_admin_users_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/UserCreate"}}}, "required": true}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/admin/users/{id_user}": {"put": {"tags": ["Admin"], "summary": "Update User", "operationId": "update_user_admin_users__id_user__put", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "id_user", "in": "path", "required": true, "schema": {"type": "integer", "title": "Id User"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/UserUpdate"}}}}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "delete": {"tags": ["Admin"], "summary": "Delete User", "description": "Suppression douce (actif = 0).", "operationId": "delete_user_admin_users__id_user__delete", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "id_user", "in": "path", "required": true, "schema": {"type": "integer", "title": "Id User"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/admin/users/{id_user}/reset-password": {"post": {"tags": ["Admin"], "summary": "Reset Password", "operationId": "reset_password_admin_users__id_user__reset_password_post", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "id_user", "in": "path", "required": true, "schema": {"type": "integer", "title": "Id User"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/PasswordReset"}}}}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/admin/journal": {"get": {"tags": ["Admin"], "summary": "Get Journal", "operationId": "get_journal_admin_journal_get", "security": [{"OAuth2PasswordBearer": []}], "parameters": [{"name": "limit", "in": "query", "required": false, "schema": {"type": "integer", "default": 200, "title": "Limit"}}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/me/data-export": {"get": {"tags": ["RGPD"], "summary": "Export My Data", "description": "Droit à la portabilité (art. 20) : export des données de l'utilisateur.", "operationId": "export_my_data_me_data_export_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/me": {"delete": {"tags": ["RGPD"], "summary": "Delete My Account", "description": "Droit à l'oubli (art. 17) : désactivation + anonymisation.", "operationId": "delete_my_account_me_delete", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}}, "components": {"schemas": {"Body_login_auth_login_post": {"properties": {"grant_type": {"anyOf": [{"type": "string", "pattern": "^password$"}, {"type": "null"}], "title": "Grant Type"}, "username": {"type": "string", "title": "Username"}, "password": {"type": "string", "format": "password", "title": "Password"}, "scope": {"type": "string", "title": "Scope", "default": ""}, "client_id": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Client Id"}, "client_secret": {"anyOf": [{"type": "string"}, {"type": "null"}], "format": "password", "title": "Client Secret"}}, "type": "object", "required": ["username", "password"], "title": "Body_login_auth_login_post"}, "CategorieBody": {"properties": {"intitule_categorie": {"type": "string", "maxLength": 100, "minLength": 1, "title": "Intitule Categorie"}}, "type": "object", "required": ["intitule_categorie"], "title": "CategorieBody"}, "ContactBody": {"properties": {"id_service": {"type": "integer", "title": "Id Service"}, "intitule_contact": {"type": "string", "maxLength": 100, "minLength": 1, "title": "Intitule Contact"}, "nom": {"type": "string", "maxLength": 100, "minLength": 1, "title": "Nom"}, "prenom": {"type": "string", "maxLength": 100, "minLength": 1, "title": "Prenom"}, "mail": {"type": "string", "maxLength": 200, "minLength": 1, "title": "Mail"}}, "type": "object", "required": ["id_service", "intitule_contact", "nom", "prenom", "mail"], "title": "ContactBody"}, "HTTPValidationError": {"properties": {"detail": {"items": {"$ref": "#/components/schemas/ValidationError"}, "type": "array", "title": "Detail"}}, "type": "object", "title": "HTTPValidationError"}, "MonitoringBody": {"properties": {"monito_intitule": {"type": "string", "maxLength": 200, "minLength": 1, "title": "Monito Intitule"}, "id_service": {"type": "integer", "title": "Id Service"}, "id_categorie": {"type": "integer", "title": "Id Categorie"}, "table_source": {"type": "string", "maxLength": 100, "minLength": 1, "title": "Table Source"}, "bdd_source": {"type": "string", "maxLength": 50, "minLength": 1, "title": "Bdd Source"}}, "type": "object", "required": ["monito_intitule", "id_service", "id_categorie", "table_source", "bdd_source"], "title": "MonitoringBody"}, "MonitoringUpdate": {"properties": {"monito_intitule": {"anyOf": [{"type": "string", "maxLength": 200, "minLength": 1}, {"type": "null"}], "title": "Monito Intitule"}, "id_service": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Id Service"}, "id_categorie": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Id Categorie"}, "table_source": {"anyOf": [{"type": "string", "maxLength": 100, "minLength": 1}, {"type": "null"}], "title": "Table Source"}, "bdd_source": {"anyOf": [{"type": "string", "maxLength": 50, "minLength": 1}, {"type": "null"}], "title": "Bdd Source"}, "actif": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Actif"}}, "type": "object", "title": "MonitoringUpdate"}, "PasswordReset": {"properties": {"password": {"type": "string", "title": "Password"}}, "type": "object", "required": ["password"], "title": "PasswordReset"}, "ServiceBody": {"properties": {"nom_service": {"type": "string", "maxLength": 100, "minLength": 1, "title": "Nom Service"}}, "type": "object", "required": ["nom_service"], "title": "ServiceBody"}, "UserCreate": {"properties": {"username": {"type": "string", "title": "Username"}, "email": {"type": "string", "title": "Email"}, "password": {"type": "string", "title": "Password"}, "role": {"$ref": "#/components/schemas/UserRole"}}, "type": "object", "required": ["username", "email", "password", "role"], "title": "UserCreate"}, "UserRole": {"type": "string", "enum": ["Admin", "Superviseur", "Consultant"], "title": "UserRole", "description": "Rôles applicatifs — miroir de la contrainte CK_USER_ROLE en base."}, "UserUpdate": {"properties": {"email": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Email"}, "role": {"anyOf": [{"$ref": "#/components/schemas/UserRole"}, {"type": "null"}]}, "actif": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "title": "Actif"}}, "type": "object", "title": "UserUpdate"}, "ValidationError": {"properties": {"loc": {"items": {"anyOf": [{"type": "string"}, {"type": "integer"}]}, "type": "array", "title": "Location"}, "msg": {"type": "string", "title": "Message"}, "type": {"type": "string", "title": "Error Type"}, "input": {"title": "Input"}, "ctx": {"type": "object", "title": "Context"}}, "type": "object", "required": ["loc", "msg", "type"], "title": "ValidationError"}}, "securitySchemes": {"OAuth2PasswordBearer": {"type": "oauth2", "flows": {"password": {"scopes": {}, "tokenUrl": "auth/login"}}}}}} \ No newline at end of file