feat: LAD A-Team CIS Compliance Report, React SPA, i18n, report management, explorer UX fixes
- Professional Oracle-format compliance report with RAG remediation from ADB vector store - React 19 SPA at /app/ (16 pages, TypeScript, Vite, Zustand, i18n pt/en 625 keys) - Report delete endpoint, embed individual report files into vector store - Terraform ZIP download (client-side, pure JS) - Explorer silent polling during start/stop (no flickering) - HTML report and compliance report sections start minimized - Token auth fix for compliance report CSV download links
This commit is contained in:
30
frontend-react/src/api/client.ts
Normal file
30
frontend-react/src/api/client.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import axios from 'axios';
|
||||
|
||||
const client = axios.create({
|
||||
baseURL: '/api',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
|
||||
// Request: attach JWT
|
||||
client.interceptors.request.use((config) => {
|
||||
const token = localStorage.getItem('t');
|
||||
if (token) {
|
||||
config.headers.Authorization = `Bearer ${token}`;
|
||||
}
|
||||
return config;
|
||||
});
|
||||
|
||||
// Response: handle 401 + extract data
|
||||
client.interceptors.response.use(
|
||||
(res) => res.data,
|
||||
(err) => {
|
||||
if (err.response?.status === 401) {
|
||||
localStorage.removeItem('t');
|
||||
window.location.href = '/app/login';
|
||||
}
|
||||
const detail = err.response?.data?.detail || err.response?.data?.message || err.message || 'Erro';
|
||||
return Promise.reject(new Error(detail));
|
||||
}
|
||||
);
|
||||
|
||||
export default client;
|
||||
71
frontend-react/src/api/endpoints/adb.ts
Normal file
71
frontend-react/src/api/endpoints/adb.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import client from '../client';
|
||||
|
||||
export interface AdbTableDef {
|
||||
id: string;
|
||||
table_name: string;
|
||||
description?: string;
|
||||
is_active: boolean;
|
||||
}
|
||||
|
||||
export interface AdbConfigFull {
|
||||
id: string;
|
||||
config_name: string;
|
||||
dsn: string;
|
||||
username: string;
|
||||
table_name?: string;
|
||||
use_mtls: boolean;
|
||||
is_active: boolean;
|
||||
wallet_dir?: string;
|
||||
genai_config_id?: string;
|
||||
embedding_model_id?: string;
|
||||
tables?: AdbTableDef[];
|
||||
created_at?: string;
|
||||
}
|
||||
|
||||
export interface AdbTestResult {
|
||||
status: 'success' | 'error';
|
||||
message: string;
|
||||
}
|
||||
|
||||
export interface WalletParseResult {
|
||||
dsn_names: string[];
|
||||
files: string[];
|
||||
}
|
||||
|
||||
export const adbApi = {
|
||||
list: () =>
|
||||
client.get('/adb/configs') as unknown as Promise<AdbConfigFull[]>,
|
||||
|
||||
create: (fd: FormData) =>
|
||||
client.post('/adb/config', fd, { headers: { 'Content-Type': 'multipart/form-data' } }) as unknown as Promise<{ id: string; config_name: string }>,
|
||||
|
||||
update: (id: string, fd: FormData) =>
|
||||
client.put(`/adb/configs/${id}`, fd, { headers: { 'Content-Type': 'multipart/form-data' } }) as unknown as Promise<{ id: string; config_name: string }>,
|
||||
|
||||
remove: (id: string) =>
|
||||
client.delete(`/adb/configs/${id}`) as unknown as Promise<{ ok: boolean }>,
|
||||
|
||||
test: (id: string) =>
|
||||
client.post(`/adb/test/${id}`) as unknown as Promise<AdbTestResult>,
|
||||
|
||||
parseWallet: (file: File) => {
|
||||
const fd = new FormData();
|
||||
fd.append('wallet', file);
|
||||
return client.post('/adb/parse-wallet', fd, { headers: { 'Content-Type': 'multipart/form-data' } }) as unknown as Promise<WalletParseResult>;
|
||||
},
|
||||
|
||||
uploadWallet: (id: string, file: File) => {
|
||||
const fd = new FormData();
|
||||
fd.append('wallet', file);
|
||||
return client.post(`/adb/${id}/upload-wallet`, fd, { headers: { 'Content-Type': 'multipart/form-data' } }) as unknown as Promise<{ ok: boolean; files: string[] }>;
|
||||
},
|
||||
|
||||
addTable: (vid: string, tableName: string, description: string) =>
|
||||
client.post(`/adb/${vid}/tables`, { table_name: tableName, description }) as unknown as Promise<{ id: string }>,
|
||||
|
||||
updateTable: (vid: string, tid: string, body: { table_name?: string; description?: string; is_active?: boolean }) =>
|
||||
client.put(`/adb/${vid}/tables/${tid}`, body) as unknown as Promise<{ ok: boolean }>,
|
||||
|
||||
removeTable: (vid: string, tid: string) =>
|
||||
client.delete(`/adb/${vid}/tables/${tid}`) as unknown as Promise<{ ok: boolean }>,
|
||||
};
|
||||
72
frontend-react/src/api/endpoints/admin.ts
Normal file
72
frontend-react/src/api/endpoints/admin.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import client from '../client';
|
||||
|
||||
/* ── Types ── */
|
||||
|
||||
export interface AuditEntry {
|
||||
id: string;
|
||||
user_id: string | null;
|
||||
username: string | null;
|
||||
action: string;
|
||||
resource: string | null;
|
||||
details: string | null;
|
||||
ip: string | null;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export interface ConfigLogEntry {
|
||||
id: string;
|
||||
config_type: string;
|
||||
config_id: string;
|
||||
config_name: string | null;
|
||||
severity: string;
|
||||
action: string;
|
||||
message: string;
|
||||
user_id: string | null;
|
||||
username: string | null;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export interface ChatLogEntry {
|
||||
id: string;
|
||||
session_id: string | null;
|
||||
message_id: string | null;
|
||||
user_id: string | null;
|
||||
severity: string;
|
||||
source: string;
|
||||
action: string;
|
||||
message: string;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
/* ── API calls ── */
|
||||
|
||||
export const adminApi = {
|
||||
getAuditLog: (limit = 100) =>
|
||||
client.get(`/audit-log?limit=${limit}`) as unknown as Promise<AuditEntry[]>,
|
||||
|
||||
getConfigLogs: (params: {
|
||||
config_type?: string;
|
||||
config_id?: string;
|
||||
severity?: string;
|
||||
limit?: number;
|
||||
} = {}) => {
|
||||
const qs = new URLSearchParams();
|
||||
if (params.config_type) qs.set('config_type', params.config_type);
|
||||
if (params.config_id) qs.set('config_id', params.config_id);
|
||||
if (params.severity) qs.set('severity', params.severity);
|
||||
qs.set('limit', String(params.limit ?? 50));
|
||||
return client.get(`/config-logs?${qs}`) as unknown as Promise<ConfigLogEntry[]>;
|
||||
},
|
||||
|
||||
getChatLogs: (params: {
|
||||
severity?: string;
|
||||
session_id?: string;
|
||||
limit?: number;
|
||||
} = {}) => {
|
||||
const qs = new URLSearchParams();
|
||||
if (params.severity) qs.set('severity', params.severity);
|
||||
if (params.session_id) qs.set('session_id', params.session_id);
|
||||
qs.set('limit', String(params.limit ?? 50));
|
||||
return client.get(`/chat-logs?${qs}`) as unknown as Promise<ChatLogEntry[]>;
|
||||
},
|
||||
};
|
||||
31
frontend-react/src/api/endpoints/auth.ts
Normal file
31
frontend-react/src/api/endpoints/auth.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import client from '../client';
|
||||
|
||||
export interface User {
|
||||
id: string;
|
||||
username: string;
|
||||
role: string;
|
||||
first_name?: string;
|
||||
last_name?: string;
|
||||
email?: string;
|
||||
mfa_enabled?: boolean;
|
||||
}
|
||||
|
||||
export interface LoginResponse {
|
||||
token?: string;
|
||||
user?: User;
|
||||
mfa_required?: boolean;
|
||||
mfa_setup?: boolean;
|
||||
totp_uri?: string;
|
||||
}
|
||||
|
||||
export const authApi = {
|
||||
login: (username: string, password: string, totp_code?: string) =>
|
||||
client.post<never, LoginResponse>('/auth/login', { username, password, ...(totp_code && { totp_code }) }),
|
||||
|
||||
logout: () => client.post('/auth/logout'),
|
||||
|
||||
me: () => client.get<never, User>('/users/me'),
|
||||
|
||||
changePassword: (current_password: string, new_password: string) =>
|
||||
client.post('/auth/change-password', { current_password, new_password }),
|
||||
};
|
||||
94
frontend-react/src/api/endpoints/chat.ts
Normal file
94
frontend-react/src/api/endpoints/chat.ts
Normal file
@@ -0,0 +1,94 @@
|
||||
import client from '../client';
|
||||
|
||||
/* ── Types ── */
|
||||
|
||||
export interface ChatSession {
|
||||
id: string;
|
||||
title: string;
|
||||
agent_type: string;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
message_count?: number;
|
||||
}
|
||||
|
||||
export interface ChatMessage {
|
||||
id: string;
|
||||
session_id?: string;
|
||||
role: 'user' | 'assistant';
|
||||
content: string;
|
||||
model_id?: string;
|
||||
status?: 'processing' | 'done' | 'failed';
|
||||
created_at?: string;
|
||||
}
|
||||
|
||||
export interface ChatSendBody {
|
||||
message: string;
|
||||
session_id?: string;
|
||||
genai_config_id?: string;
|
||||
model_id?: string;
|
||||
oci_config_id?: string;
|
||||
genai_region?: string;
|
||||
compartment_id?: string;
|
||||
temperature?: number;
|
||||
max_tokens?: number;
|
||||
top_p?: number;
|
||||
top_k?: number;
|
||||
frequency_penalty?: number;
|
||||
presence_penalty?: number;
|
||||
reasoning_effort?: string;
|
||||
use_tools?: boolean;
|
||||
}
|
||||
|
||||
export interface ChatSendResponse {
|
||||
message_id: string;
|
||||
session_id: string;
|
||||
status: 'processing' | 'done';
|
||||
}
|
||||
|
||||
export interface MessageStatusResponse {
|
||||
id: string;
|
||||
session_id: string;
|
||||
role: string;
|
||||
content: string;
|
||||
model_id?: string;
|
||||
status: 'processing' | 'done' | 'failed';
|
||||
}
|
||||
|
||||
export interface SessionMessagesResponse {
|
||||
session: ChatSession;
|
||||
messages: ChatMessage[];
|
||||
}
|
||||
|
||||
/* ── API ── */
|
||||
|
||||
export const chatApi = {
|
||||
/** Send a chat message (text only) */
|
||||
send: (body: ChatSendBody) =>
|
||||
client.post('/chat', body) as unknown as Promise<ChatSendResponse>,
|
||||
|
||||
/** Send a chat message with file attachments */
|
||||
upload: (formData: FormData) =>
|
||||
client.post('/chat/upload', formData, {
|
||||
headers: { 'Content-Type': 'multipart/form-data' },
|
||||
}) as unknown as Promise<ChatSendResponse>,
|
||||
|
||||
/** Poll message processing status */
|
||||
pollStatus: (messageId: string) =>
|
||||
client.get(`/chat/${messageId}/status`) as unknown as Promise<MessageStatusResponse>,
|
||||
|
||||
/** List chat sessions */
|
||||
listSessions: (agentType = 'chat', limit = 50) =>
|
||||
client.get(`/chat/sessions?agent_type=${agentType}&limit=${limit}`) as unknown as Promise<ChatSession[]>,
|
||||
|
||||
/** Load session with all messages */
|
||||
loadSession: (sessionId: string) =>
|
||||
client.get(`/chat/sessions/${sessionId}/messages`) as unknown as Promise<SessionMessagesResponse>,
|
||||
|
||||
/** Delete a session */
|
||||
deleteSession: (sessionId: string) =>
|
||||
client.delete(`/chat/${sessionId}`) as unknown as Promise<{ ok: boolean }>,
|
||||
|
||||
/** Rename a session */
|
||||
renameSession: (sessionId: string, title: string) =>
|
||||
client.put(`/chat/sessions/${sessionId}/title`, { title }) as unknown as Promise<{ ok: boolean }>,
|
||||
};
|
||||
97
frontend-react/src/api/endpoints/embeddings.ts
Normal file
97
frontend-react/src/api/endpoints/embeddings.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
import client from '../client';
|
||||
|
||||
/* ── Types ── */
|
||||
|
||||
export interface EmbeddingDoc {
|
||||
id: string;
|
||||
metadata: string;
|
||||
}
|
||||
|
||||
export interface EmbeddingListResult {
|
||||
total: number;
|
||||
offset: number;
|
||||
limit: number;
|
||||
documents: EmbeddingDoc[];
|
||||
}
|
||||
|
||||
export interface EmbedUploadResult {
|
||||
ok: boolean;
|
||||
message: string;
|
||||
chunks: number;
|
||||
filename?: string;
|
||||
url?: string;
|
||||
}
|
||||
|
||||
export interface PurgeResult {
|
||||
ok: boolean;
|
||||
deleted: number;
|
||||
table: string;
|
||||
tenancy: string;
|
||||
}
|
||||
|
||||
export interface ConsultResult {
|
||||
answer: string;
|
||||
documents: {
|
||||
content: string;
|
||||
source: string;
|
||||
distance: number;
|
||||
metadata: string;
|
||||
}[];
|
||||
total: number;
|
||||
}
|
||||
|
||||
/* ── API ── */
|
||||
|
||||
export const embeddingsApi = {
|
||||
/** List embeddings for a given ADB config + optional table */
|
||||
list: (vid: string, tableName?: string, limit = 50, offset = 0) => {
|
||||
const params = new URLSearchParams();
|
||||
if (tableName) params.set('table_name', tableName);
|
||||
params.set('limit', String(limit));
|
||||
params.set('offset', String(offset));
|
||||
return client.get(`/embeddings/${vid}/list?${params}`) as unknown as Promise<EmbeddingListResult>;
|
||||
},
|
||||
|
||||
/** Delete a single embedding document */
|
||||
remove: (vid: string, docId: string, tableName?: string) => {
|
||||
const qs = tableName ? `?table_name=${encodeURIComponent(tableName)}` : '';
|
||||
return client.delete(`/embeddings/${vid}/${docId}${qs}`) as unknown as Promise<{ ok: boolean }>;
|
||||
},
|
||||
|
||||
/** Upload a file to generate embeddings */
|
||||
uploadFile: (adbConfigId: string, tableName: string, file: File) => {
|
||||
const fd = new FormData();
|
||||
fd.append('adb_config_id', adbConfigId);
|
||||
fd.append('table_name', tableName);
|
||||
fd.append('file', file);
|
||||
return client.post('/embeddings/upload', fd, {
|
||||
headers: { 'Content-Type': 'multipart/form-data' },
|
||||
}) as unknown as Promise<EmbedUploadResult>;
|
||||
},
|
||||
|
||||
/** Import a URL to generate embeddings */
|
||||
uploadUrl: (adbConfigId: string, tableName: string, url: string) => {
|
||||
const fd = new FormData();
|
||||
fd.append('adb_config_id', adbConfigId);
|
||||
fd.append('table_name', tableName);
|
||||
fd.append('url', url);
|
||||
return client.post('/embeddings/upload-url', fd, {
|
||||
headers: { 'Content-Type': 'multipart/form-data' },
|
||||
}) as unknown as Promise<EmbedUploadResult>;
|
||||
},
|
||||
|
||||
/** Purge embeddings from a table (optionally filtered by tenancy) */
|
||||
purge: (vid: string, tableName: string, tenancy?: string) =>
|
||||
client.post(`/embeddings/${vid}/purge`, {
|
||||
table_name: tableName,
|
||||
tenancy: tenancy || '',
|
||||
}) as unknown as Promise<PurgeResult>,
|
||||
|
||||
/** Consult embeddings with a question */
|
||||
consult: (query: string, tableName?: string, topK = 10) =>
|
||||
client.post('/embeddings/consult', {
|
||||
query,
|
||||
table_name: tableName || '',
|
||||
top_k: topK,
|
||||
}) as unknown as Promise<ConsultResult>,
|
||||
};
|
||||
129
frontend-react/src/api/endpoints/explorer.ts
Normal file
129
frontend-react/src/api/endpoints/explorer.ts
Normal file
@@ -0,0 +1,129 @@
|
||||
import client from '../client';
|
||||
|
||||
/* ── Types ─────────────────────────────────────────────────────────────────── */
|
||||
|
||||
export interface CompartmentNode {
|
||||
id: string;
|
||||
name: string;
|
||||
parent_id: string | null;
|
||||
lifecycle_state?: string;
|
||||
}
|
||||
|
||||
export interface Region {
|
||||
name: string;
|
||||
key: string;
|
||||
status: string;
|
||||
is_home: boolean;
|
||||
}
|
||||
|
||||
export interface CountEntry {
|
||||
count: number;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
export type CountsMap = Record<string, CountEntry>;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export type ResourceItem = Record<string, any>;
|
||||
|
||||
export interface ActionResult {
|
||||
ok: boolean;
|
||||
action: string;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export interface MyIpResult {
|
||||
ip: string;
|
||||
}
|
||||
|
||||
/* ── API ───────────────────────────────────────────────────────────────────── */
|
||||
|
||||
export const explorerApi = {
|
||||
/* Compartment tree (flat list with parent_id) */
|
||||
getCompartmentTree: (cid: string) =>
|
||||
client.get(`/oci/explore/${cid}/compartment-tree`) as unknown as Promise<CompartmentNode[]>,
|
||||
|
||||
/* Regions subscribed */
|
||||
getRegions: (cid: string) =>
|
||||
client.get(`/oci/explore/${cid}/regions`) as unknown as Promise<Region[]>,
|
||||
|
||||
/* Cached counts */
|
||||
getCounts: (cid: string, compartmentId: string, regions: string = '') =>
|
||||
client.get(`/oci/explore/${cid}/counts`, {
|
||||
params: { compartment_id: compartmentId, regions },
|
||||
}) as unknown as Promise<CountsMap>,
|
||||
|
||||
/* Trigger background counts refresh */
|
||||
refreshCounts: (cid: string, compartmentId: string, regions: string[]) =>
|
||||
client.post(`/oci/explore/${cid}/counts/refresh`, {
|
||||
compartment_id: compartmentId,
|
||||
regions,
|
||||
}) as unknown as Promise<{ status: string; types: number }>,
|
||||
|
||||
/* Fetch resources of a given type */
|
||||
getResources: (cid: string, resourceType: string, compartmentId: string, region?: string) =>
|
||||
client.get(`/oci/explore/${cid}/${resourceType}`, {
|
||||
params: { compartment_id: compartmentId, region: region || undefined },
|
||||
}) as unknown as Promise<ResourceItem[]>,
|
||||
|
||||
/* ── Actions ─────────────────────────────────────────────────────────────── */
|
||||
|
||||
instanceAction: (id: string, action: 'START' | 'STOP', ociConfigId: string, region?: string) =>
|
||||
client.post(`/oci/instances/${id}/action`, {
|
||||
action, oci_config_id: ociConfigId, region,
|
||||
}) as unknown as Promise<ActionResult>,
|
||||
|
||||
adbAction: (id: string, action: 'start' | 'stop', ociConfigId: string, region?: string) =>
|
||||
client.post(`/oci/autonomous-databases/${id}/action`, {
|
||||
action, oci_config_id: ociConfigId, region,
|
||||
}) as unknown as Promise<ActionResult>,
|
||||
|
||||
dbSystemAction: (id: string, action: 'START' | 'STOP', ociConfigId: string, region?: string) =>
|
||||
client.post(`/oci/db-systems/${id}/action`, {
|
||||
action, oci_config_id: ociConfigId, region,
|
||||
}) as unknown as Promise<ActionResult>,
|
||||
|
||||
mysqlAction: (id: string, action: 'start' | 'stop', ociConfigId: string, region?: string) =>
|
||||
client.post(`/oci/mysql-db-systems/${id}/action`, {
|
||||
action, oci_config_id: ociConfigId, region,
|
||||
}) as unknown as Promise<ActionResult>,
|
||||
|
||||
containerAction: (id: string, action: 'start' | 'stop', ociConfigId: string, region?: string) =>
|
||||
client.post(`/oci/container-instances/${id}/action`, {
|
||||
action, oci_config_id: ociConfigId, region,
|
||||
}) as unknown as Promise<ActionResult>,
|
||||
|
||||
getMyIp: () =>
|
||||
client.get('/oci/my-ip') as unknown as Promise<MyIpResult>,
|
||||
|
||||
adbUpdateNetwork: (id: string, ip: string, ociConfigId: string, region?: string) =>
|
||||
client.post(`/oci/autonomous-databases/${id}/update-network-access`, {
|
||||
ip, oci_config_id: ociConfigId, region,
|
||||
}) as unknown as Promise<ActionResult>,
|
||||
|
||||
/* ── Security List Rules ─────────────────────────────────────────────────── */
|
||||
|
||||
addSlRule: (cid: string, slId: string, body: {
|
||||
direction: string; protocol: string; source_dest: string;
|
||||
port_min?: number; port_max?: number; description?: string;
|
||||
is_stateless?: boolean; region?: string;
|
||||
}) =>
|
||||
client.post(`/oci/explore/${cid}/security_lists/${slId}/rules`, body) as unknown as Promise<ActionResult>,
|
||||
|
||||
removeSlRule: (cid: string, slId: string, body: {
|
||||
direction: string; rule_index: number; region?: string;
|
||||
}) =>
|
||||
client.delete(`/oci/explore/${cid}/security_lists/${slId}/rules`, { data: body }) as unknown as Promise<ActionResult>,
|
||||
|
||||
/* ── NSG Rules ───────────────────────────────────────────────────────────── */
|
||||
|
||||
addNsgRule: (cid: string, nsgId: string, body: {
|
||||
direction: string; protocol: string; source_dest: string;
|
||||
source_dest_type?: string; port_min?: number; port_max?: number;
|
||||
description?: string; is_stateless?: boolean; region?: string;
|
||||
}) =>
|
||||
client.post(`/oci/explore/${cid}/nsgs/${nsgId}/rules`, body) as unknown as Promise<ActionResult>,
|
||||
|
||||
removeNsgRule: (cid: string, nsgId: string, ruleId: string, region?: string) =>
|
||||
client.delete(`/oci/explore/${cid}/nsgs/${nsgId}/rules/${ruleId}${region ? `?region=${region}` : ''}`) as unknown as Promise<ActionResult>,
|
||||
};
|
||||
66
frontend-react/src/api/endpoints/genai.ts
Normal file
66
frontend-react/src/api/endpoints/genai.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import client from '../client';
|
||||
|
||||
export interface GenAiConfigFull {
|
||||
id: string;
|
||||
name: string;
|
||||
oci_config_id: string;
|
||||
model_id: string;
|
||||
model_ocid?: string;
|
||||
compartment_id: string;
|
||||
genai_region: string;
|
||||
endpoint?: string;
|
||||
serving_type: string;
|
||||
dedicated_endpoint_id?: string;
|
||||
temperature: number;
|
||||
max_tokens: number;
|
||||
top_p: number;
|
||||
top_k: number;
|
||||
frequency_penalty: number;
|
||||
presence_penalty: number;
|
||||
reasoning_effort?: string;
|
||||
is_default: boolean;
|
||||
created_at?: string;
|
||||
}
|
||||
|
||||
export interface GenAiConfigPayload {
|
||||
name: string;
|
||||
oci_config_id: string;
|
||||
model_id: string;
|
||||
model_ocid?: string | null;
|
||||
compartment_id: string;
|
||||
genai_region: string;
|
||||
endpoint?: string | null;
|
||||
serving_type?: string;
|
||||
dedicated_endpoint_id?: string | null;
|
||||
temperature?: number;
|
||||
max_tokens?: number;
|
||||
top_p?: number;
|
||||
top_k?: number;
|
||||
frequency_penalty?: number;
|
||||
presence_penalty?: number;
|
||||
reasoning_effort?: string | null;
|
||||
is_default?: boolean;
|
||||
}
|
||||
|
||||
export interface GenAiTestResult {
|
||||
status: 'success' | 'error';
|
||||
message: string;
|
||||
response?: string;
|
||||
}
|
||||
|
||||
export const genaiApi = {
|
||||
list: () =>
|
||||
client.get('/genai/configs') as unknown as Promise<GenAiConfigFull[]>,
|
||||
|
||||
create: (body: GenAiConfigPayload) =>
|
||||
client.post('/genai/config', body) as unknown as Promise<{ id: string; model_id: string; endpoint: string }>,
|
||||
|
||||
update: (id: string, body: GenAiConfigPayload) =>
|
||||
client.put(`/genai/configs/${id}`, body) as unknown as Promise<{ id: string; model_id: string; endpoint: string }>,
|
||||
|
||||
remove: (id: string) =>
|
||||
client.delete(`/genai/configs/${id}`) as unknown as Promise<{ ok: boolean }>,
|
||||
|
||||
test: (id: string) =>
|
||||
client.post(`/genai/test/${id}`) as unknown as Promise<GenAiTestResult>,
|
||||
};
|
||||
68
frontend-react/src/api/endpoints/mcp.ts
Normal file
68
frontend-react/src/api/endpoints/mcp.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import client from '../client';
|
||||
|
||||
export interface McpServerFull {
|
||||
id: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
server_type: string;
|
||||
command?: string;
|
||||
args?: string[];
|
||||
env_vars?: Record<string, string>;
|
||||
url?: string;
|
||||
module_path?: string;
|
||||
is_active: boolean;
|
||||
tools?: McpToolDef[];
|
||||
linked_adb_id?: string;
|
||||
created_at?: string;
|
||||
}
|
||||
|
||||
export interface McpToolDef {
|
||||
name: string;
|
||||
description?: string;
|
||||
input_schema?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export interface McpServerPayload {
|
||||
name: string;
|
||||
description?: string | null;
|
||||
server_type: string;
|
||||
command?: string | null;
|
||||
args?: string[] | null;
|
||||
env_vars?: Record<string, string> | null;
|
||||
url?: string | null;
|
||||
module_path?: string | null;
|
||||
tools?: McpToolDef[] | null;
|
||||
linked_adb_id?: string | null;
|
||||
}
|
||||
|
||||
export const mcpApi = {
|
||||
list: () =>
|
||||
client.get('/mcp/servers') as unknown as Promise<McpServerFull[]>,
|
||||
|
||||
create: (body: McpServerPayload) =>
|
||||
client.post('/mcp/servers', body) as unknown as Promise<{ id: string; name: string; server_type: string }>,
|
||||
|
||||
update: (id: string, body: McpServerPayload) =>
|
||||
client.put(`/mcp/servers/${id}`, body) as unknown as Promise<{ id: string; name: string; server_type: string }>,
|
||||
|
||||
remove: (id: string) =>
|
||||
client.delete(`/mcp/servers/${id}`) as unknown as Promise<{ ok: boolean }>,
|
||||
|
||||
toggle: (id: string) =>
|
||||
client.put(`/mcp/servers/${id}/toggle`) as unknown as Promise<{ ok: boolean; is_active: boolean }>,
|
||||
|
||||
discoverTools: (id: string) =>
|
||||
client.post(`/mcp/servers/${id}/discover-tools`) as unknown as Promise<{ ok: boolean; discovered: number; total: number; tools: McpToolDef[] }>,
|
||||
|
||||
updateTools: (id: string, tools: McpToolDef[]) =>
|
||||
client.put(`/mcp/servers/${id}/tools`, { tools }) as unknown as Promise<{ ok: boolean }>,
|
||||
|
||||
upload: (id: string, file: File) => {
|
||||
const fd = new FormData();
|
||||
fd.append('file', file);
|
||||
return client.post(`/mcp/servers/${id}/upload`, fd, { headers: { 'Content-Type': 'multipart/form-data' } }) as unknown as Promise<{ ok: boolean; path: string }>;
|
||||
},
|
||||
|
||||
linkAdb: (id: string, adbId: string) =>
|
||||
client.put(`/mcp/servers/${id}/link-adb?adb_id=${adbId}`) as unknown as Promise<{ ok: boolean }>,
|
||||
};
|
||||
39
frontend-react/src/api/endpoints/oci.ts
Normal file
39
frontend-react/src/api/endpoints/oci.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import client from '../client';
|
||||
|
||||
export interface OciConfigFull {
|
||||
id: string;
|
||||
user_id: string;
|
||||
tenancy_name: string;
|
||||
tenancy_ocid: string;
|
||||
user_ocid: string;
|
||||
fingerprint: string;
|
||||
region: string;
|
||||
compartment_id?: string;
|
||||
ssh_public_key?: string;
|
||||
ssh_public_key_preview?: string;
|
||||
auth_type: 'api_key' | 'session_token';
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export interface OciTestResult {
|
||||
status: 'success' | 'error';
|
||||
message: string;
|
||||
data?: unknown;
|
||||
}
|
||||
|
||||
export const ociApi = {
|
||||
list: () =>
|
||||
client.get('/oci/configs') as unknown as Promise<OciConfigFull[]>,
|
||||
|
||||
create: (fd: FormData) =>
|
||||
client.post('/oci/config', fd, { headers: { 'Content-Type': 'multipart/form-data' } }) as unknown as Promise<{ id: string; tenancy_name: string; region: string }>,
|
||||
|
||||
update: (id: string, fd: FormData) =>
|
||||
client.put(`/oci/configs/${id}`, fd, { headers: { 'Content-Type': 'multipart/form-data' } }) as unknown as Promise<{ id: string; tenancy_name: string; region: string }>,
|
||||
|
||||
remove: (id: string) =>
|
||||
client.delete(`/oci/configs/${id}`) as unknown as Promise<{ ok: boolean }>,
|
||||
|
||||
test: (id: string) =>
|
||||
client.post(`/oci/test/${id}`) as unknown as Promise<OciTestResult>,
|
||||
};
|
||||
142
frontend-react/src/api/endpoints/reports.ts
Normal file
142
frontend-react/src/api/endpoints/reports.ts
Normal file
@@ -0,0 +1,142 @@
|
||||
import client from '../client';
|
||||
|
||||
/* ── Types ── */
|
||||
|
||||
export interface Report {
|
||||
id: string;
|
||||
tenancy_name: string;
|
||||
status: string;
|
||||
level: number | null;
|
||||
regions?: string[];
|
||||
obp?: boolean;
|
||||
raw?: boolean;
|
||||
redact_output?: boolean;
|
||||
created_at: string;
|
||||
completed_at: string | null;
|
||||
error_msg?: string | null;
|
||||
}
|
||||
|
||||
export interface RunReportBody {
|
||||
config_id: string;
|
||||
regions?: string[];
|
||||
level: 1 | 2;
|
||||
obp: boolean;
|
||||
raw: boolean;
|
||||
redact_output: boolean;
|
||||
}
|
||||
|
||||
export interface RunReportResult {
|
||||
report_id: string;
|
||||
status: string;
|
||||
}
|
||||
|
||||
export interface ReportProgress {
|
||||
status: string;
|
||||
progress: string[];
|
||||
created_at: string;
|
||||
completed_at: string | null;
|
||||
error_msg: string | null;
|
||||
}
|
||||
|
||||
export interface ReportSummary {
|
||||
tenancy: string;
|
||||
level: number;
|
||||
regions: string[];
|
||||
generated_at: string;
|
||||
total: number;
|
||||
passed: number;
|
||||
failed: number;
|
||||
total_findings: number;
|
||||
total_compliant: number;
|
||||
score: number;
|
||||
sections: Record<string, { passed: number; failed: number; total: number }>;
|
||||
}
|
||||
|
||||
export interface ReportFile {
|
||||
id: string;
|
||||
file_name: string;
|
||||
file_type: string;
|
||||
file_category: string;
|
||||
file_size: number;
|
||||
}
|
||||
|
||||
export interface CisEngineVersion {
|
||||
version: string;
|
||||
updated_date: string;
|
||||
file_path: string;
|
||||
}
|
||||
|
||||
export interface CisEngineUpdate {
|
||||
local_version: string;
|
||||
local_date: string;
|
||||
remote_version: string;
|
||||
remote_date: string;
|
||||
update_available: boolean;
|
||||
}
|
||||
|
||||
/* ── API ── */
|
||||
|
||||
export const reportsApi = {
|
||||
run: (body: RunReportBody) =>
|
||||
client.post('/reports/run', body) as unknown as Promise<RunReportResult>,
|
||||
|
||||
list: () =>
|
||||
client.get('/reports') as unknown as Promise<Report[]>,
|
||||
|
||||
progress: (rid: string) =>
|
||||
client.get(`/reports/${rid}/progress`) as unknown as Promise<ReportProgress>,
|
||||
|
||||
cancel: (rid: string) =>
|
||||
client.post(`/reports/${rid}/cancel`) as unknown as Promise<{ ok: boolean }>,
|
||||
|
||||
delete: (rid: string) =>
|
||||
client.delete(`/reports/${rid}`) as unknown as Promise<{ ok: boolean }>,
|
||||
|
||||
summary: (rid: string) =>
|
||||
client.get(`/reports/${rid}/summary`) as unknown as Promise<ReportSummary>,
|
||||
|
||||
htmlUrl: (rid: string) => {
|
||||
const token = localStorage.getItem('t') || '';
|
||||
return `/api/reports/${rid}/html?token=${encodeURIComponent(token)}`;
|
||||
},
|
||||
|
||||
complianceReportUrl: (rid: string) => {
|
||||
const token = localStorage.getItem('t') || '';
|
||||
return `/api/reports/${rid}/compliance-report?token=${encodeURIComponent(token)}`;
|
||||
},
|
||||
|
||||
generateComplianceReport: (rid: string) =>
|
||||
client.post(`/reports/${rid}/compliance-report/generate`) as unknown as Promise<{ status: string; has_rag: boolean }>,
|
||||
|
||||
complianceReportStatus: (rid: string) =>
|
||||
client.get(`/reports/${rid}/compliance-report/status`) as unknown as Promise<{ ready: boolean }>,
|
||||
|
||||
files: (rid: string) =>
|
||||
client.get(`/reports/${rid}/files`) as unknown as Promise<ReportFile[]>,
|
||||
|
||||
downloadUrl: (rid: string, fid: string) => {
|
||||
const token = localStorage.getItem('t') || '';
|
||||
return `/api/reports/${rid}/files/${fid}/download?token=${encodeURIComponent(token)}`;
|
||||
},
|
||||
|
||||
engineVersion: () =>
|
||||
client.get('/cis-engine/version') as unknown as Promise<CisEngineVersion>,
|
||||
|
||||
engineCheckUpdate: () =>
|
||||
client.get('/cis-engine/check-update') as unknown as Promise<CisEngineUpdate>,
|
||||
|
||||
engineUpdate: () =>
|
||||
client.post('/cis-engine/update') as unknown as Promise<{ ok: boolean }>,
|
||||
|
||||
embedReport: (rid: string, adbConfigId: string, tableName?: string) =>
|
||||
client.post(`/embeddings/report/${rid}`, {
|
||||
adb_config_id: adbConfigId,
|
||||
...(tableName ? { table_name: tableName } : {}),
|
||||
}) as unknown as Promise<{ ok: boolean; message: string; sections: number; tenancy: string }>,
|
||||
|
||||
embedFile: (rid: string, fid: string, adbConfigId: string, tableName?: string) =>
|
||||
client.post(`/embeddings/report/${rid}/file/${fid}`, {
|
||||
adb_config_id: adbConfigId,
|
||||
...(tableName ? { table_name: tableName } : {}),
|
||||
}) as unknown as Promise<{ ok: boolean; message: string; chunks: number }>,
|
||||
};
|
||||
136
frontend-react/src/api/endpoints/terraform.ts
Normal file
136
frontend-react/src/api/endpoints/terraform.ts
Normal file
@@ -0,0 +1,136 @@
|
||||
import client from '../client';
|
||||
|
||||
/* ── Types ── */
|
||||
|
||||
export type WsStatus =
|
||||
| 'draft' | 'planning' | 'planned'
|
||||
| 'applying' | 'applied'
|
||||
| 'destroying' | 'destroyed'
|
||||
| 'failed';
|
||||
|
||||
export interface Workspace {
|
||||
id: string;
|
||||
name: string;
|
||||
session_id: string;
|
||||
session_title?: string;
|
||||
oci_config_id: string;
|
||||
compartment_id?: string;
|
||||
status: WsStatus;
|
||||
plan_output?: string;
|
||||
apply_output?: string;
|
||||
destroy_output?: string;
|
||||
error?: string;
|
||||
tf_code?: string;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
export interface WsStatusResult {
|
||||
status: WsStatus;
|
||||
plan_output?: string;
|
||||
apply_output?: string;
|
||||
destroy_output?: string;
|
||||
error?: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
export interface ChatSession {
|
||||
id: string;
|
||||
title: string;
|
||||
agent_type: string;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
export interface ChatMessage {
|
||||
id: string;
|
||||
role: 'user' | 'assistant';
|
||||
content: string;
|
||||
created_at?: string;
|
||||
}
|
||||
|
||||
export interface PromptResponse {
|
||||
message_id: string;
|
||||
session_id: string;
|
||||
status: 'processing' | 'done';
|
||||
prompt?: string;
|
||||
}
|
||||
|
||||
export interface MessageStatus {
|
||||
status: 'processing' | 'done' | 'failed';
|
||||
content: string;
|
||||
}
|
||||
|
||||
/* ── Workspace API ── */
|
||||
|
||||
export const terraformApi = {
|
||||
/* Workspaces */
|
||||
listWorkspaces: () =>
|
||||
client.get('/terraform/workspaces') as unknown as Promise<Workspace[]>,
|
||||
|
||||
getWorkspace: (wid: string) =>
|
||||
client.get(`/terraform/workspaces/${wid}`) as unknown as Promise<Workspace>,
|
||||
|
||||
deleteWorkspace: (wid: string) =>
|
||||
client.delete(`/terraform/workspaces/${wid}`) as unknown as Promise<{ ok: boolean }>,
|
||||
|
||||
plan: (wid: string) =>
|
||||
client.post(`/terraform/workspaces/${wid}/plan`) as unknown as Promise<{ status: string }>,
|
||||
|
||||
apply: (wid: string) =>
|
||||
client.post(`/terraform/workspaces/${wid}/apply`) as unknown as Promise<{ status: string }>,
|
||||
|
||||
destroy: (wid: string) =>
|
||||
client.post(`/terraform/workspaces/${wid}/destroy`, { confirm: 'DESTROY' }) as unknown as Promise<{ status: string }>,
|
||||
|
||||
cancel: (wid: string) =>
|
||||
client.post(`/terraform/workspaces/${wid}/cancel`) as unknown as Promise<{ ok: boolean }>,
|
||||
|
||||
status: (wid: string) =>
|
||||
client.get(`/terraform/workspaces/${wid}/status`) as unknown as Promise<WsStatusResult>,
|
||||
|
||||
downloadUrl: (wid: string) => {
|
||||
const token = localStorage.getItem('t') || '';
|
||||
return `/api/terraform/workspaces/${wid}/download?token=${encodeURIComponent(token)}`;
|
||||
},
|
||||
|
||||
/* Prompt Generator */
|
||||
generatePrompt: (body: {
|
||||
message: string;
|
||||
session_id?: string;
|
||||
genai_config: Record<string, string | undefined>;
|
||||
history?: { role: string; content: string }[] | null;
|
||||
}) =>
|
||||
client.post('/terraform/generate-prompt', body) as unknown as Promise<PromptResponse>,
|
||||
|
||||
/* Chat sessions / history */
|
||||
listSessions: (agentType: string, limit = 50) =>
|
||||
client.get(`/chat/sessions?agent_type=${agentType}&limit=${limit}`) as unknown as Promise<ChatSession[]>,
|
||||
|
||||
loadSession: (sid: string) =>
|
||||
client.get(`/chat/sessions/${sid}/messages`) as unknown as Promise<{ messages: ChatMessage[] }>,
|
||||
|
||||
deleteSession: (sid: string) =>
|
||||
client.delete(`/chat/sessions/${sid}`) as unknown as Promise<{ ok: boolean }>,
|
||||
|
||||
pollMessageStatus: (mid: string) =>
|
||||
client.get(`/chat/${mid}/status`) as unknown as Promise<MessageStatus>,
|
||||
|
||||
createWorkspace: (body: { session_id?: string; oci_config_id: string; compartment_id?: string; name: string; tf_code: string }) =>
|
||||
client.post('/terraform/workspaces', body) as unknown as Promise<Workspace>,
|
||||
|
||||
updateCode: (wid: string, tf_code: string) =>
|
||||
client.put(`/terraform/workspaces/${wid}/code`, { tf_code }) as unknown as Promise<{ ok: boolean }>,
|
||||
|
||||
chat: (body: { message: string; session_id?: string; genai_config_id?: string; model_id?: string; oci_config_id?: string; genai_region?: string; compartment_id?: string; temperature?: number; max_tokens?: number; use_tools?: boolean }) =>
|
||||
client.post('/terraform/chat', body) as unknown as Promise<{ message_id: string; session_id: string; status: string }>,
|
||||
|
||||
loadResources: (ociConfigId: string, compartmentId: string, region?: string) => {
|
||||
const params = new URLSearchParams({ oci_config_id: ociConfigId, compartment_id: compartmentId });
|
||||
if (region) params.set('region', region);
|
||||
return client.get(`/terraform/resources?${params}`) as unknown as Promise<Record<string, any[]>>;
|
||||
},
|
||||
|
||||
getCompartmentTree: (cid: string) =>
|
||||
client.get(`/oci/explore/${cid}/compartment-tree`) as unknown as Promise<any[]>,
|
||||
};
|
||||
Reference in New Issue
Block a user