/** * 노드 기반 데이터 플로우 API */ import { Router, Request, Response } from "express"; import { query, queryOne } from "../../database/db"; import { logger } from "../../utils/logger"; import { NodeFlowExecutionService } from "../../services/nodeFlowExecutionService"; import { AuthenticatedRequest } from "../../types/auth"; import { authenticateToken } from "../../middleware/authMiddleware"; const router = Router(); /** * 플로우 목록 조회 */ router.get("/", async (req: AuthenticatedRequest, res: Response) => { try { const userCompanyCode = req.user?.companyCode; let sqlQuery = ` SELECT flow_id as "flowId", flow_name as "flowName", flow_description as "flowDescription", company_code as "companyCode", created_at as "createdAt", updated_at as "updatedAt" FROM node_flows `; const params: any[] = []; // 슈퍼 관리자가 아니면 회사별 필터링 if (userCompanyCode && userCompanyCode !== "*") { sqlQuery += ` WHERE company_code = $1`; params.push(userCompanyCode); } sqlQuery += ` ORDER BY updated_at DESC`; const flows = await query(sqlQuery, params); return res.json({ success: true, data: flows, }); } catch (error) { logger.error("플로우 목록 조회 실패:", error); return res.status(500).json({ success: false, message: "플로우 목록을 조회하지 못했습니다.", }); } }); /** * 플로우 상세 조회 */ router.get("/:flowId", async (req: Request, res: Response) => { try { const { flowId } = req.params; const flow = await queryOne( ` SELECT flow_id as "flowId", flow_name as "flowName", flow_description as "flowDescription", flow_data as "flowData", created_at as "createdAt", updated_at as "updatedAt" FROM node_flows WHERE flow_id = $1 `, [flowId] ); if (!flow) { return res.status(404).json({ success: false, message: "플로우를 찾을 수 없습니다.", }); } return res.json({ success: true, data: flow, }); } catch (error) { logger.error("플로우 조회 실패:", error); return res.status(500).json({ success: false, message: "플로우를 조회하지 못했습니다.", }); } }); /** * 플로우 저장 (신규) */ router.post("/", async (req: AuthenticatedRequest, res: Response) => { try { const { flowName, flowDescription, flowData } = req.body; const userCompanyCode = req.user?.companyCode || "*"; if (!flowName || !flowData) { return res.status(400).json({ success: false, message: "플로우 이름과 데이터는 필수입니다.", }); } const result = await queryOne( ` INSERT INTO node_flows (flow_name, flow_description, flow_data, company_code) VALUES ($1, $2, $3, $4) RETURNING flow_id as "flowId" `, [flowName, flowDescription || "", flowData, userCompanyCode] ); logger.info( `플로우 저장 성공: ${result.flowId} (회사: ${userCompanyCode})` ); return res.json({ success: true, message: "플로우가 저장되었습니다.", data: { flowId: result.flowId, }, }); } catch (error) { logger.error("플로우 저장 실패:", error); return res.status(500).json({ success: false, message: "플로우를 저장하지 못했습니다.", }); } }); /** * 플로우 수정 */ router.put("/", async (req: Request, res: Response) => { try { const { flowId, flowName, flowDescription, flowData } = req.body; if (!flowId || !flowName || !flowData) { return res.status(400).json({ success: false, message: "플로우 ID, 이름, 데이터는 필수입니다.", }); } await query( ` UPDATE node_flows SET flow_name = $1, flow_description = $2, flow_data = $3, updated_at = NOW() WHERE flow_id = $4 `, [flowName, flowDescription || "", flowData, flowId] ); logger.info(`플로우 수정 성공: ${flowId}`); return res.json({ success: true, message: "플로우가 수정되었습니다.", data: { flowId, }, }); } catch (error) { logger.error("플로우 수정 실패:", error); return res.status(500).json({ success: false, message: "플로우를 수정하지 못했습니다.", }); } }); /** * 플로우 삭제 */ router.delete("/:flowId", async (req: Request, res: Response) => { try { const { flowId } = req.params; await query( ` DELETE FROM node_flows WHERE flow_id = $1 `, [flowId] ); logger.info(`플로우 삭제 성공: ${flowId}`); return res.json({ success: true, message: "플로우가 삭제되었습니다.", }); } catch (error) { logger.error("플로우 삭제 실패:", error); return res.status(500).json({ success: false, message: "플로우를 삭제하지 못했습니다.", }); } }); /** * 플로우 실행 * POST /api/dataflow/node-flows/:flowId/execute */ router.post("/:flowId/execute", authenticateToken, async (req: AuthenticatedRequest, res: Response) => { try { const { flowId } = req.params; const contextData = req.body; logger.info(`🚀 플로우 실행 요청: flowId=${flowId}`, { contextDataKeys: Object.keys(contextData), userId: req.user?.userId, companyCode: req.user?.companyCode, }); // 사용자 정보를 contextData에 추가 const enrichedContextData = { ...contextData, userId: req.user?.userId, userName: req.user?.userName, companyCode: req.user?.companyCode, }; // 플로우 실행 const result = await NodeFlowExecutionService.executeFlow( parseInt(flowId, 10), enrichedContextData ); return res.json({ success: result.success, message: result.message, data: result, }); } catch (error) { logger.error("플로우 실행 실패:", error); return res.status(500).json({ success: false, message: error instanceof Error ? error.message : "플로우 실행 중 오류가 발생했습니다.", }); } }); export default router;