/** * API Service Layer * Handles all communication with the backend API */ const API_BASE_URL = import.meta.env.VITE_API_URL || 'http://localhost:8001/api' type ApiOptions = { method?: 'GET' | 'POST' | 'PUT' | 'DELETE' body?: unknown token?: string | null } async function apiCall( endpoint: string, options: ApiOptions = {} ): Promise { const { method = 'GET', body, token } = options const headers: HeadersInit = { 'Content-Type': 'application/json', } if (token) { headers['Authorization'] = `Bearer ${token}` } const config: RequestInit = { method, headers, credentials: 'include', } if (body) { config.body = JSON.stringify(body) } const response = await fetch(`${API_BASE_URL}${endpoint}`, config) if (!response.ok) { const error = await response.json().catch(() => ({})) throw new Error(error.detail || `API error: ${response.statusText}`) } return response.json() as Promise } // ============================================ // USER ENDPOINTS // ============================================ export async function registerUser( userData: { email: string displayName?: string photoURL?: string }, token: string ) { return apiCall('/users/register', { method: 'POST', body: userData, token, }) } export async function getUserByEmail(email: string, token: string) { return apiCall(`/users/by-email/${email}`, { token }) } export async function updateUserProfile( userId: string, updates: { displayName?: string; photoURL?: string; theme?: string; tutorial?: boolean }, token: string ) { return apiCall(`/users/${userId}`, { method: 'PUT', body: updates, token, }) } export async function deleteUser(userId: string, token: string) { return apiCall<{ message: string; user_deleted: number; entries_deleted: number }>( `/users/${userId}`, { method: 'DELETE', token, } ) } // ============================================ // ENTRY ENDPOINTS // ============================================ export interface EncryptionMetadata { encrypted: boolean ciphertext?: string // Base64-encoded encrypted content nonce?: string // Base64-encoded nonce algorithm?: string // e.g., "XSalsa20-Poly1305" } export interface JournalEntryCreate { title?: string // Optional if encrypted content?: string // Optional if encrypted mood?: string tags?: string[] isPublic?: boolean encryption?: EncryptionMetadata } export interface JournalEntry extends JournalEntryCreate { id: string userId: string createdAt: string updatedAt: string entryDate?: string encryption?: EncryptionMetadata } export async function createEntry( userId: string, entryData: JournalEntryCreate, token: string ) { return apiCall<{ id: string; message: string }>( `/entries/${userId}`, { method: 'POST', body: entryData, token, } ) } export async function getUserEntries( userId: string, token: string, limit = 50, skip = 0 ) { return apiCall<{ entries: JournalEntry[]; total: number }>( `/entries/${userId}?limit=${limit}&skip=${skip}`, { token } ) } export async function getEntry( userId: string, entryId: string, token: string ) { return apiCall(`/entries/${userId}/${entryId}`, { token, }) } export async function updateEntry( userId: string, entryId: string, updates: Partial, token: string ) { return apiCall(`/entries/${userId}/${entryId}`, { method: 'PUT', body: updates, token, }) } export async function deleteEntry( userId: string, entryId: string, token: string ) { return apiCall(`/entries/${userId}/${entryId}`, { method: 'DELETE', token, }) } export async function getEntriesByDate( userId: string, startDate: string, endDate: string, token: string ) { return apiCall( `/entries/${userId}/date-range?startDate=${startDate}&endDate=${endDate}`, { token } ) } // ============================================ // TIMEZONE CONVERSION ENDPOINTS // ============================================ export async function convertUTCToIST(utcTimestamp: string) { return apiCall<{ utc: string; ist: string }>( `/entries/convert-timestamp/utc-to-ist`, { method: 'POST', body: { timestamp: utcTimestamp }, } ) }