import { Router } from 'express'; import { authRequired } from '../middleware/authRequired.js'; import { csrfRequired } from '../middleware/csrfRequired.js'; import * as scenarioService from '../services/scenario.service.js'; const router = Router(); /** * POST /api/scenario/:scenarioId/start * Запуск сценария. body должен быть пустым {}. */ router.post('/:scenarioId/start', authRequired, csrfRequired, async (req, res, next) => { try { const { scenarioId } = req.params; const input = req.body; // 1) проверить, что пользователь имеет право на этот scenarioId await scenarioService.assertUserCanStartScenario({ userId: req.user.id, scenarioId, }); // 2) создать generation и вызвать n8n const generation = await scenarioService.startScenario({ userId: req.user.id, scenarioId, input, user: req.user, }); res.status(202).json({ ok: true, generationUuid: generation.generationUuid, status: generation.status, currentStepId: generation.currentStepId, }); } catch (err) { next(err); } }); /** * POST /api/scenario/:scenarioId/step/:stepId * Выполнение шага сценария. body должен соответствовать input_schema из БД. */ router.post('/:scenarioId/step/:stepId', authRequired, csrfRequired, async (req, res, next) => { try { const { scenarioId, stepId } = req.params; const input = req.body; // 1) проверить, что пользователь может выполнять этот шаг await scenarioService.assertUserCanExecuteStep({ userId: req.user.id, scenarioId, stepId, }); // 2) выполнить шаг (с валидацией input_schema) const result = await scenarioService.executeStep({ userId: req.user.id, scenarioId, stepId, input, user: req.user, }); res.status(202).json({ ok: true, generationUuid: result.generationUuid, stepState: result.stepState, nextStepId: result.nextStepId, }); } catch (err) { next(err); } }); /** * POST /api/scenario/:scenarioId/step/:stepId/record * Создать запись generation_step и вернуть её ID (для загрузки файлов). * Используется когда нужно загрузить файл ДО выполнения шага. */ router.post('/:scenarioId/step/:stepId/record', authRequired, csrfRequired, async (req, res, next) => { try { const { scenarioId, stepId } = req.params; const input = req.body; const userId = req.user.id; // Проверка прав await scenarioService.assertUserCanExecuteStep({ userId, scenarioId, stepId, }); // Найти активную generation const { pool } = await import('../db.js'); const genQuery = ` SELECT generation_uuid, current_step_id, status FROM uno_bff.generations WHERE user_id = $1 AND scenario_id = $2 AND status IN ('running', 'waiting_for_input') ORDER BY created_at DESC LIMIT 1 `; const genResult = await pool.query(genQuery, [userId, scenarioId]); const generation = genResult.rows[0]; if (!generation) { return res.status(400).json({ ok: false, error: 'GENERATION_NOT_FOUND', message: `No active generation found for scenario '${scenarioId}'`, }); } // Найти шаг сценария для получения step_order const step = await scenarioService.getScenarioStep(scenarioId, stepId); if (!step) { return res.status(404).json({ ok: false, error: 'STEP_NOT_FOUND', message: `Step '${stepId}' not found`, }); } // Создать/обновить запись generation_step const stepQuery = ` INSERT INTO uno_bff.generation_steps (generation_uuid, scenario_id, step_id, step_order, status, request_payload) VALUES ($1, $2, $3, $4, 'running', $5) ON CONFLICT (generation_uuid, step_id) DO UPDATE SET status = 'running', request_payload = $5, updated_at = now() RETURNING id `; const stepResult = await pool.query(stepQuery, [ generation.generation_uuid, scenarioId, stepId, step.step_order, JSON.stringify(input), ]); res.status(201).json({ ok: true, stepRecordId: stepResult.rows[0].id, generationUuid: generation.generation_uuid, }); } catch (err) { next(err); } }); export default router;