Files
microdao-daarion/services/city-service/routes_audit.py
Apple e078a24540 feat(city-service): add Governance, Audit, Incidents API endpoints
- Added repo_governance.py with database operations
- Added routes_governance.py (/api/v1/governance/*)
- Added routes_audit.py (/api/v1/audit/*)
- Added routes_incidents.py (/api/v1/incidents/*)
- Updated main.py to include new routers
2025-11-29 17:01:58 -08:00

150 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
# =============================================================================
class AuditEvent(BaseModel):
id: str
event_type: str
payload: Optional[dict] = None
status: Optional[str] = None
created_at: Optional[str] = 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))