Files
cdp/ingestion/console/src/api/client.ts
2026-05-24 22:59:24 +07:00

91 lines
2.6 KiB
TypeScript

// Thin fetch wrapper. Throws on non-2xx with a structured ApiError.
export class ApiError extends Error {
status: number;
field?: string;
constructor(status: number, message: string, field?: string) {
super(message);
this.status = status;
this.field = field;
}
}
const INGEST_BASE = import.meta.env.VITE_API_BASE_URL ?? '/api/ingest';
const ROTOR_BASE = import.meta.env.VITE_ROTOR_BASE_URL ?? '/api/rotor';
const BULKER_BASE = import.meta.env.VITE_BULKER_BASE_URL ?? '/api/bulker';
async function request<T>(base: string, path: string, init?: RequestInit): Promise<T> {
const res = await fetch(`${base}${path}`, {
...init,
headers: {
'content-type': 'application/json',
...(init?.headers ?? {}),
},
});
const text = await res.text();
const data = text ? safeJSON(text) : undefined;
if (!res.ok) {
const msg = (data as { error?: string })?.error ?? res.statusText;
const field = (data as { field?: string })?.field;
throw new ApiError(res.status, msg, field);
}
return data as T;
}
function safeJSON(text: string): unknown {
try {
return JSON.parse(text);
} catch {
return text;
}
}
// ---------------------------------------------------------------------------
// Ingest API
// ---------------------------------------------------------------------------
export const ingest = {
health: () => request<{ status: string }>(INGEST_BASE, '/health'),
ready: () => request<{ status: string }>(INGEST_BASE, '/ready'),
track: (writeKey: string, body: Record<string, unknown>) =>
request<{ ok: boolean }>(INGEST_BASE, '/track', {
method: 'POST',
headers: { Authorization: `Bearer ${writeKey}` },
body: JSON.stringify(body),
}),
};
// ---------------------------------------------------------------------------
// Rotor API
// ---------------------------------------------------------------------------
export interface RunRequest {
code: string;
event: Record<string, unknown>;
}
export interface RunResponse {
result: unknown;
}
export const rotor = {
run: (body: RunRequest) =>
request<RunResponse>(ROTOR_BASE, '/v1/run', {
method: 'POST',
body: JSON.stringify(body),
}),
upsert: (body: { workspace_id: string; slug: string; code: string }) =>
request<{ ok: boolean }>(ROTOR_BASE, '/v1/functions', {
method: 'POST',
body: JSON.stringify(body),
}),
};
// ---------------------------------------------------------------------------
// Bulker API
// ---------------------------------------------------------------------------
export const bulker = {
health: () => request<{ status: string }>(BULKER_BASE, '/health'),
};