- New /actiontrail page with 4 tabs: Timeline, Agents, Alerts, Approvals - Summary cards: total actions, critical/high count, pending approvals, active alerts - Risk-level filtering on timeline, color-coded risk badges - Server-side API proxy route (/api/actiontrail) to ActionTrail backend (port 4018) - actiontrail-client.ts: typed API client using @bytelyst/api-client - Sidebar nav item with Crosshair icon - ACTIONTRAIL_SERVICE_URL added to .env.example - Graceful fallback when ActionTrail service is unavailable
124 lines
3.2 KiB
TypeScript
124 lines
3.2 KiB
TypeScript
/**
|
|
* ActionTrail backend API client (server-side).
|
|
* Used by Next.js API routes to proxy requests to the ActionTrail backend.
|
|
*/
|
|
|
|
import { createApiClient } from '@bytelyst/api-client';
|
|
|
|
const actiontrailApi = createApiClient({
|
|
baseUrl: `${process.env.ACTIONTRAIL_SERVICE_URL || 'http://localhost:4018'}/api`,
|
|
defaultHeaders: {
|
|
'x-product-id': 'actiontrail',
|
|
},
|
|
});
|
|
|
|
// ── Types ─────────────────────────────────────────────────────────
|
|
|
|
export interface ActionItem {
|
|
id: string;
|
|
userId: string;
|
|
agentId: string;
|
|
sourceProduct: string;
|
|
action: string;
|
|
category: string;
|
|
description: string;
|
|
riskLevel: 'low' | 'medium' | 'high' | 'critical';
|
|
riskScore: number;
|
|
status: string;
|
|
correlationId?: string;
|
|
tags: string[];
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
}
|
|
|
|
export interface AgentItem {
|
|
id: string;
|
|
userId: string;
|
|
name: string;
|
|
description: string;
|
|
sourceProduct: string;
|
|
trustLevel: string;
|
|
status: string;
|
|
allowedCategories: string[];
|
|
maxRiskLevel: string;
|
|
actionCount: number;
|
|
createdAt: string;
|
|
}
|
|
|
|
export interface AlertItem {
|
|
id: string;
|
|
userId: string;
|
|
actionId: string;
|
|
agentId: string;
|
|
severity: string;
|
|
message: string;
|
|
reasons: string[];
|
|
riskScore: number;
|
|
acknowledged: boolean;
|
|
createdAt: string;
|
|
}
|
|
|
|
export interface ApprovalItem {
|
|
id: string;
|
|
userId: string;
|
|
actionId: string;
|
|
agentId: string;
|
|
action: string;
|
|
riskLevel: string;
|
|
riskScore: number;
|
|
status: string;
|
|
expiresAt: string;
|
|
createdAt: string;
|
|
}
|
|
|
|
export interface InsightSummary {
|
|
totalActions: number;
|
|
totalAgents: number;
|
|
totalAlerts: number;
|
|
riskDistribution: Record<string, number>;
|
|
categoryDistribution: Record<string, number>;
|
|
topAgents: Array<{ agentId: string; name: string; actionCount: number }>;
|
|
}
|
|
|
|
// ── API Functions ─────────────────────────────────────────────────
|
|
|
|
export async function getActions(userId: string, limit = 50) {
|
|
return actiontrailApi.fetch<{ items: ActionItem[]; cursor: string | null }>(
|
|
`/actions?limit=${limit}`,
|
|
{ headers: { 'x-user-id': userId } }
|
|
);
|
|
}
|
|
|
|
export async function getAgents(userId: string) {
|
|
return actiontrailApi.fetch<AgentItem[]>('/agents', { headers: { 'x-user-id': userId } });
|
|
}
|
|
|
|
export async function getAlerts(userId: string, limit = 50) {
|
|
return actiontrailApi.fetch<{ items: AlertItem[]; cursor: string | null }>(
|
|
`/alerts?limit=${limit}`,
|
|
{ headers: { 'x-user-id': userId } }
|
|
);
|
|
}
|
|
|
|
export async function getPendingApprovals(userId: string) {
|
|
return actiontrailApi.fetch<{ items: ApprovalItem[]; cursor: string | null }>(
|
|
'/approvals?status=pending',
|
|
{ headers: { 'x-user-id': userId } }
|
|
);
|
|
}
|
|
|
|
export async function getInsightsSummary(userId: string) {
|
|
return actiontrailApi.fetch<InsightSummary>('/insights/summary', {
|
|
headers: { 'x-user-id': userId },
|
|
});
|
|
}
|
|
|
|
export async function getBootstrap() {
|
|
return actiontrailApi.fetch<{
|
|
productId: string;
|
|
displayName: string;
|
|
domain: string;
|
|
surfaces: { primary: string; mobile: boolean };
|
|
}>('/bootstrap');
|
|
}
|