@directus/api
Version:
Directus is a real-time API and App dashboard for managing SQL database content
53 lines (51 loc) • 2.17 kB
JavaScript
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 };