117 lines
3.9 KiB
TypeScript
117 lines
3.9 KiB
TypeScript
/**
|
|
* ChronoMind backend client — typed HTTP wrappers for the chronomind-backend (port 4011).
|
|
*
|
|
* Auth: Bearer token from the caller's JWT (same JWT_SECRET as platform-service).
|
|
*/
|
|
|
|
import { config } from './config.js';
|
|
|
|
export interface ChronoMindClientOptions {
|
|
token?: string;
|
|
requestId?: string;
|
|
}
|
|
|
|
async function chronomindFetch<T>(
|
|
path: string,
|
|
init: RequestInit,
|
|
opts: ChronoMindClientOptions
|
|
): Promise<T> {
|
|
const headers: Record<string, string> = {
|
|
'Content-Type': 'application/json',
|
|
...(opts.token ? { Authorization: `Bearer ${opts.token}` } : {}),
|
|
...(opts.requestId ? { 'x-request-id': opts.requestId } : {}),
|
|
};
|
|
const res = await fetch(`${config.CHRONOMIND_BACKEND_URL}${path}`, {
|
|
...init,
|
|
headers: { ...((init.headers as Record<string, string>) ?? {}), ...headers },
|
|
signal: AbortSignal.timeout(15_000),
|
|
});
|
|
if (!res.ok) {
|
|
const body = await res.text().catch(() => '');
|
|
throw new Error(`chronomind-backend ${init.method ?? 'GET'} ${path} → ${res.status}: ${body}`);
|
|
}
|
|
return res.json() as Promise<T>;
|
|
}
|
|
|
|
// ── Timers ─────────────────────────────────────────────────────────────────
|
|
|
|
export interface TimerDoc {
|
|
id: string;
|
|
userId: string;
|
|
productId: string;
|
|
label: string;
|
|
description?: string;
|
|
type: 'alarm' | 'countdown' | 'pomodoro' | 'event';
|
|
state: 'idle' | 'active' | 'paused' | 'warning' | 'fired' | 'completed';
|
|
urgency: 'critical' | 'important' | 'standard' | 'gentle' | 'passive';
|
|
duration?: number;
|
|
targetTime?: string;
|
|
category?: string;
|
|
createdAt: string;
|
|
lastSyncedAt: string;
|
|
syncVersion: number;
|
|
}
|
|
|
|
export function chronomindTimersList(
|
|
params: { limit?: number; offset?: number; type?: string; state?: string },
|
|
opts: ChronoMindClientOptions
|
|
): Promise<{ items: TimerDoc[]; total: number }> {
|
|
const qs = new URLSearchParams();
|
|
if (params.limit !== undefined) qs.set('limit', String(params.limit));
|
|
if (params.offset !== undefined) qs.set('offset', String(params.offset));
|
|
if (params.type) qs.set('type', params.type);
|
|
if (params.state) qs.set('state', params.state);
|
|
const q = qs.toString();
|
|
return chronomindFetch(`/timers${q ? `?${q}` : ''}`, { method: 'GET' }, opts);
|
|
}
|
|
|
|
export function chronomindSyncStatus(opts: ChronoMindClientOptions): Promise<{
|
|
userId: string;
|
|
productId: string;
|
|
totalTimers: number;
|
|
active: number;
|
|
pending: number;
|
|
unsyncedCount: number;
|
|
lastSyncedAt: string | null;
|
|
generatedAt: string;
|
|
}> {
|
|
return chronomindFetch('/timers/sync-status', { method: 'GET' }, opts);
|
|
}
|
|
|
|
// ── Routines ───────────────────────────────────────────────────────────────
|
|
|
|
export interface RoutineDoc {
|
|
id: string;
|
|
userId: string;
|
|
productId: string;
|
|
name: string;
|
|
description?: string;
|
|
steps: unknown[];
|
|
totalDurationMinutes: number;
|
|
status: 'idle' | 'running' | 'paused' | 'completed';
|
|
isTemplate: boolean;
|
|
category?: string;
|
|
createdAt: string;
|
|
lastSyncedAt: string;
|
|
syncVersion: number;
|
|
}
|
|
|
|
export function chronomindRoutinesList(
|
|
params: { limit?: number; offset?: number; isTemplate?: boolean },
|
|
opts: ChronoMindClientOptions
|
|
): Promise<{ items: RoutineDoc[]; total: number }> {
|
|
const qs = new URLSearchParams();
|
|
if (params.limit !== undefined) qs.set('limit', String(params.limit));
|
|
if (params.offset !== undefined) qs.set('offset', String(params.offset));
|
|
if (params.isTemplate !== undefined) qs.set('isTemplate', String(params.isTemplate));
|
|
const q = qs.toString();
|
|
return chronomindFetch(`/routines${q ? `?${q}` : ''}`, { method: 'GET' }, opts);
|
|
}
|
|
|
|
export function chronomindTimerDelete(
|
|
timerId: string,
|
|
opts: ChronoMindClientOptions
|
|
): Promise<{ success: boolean }> {
|
|
return chronomindFetch(`/timers/${timerId}`, { method: 'DELETE' }, opts);
|
|
}
|