Files
microdao-daarion/apps/web/src/lib/api/governance.ts

214 lines
5.8 KiB
TypeScript

/**
* Governance API Client for DAARION.city
* Based on: backend/http/governance.routes.ts
*/
import type {
AgentGovLevel,
GovernanceScope,
AgentRolesResponse,
GovernanceAgent,
GovernancePower,
RevocationType,
} from '../types/governance';
// API base URL for governance endpoints
const getApiBase = () => {
if (typeof window === 'undefined') {
return process.env.INTERNAL_API_URL || 'http://daarion-city-service:7001';
}
return '';
};
const API_BASE = getApiBase();
async function fetchApi<T>(endpoint: string, options?: RequestInit): Promise<T> {
const url = `${API_BASE}${endpoint}`;
const response = await fetch(url, {
...options,
headers: {
'Content-Type': 'application/json',
...options?.headers,
},
});
if (!response.ok) {
const error = await response.json().catch(() => ({ detail: 'Unknown error' }));
throw new Error(error.detail || `HTTP ${response.status}`);
}
return response.json();
}
// ============================================================================
// AGENT GOVERNANCE
// ============================================================================
export async function promoteAgent(
actorId: string,
targetId: string,
newLevel: AgentGovLevel,
scope: GovernanceScope,
reason?: string
): Promise<{ success: boolean; message: string }> {
return fetchApi('/api/v1/governance/agent/promote', {
method: 'POST',
body: JSON.stringify({ actorId, targetId, newLevel, scope, reason }),
});
}
export async function demoteAgent(
actorId: string,
targetId: string,
newLevel: AgentGovLevel,
scope: GovernanceScope,
reason?: string
): Promise<{ success: boolean; message: string }> {
return fetchApi('/api/v1/governance/agent/demote', {
method: 'POST',
body: JSON.stringify({ actorId, targetId, newLevel, scope, reason }),
});
}
export async function revokeAgent(
actorId: string,
targetId: string,
reason: string,
scope: GovernanceScope,
revocationType: RevocationType = 'soft'
): Promise<{ success: boolean; message: string }> {
return fetchApi('/api/v1/governance/agent/revoke', {
method: 'POST',
body: JSON.stringify({ actorId, targetId, reason, scope, revocationType }),
});
}
export async function suspendAgent(
actorId: string,
targetId: string,
reason: string,
scope: GovernanceScope
): Promise<{ success: boolean; message: string }> {
return fetchApi('/api/v1/governance/agent/suspend', {
method: 'POST',
body: JSON.stringify({ actorId, targetId, reason, scope }),
});
}
export async function reinstateAgent(
actorId: string,
targetId: string,
scope: GovernanceScope
): Promise<{ success: boolean; message: string }> {
return fetchApi('/api/v1/governance/agent/reinstate', {
method: 'POST',
body: JSON.stringify({ actorId, targetId, scope }),
});
}
export async function getAgentRoles(agentId: string): Promise<AgentRolesResponse> {
return fetchApi(`/api/v1/governance/agent/${agentId}/roles`);
}
export async function getAgentPermissions(agentId: string): Promise<GovernancePower[]> {
return fetchApi(`/api/v1/governance/agent/${agentId}/permissions`);
}
export async function checkPermission(
actorId: string,
action: string,
targetType: string,
targetId: string
): Promise<{ allowed: boolean; reason?: string }> {
return fetchApi('/api/v1/governance/check', {
method: 'POST',
body: JSON.stringify({ actorId, action, targetType, targetId }),
});
}
// ============================================================================
// CITY GOVERNANCE AGENTS
// ============================================================================
export async function getCityAgents(): Promise<GovernanceAgent[]> {
const raw = await fetchApi<Array<{
id: string;
display_name: string;
avatar_url?: string;
gov_level: string;
status?: string;
}>>('/api/v1/governance/agents/city');
return raw.map((agent) => ({
id: agent.id,
displayName: agent.display_name,
avatarUrl: agent.avatar_url,
govLevel: (agent.gov_level || 'guest') as AgentGovLevel,
status: (agent.status || 'active') as 'active' | 'suspended' | 'revoked',
}));
}
export async function getDistrictLeadAgents(): Promise<GovernanceAgent[]> {
try {
const raw = await fetchApi<Array<{
id: string;
display_name: string;
avatar_url?: string;
gov_level: string;
status?: string;
home_microdao_id?: string;
home_microdao_name?: string;
}>>('/api/v1/governance/agents/district-leads');
return raw.map((agent) => ({
id: agent.id,
displayName: agent.display_name,
avatarUrl: agent.avatar_url,
govLevel: (agent.gov_level || 'district_lead') as AgentGovLevel,
status: (agent.status || 'active') as 'active' | 'suspended' | 'revoked',
homeMicrodaoId: agent.home_microdao_id,
homeMicrodaoName: agent.home_microdao_name,
}));
} catch {
return [];
}
}
export async function getAgentsByLevel(level: AgentGovLevel): Promise<GovernanceAgent[]> {
return fetchApi(`/api/v1/governance/agents/by-level/${level}`);
}
// ============================================================================
// DAIS KEYS
// ============================================================================
export async function revokeDaisKey(
daisId: string,
keyId: string,
reason: string,
revokedBy: string
): Promise<{ success: boolean }> {
return fetchApi('/api/v1/governance/dais/keys/revoke', {
method: 'POST',
body: JSON.stringify({ daisId, keyId, reason, revokedBy }),
});
}
// Export all functions as a namespace
export const governanceApi = {
promoteAgent,
demoteAgent,
revokeAgent,
suspendAgent,
reinstateAgent,
getAgentRoles,
getAgentPermissions,
checkPermission,
getCityAgents,
getDistrictLeadAgents,
getAgentsByLevel,
revokeDaisKey,
};