""" 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))