refactor: decompose monolith — app.py 10,600→143 lines, 30+ modules
Fase 0 complete: extract all business logic, auth, database, and 176 endpoints from monolithic app.py into dedicated modules. Structure: - app.py: FastAPI hub (CORS, routers, startup/shutdown) - models.py: 13 Pydantic request models - utils.py: shared utilities (embed status, upload validation, process registries) - config.py: constants, env vars, model catalogs - auth/: crypto, jwt_auth, oidc, rate_limit - database/: db(), init_db(), schema DDL - services/: genai, compliance, chat, terraform, embeddings, cis_reports, mcp - routes/: 13 APIRouter modules (auth, users, oci_config, oci_explorer, genai, mcp, adb, embeddings, reports, chat, terraform, settings, cis_engine) Also: README updated with OCIR auth instructions for manual docker run. 86 tests passing.
This commit is contained in:
64
backend/routes/users.py
Normal file
64
backend/routes/users.py
Normal file
@@ -0,0 +1,64 @@
|
||||
"""User routes — list, me, update, delete, timezone."""
|
||||
from fastapi import APIRouter, HTTPException, Depends
|
||||
|
||||
from database import db
|
||||
from auth.jwt_auth import current_user, require, _audit
|
||||
from models import UserUpdateReq
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
# ── Users ─────────────────────────────────────────────────────────────────────
|
||||
@router.get("/api/users")
|
||||
async def list_users(u=Depends(require("admin"))):
|
||||
with db() as c: rows = c.execute("SELECT id,first_name,last_name,username,email,role,mfa_enabled,timezone,is_active,created_at,last_login,auth_provider FROM users").fetchall()
|
||||
return [dict(r) for r in rows]
|
||||
|
||||
@router.get("/api/users/me")
|
||||
async def me(u=Depends(current_user)):
|
||||
fields = ("id","first_name","last_name","username","email","role","mfa_enabled","timezone","auth_provider","must_change_password")
|
||||
r = {k: u.get(k, "local" if k == "auth_provider" else None) for k in fields}
|
||||
r["must_change_password"] = bool(r.get("must_change_password", 0))
|
||||
return r
|
||||
|
||||
@router.put("/api/users/{uid}")
|
||||
async def update_user(uid: str, req: UserUpdateReq, adm=Depends(require("admin"))):
|
||||
sets, vals = [], []
|
||||
if req.email is not None: sets.append("email=?"); vals.append(req.email)
|
||||
if req.role is not None:
|
||||
if req.role not in ("admin","user","viewer"): raise HTTPException(400, "Role inválida")
|
||||
sets.append("role=?"); vals.append(req.role)
|
||||
if req.is_active is not None: sets.append("is_active=?"); vals.append(int(req.is_active))
|
||||
if req.timezone is not None: sets.append("timezone=?"); vals.append(req.timezone)
|
||||
if sets:
|
||||
vals.append(uid)
|
||||
with db() as c: c.execute(f"UPDATE users SET {','.join(sets)} WHERE id=?", vals)
|
||||
_audit(adm["id"], adm["username"], "update_user", uid)
|
||||
return {"ok": True}
|
||||
|
||||
@router.delete("/api/users/{uid}")
|
||||
async def del_user(uid: str, adm=Depends(require("admin"))):
|
||||
if uid == adm["id"]: raise HTTPException(400, "Não pode desativar a si mesmo")
|
||||
with db() as c: c.execute("UPDATE users SET is_active=0 WHERE id=?", (uid,))
|
||||
_audit(adm["id"], adm["username"], "deactivate_user", uid)
|
||||
return {"ok": True}
|
||||
|
||||
@router.put("/api/users/me/timezone")
|
||||
async def set_user_timezone(body: dict, u=Depends(current_user)):
|
||||
"""Set the current user's timezone."""
|
||||
tz = body.get("timezone", "").strip()
|
||||
if not tz:
|
||||
raise HTTPException(400, "timezone is required")
|
||||
try:
|
||||
import zoneinfo
|
||||
zoneinfo.ZoneInfo(tz)
|
||||
except Exception:
|
||||
raise HTTPException(400, f"Invalid timezone: {tz}")
|
||||
with db() as c:
|
||||
c.execute("UPDATE users SET timezone=? WHERE id=?", (tz, u["id"]))
|
||||
_audit(u["id"], u["username"], "set_timezone", tz)
|
||||
return {"ok": True, "timezone": tz}
|
||||
|
||||
@router.get("/api/users/me/timezone")
|
||||
async def get_user_timezone(u=Depends(current_user)):
|
||||
"""Get the current user's timezone."""
|
||||
return {"timezone": u.get("timezone", "America/Sao_Paulo")}
|
||||
Reference in New Issue
Block a user