mirror of
https://github.com/alibaba/anyproxy.git
synced 2025-04-24 08:41:31 +00:00
feat: refact wsServer, wsServerMgr, ruleLoader, systemProxyMgr into TS
This commit is contained in:
parent
4497160284
commit
bcb7451579
@ -3,7 +3,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
const ruleLoader = require('../dist/ruleLoader');
|
const ruleLoader = require('../dist/ruleLoader');
|
||||||
const logUtil = require('../dist/log');
|
const logUtil = require('../dist/log').default;
|
||||||
const AnyProxy = require('../dist/proxy');
|
const AnyProxy = require('../dist/proxy');
|
||||||
|
|
||||||
module.exports = function startServer(program) {
|
module.exports = function startServer(program) {
|
||||||
|
@ -9,11 +9,10 @@ import * as color from 'colorful';
|
|||||||
import * as WebSocket from 'ws';
|
import * as WebSocket from 'ws';
|
||||||
import * as constants from 'constants';
|
import * as constants from 'constants';
|
||||||
import * as AsyncTask from 'async-task-mgr';
|
import * as AsyncTask from 'async-task-mgr';
|
||||||
import Recorder from './recorder';
|
|
||||||
import certMgr from './certMgr';
|
import certMgr from './certMgr';
|
||||||
import logUtil from './log';
|
import logUtil from './log';
|
||||||
import util from './util';
|
import util from './util';
|
||||||
import * as wsServerMgr from './wsServerMgr';
|
import wsServerMgr from './wsServerMgr';
|
||||||
import * as co from 'co';
|
import * as co from 'co';
|
||||||
|
|
||||||
// // manage https servers
|
// // manage https servers
|
||||||
@ -31,8 +30,7 @@ import * as co from 'co';
|
|||||||
// asyncTask = require('async-task-mgr');
|
// asyncTask = require('async-task-mgr');
|
||||||
|
|
||||||
declare type THttpsRequestHanlder = (req: http.IncomingMessage, userRes: http.ServerResponse) => void;
|
declare type THttpsRequestHanlder = (req: http.IncomingMessage, userRes: http.ServerResponse) => void;
|
||||||
declare type TWsRequestHandler =
|
declare type TWsRequestHandler = (wsClient: WebSocket, wsReq: http.IncomingMessage) => void;
|
||||||
(userRule: AnyProxyRule, recorder: Recorder, wsClient: WebSocket, wsReq: http.IncomingMessage) => void;
|
|
||||||
|
|
||||||
const createSecureContext = tls.createSecureContext || (crypto as any).createSecureContext;
|
const createSecureContext = tls.createSecureContext || (crypto as any).createSecureContext;
|
||||||
// using sni to avoid multiple ports
|
// using sni to avoid multiple ports
|
||||||
|
44
lib/log.ts
44
lib/log.ts
@ -1,5 +1,5 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
/*tslint:disable:no-console */
|
||||||
import * as color from 'colorful';
|
import * as color from 'colorful';
|
||||||
import util from './util';
|
import util from './util';
|
||||||
|
|
||||||
@ -12,7 +12,7 @@ enum LogLevelMap {
|
|||||||
rule_error = 2,
|
rule_error = 2,
|
||||||
warn = 3,
|
warn = 3,
|
||||||
debug = 4,
|
debug = 4,
|
||||||
};
|
}
|
||||||
|
|
||||||
function setPrintStatus(status: boolean): void {
|
function setPrintStatus(status: boolean): void {
|
||||||
ifPrint = !!status;
|
ifPrint = !!status;
|
||||||
@ -22,7 +22,7 @@ function setLogLevel(level: string): void {
|
|||||||
logLevel = parseInt(level, 10);
|
logLevel = parseInt(level, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
function printLog(content: string, type?: LogLevelMap) {
|
function printLog(content: string, type?: LogLevelMap): void {
|
||||||
if (!ifPrint) {
|
if (!ifPrint) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -75,35 +75,34 @@ function printLog(content: string, type?: LogLevelMap) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports.printLog = printLog;
|
function debug(content: string): void {
|
||||||
|
|
||||||
function debug (content): void {
|
|
||||||
printLog(content, LogLevelMap.debug);
|
printLog(content, LogLevelMap.debug);
|
||||||
};
|
}
|
||||||
|
|
||||||
function info (content): void {
|
function info(content: string): void {
|
||||||
printLog(content, LogLevelMap.tip);
|
printLog(content, LogLevelMap.tip);
|
||||||
};
|
}
|
||||||
|
|
||||||
function warn (content) {
|
function warn(content: string): void {
|
||||||
printLog(content, LogLevelMap.warn);
|
printLog(content, LogLevelMap.warn);
|
||||||
};
|
}
|
||||||
|
|
||||||
function error (content) {
|
function error(content: string): void {
|
||||||
printLog(content, LogLevelMap.system_error);
|
printLog(content, LogLevelMap.system_error);
|
||||||
};
|
}
|
||||||
|
|
||||||
function ruleError (content) {
|
function ruleError(content: string): void {
|
||||||
printLog(content, LogLevelMap.rule_error);
|
printLog(content, LogLevelMap.rule_error);
|
||||||
};
|
}
|
||||||
|
|
||||||
module.exports.setPrintStatus = setPrintStatus;
|
// module.exports.setPrintStatus = setPrintStatus;
|
||||||
module.exports.setLogLevel = setLogLevel;
|
// module.exports.setLogLevel = setLogLevel;
|
||||||
module.exports.T_TIP = LogLevelMap.tip;
|
// module.exports.T_TIP = LogLevelMap.tip;
|
||||||
module.exports.T_ERR = LogLevelMap.system_error;
|
// module.exports.T_ERR = LogLevelMap.system_error;
|
||||||
module.exports.T_RULE_ERROR = LogLevelMap.rule_error;
|
// module.exports.T_RULE_ERROR = LogLevelMap.rule_error;
|
||||||
module.exports.T_WARN = LogLevelMap.warn;
|
// module.exports.T_WARN = LogLevelMap.warn;
|
||||||
module.exports.T_DEBUG = LogLevelMap.debug;
|
// module.exports.T_DEBUG = LogLevelMap.debug;
|
||||||
|
// module.exports.printLog = printLog;
|
||||||
|
|
||||||
const LogUtil = {
|
const LogUtil = {
|
||||||
setPrintStatus,
|
setPrintStatus,
|
||||||
@ -121,5 +120,4 @@ const LogUtil = {
|
|||||||
T_DEBUG: LogLevelMap.debug,
|
T_DEBUG: LogLevelMap.debug,
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.LogUtil = LogUtil;
|
|
||||||
export default LogUtil;
|
export default LogUtil;
|
||||||
|
@ -6,12 +6,12 @@ const http = require('http'),
|
|||||||
color = require('colorful'),
|
color = require('colorful'),
|
||||||
certMgr = require('./certMgr').default,
|
certMgr = require('./certMgr').default,
|
||||||
Recorder = require('./recorder').default,
|
Recorder = require('./recorder').default,
|
||||||
logUtil = require('./log'),
|
logUtil = require('./log').default,
|
||||||
util = require('./util').default,
|
util = require('./util').default,
|
||||||
events = require('events'),
|
events = require('events'),
|
||||||
co = require('co'),
|
co = require('co'),
|
||||||
WebInterface = require('./webInterface'),
|
WebInterface = require('./webInterface'),
|
||||||
wsServerMgr = require('./wsServerMgr'),
|
wsServerMgr = require('./wsServerMgr').default,
|
||||||
ThrottleGroup = require('stream-throttle').ThrottleGroup;
|
ThrottleGroup = require('stream-throttle').ThrottleGroup;
|
||||||
|
|
||||||
// const memwatch = require('memwatch-next');
|
// const memwatch = require('memwatch-next');
|
||||||
|
@ -414,7 +414,7 @@ class RequestHandler {
|
|||||||
public wsIntercept: boolean;
|
public wsIntercept: boolean;
|
||||||
public connectReqHandler: () => void;
|
public connectReqHandler: () => void;
|
||||||
private userRequestHandler: () => void;
|
private userRequestHandler: () => void;
|
||||||
private wsHandler: () => void;
|
private wsHandler: (wsClient: WebSocket, wsReq: http.IncomingMessage) => void;
|
||||||
private httpsServerMgr: HttpsServerMgr;
|
private httpsServerMgr: HttpsServerMgr;
|
||||||
/**
|
/**
|
||||||
* Creates an instance of RequestHandler.
|
* Creates an instance of RequestHandler.
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const proxyUtil = require('./util').default;
|
import proxyUtil from './util';
|
||||||
const path = require('path');
|
import * as path from 'path';
|
||||||
const fs = require('fs');
|
import * as fs from 'fs';
|
||||||
const request = require('request');
|
import * as request from 'request';
|
||||||
|
|
||||||
const cachePath = proxyUtil.getAnyProxyPath('cache');
|
const cachePath = proxyUtil.getAnyProxyPath('cache');
|
||||||
|
|
||||||
@ -13,7 +13,7 @@ const cachePath = proxyUtil.getAnyProxyPath('cache');
|
|||||||
* @param {any} url
|
* @param {any} url
|
||||||
* @returns {string} cachePath
|
* @returns {string} cachePath
|
||||||
*/
|
*/
|
||||||
function cacheRemoteFile(url) {
|
function cacheRemoteFile(url: string): Promise<string> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
request(url, (error, response, body) => {
|
request(url, (error, response, body) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
@ -39,7 +39,7 @@ function cacheRemoteFile(url) {
|
|||||||
* @param {any} filePath
|
* @param {any} filePath
|
||||||
* @returns module
|
* @returns module
|
||||||
*/
|
*/
|
||||||
function loadLocalPath(filePath) {
|
function loadLocalPath(filePath: string): Promise<NodeJS.Module> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const ruleFilePath = path.resolve(process.cwd(), filePath);
|
const ruleFilePath = path.resolve(process.cwd(), filePath);
|
||||||
if (fs.existsSync(ruleFilePath)) {
|
if (fs.existsSync(ruleFilePath)) {
|
||||||
@ -57,14 +57,14 @@ function loadLocalPath(filePath) {
|
|||||||
* @param {any} urlOrPath
|
* @param {any} urlOrPath
|
||||||
* @returns module
|
* @returns module
|
||||||
*/
|
*/
|
||||||
function requireModule(urlOrPath) {
|
function requireModule(urlOrPath: string): Promise<NodeJS.Module> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
if (/^http/i.test(urlOrPath)) {
|
if (/^http/i.test(urlOrPath)) {
|
||||||
resolve(cacheRemoteFile(urlOrPath));
|
resolve(cacheRemoteFile(urlOrPath));
|
||||||
} else {
|
} else {
|
||||||
resolve(urlOrPath);
|
resolve(urlOrPath);
|
||||||
}
|
}
|
||||||
}).then(localPath => loadLocalPath(localPath));
|
}).then((localPath: string) => loadLocalPath(localPath));
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
@ -17,7 +17,7 @@ module.exports = {
|
|||||||
* @param {buffer} requestDetail.response.body
|
* @param {buffer} requestDetail.response.body
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
*beforeSendRequest(requestDetail) {
|
*beforeSendRequest(requestDetail: AnyProxyRequestDetail): Generator {
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -28,7 +28,7 @@ module.exports = {
|
|||||||
* @param {object} requestDetail
|
* @param {object} requestDetail
|
||||||
* @param {object} responseDetail
|
* @param {object} responseDetail
|
||||||
*/
|
*/
|
||||||
*beforeSendResponse(requestDetail, responseDetail) {
|
*beforeSendResponse(requestDetail: AnyProxyRequestDetail, responseDetail: AnyProxyReponseDetail): Generator {
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -40,7 +40,7 @@ module.exports = {
|
|||||||
* @param {any} requestDetail
|
* @param {any} requestDetail
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
*beforeDealHttpsRequest(requestDetail) {
|
*beforeDealHttpsRequest(requestDetail: AnyProxyRequestDetail): Generator {
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -51,7 +51,7 @@ module.exports = {
|
|||||||
* @param {any} error
|
* @param {any} error
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
*onError(requestDetail, error) {
|
*onError(requestDetail: AnyProxyRequestDetail, error: NodeJS.ErrnoException): Generator {
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -63,7 +63,7 @@ module.exports = {
|
|||||||
* @param {any} error
|
* @param {any} error
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
*onConnectError(requestDetail, error) {
|
*onConnectError(requestDetail: AnyProxyRequestDetail, error: NodeJS.ErrnoException): Generator {
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
};
|
};
|
@ -1,12 +1,21 @@
|
|||||||
'use strict'
|
'use strict';
|
||||||
|
/* tslint:disable:max-line-length */
|
||||||
|
import * as child_process from 'child_process';
|
||||||
|
import logUtil from './log';
|
||||||
|
|
||||||
const child_process = require('child_process');
|
declare interface IProxyManager {
|
||||||
|
networkType?: string;
|
||||||
|
getNetworkType?: () => string;
|
||||||
|
enableGlobalProxy?: (ip: string, port: string, proxyType: 'http' | 'https') => void;
|
||||||
|
disableGlobalProxy?: (proxyType: 'http' | 'https') => void;
|
||||||
|
getProxyState?: () => IExecScriptResult;
|
||||||
|
}
|
||||||
|
|
||||||
const networkTypes = ['Ethernet', 'Thunderbolt Ethernet', 'Wi-Fi'];
|
const networkTypes = ['Ethernet', 'Thunderbolt Ethernet', 'Wi-Fi'];
|
||||||
|
|
||||||
function execSync(cmd) {
|
function execSync(cmd: string): IExecScriptResult {
|
||||||
let stdout,
|
let stdout;
|
||||||
status = 0;
|
let status = 0;
|
||||||
try {
|
try {
|
||||||
stdout = child_process.execSync(cmd);
|
stdout = child_process.execSync(cmd);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@ -16,7 +25,7 @@ function execSync(cmd) {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
stdout: stdout.toString(),
|
stdout: stdout.toString(),
|
||||||
status
|
status,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,13 +60,12 @@ function execSync(cmd) {
|
|||||||
* ------------------------------------------------------------------------
|
* ------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const macProxyManager = {};
|
const macProxyManager: IProxyManager = {
|
||||||
|
};
|
||||||
macProxyManager.getNetworkType = () => {
|
|
||||||
for (let i = 0; i < networkTypes.length; i++) {
|
|
||||||
const type = networkTypes[i],
|
|
||||||
result = execSync('networksetup -getwebproxy ' + type);
|
|
||||||
|
|
||||||
|
macProxyManager.getNetworkType = function(): string {
|
||||||
|
for (const type of networkTypes) {
|
||||||
|
const result = execSync('networksetup -getwebproxy ' + type);
|
||||||
if (result.status === 0) {
|
if (result.status === 0) {
|
||||||
macProxyManager.networkType = type;
|
macProxyManager.networkType = type;
|
||||||
return type;
|
return type;
|
||||||
@ -70,7 +78,7 @@ macProxyManager.getNetworkType = () => {
|
|||||||
|
|
||||||
macProxyManager.enableGlobalProxy = (ip, port, proxyType) => {
|
macProxyManager.enableGlobalProxy = (ip, port, proxyType) => {
|
||||||
if (!ip || !port) {
|
if (!ip || !port) {
|
||||||
console.log('failed to set global proxy server.\n ip and port are required.');
|
logUtil.warn('failed to set global proxy server.\n ip and port are required.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,11 +133,11 @@ macProxyManager.getProxyState = () => {
|
|||||||
* ------------------------------------------------------------------------
|
* ------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const winProxyManager = {};
|
const winProxyManager: IProxyManager = {};
|
||||||
|
|
||||||
winProxyManager.enableGlobalProxy = (ip, port) => {
|
winProxyManager.enableGlobalProxy = (ip, port) => {
|
||||||
if (!ip && !port) {
|
if (!ip && !port) {
|
||||||
console.log('failed to set global proxy server.\n ip and port are required.');
|
logUtil.warn('failed to set global proxy server.\n ip and port are required.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,8 +153,12 @@ winProxyManager.enableGlobalProxy = (ip, port) => {
|
|||||||
|
|
||||||
winProxyManager.disableGlobalProxy = () => execSync('reg add "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings" /v ProxyEnable /t REG_DWORD /d 0 /f');
|
winProxyManager.disableGlobalProxy = () => execSync('reg add "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings" /v ProxyEnable /t REG_DWORD /d 0 /f');
|
||||||
|
|
||||||
winProxyManager.getProxyState = () => ''
|
winProxyManager.getProxyState = () => {
|
||||||
|
return {
|
||||||
|
status: -1,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
winProxyManager.getNetworkType = () => ''
|
winProxyManager.getNetworkType = () => '';
|
||||||
|
|
||||||
module.exports = /^win/.test(process.platform) ? winProxyManager : macProxyManager;
|
module.exports = /^win/.test(process.platform) ? winProxyManager : macProxyManager;
|
112
lib/util.ts
112
lib/util.ts
@ -15,12 +15,13 @@ import * as color from 'colorful';
|
|||||||
import { Buffer } from 'buffer';
|
import { Buffer } from 'buffer';
|
||||||
import { execSync } from 'child_process';
|
import { execSync } from 'child_process';
|
||||||
import logUtil from './log';
|
import logUtil from './log';
|
||||||
|
import * as os from 'os';
|
||||||
|
import { IncomingHttpHeaders } from 'http';
|
||||||
|
|
||||||
|
const networkInterfaces = os.networkInterfaces();
|
||||||
const networkInterfaces = require('os').networkInterfaces();
|
|
||||||
|
|
||||||
// {"Content-Encoding":"gzip"} --> {"content-encoding":"gzip"}
|
// {"Content-Encoding":"gzip"} --> {"content-encoding":"gzip"}
|
||||||
function lower_keys (obj: object): object {
|
function lower_keys(obj: object): object {
|
||||||
for (const key in obj) {
|
for (const key in obj) {
|
||||||
const val = obj[key];
|
const val = obj[key];
|
||||||
delete obj[key];
|
delete obj[key];
|
||||||
@ -29,15 +30,15 @@ function lower_keys (obj: object): object {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return obj;
|
return obj;
|
||||||
};
|
}
|
||||||
|
|
||||||
function merge (baseObj: object, extendObj: object): object {
|
function merge(baseObj: object, extendObj: object): object {
|
||||||
for (const key in extendObj) {
|
for (const key in extendObj) {
|
||||||
baseObj[key] = extendObj[key];
|
baseObj[key] = extendObj[key];
|
||||||
}
|
}
|
||||||
|
|
||||||
return baseObj;
|
return baseObj;
|
||||||
};
|
}
|
||||||
|
|
||||||
function getUserHome(): string {
|
function getUserHome(): string {
|
||||||
return process.env.HOME || process.env.USERPROFILE;
|
return process.env.HOME || process.env.USERPROFILE;
|
||||||
@ -51,7 +52,7 @@ function getAnyProxyHome(): string {
|
|||||||
return home;
|
return home;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAnyProxyPath (pathName): string {
|
function getAnyProxyPath(pathName: string): string {
|
||||||
const home = getAnyProxyHome();
|
const home = getAnyProxyHome();
|
||||||
const targetPath = path.join(home, pathName);
|
const targetPath = path.join(home, pathName);
|
||||||
if (!fs.existsSync(targetPath)) {
|
if (!fs.existsSync(targetPath)) {
|
||||||
@ -63,41 +64,41 @@ function getAnyProxyPath (pathName): string {
|
|||||||
/**
|
/**
|
||||||
* 简易字符串render替换
|
* 简易字符串render替换
|
||||||
*/
|
*/
|
||||||
function simpleRender (str: string, object: object, regexp: RegExp) {
|
function simpleRender(str: string, object: object, regexp: RegExp): string {
|
||||||
return String(str).replace(regexp || (/\{\{([^{}]+)\}\}/g), (match, name) => {
|
return String(str).replace(regexp || (/\{\{([^{}]+)\}\}/g), (match, name) => {
|
||||||
if (match.charAt(0) === '\\') {
|
if (match.charAt(0) === '\\') {
|
||||||
return match.slice(1);
|
return match.slice(1);
|
||||||
}
|
}
|
||||||
return (object[name] != null) ? object[name] : '';
|
return (object[name] != null) ? object[name] : '';
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 读取指定目录下的子目录
|
* 读取指定目录下的子目录
|
||||||
*/
|
*/
|
||||||
function filewalker (root: string, cb: Function) {
|
function filewalker(root: string, cb: (err: Error, result: any) => void): void {
|
||||||
root = root || process.cwd();
|
root = root || process.cwd();
|
||||||
|
|
||||||
const ret = {
|
const ret = {
|
||||||
directory: [],
|
directory: [],
|
||||||
file: []
|
file: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
fs.readdir(root, (err, list) => {
|
fs.readdir(root, (err, list) => {
|
||||||
if (list && list.length) {
|
if (list && list.length) {
|
||||||
list.map((item) => {
|
list.map((item) => {
|
||||||
const fullPath = path.join(root, item),
|
const fullPath = path.join(root, item);
|
||||||
stat = fs.lstatSync(fullPath);
|
const stat = fs.lstatSync(fullPath);
|
||||||
|
|
||||||
if (stat.isFile()) {
|
if (stat.isFile()) {
|
||||||
ret.file.push({
|
ret.file.push({
|
||||||
name: item,
|
name: item,
|
||||||
fullPath
|
fullPath,
|
||||||
});
|
});
|
||||||
} else if (stat.isDirectory()) {
|
} else if (stat.isDirectory()) {
|
||||||
ret.directory.push({
|
ret.directory.push({
|
||||||
name: item,
|
name: item,
|
||||||
fullPath
|
fullPath,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -105,20 +106,20 @@ function filewalker (root: string, cb: Function) {
|
|||||||
|
|
||||||
cb && cb.apply(null, [null, ret]);
|
cb && cb.apply(null, [null, ret]);
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 获取文件所对应的content-type以及content-length等信息
|
* 获取文件所对应的content-type以及content-length等信息
|
||||||
* 比如在useLocalResponse的时候会使用到
|
* 比如在useLocalResponse的时候会使用到
|
||||||
*/
|
*/
|
||||||
function contentType (filepath: string): string {
|
function contentType(filepath: string): string {
|
||||||
return mime.contentType(path.extname(filepath)) || '';
|
return mime.contentType(path.extname(filepath)) || '';
|
||||||
};
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 读取file的大小,以byte为单位
|
* 读取file的大小,以byte为单位
|
||||||
*/
|
*/
|
||||||
function contentLength (filepath: string): number {
|
function contentLength(filepath: string): number {
|
||||||
try {
|
try {
|
||||||
const stat = fs.statSync(filepath);
|
const stat = fs.statSync(filepath);
|
||||||
return stat.size;
|
return stat.size;
|
||||||
@ -127,30 +128,30 @@ function contentLength (filepath: string): number {
|
|||||||
logUtil.printLog(color.red(e));
|
logUtil.printLog(color.red(e));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* remove the cache before requiring, the path SHOULD BE RELATIVE TO UTIL.JS
|
* remove the cache before requiring, the path SHOULD BE RELATIVE TO UTIL.JS
|
||||||
*/
|
*/
|
||||||
function freshRequire (modulePath: string): NodeModule {
|
function freshRequire(modulePath: string): NodeModule {
|
||||||
delete require.cache[require.resolve(modulePath)];
|
delete require.cache[require.resolve(modulePath)];
|
||||||
return require(modulePath);
|
return require(modulePath);
|
||||||
};
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* format the date string
|
* format the date string
|
||||||
* @param date Date or timestamp
|
* @param date Date or timestamp
|
||||||
* @param formatter YYYYMMDDHHmmss
|
* @param formatter YYYYMMDDHHmmss
|
||||||
*/
|
*/
|
||||||
function formatDate (date: Date | number, formatter: string): string {
|
function formatDate(date: Date | number, formatter: string): string {
|
||||||
let finalDate : Date;
|
let finalDate: Date;
|
||||||
if (typeof date !== 'object') {
|
if (typeof date !== 'object') {
|
||||||
finalDate = new Date(date);
|
finalDate = new Date(date);
|
||||||
} else {
|
} else {
|
||||||
finalDate = date;
|
finalDate = date;
|
||||||
}
|
}
|
||||||
const transform = function (value) {
|
const transform = function(value: number): string {
|
||||||
return value < 10 ? '0' + value : value;
|
return value < 10 ? '0' + value : '' + value;
|
||||||
};
|
};
|
||||||
return formatter.replace(/^YYYY|MM|DD|hh|mm|ss/g, (match) => {
|
return formatter.replace(/^YYYY|MM|DD|hh|mm|ss/g, (match) => {
|
||||||
switch (match) {
|
switch (match) {
|
||||||
@ -167,11 +168,10 @@ function formatDate (date: Date | number, formatter: string): string {
|
|||||||
case 'ss':
|
case 'ss':
|
||||||
return transform(finalDate.getSeconds());
|
return transform(finalDate.getSeconds());
|
||||||
default:
|
default:
|
||||||
return ''
|
return '';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get headers(Object) from rawHeaders(Array)
|
* get headers(Object) from rawHeaders(Array)
|
||||||
@ -179,9 +179,9 @@ function formatDate (date: Date | number, formatter: string): string {
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function getHeaderFromRawHeaders (rawHeaders: Array<string>) {
|
function getHeaderFromRawHeaders(rawHeaders: string[]): IncomingHttpHeaders {
|
||||||
const headerObj = {};
|
const headerObj = {};
|
||||||
const _handleSetCookieHeader = function (key, value) {
|
const handleSetCookieHeader = function(key: string, value: string): void {
|
||||||
if (headerObj[key].constructor === Array) {
|
if (headerObj[key].constructor === Array) {
|
||||||
headerObj[key].push(value);
|
headerObj[key].push(value);
|
||||||
} else {
|
} else {
|
||||||
@ -204,7 +204,7 @@ function getHeaderFromRawHeaders (rawHeaders: Array<string>) {
|
|||||||
// headers with same fields could be combined with comma. Ref: https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
|
// headers with same fields could be combined with comma. Ref: https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
|
||||||
// set-cookie should NOT be combined. Ref: https://tools.ietf.org/html/rfc6265
|
// set-cookie should NOT be combined. Ref: https://tools.ietf.org/html/rfc6265
|
||||||
if (key.toLowerCase() === 'set-cookie') {
|
if (key.toLowerCase() === 'set-cookie') {
|
||||||
_handleSetCookieHeader(key, value);
|
handleSetCookieHeader(key, value);
|
||||||
} else {
|
} else {
|
||||||
headerObj[key] = headerObj[key] + ',' + value;
|
headerObj[key] = headerObj[key] + ',' + value;
|
||||||
}
|
}
|
||||||
@ -212,9 +212,9 @@ function getHeaderFromRawHeaders (rawHeaders: Array<string>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return headerObj;
|
return headerObj;
|
||||||
};
|
}
|
||||||
|
|
||||||
function getAllIpAddress (): Array<string> {
|
function getAllIpAddress(): string[] {
|
||||||
const allIp = [];
|
const allIp = [];
|
||||||
|
|
||||||
Object.keys(networkInterfaces).map((nic) => {
|
Object.keys(networkInterfaces).map((nic) => {
|
||||||
@ -226,7 +226,7 @@ function getAllIpAddress (): Array<string> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return allIp.length ? allIp : ['127.0.0.1'];
|
return allIp.length ? allIp : ['127.0.0.1'];
|
||||||
};
|
}
|
||||||
|
|
||||||
function deleteFolderContentsRecursive(dirPath: string, ifClearFolderItself: boolean): void {
|
function deleteFolderContentsRecursive(dirPath: string, ifClearFolderItself: boolean): void {
|
||||||
if (!dirPath.trim() || dirPath === '/') {
|
if (!dirPath.trim() || dirPath === '/') {
|
||||||
@ -254,7 +254,9 @@ function deleteFolderContentsRecursive(dirPath: string, ifClearFolderItself: boo
|
|||||||
} catch (er) {
|
} catch (er) {
|
||||||
if (process.platform === 'win32' && (er.code === 'ENOTEMPTY' || er.code === 'EBUSY' || er.code === 'EPERM')) {
|
if (process.platform === 'win32' && (er.code === 'ENOTEMPTY' || er.code === 'EBUSY' || er.code === 'EPERM')) {
|
||||||
// Retry on windows, sometimes it takes a little time before all the files in the directory are gone
|
// Retry on windows, sometimes it takes a little time before all the files in the directory are gone
|
||||||
if (Date.now() - start > 1000) throw er;
|
if (Date.now() - start > 1000) {
|
||||||
|
throw er;
|
||||||
|
}
|
||||||
} else if (er.code === 'ENOENT') {
|
} else if (er.code === 'ENOENT') {
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
@ -269,7 +271,7 @@ function deleteFolderContentsRecursive(dirPath: string, ifClearFolderItself: boo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFreePort (): Promise<number> {
|
function getFreePort(): Promise<number> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const server = require('net').createServer();
|
const server = require('net').createServer();
|
||||||
server.unref();
|
server.unref();
|
||||||
@ -283,7 +285,7 @@ function getFreePort (): Promise<number> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function collectErrorLog (error: any): string {
|
function collectErrorLog(error: any): string {
|
||||||
if (error && error.code && error.toString()) {
|
if (error && error.code && error.toString()) {
|
||||||
return error.toString();
|
return error.toString();
|
||||||
} else {
|
} else {
|
||||||
@ -291,40 +293,41 @@ function collectErrorLog (error: any): string {
|
|||||||
try {
|
try {
|
||||||
const errorString = error.toString();
|
const errorString = error.toString();
|
||||||
if (errorString.indexOf('You may only yield a function') >= 0) {
|
if (errorString.indexOf('You may only yield a function') >= 0) {
|
||||||
result = 'Function is not yieldable. Did you forget to provide a generator or promise in rule file ? \nFAQ http://anyproxy.io/4.x/#faq';
|
result = 'Function is not yieldable. Did you forget to provide a generator or promise in rule file ? '
|
||||||
|
+ '\nFAQ http://anyproxy.io/4.x/#faq';
|
||||||
}
|
}
|
||||||
} catch (e) {}
|
} catch (e) { logUtil.error(e.stack); }
|
||||||
return result
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function isFunc (source: object): boolean {
|
function isFunc(source: object): boolean {
|
||||||
return source && Object.prototype.toString.call(source) === '[object Function]';
|
return source && Object.prototype.toString.call(source) === '[object Function]';
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {object} content
|
* @param {object} content
|
||||||
* @returns the size of the content
|
* @returns the size of the content
|
||||||
*/
|
*/
|
||||||
function getByteSize (content: Buffer): number {
|
function getByteSize(content: Buffer): number {
|
||||||
return Buffer.byteLength(content);
|
return Buffer.byteLength(content);
|
||||||
};
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* identify whether the
|
* identify whether the
|
||||||
*/
|
*/
|
||||||
function isIpDomain (domain: string): boolean {
|
function isIpDomain(domain: string): boolean {
|
||||||
if (!domain) {
|
if (!domain) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const ipReg = /^\d+?\.\d+?\.\d+?\.\d+?$/;
|
const ipReg = /^\d+?\.\d+?\.\d+?\.\d+?$/;
|
||||||
|
|
||||||
return ipReg.test(domain);
|
return ipReg.test(domain);
|
||||||
};
|
}
|
||||||
|
|
||||||
function execScriptSync (cmd: string): object {
|
function execScriptSync(cmd: string): object {
|
||||||
let stdout,
|
let stdout;
|
||||||
status = 0;
|
let status = 0;
|
||||||
try {
|
try {
|
||||||
stdout = execSync(cmd);
|
stdout = execSync(cmd);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@ -334,14 +337,13 @@ function execScriptSync (cmd: string): object {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
stdout: stdout.toString(),
|
stdout: stdout.toString(),
|
||||||
status
|
status,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
function guideToHomePage (): void {
|
function guideToHomePage(): void {
|
||||||
logUtil.info('Refer to http://anyproxy.io for more detail');
|
logUtil.info('Refer to http://anyproxy.io for more detail');
|
||||||
};
|
}
|
||||||
|
|
||||||
|
|
||||||
const Util = {
|
const Util = {
|
||||||
lower_keys,
|
lower_keys,
|
||||||
|
@ -1,20 +1,43 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
//websocket server manager
|
// websocket server manager
|
||||||
|
import * as WebSocket from 'ws';
|
||||||
|
import Recorder from './recorder';
|
||||||
|
import logUtil from './log';
|
||||||
|
import { Server } from 'http';
|
||||||
|
|
||||||
const WebSocketServer = require('ws').Server;
|
declare interface IWsMessage {
|
||||||
const logUtil = require('./log');
|
type?: 'error' | 'body';
|
||||||
|
error?: string;
|
||||||
|
reqRef?: string;
|
||||||
|
content?: {
|
||||||
|
id: number;
|
||||||
|
body: string;
|
||||||
|
error?: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function resToMsg(msg, recorder, cb) {
|
declare interface IWsServerConfig {
|
||||||
let result = {},
|
server: Server;
|
||||||
jsonData;
|
}
|
||||||
|
|
||||||
|
declare interface IMultiMessageQueue {
|
||||||
|
type: 'updateMultiple' | 'updateLatestWsMsg';
|
||||||
|
content: AnyProxyRecorder.WsResourceInfo[] | AnyProxyRecorder.WsResourceInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
const WebSocketServer = WebSocket.Server;
|
||||||
|
|
||||||
|
function resToMsg(msg: string, recorder: Recorder, cb: (result: IWsMessage) => void): void {
|
||||||
|
let result: IWsMessage = {};
|
||||||
|
let jsonData;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
jsonData = JSON.parse(msg);
|
jsonData = JSON.parse(msg);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
result = {
|
result = {
|
||||||
type: 'error',
|
type: 'error',
|
||||||
error: 'failed to parse your request : ' + e.toString()
|
error: 'failed to parse your request : ' + e.toString(),
|
||||||
};
|
};
|
||||||
cb && cb(result);
|
cb && cb(result);
|
||||||
return;
|
return;
|
||||||
@ -31,12 +54,12 @@ function resToMsg(msg, recorder, cb) {
|
|||||||
result.content = {
|
result.content = {
|
||||||
id: null,
|
id: null,
|
||||||
body: null,
|
body: null,
|
||||||
error: err.toString()
|
error: err.toString(),
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
result.content = {
|
result.content = {
|
||||||
id: jsonData.id,
|
id: jsonData.id,
|
||||||
body: data.toString()
|
body: data.toString(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
cb && cb(result);
|
cb && cb(result);
|
||||||
@ -46,10 +69,13 @@ function resToMsg(msg, recorder, cb) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//config.server
|
// config.server
|
||||||
|
|
||||||
class wsServer {
|
class WsServer {
|
||||||
constructor(config, recorder) {
|
private config: IWsServerConfig;
|
||||||
|
private recorder: Recorder;
|
||||||
|
private wss: WebSocket.Server;
|
||||||
|
constructor(config: IWsServerConfig, recorder: Recorder) {
|
||||||
if (!recorder) {
|
if (!recorder) {
|
||||||
throw new Error('proxy recorder is required');
|
throw new Error('proxy recorder is required');
|
||||||
} else if (!config || !config.server) {
|
} else if (!config || !config.server) {
|
||||||
@ -61,12 +87,12 @@ class wsServer {
|
|||||||
self.recorder = recorder;
|
self.recorder = recorder;
|
||||||
}
|
}
|
||||||
|
|
||||||
start() {
|
public start(): Promise<any> {
|
||||||
const self = this;
|
const self = this;
|
||||||
const config = self.config;
|
const config = self.config;
|
||||||
const recorder = self.recorder;
|
const recorder = self.recorder;
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
//web socket interface
|
// web socket interface
|
||||||
const wss = new WebSocketServer({
|
const wss = new WebSocketServer({
|
||||||
server: config.server,
|
server: config.server,
|
||||||
clientTracking: true,
|
clientTracking: true,
|
||||||
@ -83,12 +109,12 @@ class wsServer {
|
|||||||
sendMultipleMessage();
|
sendMultipleMessage();
|
||||||
}, 50);
|
}, 50);
|
||||||
|
|
||||||
function sendMultipleMessage(data) {
|
function sendMultipleMessage(data?: AnyProxyRecorder.WsResourceInfo): void {
|
||||||
// if the flag goes to be true, and there are records to send
|
// if the flag goes to be true, and there are records to send
|
||||||
if (broadcastFlag && messageQueue.length > 0) {
|
if (broadcastFlag && messageQueue.length > 0) {
|
||||||
wss && wss.broadcast({
|
wss && broadcast({
|
||||||
type: 'updateMultiple',
|
type: 'updateMultiple',
|
||||||
content: messageQueue
|
content: messageQueue,
|
||||||
});
|
});
|
||||||
messageQueue = [];
|
messageQueue = [];
|
||||||
broadcastFlag = false;
|
broadcastFlag = false;
|
||||||
@ -97,7 +123,7 @@ class wsServer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
wss.broadcast = function (data) {
|
function broadcast(data?: IMultiMessageQueue | string): void {
|
||||||
if (typeof data === 'object') {
|
if (typeof data === 'object') {
|
||||||
try {
|
try {
|
||||||
data = JSON.stringify(data);
|
data = JSON.stringify(data);
|
||||||
@ -105,17 +131,17 @@ class wsServer {
|
|||||||
console.error('==> errorr when do broadcast ', e, data);
|
console.error('==> errorr when do broadcast ', e, data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const client of wss.clients) {
|
wss.clients.forEach(function(client: WebSocket): void {
|
||||||
try {
|
try {
|
||||||
client.send(data);
|
client.send(data);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logUtil.printLog('websocket failed to send data, ' + e, logUtil.T_ERR);
|
logUtil.printLog('websocket failed to send data, ' + e, logUtil.T_ERR);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
wss.on('connection', (ws) => {
|
wss.on('connection', (ws) => {
|
||||||
ws.on('message', (msg) => {
|
ws.on('message', (msg: string) => {
|
||||||
resToMsg(msg, recorder, (res) => {
|
resToMsg(msg, recorder, (res) => {
|
||||||
res && ws.send(JSON.stringify(res));
|
res && ws.send(JSON.stringify(res));
|
||||||
});
|
});
|
||||||
@ -130,28 +156,27 @@ class wsServer {
|
|||||||
logUtil.printLog('websocket error, ' + e, logUtil.T_ERR);
|
logUtil.printLog('websocket error, ' + e, logUtil.T_ERR);
|
||||||
});
|
});
|
||||||
|
|
||||||
wss.on('close', () => { });
|
wss.on('close', (err: Error) => { logUtil.error(err.stack); });
|
||||||
|
|
||||||
recorder.on('update', (data) => {
|
recorder.on('update', (data) => {
|
||||||
try {
|
try {
|
||||||
sendMultipleMessage(data);
|
sendMultipleMessage(data);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log('ws error');
|
logUtil.error('ws error');
|
||||||
console.log(e);
|
logUtil.error(e.stack);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
recorder.on('updateLatestWsMsg', (data) => {
|
recorder.on('updateLatestWsMsg', (data) => {
|
||||||
try {
|
try {
|
||||||
// console.info('==> update latestMsg ', data);
|
// console.info('==> update latestMsg ', data);
|
||||||
wss && wss.broadcast({
|
wss && broadcast({
|
||||||
type: 'updateLatestWsMsg',
|
type: 'updateLatestWsMsg',
|
||||||
content: data
|
content: data,
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logUtil.error(e.message);
|
logUtil.error(e.message);
|
||||||
logUtil.error(e.stack);
|
logUtil.error(e.stack);
|
||||||
console.error(e);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -159,7 +184,7 @@ class wsServer {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
closeAll() {
|
public closeAll(): Promise<any> {
|
||||||
const self = this;
|
const self = this;
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
self.wss.close((e) => {
|
self.wss.close((e) => {
|
||||||
@ -173,4 +198,4 @@ class wsServer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = wsServer;
|
module.exports = WsServer;
|
@ -2,8 +2,9 @@
|
|||||||
* manage the websocket server
|
* manage the websocket server
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
const ws = require('ws');
|
import * as ws from 'ws';
|
||||||
const logUtil = require('./log.js');
|
import * as http from 'http';
|
||||||
|
import logUtil from './log.js';
|
||||||
|
|
||||||
const WsServer = ws.Server;
|
const WsServer = ws.Server;
|
||||||
|
|
||||||
@ -13,9 +14,12 @@ const WsServer = ws.Server;
|
|||||||
{string} config.server
|
{string} config.server
|
||||||
{handler} config.handler
|
{handler} config.handler
|
||||||
*/
|
*/
|
||||||
function getWsServer(config) {
|
function getWsServer(config: {
|
||||||
|
server: http.Server;
|
||||||
|
connHandler: (wsClient: ws, wsReq: http.IncomingMessage) => void;
|
||||||
|
}): ws.Server {
|
||||||
const wss = new WsServer({
|
const wss = new WsServer({
|
||||||
server: config.server
|
server: config.server,
|
||||||
});
|
});
|
||||||
|
|
||||||
wss.on('connection', config.connHandler);
|
wss.on('connection', config.connHandler);
|
||||||
@ -24,16 +28,19 @@ function getWsServer(config) {
|
|||||||
headers.push('x-anyproxy-websocket:true');
|
headers.push('x-anyproxy-websocket:true');
|
||||||
});
|
});
|
||||||
|
|
||||||
wss.on('error', e => {
|
wss.on('error', (e) => {
|
||||||
logUtil.error(`error in websocket proxy: ${e.message},\r\n ${e.stack}`);
|
logUtil.error(`error in websocket proxy: ${e.message},\r\n ${e.stack}`);
|
||||||
console.error('error happened in proxy websocket:', e)
|
console.error('error happened in proxy websocket:', e);
|
||||||
});
|
});
|
||||||
|
|
||||||
wss.on('close', e => {
|
wss.on('close', (e) => {
|
||||||
console.error('==> closing the ws server');
|
console.error('==> closing the ws server');
|
||||||
});
|
});
|
||||||
|
|
||||||
return wss;
|
return wss;
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports.getWsServer = getWsServer;
|
export default {
|
||||||
|
getWsServer,
|
||||||
|
};
|
||||||
|
|
@ -13,6 +13,7 @@
|
|||||||
"ordered-imports": false,
|
"ordered-imports": false,
|
||||||
"only-arrow-functions": false,
|
"only-arrow-functions": false,
|
||||||
"no-reference": false,
|
"no-reference": false,
|
||||||
|
"forin": false,
|
||||||
"typedef": [
|
"typedef": [
|
||||||
true,
|
true,
|
||||||
"call-signature",
|
"call-signature",
|
||||||
|
1
typings/index.d.ts
vendored
1
typings/index.d.ts
vendored
@ -85,4 +85,5 @@ declare interface OneLevelObjectType {
|
|||||||
|
|
||||||
declare interface IExecScriptResult {
|
declare interface IExecScriptResult {
|
||||||
status: number;
|
status: number;
|
||||||
|
stdout?: string;
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user