/** * API Service for handling all backend communication */ class APIService { constructor() { this.baseURL = CONFIG.API_BASE_URL; this.token = localStorage.getItem(CONFIG.STORAGE_KEYS.AUTH_TOKEN); } /** * Set authentication token */ setToken(token) { this.token = token; if (token) { localStorage.setItem(CONFIG.STORAGE_KEYS.AUTH_TOKEN, token); } else { localStorage.removeItem(CONFIG.STORAGE_KEYS.AUTH_TOKEN); } } /** * Get authentication headers */ getHeaders() { const headers = { 'Content-Type': 'application/json', }; if (this.token) { headers['Authorization'] = `Bearer ${this.token}`; } return headers; } /** * Make API request */ async request(endpoint, options = {}) { const url = `${this.baseURL}${endpoint}`; const config = { headers: this.getHeaders(), ...options, }; try { const response = await fetch(url, config); if (response.status === 401) { // Token expired or invalid this.setToken(null); throw new Error('Authentication required'); } if (!response.ok) { const errorData = await response.json().catch(() => ({})); throw new Error(errorData.message || `HTTP ${response.status}`); } const contentType = response.headers.get('content-type'); if (contentType && contentType.includes('application/json')) { return await response.json(); } return response; } catch (error) { console.error('API Request Error:', error); throw error; } } /** * GET request */ async get(endpoint) { return this.request(endpoint, { method: 'GET' }); } /** * POST request */ async post(endpoint, data) { return this.request(endpoint, { method: 'POST', body: JSON.stringify(data), }); } /** * PUT request */ async put(endpoint, data) { return this.request(endpoint, { method: 'PUT', body: JSON.stringify(data), }); } /** * DELETE request */ async delete(endpoint) { return this.request(endpoint, { method: 'DELETE' }); } /** * Upload files with FormData */ async upload(endpoint, formData, onProgress = null) { const url = `${this.baseURL}${endpoint}`; return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); // Track upload progress if (onProgress) { xhr.upload.addEventListener('progress', (event) => { if (event.lengthComputable) { const percentComplete = (event.loaded / event.total) * 100; onProgress(percentComplete); } }); } xhr.addEventListener('load', () => { if (xhr.status >= 200 && xhr.status < 300) { try { const response = JSON.parse(xhr.responseText); resolve(response); } catch (error) { resolve(xhr.responseText); } } else { reject(new Error(`Upload failed: ${xhr.status}`)); } }); xhr.addEventListener('error', () => { reject(new Error('Upload failed')); }); xhr.open('POST', url); // Set auth header if (this.token) { xhr.setRequestHeader('Authorization', `Bearer ${this.token}`); } xhr.send(formData); }); } // Auth API methods async getProfile() { return this.get(CONFIG.ENDPOINTS.ME); } async logout() { const result = await this.post(CONFIG.ENDPOINTS.LOGOUT); this.setToken(null); return result; } // User API methods async getUserStats() { return this.get(CONFIG.ENDPOINTS.USER_STATS); } async getUserQuota() { return this.get(CONFIG.ENDPOINTS.USER_QUOTA); } // Batch API methods async createBatch(data) { return this.post(CONFIG.ENDPOINTS.BATCHES, data); } async getBatch(batchId) { return this.get(CONFIG.ENDPOINTS.BATCHES.replace(':id', batchId)); } async getBatchStatus(batchId) { return this.get(CONFIG.ENDPOINTS.BATCH_STATUS.replace(':id', batchId)); } async getBatchImages(batchId) { return this.get(CONFIG.ENDPOINTS.BATCH_IMAGES.replace(':id', batchId)); } async getBatches(page = 1, limit = 10) { return this.get(`${CONFIG.ENDPOINTS.BATCHES}?page=${page}&limit=${limit}`); } // Image API methods async uploadImages(files, batchId, onProgress = null) { const formData = new FormData(); formData.append('batchId', batchId); files.forEach((file, index) => { formData.append('images', file); }); return this.upload(CONFIG.ENDPOINTS.IMAGE_UPLOAD, formData, onProgress); } async updateImageFilename(imageId, filename) { return this.put(CONFIG.ENDPOINTS.IMAGE_UPDATE.replace(':id', imageId), { filename, }); } // Keyword API methods async enhanceKeywords(keywords) { return this.post(CONFIG.ENDPOINTS.KEYWORD_ENHANCE, { keywords }); } // Payment API methods async getPlans() { return this.get(CONFIG.ENDPOINTS.PAYMENT_PLANS); } async getSubscription() { return this.get(CONFIG.ENDPOINTS.PAYMENT_SUBSCRIPTION); } async createCheckoutSession(plan, successUrl, cancelUrl) { return this.post(CONFIG.ENDPOINTS.PAYMENT_CHECKOUT, { plan, successUrl, cancelUrl, }); } async createPortalSession(returnUrl) { return this.post(CONFIG.ENDPOINTS.PAYMENT_PORTAL, { returnUrl, }); } async cancelSubscription() { return this.post('/api/payments/cancel-subscription'); } async upgradePlan(plan, successUrl, cancelUrl) { return this.post('/api/payments/upgrade', { plan, successUrl, cancelUrl, }); } // Download API methods async createDownload(batchId) { return this.post(CONFIG.ENDPOINTS.DOWNLOAD_CREATE, { batchId }); } async getDownloadStatus(downloadId) { return this.get(CONFIG.ENDPOINTS.DOWNLOAD_STATUS.replace(':id', downloadId)); } async getDownloadHistory() { return this.get(CONFIG.ENDPOINTS.DOWNLOAD_HISTORY); } getDownloadUrl(downloadId) { return `${this.baseURL}${CONFIG.ENDPOINTS.DOWNLOAD_FILE.replace(':id', downloadId)}`; } // Utility methods buildUrl(endpoint, params = {}) { let url = endpoint; Object.keys(params).forEach(key => { url = url.replace(`:${key}`, params[key]); }); return url; } async healthCheck() { try { await this.get('/api/health'); return true; } catch (error) { return false; } } } // Create global API instance const API = new APIService(); // Export for use in other modules if (typeof module !== 'undefined' && module.exports) { module.exports = { APIService, API }; } else if (typeof window !== 'undefined') { window.API = API; window.APIService = APIService; }