import Constants from "expo-constants"; import * as SecureStore from "expo-secure-store"; function getApiBaseUrl() { const envUrl = (process as any)?.env?.API_BASE_URL; const extraUrl = (Constants.manifest as any)?.extra?.API_BASE_URL || (Constants.expoConfig as any)?.extra?.API_BASE_URL; if (envUrl) return envUrl; if (extraUrl) return extraUrl; throw new Error("API_BASE_URL is not defined in environment or Expo config"); } const API_BASE_URL = getApiBaseUrl(); const AUTH_TOKEN_KEY = "buddy_auth_token"; const SELECTED_DEVICE_KEY = "buddy_selected_device"; class ApiClient { private token: string | null = null; private selectedDeviceId: string | null = null; async initialize() { try { this.token = await SecureStore.getItemAsync(AUTH_TOKEN_KEY); this.selectedDeviceId = await SecureStore.getItemAsync(SELECTED_DEVICE_KEY); } catch (e) { console.error("Failed to load auth token", e); } } async setToken(token: string) { this.token = token; await SecureStore.setItemAsync(AUTH_TOKEN_KEY, token); } async clearToken() { this.token = null; await SecureStore.deleteItemAsync(AUTH_TOKEN_KEY); } async setSelectedDevice(deviceId: string) { this.selectedDeviceId = deviceId; await SecureStore.setItemAsync(SELECTED_DEVICE_KEY, deviceId); } getSelectedDeviceId(): string | null { return this.selectedDeviceId; } isAuthenticated(): boolean { return this.token !== null; } private async request( method: "GET" | "POST" | "PUT" | "DELETE", endpoint: string, body?: any, ): Promise { const headers: HeadersInit = { "Content-Type": "application/json", }; if (this.token) { headers["Authorization"] = `Bearer ${this.token}`; } const response = await fetch(`${API_BASE_URL}${endpoint}`, { method, headers, body: body ? JSON.stringify(body) : undefined, }); if (!response.ok) { const error = await response .json() .catch(() => ({ reason: "Unknown error" })); throw new Error(error.reason || `HTTP ${response.status}`); } return response.json(); } async get(endpoint: string): Promise { return this.request("GET", endpoint); } async post(endpoint: string, body?: any): Promise { return this.request("POST", endpoint, body); } async delete(endpoint: string, body?: any): Promise { return this.request("DELETE", endpoint, body); } // Auth endpoints async signIn( email: string, password: string, ): Promise<{ success: boolean; token?: string; reason?: string }> { const result = await this.post<{ success: boolean; token: string; reason: string; }>("/signin", { email, password, }); if (result.success && result.token) { await this.setToken(result.token); } return result; } async signUp( email: string, password: string, ): Promise<{ success: boolean; token?: string; reason?: string }> { const result = await this.post<{ success: boolean; token: string; reason: string; }>("/signup", { email, password, }); if (result.success && result.token) { await this.setToken(result.token); } return result; } async signOut() { await this.clearToken(); } } export const apiClient = new ApiClient(); // Initialize on import apiClient.initialize();