mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 10:02:05 +00:00
fix(Code Node): Consistent redirection of stdout for JS and Python sandboxes (#6818)
Co-authored-by: Marcus <marcus@n8n.io>
This commit is contained in:
committed by
GitHub
parent
34df8b6238
commit
f718c2291f
@@ -92,8 +92,9 @@ export class Code implements INodeType {
|
|||||||
const nodeMode = this.getNodeParameter('mode', 0) as CodeExecutionMode;
|
const nodeMode = this.getNodeParameter('mode', 0) as CodeExecutionMode;
|
||||||
const workflowMode = this.getMode();
|
const workflowMode = this.getMode();
|
||||||
|
|
||||||
|
const node = this.getNode();
|
||||||
const language: CodeNodeEditorLanguage =
|
const language: CodeNodeEditorLanguage =
|
||||||
this.getNode()?.typeVersion === 2
|
node.typeVersion === 2
|
||||||
? (this.getNodeParameter('language', 0) as CodeNodeEditorLanguage)
|
? (this.getNodeParameter('language', 0) as CodeNodeEditorLanguage)
|
||||||
: 'javaScript';
|
: 'javaScript';
|
||||||
const codeParameterName = language === 'python' ? 'pythonCode' : 'jsCode';
|
const codeParameterName = language === 'python' ? 'pythonCode' : 'jsCode';
|
||||||
@@ -107,16 +108,16 @@ export class Code implements INodeType {
|
|||||||
context.item = context.$input.item;
|
context.item = context.$input.item;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (language === 'python') {
|
const Sandbox = language === 'python' ? PythonSandbox : JavaScriptSandbox;
|
||||||
context.printOverwrite = workflowMode === 'manual' ? this.sendMessageToUI : null;
|
const sandbox = new Sandbox(context, code, index, this.helpers);
|
||||||
return new PythonSandbox(context, code, index, this.helpers);
|
sandbox.on(
|
||||||
} else {
|
'output',
|
||||||
const sandbox = new JavaScriptSandbox(context, code, index, workflowMode, this.helpers);
|
workflowMode === 'manual'
|
||||||
if (workflowMode === 'manual') {
|
? this.sendMessageToUI
|
||||||
sandbox.vm.on('console.log', this.sendMessageToUI);
|
: (...args) =>
|
||||||
}
|
console.log(`[Workflow "${this.getWorkflow().id}"][Node "${node.name}"]`, ...args),
|
||||||
return sandbox;
|
);
|
||||||
}
|
return sandbox;
|
||||||
};
|
};
|
||||||
|
|
||||||
// ----------------------------------
|
// ----------------------------------
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import type { NodeVMOptions } from 'vm2';
|
|
||||||
import { NodeVM, makeResolverFromLegacyOptions } from 'vm2';
|
import { NodeVM, makeResolverFromLegacyOptions } from 'vm2';
|
||||||
import type { IExecuteFunctions, INodeExecutionData, WorkflowExecuteMode } from 'n8n-workflow';
|
import type { IExecuteFunctions, INodeExecutionData } from 'n8n-workflow';
|
||||||
|
|
||||||
import { ValidationError } from './ValidationError';
|
import { ValidationError } from './ValidationError';
|
||||||
import { ExecutionError } from './ExecutionError';
|
import { ExecutionError } from './ExecutionError';
|
||||||
@@ -20,23 +19,13 @@ export const vmResolver = makeResolverFromLegacyOptions({
|
|||||||
builtin: builtIn?.split(',') ?? [],
|
builtin: builtIn?.split(',') ?? [],
|
||||||
});
|
});
|
||||||
|
|
||||||
const getSandboxOptions = (
|
|
||||||
context: SandboxContext,
|
|
||||||
workflowMode: WorkflowExecuteMode,
|
|
||||||
): NodeVMOptions => ({
|
|
||||||
console: workflowMode === 'manual' ? 'redirect' : 'inherit',
|
|
||||||
sandbox: context,
|
|
||||||
require: vmResolver,
|
|
||||||
});
|
|
||||||
|
|
||||||
export class JavaScriptSandbox extends Sandbox {
|
export class JavaScriptSandbox extends Sandbox {
|
||||||
readonly vm: NodeVM;
|
private readonly vm: NodeVM;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
context: SandboxContext,
|
context: SandboxContext,
|
||||||
private jsCode: string,
|
private jsCode: string,
|
||||||
itemIndex: number | undefined,
|
itemIndex: number | undefined,
|
||||||
workflowMode: WorkflowExecuteMode,
|
|
||||||
helpers: IExecuteFunctions['helpers'],
|
helpers: IExecuteFunctions['helpers'],
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
@@ -49,7 +38,13 @@ export class JavaScriptSandbox extends Sandbox {
|
|||||||
itemIndex,
|
itemIndex,
|
||||||
helpers,
|
helpers,
|
||||||
);
|
);
|
||||||
this.vm = new NodeVM(getSandboxOptions(context, workflowMode));
|
this.vm = new NodeVM({
|
||||||
|
console: 'redirect',
|
||||||
|
sandbox: context,
|
||||||
|
require: vmResolver,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.vm.on('console.log', (...args: unknown[]) => this.emit('output', ...args));
|
||||||
}
|
}
|
||||||
|
|
||||||
async runCodeAllItems(): Promise<INodeExecutionData[]> {
|
async runCodeAllItems(): Promise<INodeExecutionData[]> {
|
||||||
|
|||||||
@@ -67,10 +67,7 @@ export class PythonSandbox extends Sandbox {
|
|||||||
globalsDict.set(key, value);
|
globalsDict.set(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
await pyodide.runPythonAsync(`
|
pyodide.setStdout({ batched: (str) => this.emit('output', str) });
|
||||||
if 'printOverwrite' in globals():
|
|
||||||
print = printOverwrite
|
|
||||||
`);
|
|
||||||
|
|
||||||
const runCode = `
|
const runCode = `
|
||||||
async def __main():
|
async def __main():
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { EventEmitter } from 'events';
|
||||||
import { ValidationError } from './ValidationError';
|
import { ValidationError } from './ValidationError';
|
||||||
import { isObject } from './utils';
|
import { isObject } from './utils';
|
||||||
|
|
||||||
@@ -31,12 +32,14 @@ export function getSandboxContext(this: IExecuteFunctions, index: number): Sandb
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export abstract class Sandbox {
|
export abstract class Sandbox extends EventEmitter {
|
||||||
constructor(
|
constructor(
|
||||||
private textKeys: SandboxTextKeys,
|
private textKeys: SandboxTextKeys,
|
||||||
protected itemIndex: number | undefined,
|
protected itemIndex: number | undefined,
|
||||||
protected helpers: IExecuteFunctions['helpers'],
|
protected helpers: IExecuteFunctions['helpers'],
|
||||||
) {}
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
abstract runCodeAllItems(): Promise<INodeExecutionData[]>;
|
abstract runCodeAllItems(): Promise<INodeExecutionData[]>;
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ describe('Test Code Node', () => {
|
|||||||
describe('Code Node unit test', () => {
|
describe('Code Node unit test', () => {
|
||||||
const node = new Code();
|
const node = new Code();
|
||||||
const thisArg = mock<IExecuteFunctions>({
|
const thisArg = mock<IExecuteFunctions>({
|
||||||
|
getNode: () => mock(),
|
||||||
helpers: { normalizeItems },
|
helpers: { normalizeItems },
|
||||||
prepareOutputData: NodeHelpers.prepareOutputData,
|
prepareOutputData: NodeHelpers.prepareOutputData,
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user