Fix script execution logs being lost

- Add ScriptExecutionError class that preserves captured logs/queries
- IsolatedScriptExecutor: throw ScriptExecutionError with accumulated
  logs instead of plain Error on script failure
- ScriptExecutor (Python): same fix for Python execution errors
- testEndpoint: return captured logs/queries on script errors
- dynamicApiController: correctly extract scriptResult.result instead
  of stuffing entire IsolatedExecutionResult into rows; include logs
  in detailed_response output

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-02 02:52:51 +03:00
parent 3a3c87164d
commit 49f262d8ae
5 changed files with 35 additions and 12 deletions

View File

@@ -1,7 +1,7 @@
import * as vm from 'vm';
import { sqlExecutor } from './SqlExecutor';
import { aqlExecutor } from './AqlExecutor';
import { ScriptQuery, EndpointParameter, LogEntry, QueryExecution, IsolatedExecutionResult } from '../types';
import { ScriptQuery, EndpointParameter, LogEntry, QueryExecution, IsolatedExecutionResult, ScriptExecutionError } from '../types';
import { databasePoolManager } from './DatabasePoolManager';
interface IsolatedScriptContext {
@@ -228,7 +228,9 @@ export class IsolatedScriptExecutor {
}
timerIds.clear();
throw new Error(`JavaScript execution error: ${error.message}`);
// Preserve captured logs and queries in the error
logs.push({ type: 'error', message: error.message, timestamp: Date.now() });
throw new ScriptExecutionError(`JavaScript execution error: ${error.message}`, logs, queries);
}
}

View File

@@ -1,7 +1,7 @@
import { spawn } from 'child_process';
import { sqlExecutor } from './SqlExecutor';
import { aqlExecutor } from './AqlExecutor';
import { ScriptQuery, EndpointParameter, LogEntry, QueryExecution, IsolatedExecutionResult } from '../types';
import { ScriptQuery, EndpointParameter, LogEntry, QueryExecution, IsolatedExecutionResult, ScriptExecutionError } from '../types';
import { databasePoolManager } from './DatabasePoolManager';
import { isolatedScriptExecutor } from './IsolatedScriptExecutor';
@@ -258,7 +258,8 @@ print(json.dumps(result))
python.on('close', (exitCode) => {
if (exitCode !== 0) {
reject(new Error(`Python execution error: ${errorOutput}`));
logs.push({ type: 'error', message: errorOutput, timestamp: Date.now() });
reject(new ScriptExecutionError(`Python execution error: ${errorOutput}`, logs, queries));
} else {
try {
// Последняя строка вывода - результат, остальные - логи
@@ -276,7 +277,7 @@ print(json.dumps(result))
const result = JSON.parse(resultLine);
resolve({ result, logs, queries });
} catch (error) {
reject(new Error(`Failed to parse Python output: ${output}`));
reject(new ScriptExecutionError(`Failed to parse Python output: ${output}`, logs, queries));
}
}
});
@@ -284,7 +285,7 @@ print(json.dumps(result))
// Таймаут 10 минут
setTimeout(() => {
python.kill();
reject(new Error('Python script execution timeout (10min)'));
reject(new ScriptExecutionError('Python script execution timeout (10min)', logs, queries));
}, 600000);
});
}