From 93fd95a6b329cb426cc1afdabae149b69ee5b203 Mon Sep 17 00:00:00 2001 From: admin Date: Sun, 24 May 2026 14:35:05 +0700 Subject: [PATCH] 690524:1435 ADR-028-228-migration #03 --- backend/src/modules/ai/ai.controller.ts | 4 ++++ specs/03-Data-and-Storage/n8n.workflow.v2.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/backend/src/modules/ai/ai.controller.ts b/backend/src/modules/ai/ai.controller.ts index 07dab9ee..f416aed0 100644 --- a/backend/src/modules/ai/ai.controller.ts +++ b/backend/src/modules/ai/ai.controller.ts @@ -696,6 +696,7 @@ export class AiController { @Get('migration/checkpoint/:batchId') @UseGuards(JwtAuthGuard) @ApiBearerAuth() + @Throttle({ default: { limit: 500, ttl: 60000 } }) // 500 req/min for n8n workflow loop @ApiOperation({ summary: 'Migration: ดึง Checkpoint ของ Batch (ADR-023A)' }) @ApiParam({ name: 'batchId', @@ -708,6 +709,7 @@ export class AiController { @Post('migration/checkpoint') @UseGuards(JwtAuthGuard) @ApiBearerAuth() + @Throttle({ default: { limit: 500, ttl: 60000 } }) // 500 req/min for n8n workflow loop @HttpCode(HttpStatus.OK) @ApiOperation({ summary: 'Migration: บันทึก/อัพเดต Checkpoint (ADR-023A)' }) async saveMigrationCheckpoint(@Body() dto: SaveCheckpointDto) { @@ -717,6 +719,7 @@ export class AiController { @Post('migration/queue/record') @UseGuards(JwtAuthGuard) @ApiBearerAuth() + @Throttle({ default: { limit: 500, ttl: 60000 } }) // 500 req/min for n8n workflow loop @HttpCode(HttpStatus.OK) @ApiOperation({ summary: 'Migration: บันทึกรายการเข้า Review Queue (ADR-023A)', @@ -728,6 +731,7 @@ export class AiController { @Post('migration/errors') @UseGuards(JwtAuthGuard) @ApiBearerAuth() + @Throttle({ default: { limit: 500, ttl: 60000 } }) // 500 req/min for n8n workflow loop @HttpCode(HttpStatus.OK) @ApiOperation({ summary: 'Migration: บันทึก Error Log (ADR-023A)' }) async logMigrationError(@Body() dto: MigrationErrorLogDto) { diff --git a/specs/03-Data-and-Storage/n8n.workflow.v2.json b/specs/03-Data-and-Storage/n8n.workflow.v2.json index f392500f..767e4369 100644 --- a/specs/03-Data-and-Storage/n8n.workflow.v2.json +++ b/specs/03-Data-and-Storage/n8n.workflow.v2.json @@ -33,7 +33,7 @@ }, { "parameters": { - "jsCode": "// ============================================\n// CONFIGURATION v2.0 — ADR-023A Compliant\n// n8n เรียกแค่ DMS Backend API เท่านั้น — ห้ามเรียก Ollama โดยตรง\n// ============================================\nconst formData = $('Form Trigger').first()?.json || {};\nconst batchSizeInput = parseInt(String(formData['Batch Size'] || '0'));\nconst excelFileInput = String(formData['Excel File Path'] || '').trim();\nconst jwtTokenInput = String($env.N8N_MIGRATION_TOKEN || '').trim();\nconst resolvedExcelFile = excelFileInput || '/home/node/.n8n-files/staging_ai/C22024.xlsx';\n\n// Validate Excel file path to prevent path traversal\nif (resolvedExcelFile.includes('..') || resolvedExcelFile.includes('~')) {\n throw new Error('Invalid Excel file path: path traversal detected');\n}\n\nif (!jwtTokenInput || !jwtTokenInput.startsWith('Bearer ')) {\n throw new Error('N8N_MIGRATION_TOKEN env var is missing or must start with \"Bearer \"');\n}\n\nconst BATCH_ID = (() => {\n // ใช้ Excel filename เป็น BATCH_ID เพื่อให้ checkpoint ทำงานถูกต้อง\n // เปลี่ยน Excel file = BATCH_ID ใหม่ = เริ่มใหม่ (ปลอดภัย)\n // ใช้ Excel file เดิม = BATCH_ID เดิม = resume ได้\n const filename = resolvedExcelFile.split('/').pop().split('\\\\').pop();\n const baseName = filename.replace(/\\.xlsx?$/i, '');\n return baseName + '-MIGRATION';\n})();\n\nconst CONFIG = {\n // Backend Settings\n BACKEND_URL: 'https://backend.np-dms.work',\n MIGRATION_TOKEN: jwtTokenInput,\n\n // Batch Settings\n BATCH_SIZE: batchSizeInput > 0 ? batchSizeInput : 10,\n BATCH_ID: BATCH_ID,\n DELAY_MS: 5000,\n\n // AI Job Settings (ADR-023A)\n AI_JOB_POLL_INTERVAL_MS: 5000,\n AI_JOB_TIMEOUT_MS: 120000,\n\n // Confidence Thresholds\n CONFIDENCE_HIGH: 0.85,\n CONFIDENCE_LOW: 0.60,\n\n // Paths (Container paths — ต้องตรงกับ volume mount ใน docker-compose)\n EXCEL_FILE: resolvedExcelFile,\n SOURCE_PDF_DIR: '/home/node/.n8n-files/staging_ai',\n LOG_PATH: '/home/node/.n8n-files/migration_logs',\n};\n\nreturn [{ json: { config: CONFIG } }];" + "jsCode": "// ============================================\n// CONFIGURATION v2.0 — ADR-023A Compliant\n// n8n เรียกแค่ DMS Backend API เท่านั้น — ห้ามเรียก Ollama โดยตรง\n// ============================================\nconst formData = $('Form Trigger').first()?.json || {};\nconst batchSizeInput = parseInt(String(formData['Batch Size'] || '0'));\nconst excelFileInput = String(formData['Excel File Path'] || '').trim();\nconst jwtTokenInput = 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6Im1pZ3JhdGlvbl9ib3QiLCJzdWIiOjUsInNjb3BlIjoiR2xvYmFsIiwiaWF0IjoxNzc5NTQwNDk4LCJleHAiOjQ5MzUzMDA0OTh9.TZVzG8u4Cyz8hi0cU3eGaeXinKWKHN3HtYnDcS5p_5I';\nconst resolvedExcelFile = excelFileInput || '/home/node/.n8n-files/staging_ai/C22024.xlsx';\n\n// Validate Excel file path to prevent path traversal\nif (resolvedExcelFile.includes('..') || resolvedExcelFile.includes('~')) {\n throw new Error('Invalid Excel file path: path traversal detected');\n}\n\nif (!jwtTokenInput || !jwtTokenInput.startsWith('Bearer ')) {\n throw new Error('JWT Token is required and must start with \"Bearer \"');\n}\n\nconst BATCH_ID = (() => {\n // ใช้ Excel filename เป็น BATCH_ID เพื่อให้ checkpoint ทำงานถูกต้อง\n // เปลี่ยน Excel file = BATCH_ID ใหม่ = เริ่มใหม่ (ปลอดภัย)\n // ใช้ Excel file เดิม = BATCH_ID เดิม = resume ได้\n const filename = resolvedExcelFile.split('/').pop().split('\\\\').pop();\n const baseName = filename.replace(/\\.xlsx?$/i, '');\n return baseName + '-MIGRATION';\n})();\n\nconst CONFIG = {\n // Backend Settings\n BACKEND_URL: 'https://backend.np-dms.work',\n MIGRATION_TOKEN: jwtTokenInput,\n\n // Batch Settings\n BATCH_SIZE: batchSizeInput > 0 ? batchSizeInput : 10,\n BATCH_ID: BATCH_ID,\n DELAY_MS: 20000,\n\n // AI Job Settings (ADR-023A)\n AI_JOB_POLL_INTERVAL_MS: 5000,\n AI_JOB_TIMEOUT_MS: 120000,\n\n // Confidence Thresholds\n CONFIDENCE_HIGH: 0.85,\n CONFIDENCE_LOW: 0.60,\n\n // Paths (Container paths — ต้องตรงกับ volume mount ใน docker-compose)\n EXCEL_FILE: resolvedExcelFile,\n SOURCE_PDF_DIR: '/home/node/.n8n-files/staging_ai',\n LOG_PATH: '/home/node/.n8n-files/migration_logs',\n};\n\nreturn [{ json: { config: CONFIG } }];" }, "id": "8f1d3378-cca6-48b6-99db-693e46ac81ef", "name": "Set Configuration",