from __future__ import annotations import uuid from fastapi import APIRouter, Depends, HTTPException from pydantic import BaseModel, Field from auth import create_session, hash_password, require_auth, verify_password from db import get_db, row_to_dict from version import APP_VERSION router = APIRouter(prefix="/api/auth", tags=["auth"]) class LoginRequest(BaseModel): email: str password: str = Field(min_length=4) class SetupRequest(BaseModel): email: str name: str = Field(min_length=1) password: str = Field(min_length=4) def _profile_count() -> int: with get_db() as conn: row = conn.execute("SELECT COUNT(*) AS n FROM profiles").fetchone() return int(row["n"]) @router.get("/status") def auth_status(): return { "status": "ok", "service": "kansho", "version": APP_VERSION, "needs_setup": _profile_count() == 0, } @router.post("/setup") def setup(req: SetupRequest): if _profile_count() > 0: raise HTTPException(409, "Setup ist bereits abgeschlossen") profile_id = str(uuid.uuid4()) with get_db() as conn: conn.execute( """ INSERT INTO profiles (id, email, name, password_hash, role) VALUES (?, ?, ?, ?, 'admin') """, (profile_id, req.email.lower().strip(), req.name.strip(), hash_password(req.password)), ) token, expires = create_session(profile_id) return { "token": token, "profile_id": profile_id, "name": req.name.strip(), "role": "admin", "expires_at": expires.isoformat(), } @router.post("/login") def login(req: LoginRequest): with get_db() as conn: prof = row_to_dict( conn.execute( "SELECT * FROM profiles WHERE email = ?", (req.email.lower().strip(),), ).fetchone() ) if not prof or not verify_password(req.password, prof["password_hash"]): raise HTTPException(401, "Ungültige Zugangsdaten") if (prof.get("status") or "active") != "active": raise HTTPException(401, "Ungültige Zugangsdaten") token, expires = create_session(prof["id"], prof.get("session_days") or 30) return { "token": token, "profile_id": prof["id"], "name": prof["name"], "role": prof["role"], "expires_at": expires.isoformat(), } @router.post("/logout") def logout(session: dict = Depends(require_auth)): with get_db() as conn: conn.execute("DELETE FROM sessions WHERE token = ?", (session["token"],)) return {"ok": True} @router.get("/me") def me(session: dict = Depends(require_auth)): with get_db() as conn: prof = row_to_dict( conn.execute("SELECT id, email, name, role, status, tier_id FROM profiles WHERE id = ?", (session["profile_id"],)).fetchone() ) if not prof: raise HTTPException(401, "Profil nicht gefunden") return prof