298 lines
6.7 KiB
JavaScript
298 lines
6.7 KiB
JavaScript
![]() |
/**
|
||
|
* 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;
|
||
|
}
|