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) // ВАЖНО: SecureStore принимает ТОЛЬКО строки, проверяем каждое значение if (response.data.access_token) { await SecureStore.setItemAsync('accessToken', String(response.data.access_token)); } if (response.data.user_id != null) { await SecureStore.setItemAsync('userId', String(response.data.user_id)); } if (username) { await SecureStore.setItemAsync('userName', String(username)); } if (password) { await SecureStore.setItemAsync('userPassword', String(password)); // Store for token refresh } await SecureStore.setItemAsync('privileges', String(response.data.privileges || '')); await SecureStore.setItemAsync('maxRole', String(response.data.max_role ?? 0)); } return response; } // Refresh token using stored credentials async refreshToken(): Promise> { try { const userName = await SecureStore.getItemAsync('userName'); const password = await SecureStore.getItemAsync('userPassword'); console.log('[API] refreshToken - userName:', userName ? 'exists' : 'missing'); console.log('[API] refreshToken - password:', password ? 'exists' : 'missing'); if (!userName || !password) { console.log('[API] refreshToken - NO_CREDENTIALS'); return { ok: false, error: { message: 'No stored credentials', code: 'NO_CREDENTIALS' } }; } console.log('[API] Refreshing token for user:', userName); const result = await this.login(userName, password); console.log('[API] refreshToken result:', result.ok ? 'SUCCESS' : result.error?.message); return result; } catch (error) { console.error('[API] refreshToken error:', 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 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; } } // Deployment ID management async setDeploymentId(deploymentId: string): Promise { await SecureStore.setItemAsync('deploymentId', deploymentId); } async getDeploymentId(): Promise { try { return await SecureStore.getItemAsync('deploymentId'); } catch { return null; } } async clearDeploymentId(): Promise { await SecureStore.deleteItemAsync('deploymentId'); } // 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();