from pathlib import Path from app.core.app_settings import apply_settings_to_app, get_settings_payload, write_settings from app.core.audit_log import read_audit_events, write_audit_event from app.core.config import settings from app.core.install_secrets import install_secrets_payload, reveal_install_secret from app.core.security import hash_password from app.modules.admin.security_diagnostics import build_security_diagnostics_report from app.modules.auth.service import revoke_user_refresh_family from app.modules.users import repository def list_admin_users(page: int, limit: int) -> dict: users, total = repository.list_users(page, limit) return { "data": [ { "id": user.id, "email": user.email, "role": user.role, "is_superuser": user.is_superuser, "status": user.status, } for user in users ], "meta": {"total": total, "page": page, "limit": limit}, } def _ensure_last_superuser_protection(target, role: str | None, is_superuser: bool | None) -> None: super_count = repository.count_superusers() role_becomes_admin = target.role if role is None else role super_becomes_true = target.is_superuser if is_superuser is None else is_superuser is_losing_super = target.role == "admin" and target.is_superuser and ( role_becomes_admin != "admin" or not super_becomes_true ) if is_losing_super and super_count <= 1: raise ValueError("LAST_SUPERUSER_PROTECTED") def patch_user( admin_user, target_user_id: str, role: str | None, status: str | None, is_superuser: bool | None = None ) -> dict: target = repository.get_user_by_id(target_user_id) if not target: raise ValueError("USER_NOT_FOUND") if admin_user.id == target_user_id and role and role != "admin": raise ValueError("SELF_DEMOTION_FORBIDDEN") if admin_user.id == target_user_id and is_superuser is False: raise ValueError("SELF_DEMOTION_FORBIDDEN") if admin_user.id == target_user_id and status == "blocked": raise ValueError("SELF_BLOCK_FORBIDDEN") admin_count = repository.count_admins() if target.role == "admin" and role and role != "admin" and admin_count <= 1: raise ValueError("LAST_ADMIN_PROTECTED") _ensure_last_superuser_protection(target, role, is_superuser) if role: target.role = role if is_superuser is not None: target.is_superuser = bool(is_superuser) if status: target.status = status if status == "blocked": revoke_user_refresh_family(target.id) repository.update_user(target) write_audit_event( action="admin.user.patch", actor_user_id=admin_user.id, actor_email=admin_user.email, details={"target_user_id": target.id}, ) return { "id": target.id, "email": target.email, "role": target.role, "is_superuser": target.is_superuser, "status": target.status, } def create_admin_user(admin_user, email: str, password: str, role: str, is_superuser: bool, status: str) -> dict: if repository.get_user_by_email(email): raise ValueError("USER_EXISTS") user = repository.create_user( email=email, password_hash=hash_password(password), role=role, is_superuser=is_superuser, status=status, ) write_audit_event( action="admin.user.create", actor_user_id=admin_user.id, actor_email=admin_user.email, details={"target_user_id": user.id}, ) return { "id": user.id, "email": user.email, "role": user.role, "is_superuser": user.is_superuser, "status": user.status, } def reset_user_password(admin_user, target_user_id: str, password: str) -> dict: target = repository.get_user_by_id(target_user_id) if not target: raise ValueError("USER_NOT_FOUND") target.password_hash = hash_password(password) repository.update_user(target) revoke_user_refresh_family(target.id) write_audit_event( action="admin.user.reset_password", actor_user_id=admin_user.id, actor_email=admin_user.email, details={"target_user_id": target.id}, ) return {"status": "ok"} def delete_admin_user(admin_user, target_user_id: str) -> dict: target = repository.get_user_by_id(target_user_id) if not target: raise ValueError("USER_NOT_FOUND") if admin_user.id == target.id: raise ValueError("SELF_DELETE_FORBIDDEN") _ensure_last_superuser_protection(target, "user", False) if target.role == "admin" and repository.count_admins() <= 1: raise ValueError("LAST_ADMIN_PROTECTED") repository.delete_user(target.id) revoke_user_refresh_family(target.id) write_audit_event( action="admin.user.delete", actor_user_id=admin_user.id, actor_email=admin_user.email, details={"target_user_id": target.id}, ) return {"status": "deleted"} def get_admin_summary() -> dict: return { "users_count": repository.count_users(), "registrations_day": repository.count_users_registered_today(), "admins_count": repository.count_admins(), "superusers_count": repository.count_superusers(), } def get_admin_settings() -> dict: return get_settings_payload() def patch_admin_settings(admin_user, values: dict) -> dict: merged = write_settings(values) apply_settings_to_app(merged) write_audit_event( action="admin.settings.patch", actor_user_id=admin_user.id, actor_email=admin_user.email, details={"updated_keys": sorted(values.keys())}, ) return get_settings_payload() def get_diagnostics_report() -> dict: return build_security_diagnostics_report() def list_activity_feed(limit: int = 200) -> dict: return {"events": read_audit_events(limit=limit)} def record_ui_activity(admin_user, event: str, meta: dict | None) -> dict: write_audit_event( action=f"ui.{event}", actor_user_id=admin_user.id, actor_email=admin_user.email, details=meta or {}, ) return {"status": "ok"} def get_server_log_tail(lines: int = 200) -> dict: path = Path(settings.server_log_path) if not path.exists(): return {"lines": []} return {"lines": path.read_text(encoding="utf-8", errors="ignore").splitlines()[-lines:]} def get_install_secrets() -> dict: return install_secrets_payload() def reveal_secret(admin_user, key: str) -> dict: value = reveal_install_secret(key) write_audit_event( action="admin.secrets.reveal", actor_user_id=admin_user.id, actor_email=admin_user.email, details={"key": key}, ) return {"key": key, "value": value}