UNPKG

@directus/api

Version:

Directus is a real-time API and App dashboard for managing SQL database content

53 lines (51 loc) 2.17 kB
import { createRequire } from "node:module"; import { sieveFunctions } from "@directus/utils"; import { defineOperationApi } from "@directus/extensions"; //#region src/operations/exec/index.ts const ivm = createRequire(import.meta.url)("isolated-vm"); /** * A helper for making the logs prettier. * The logger prints arrays with their indices but this looks "bad" when you have only one argument. */ function unpackArgs(args) { return args.length === 1 ? args[0] : args; } var exec_default = defineOperationApi({ id: "exec", handler: async ({ code }, { data, env, logger }) => { const allowedEnv = data["$env"] ?? {}; const isolateSizeMb = env["FLOWS_RUN_SCRIPT_MAX_MEMORY"]; const scriptTimeoutMs = env["FLOWS_RUN_SCRIPT_TIMEOUT"]; const isolate = new ivm.Isolate({ memoryLimit: isolateSizeMb }); const context = isolate.createContextSync(); const jail = context.global; jail.setSync("global", jail.derefInto()); jail.setSync("process", { env: allowedEnv }, { copy: true }); jail.setSync("module", { exports: null }, { copy: true }); jail.setSync("console", { log: new ivm.Callback((...args) => logger.info(unpackArgs(args)), { sync: true }), info: new ivm.Callback((...args) => logger.info(unpackArgs(args)), { sync: true }), warn: new ivm.Callback((...args) => logger.warn(unpackArgs(args)), { sync: true }), error: new ivm.Callback((...args) => logger.error(unpackArgs(args)), { sync: true }), trace: new ivm.Callback((...args) => logger.trace(unpackArgs(args)), { sync: true }), debug: new ivm.Callback((...args) => logger.debug(unpackArgs(args)), { sync: true }) }, { copy: true }); await context.eval(code, { timeout: scriptTimeoutMs }); const inputData = new ivm.ExternalCopy({ data: sieveFunctions(data) }); const resultRef = await context.evalClosure(`return module.exports($0.data)`, [inputData.copyInto()], { result: { reference: true, promise: true }, timeout: scriptTimeoutMs }); const result = await resultRef.copy(); resultRef.release(); inputData.release(); context.release(); isolate.dispose(); return result; } }); //#endregion export { exec_default as default };