from fastapi import APIRouter, Request, Depends, Form from fastapi.responses import HTMLResponse, RedirectResponse from fastapi.templating import Jinja2Templates from sqlalchemy import text from ..dependencies import get_db, get_current_user from ..auth import verify_password, create_access_token, hash_password from ..services.audit_service import log_login, log_logout, log_login_failed from ..config import APP_NAME, APP_VERSION router = APIRouter() templates = Jinja2Templates(directory="app/templates") @router.get("/login", response_class=HTMLResponse) async def login_page(request: Request): return templates.TemplateResponse("login.html", { "request": request, "app_name": APP_NAME, "version": APP_VERSION, "error": None }) @router.post("/login") async def login(request: Request, username: str = Form(...), password: str = Form(...), db=Depends(get_db)): row = db.execute(text("SELECT id, username, password_hash, role FROM users WHERE LOWER(username) = LOWER(:u)"), {"u": username}).fetchone() if not row: log_login_failed(db, request, username) db.commit() return templates.TemplateResponse("login.html", { "request": request, "app_name": APP_NAME, "version": APP_VERSION, "error": "Utilisateur inconnu" }) try: ok = verify_password(password, row.password_hash) except Exception: ok = False if not ok: log_login_failed(db, request, username) db.commit() return templates.TemplateResponse("login.html", { "request": request, "app_name": APP_NAME, "version": APP_VERSION, "error": "Mot de passe incorrect" }) token = create_access_token({"sub": row.username, "role": row.role, "uid": row.id}) user = {"sub": row.username, "role": row.role, "uid": row.id} log_login(db, request, user) db.commit() response = RedirectResponse(url="/dashboard", status_code=303) response.set_cookie(key="access_token", value=token, httponly=True, samesite="lax", max_age=3600) return response @router.get("/logout") async def logout(request: Request, db=Depends(get_db)): user = get_current_user(request) if user: log_logout(db, request, user) db.commit() response = RedirectResponse(url="/login", status_code=302) response.delete_cookie("access_token") return response