Download backend/app/api/v1/admin.py from Punit1/Enterprise-AI-Copilot: direct link, hf CLI and curl.
- Browser
- Download file 5.48 kB
-
https://huggingface.co/spaces/Punit1/Enterprise-AI-Copilot/resolve/main/backend/app/api/v1/admin.py
- Command line
-
hf download hf://spaces/Punit1/Enterprise-AI-Copilot/backend/app/api/v1/admin.py
-
curl -L -o admin.py https://huggingface.co/spaces/Punit1/Enterprise-AI-Copilot/resolve/main/backend/app/api/v1/admin.py
5.48 kB
| """ | |
| Admin API Endpoints | |
| =================== | |
| User management, role assignment, audit logs query, and workspace configuration. | |
| Protected with strict RBAC: require_roles(UserRole.ADMIN) | |
| """ | |
| from __future__ import annotations | |
| from typing import List, Optional | |
| import structlog | |
| from fastapi import APIRouter, Depends, HTTPException, status | |
| from pydantic import BaseModel, EmailStr | |
| from sqlalchemy import select, func | |
| from app.core.dependencies import AuthUser, DBSession | |
| from app.core.security import UserRole, require_roles, hash_password | |
| from app.models.user import User | |
| from app.models.tenant import Tenant | |
| from app.models.audit import AuditLog | |
| logger = structlog.get_logger(__name__) | |
| router = APIRouter(prefix="/admin", dependencies=[Depends(require_roles(UserRole.ADMIN))]) | |
| # ββ Pydantic Schemas ββββββββββββββββββββββββββββββββββββββββββ | |
| class AdminUserResponse(BaseModel): | |
| id: str | |
| email: str | |
| full_name: str | |
| role: str | |
| is_active: bool | |
| created_at: Optional[str] = None | |
| model_config = {"from_attributes": True} | |
| class InviteUserRequest(BaseModel): | |
| email: EmailStr | |
| full_name: str | |
| role: UserRole = UserRole.EMPLOYEE | |
| password: str = "Welcome@123!" | |
| class AuditLogResponse(BaseModel): | |
| id: str | |
| user_id: str | |
| action: str | |
| agent_name: Optional[str] | |
| outcome: str | |
| tokens_used: Optional[int] | |
| latency_ms: Optional[int] | |
| created_at: Optional[str] = None | |
| model_config = {"from_attributes": True} | |
| class TenantDetailsResponse(BaseModel): | |
| id: str | |
| name: str | |
| slug: str | |
| plan: str | |
| is_active: bool | |
| user_count: int | |
| # ββ Routes ββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| async def list_tenant_users( | |
| db: DBSession, | |
| current_user: AuthUser, | |
| ): | |
| """List all users within the current user's tenant (Admin only).""" | |
| result = await db.execute( | |
| select(User).where(User.tenant_id == current_user.tenant_id).order_by(User.created_at.desc()) | |
| ) | |
| users = result.scalars().all() | |
| return [ | |
| AdminUserResponse( | |
| id=u.id, | |
| email=u.email, | |
| full_name=u.full_name, | |
| role=u.role, | |
| is_active=u.is_active, | |
| created_at=u.created_at.isoformat() if u.created_at else None, | |
| ) | |
| for u in users | |
| ] | |
| async def invite_user( | |
| data: InviteUserRequest, | |
| db: DBSession, | |
| current_user: AuthUser, | |
| ): | |
| """Invite/Create a new user in the tenant with a specified role (Admin only).""" | |
| existing = await db.execute(select(User).where(User.email == data.email)) | |
| if existing.scalar_one_or_none(): | |
| raise HTTPException( | |
| status_code=status.HTTP_409_CONFLICT, | |
| detail={"error": "user_exists", "message": "User with this email already exists"}, | |
| ) | |
| new_user = User( | |
| email=data.email, | |
| hashed_password=hash_password(data.password), | |
| full_name=data.full_name, | |
| role=data.role.value, | |
| tenant_id=current_user.tenant_id, | |
| is_active=True, | |
| is_verified=True, | |
| ) | |
| db.add(new_user) | |
| await db.commit() | |
| logger.info("Admin invited new user", admin=current_user.email, invited=new_user.email, role=new_user.role) | |
| return AdminUserResponse( | |
| id=new_user.id, | |
| email=new_user.email, | |
| full_name=new_user.full_name, | |
| role=new_user.role, | |
| is_active=new_user.is_active, | |
| created_at=new_user.created_at.isoformat() if new_user.created_at else None, | |
| ) | |
| async def get_audit_logs( | |
| db: DBSession, | |
| current_user: AuthUser, | |
| limit: int = 50, | |
| ): | |
| """Get audit logs for current tenant (Admin only).""" | |
| result = await db.execute( | |
| select(AuditLog) | |
| .where(AuditLog.tenant_id == current_user.tenant_id) | |
| .order_by(AuditLog.created_at.desc()) | |
| .limit(limit) | |
| ) | |
| logs = result.scalars().all() | |
| return [ | |
| AuditLogResponse( | |
| id=l.id, | |
| user_id=l.user_id, | |
| action=l.action, | |
| agent_name=l.agent_name, | |
| outcome=l.outcome, | |
| tokens_used=l.tokens_used, | |
| latency_ms=l.latency_ms, | |
| created_at=l.created_at.isoformat() if l.created_at else None, | |
| ) | |
| for l in logs | |
| ] | |
| async def get_tenant_details( | |
| db: DBSession, | |
| current_user: AuthUser, | |
| ): | |
| """Get workspace tenant info and user count (Admin only).""" | |
| tenant_res = await db.execute(select(Tenant).where(Tenant.id == current_user.tenant_id)) | |
| tenant = tenant_res.scalar_one_or_none() | |
| if not tenant: | |
| raise HTTPException(status_code=404, detail="Tenant not found") | |
| count_res = await db.execute( | |
| select(func.count(User.id)).where(User.tenant_id == current_user.tenant_id) | |
| ) | |
| user_count = count_res.scalar() or 0 | |
| return TenantDetailsResponse( | |
| id=tenant.id, | |
| name=tenant.name, | |
| slug=tenant.slug, | |
| plan=tenant.plan, | |
| is_active=tenant.is_active, | |
| user_count=user_count, | |
| ) | |