Arag / app /superadmin /routers /platform.py
AuthorBot
P0 security: async backup, widget CORS, Redis auth.
937692a
Raw
History Blame Contribute Delete
4.26 kB
"""superadmin/routers/platform.py β€” Platform-wide operations.
Routes:
GET /
GET /diag
GET /health
GET /audit
POST /announce
POST /backup
"""
from fastapi import APIRouter, Depends, Query
from sqlalchemy.ext.asyncio import AsyncSession
from app.dependencies import check_health, get_db, get_current_superadmin
from app.repositories.audit_repo import AuditRepository
from app.schemas.admin import AnnouncementRequest
from app.superadmin.routers._utils import _err
router = APIRouter()
@router.get("/")
async def superadmin_index():
"""SuperAdmin API index β€” lists available endpoints."""
return {
"message": "SuperAdmin API. Requires SuperAdmin JWT (Bearer token).",
"endpoints": {
"list_authors": "GET /api/super/authors",
"create_author": "POST /api/super/authors",
"suspend_author": "POST /api/super/authors/{id}/suspend",
"grant_access": "POST /api/super/authors/{id}/grant",
"revoke_access": "POST /api/super/grants/{id}/revoke",
"audit_log": "GET /api/super/audit",
"platform_health": "GET /api/super/health",
},
"docs": "/docs",
}
@router.get("/diag")
async def diagnostic(superadmin=Depends(get_current_superadmin)):
"""R-045: Diagnostic endpoint β€” requires SuperAdmin auth."""
return {"status": "ok", "router": "superadmin", "auth": "verified"}
@router.get("/health")
async def platform_health(superadmin=Depends(get_current_superadmin)):
"""Platform health: Redis, ChromaDB, DB status."""
try:
return await check_health()
except Exception as e:
return _err(e, "platform_health")
@router.get("/audit")
async def get_audit_log(
limit: int = Query(100, ge=1, le=500),
cursor: str | None = Query(None),
actor_id: str | None = Query(None),
action: str | None = Query(None),
superadmin=Depends(get_current_superadmin),
db: AsyncSession = Depends(get_db),
):
"""Retrieve the immutable audit log. No DELETE allowed."""
try:
audit_repo = AuditRepository(db)
entries = await audit_repo.list_recent(
limit=limit, cursor=cursor, actor_id=actor_id, action=action
)
result = [
{
"id": e.id,
"timestamp": e.timestamp.isoformat() if e.timestamp else None,
"actor_email": e.actor_email,
"action": e.action,
"target_type": e.target_type,
"target_id": e.target_id,
"details": e.details,
}
for e in entries
]
return {"entries": result, "count": len(result)}
except Exception as e:
return _err(e, "get_audit_log")
@router.post("/announce", status_code=201)
async def broadcast_announcement(
body: AnnouncementRequest,
superadmin=Depends(get_current_superadmin),
db: AsyncSession = Depends(get_db),
):
"""Broadcast an announcement visible to all authors. R-029: Validated via Pydantic schema."""
try:
audit_repo = AuditRepository(db)
await audit_repo.log(
actor_id=superadmin.id,
actor_email=superadmin.email,
action="broadcast_announcement",
target_type="platform",
target_id=None,
details={"message": body.message},
)
return {"message": "Announcement sent"}
except Exception as e:
return _err(e, "broadcast_announcement")
@router.post("/backup", status_code=202)
async def trigger_backup(
superadmin=Depends(get_current_superadmin),
db: AsyncSession = Depends(get_db),
):
"""Dispatch backup to Celery worker (R-080 β€” non-blocking)."""
try:
from app.tasks.backup_task import run_backup
task = run_backup.delay()
audit_repo = AuditRepository(db)
await audit_repo.log(
actor_id=superadmin.id,
actor_email=superadmin.email,
action="trigger_backup",
target_type="platform",
target_id=None,
details={"task_id": task.id},
)
return {"message": "Backup dispatched", "task_id": task.id}
except Exception as e:
return _err(e, "trigger_backup")