from fastapi import FastAPI, Request from fastapi.responses import HTMLResponse, RedirectResponse from fastapi.staticfiles import StaticFiles from fastapi.templating import Jinja2Templates from fastapi.exceptions import HTTPException from starlette.middleware.base import BaseHTTPMiddleware from database import init_db from routers import clients, devis, factures from routers import auth as auth_router from config import settings app = FastAPI(title=settings.app_name) app.mount("/static", StaticFiles(directory="static"), name="static") templates = Jinja2Templates(directory="templates") app.include_router(auth_router.router) app.include_router(clients.router) app.include_router(devis.router) app.include_router(factures.router) # ── Middleware : current_user dans request.state ─────────────────────────────── class AuthMiddleware(BaseHTTPMiddleware): async def dispatch(self, request: Request, call_next): from database import SessionLocal from auth import redirect_if_not_logged db = SessionLocal() try: request.state.current_user = redirect_if_not_logged(request, db) except Exception: request.state.current_user = None finally: db.close() try: return await call_next(request) except Exception: request.state.current_user = None raise app.add_middleware(AuthMiddleware) # ── Startup ──────────────────────────────────────────────────────────────────── @app.on_event("startup") def startup(): init_db() _creer_admin_si_absent() def _creer_admin_si_absent(): from database import SessionLocal from models import User from auth import hasher_mot_de_passe db = SessionLocal() try: if db.query(User).count() == 0: admin = User( username="admin", hashed_password=hasher_mot_de_passe("admin"), is_admin=True, ) db.add(admin) db.commit() print("⚠️ Compte admin créé : admin / admin — À changer immédiatement !") finally: db.close() # ── Gestion erreurs ──────────────────────────────────────────────────────────── @app.exception_handler(HTTPException) async def http_exception_handler(request: Request, exc: HTTPException): if exc.status_code == 302: return RedirectResponse(url=exc.headers["Location"]) return templates.TemplateResponse("erreur.html", { "request": request, "status_code": exc.status_code, "detail": exc.detail, "current_user": getattr(request.state, "current_user", None), }, status_code=exc.status_code) @app.get("/", response_class=HTMLResponse) def accueil(): return RedirectResponse("/factures/") # ── Filtres Jinja2 ───────────────────────────────────────────────────────────── def format_montant(valeur): if valeur is None: return "0,00 €" return f"{valeur:,.2f} €".replace(",", " ").replace(".", ",") def format_date_fr(d): if d is None: return "" mois = ["janvier", "février", "mars", "avril", "mai", "juin", "juillet", "août", "septembre", "octobre", "novembre", "décembre"] return f"{d.day} {mois[d.month - 1]} {d.year}" for tpl in [templates, clients.templates, devis.templates, factures.templates, auth_router.templates]: tpl.env.filters["montant"] = format_montant tpl.env.filters["date_fr"] = format_date_fr