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 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; events: Event[]; journal: Journal | null }>('GET', `/days/${date}`); } async deleteDay(date: string) { return this.request<{ deleted: boolean }>('DELETE', `/days/${date}`); } async createEvent(date: string, type: string, content: string, metadata?: object, location?: { latitude: number; longitude: number; placeName?: string }) { return this.request('POST', '/events', { date, type, content, metadata, ...location }); } async updateEvent(id: string, content: string, metadata?: object) { return this.request('PUT', `/events/${id}`, { content, metadata }); } async deleteEvent(id: string) { return this.request<{ deleted: boolean }>('DELETE', `/events/${id}`); } async deleteJournal(date: string) { return this.request<{ deleted: boolean }>('DELETE', `/journal/${date}`); } async uploadPhoto(eventId: 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}/events/${eventId}/photo`, { method: 'POST', headers, body: formData, }); return response.json() as Promise>; } async uploadVoice(eventId: 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}/events/${eventId}/voice`, { method: 'POST', headers, body: formData, }); return response.json() as Promise>; } async generateJournal(date: string, instructions?: string) { return this.request<{ journal: Journal; task: Task }>('POST', `/journal/generate/${date}`, { instructions: instructions || '' }); } async getJournal(date: string) { return this.request('GET', `/journal/${date}`); } async getJournals(page: number = 1, limit: number = 10) { return this.request<{ journals: Journal[]; total: number; page: number; limit: number; totalPages: number }>('GET', `/journals?page=${page}&limit=${limit}`); } 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 search(query: string) { return this.request<{ journals: Array<{ date: string; title: string; excerpt: string }>; events: Array<{ date: string; type: string; content: string }> }>('GET', `/search?q=${encodeURIComponent(query)}`); } async updateSettings(settings: Partial) { return this.request('PUT', '/settings', settings); } async exportData() { return this.request('GET', '/export'); } async importData(data: ExportData) { return this.request('POST', '/import', data); } async deleteAccount() { return this.request<{ deleted: boolean }>('DELETE', '/account'); } async resetAccount() { return this.request<{ reset: boolean }>('POST', '/account/reset'); } async changePassword(currentPassword: string, newPassword: string) { return this.request<{ changed: boolean }>('POST', '/account/password', { currentPassword, newPassword }); } async register(email: string, password: string) { return this.request<{ apiKey: string; userId: string }>('POST', '/auth/register', { email, password }); } } export interface Event { id: string; date: string; type: string; content: string; mediaPath?: string; metadata?: string; latitude?: number; longitude?: number; placeName?: string; createdAt: string; } export interface Journal { id: string; date: string; title?: string; content: string; eventCount: 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; title?: string | null; createdAt: string; completedAt?: string; } export interface Settings { aiProvider: 'openai' | 'anthropic' | 'ollama' | 'lmstudio' | 'groq' | 'xai' | 'custom'; aiApiKey?: string; aiModel: string; aiBaseUrl?: string; journalPrompt: string; language: string; timezone: string; journalContextDays: number; useSystemDefault: boolean; providerSettings?: Record; parameters?: Record; }>; } export interface ExportData { version: string; exportedAt: string; settings: { aiProvider: string; aiApiKey?: string; aiModel?: string; aiBaseUrl?: string; journalPrompt?: string; language?: string; timezone?: string; providerSettings?: string; journalContextDays?: number; }; events: Array<{ id: string; date: string; type: string; content: string; mediaPath?: string; metadata?: string; latitude?: number; longitude?: number; placeName?: string; createdAt: string; }>; journals: Array<{ id: string; date: string; title?: string; content: string; eventCount: number; generatedAt: string; }>; tasks: Array<{ id: string; journalId: string; date: string; type: string; status: string; provider: string; model?: string; prompt?: string; request?: string; response?: string; error?: string; title?: string; createdAt: string; completedAt?: string; }>; } export interface ImportResult { compatible: boolean; importedEvents: number; importedJournals: number; importedTasks: number; skippedEvents: number; skippedJournals: number; totalEvents: number; totalJournals: number; totalTasks: number; warning?: string; } export const api = new ApiClient();