const API_BASE = '/api/v1'; interface ApiResponse { data: T | null; error: { code: string; message: string } | null; } class ApiClient { private apiKey: string | null = null; setApiKey(key: string) { this.apiKey = key; localStorage.setItem('apiKey', key); } getApiKey(): string | null { if (this.apiKey) return this.apiKey; this.apiKey = localStorage.getItem('apiKey'); return this.apiKey; } clearApiKey() { this.apiKey = null; localStorage.removeItem('apiKey'); } private async request( method: string, path: string, body?: unknown ): Promise> { const headers: Record = { 'Content-Type': 'application/json', }; if (this.getApiKey()) { headers['Authorization'] = `Bearer ${this.getApiKey()}`; } const response = await fetch(`${API_BASE}${path}`, { method, headers, body: body ? JSON.stringify(body) : undefined, }); return response.json() as Promise>; } async register(email: string, password: string) { return this.request<{ user: { id: string; email: string } }>('POST', '/auth/register', { email, password }); } async login(email: string, password: string) { return this.request<{ token: string; userId: string }>('POST', '/auth/login', { email, password }); } async createApiKey(name: string, token: string) { const headers: Record = { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}`, }; const response = await fetch(`${API_BASE}/auth/api-key`, { method: 'POST', headers, body: JSON.stringify({ name }), }); return response.json() as Promise>; } async getDays() { return this.request>('GET', '/days'); } async getDay(date: string) { return this.request<{ date: string; entries: Entry[]; journal: Journal | null }>('GET', `/days/${date}`); } async deleteDay(date: string) { return this.request<{ deleted: boolean }>('DELETE', `/days/${date}`); } async createEntry(date: string, type: string, content: string, metadata?: object) { return this.request('POST', '/entries', { date, type, content, metadata }); } async updateEntry(id: string, content: string, metadata?: object) { return this.request('PUT', `/entries/${id}`, { content, metadata }); } async deleteEntry(id: string) { return this.request<{ deleted: boolean }>('DELETE', `/entries/${id}`); } async uploadPhoto(entryId: string, file: File) { const formData = new FormData(); formData.append('file', file); const headers: Record = {}; if (this.getApiKey()) { headers['Authorization'] = `Bearer ${this.getApiKey()}`; } const response = await fetch(`${API_BASE}/entries/${entryId}/photo`, { method: 'POST', headers, body: formData, }); return response.json() as Promise>; } async uploadVoice(entryId: string, file: File) { const formData = new FormData(); formData.append('file', file); const headers: Record = {}; if (this.getApiKey()) { headers['Authorization'] = `Bearer ${this.getApiKey()}`; } const response = await fetch(`${API_BASE}/entries/${entryId}/voice`, { method: 'POST', headers, body: formData, }); return response.json() as Promise>; } async generateJournal(date: string) { return this.request<{ journal: Journal; task: Task }>('POST', `/journal/generate/${date}`); } async getJournal(date: string) { return this.request('GET', `/journal/${date}`); } async getJournalTasks(date: string) { return this.request('GET', `/journal/${date}/tasks`); } async getTask(taskId: string) { return this.request('GET', `/tasks/${taskId}`); } async getSettings() { return this.request('GET', '/settings'); } async updateSettings(settings: Partial) { return this.request('PUT', '/settings', settings); } } export interface Entry { id: string; date: string; type: 'text' | 'voice' | 'photo' | 'health' | 'location'; content: string; mediaPath?: string; metadata?: string; createdAt: string; } export interface Journal { id: string; date: string; content: string; entryCount: number; generatedAt: string; } export interface Task { id: string; journalId: string; type: string; status: 'pending' | 'completed' | 'failed'; provider: string; model?: string; prompt?: string; request?: string; response?: string; error?: string; createdAt: string; completedAt?: string; } export interface Settings { aiProvider: 'openai' | 'anthropic' | 'ollama' | 'lmstudio' | 'groq'; aiApiKey?: string; aiModel: string; aiBaseUrl?: string; journalPrompt: string; language: string; timezone: string; journalContextDays: number; providerSettings?: Record; } export const api = new ApiClient();