diff --git a/features/conversation-assistant/frontend/src/api/client.ts b/features/conversation-assistant/frontend/src/api/client.ts index 340ce7224..3d77a4d9c 100644 --- a/features/conversation-assistant/frontend/src/api/client.ts +++ b/features/conversation-assistant/frontend/src/api/client.ts @@ -1,35 +1,80 @@ const API_BASE = '/api'; +const TOKEN_KEY = 'conversation-assistant-token'; +const DEVICE_ID_KEY = 'conversation-assistant-device-id'; + +export function getStoredToken(): string | null { + return localStorage.getItem(TOKEN_KEY); +} + +export function setStoredToken(token: string): void { + localStorage.setItem(TOKEN_KEY, token); +} + +export function getStoredDeviceId(): string | null { + return localStorage.getItem(DEVICE_ID_KEY); +} + +export function setStoredDeviceId(deviceId: string): void { + localStorage.setItem(DEVICE_ID_KEY, deviceId); +} + +export function clearAuth(): void { + localStorage.removeItem(TOKEN_KEY); + localStorage.removeItem(DEVICE_ID_KEY); +} async function request( endpoint: string, - options: RequestInit = {} + options: RequestInit = {}, + skipAuth = false ): Promise { + const token = getStoredToken(); + const headers: Record = { + 'Content-Type': 'application/json', + ...(options.headers as Record), + }; + + if (token && !skipAuth) { + headers['Authorization'] = `Bearer ${token}`; + } + const response = await fetch(`${API_BASE}${endpoint}`, { ...options, - headers: { - 'Content-Type': 'application/json', - ...options.headers, - }, + headers, }); const data = await response.json(); if (!response.ok || !data.success) { - throw new Error(data.error?.message || 'Request failed'); + const error = new Error(data.error?.message || data.message || 'Request failed'); + (error as Error & { status?: number }).status = response.status; + throw error; } return data.data; } export const api = { - get: (endpoint: string) => request(endpoint), + get: (endpoint: string, skipAuth = false) => request(endpoint, {}, skipAuth), - post: (endpoint: string, body?: unknown) => + post: (endpoint: string, body?: unknown, skipAuth = false) => request(endpoint, { method: 'POST', body: body ? JSON.stringify(body) : undefined, - }), + }, skipAuth), - delete: (endpoint: string) => - request(endpoint, { method: 'DELETE' }), + delete: (endpoint: string, skipAuth = false) => + request(endpoint, { method: 'DELETE' }, skipAuth), +}; + +// Auth-specific API calls (no auth required) +export const authApi = { + register: (data: { name: string; hardwareId: string; platform: string; osVersion: string }) => + api.post<{ deviceId: string; code: string; expiresAt: string }>('/devices/register', data, true), + + verify: (deviceId: string, code: string) => + api.post<{ token: string }>('/devices/verify', { deviceId, code }, true), + + checkStatus: (deviceId: string) => + api.get<{ isActive: boolean; token?: string }>(`/devices/${deviceId}/status`, true), };