const axios = require('axios'); const FormData = require('form-data'); const Config = require('../models/Config'); const { decrypt } = require('../utils/crypto'); exports.analyzeImage = async (req, res) => { try { if (!req.file) { return res.status(400).json({ error: 'No image provided' }); } // Fetch API Key from DB const apiKeyConfig = await Config.findOne({ keyName: 'PERFECT_CORP_API_KEY' }); if (!apiKeyConfig) { return res.status(500).json({ error: 'API Key configuration missing' }); } const decryptedApiKey = decrypt(apiKeyConfig.encryptedValue, apiKeyConfig.iv); const baseUrl = process.env.PERFECT_CORP_API_URL || 'https://yce-api-01.makeupar.com/s2s/v2.0'; // Step 1: Initialize Upload console.log('Step 1: Initializing Upload...'); const initPayload = { files: [ { content_type: req.file.mimetype || 'image/jpeg', file_name: req.file.originalname || 'image.jpg', file_size: req.file.size } ] }; const initResponse = await axios.post(`${baseUrl}/file`, initPayload, { headers: { 'Authorization': `Bearer ${decryptedApiKey}`, 'Content-Type': 'application/json' } }); const fileData = initResponse.data?.data?.files?.[0]; if (!fileData || !fileData.requests || !fileData.requests[0]) { throw new Error('Failed to get upload URL from Perfect Corp API'); } const fileId = fileData.file_id; const uploadRequest = fileData.requests[0]; // Step 2: S3 Upload console.log(`Step 2: Uploading image to S3 (fileId: ${fileId})...`); await axios.put(uploadRequest.url, req.file.buffer, { headers: uploadRequest.headers }); // Step 3: Create Task console.log('Step 3: Creating AI Task...'); const taskPayload = { src_file_id: fileId, dst_actions: [ "wrinkle", "pore", "texture", "acne", "moisture", "eye_bag", "firmness", "age_spot", "radiance", "redness", "oiliness" ], miniserver_args: { enable_mask_overlay: false // Generate multiple output masks }, format: "json" }; const taskResponse = await axios.post(`${baseUrl}/task/skin-analysis`, taskPayload, { headers: { 'Authorization': `Bearer ${decryptedApiKey}`, 'Content-Type': 'application/json' } }); const taskId = taskResponse.data?.data?.task_id; if (!taskId) { throw new Error('Failed to create AI task'); } // Step 4: Poll Task Status console.log(`Step 4: Polling task status for taskId: ${taskId}...`); let taskFinished = false; let finalResult = null; let attempts = 0; const maxAttempts = 30; // 60 seconds max while (!taskFinished && attempts < maxAttempts) { await new Promise(resolve => setTimeout(resolve, 2000)); // Wait 2 seconds attempts++; const pollResponse = await axios.get(`${baseUrl}/task/skin-analysis/${taskId}`, { headers: { 'Authorization': `Bearer ${decryptedApiKey}` } }); const status = pollResponse.data?.data?.task_status; if (status === 'success') { taskFinished = true; finalResult = pollResponse.data; } else if (status === 'error' || status === 'failed') { throw new Error(`AI Task failed with status: ${status}`); } else { console.log(`Polling... (status: ${status}, attempt: ${attempts})`); } } if (!taskFinished) { throw new Error('AI Task polling timed out'); } console.log('Task finished successfully!'); // Format output to mirror the score_info.json structure const outputArray = finalResult?.data?.results?.output || []; const formattedScores = {}; for (const item of outputArray) { if (item.type) { formattedScores[item.type] = { value: item.raw_score || item.ui_score || 0, ui_score: item.ui_score, raw_score: item.raw_score, output_mask_name: (item.mask_urls && item.mask_urls.length > 0) ? item.mask_urls[0] : null }; } } // Send formatted scores back to frontend res.json({ result: { scores: formattedScores, raw_response: finalResult } }); } catch (error) { console.error('Error analyzing image:', error.response ? JSON.stringify(error.response.data) : error.message); res.status(500).json({ error: 'Failed to analyze image' }); } };