- 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
278 lines
8.2 KiB
Python
278 lines
8.2 KiB
Python
"""
|
|
Governance API Routes для DAARION City Service
|
|
/api/v1/governance/*
|
|
"""
|
|
|
|
from fastapi import APIRouter, HTTPException, Query
|
|
from pydantic import BaseModel, Field
|
|
from typing import Optional, List
|
|
import logging
|
|
|
|
import repo_governance as repo
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
router = APIRouter(prefix="/api/v1/governance", tags=["governance"])
|
|
|
|
|
|
# =============================================================================
|
|
# Pydantic Models
|
|
# =============================================================================
|
|
|
|
class AgentSummary(BaseModel):
|
|
id: str
|
|
display_name: str
|
|
kind: Optional[str] = None
|
|
avatar_url: Optional[str] = None
|
|
status: Optional[str] = None
|
|
gov_level: Optional[str] = None
|
|
node_id: Optional[str] = None
|
|
role: Optional[str] = None
|
|
is_core: Optional[bool] = None
|
|
|
|
|
|
class AgentRolesResponse(BaseModel):
|
|
agent: dict
|
|
assignments: List[dict]
|
|
permissions: List[dict]
|
|
|
|
|
|
class PromoteRequest(BaseModel):
|
|
agent_id: str
|
|
new_level: str
|
|
actor_id: str = "dais-demo-user"
|
|
|
|
|
|
class RevokeRequest(BaseModel):
|
|
agent_id: str
|
|
reason: str
|
|
revocation_type: str = "soft" # soft or hard
|
|
actor_id: str = "dais-demo-user"
|
|
|
|
|
|
class SuspendRequest(BaseModel):
|
|
agent_id: str
|
|
reason: str
|
|
actor_id: str = "dais-demo-user"
|
|
|
|
|
|
class ReinstateRequest(BaseModel):
|
|
agent_id: str
|
|
actor_id: str = "dais-demo-user"
|
|
|
|
|
|
class CheckPermissionRequest(BaseModel):
|
|
agent_id: str
|
|
action: str
|
|
target: str
|
|
scope_type: Optional[str] = None
|
|
scope_id: Optional[str] = None
|
|
|
|
|
|
class CheckPermissionResponse(BaseModel):
|
|
allowed: bool
|
|
reason: str
|
|
|
|
|
|
# =============================================================================
|
|
# Routes
|
|
# =============================================================================
|
|
|
|
@router.get("/agents/city", response_model=List[AgentSummary])
|
|
async def get_city_governance_agents():
|
|
"""
|
|
Отримати City Governance агентів (DAARWIZZ, DARIO, DARIA)
|
|
"""
|
|
try:
|
|
agents = await repo.get_city_governance_agents()
|
|
return agents
|
|
except Exception as e:
|
|
logger.error(f"Error fetching city governance agents: {e}")
|
|
raise HTTPException(status_code=500, detail=str(e))
|
|
|
|
|
|
@router.get("/agents/district/{district_id}", response_model=List[AgentSummary])
|
|
async def get_district_agents(district_id: str):
|
|
"""
|
|
Отримати агентів дистрикту (District Lead + core-team)
|
|
"""
|
|
try:
|
|
agents = await repo.get_district_agents(district_id)
|
|
return agents
|
|
except Exception as e:
|
|
logger.error(f"Error fetching district agents: {e}")
|
|
raise HTTPException(status_code=500, detail=str(e))
|
|
|
|
|
|
@router.get("/agents/microdao/{microdao_id}", response_model=List[AgentSummary])
|
|
async def get_microdao_agents(microdao_id: str):
|
|
"""
|
|
Отримати агентів MicroDAO (Orchestrator + workers)
|
|
"""
|
|
try:
|
|
agents = await repo.get_microdao_agents(microdao_id)
|
|
return agents
|
|
except Exception as e:
|
|
logger.error(f"Error fetching microdao agents: {e}")
|
|
raise HTTPException(status_code=500, detail=str(e))
|
|
|
|
|
|
@router.get("/agents/by-level/{level}", response_model=List[AgentSummary])
|
|
async def get_agents_by_level(level: str):
|
|
"""
|
|
Отримати агентів за рівнем gov_level
|
|
|
|
Рівні: guest, personal, member, worker, core_team, orchestrator, district_lead, city_governance
|
|
"""
|
|
try:
|
|
agents = await repo.get_agents_by_level(level)
|
|
return agents
|
|
except Exception as e:
|
|
logger.error(f"Error fetching agents by level: {e}")
|
|
raise HTTPException(status_code=500, detail=str(e))
|
|
|
|
|
|
@router.get("/agent/{agent_id}/roles", response_model=AgentRolesResponse)
|
|
async def get_agent_roles(agent_id: str):
|
|
"""
|
|
Отримати ролі та повноваження агента
|
|
"""
|
|
try:
|
|
result = await repo.get_agent_roles(agent_id)
|
|
if "error" in result:
|
|
raise HTTPException(status_code=404, detail=result["error"])
|
|
return result
|
|
except HTTPException:
|
|
raise
|
|
except Exception as e:
|
|
logger.error(f"Error fetching agent roles: {e}")
|
|
raise HTTPException(status_code=500, detail=str(e))
|
|
|
|
|
|
@router.post("/agent/promote")
|
|
async def promote_agent(request: PromoteRequest):
|
|
"""
|
|
Підвищити агента до нового рівня
|
|
"""
|
|
try:
|
|
result = await repo.promote_agent(
|
|
agent_id=request.agent_id,
|
|
new_level=request.new_level,
|
|
actor_id=request.actor_id
|
|
)
|
|
if not result:
|
|
raise HTTPException(status_code=404, detail="Agent not found")
|
|
return {"success": True, "agent": result}
|
|
except HTTPException:
|
|
raise
|
|
except Exception as e:
|
|
logger.error(f"Error promoting agent: {e}")
|
|
raise HTTPException(status_code=500, detail=str(e))
|
|
|
|
|
|
@router.post("/agent/demote")
|
|
async def demote_agent(request: PromoteRequest):
|
|
"""
|
|
Понизити рівень агента
|
|
"""
|
|
try:
|
|
result = await repo.demote_agent(
|
|
agent_id=request.agent_id,
|
|
new_level=request.new_level,
|
|
actor_id=request.actor_id
|
|
)
|
|
if not result:
|
|
raise HTTPException(status_code=404, detail="Agent not found")
|
|
return {"success": True, "agent": result}
|
|
except HTTPException:
|
|
raise
|
|
except Exception as e:
|
|
logger.error(f"Error demoting agent: {e}")
|
|
raise HTTPException(status_code=500, detail=str(e))
|
|
|
|
|
|
@router.post("/agent/revoke")
|
|
async def revoke_agent(request: RevokeRequest):
|
|
"""
|
|
Відкликати агента (soft/hard)
|
|
|
|
- soft: тимчасове призупинення
|
|
- hard: повне відкликання з блокуванням DAIS ключів
|
|
"""
|
|
try:
|
|
result = await repo.revoke_agent(
|
|
agent_id=request.agent_id,
|
|
actor_id=request.actor_id,
|
|
reason=request.reason,
|
|
revocation_type=request.revocation_type
|
|
)
|
|
if not result:
|
|
raise HTTPException(status_code=404, detail="Agent not found")
|
|
return {"success": True, "agent": result}
|
|
except HTTPException:
|
|
raise
|
|
except Exception as e:
|
|
logger.error(f"Error revoking agent: {e}")
|
|
raise HTTPException(status_code=500, detail=str(e))
|
|
|
|
|
|
@router.post("/agent/suspend")
|
|
async def suspend_agent(request: SuspendRequest):
|
|
"""
|
|
Тимчасово призупинити агента
|
|
"""
|
|
try:
|
|
result = await repo.suspend_agent(
|
|
agent_id=request.agent_id,
|
|
actor_id=request.actor_id,
|
|
reason=request.reason
|
|
)
|
|
if not result:
|
|
raise HTTPException(status_code=404, detail="Agent not found")
|
|
return {"success": True, "agent": result}
|
|
except HTTPException:
|
|
raise
|
|
except Exception as e:
|
|
logger.error(f"Error suspending agent: {e}")
|
|
raise HTTPException(status_code=500, detail=str(e))
|
|
|
|
|
|
@router.post("/agent/reinstate")
|
|
async def reinstate_agent(request: ReinstateRequest):
|
|
"""
|
|
Відновити призупиненого агента
|
|
"""
|
|
try:
|
|
result = await repo.reinstate_agent(
|
|
agent_id=request.agent_id,
|
|
actor_id=request.actor_id
|
|
)
|
|
if not result:
|
|
raise HTTPException(status_code=404, detail="Agent not found")
|
|
return {"success": True, "agent": result}
|
|
except HTTPException:
|
|
raise
|
|
except Exception as e:
|
|
logger.error(f"Error reinstating agent: {e}")
|
|
raise HTTPException(status_code=500, detail=str(e))
|
|
|
|
|
|
@router.post("/check", response_model=CheckPermissionResponse)
|
|
async def check_permission(request: CheckPermissionRequest):
|
|
"""
|
|
Перевірити чи має агент право на дію
|
|
"""
|
|
try:
|
|
result = await repo.check_permission(
|
|
agent_id=request.agent_id,
|
|
action=request.action,
|
|
target=request.target,
|
|
scope_type=request.scope_type,
|
|
scope_id=request.scope_id
|
|
)
|
|
return result
|
|
except Exception as e:
|
|
logger.error(f"Error checking permission: {e}")
|
|
raise HTTPException(status_code=500, detail=str(e))
|
|
|