152 lines
4.6 KiB
Python
152 lines
4.6 KiB
Python
"""
|
|
Audit API Routes для DAARION City Service
|
|
/api/v1/audit/*
|
|
"""
|
|
|
|
from fastapi import APIRouter, HTTPException, Query
|
|
from pydantic import BaseModel
|
|
from typing import Optional, List
|
|
import logging
|
|
|
|
import repo_governance as repo
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
router = APIRouter(prefix="/api/v1/audit", tags=["audit"])
|
|
|
|
|
|
# =============================================================================
|
|
# Pydantic Models
|
|
# =============================================================================
|
|
|
|
from datetime import datetime as dt
|
|
|
|
class AuditEvent(BaseModel):
|
|
id: str
|
|
event_type: str
|
|
payload: Optional[dict] = None
|
|
status: Optional[str] = None
|
|
created_at: Optional[dt] = None
|
|
actor_id: Optional[str] = None
|
|
target_id: Optional[str] = None
|
|
scope: Optional[str] = None
|
|
|
|
|
|
class AuditStats(BaseModel):
|
|
total_events: int
|
|
events_24h: int
|
|
events_7d: int
|
|
unique_actors: int
|
|
unique_targets: int
|
|
top_event_types: List[dict]
|
|
|
|
|
|
# =============================================================================
|
|
# Routes
|
|
# =============================================================================
|
|
|
|
@router.get("/events", response_model=List[AuditEvent])
|
|
async def get_audit_events(
|
|
limit: int = Query(50, ge=1, le=500),
|
|
offset: int = Query(0, ge=0),
|
|
event_type: Optional[str] = None,
|
|
actor_id: Optional[str] = None,
|
|
target_id: Optional[str] = None,
|
|
scope: Optional[str] = None
|
|
):
|
|
"""
|
|
Отримати події аудиту з фільтрами
|
|
|
|
Параметри:
|
|
- limit: кількість записів (1-500)
|
|
- offset: зміщення для пагінації
|
|
- event_type: фільтр по типу події (agent.promoted, agent.revoked, etc.)
|
|
- actor_id: фільтр по актору
|
|
- target_id: фільтр по цілі
|
|
- scope: фільтр по scope (city, district, microdao)
|
|
"""
|
|
try:
|
|
events = await repo.get_audit_events(
|
|
limit=limit,
|
|
offset=offset,
|
|
event_type=event_type,
|
|
actor_id=actor_id,
|
|
target_id=target_id,
|
|
scope=scope
|
|
)
|
|
return events
|
|
except Exception as e:
|
|
logger.error(f"Error fetching audit events: {e}")
|
|
raise HTTPException(status_code=500, detail=str(e))
|
|
|
|
|
|
@router.get("/events/{event_id}", response_model=AuditEvent)
|
|
async def get_audit_event(event_id: str):
|
|
"""
|
|
Отримати подію аудиту за ID
|
|
"""
|
|
try:
|
|
event = await repo.get_audit_event_by_id(event_id)
|
|
if not event:
|
|
raise HTTPException(status_code=404, detail="Event not found")
|
|
return event
|
|
except HTTPException:
|
|
raise
|
|
except Exception as e:
|
|
logger.error(f"Error fetching audit event: {e}")
|
|
raise HTTPException(status_code=500, detail=str(e))
|
|
|
|
|
|
@router.get("/actor/{actor_id}", response_model=List[AuditEvent])
|
|
async def get_events_by_actor(
|
|
actor_id: str,
|
|
limit: int = Query(50, ge=1, le=500)
|
|
):
|
|
"""
|
|
Отримати події по актору (хто виконав дію)
|
|
"""
|
|
try:
|
|
events = await repo.get_audit_events_by_actor(actor_id, limit=limit)
|
|
return events
|
|
except Exception as e:
|
|
logger.error(f"Error fetching events by actor: {e}")
|
|
raise HTTPException(status_code=500, detail=str(e))
|
|
|
|
|
|
@router.get("/target/{target_id}", response_model=List[AuditEvent])
|
|
async def get_events_by_target(
|
|
target_id: str,
|
|
limit: int = Query(50, ge=1, le=500)
|
|
):
|
|
"""
|
|
Отримати події по цілі (над ким виконано дію)
|
|
"""
|
|
try:
|
|
events = await repo.get_audit_events_by_target(target_id, limit=limit)
|
|
return events
|
|
except Exception as e:
|
|
logger.error(f"Error fetching events by target: {e}")
|
|
raise HTTPException(status_code=500, detail=str(e))
|
|
|
|
|
|
@router.get("/stats", response_model=AuditStats)
|
|
async def get_audit_stats():
|
|
"""
|
|
Отримати статистику аудиту
|
|
|
|
Повертає:
|
|
- total_events: загальна кількість подій
|
|
- events_24h: події за останні 24 години
|
|
- events_7d: події за останні 7 днів
|
|
- unique_actors: кількість унікальних акторів
|
|
- unique_targets: кількість унікальних цілей
|
|
- top_event_types: топ-10 типів подій
|
|
"""
|
|
try:
|
|
stats = await repo.get_audit_stats()
|
|
return stats
|
|
except Exception as e:
|
|
logger.error(f"Error fetching audit stats: {e}")
|
|
raise HTTPException(status_code=500, detail=str(e))
|
|
|