feat: add custom script monitor provider
This commit is contained in:
parent
e0e338f819
commit
8dfa679127
@ -52,6 +52,7 @@
|
||||
"filesize": "^10.0.12",
|
||||
"fs-extra": "^11.1.1",
|
||||
"is-localhost-ip": "^2.0.0",
|
||||
"isolated-vm": "^4.6.0",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"lodash": "^4.17.21",
|
||||
"lodash-es": "^4.17.21",
|
||||
|
@ -91,6 +91,9 @@ dependencies:
|
||||
is-localhost-ip:
|
||||
specifier: ^2.0.0
|
||||
version: 2.0.0
|
||||
isolated-vm:
|
||||
specifier: ^4.6.0
|
||||
version: 4.6.0
|
||||
jsonwebtoken:
|
||||
specifier: ^9.0.2
|
||||
version: 9.0.2
|
||||
@ -3184,7 +3187,6 @@ packages:
|
||||
buffer: 5.7.1
|
||||
inherits: 2.0.4
|
||||
readable-stream: 3.6.2
|
||||
dev: true
|
||||
|
||||
/bl@5.1.0:
|
||||
resolution: {integrity: sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==}
|
||||
@ -3430,6 +3432,10 @@ packages:
|
||||
fsevents: 2.3.3
|
||||
dev: true
|
||||
|
||||
/chownr@1.1.4:
|
||||
resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==}
|
||||
dev: false
|
||||
|
||||
/chownr@2.0.0:
|
||||
resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==}
|
||||
engines: {node: '>=10'}
|
||||
@ -4181,7 +4187,6 @@ packages:
|
||||
engines: {node: '>=10'}
|
||||
dependencies:
|
||||
mimic-response: 3.1.0
|
||||
dev: true
|
||||
|
||||
/deep-equal@1.1.1:
|
||||
resolution: {integrity: sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==}
|
||||
@ -4197,7 +4202,6 @@ packages:
|
||||
/deep-extend@0.6.0:
|
||||
resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==}
|
||||
engines: {node: '>=4.0.0'}
|
||||
dev: true
|
||||
|
||||
/default-browser-id@3.0.0:
|
||||
resolution: {integrity: sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==}
|
||||
@ -4302,6 +4306,11 @@ packages:
|
||||
resolution: {integrity: sha512-53rsFbGdwMwlF7qvCt0ypLM5V5/Mbl0szB7GPN8y9NCcbknYOeVVXdrXEq+90IwAfrrzt6Hd+u2E2ntakICU8w==}
|
||||
dev: false
|
||||
|
||||
/detect-libc@2.0.2:
|
||||
resolution: {integrity: sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==}
|
||||
engines: {node: '>=8'}
|
||||
dev: false
|
||||
|
||||
/devtools-protocol@0.0.1179426:
|
||||
resolution: {integrity: sha512-KKC7IGwdOr7u9kTGgjUvGTov/z1s2H7oHi3zKCdR9eSDyCPia5CBi4aRhtp7d8uR7l0GS5UTDw3TjKGu5CqINg==}
|
||||
dev: false
|
||||
@ -4683,6 +4692,11 @@ packages:
|
||||
strip-final-newline: 3.0.0
|
||||
dev: true
|
||||
|
||||
/expand-template@2.0.3:
|
||||
resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==}
|
||||
engines: {node: '>=6'}
|
||||
dev: false
|
||||
|
||||
/express-async-errors@3.1.1(express@4.18.2):
|
||||
resolution: {integrity: sha512-h6aK1da4tpqWSbyCa3FxB/V6Ehd4EEB15zyQq9qe75OZBp0krinNKuH4rAY+S/U/2I36vdLAUFSjQJ+TFmODng==}
|
||||
peerDependencies:
|
||||
@ -4990,7 +5004,6 @@ packages:
|
||||
|
||||
/fs-constants@1.0.0:
|
||||
resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==}
|
||||
dev: true
|
||||
|
||||
/fs-extra@11.1.1:
|
||||
resolution: {integrity: sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==}
|
||||
@ -5135,6 +5148,10 @@ packages:
|
||||
git-up: 7.0.0
|
||||
dev: true
|
||||
|
||||
/github-from-package@0.0.0:
|
||||
resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==}
|
||||
dev: false
|
||||
|
||||
/gl-matrix@3.4.3:
|
||||
resolution: {integrity: sha512-wcCp8vu8FT22BnvKVPjXa/ICBWRq/zjFfdofZy1WSpQZpphblv12/bOQLBC1rMM7SGOFS9ltVmKOHil5+Ml7gA==}
|
||||
dev: false
|
||||
@ -5516,7 +5533,6 @@ packages:
|
||||
|
||||
/ini@1.3.8:
|
||||
resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==}
|
||||
dev: true
|
||||
|
||||
/ini@2.0.0:
|
||||
resolution: {integrity: sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==}
|
||||
@ -5859,6 +5875,14 @@ packages:
|
||||
/isexe@2.0.0:
|
||||
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
|
||||
|
||||
/isolated-vm@4.6.0:
|
||||
resolution: {integrity: sha512-MEnfC/54q5PED3VJ9UJYJPOlU6mYFHS3ivR9E8yeNNBEFRFUNBnY0xO4Rj3D/SOtFKPNmsQp9NWUYSKZqAoZiA==}
|
||||
engines: {node: '>=16.0.0'}
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
prebuild-install: 7.1.1
|
||||
dev: false
|
||||
|
||||
/issue-parser@6.0.0:
|
||||
resolution: {integrity: sha512-zKa/Dxq2lGsBIXQ7CUZWTHfvxPC2ej0KfO7fIPqLlHB9J2hJ7rGhZ5rilhuufylr4RXYPzJUeFjKxz305OsNlA==}
|
||||
engines: {node: '>=10.13'}
|
||||
@ -6389,7 +6413,6 @@ packages:
|
||||
/mimic-response@3.1.0:
|
||||
resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==}
|
||||
engines: {node: '>=10'}
|
||||
dev: true
|
||||
|
||||
/mimic-response@4.0.0:
|
||||
resolution: {integrity: sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==}
|
||||
@ -6557,6 +6580,10 @@ packages:
|
||||
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
||||
hasBin: true
|
||||
|
||||
/napi-build-utils@1.0.2:
|
||||
resolution: {integrity: sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==}
|
||||
dev: false
|
||||
|
||||
/negotiator@0.6.3:
|
||||
resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==}
|
||||
engines: {node: '>= 0.6'}
|
||||
@ -6582,6 +6609,13 @@ packages:
|
||||
type-fest: 2.19.0
|
||||
dev: true
|
||||
|
||||
/node-abi@3.52.0:
|
||||
resolution: {integrity: sha512-JJ98b02z16ILv7859irtXn4oUaFWADtvkzy2c0IAatNVX2Mc9Yoh8z6hZInn3QwvMEYhHuQloYi+TTQy67SIdQ==}
|
||||
engines: {node: '>=10'}
|
||||
dependencies:
|
||||
semver: 7.5.4
|
||||
dev: false
|
||||
|
||||
/node-domexception@1.0.0:
|
||||
resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==}
|
||||
engines: {node: '>=10.5.0'}
|
||||
@ -7260,6 +7294,25 @@ packages:
|
||||
resolution: {integrity: sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ==}
|
||||
dev: false
|
||||
|
||||
/prebuild-install@7.1.1:
|
||||
resolution: {integrity: sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==}
|
||||
engines: {node: '>=10'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
detect-libc: 2.0.2
|
||||
expand-template: 2.0.3
|
||||
github-from-package: 0.0.0
|
||||
minimist: 1.2.8
|
||||
mkdirp-classic: 0.5.3
|
||||
napi-build-utils: 1.0.2
|
||||
node-abi: 3.52.0
|
||||
pump: 3.0.0
|
||||
rc: 1.2.8
|
||||
simple-get: 4.0.1
|
||||
tar-fs: 2.1.1
|
||||
tunnel-agent: 0.6.0
|
||||
dev: false
|
||||
|
||||
/prettier@2.8.8:
|
||||
resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==}
|
||||
engines: {node: '>=10.13.0'}
|
||||
@ -8108,7 +8161,6 @@ packages:
|
||||
ini: 1.3.8
|
||||
minimist: 1.2.8
|
||||
strip-json-comments: 2.0.1
|
||||
dev: true
|
||||
|
||||
/react-color@2.17.1(react@18.2.0):
|
||||
resolution: {integrity: sha512-S+I6TkUKJaqfALLkAIfiCZ/MANQyy7dKkf7g9ZU5GTUy2rf8c2Rx62otyvADAviWR+6HRkzdf2vL1Qvz9goCLQ==}
|
||||
@ -8791,6 +8843,18 @@ packages:
|
||||
engines: {node: '>=14'}
|
||||
dev: true
|
||||
|
||||
/simple-concat@1.0.1:
|
||||
resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==}
|
||||
dev: false
|
||||
|
||||
/simple-get@4.0.1:
|
||||
resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==}
|
||||
dependencies:
|
||||
decompress-response: 6.0.0
|
||||
once: 1.4.0
|
||||
simple-concat: 1.0.1
|
||||
dev: false
|
||||
|
||||
/simple-swizzle@0.2.2:
|
||||
resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==}
|
||||
dependencies:
|
||||
@ -9132,7 +9196,6 @@ packages:
|
||||
/strip-json-comments@2.0.1:
|
||||
resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: true
|
||||
|
||||
/stylis@4.3.0:
|
||||
resolution: {integrity: sha512-E87pIogpwUsUwXw7dNyU4QDjdgVMy52m+XEOPEKUn161cCzWjjhPSQhByfd1CcNvrOLnXQ6OnnZDwnJrz/Z4YQ==}
|
||||
@ -9253,6 +9316,15 @@ packages:
|
||||
through: 2.3.8
|
||||
dev: false
|
||||
|
||||
/tar-fs@2.1.1:
|
||||
resolution: {integrity: sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==}
|
||||
dependencies:
|
||||
chownr: 1.1.4
|
||||
mkdirp-classic: 0.5.3
|
||||
pump: 3.0.0
|
||||
tar-stream: 2.2.0
|
||||
dev: false
|
||||
|
||||
/tar-fs@3.0.4:
|
||||
resolution: {integrity: sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w==}
|
||||
dependencies:
|
||||
@ -9270,7 +9342,6 @@ packages:
|
||||
fs-constants: 1.0.0
|
||||
inherits: 2.0.4
|
||||
readable-stream: 3.6.2
|
||||
dev: true
|
||||
|
||||
/tar-stream@3.1.6:
|
||||
resolution: {integrity: sha512-B/UyjYwPpMBv+PaFSWAmtYjwdrlEaZQEhMIBFNC5oEG8lpiW8XjcSdmEaClj28ArfKScKHs2nshz3k2le6crsg==}
|
||||
@ -9525,6 +9596,12 @@ packages:
|
||||
/tslib@2.6.2:
|
||||
resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==}
|
||||
|
||||
/tunnel-agent@0.6.0:
|
||||
resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==}
|
||||
dependencies:
|
||||
safe-buffer: 5.2.1
|
||||
dev: false
|
||||
|
||||
/type-fest@0.16.0:
|
||||
resolution: {integrity: sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==}
|
||||
engines: {node: '>=10'}
|
||||
|
24
src/client/components/monitor/provider/custom.tsx
Normal file
24
src/client/components/monitor/provider/custom.tsx
Normal file
@ -0,0 +1,24 @@
|
||||
import { Form, Input } from 'antd';
|
||||
import React from 'react';
|
||||
import { MonitorProvider } from './types';
|
||||
|
||||
export const MonitorCustom: React.FC = React.memo(() => {
|
||||
return (
|
||||
<>
|
||||
<Form.Item
|
||||
label="Script JS Code"
|
||||
name={['payload', 'code']}
|
||||
rules={[{ required: true }]}
|
||||
>
|
||||
<Input.TextArea placeholder="return 1" rows={10} />
|
||||
</Form.Item>
|
||||
</>
|
||||
);
|
||||
});
|
||||
MonitorCustom.displayName = 'MonitorCustom';
|
||||
|
||||
export const customProvider: MonitorProvider = {
|
||||
label: 'Custom',
|
||||
name: 'custom',
|
||||
form: MonitorCustom,
|
||||
};
|
@ -4,11 +4,13 @@ import { pingProvider } from './ping';
|
||||
import { httpProvider } from './http';
|
||||
import { MonitorProvider } from './types';
|
||||
import { openaiProvider } from './openai';
|
||||
import { customProvider } from './custom';
|
||||
|
||||
export const monitorProviders: MonitorProvider[] = [
|
||||
pingProvider, // ping
|
||||
httpProvider, // http
|
||||
openaiProvider, // http
|
||||
customProvider, // custom node script
|
||||
];
|
||||
|
||||
export function getMonitorProvider(type: string) {
|
||||
|
46
src/server/model/monitor/provider/custom.ts
Normal file
46
src/server/model/monitor/provider/custom.ts
Normal file
@ -0,0 +1,46 @@
|
||||
import { MonitorProvider } from './type';
|
||||
import ivm from 'isolated-vm';
|
||||
import { buildSandbox, environmentScript } from '../../../utils/sandbox';
|
||||
import { env } from '../../../utils/env';
|
||||
|
||||
export const custom: MonitorProvider<{
|
||||
code: string;
|
||||
}> = {
|
||||
run: async (monitor) => {
|
||||
if (typeof monitor.payload !== 'object') {
|
||||
throw new Error('monitor.payload should be object');
|
||||
}
|
||||
|
||||
const { code } = monitor.payload;
|
||||
|
||||
const res = await runCodeInVM(code);
|
||||
|
||||
if (typeof res !== 'number') {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return res;
|
||||
},
|
||||
};
|
||||
|
||||
async function runCodeInVM(_code: string) {
|
||||
const isolate = new ivm.Isolate({ memoryLimit: env.sandboxMemoryLimit });
|
||||
|
||||
const code = `${environmentScript}\n\n;(async () => {${_code}})()`;
|
||||
|
||||
const [context, script] = await Promise.all([
|
||||
isolate.createContext(),
|
||||
isolate.compileScript(code),
|
||||
]);
|
||||
|
||||
buildSandbox(context);
|
||||
|
||||
const res = await script.run(context, {
|
||||
promise: true,
|
||||
});
|
||||
|
||||
context.release();
|
||||
script.release();
|
||||
|
||||
return res;
|
||||
}
|
@ -2,9 +2,11 @@ import { http } from './http';
|
||||
import { ping } from './ping';
|
||||
import { openai } from './openai';
|
||||
import type { MonitorProvider } from './type';
|
||||
import { custom } from './custom';
|
||||
|
||||
export const monitorProviders: Record<string, MonitorProvider<any>> = {
|
||||
ping,
|
||||
http,
|
||||
openai,
|
||||
custom,
|
||||
};
|
||||
|
@ -3,6 +3,9 @@ export const env = {
|
||||
allowOpenapi: checkEnvTrusty(process.env.ALLOW_OPENAPI),
|
||||
websiteId: process.env.WEBSITE_ID,
|
||||
serverUrl: process.env.SERVER_URL, // example: https://tianji.example.com
|
||||
sandboxMemoryLimit: process.env.SANDBOX_MEMORY_LIMIT
|
||||
? Number(process.env.SANDBOX_MEMORY_LIMIT)
|
||||
: 16, // MB
|
||||
};
|
||||
|
||||
export function checkEnvTrusty(env: string | undefined): boolean {
|
||||
|
174
src/server/utils/sandbox.ts
Normal file
174
src/server/utils/sandbox.ts
Normal file
@ -0,0 +1,174 @@
|
||||
import axios, { AxiosRequestConfig } from 'axios';
|
||||
import EventEmitter from 'events';
|
||||
import ivm, { Context } from 'isolated-vm';
|
||||
|
||||
function isTransferable(data: any): data is ivm.Transferable {
|
||||
const dataType = typeof data;
|
||||
|
||||
if (data === ivm) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (
|
||||
['null', 'undefined', 'string', 'number', 'boolean', 'function'].includes(
|
||||
dataType
|
||||
)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (dataType !== 'object') {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
data instanceof ivm.Isolate ||
|
||||
data instanceof ivm.Context ||
|
||||
data instanceof ivm.Script ||
|
||||
data instanceof ivm.ExternalCopy ||
|
||||
data instanceof ivm.Callback ||
|
||||
data instanceof ivm.Reference
|
||||
);
|
||||
}
|
||||
|
||||
function proxyObject(
|
||||
obj: Record<string, any>,
|
||||
forbiddenKeys: string[] = []
|
||||
): Record<string, any> {
|
||||
return copyObject({
|
||||
isProxy: true,
|
||||
get: (key: string) => {
|
||||
if (forbiddenKeys.includes(key)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const value = obj[key];
|
||||
|
||||
if (typeof value === 'function') {
|
||||
return new ivm.Reference(async (...args: any[]) => {
|
||||
const result = (obj[key] as any)(...args);
|
||||
|
||||
if (result && result instanceof Promise) {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
const awaitedResult = await result;
|
||||
resolve(makeTransferable(awaitedResult));
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return makeTransferable(result);
|
||||
});
|
||||
}
|
||||
|
||||
return makeTransferable(value);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// Semi-transferable data can be copied with an ivm.ExternalCopy without needing any manipulation.
|
||||
function isSemiTransferable(data: any) {
|
||||
return data instanceof ArrayBuffer;
|
||||
}
|
||||
|
||||
export function copyObject(
|
||||
obj: Record<string, any> | any[]
|
||||
): Record<string, any> | any[] {
|
||||
if (Array.isArray(obj)) {
|
||||
return obj.map((data) => copyData(data));
|
||||
}
|
||||
|
||||
if (obj instanceof Response) {
|
||||
return proxyObject(obj, ['clone']);
|
||||
}
|
||||
|
||||
if (isSemiTransferable(obj)) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
if (typeof obj[Symbol.iterator as any] === 'function') {
|
||||
return copyObject(Array.from(obj as any));
|
||||
}
|
||||
|
||||
if (obj instanceof EventEmitter) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const keys = Object.keys(obj);
|
||||
|
||||
return {
|
||||
...Object.fromEntries(
|
||||
keys.map((key) => {
|
||||
const data = obj[key];
|
||||
|
||||
if (typeof data === 'function') {
|
||||
return [key, new ivm.Callback((...args: any[]) => obj[key](...args))];
|
||||
}
|
||||
|
||||
return [key, copyData(data)];
|
||||
})
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
function copyData<T extends ivm.Transferable | Record<string, any> | any[]>(
|
||||
data: T
|
||||
) {
|
||||
return isTransferable(data) ? data : copyObject(data);
|
||||
}
|
||||
|
||||
function makeTransferable(data: any) {
|
||||
return isTransferable(data)
|
||||
? data
|
||||
: new ivm.ExternalCopy(copyObject(data)).copyInto();
|
||||
}
|
||||
|
||||
export function buildSandbox(context: Context) {
|
||||
const jail = context.global;
|
||||
jail.setSync('global', jail.derefInto());
|
||||
jail.setSync('ivm', ivm);
|
||||
jail.setSync('console', makeTransferable(console));
|
||||
jail.setSync(
|
||||
'_request',
|
||||
new ivm.Reference(async (config: AxiosRequestConfig) => {
|
||||
const result = await axios.request(config);
|
||||
|
||||
return makeTransferable({
|
||||
data: result.data,
|
||||
status: result.status,
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
export const environmentScript = `
|
||||
const reproxy = (reference) => {
|
||||
return new Proxy(reference, {
|
||||
get(target, p, receiver) {
|
||||
if (target !== reference || p === 'then') {
|
||||
return Reflect.get(target, p, receiver);
|
||||
}
|
||||
|
||||
const data = reference.get(p);
|
||||
|
||||
if (typeof data === 'object' && data instanceof ivm.Reference && data.typeof === 'function') {
|
||||
return (...args) => data.apply(undefined, args, { arguments: { copy: true }, result: { promise: true } });
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const request = async (...args) => {
|
||||
const result = await _request.apply(undefined, args, { arguments: { copy: true }, result: { promise: true } });
|
||||
|
||||
if (result && typeof result === 'object' && result.isProxy) {
|
||||
return reproxy(result);
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
`;
|
@ -53,7 +53,7 @@ export function initSocketio(httpServer: HTTPServer) {
|
||||
}
|
||||
|
||||
socket.onAny((eventName, eventData, callback) => {
|
||||
console.log('[Socket] receive:', { eventName, eventData });
|
||||
// console.log('[Socket] receive:', { eventName, eventData });
|
||||
socketEventBus.emit(eventName, eventData, socket, callback);
|
||||
});
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user