import * as SecureStore from 'expo-secure-store'; import type { AuthResponse, ChatResponse, Beneficiary, ApiResponse, ApiError } from '@/types'; const API_BASE_URL = 'https://eluxnetworks.net/function/well-api/api'; const CLIENT_ID = 'MA_001'; class ApiService { private async getToken(): Promise { try { return await SecureStore.getItemAsync('accessToken'); } catch { return null; } } private async getUserName(): Promise { try { return await SecureStore.getItemAsync('userName'); } catch { return null; } } private generateNonce(): string { return Math.floor(Math.random() * 1000000).toString(); } private async makeRequest(params: Record): Promise> { try { const formData = new URLSearchParams(); Object.entries(params).forEach(([key, value]) => { formData.append(key, value); }); const response = await fetch(API_BASE_URL, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, body: formData.toString(), }); const data = await response.json(); if (data.status === '200 OK' || data.ok === true) { return { data: data as T, ok: true }; } return { ok: false, error: { message: data.message || data.error || 'Request failed', status: response.status, }, }; } catch (error) { const apiError: ApiError = { message: error instanceof Error ? error.message : 'Network error', code: 'NETWORK_ERROR', }; return { ok: false, error: apiError }; } } // Authentication async login(username: string, password: string): Promise> { const response = await this.makeRequest({ function: 'credentials', user_name: username, ps: password, clientId: CLIENT_ID, nonce: this.generateNonce(), }); if (response.ok && response.data) { // Save credentials to SecureStore (including password for auto-refresh) await SecureStore.setItemAsync('accessToken', response.data.access_token); await SecureStore.setItemAsync('userId', response.data.user_id.toString()); await SecureStore.setItemAsync('userName', username); await SecureStore.setItemAsync('userPassword', password); // Store for token refresh await SecureStore.setItemAsync('privileges', response.data.privileges); await SecureStore.setItemAsync('maxRole', response.data.max_role.toString()); } return response; } // Refresh token using stored credentials async refreshToken(): Promise> { try { const userName = await SecureStore.getItemAsync('userName'); const password = await SecureStore.getItemAsync('userPassword'); if (!userName || !password) { return { ok: false, error: { message: 'No stored credentials', code: 'NO_CREDENTIALS' } }; } console.log('Refreshing token for user:', userName); return await this.login(userName, password); } catch (error) { return { ok: false, error: { message: 'Failed to refresh token', code: 'REFRESH_ERROR' } }; } } // Check if token is about to expire (within 1 hour) async isTokenExpiringSoon(): Promise { try { const token = await this.getToken(); if (!token) return true; // Decode JWT to get expiration const parts = token.split('.'); if (parts.length !== 3) return true; const payload = JSON.parse(atob(parts[1])); const exp = payload.exp; if (!exp) return true; const now = Math.floor(Date.now() / 1000); const oneHour = 60 * 60; return (exp - now) < oneHour; } catch { return true; } } // Get all credentials for WebView injection async getWebViewCredentials(): Promise<{ token: string; userName: string; userId: string; } | null> { try { const token = await SecureStore.getItemAsync('accessToken'); const userName = await SecureStore.getItemAsync('userName'); const userId = await SecureStore.getItemAsync('userId'); if (!token || !userName || !userId) return null; return { token, userName, userId }; } catch { return null; } } async deleteAccount(): Promise> { const token = await this.getToken(); const userName = await this.getUserName(); if (!token || !userName) { return { ok: false, error: { message: 'Not authenticated', code: 'UNAUTHORIZED' } }; } const response = await this.makeRequest({ function: 'delete_account', user_name: userName, token: token, }); // If successful, logout locally if (response.ok) { await this.logout(); } return response; } async logout(): Promise { await SecureStore.deleteItemAsync('accessToken'); await SecureStore.deleteItemAsync('userId'); await SecureStore.deleteItemAsync('userName'); await SecureStore.deleteItemAsync('userPassword'); await SecureStore.deleteItemAsync('privileges'); await SecureStore.deleteItemAsync('maxRole'); } async isAuthenticated(): Promise { const token = await this.getToken(); return !!token; } // Get stored user info async getStoredUser() { try { const userId = await SecureStore.getItemAsync('userId'); const userName = await SecureStore.getItemAsync('userName'); const privileges = await SecureStore.getItemAsync('privileges'); const maxRole = await SecureStore.getItemAsync('maxRole'); if (!userId || !userName) return null; return { user_id: parseInt(userId, 10), user_name: userName, privileges: privileges || '', max_role: parseInt(maxRole || '0', 10), }; } catch { return null; } } // Beneficiaries (elderly people being monitored) async getBeneficiaries(): Promise> { const token = await this.getToken(); if (!token) { return { ok: false, error: { message: 'Not authenticated', code: 'UNAUTHORIZED' } }; } // Note: Using mock data since API structure is not fully documented // Replace with actual API call when available const mockBeneficiaries: Beneficiary[] = [ { id: 1, name: 'Julia Smith', status: 'online', relationship: 'Mother', last_activity: '2 min ago', sensor_data: { motion_detected: true, last_motion: '2 min ago', door_status: 'closed', temperature: 22, humidity: 45, }, }, { id: 2, name: 'Robert Johnson', status: 'offline', relationship: 'Father', last_activity: '1 hour ago', sensor_data: { motion_detected: false, last_motion: '1 hour ago', door_status: 'closed', temperature: 21, humidity: 50, }, }, ]; return { data: { beneficiaries: mockBeneficiaries }, ok: true }; } async getBeneficiary(id: number): Promise> { const response = await this.getBeneficiaries(); if (!response.ok || !response.data) { return { ok: false, error: response.error }; } const beneficiary = response.data.beneficiaries.find((b) => b.id === id); if (!beneficiary) { return { ok: false, error: { message: 'Beneficiary not found', code: 'NOT_FOUND' } }; } return { data: beneficiary, ok: true }; } // Get all beneficiaries using deployments_list API (real data) async getAllBeneficiaries(): Promise> { const token = await this.getToken(); const userName = await this.getUserName(); if (!token || !userName) { return { ok: false, error: { message: 'Not authenticated', code: 'UNAUTHORIZED' } }; } const response = await this.makeRequest<{ result_list: Array<{ deployment_id: number; email: string; first_name: string; last_name: string; }> }>({ function: 'deployments_list', user_name: userName, token: token, first: '0', last: '100', }); if (!response.ok || !response.data?.result_list) { return { ok: false, error: response.error || { message: 'Failed to get beneficiaries' } }; } const beneficiaries: Beneficiary[] = response.data.result_list.map(item => ({ id: item.deployment_id, name: `${item.first_name} ${item.last_name}`.trim(), status: 'offline' as const, email: item.email, })); return { data: beneficiaries, ok: true }; } // AI Chat async sendMessage(question: string, deploymentId: string = '21'): Promise> { const token = await this.getToken(); const userName = await this.getUserName(); if (!token || !userName) { return { ok: false, error: { message: 'Not authenticated', code: 'UNAUTHORIZED' } }; } return this.makeRequest({ function: 'voice_ask', clientId: CLIENT_ID, user_name: userName, token: token, question: question, deployment_id: deploymentId, }); } } export const api = new ApiService();