summaryrefslogtreecommitdiff
path: root/frontend/src/lib/client.ts
blob: 49379020d08ef84d4706b2c4f65d2644e61bda76 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
// Central typed binding layer for the 策研 Strategy Lab HTTP contract.
import { apiFetch } from './api';
import type {
  AIAssist,
  AIUsage,
  AdminUserUpdate,
  BacktestResult,
  Capabilities,
  Dataset,
  DatasetPreview,
  DatasetRequest,
  InstrumentSearchResponse,
  Invitation,
  Project,
  Run,
  SessionInfo,
  User,
  Version
} from './types';

export const getHealth = () =>
  apiFetch<{ status: string; version: string; worker_available: boolean; ai_configured: boolean }>('/api/health');

// ---- auth ----
export const postLogin = (email: string, password: string) =>
  apiFetch<{ user: User }>('/api/auth/login', { method: 'POST', body: { email, password } });
export const postLogout = () => apiFetch<void>('/api/auth/logout', { method: 'POST', body: {} });
export const getMe = () => apiFetch<{ user: User }>('/api/auth/me');
export const postRegister = (invite_token: string, name: string, email: string, password: string) =>
  apiFetch<{ user: User }>('/api/auth/register', { method: 'POST', body: { invite_token, name, email, password } });
export const patchProfile = (name: string) => apiFetch<{ user: User }>('/api/auth/profile', { method: 'PATCH', body: { name } });
export const postPassword = (current_password: string, new_password: string) =>
  apiFetch<void>('/api/auth/password', { method: 'POST', body: { current_password, new_password } });
export const getSessions = () => apiFetch<{ items: SessionInfo[] }>('/api/auth/sessions');
export const deleteSession = (id: string) => apiFetch<void>(`/api/auth/sessions/${id}`, { method: 'DELETE', body: {} });
export const postResetPassword = (token: string, new_password: string) =>
  apiFetch<{ user?: User }>('/api/auth/reset-password', { method: 'POST', body: { token, new_password } });

// ---- capabilities ----
export const getCapabilities = () => apiFetch<Capabilities>('/api/capabilities');

// ---- instruments ----
export const searchInstruments = (q: string, signal?: AbortSignal) =>
  apiFetch<InstrumentSearchResponse>(`/api/instruments?q=${encodeURIComponent(q)}`, { signal });

// ---- projects ----
export const listProjects = () => apiFetch<{ items: Project[] }>('/api/projects');
export const createProject = (name: string, description?: string) =>
  apiFetch<Project>('/api/projects', { method: 'POST', body: { name, description } });
export const getProject = (id: string) => apiFetch<Project>(`/api/projects/${id}`);
export const patchProject = (id: string, body: { name?: string; description?: string }) =>
  apiFetch<Project>(`/api/projects/${id}`, { method: 'PATCH', body });
export const putDraft = (id: string, code: string, expected_generation: number) =>
  apiFetch<Project>(`/api/projects/${id}/draft`, { method: 'PUT', body: { code, expected_generation } });
export const listVersions = (id: string) => apiFetch<{ items: Version[] }>(`/api/projects/${id}/versions`);
export const postVersion = (id: string, message: string) =>
  apiFetch<Version>(`/api/projects/${id}/versions`, { method: 'POST', body: { message } });
export const getVersionCode = (id: string, versionId: string) => apiFetch<{ code: string }>(`/api/projects/${id}/versions/${versionId}`);
export const postRestore = (id: string, version_id: string, expected_generation: number) =>
  apiFetch<Project>(`/api/projects/${id}/restore`, { method: 'POST', body: { version_id, expected_generation } });

// ---- datasets ----
export const listDatasets = () => apiFetch<{ items: Dataset[] }>('/api/datasets');
export const createDataset = (body: DatasetRequest) => apiFetch<Dataset>('/api/datasets', { method: 'POST', body });
export const getDataset = (id: string) => apiFetch<Dataset>(`/api/datasets/${id}`);
export const getDatasetPreview = (id: string) => apiFetch<DatasetPreview>(`/api/datasets/${id}/preview`);

// ---- runs ----
export const listRuns = (project_id?: string) =>
  apiFetch<{ items: Run[] }>(project_id ? `/api/runs?project_id=${encodeURIComponent(project_id)}` : '/api/runs');
export interface RunCreateBody {
  project_id: string;
  dataset_id: string;
  capital: number;
  commission: number;
  slippage: number;
  benchmark_symbol?: string | null;
  parameters?: Record<string, unknown>;
  acknowledge_warnings?: boolean;
}
export const createRun = (body: RunCreateBody) => apiFetch<Run>('/api/runs', { method: 'POST', body });
export const getRun = (id: string) => apiFetch<Run>(`/api/runs/${id}`);
export const cancelRun = (id: string) => apiFetch<Run>(`/api/runs/${id}/cancel`, { method: 'POST', body: {} });
export const rerunRun = (id: string, use_original_data = true) =>
  apiFetch<Run>(`/api/runs/${id}/rerun`, { method: 'POST', body: { use_original_data } });

// ---- AI ----
export const postAIAssist = (project_id: string, instruction: string, expected_generation: number) =>
  apiFetch<AIAssist>('/api/ai/assist', { method: 'POST', body: { project_id, instruction, expected_generation } });
export const postAIAccept = (aiId: string, expected_generation: number) =>
  apiFetch<Project>(`/api/ai/${aiId}/accept`, { method: 'POST', body: { expected_generation } });
export const getAIUsage = () => apiFetch<AIUsage>('/api/ai/usage');

// ---- admin ----
export const listAdminUsers = () => apiFetch<{ items: User[] }>('/api/admin/users');
export const patchAdminUser = (id: string, body: AdminUserUpdate) =>
  apiFetch<User>(`/api/admin/users/${id}`, { method: 'PATCH', body });
export const postAdminUserPassword = (id: string) =>
  apiFetch<{ reset_token: string; expires_at: string }>(`/api/admin/users/${id}/reset-password`, { method: 'POST', body: {} });
export const createInvitation = (body: { email?: string; role?: string; expires_hours?: number }) =>
  apiFetch<{ token: string; expires_at: string }>('/api/admin/invitations', { method: 'POST', body });
export const listInvitations = () => apiFetch<{ items: Invitation[] }>('/api/admin/invitations');
export const deleteInvitation = (id: string) => apiFetch<void>(`/api/admin/invitations/${id}`, { method: 'DELETE', body: {} });
export const getAdminAudit = () =>
  apiFetch<{ items: { actor?: string | null; action: string; target?: string | null; status: string; time: string; detail?: string | null }[] }>(
    '/api/admin/audit'
  );

// helpers
export async function poll<T>(fn: () => Promise<T>, until: (v: T) => boolean, intervalMs = 1500): Promise<T> {
  for (;;) {
    const v = await fn();
    if (until(v)) return v;
    await new Promise((r) => setTimeout(r, intervalMs));
  }
}

export type { BacktestResult };