feat: setup S2S integration, tailwind fixes, admin routing, nginx fallback
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
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 {
|
||||
@@ -7,50 +9,128 @@ exports.analyzeImage = async (req, res) => {
|
||||
return res.status(400).json({ error: 'No image provided' });
|
||||
}
|
||||
|
||||
// In a real scenario, we would forward this to the Perfect Corp API
|
||||
// For now, we will return a mock response matching the expected structure
|
||||
// or you could proxy it like this:
|
||||
/*
|
||||
const formData = new FormData();
|
||||
formData.append('image', req.file.buffer, req.file.originalname);
|
||||
|
||||
const response = await axios.post(process.env.PERFECT_CORP_API_URL, formData, {
|
||||
headers: {
|
||||
...formData.getHeaders(),
|
||||
'Authorization': `Bearer ${process.env.PERFECT_CORP_API_KEY}`
|
||||
}
|
||||
});
|
||||
return res.json({ result: response.data });
|
||||
*/
|
||||
// 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);
|
||||
|
||||
// MOCK RESPONSE
|
||||
console.log('Mocking perfect corp analysis for image:', req.file.originalname);
|
||||
|
||||
const mockResult = {
|
||||
results: [
|
||||
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: [
|
||||
{
|
||||
data: [
|
||||
{
|
||||
url: 'https://example.com/mock-mask.zip'
|
||||
}
|
||||
]
|
||||
content_type: req.file.mimetype || 'image/jpeg',
|
||||
file_name: req.file.originalname || 'image.jpg',
|
||||
file_size: req.file.size
|
||||
}
|
||||
],
|
||||
// Add other mock data that the frontend expects to parse...
|
||||
face_attributes: {
|
||||
age: 30,
|
||||
gender: 'female'
|
||||
},
|
||||
skin_issues: {
|
||||
wrinkles: 10,
|
||||
spots: 5,
|
||||
acne: 2
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
res.json({ result: mockResult });
|
||||
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);
|
||||
console.error('Error analyzing image:', error.response ? JSON.stringify(error.response.data) : error.message);
|
||||
res.status(500).json({ error: 'Failed to analyze image' });
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user