initial commit
This commit is contained in:
@@ -0,0 +1,178 @@
|
||||
import { Router } from 'express';
|
||||
import { authRequired } from '../middleware/authRequired.js';
|
||||
import * as resultService from '../services/result.service.js';
|
||||
import axios from 'axios';
|
||||
import { env } from '../config/env.js';
|
||||
|
||||
const router = Router();
|
||||
|
||||
/**
|
||||
* GET /api/result/:generationUuid
|
||||
* Получить результат генерации
|
||||
*/
|
||||
router.get('/:generationUuid', authRequired, async (req, res, next) => {
|
||||
try {
|
||||
const { generationUuid } = req.params;
|
||||
const userId = req.user.id;
|
||||
|
||||
// Получаем метаданные генерации из БД (только для получения scenarioId)
|
||||
const generationMeta = await resultService.getGenerationMeta({ userId, generationUuid });
|
||||
|
||||
if (!generationMeta) {
|
||||
return res.status(404).json({
|
||||
error: 'NOT_FOUND',
|
||||
message: 'Result not found or access denied',
|
||||
});
|
||||
}
|
||||
|
||||
// Вызываем n8n webhook для получения результата
|
||||
const stepData = {};
|
||||
|
||||
const n8nUrl = 'https://n8n.uno-click.pip-test.ru/webhook/result';
|
||||
const n8nResponse = await axios.post(n8nUrl, {
|
||||
meta: { generationUuid, userId, scenarioId: generationMeta.scenarioId, stepData },
|
||||
body: stepData
|
||||
});
|
||||
|
||||
// Проверяем формат ответа от n8n
|
||||
const n8nData = n8nResponse.data;
|
||||
console.log('[result] n8n response:', JSON.stringify(n8nData, null, 2));
|
||||
|
||||
// n8n может вернуть данные в разных форматах
|
||||
// Формат 1: { response: { body: { success: {...} } } }
|
||||
// Формат 2: { success: {...} }
|
||||
// Формат 3: [{ response: { body: { success: {...} } } }] - массив
|
||||
let responseData = n8nData;
|
||||
|
||||
// Если массив - берём первый элемент
|
||||
if (Array.isArray(n8nData) && n8nData.length > 0) {
|
||||
responseData = n8nData[0];
|
||||
}
|
||||
|
||||
// Если есть response.body - извлекаем
|
||||
if (responseData?.response?.body) {
|
||||
responseData = responseData.response.body;
|
||||
}
|
||||
|
||||
console.log('[result] extracted responseData:', JSON.stringify(responseData, null, 2));
|
||||
|
||||
// Вариант 1: n8n вернул output_s3 в response_payload
|
||||
if (responseData?.success?.response_payload?.output_s3) {
|
||||
const s3Key = responseData.success.response_payload.output_s3.replace('s3://uno-click/', '');
|
||||
const publicUrl = `/files/${s3Key}`;
|
||||
|
||||
return res.json({
|
||||
ok: true,
|
||||
result: {
|
||||
generationUuid,
|
||||
scenarioId: generationMeta.scenarioId,
|
||||
scenarioName: generationMeta.scenarioName,
|
||||
status: 'completed',
|
||||
files: [{
|
||||
contentType: 'video',
|
||||
url: publicUrl,
|
||||
}],
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// Вариант 2: n8n вернул files массив с s3:// URL
|
||||
if (responseData?.success?.files && Array.isArray(responseData.success.files)) {
|
||||
const convertedFiles = responseData.success.files.map(file => {
|
||||
// Конвертируем s3:// в /files/...
|
||||
// Поддерживаем оба формата: { url: "s3://..." } и { output_s3: "s3://..." }
|
||||
let fileUrl = file.url || file.output_s3;
|
||||
|
||||
if (fileUrl && fileUrl.startsWith('s3://uno-click/')) {
|
||||
const s3Key = fileUrl.replace('s3://uno-click/', '');
|
||||
return {
|
||||
contentType: file.contentType,
|
||||
url: `/files/${s3Key}`,
|
||||
};
|
||||
}
|
||||
return file;
|
||||
});
|
||||
|
||||
return res.json({
|
||||
ok: true,
|
||||
result: {
|
||||
generationUuid,
|
||||
scenarioId: generationMeta.scenarioId,
|
||||
scenarioName: generationMeta.scenarioName,
|
||||
status: 'completed',
|
||||
files: convertedFiles,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// Вариант 3: success на верхнем уровне (для совместимости)
|
||||
if (n8nData?.success?.files && Array.isArray(n8nData.success.files)) {
|
||||
const convertedFiles = n8nData.success.files.map(file => {
|
||||
if (file.url && file.url.startsWith('s3://uno-click/')) {
|
||||
const s3Key = file.url.replace('s3://uno-click/', '');
|
||||
return {
|
||||
...file,
|
||||
url: `/files/${s3Key}`,
|
||||
};
|
||||
}
|
||||
return file;
|
||||
});
|
||||
|
||||
return res.json({
|
||||
ok: true,
|
||||
result: {
|
||||
generationUuid,
|
||||
scenarioId: generationMeta.scenarioId,
|
||||
scenarioName: generationMeta.scenarioName,
|
||||
status: 'completed',
|
||||
files: convertedFiles,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// Вариант 4: провайдер вернул fail с сообщением — пробрасываем наверх
|
||||
if (responseData?.success?.code === 'fail') {
|
||||
return res.json({
|
||||
success: {
|
||||
code: 'fail',
|
||||
message: responseData.success.message || responseData.success.msg,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// Вариант 5: ошибка от n8n с code/message — пробрасываем наверх
|
||||
if (responseData?.error?.code) {
|
||||
return res.json({
|
||||
error: {
|
||||
code: responseData.error.code,
|
||||
message: responseData.error.message,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// Pass-through для не-успешных кодов (waiting/fail) от universal result-workflow (n8n).
|
||||
// n8n возвращает {success:{code:'fail'|'waiting', message}} — фронт ждёт success на корне.
|
||||
// Без этого блока success теряется в spread внутри data.result, и фронт крутится
|
||||
// в "Рендерит..." до таймаута вместо показа реальной ошибки kie.ai.
|
||||
if (responseData?.success && typeof responseData.success === 'object'
|
||||
&& responseData.success.code && responseData.success.code !== 'success') {
|
||||
console.log('[result] pass-through non-success:', JSON.stringify(responseData.success));
|
||||
return res.json({ success: responseData.success });
|
||||
}
|
||||
|
||||
res.json({
|
||||
ok: true,
|
||||
result: {
|
||||
generationUuid,
|
||||
scenarioId: generationMeta.scenarioId,
|
||||
scenarioName: generationMeta.scenarioName,
|
||||
...responseData,
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
console.error('[result] Error:', err);
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
||||
Reference in New Issue
Block a user