Files
isolator-js/server.js
Peter Stockings ab04b77526 Initial commit
2023-12-14 21:46:49 +11:00

175 lines
3.9 KiB
JavaScript

const express = require("express");
const bodyParser = require("body-parser");
const { VM } = require("vm2");
const { JSDOM } = require("jsdom");
const app = express();
const port = 5000;
app.use(bodyParser.json());
app.use((req, res, next) => {
res.header("Access-Control-Allow-Origin", "*");
res.header(
"Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept"
);
next();
});
const TIMEOUT_MS = 5000; // Set timeout to 5000 milliseconds (5 seconds)
async function executeUserCode(code, requestObject, timeout = TIMEOUT_MS) {
const logs = [];
const States = {
SUCCESS: "SUCCESS",
NOT_A_FUNCTION: "NOT_A_FUNCTION",
SCRIPT_ERROR: "ERROR",
TIMEOUT: "TIMEOUT",
};
const HTTP_STATUS_CODES = {
OK: 200,
BAD_REQUEST: 400,
UNAUTHORIZED: 401,
FORBIDDEN: 403,
NOT_FOUND: 404,
INTERNAL_SERVER_ERROR: 500,
BAD_GATEWAY: 502,
SERVICE_UNAVAILABLE: 503,
GATEWAY_TIMEOUT: 504,
};
const Response = (
body = "",
headers = {},
status = HTTP_STATUS_CODES.OK
) => ({
body,
status,
headers,
});
const JsonResponse = (
body = {},
headers = {},
status = HTTP_STATUS_CODES.OK
) => ({
status,
body: JSON.stringify(body),
headers: {
"Content-Type": "application/json",
...headers,
},
});
const HtmlResponse = (
body = "",
headers = {},
status = HTTP_STATUS_CODES.OK
) => ({
status,
body,
headers: {
"Content-Type": "text/html",
...headers,
},
});
const TextResponse = (
body = "",
headers = {},
status = HTTP_STATUS_CODES.OK
) => ({
status,
body,
headers: {
"Content-Type": "text/plain",
...headers,
},
});
const Result = (state, result) => {
console.log(`Status: ${state}`);
console.log(`Result: ${JSON.stringify(result, null, 2)}`);
console.log(`Logs: ${JSON.stringify(logs, null, 2)}`);
console.log(`\n`);
return {
state,
result,
logs,
};
};
// Dynamically import node-fetch
const fetch = await import("node-fetch").then((module) => module.default);
const vm = new VM({
timeout,
sandbox: {
fetch,
parseHTML: async (html) => {
const dom = new JSDOM(html);
return dom.window.document;
},
console: {
log: (...args) => {
logs.push(args);
console.log(...args);
},
error: (...args) => {
logs.push(args);
console.error(...args);
},
},
requestObject,
HTTP_STATUS_CODES,
Response,
JsonResponse,
HtmlResponse,
TextResponse,
},
require: {
external: true,
},
});
try {
// If the user code is a function that needs to be invoked
const userFunction = vm.run(code);
if (typeof userFunction === "function") {
console.log(`Function: ${code}`);
let requestObjectString = JSON.stringify(requestObject, null, 2);
console.log(`Request: ${requestObjectString}`);
// Call the user function with request object
let result = await userFunction(requestObject);
return Result(States.SUCCESS, result);
} else {
return Result(States.NOT_A_FUNCTION, null);
}
} catch (err) {
if (err.message === "Script execution timed out.") {
return Result(States.TIMEOUT, null);
} else {
return Result(States.SCRIPT_ERROR, err.message || err);
}
}
}
app.post("/execute", async (req, res) => {
const { code, request } = req.body;
const timeout = req.query.timeout || TIMEOUT_MS;
const result = await executeUserCode(code, request, timeout);
res.send(result);
});
app.listen(port, () => {
console.log(`Server listening on port: ${port}`);
});