main.py comptait 829 lignes et regroupait 9 domaines fonctionnels. Il ne fait plus que l'assemblage de l'application (configuration, middlewares, montage des routeurs), soit 115 lignes. - routers/ : un module par domaine, chacun déclarant sa propre dépendance d'authentification - domain.py : enums UserRole et AuditAction, mapping MONITO_TABLES ; les rôles étaient jusqu'ici répétés en dur à deux endroits - helpers.py : conversion des lignes pyodbc, écriture du journal - rate_limit.py : limiteur partagé, isolé pour éviter un import circulaire entre main.py et le routeur d'authentification Les codes HTTP littéraux (404, 401, 400, 201) passent aux constantes fastapi.status, comme le faisait déjà auth.py. Les actions du journal d'audit passent en paramètre SQL au lieu d'être concaténées. Aucune route modifiée : la comparaison des specs OpenAPI avant/après confirme que les 26 URL existantes sont identiques. conftest patchait main.get_cursor ; chaque routeur important désormais get_cursor dans son propre espace de noms, la fixture remplace le nom dans tous les modules concernés.
116 lines
3.6 KiB
Python
116 lines
3.6 KiB
Python
# ============================================================
|
|
# main.py — API FastAPI Data Sentinel
|
|
# COYAUD Anthony | 2026
|
|
# V2 : une table dédiée par monitoring
|
|
#
|
|
# Ce module ne contient que l'assemblage de l'application :
|
|
# configuration, middlewares et montage des routeurs. Chaque
|
|
# domaine fonctionnel vit dans routers/.
|
|
#
|
|
# Lancement : uvicorn main:app --reload --port 8000
|
|
# Swagger : http://localhost:8000/docs
|
|
# ============================================================
|
|
|
|
import os
|
|
|
|
from fastapi import FastAPI, Request
|
|
from fastapi.middleware.cors import CORSMiddleware
|
|
|
|
from slowapi import _rate_limit_exceeded_handler
|
|
from slowapi.errors import RateLimitExceeded
|
|
|
|
from config import Config, get_cursor
|
|
from domain import MONITO_TABLES
|
|
from rate_limit import limiter
|
|
from routers import (
|
|
admin,
|
|
authentification,
|
|
dashboard,
|
|
evolution,
|
|
historique,
|
|
monitorings,
|
|
referentiels,
|
|
rgpd,
|
|
)
|
|
|
|
# Origines autorisées : depuis CORS_ORIGINS (séparées par des virgules) en prod,
|
|
# localhost par défaut en dev.
|
|
CORS_ORIGINS = [
|
|
o.strip()
|
|
for o in os.getenv("CORS_ORIGINS", "http://localhost:5173,http://localhost:3000").split(",")
|
|
if o.strip()
|
|
]
|
|
|
|
# ============================================================
|
|
# Initialisation
|
|
# ============================================================
|
|
|
|
app = FastAPI(
|
|
title = Config.API_TITLE,
|
|
version = Config.API_VERSION,
|
|
description = Config.API_DESCRIPTION,
|
|
)
|
|
|
|
# Limiteur de débit (anti brute-force sur /auth/login).
|
|
app.state.limiter = limiter
|
|
app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)
|
|
|
|
app.add_middleware(
|
|
CORSMiddleware,
|
|
allow_origins = CORS_ORIGINS,
|
|
allow_methods = ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
|
|
allow_headers = ["*"],
|
|
allow_credentials = True,
|
|
)
|
|
|
|
|
|
# En-têtes de sécurité sur toutes les réponses.
|
|
@app.middleware("http")
|
|
async def security_headers(request: Request, call_next):
|
|
response = await call_next(request)
|
|
response.headers["X-Content-Type-Options"] = "nosniff"
|
|
response.headers["X-Frame-Options"] = "DENY"
|
|
response.headers["Referrer-Policy"] = "strict-origin-when-cross-origin"
|
|
response.headers["Strict-Transport-Security"] = "max-age=31536000; includeSubDomains"
|
|
return response
|
|
|
|
|
|
# ============================================================
|
|
# SANTÉ — route publique
|
|
# ============================================================
|
|
|
|
@app.get("/health", tags=["Système"])
|
|
def health_check():
|
|
"""Ping API + test connexion SQL Server."""
|
|
try:
|
|
with get_cursor() as cursor:
|
|
cursor.execute("SELECT 1")
|
|
cursor.fetchone()
|
|
db_status = "ok"
|
|
except Exception as e:
|
|
db_status = f"error: {str(e)}"
|
|
|
|
return {
|
|
"api" : "ok",
|
|
"database" : db_status,
|
|
"version" : Config.API_VERSION,
|
|
"nb_monitorings" : len(MONITO_TABLES),
|
|
}
|
|
|
|
|
|
# ============================================================
|
|
# Montage des routeurs
|
|
#
|
|
# Les routeurs de données déclarent eux-mêmes leur dépendance
|
|
# d'authentification. /health et /auth/login restent publics.
|
|
# ============================================================
|
|
|
|
app.include_router(authentification.router)
|
|
app.include_router(referentiels.router)
|
|
app.include_router(monitorings.router, prefix="/monitorings")
|
|
app.include_router(dashboard.router, prefix="/dashboard")
|
|
app.include_router(historique.router, prefix="/historique")
|
|
app.include_router(evolution.router, prefix="/evolution")
|
|
app.include_router(admin.router)
|
|
app.include_router(rgpd.router)
|