import * as SecureStore from 'expo-secure-store'; import { User, AuthResponse, VerifyTokenResponse, SignUpPayload, ResetPasswordRequestResponse, ResetPasswordResponse, UserUpdatePayload, UserUpdateResponse, MoodComponentPayload, MoodEntryPayload, PaginatedResponse } from '@/lib/api/types'; const API_BASE_URL = process.env.EXPO_PUBLIC_API_BASE_URL; const TOKEN_KEY = process.env.EXPO_PUBLIC_TOKEN_KEY; // Gary B.B Coleman fucking slaps class ApiClient { private async getStoredToken(): Promise { try { return await SecureStore.getItemAsync(TOKEN_KEY); } catch (error) { console.error('Failed to get stored token:', error); return null; } } private async storeToken(token: string): Promise { try { await SecureStore.setItemAsync(TOKEN_KEY, token); } catch (error) { console.error('Failed to store token:', error); throw error; } } private async removeToken(): Promise { try { await SecureStore.deleteItemAsync(TOKEN_KEY); } catch (error) { console.error('Failed to remove token:', error); } } async getStoredAuthData(): Promise<{ token: string | null }> { const token = await this.getStoredToken(); return { token }; } private async request( endpoint: string, options: RequestInit = {} ): Promise { const token = await this.getStoredToken(); const isFormData = options.body instanceof FormData; const config: RequestInit = { ...options, headers: { ...(token && { Authorization: `Bearer ${token}` }), ...(!isFormData && { 'Content-Type': 'application/json' }), ...options.headers, }, }; const response = await fetch(`${API_BASE_URL}${endpoint}`, config); if (!response.ok) { const error = await response.json() .catch(() => ({ error: 'Network error' })); console.error("[API]: ", error, response.status) throw new Error(error.error || `HTTP ${response.status}`); } return response.json(); } // Auth async login(email: string, password: string): Promise { const response = await this.request('/auth/login', { method: 'POST', body: JSON.stringify({ email, password }), }); await this.storeToken(response.token); return response; } async signup(data: SignUpPayload): Promise { const response = await this.request('/users', { method: 'POST', body: JSON.stringify(data), }); await this.storeToken(response.token); return response; } async forgotPassword(email: string): Promise { return this.request('/auth/forgot-password', { method: 'POST', body: JSON.stringify({ email }), }); } async resetPassword(token: string, password: string): Promise { return this.request('/auth/reset-password', { method: 'POST', body: JSON.stringify({ token, newPassword: password }), }); } async verifyToken(): Promise { try { return await this.request('/auth/verify'); } catch (error) { // Token is invalid or expired await this.removeToken(); throw error; } } async logout(): Promise { await this.removeToken(); } // User-related async updateUser(user: UserUpdatePayload): Promise { return await this.request(`/users/${user.id}`, { method: 'PUT', body: JSON.stringify({ user }) }) } async avatar(formData: FormData): Promise { return await this.request(`/users/me/avatar`, { method: 'POST', body: formData, headers: { 'Content-Type': 'multipart/form-data', } as any, }) } async removeAvatar(): Promise { return await this.request(`/users/me/avatar`, { method: 'DELETE' }); } // Mood-related async createMoodEntry(moodEntry): Promise { return await this.request(`/moods`, { method: 'POST', body: JSON.stringify({ mood: moodEntry }) }) } } export const apiClient = new ApiClient();