feat: refact wsServer, wsServerMgr, ruleLoader, systemProxyMgr into TS

This commit is contained in:
砚然 2018-08-29 14:51:36 +08:00
parent 4497160284
commit bcb7451579
13 changed files with 199 additions and 155 deletions

View File

@ -3,7 +3,7 @@
*/
const ruleLoader = require('../dist/ruleLoader');
const logUtil = require('../dist/log');
const logUtil = require('../dist/log').default;
const AnyProxy = require('../dist/proxy');
module.exports = function startServer(program) {

View File

@ -9,11 +9,10 @@ import * as color from 'colorful';
import * as WebSocket from 'ws';
import * as constants from 'constants';
import * as AsyncTask from 'async-task-mgr';
import Recorder from './recorder';
import certMgr from './certMgr';
import logUtil from './log';
import util from './util';
import * as wsServerMgr from './wsServerMgr';
import wsServerMgr from './wsServerMgr';
import * as co from 'co';
// // manage https servers
@ -31,8 +30,7 @@ import * as co from 'co';
// asyncTask = require('async-task-mgr');
declare type THttpsRequestHanlder = (req: http.IncomingMessage, userRes: http.ServerResponse) => void;
declare type TWsRequestHandler =
(userRule: AnyProxyRule, recorder: Recorder, wsClient: WebSocket, wsReq: http.IncomingMessage) => void;
declare type TWsRequestHandler = (wsClient: WebSocket, wsReq: http.IncomingMessage) => void;
const createSecureContext = tls.createSecureContext || (crypto as any).createSecureContext;
// using sni to avoid multiple ports

View File

@ -1,5 +1,5 @@
'use strict';
/*tslint:disable:no-console */
import * as color from 'colorful';
import util from './util';
@ -12,7 +12,7 @@ enum LogLevelMap {
rule_error = 2,
warn = 3,
debug = 4,
};
}
function setPrintStatus(status: boolean): void {
ifPrint = !!status;
@ -22,7 +22,7 @@ function setLogLevel(level: string): void {
logLevel = parseInt(level, 10);
}
function printLog(content: string, type?: LogLevelMap) {
function printLog(content: string, type?: LogLevelMap): void {
if (!ifPrint) {
return;
}
@ -75,35 +75,34 @@ function printLog(content: string, type?: LogLevelMap) {
}
}
module.exports.printLog = printLog;
function debug (content): void {
function debug(content: string): void {
printLog(content, LogLevelMap.debug);
};
}
function info (content): void {
function info(content: string): void {
printLog(content, LogLevelMap.tip);
};
}
function warn (content) {
function warn(content: string): void {
printLog(content, LogLevelMap.warn);
};
}
function error (content) {
function error(content: string): void {
printLog(content, LogLevelMap.system_error);
};
}
function ruleError (content) {
function ruleError(content: string): void {
printLog(content, LogLevelMap.rule_error);
};
}
module.exports.setPrintStatus = setPrintStatus;
module.exports.setLogLevel = setLogLevel;
module.exports.T_TIP = LogLevelMap.tip;
module.exports.T_ERR = LogLevelMap.system_error;
module.exports.T_RULE_ERROR = LogLevelMap.rule_error;
module.exports.T_WARN = LogLevelMap.warn;
module.exports.T_DEBUG = LogLevelMap.debug;
// module.exports.setPrintStatus = setPrintStatus;
// module.exports.setLogLevel = setLogLevel;
// module.exports.T_TIP = LogLevelMap.tip;
// module.exports.T_ERR = LogLevelMap.system_error;
// module.exports.T_RULE_ERROR = LogLevelMap.rule_error;
// module.exports.T_WARN = LogLevelMap.warn;
// module.exports.T_DEBUG = LogLevelMap.debug;
// module.exports.printLog = printLog;
const LogUtil = {
setPrintStatus,
@ -121,5 +120,4 @@ const LogUtil = {
T_DEBUG: LogLevelMap.debug,
};
exports.LogUtil = LogUtil;
export default LogUtil;

View File

@ -6,12 +6,12 @@ const http = require('http'),
color = require('colorful'),
certMgr = require('./certMgr').default,
Recorder = require('./recorder').default,
logUtil = require('./log'),
logUtil = require('./log').default,
util = require('./util').default,
events = require('events'),
co = require('co'),
WebInterface = require('./webInterface'),
wsServerMgr = require('./wsServerMgr'),
wsServerMgr = require('./wsServerMgr').default,
ThrottleGroup = require('stream-throttle').ThrottleGroup;
// const memwatch = require('memwatch-next');

View File

@ -414,7 +414,7 @@ class RequestHandler {
public wsIntercept: boolean;
public connectReqHandler: () => void;
private userRequestHandler: () => void;
private wsHandler: () => void;
private wsHandler: (wsClient: WebSocket, wsReq: http.IncomingMessage) => void;
private httpsServerMgr: HttpsServerMgr;
/**
* Creates an instance of RequestHandler.

View File

@ -1,9 +1,9 @@
'use strict';
const proxyUtil = require('./util').default;
const path = require('path');
const fs = require('fs');
const request = require('request');
import proxyUtil from './util';
import * as path from 'path';
import * as fs from 'fs';
import * as request from 'request';
const cachePath = proxyUtil.getAnyProxyPath('cache');
@ -13,7 +13,7 @@ const cachePath = proxyUtil.getAnyProxyPath('cache');
* @param {any} url
* @returns {string} cachePath
*/
function cacheRemoteFile(url) {
function cacheRemoteFile(url: string): Promise<string> {
return new Promise((resolve, reject) => {
request(url, (error, response, body) => {
if (error) {
@ -39,7 +39,7 @@ function cacheRemoteFile(url) {
* @param {any} filePath
* @returns module
*/
function loadLocalPath(filePath) {
function loadLocalPath(filePath: string): Promise<NodeJS.Module> {
return new Promise((resolve, reject) => {
const ruleFilePath = path.resolve(process.cwd(), filePath);
if (fs.existsSync(ruleFilePath)) {
@ -57,14 +57,14 @@ function loadLocalPath(filePath) {
* @param {any} urlOrPath
* @returns module
*/
function requireModule(urlOrPath) {
function requireModule(urlOrPath: string): Promise<NodeJS.Module> {
return new Promise((resolve, reject) => {
if (/^http/i.test(urlOrPath)) {
resolve(cacheRemoteFile(urlOrPath));
} else {
resolve(urlOrPath);
}
}).then(localPath => loadLocalPath(localPath));
}).then((localPath: string) => loadLocalPath(localPath));
}
module.exports = {

View File

@ -17,7 +17,7 @@ module.exports = {
* @param {buffer} requestDetail.response.body
* @returns
*/
*beforeSendRequest(requestDetail) {
*beforeSendRequest(requestDetail: AnyProxyRequestDetail): Generator {
return null;
},
@ -28,7 +28,7 @@ module.exports = {
* @param {object} requestDetail
* @param {object} responseDetail
*/
*beforeSendResponse(requestDetail, responseDetail) {
*beforeSendResponse(requestDetail: AnyProxyRequestDetail, responseDetail: AnyProxyReponseDetail): Generator {
return null;
},
@ -40,7 +40,7 @@ module.exports = {
* @param {any} requestDetail
* @returns
*/
*beforeDealHttpsRequest(requestDetail) {
*beforeDealHttpsRequest(requestDetail: AnyProxyRequestDetail): Generator {
return null;
},
@ -51,7 +51,7 @@ module.exports = {
* @param {any} error
* @returns
*/
*onError(requestDetail, error) {
*onError(requestDetail: AnyProxyRequestDetail, error: NodeJS.ErrnoException): Generator {
return null;
},
@ -63,7 +63,7 @@ module.exports = {
* @param {any} error
* @returns
*/
*onConnectError(requestDetail, error) {
*onConnectError(requestDetail: AnyProxyRequestDetail, error: NodeJS.ErrnoException): Generator {
return null;
},
};

View File

@ -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'];
function execSync(cmd) {
let stdout,
status = 0;
function execSync(cmd: string): IExecScriptResult {
let stdout;
let status = 0;
try {
stdout = child_process.execSync(cmd);
} catch (err) {
@ -16,7 +25,7 @@ function execSync(cmd) {
return {
stdout: stdout.toString(),
status
status,
};
}
@ -51,13 +60,12 @@ function execSync(cmd) {
* ------------------------------------------------------------------------
*/
const macProxyManager = {};
macProxyManager.getNetworkType = () => {
for (let i = 0; i < networkTypes.length; i++) {
const type = networkTypes[i],
result = execSync('networksetup -getwebproxy ' + type);
const macProxyManager: IProxyManager = {
};
macProxyManager.getNetworkType = function(): string {
for (const type of networkTypes) {
const result = execSync('networksetup -getwebproxy ' + type);
if (result.status === 0) {
macProxyManager.networkType = type;
return type;
@ -70,7 +78,7 @@ macProxyManager.getNetworkType = () => {
macProxyManager.enableGlobalProxy = (ip, port, proxyType) => {
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;
}
@ -125,11 +133,11 @@ macProxyManager.getProxyState = () => {
* ------------------------------------------------------------------------
*/
const winProxyManager = {};
const winProxyManager: IProxyManager = {};
winProxyManager.enableGlobalProxy = (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;
}
@ -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.getProxyState = () => ''
winProxyManager.getProxyState = () => {
return {
status: -1,
};
};
winProxyManager.getNetworkType = () => ''
winProxyManager.getNetworkType = () => '';
module.exports = /^win/.test(process.platform) ? winProxyManager : macProxyManager;

View File

@ -15,12 +15,13 @@ import * as color from 'colorful';
import { Buffer } from 'buffer';
import { execSync } from 'child_process';
import logUtil from './log';
import * as os from 'os';
import { IncomingHttpHeaders } from 'http';
const networkInterfaces = require('os').networkInterfaces();
const networkInterfaces = os.networkInterfaces();
// {"Content-Encoding":"gzip"} --> {"content-encoding":"gzip"}
function lower_keys (obj: object): object {
function lower_keys(obj: object): object {
for (const key in obj) {
const val = obj[key];
delete obj[key];
@ -29,15 +30,15 @@ function lower_keys (obj: object): object {
}
return obj;
};
}
function merge (baseObj: object, extendObj: object): object {
function merge(baseObj: object, extendObj: object): object {
for (const key in extendObj) {
baseObj[key] = extendObj[key];
}
return baseObj;
};
}
function getUserHome(): string {
return process.env.HOME || process.env.USERPROFILE;
@ -51,7 +52,7 @@ function getAnyProxyHome(): string {
return home;
}
function getAnyProxyPath (pathName): string {
function getAnyProxyPath(pathName: string): string {
const home = getAnyProxyHome();
const targetPath = path.join(home, pathName);
if (!fs.existsSync(targetPath)) {
@ -63,41 +64,41 @@ function getAnyProxyPath (pathName): string {
/**
* 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) => {
if (match.charAt(0) === '\\') {
return match.slice(1);
}
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();
const ret = {
directory: [],
file: []
file: [],
};
fs.readdir(root, (err, list) => {
if (list && list.length) {
list.map((item) => {
const fullPath = path.join(root, item),
stat = fs.lstatSync(fullPath);
const fullPath = path.join(root, item);
const stat = fs.lstatSync(fullPath);
if (stat.isFile()) {
ret.file.push({
name: item,
fullPath
fullPath,
});
} else if (stat.isDirectory()) {
ret.directory.push({
name: item,
fullPath
fullPath,
});
}
});
@ -105,20 +106,20 @@ function filewalker (root: string, cb: Function) {
cb && cb.apply(null, [null, ret]);
});
};
}
/*
* content-type以及content-length等信息
* useLocalResponse的时候会使用到
*/
function contentType (filepath: string): string {
function contentType(filepath: string): string {
return mime.contentType(path.extname(filepath)) || '';
};
}
/*
* file的大小byte为单位
*/
function contentLength (filepath: string): number {
function contentLength(filepath: string): number {
try {
const stat = fs.statSync(filepath);
return stat.size;
@ -127,30 +128,30 @@ function contentLength (filepath: string): number {
logUtil.printLog(color.red(e));
return 0;
}
};
}
/*
* 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)];
return require(modulePath);
};
}
/*
* format the date string
* @param date Date or timestamp
* @param formatter YYYYMMDDHHmmss
*/
function formatDate (date: Date | number, formatter: string): string {
let finalDate : Date;
function formatDate(date: Date | number, formatter: string): string {
let finalDate: Date;
if (typeof date !== 'object') {
finalDate = new Date(date);
} else {
finalDate = date;
}
const transform = function (value) {
return value < 10 ? '0' + value : value;
const transform = function(value: number): string {
return value < 10 ? '0' + value : '' + value;
};
return formatter.replace(/^YYYY|MM|DD|hh|mm|ss/g, (match) => {
switch (match) {
@ -167,11 +168,10 @@ function formatDate (date: Date | number, formatter: string): string {
case 'ss':
return transform(finalDate.getSeconds());
default:
return ''
return '';
}
});
};
}
/**
* 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 _handleSetCookieHeader = function (key, value) {
const handleSetCookieHeader = function(key: string, value: string): void {
if (headerObj[key].constructor === Array) {
headerObj[key].push(value);
} 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
// set-cookie should NOT be combined. Ref: https://tools.ietf.org/html/rfc6265
if (key.toLowerCase() === 'set-cookie') {
_handleSetCookieHeader(key, value);
handleSetCookieHeader(key, value);
} else {
headerObj[key] = headerObj[key] + ',' + value;
}
@ -212,9 +212,9 @@ function getHeaderFromRawHeaders (rawHeaders: Array<string>) {
}
}
return headerObj;
};
}
function getAllIpAddress (): Array<string> {
function getAllIpAddress(): string[] {
const allIp = [];
Object.keys(networkInterfaces).map((nic) => {
@ -226,7 +226,7 @@ function getAllIpAddress (): Array<string> {
});
return allIp.length ? allIp : ['127.0.0.1'];
};
}
function deleteFolderContentsRecursive(dirPath: string, ifClearFolderItself: boolean): void {
if (!dirPath.trim() || dirPath === '/') {
@ -254,7 +254,9 @@ function deleteFolderContentsRecursive(dirPath: string, ifClearFolderItself: boo
} catch (er) {
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
if (Date.now() - start > 1000) throw er;
if (Date.now() - start > 1000) {
throw er;
}
} else if (er.code === 'ENOENT') {
break;
} else {
@ -269,7 +271,7 @@ function deleteFolderContentsRecursive(dirPath: string, ifClearFolderItself: boo
}
}
function getFreePort (): Promise<number> {
function getFreePort(): Promise<number> {
return new Promise((resolve, reject) => {
const server = require('net').createServer();
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()) {
return error.toString();
} else {
@ -291,40 +293,41 @@ function collectErrorLog (error: any): string {
try {
const errorString = error.toString();
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) {}
return result
} catch (e) { logUtil.error(e.stack); }
return result;
}
}
function isFunc (source: object): boolean {
function isFunc(source: object): boolean {
return source && Object.prototype.toString.call(source) === '[object Function]';
};
}
/**
* @param {object} content
* @returns the size of the content
*/
function getByteSize (content: Buffer): number {
function getByteSize(content: Buffer): number {
return Buffer.byteLength(content);
};
}
/*
* identify whether the
*/
function isIpDomain (domain: string): boolean {
function isIpDomain(domain: string): boolean {
if (!domain) {
return false;
}
const ipReg = /^\d+?\.\d+?\.\d+?\.\d+?$/;
return ipReg.test(domain);
};
}
function execScriptSync (cmd: string): object {
let stdout,
status = 0;
function execScriptSync(cmd: string): object {
let stdout;
let status = 0;
try {
stdout = execSync(cmd);
} catch (err) {
@ -334,14 +337,13 @@ function execScriptSync (cmd: string): object {
return {
stdout: stdout.toString(),
status
status,
};
};
}
function guideToHomePage (): void {
function guideToHomePage(): void {
logUtil.info('Refer to http://anyproxy.io for more detail');
};
}
const Util = {
lower_keys,

View File

@ -1,20 +1,43 @@
'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;
const logUtil = require('./log');
declare interface IWsMessage {
type?: 'error' | 'body';
error?: string;
reqRef?: string;
content?: {
id: number;
body: string;
error?: string;
};
}
function resToMsg(msg, recorder, cb) {
let result = {},
jsonData;
declare interface IWsServerConfig {
server: Server;
}
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 {
jsonData = JSON.parse(msg);
} catch (e) {
result = {
type: 'error',
error: 'failed to parse your request : ' + e.toString()
error: 'failed to parse your request : ' + e.toString(),
};
cb && cb(result);
return;
@ -31,12 +54,12 @@ function resToMsg(msg, recorder, cb) {
result.content = {
id: null,
body: null,
error: err.toString()
error: err.toString(),
};
} else {
result.content = {
id: jsonData.id,
body: data.toString()
body: data.toString(),
};
}
cb && cb(result);
@ -46,10 +69,13 @@ function resToMsg(msg, recorder, cb) {
}
}
//config.server
// config.server
class wsServer {
constructor(config, recorder) {
class WsServer {
private config: IWsServerConfig;
private recorder: Recorder;
private wss: WebSocket.Server;
constructor(config: IWsServerConfig, recorder: Recorder) {
if (!recorder) {
throw new Error('proxy recorder is required');
} else if (!config || !config.server) {
@ -61,12 +87,12 @@ class wsServer {
self.recorder = recorder;
}
start() {
public start(): Promise<any> {
const self = this;
const config = self.config;
const recorder = self.recorder;
return new Promise((resolve, reject) => {
//web socket interface
// web socket interface
const wss = new WebSocketServer({
server: config.server,
clientTracking: true,
@ -83,12 +109,12 @@ class wsServer {
sendMultipleMessage();
}, 50);
function sendMultipleMessage(data) {
function sendMultipleMessage(data?: AnyProxyRecorder.WsResourceInfo): void {
// if the flag goes to be true, and there are records to send
if (broadcastFlag && messageQueue.length > 0) {
wss && wss.broadcast({
wss && broadcast({
type: 'updateMultiple',
content: messageQueue
content: messageQueue,
});
messageQueue = [];
broadcastFlag = false;
@ -97,7 +123,7 @@ class wsServer {
}
}
wss.broadcast = function (data) {
function broadcast(data?: IMultiMessageQueue | string): void {
if (typeof data === 'object') {
try {
data = JSON.stringify(data);
@ -105,17 +131,17 @@ class wsServer {
console.error('==> errorr when do broadcast ', e, data);
}
}
for (const client of wss.clients) {
wss.clients.forEach(function(client: WebSocket): void {
try {
client.send(data);
} catch (e) {
logUtil.printLog('websocket failed to send data, ' + e, logUtil.T_ERR);
}
});
}
};
wss.on('connection', (ws) => {
ws.on('message', (msg) => {
ws.on('message', (msg: string) => {
resToMsg(msg, recorder, (res) => {
res && ws.send(JSON.stringify(res));
});
@ -130,28 +156,27 @@ class wsServer {
logUtil.printLog('websocket error, ' + e, logUtil.T_ERR);
});
wss.on('close', () => { });
wss.on('close', (err: Error) => { logUtil.error(err.stack); });
recorder.on('update', (data) => {
try {
sendMultipleMessage(data);
} catch (e) {
console.log('ws error');
console.log(e);
logUtil.error('ws error');
logUtil.error(e.stack);
}
});
recorder.on('updateLatestWsMsg', (data) => {
try {
// console.info('==> update latestMsg ', data);
wss && wss.broadcast({
wss && broadcast({
type: 'updateLatestWsMsg',
content: data
content: data,
});
} catch (e) {
logUtil.error(e.message);
logUtil.error(e.stack);
console.error(e);
}
});
@ -159,7 +184,7 @@ class wsServer {
});
}
closeAll() {
public closeAll(): Promise<any> {
const self = this;
return new Promise((resolve, reject) => {
self.wss.close((e) => {
@ -173,4 +198,4 @@ class wsServer {
}
}
module.exports = wsServer;
module.exports = WsServer;

View File

@ -2,8 +2,9 @@
* manage the websocket server
*
*/
const ws = require('ws');
const logUtil = require('./log.js');
import * as ws from 'ws';
import * as http from 'http';
import logUtil from './log.js';
const WsServer = ws.Server;
@ -13,9 +14,12 @@ const WsServer = ws.Server;
{string} config.server
{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({
server: config.server
server: config.server,
});
wss.on('connection', config.connHandler);
@ -24,16 +28,19 @@ function getWsServer(config) {
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}`);
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');
});
return wss;
}
module.exports.getWsServer = getWsServer;
export default {
getWsServer,
};

View File

@ -13,6 +13,7 @@
"ordered-imports": false,
"only-arrow-functions": false,
"no-reference": false,
"forin": false,
"typedef": [
true,
"call-signature",

1
typings/index.d.ts vendored
View File

@ -85,4 +85,5 @@ declare interface OneLevelObjectType {
declare interface IExecScriptResult {
status: number;
stdout?: string;
}