Antho e8b4a604e9
Build & Deploy / build (push) Successful in 21s
docs: corrige la référence à GitHub dans le RUNBOOK
Les dépôts sont hébergés sur Gitea (git.nfteam.ovh), jamais sur GitHub.
2026-08-16 14:56:41 +02:00
2026-05-20 10:30:04 +02:00

Api-DataSentinel

API FastAPI de monitoring de la qualité des données — Data Sentinel (XEFI). Backend en lecture seule sur SQL Server, sécurisé par authentification JWT.

Stack

  • Python 3.12 · FastAPI 0.136 · uvicorn
  • SQL Server 2022 via pyodbc (ODBC Driver 18)
  • Auth : JWT (python-jose) + bcrypt (passlib), rate-limit (slowapi)

Lancement en local (dev)

python -m venv .venv
.\.venv\Scripts\Activate.ps1
pip install -r requirements.txt
python -m uvicorn main:app --reload --port 8000
  • Swagger : http://127.0.0.1:8000/docs · Santé : http://127.0.0.1:8000/health

Sans variables d'environnement, l'API se connecte en authentification Windows sur localhost. Pour une instance nommée, définir DB_SERVER (ex. DB_SERVER=MonPoste\SQLEXPRESS, sans DB_PORT).

Configuration (variables d'environnement)

Variable Rôle Défaut
DB_SERVER hôte SQL Server (ou hôte\instance) localhost
DB_PORT / DB_NAME port / base — laisser DB_PORT vide pour une instance nommée — / DataSentinel
DB_USER / DB_PASSWORD compte applicatif ; sa présence active l'authentification SQL — (sinon auth Windows)
DB_TRUSTED_CONNECTION force l'authentification Windows même si DB_USER est défini
DB_DRIVER pilote ODBC ODBC Driver 18 for SQL Server
CORS_ORIGINS origines autorisées (séparées par ,) localhost:5173,localhost:3000
JWT_SECRET clé de signature JWT — obligatoire en déploiement clé aléatoire régénérée à chaque démarrage
APP_BUILD SHA du commit construit, injecté par la CI et exposé par /health local
JWT_ALGORITHM / JWT_EXPIRE_MINUTES algo / durée du token HS256 / 60

Authentification & rôles

  • POST /auth/login (form username/password) → {access_token, token_type, user}. Rate-limité 5/min.
  • GET /auth/me → profil courant.
  • Tous les endpoints de données exigent Authorization: Bearer <token> (401 sinon). Seul /health est public.
  • Rôles : Admin, Superviseur, Consultant. Les routes /admin/* exigent Admin (403 sinon).

Comptes de démo

Identifiant Mot de passe Rôle Usage
Juré (JURE@NEXA.com) 123456 Admin Compte de visite / évaluation — à communiquer aux personnes qui consultent le site
admin Admin2026! Admin Compte d'administration technique
superviseur Super2026! Superviseur Illustration du RBAC (pas d'accès /admin/*)
consultant Conseil2026! Consultant Illustration du RBAC (lecture seule)

Ces comptes sont créés par sql/data_sentinel_auth.sql. Pour (re)créer le seul compte Juré sur une base déjà déployée, sans rejouer tout le script d'auth :

sqlcmd -S <serveur> -d DataSentinel -U <user> -P <mdp> -i sql/create_admin_jure.sql

Les mots de passe ci-dessus sont des identifiants de démonstration : ils sont stockés hachés (bcrypt, coût 12) et doivent être régénérés avant toute mise en production réelle (POST /admin/users/{id}/reset-password).

Organisation du code

main.py ne fait que l'assemblage (configuration, middlewares, montage des routeurs). Chaque domaine fonctionnel vit dans routers/ :

Module Responsabilité
routers/authentification.py /auth/login (rate-limité), /auth/me
routers/referentiels.py services, catégories, contacts — lecture + CRUD Admin
routers/monitorings.py nomenclature + données détaillées par table dédiée
routers/dashboard.py VUE_CONSO : vue consolidée, filtres, KPI
routers/historique.py snapshots journaliers (TABLE_FINAL)
routers/evolution.py VUE_TABLE_FINAL_CONSO : courbes global / monitoring / service
routers/admin.py comptes utilisateurs et journal d'audit
routers/rgpd.py portabilité et droit à l'oubli
domain.py enums (UserRole, AuditAction) et mapping MONITO_TABLES
helpers.py conversion des lignes pyodbc, écriture du journal d'audit

Endpoints (résumé)

  • Données (protégés) : /categories, /services, /contacts, /monitorings[...], /dashboard[...], /historique[...], /evolution/*.
  • Référentiels (Admin en écriture) : POST/PUT/DELETE sur /services, /categories, /contacts et /monitorings. La suppression est refusée (409) tant que des enregistrements y sont rattachés ; un monitoring est désactivé (actif = 0) et jamais supprimé, car TABLE_FINAL référence son identifiant.
  • Admin (Admin) : GET/POST /admin/users, PUT/DELETE /admin/users/{id}, POST /admin/users/{id}/reset-password, GET /admin/journal.
  • RGPD : GET /me/data-export (portabilité), DELETE /me (droit à l'oubli + anonymisation).

Spécification complète : GET /openapi.json (export dans docs/openapi.json).

Vérifier qu'un déploiement a pris

GET /health expose le SHA du commit dont l'image a été construite :

curl -s https://datasentinel-api.nfteam.ovh/health
# {"api":"ok","database":"ok","version":"1.0.0","build":"cb84e20e1f2a",...}

Comparer build au dernier commit poussé sur main. S'ils diffèrent, le conteneur tourne encore une ancienne image : docker compose pull puis docker compose up -d (un up -d seul ne retélécharge rien). Le front expose la même information sur /version.json.

Sécurité

  • JWT_SECRET doit être défini en déploiement. À défaut, l'API démarre quand même mais tire une clé aléatoire à chaque lancement (sessions perdues au redémarrage) : aucun secret de repli n'est écrit dans le dépôt, un secret public permettrait de forger un jeton d'administrateur.
  • En-têtes : X-Content-Type-Options, X-Frame-Options, Referrer-Policy, Strict-Transport-Security.
  • CORS restreint aux origines CORS_ORIGINS, tous verbes + credentials.
  • Requêtes SQL paramétrées (noms de tables/colonnes whitelistés) ; le compte applicatif n'est pas sa. Tables d'auth : [USER] + JOURNAL_AUDIT (sql/data_sentinel_auth.sql, identique au dump livré dans RENDU/02_Dump_SQL/).
  • Journal d'audit alimenté à chaque login + action admin.

Tests

pip install pytest httpx && pytest -q

82 tests, curseur SQL simulé (aucune vraie base requise) :

Fichier Couverture
test_endpoints.py santé, référentiels, authentification, filtres du dashboard, rôles par enum
test_historique.py bornes de dates, validation du format, agrégation des courbes par monitoring et par service
test_securite.py protection 401 de chaque route, RBAC 403, jeton forgé rejeté, en-têtes de sécurité, rate-limit 429, compte désactivé, droits RGPD
test_referentiels.py CRUD complet, refus 409 sur rattachement, modification partielle, paramétrage des requêtes de recherche

Le compteur anti brute-force est remis à zéro entre les tests (fixture limiteur_vierge) : c'est un état global, et sans cela les tests de connexion se comptabilisent entre eux et échouent selon l'ordre d'exécution.

Docker & CI/CD

  • Dockerfile : image Python 3.12 + msodbcsql18.
  • CI Gitea Actions (.gitea/workflows/build.yml) : build → pytest (dans l'image) → push vers git.nfteam.ovh/neckfire/datasentinel-api + notification ntfy. Déclenché sur push main.
  • Déploiement, exploitation, sauvegarde : voir RUNBOOK.md et le stack d'hébergement (homelab/dev/datasentinel/). La base tourne sur un SQL Server partagé (dev-mssql), pas dédiée à l'app.
S
Description
No description provided
Readme
171 KiB
Languages
Python 89.2%
TSQL 9.5%
Dockerfile 1.3%