Files

91 lines
2.6 KiB
Python

from __future__ import annotations
import uuid
from fastapi import APIRouter, Depends, HTTPException, Request, status
from pydantic import BaseModel, field_validator
from sqlalchemy.ext.asyncio import AsyncSession
from db.models import Quota, User
from deps.auth import get_current_admin
from deps.db import get_db
from deps.utils import get_client_ip
from services.audit import write_audit_log
router = APIRouter() # NO prefix — parent __init__.py carries /api/admin (D-04)
class QuotaUpdate(BaseModel):
limit_bytes: int
@field_validator("limit_bytes")
@classmethod
def must_be_positive(cls, v: int) -> int:
if v <= 0:
raise ValueError("limit_bytes must be greater than 0")
return v
@router.get("/users/{user_id}/quota")
async def get_user_quota(
user_id: uuid.UUID,
session: AsyncSession = Depends(get_db),
_admin: User = Depends(get_current_admin),
) -> dict:
quota = await session.get(Quota, user_id)
if quota is None:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Quota not found")
return {
"user_id": str(quota.user_id),
"limit_bytes": quota.limit_bytes,
"used_bytes": quota.used_bytes,
"limit_mb": quota.limit_bytes // 1048576,
"used_mb": quota.used_bytes // 1048576,
}
@router.patch("/users/{user_id}/quota")
async def update_user_quota(
user_id: uuid.UUID,
body: QuotaUpdate,
request: Request,
session: AsyncSession = Depends(get_db),
_admin: User = Depends(get_current_admin),
) -> dict:
quota = await session.get(Quota, user_id)
if quota is None:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Quota not found")
warning = body.limit_bytes < quota.used_bytes
warning_message = (
"New limit is below current usage. Uploads will be blocked but existing documents are preserved."
if warning
else None
)
_ip_addr = get_client_ip(request)
old_limit = quota.limit_bytes
quota.limit_bytes = body.limit_bytes
session.add(quota)
# D-13: quota changed event
await write_audit_log(
session,
event_type="admin.quota_changed",
user_id=user_id,
actor_id=_admin.id,
resource_id=None,
ip_address=_ip_addr,
metadata_={"old_bytes": old_limit, "new_bytes": body.limit_bytes},
)
await session.commit()
response: dict = {
"user_id": str(quota.user_id),
"limit_bytes": quota.limit_bytes,
"used_bytes": quota.used_bytes,
"warning": warning,
}
if warning_message:
response["message"] = warning_message
return response