60 lines
1.6 KiB
TypeScript
60 lines
1.6 KiB
TypeScript
import type { ZodType } from 'zod';
|
|
import {
|
|
botStatsSchema,
|
|
controlResponseSchema,
|
|
timersSchema,
|
|
type BotStats,
|
|
type ControlResponse,
|
|
type Timers,
|
|
} from './contracts.js';
|
|
import { createDashboardRequest } from './request.js';
|
|
|
|
export class ApiError extends Error {
|
|
constructor(
|
|
message: string,
|
|
readonly status: number,
|
|
) {
|
|
super(message);
|
|
this.name = 'ApiError';
|
|
}
|
|
}
|
|
|
|
interface ErrorPayload {
|
|
error?: unknown;
|
|
}
|
|
|
|
async function parseResponse<T>(response: Response, schema: ZodType<T>): Promise<T> {
|
|
const payload: unknown = await response.json();
|
|
if (!response.ok) {
|
|
const errorPayload = payload as ErrorPayload;
|
|
const message = typeof errorPayload.error === 'string' ? errorPayload.error : `Request failed (${response.status})`;
|
|
throw new ApiError(message, response.status);
|
|
}
|
|
return schema.parse(payload);
|
|
}
|
|
|
|
export interface DashboardApi {
|
|
getStats(): Promise<BotStats>;
|
|
getTimers(): Promise<Timers>;
|
|
setRunning(isRunning: boolean): Promise<ControlResponse>;
|
|
}
|
|
|
|
export function createDashboardApi(fetchImplementation?: typeof fetch): DashboardApi {
|
|
const request = createDashboardRequest(fetchImplementation);
|
|
|
|
return {
|
|
async getStats() {
|
|
return parseResponse(await request('/api/stats'), botStatsSchema);
|
|
},
|
|
async getTimers() {
|
|
return parseResponse(await request('/api/timers'), timersSchema);
|
|
},
|
|
async setRunning(isRunning) {
|
|
const endpoint = isRunning ? '/api/start' : '/api/stop';
|
|
return parseResponse(await request(endpoint, { method: 'POST' }), controlResponseSchema);
|
|
},
|
|
};
|
|
}
|
|
|
|
export const dashboardApi = createDashboardApi();
|