feat(dashboard): add performance and operations slices

This commit is contained in:
jjbrunton committed 2026-08-08 15:10:52 +01:00
1 parent 20fa83f245
commit 5917b63755
13 files changed
+1059

No files matched your search

@@ -0,0 +1,54 @@
import { z } from 'zod';
import {
ActivityResponseSchema,
ChatSyncStatusSchema,
} from '../../../../../packages/api-contracts/src/index.js';
export const RawLogEntrySchema = z.strictObject({
timestamp: z.number(),
level: z.union([z.literal('debug'), z.literal('info'), z.literal('warn'), z.literal('error')]),
message: z.string(),
data: z.unknown().optional(),
});
export const RawLogsResponseSchema = z.array(RawLogEntrySchema);
export type RawLogEntry = z.infer<typeof RawLogEntrySchema>;
async function parseResponse<T>(
response: Response,
schema: { parse(value: unknown): T },
): Promise<T> {
const payload: unknown = await response.json();
if (!response.ok) {
const message =
typeof payload === 'object' &&
payload !== null &&
'error' in payload &&
typeof payload.error === 'string'
? payload.error
: `Request failed (${response.status})`;
throw new Error(message);
}
return schema.parse(payload);
}
export function createOperationsApi(fetchImplementation?: typeof fetch) {
function request(path: string): Promise<Response> {
return (fetchImplementation ?? globalThis.fetch)(path);
}
return {
async getActivity() {
return parseResponse(await request('/api/activity'), ActivityResponseSchema);
},
async getLogs(limit = 200) {
return parseResponse(
await request(`/api/logs?limit=${limit}`),
RawLogsResponseSchema,
);
},
async getSyncStatus() {
return parseResponse(await request('/api/chats/sync'), ChatSyncStatusSchema);
},
};
}
export const operationsApi = createOperationsApi();