import apiClient from './apiClient'; class PostService { /** * Get all posts for the current user * @param {Object} params - Query parameters * @param {boolean} params.published - Filter by published status * @returns {Promise} Promise that resolves to the posts data */ async getAll(params = {}) { try { const response = await apiClient.get('/posts', { params }); if (import.meta.env.VITE_NODE_ENV === 'development') { console.log('📝 [Post] Retrieved posts:', response.data); } return response; } catch (error) { if (import.meta.env.VITE_NODE_ENV === 'development') { console.error('📝 [Post] Get posts error:', error.response?.data || error.message); } throw error; } } /** * Generate a new post using AI asynchronously * @returns {Promise} Promise that resolves to the generated post content */ async generate() { try { // Step 1: Start the generation process and get job ID const startResponse = await apiClient.post('/posts/generate', {}); if (import.meta.env.VITE_NODE_ENV === 'development') { console.log('📝 [Post] AI post generation started:', startResponse.data); } const jobId = startResponse.data.job_id; // Step 2: Poll for the result const generatedResult = await this.pollForJobResult(jobId); // Ensure we return a default value if content is null/undefined const finalContent = generatedResult.content !== undefined && generatedResult.content !== null ? generatedResult.content : "Generated content will appear here..."; const finalImage = generatedResult.image || null; return { data: { success: true, content: finalContent, image: finalImage } }; } catch (error) { if (import.meta.env.VITE_NODE_ENV === 'development') { console.error('📝 [Post] Generate post error:', error.response?.data || error.message); } throw error; } } /** * Poll for job result * @param {string} jobId - Job ID to poll * @returns {Promise} Promise that resolves to the generated content */ async pollForJobResult(jobId) { const pollInterval = 6000; // 6 seconds to match backend logs const maxAttempts = 60; // 6 minutes (60 * 6 seconds = 360 seconds) for (let attempt = 1; attempt <= maxAttempts; attempt++) { try { const response = await apiClient.get(`/posts/jobs/${jobId}`); const jobData = response.data; if (import.meta.env.VITE_NODE_ENV === 'development') { console.log(`📝 [Post] Job status check ${attempt}/${maxAttempts}:`, jobData); } switch (jobData.status) { case 'completed': // Log the raw job data for debugging if (import.meta.env.VITE_NODE_ENV === 'development') { console.log('📝 [Post] Raw job data:', jobData); } // Extract content and image data from the job result let content = ''; let imageData = null; // Handle the new structure of the result if (jobData.content !== undefined) { content = jobData.content; } // Handle image data if it exists if (jobData.image_url !== undefined && jobData.image_url !== null) { imageData = jobData.image_url; } else if (jobData.has_image_data) { // Special marker for image data that exists but can't be displayed directly // If we have actual image data (possibly base64), we should handle it if (jobData.image_data) { // Check if it's base64 encoded data if (typeof jobData.image_data === 'string' && jobData.image_data.startsWith('data:image')) { imageData = jobData.image_data; // Use the base64 data directly } else { imageData = 'HAS_IMAGE_DATA_BUT_NOT_URL'; } } else { imageData = 'HAS_IMAGE_DATA_BUT_NOT_URL'; } } // If content is an array, take the first element if (Array.isArray(content)) { content = content[0] || ''; } // Ensure we return the content even if it's an empty string return { content: content !== undefined ? content : '', image: imageData }; case 'failed': throw new Error(jobData.error || 'Job failed'); case 'processing': case 'pending': // Wait before next poll await new Promise(resolve => setTimeout(resolve, pollInterval)); break; default: throw new Error(`Unknown job status: ${jobData.status}`); } } catch (error) { if (error.response?.status === 404) { throw new Error('Job not found'); } throw error; } } // If we've reached here, we've exceeded the max attempts throw new Error('Job polling timed out after 6 minutes. Please check back later.'); } /** * Create a new post * @param {Object} postData - Post data * @param {string} postData.social_account_id - Social account ID * @param {string} postData.text_content - Post text content * @param {string} [postData.image_content_url] - Image URL (optional) * @param {string} [postData.scheduled_at] - Scheduled time (optional) * @returns {Promise} Promise that resolves to the create post response */ async create(postData) { try { if (import.meta.env.VITE_NODE_ENV === 'development') { console.log('📝 [Post] Creating post with data:', postData); } const response = await apiClient.post('/posts', { social_account_id: postData.social_account_id, text_content: postData.text_content, image_content_url: postData.image_content_url, scheduled_at: postData.scheduled_at }); if (import.meta.env.VITE_NODE_ENV === 'development') { console.log('📝 [Post] Post created:', response.data); } return response; } catch (error) { if (import.meta.env.VITE_NODE_ENV === 'development') { console.error('📝 [Post] Create post error:', error.response?.data || error.message); console.error('📝 [Post] Error details:', { status: error.response?.status, statusText: error.response?.statusText, headers: error.response?.headers, data: error.response?.data }); } throw error; } } /** * Publish a post directly to social media * @param {Object} publishData - Publish data * @param {string} publishData.social_account_id - Social account ID * @param {string} publishData.text_content - Post text content * @returns {Promise} Promise that resolves to the publish post response */ async publishDirect(publishData) { try { if (import.meta.env.VITE_NODE_ENV === 'development') { console.log('📝 [Post] Publishing post directly to social media with data:', publishData); } const response = await apiClient.post('/posts/publish-direct', publishData); if (import.meta.env.VITE_NODE_ENV === 'development') { console.log('📝 [Post] Post published directly:', response.data); } return response; } catch (error) { if (import.meta.env.VITE_NODE_ENV === 'development') { console.error('📝 [Post] Publish post directly error:', error.response?.data || error.message); console.error('📝 [Post] Error details:', { status: error.response?.status, statusText: error.response?.statusText, headers: error.response?.headers, data: error.response?.data }); } throw error; } } /** * Delete a post * @param {string} postId - Post ID * @returns {Promise} Promise that resolves to the delete post response */ async delete(postId) { try { const response = await apiClient.delete(`/posts/${postId}`); if (import.meta.env.VITE_NODE_ENV === 'development') { console.log('📝 [Post] Post deleted:', response.data); } return response; } catch (error) { if (import.meta.env.VITE_NODE_ENV === 'development') { console.error('📝 [Post] Delete post error:', error.response?.data || error.message); } throw error; } } } export default new PostService();