879 lines
27 KiB
TypeScript
879 lines
27 KiB
TypeScript
import axios, { AxiosResponse } from 'axios';
|
|
import {
|
|
Agent,
|
|
UpdatePackage,
|
|
AgentUpdatePackage,
|
|
DashboardStats,
|
|
AgentListResponse,
|
|
UpdateListResponse,
|
|
UpdateApprovalRequest,
|
|
ScanRequest,
|
|
ListQueryParams,
|
|
ApiError,
|
|
DockerContainer,
|
|
DockerImage,
|
|
DockerContainerListResponse,
|
|
DockerStats,
|
|
DockerUpdateRequest,
|
|
BulkDockerUpdateRequest,
|
|
RegistrationToken,
|
|
CreateRegistrationTokenRequest,
|
|
RegistrationTokenStats,
|
|
RateLimitConfig,
|
|
RateLimitStats,
|
|
RateLimitUsage,
|
|
RateLimitSummary,
|
|
AgentSubsystem,
|
|
SubsystemConfig,
|
|
SubsystemStats
|
|
} from '@/types';
|
|
|
|
// Base URL for API - use nginx proxy
|
|
export const API_BASE_URL = '/api/v1';
|
|
|
|
// Create axios instance
|
|
const api = axios.create({
|
|
baseURL: API_BASE_URL,
|
|
timeout: 30000,
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
});
|
|
|
|
// Request interceptor to add auth token
|
|
api.interceptors.request.use((config) => {
|
|
const token = localStorage.getItem('auth_token');
|
|
if (token) {
|
|
config.headers.Authorization = `Bearer ${token}`;
|
|
}
|
|
return config;
|
|
});
|
|
|
|
// Response interceptor to handle errors
|
|
api.interceptors.response.use(
|
|
(response: AxiosResponse) => response,
|
|
async (error) => {
|
|
if (error.response?.status === 401) {
|
|
const { useAuthStore } = await import('./store');
|
|
useAuthStore.getState().logout();
|
|
localStorage.removeItem('auth_token');
|
|
localStorage.removeItem('user');
|
|
window.location.href = '/login';
|
|
}
|
|
return Promise.reject(error);
|
|
}
|
|
);
|
|
|
|
// API endpoints
|
|
export const agentApi = {
|
|
// Get all agents
|
|
getAgents: async (params?: ListQueryParams): Promise<AgentListResponse> => {
|
|
const response = await api.get('/agents', { params });
|
|
return response.data;
|
|
},
|
|
|
|
// Get single agent
|
|
getAgent: async (id: string): Promise<Agent> => {
|
|
const response = await api.get(`/agents/${id}`);
|
|
return response.data;
|
|
},
|
|
|
|
// Trigger scan on agents
|
|
triggerScan: async (request: ScanRequest): Promise<void> => {
|
|
await api.post('/agents/scan', request);
|
|
},
|
|
|
|
// Trigger scan on single agent
|
|
scanAgent: async (id: string): Promise<void> => {
|
|
await api.post(`/agents/${id}/scan`);
|
|
},
|
|
|
|
// Trigger heartbeat toggle on single agent
|
|
toggleHeartbeat: async (id: string, enabled: boolean, durationMinutes: number = 10): Promise<{ message: string; command_id: string; enabled: boolean }> => {
|
|
const response = await api.post(`/agents/${id}/heartbeat`, {
|
|
enabled: enabled,
|
|
duration_minutes: durationMinutes,
|
|
});
|
|
return response.data;
|
|
},
|
|
|
|
// Get heartbeat status for single agent
|
|
getHeartbeatStatus: async (id: string): Promise<{ enabled: boolean; until: string | null; active: boolean; duration_minutes: number }> => {
|
|
const response = await api.get(`/agents/${id}/heartbeat`);
|
|
return response.data;
|
|
},
|
|
|
|
// Trigger agent reboot
|
|
rebootAgent: async (id: string, delayMinutes: number = 1, message?: string): Promise<void> => {
|
|
await api.post(`/agents/${id}/reboot`, {
|
|
delay_minutes: delayMinutes,
|
|
message: message || 'System reboot requested by RedFlag'
|
|
});
|
|
},
|
|
|
|
// Unregister/remove agent
|
|
unregisterAgent: async (id: string): Promise<void> => {
|
|
await api.delete(`/agents/${id}`);
|
|
},
|
|
|
|
// Subsystem Management
|
|
getSubsystems: async (agentId: string): Promise<AgentSubsystem[]> => {
|
|
const response = await api.get(`/agents/${agentId}/subsystems`);
|
|
return response.data;
|
|
},
|
|
|
|
getSubsystem: async (agentId: string, subsystem: string): Promise<AgentSubsystem> => {
|
|
const response = await api.get(`/agents/${agentId}/subsystems/${subsystem}`);
|
|
return response.data;
|
|
},
|
|
|
|
updateSubsystem: async (agentId: string, subsystem: string, config: SubsystemConfig): Promise<{ message: string }> => {
|
|
const response = await api.patch(`/agents/${agentId}/subsystems/${subsystem}`, config);
|
|
return response.data;
|
|
},
|
|
|
|
enableSubsystem: async (agentId: string, subsystem: string): Promise<{ message: string }> => {
|
|
const response = await api.post(`/agents/${agentId}/subsystems/${subsystem}/enable`);
|
|
return response.data;
|
|
},
|
|
|
|
disableSubsystem: async (agentId: string, subsystem: string): Promise<{ message: string }> => {
|
|
const response = await api.post(`/agents/${agentId}/subsystems/${subsystem}/disable`);
|
|
return response.data;
|
|
},
|
|
|
|
triggerSubsystem: async (agentId: string, subsystem: string): Promise<{ message: string; command_id: string }> => {
|
|
const response = await api.post(`/agents/${agentId}/subsystems/${subsystem}/trigger`);
|
|
return response.data;
|
|
},
|
|
|
|
getSubsystemStats: async (agentId: string, subsystem: string): Promise<SubsystemStats> => {
|
|
const response = await api.get(`/agents/${agentId}/subsystems/${subsystem}/stats`);
|
|
return response.data;
|
|
},
|
|
|
|
setSubsystemAutoRun: async (agentId: string, subsystem: string, autoRun: boolean): Promise<{ message: string }> => {
|
|
const response = await api.post(`/agents/${agentId}/subsystems/${subsystem}/auto-run`, { auto_run: autoRun });
|
|
return response.data;
|
|
},
|
|
|
|
setSubsystemInterval: async (agentId: string, subsystem: string, intervalMinutes: number): Promise<{ message: string }> => {
|
|
const response = await api.post(`/agents/${agentId}/subsystems/${subsystem}/interval`, { interval_minutes: intervalMinutes });
|
|
return response.data;
|
|
},
|
|
|
|
// Update single agent
|
|
updateAgent: async (agentId: string, updateData: {
|
|
version: string;
|
|
platform: string;
|
|
scheduled?: string;
|
|
}): Promise<{ message: string; update_id: string; download_url: string; signature: string; checksum: string; file_size: number; estimated_time: number }> => {
|
|
const response = await api.post(`/agents/${agentId}/update`, updateData);
|
|
return response.data;
|
|
},
|
|
|
|
// Check if update available for agent
|
|
checkForUpdateAvailable: async (agentId: string): Promise<{ hasUpdate: boolean; currentVersion: string; latestVersion?: string; reason?: string; platform?: string }> => {
|
|
const response = await api.get(`/agents/${agentId}/updates/available`);
|
|
return response.data;
|
|
},
|
|
|
|
// Get update status for agent
|
|
getUpdateStatus: async (agentId: string): Promise<{ status: string; progress?: number; error?: string }> => {
|
|
const response = await api.get(`/agents/${agentId}/updates/status`);
|
|
return response.data;
|
|
},
|
|
|
|
// Generate update nonce for agent (new security feature)
|
|
generateUpdateNonce: async (agentId: string, targetVersion: string): Promise<{ agent_id: string; target_version: string; update_nonce: string; expires_at: number; expires_in_seconds: number }> => {
|
|
const response = await api.post(`/agents/${agentId}/update-nonce?target_version=${targetVersion}`);
|
|
return response.data;
|
|
},
|
|
|
|
// Get agent metrics
|
|
getAgentMetrics: async (agentId: string): Promise<any> => {
|
|
const response = await api.get(`/agents/${agentId}/metrics`);
|
|
return response.data;
|
|
},
|
|
|
|
// Get agent storage metrics
|
|
getAgentStorageMetrics: async (agentId: string): Promise<any> => {
|
|
const response = await api.get(`/agents/${agentId}/metrics/storage`);
|
|
return response.data;
|
|
},
|
|
|
|
// Get agent system metrics
|
|
getAgentSystemMetrics: async (agentId: string): Promise<any> => {
|
|
const response = await api.get(`/agents/${agentId}/metrics/system`);
|
|
return response.data;
|
|
},
|
|
|
|
// Get agent Docker images
|
|
getAgentDockerImages: async (agentId: string, params?: any): Promise<any> => {
|
|
const response = await api.get(`/agents/${agentId}/docker-images`, { params });
|
|
return response.data;
|
|
},
|
|
|
|
// Get agent Docker info
|
|
getAgentDockerInfo: async (agentId: string): Promise<any> => {
|
|
const response = await api.get(`/agents/${agentId}/docker-info`);
|
|
return response.data;
|
|
},
|
|
|
|
// Update multiple agents (bulk)
|
|
updateMultipleAgents: async (updateData: {
|
|
agent_ids: string[];
|
|
version: string;
|
|
platform: string;
|
|
scheduled?: string;
|
|
}): Promise<{ message: string; updated: Array<{ agent_id: string; hostname: string; update_id: string; status: string }>; failed: string[]; total_agents: number; package_info: any }> => {
|
|
const response = await api.post('/agents/bulk-update', updateData);
|
|
return response.data;
|
|
},
|
|
};
|
|
|
|
export const updateApi = {
|
|
// Get all updates
|
|
getUpdates: async (params?: ListQueryParams): Promise<UpdateListResponse> => {
|
|
const response = await api.get('/updates', { params });
|
|
return response.data;
|
|
},
|
|
|
|
// Get single update
|
|
getUpdate: async (id: string): Promise<UpdatePackage> => {
|
|
const response = await api.get(`/updates/${id}`);
|
|
return response.data;
|
|
},
|
|
|
|
// Approve updates
|
|
approveUpdates: async (request: UpdateApprovalRequest): Promise<void> => {
|
|
await api.post('/updates/approve', request);
|
|
},
|
|
|
|
// Approve single update
|
|
approveUpdate: async (id: string, scheduledAt?: string): Promise<void> => {
|
|
await api.post(`/updates/${id}/approve`, { scheduled_at: scheduledAt });
|
|
},
|
|
|
|
// Approve multiple updates
|
|
approveMultiple: async (updateIds: string[]): Promise<void> => {
|
|
await api.post('/updates/approve', { update_ids: updateIds });
|
|
},
|
|
|
|
// Reject/cancel update
|
|
rejectUpdate: async (id: string): Promise<void> => {
|
|
await api.post(`/updates/${id}/reject`);
|
|
},
|
|
|
|
// Install update immediately
|
|
installUpdate: async (id: string): Promise<void> => {
|
|
await api.post(`/updates/${id}/install`);
|
|
},
|
|
|
|
// Get update logs
|
|
getUpdateLogs: async (id: string, limit?: number): Promise<{ logs: any[]; count: number }> => {
|
|
const response = await api.get(`/updates/${id}/logs`, {
|
|
params: limit ? { limit } : undefined
|
|
});
|
|
return response.data;
|
|
},
|
|
|
|
// Retry a failed, timed_out, or cancelled command
|
|
retryCommand: async (commandId: string): Promise<{ message: string; command_id: string; new_id: string }> => {
|
|
const response = await api.post(`/commands/${commandId}/retry`);
|
|
return response.data;
|
|
},
|
|
|
|
// Cancel a pending or sent command
|
|
cancelCommand: async (commandId: string): Promise<{ message: string }> => {
|
|
const response = await api.post(`/commands/${commandId}/cancel`);
|
|
return response.data;
|
|
},
|
|
|
|
// Get active commands for live command control
|
|
getActiveCommands: async (): Promise<{ commands: any[]; count: number }> => {
|
|
const response = await api.get('/commands/active');
|
|
return response.data;
|
|
},
|
|
|
|
// Get recent commands for retry functionality
|
|
getRecentCommands: async (limit?: number): Promise<{ commands: any[]; count: number; limit: number }> => {
|
|
const response = await api.get('/commands/recent', {
|
|
params: limit ? { limit } : undefined
|
|
});
|
|
return response.data;
|
|
},
|
|
|
|
// Clear failed commands with filtering options
|
|
clearFailedCommands: async (options?: {
|
|
olderThanDays?: number;
|
|
onlyRetried?: boolean;
|
|
allFailed?: boolean;
|
|
}): Promise<{ message: string; count: number; cheeky_warning?: string }> => {
|
|
const params = new URLSearchParams();
|
|
|
|
if (options?.olderThanDays !== undefined) {
|
|
params.append('older_than_days', options.olderThanDays.toString());
|
|
}
|
|
if (options?.onlyRetried) {
|
|
params.append('only_retried', 'true');
|
|
}
|
|
if (options?.allFailed) {
|
|
params.append('all_failed', 'true');
|
|
}
|
|
|
|
const response = await api.delete(`/commands/failed${params.toString() ? '?' + params.toString() : ''}`);
|
|
return response.data;
|
|
},
|
|
|
|
// Get available update packages
|
|
getPackages: async (params?: {
|
|
version?: string;
|
|
platform?: string;
|
|
limit?: number;
|
|
offset?: number;
|
|
}): Promise<{ packages: AgentUpdatePackage[]; total: number; limit: number; offset: number }> => {
|
|
const response = await api.get('/updates/packages', { params });
|
|
return response.data;
|
|
},
|
|
|
|
// Sign new update package
|
|
signPackage: async (packageData: {
|
|
version: string;
|
|
platform: string;
|
|
architecture: string;
|
|
binary_path: string;
|
|
}): Promise<{ message: string; package: UpdatePackage }> => {
|
|
const response = await api.post('/updates/packages/sign', packageData);
|
|
return response.data;
|
|
},
|
|
};
|
|
|
|
export const statsApi = {
|
|
// Get dashboard statistics
|
|
getDashboardStats: async (): Promise<DashboardStats> => {
|
|
const response = await api.get('/stats/summary');
|
|
return response.data;
|
|
},
|
|
};
|
|
|
|
export const logApi = {
|
|
// Get all logs with filtering for universal log view
|
|
getAllLogs: async (params?: {
|
|
page?: number;
|
|
page_size?: number;
|
|
agent_id?: string;
|
|
action?: string;
|
|
result?: string;
|
|
since?: string;
|
|
}): Promise<{ logs: any[]; total: number; page: number; page_size: number }> => {
|
|
const response = await api.get('/logs', { params });
|
|
return response.data;
|
|
},
|
|
|
|
// Get active operations for live status view
|
|
getActiveOperations: async (): Promise<{ operations: any[]; count: number }> => {
|
|
const response = await api.get('/logs/active');
|
|
return response.data;
|
|
},
|
|
|
|
// Get active commands for live command control
|
|
getActiveCommands: async (): Promise<{ commands: any[]; count: number }> => {
|
|
const response = await api.get('/commands/active');
|
|
return response.data;
|
|
},
|
|
|
|
// Get recent commands for retry functionality
|
|
getRecentCommands: async (limit?: number): Promise<{ commands: any[]; count: number; limit: number }> => {
|
|
const response = await api.get('/commands/recent', {
|
|
params: limit ? { limit } : undefined
|
|
});
|
|
return response.data;
|
|
},
|
|
};
|
|
|
|
export const authApi = {
|
|
// Login with username and password
|
|
login: async (credentials: { username: string; password: string }): Promise<{ token: string; user: any }> => {
|
|
const response = await api.post('/auth/login', credentials);
|
|
return response.data;
|
|
},
|
|
|
|
// Verify token
|
|
verifyToken: async (): Promise<{ valid: boolean }> => {
|
|
const response = await api.get('/auth/verify');
|
|
return response.data;
|
|
},
|
|
|
|
// Logout
|
|
logout: async (): Promise<void> => {
|
|
await api.post('/auth/logout');
|
|
},
|
|
};
|
|
|
|
// Setup API for server configuration (uses nginx proxy)
|
|
const setupApiInstance = axios.create({
|
|
baseURL: '/api',
|
|
timeout: 30000,
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
});
|
|
|
|
export const setupApi = {
|
|
// Check server health and status
|
|
checkHealth: async (): Promise<{ status: string }> => {
|
|
const response = await setupApiInstance.get('/health');
|
|
return response.data;
|
|
},
|
|
|
|
// Submit server configuration
|
|
configure: async (config: {
|
|
adminUser: string;
|
|
adminPassword: string;
|
|
dbHost: string;
|
|
dbPort: string;
|
|
dbName: string;
|
|
dbUser: string;
|
|
dbPassword: string;
|
|
serverHost: string;
|
|
serverPort: string;
|
|
maxSeats: string;
|
|
}): Promise<{ message: string; jwtSecret?: string; envContent?: string; manualRestartRequired?: boolean; manualRestartCommand?: string; configFilePath?: string }> => {
|
|
const response = await setupApiInstance.post('/setup/configure', config);
|
|
return response.data;
|
|
},
|
|
};
|
|
|
|
// Utility functions
|
|
export const createQueryString = (params: Record<string, any>): string => {
|
|
const searchParams = new URLSearchParams();
|
|
Object.entries(params).forEach(([key, value]) => {
|
|
if (value !== undefined && value !== null && value !== '') {
|
|
if (Array.isArray(value)) {
|
|
value.forEach(v => searchParams.append(key, v));
|
|
} else {
|
|
searchParams.append(key, value.toString());
|
|
}
|
|
}
|
|
});
|
|
return searchParams.toString();
|
|
};
|
|
|
|
// Error handling utility
|
|
export const handleApiError = (error: any): ApiError => {
|
|
if (axios.isAxiosError(error)) {
|
|
const status = error.response?.status;
|
|
const data = error.response?.data;
|
|
|
|
if (status === 401) {
|
|
return {
|
|
message: 'Authentication required. Please log in.',
|
|
code: 'UNAUTHORIZED',
|
|
};
|
|
}
|
|
|
|
if (status === 403) {
|
|
return {
|
|
message: 'Access denied. You do not have permission to perform this action.',
|
|
code: 'FORBIDDEN',
|
|
};
|
|
}
|
|
|
|
if (status === 404) {
|
|
return {
|
|
message: 'The requested resource was not found.',
|
|
code: 'NOT_FOUND',
|
|
};
|
|
}
|
|
|
|
if (status === 429) {
|
|
return {
|
|
message: 'Too many requests. Please try again later.',
|
|
code: 'RATE_LIMIT_EXCEEDED',
|
|
};
|
|
}
|
|
|
|
if (status && status >= 500) {
|
|
return {
|
|
message: 'Server error. Please try again later.',
|
|
code: 'SERVER_ERROR',
|
|
};
|
|
}
|
|
|
|
return {
|
|
message: data?.message || error.message || 'An error occurred',
|
|
code: data?.code || 'UNKNOWN_ERROR',
|
|
details: data?.details,
|
|
};
|
|
}
|
|
|
|
return {
|
|
message: error.message || 'An unexpected error occurred',
|
|
code: 'UNKNOWN_ERROR',
|
|
};
|
|
};
|
|
|
|
// Docker-specific API endpoints
|
|
export const dockerApi = {
|
|
// Get all Docker containers and images across all agents
|
|
getContainers: async (params?: {
|
|
page?: number;
|
|
page_size?: number;
|
|
agent?: string;
|
|
status?: string;
|
|
search?: string;
|
|
}): Promise<DockerContainerListResponse> => {
|
|
const response = await api.get('/docker/containers', { params });
|
|
return response.data;
|
|
},
|
|
|
|
// Get Docker containers for a specific agent
|
|
getAgentContainers: async (agentId: string, params?: {
|
|
page?: number;
|
|
page_size?: number;
|
|
status?: string;
|
|
search?: string;
|
|
}): Promise<DockerContainerListResponse> => {
|
|
const response = await api.get(`/agents/${agentId}/docker`, { params });
|
|
return response.data;
|
|
},
|
|
|
|
// Get Docker statistics
|
|
getStats: async (): Promise<DockerStats> => {
|
|
const response = await api.get('/docker/stats');
|
|
return response.data;
|
|
},
|
|
|
|
// Approve Docker image update
|
|
approveUpdate: async (containerId: string, imageId: string, scheduledAt?: string): Promise<void> => {
|
|
await api.post(`/docker/containers/${containerId}/images/${imageId}/approve`, {
|
|
scheduled_at: scheduledAt,
|
|
});
|
|
},
|
|
|
|
// Reject Docker image update
|
|
rejectUpdate: async (containerId: string, imageId: string): Promise<void> => {
|
|
await api.post(`/docker/containers/${containerId}/images/${imageId}/reject`);
|
|
},
|
|
|
|
// Install Docker image update
|
|
installUpdate: async (containerId: string, imageId: string): Promise<void> => {
|
|
await api.post(`/docker/containers/${containerId}/images/${imageId}/install`);
|
|
},
|
|
|
|
// Bulk approve Docker updates
|
|
bulkApproveUpdates: async (updates: Array<{ containerId: string; imageId: string }>, scheduledAt?: string): Promise<{ approved: number }> => {
|
|
const response = await api.post('/docker/updates/bulk-approve', {
|
|
updates,
|
|
scheduled_at: scheduledAt,
|
|
});
|
|
return response.data;
|
|
},
|
|
|
|
// Bulk reject Docker updates
|
|
bulkRejectUpdates: async (updates: Array<{ containerId: string; imageId: string }>): Promise<{ rejected: number }> => {
|
|
const response = await api.post('/docker/updates/bulk-reject', {
|
|
updates,
|
|
});
|
|
return response.data;
|
|
},
|
|
|
|
// Trigger Docker scan on agents
|
|
triggerScan: async (agentIds?: string[]): Promise<void> => {
|
|
await api.post('/docker/scan', { agent_ids: agentIds });
|
|
},
|
|
};
|
|
|
|
// Admin API endpoints
|
|
export const adminApi = {
|
|
// Registration Token Management
|
|
tokens: {
|
|
// Get all registration tokens
|
|
getTokens: async (params?: {
|
|
page?: number;
|
|
page_size?: number;
|
|
is_active?: boolean;
|
|
label?: string;
|
|
}): Promise<{ tokens: RegistrationToken[]; total: number; page: number; page_size: number }> => {
|
|
const response = await api.get('/admin/registration-tokens', { params });
|
|
return response.data;
|
|
},
|
|
|
|
// Get single registration token
|
|
getToken: async (id: string): Promise<RegistrationToken> => {
|
|
const response = await api.get(`/admin/registration-tokens/${id}`);
|
|
return response.data;
|
|
},
|
|
|
|
// Create new registration token
|
|
createToken: async (request: CreateRegistrationTokenRequest): Promise<RegistrationToken> => {
|
|
const response = await api.post('/admin/registration-tokens', request);
|
|
return response.data;
|
|
},
|
|
|
|
// Revoke registration token (soft delete)
|
|
revokeToken: async (id: string): Promise<void> => {
|
|
await api.delete(`/admin/registration-tokens/${id}`);
|
|
},
|
|
|
|
// Delete registration token (hard delete)
|
|
deleteToken: async (id: string): Promise<void> => {
|
|
await api.delete(`/admin/registration-tokens/delete/${id}`);
|
|
},
|
|
|
|
// Get registration token statistics
|
|
getStats: async (): Promise<RegistrationTokenStats> => {
|
|
const response = await api.get('/admin/registration-tokens/stats');
|
|
return response.data;
|
|
},
|
|
|
|
// Cleanup expired tokens
|
|
cleanup: async (): Promise<{ cleaned: number }> => {
|
|
const response = await api.post('/admin/registration-tokens/cleanup');
|
|
return response.data;
|
|
},
|
|
},
|
|
|
|
// Rate Limiting Management
|
|
rateLimits: {
|
|
// Get all rate limit configurations
|
|
getConfigs: async (): Promise<RateLimitConfig[]> => {
|
|
const response = await api.get('/admin/rate-limits');
|
|
|
|
// Backend returns { settings: {...}, updated_at: "..." }
|
|
// Transform settings object to array format expected by frontend
|
|
const settings = response.data.settings || {};
|
|
const configs: RateLimitConfig[] = Object.entries(settings).map(([endpoint, config]: [string, any]) => ({
|
|
...config,
|
|
endpoint,
|
|
updated_at: response.data.updated_at, // Preserve update timestamp
|
|
}));
|
|
|
|
return configs;
|
|
},
|
|
|
|
// Update rate limit configuration
|
|
updateConfig: async (endpoint: string, config: Partial<RateLimitConfig>): Promise<RateLimitConfig> => {
|
|
const response = await api.put(`/admin/rate-limits/${endpoint}`, config);
|
|
return response.data;
|
|
},
|
|
|
|
// Update all rate limit configurations
|
|
updateAllConfigs: async (configs: RateLimitConfig[]): Promise<RateLimitConfig[]> => {
|
|
const response = await api.put('/admin/rate-limits', { configs });
|
|
return response.data;
|
|
},
|
|
|
|
// Reset rate limit configurations to defaults
|
|
resetConfigs: async (): Promise<RateLimitConfig[]> => {
|
|
const response = await api.post('/admin/rate-limits/reset');
|
|
return response.data;
|
|
},
|
|
|
|
// Get rate limit statistics
|
|
getStats: async (): Promise<RateLimitStats[]> => {
|
|
const response = await api.get('/admin/rate-limits/stats');
|
|
return response.data;
|
|
},
|
|
|
|
// Get rate limit usage
|
|
getUsage: async (): Promise<RateLimitUsage[]> => {
|
|
const response = await api.get('/admin/rate-limits/usage');
|
|
return response.data;
|
|
},
|
|
|
|
// Get rate limit summary
|
|
getSummary: async (): Promise<RateLimitSummary> => {
|
|
const response = await api.get('/admin/rate-limits/summary');
|
|
return response.data;
|
|
},
|
|
|
|
// Cleanup expired rate limit data
|
|
cleanup: async (): Promise<{ cleaned: number }> => {
|
|
const response = await api.post('/admin/rate-limits/cleanup');
|
|
return response.data;
|
|
},
|
|
},
|
|
|
|
// System Administration
|
|
system: {
|
|
// Get system health and status
|
|
getHealth: async (): Promise<{
|
|
status: 'healthy' | 'degraded' | 'unhealthy';
|
|
uptime: number;
|
|
version: string;
|
|
database_status: 'connected' | 'disconnected';
|
|
active_agents: number;
|
|
active_tokens: number;
|
|
rate_limits_enabled: boolean;
|
|
}> => {
|
|
const response = await api.get('/admin/system/health');
|
|
return response.data;
|
|
},
|
|
|
|
// Get active agents
|
|
getActiveAgents: async (): Promise<{
|
|
agents: Array<{
|
|
id: string;
|
|
hostname: string;
|
|
last_seen: string;
|
|
status: string;
|
|
}>;
|
|
count: number;
|
|
}> => {
|
|
const response = await api.get('/admin/system/active-agents');
|
|
return response.data;
|
|
},
|
|
|
|
// Get system configuration
|
|
getConfig: async (): Promise<Record<string, any>> => {
|
|
const response = await api.get('/admin/system/config');
|
|
return response.data;
|
|
},
|
|
|
|
// Update system configuration
|
|
updateConfig: async (config: Record<string, any>): Promise<Record<string, any>> => {
|
|
const response = await api.put('/admin/system/config', config);
|
|
return response.data;
|
|
},
|
|
},
|
|
};
|
|
|
|
// Security API endpoints
|
|
export const securityApi = {
|
|
// Get comprehensive security overview
|
|
getOverview: async (): Promise<{
|
|
timestamp: string;
|
|
overall_status: 'healthy' | 'degraded' | 'unhealthy';
|
|
subsystems: {
|
|
ed25519_signing: { status: string; enabled: boolean };
|
|
nonce_validation: { status: string; enabled: boolean };
|
|
machine_binding: { status: string; enabled: boolean };
|
|
command_validation: { status: string; enabled: boolean };
|
|
};
|
|
alerts: string[];
|
|
recommendations: string[];
|
|
}> => {
|
|
const response = await api.get('/security/overview');
|
|
return response.data;
|
|
},
|
|
|
|
// Get Ed25519 signing service status
|
|
getSigningStatus: async (): Promise<{
|
|
status: string;
|
|
timestamp: string;
|
|
checks: {
|
|
service_initialized: boolean;
|
|
public_key_available: boolean;
|
|
signing_operational: boolean;
|
|
};
|
|
public_key_fingerprint?: string;
|
|
algorithm?: string;
|
|
}> => {
|
|
const response = await api.get('/security/signing');
|
|
return response.data;
|
|
},
|
|
|
|
// Get nonce validation status
|
|
getNonceStatus: async (): Promise<{
|
|
status: string;
|
|
timestamp: string;
|
|
checks: {
|
|
validation_enabled: boolean;
|
|
max_age_minutes: number;
|
|
recent_validations: number;
|
|
validation_failures: number;
|
|
};
|
|
details: {
|
|
nonce_format: string;
|
|
signature_algorithm: string;
|
|
replay_protection: string;
|
|
};
|
|
}> => {
|
|
const response = await api.get('/security/nonce');
|
|
return response.data;
|
|
},
|
|
|
|
// Get machine binding status
|
|
getMachineBindingStatus: async (): Promise<{
|
|
status: string;
|
|
timestamp: string;
|
|
checks: {
|
|
binding_enforced: boolean;
|
|
min_agent_version: string;
|
|
fingerprint_required: boolean;
|
|
recent_violations: number;
|
|
};
|
|
details: {
|
|
enforcement_method: string;
|
|
binding_scope: string;
|
|
violation_action: string;
|
|
};
|
|
}> => {
|
|
const response = await api.get('/security/machine-binding');
|
|
return response.data;
|
|
},
|
|
|
|
// Get command validation status
|
|
getCommandValidationStatus: async (): Promise<{
|
|
status: string;
|
|
timestamp: string;
|
|
metrics: {
|
|
total_pending_commands: number;
|
|
agents_with_pending: number;
|
|
commands_last_hour: number;
|
|
commands_last_24h: number;
|
|
};
|
|
checks: {
|
|
command_processing: string;
|
|
backpressure_active: boolean;
|
|
agent_responsive: string;
|
|
};
|
|
}> => {
|
|
const response = await api.get('/security/commands');
|
|
return response.data;
|
|
},
|
|
|
|
// Get detailed security metrics
|
|
getMetrics: async (): Promise<{
|
|
timestamp: string;
|
|
signing: {
|
|
public_key_fingerprint: string;
|
|
algorithm: string;
|
|
key_size: number;
|
|
configured: boolean;
|
|
};
|
|
nonce: {
|
|
max_age_seconds: number;
|
|
format: string;
|
|
};
|
|
machine_binding: {
|
|
min_version: string;
|
|
enforcement: string;
|
|
};
|
|
command_processing: {
|
|
backpressure_threshold: number;
|
|
rate_limit_per_second: number;
|
|
};
|
|
}> => {
|
|
const response = await api.get('/security/metrics');
|
|
return response.data;
|
|
},
|
|
};
|
|
|
|
// Storage Metrics API
|
|
export const storageMetricsApi = {
|
|
// Report storage metrics (agent only)
|
|
async reportStorageMetrics(agentID: string, data: any): Promise<void> {
|
|
await api.post(`/agents/${agentID}/storage-metrics`, data);
|
|
},
|
|
|
|
// Get storage metrics for an agent
|
|
async getStorageMetrics(agentID: string): Promise<any> {
|
|
const response = await api.get(`/agents/${agentID}/storage-metrics`);
|
|
return response.data;
|
|
},
|
|
};
|
|
|
|
export default api; |