125 lines
5.6 KiB
TypeScript
125 lines
5.6 KiB
TypeScript
import { auth } from "@/lib/firebase";
|
|
|
|
const BASE = process.env.NEXT_PUBLIC_C2_URL ?? "http://localhost:8000";
|
|
|
|
async function request<T>(path: string, options?: RequestInit): Promise<T> {
|
|
const user = auth.currentUser;
|
|
const token = user ? await user.getIdToken() : null;
|
|
|
|
const res = await fetch(`${BASE}${path}`, {
|
|
...options,
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
...(token ? { Authorization: `Bearer ${token}` } : {}),
|
|
...(options?.headers as Record<string, string> | undefined),
|
|
},
|
|
});
|
|
if (!res.ok) throw new Error(`C2 API error ${res.status}: ${await res.text()}`);
|
|
if (res.status === 204) return undefined as T;
|
|
return res.json();
|
|
}
|
|
|
|
export const c2api = {
|
|
// Nodes
|
|
getNodes: () => request<unknown[]>("/nodes"),
|
|
getNode: (id: string) => request<unknown>(`/nodes/${id}`),
|
|
sendCommand: (nodeId: string, payload: object) =>
|
|
request(`/nodes/${nodeId}/command`, { method: "POST", body: JSON.stringify(payload) }),
|
|
assignSystem: (nodeId: string, systemId: string) =>
|
|
request(`/nodes/${nodeId}/config/${systemId}`, { method: "POST" }),
|
|
|
|
// Systems
|
|
getSystems: () => request<unknown[]>("/systems"),
|
|
createSystem: (body: object) =>
|
|
request("/systems", { method: "POST", body: JSON.stringify(body) }),
|
|
updateSystem: (id: string, body: object) =>
|
|
request(`/systems/${id}`, { method: "PUT", body: JSON.stringify(body) }),
|
|
deleteSystem: (id: string) =>
|
|
request(`/systems/${id}`, { method: "DELETE" }),
|
|
|
|
// Tokens
|
|
getTokens: () => request<unknown[]>("/tokens"),
|
|
addToken: (body: { name: string; token: string }) =>
|
|
request("/tokens", { method: "POST", body: JSON.stringify(body) }),
|
|
deleteToken: (id: string) =>
|
|
request(`/tokens/${id}`, { method: "DELETE" }),
|
|
|
|
// Node approval
|
|
approveNode: (id: string) =>
|
|
request(`/nodes/${id}/approve`, { method: "POST" }),
|
|
rejectNode: (id: string) =>
|
|
request(`/nodes/${id}/reject`, { method: "POST" }),
|
|
|
|
// Calls
|
|
getCalls: (params?: Record<string, string>) => {
|
|
const qs = params ? "?" + new URLSearchParams(params).toString() : "";
|
|
return request<unknown[]>(`/calls${qs}`);
|
|
},
|
|
patchTranscript: (callId: string, transcript: string) =>
|
|
request(`/calls/${callId}/transcript`, { method: "PATCH", body: JSON.stringify({ transcript }) }),
|
|
|
|
// Incidents
|
|
getIncidents: (params?: { status?: string; type?: string }) => {
|
|
const qs = params ? "?" + new URLSearchParams(params as Record<string, string>).toString() : "";
|
|
return request<unknown[]>(`/incidents${qs}`);
|
|
},
|
|
getIncident: (id: string) => request<unknown>(`/incidents/${id}`),
|
|
createIncident: (body: object) =>
|
|
request("/incidents", { method: "POST", body: JSON.stringify(body) }),
|
|
updateIncident: (id: string, body: object) =>
|
|
request(`/incidents/${id}`, { method: "PUT", body: JSON.stringify(body) }),
|
|
deleteIncident: (id: string) =>
|
|
request(`/incidents/${id}`, { method: "DELETE" }),
|
|
linkCallToIncident: (incidentId: string, callId: string) =>
|
|
request(`/incidents/${incidentId}/calls/${callId}`, { method: "POST" }),
|
|
summarizeIncident: (id: string) =>
|
|
request(`/incidents/${id}/summarize`, { method: "POST" }),
|
|
|
|
// Alerts
|
|
getAlerts: (acknowledged?: boolean) => {
|
|
const qs = acknowledged !== undefined ? `?acknowledged=${acknowledged}` : "";
|
|
return request<unknown[]>(`/alerts${qs}`);
|
|
},
|
|
acknowledgeAlert: (id: string) =>
|
|
request(`/alerts/${id}/acknowledge`, { method: "POST" }),
|
|
getAlertRules: () => request<unknown[]>("/alert-rules"),
|
|
createAlertRule: (body: object) =>
|
|
request("/alert-rules", { method: "POST", body: JSON.stringify(body) }),
|
|
updateAlertRule: (id: string, body: object) =>
|
|
request(`/alert-rules/${id}`, { method: "PUT", body: JSON.stringify(body) }),
|
|
deleteAlertRule: (id: string) =>
|
|
request(`/alert-rules/${id}`, { method: "DELETE" }),
|
|
|
|
// Node key management
|
|
reissueNodeKey: (nodeId: string) =>
|
|
request(`/nodes/${nodeId}/reissue-key`, { method: "POST" }),
|
|
|
|
// Ten-codes
|
|
getTenCodes: (systemId: string) =>
|
|
request<{ ten_codes: Record<string, string> }>(`/systems/${systemId}/ten-codes`),
|
|
updateTenCodes: (systemId: string, ten_codes: Record<string, string>) =>
|
|
request(`/systems/${systemId}/ten-codes`, { method: "PUT", body: JSON.stringify({ ten_codes }) }),
|
|
|
|
// Vocabulary
|
|
getVocabulary: (systemId: string) =>
|
|
request<{ vocabulary: string[]; vocabulary_pending: { term: string; source: "induction" | "correction"; added_at: string }[]; vocabulary_bootstrapped: boolean }>(
|
|
`/systems/${systemId}/vocabulary`
|
|
),
|
|
bootstrapVocabulary: (systemId: string) =>
|
|
request<{ added: number; terms: string[] }>(`/systems/${systemId}/vocabulary/bootstrap`, { method: "POST" }),
|
|
addVocabularyTerm: (systemId: string, term: string) =>
|
|
request(`/systems/${systemId}/vocabulary/terms`, { method: "POST", body: JSON.stringify({ term }) }),
|
|
removeVocabularyTerm: (systemId: string, term: string) =>
|
|
request(`/systems/${systemId}/vocabulary/terms`, { method: "DELETE", body: JSON.stringify({ term }) }),
|
|
approvePendingTerm: (systemId: string, term: string) =>
|
|
request(`/systems/${systemId}/vocabulary/pending/approve`, { method: "POST", body: JSON.stringify({ term }) }),
|
|
dismissPendingTerm: (systemId: string, term: string) =>
|
|
request(`/systems/${systemId}/vocabulary/pending/dismiss`, { method: "POST", body: JSON.stringify({ term }) }),
|
|
|
|
// Feature flags (admin)
|
|
getFeatureFlags: () =>
|
|
request<Record<string, boolean>>("/admin/features"),
|
|
setFeatureFlags: (flags: Record<string, boolean>) =>
|
|
request<Record<string, boolean>>("/admin/features", { method: "PUT", body: JSON.stringify(flags) }),
|
|
};
|