import bcrypt from fastapi import APIRouter, Depends, Form, HTTPException, Request from fastapi.responses import RedirectResponse from db import get_database_connection from templating import templates router = APIRouter() def get_current_user(request: Request) -> dict: user = request.session.get("user") if user is None: raise HTTPException(status_code=303, headers={"Location": "/login"}) return user def require_admin(user: dict = Depends(get_current_user)) -> dict: if not user.get("ist_admin"): raise HTTPException(status_code=403, detail="Kein Admin-Zugriff.") return user @router.get("/login") def login_form(request: Request): if request.session.get("user") is not None: return RedirectResponse("/geraete", status_code=303) return templates.TemplateResponse(request, "login.html", {"error": None}) @router.post("/login") def login_submit(request: Request, email: str = Form(...), password: str = Form(...)): with get_database_connection() as conn: with conn.cursor() as cur: cur.execute( "SELECT id, organization_id, password_hash, ist_admin FROM benutzer WHERE email = %s", (email,), ) row = cur.fetchone() if row is None: return templates.TemplateResponse( request, "login.html", {"error": "E-Mail oder Passwort ist falsch."}, status_code=401 ) # psycopg liefert TEXT-Spalten hier als bytes zurück, nicht als str. stored_hash = row[2] if isinstance(row[2], bytes) else row[2].encode("utf-8") if not bcrypt.checkpw(password.encode("utf-8"), stored_hash): return templates.TemplateResponse( request, "login.html", {"error": "E-Mail oder Passwort ist falsch."}, status_code=401 ) request.session["user"] = { "id": str(row[0]), "organization_id": str(row[1]), "email": email, "ist_admin": row[3], } return RedirectResponse("/geraete", status_code=303) @router.get("/logout") def logout(request: Request): request.session.clear() return RedirectResponse("/login", status_code=303)