mirror of
https://github.com/MCSManager/MCSManager.git
synced 2025-01-30 15:19:32 +08:00
开发 兼容 docker 的启动方式
This commit is contained in:
parent
0e47254753
commit
e6f0fad283
@ -10,8 +10,9 @@ const path = require('path')
|
||||
|
||||
const properties = require("properties");
|
||||
const fs = require('fs');
|
||||
const Docker = require('dockerode');
|
||||
|
||||
var CODE_CONSOLE = MCSERVER.localProperty.console_encode;
|
||||
const CODE_CONSOLE = MCSERVER.localProperty.console_encode;
|
||||
|
||||
class ServerProcess extends EventEmitter {
|
||||
|
||||
@ -90,50 +91,136 @@ class ServerProcess extends EventEmitter {
|
||||
//使用 Docker 命令启动
|
||||
dockerStart() {
|
||||
//命令模板与准备数据
|
||||
let dockerCommand = this.dataModel.dockerConfig.dockerCommand;
|
||||
// let dockerCommand = this.dataModel.dockerConfig.dockerCommand;
|
||||
// let processCwd = process.cwd();
|
||||
let stdCwd = path.resolve(this.dataModel.cwd).replace(/\\/igm, "/");
|
||||
|
||||
//命令模板渲染
|
||||
if (this.dataModel.highCommande.trim() != "")
|
||||
dockerCommand = dockerCommand.replace(/\$\{commande\}/igm, this.dataModel.highCommande);
|
||||
else
|
||||
dockerCommand = dockerCommand.replace(/\$\{commande\}/igm, this.templateStart(true));
|
||||
//加入镜像名
|
||||
dockerCommand = dockerCommand.replace(/\$\{imagename\}/igm,
|
||||
this.dataModel.dockerConfig.dockerImageName);
|
||||
//加入端口限制
|
||||
dockerCommand = dockerCommand.replace(/\$\{ports\}/igm,
|
||||
this.dataModel.dockerConfig.dockerPorts ? "-p " + this.dataModel.dockerConfig.dockerPorts : "");
|
||||
//加入服务端绝对路径
|
||||
dockerCommand = dockerCommand.replace(/\$\{serverpath\}/igm,
|
||||
tools.CharReplaceTemp(stdCwd, " "));
|
||||
//加入最大内存限制
|
||||
dockerCommand = dockerCommand.replace(/\$\{xmx\}/igm,
|
||||
this.dataModel.dockerConfig.dockerXmx ? "-m " + this.dataModel.dockerConfig.dockerXmx : "");
|
||||
// if (this.dataModel.highCommande.trim() != "")
|
||||
// dockerCommand = dockerCommand.replace(/\$\{commande\}/igm, this.dataModel.highCommande);
|
||||
// else
|
||||
// dockerCommand = dockerCommand.replace(/\$\{commande\}/igm, this.templateStart(true));
|
||||
// //加入镜像名
|
||||
// dockerCommand = dockerCommand.replace(/\$\{imagename\}/igm,
|
||||
// this.dataModel.dockerConfig.dockerImageName);
|
||||
// //加入端口限制
|
||||
// dockerCommand = dockerCommand.replace(/\$\{ports\}/igm,
|
||||
// this.dataModel.dockerConfig.dockerPorts ? "-p " + this.dataModel.dockerConfig.dockerPorts : "");
|
||||
// //加入服务端绝对路径
|
||||
// dockerCommand = dockerCommand.replace(/\$\{serverpath\}/igm,
|
||||
// tools.CharReplaceTemp(stdCwd, " "));
|
||||
// //加入最大内存限制
|
||||
// dockerCommand = dockerCommand.replace(/\$\{xmx\}/igm,
|
||||
// this.dataModel.dockerConfig.dockerXmx ? "-m " + this.dataModel.dockerConfig.dockerXmx : "");
|
||||
|
||||
//格式替换
|
||||
let dockerCommandPart = dockerCommand.replace(/ /igm, " ").split(" ");
|
||||
// let dockerCommandPart = dockerCommand.replace(/ /igm, " ").split(" ");
|
||||
|
||||
//分割的参数全部渲染
|
||||
// for (let k in dockerCommandPart) { }
|
||||
|
||||
let execDockerCommande = [];
|
||||
for (let i = 1; i < dockerCommandPart.length; i++) {
|
||||
if (dockerCommandPart[i].trim() != "") execDockerCommande.push(tools.TempReplaceChar(dockerCommandPart[i], " "));
|
||||
// let execDockerCommande = [];
|
||||
// for (let i = 1; i < dockerCommandPart.length; i++) {
|
||||
// if (dockerCommandPart[i].trim() != "") execDockerCommande.push(tools.TempReplaceChar(dockerCommandPart[i], " "));
|
||||
// }
|
||||
|
||||
// 暂时使用 MCSMERVER.log 目前已弃用,下版本 log4js
|
||||
// MCSERVER.infoLog('Minecraft Server start (Docker)', this.dataModel.name);
|
||||
// MCSERVER.log('端实例 [' + this.dataModel.name + '] 启动 Docker 容器:');
|
||||
// MCSERVER.log('-------------------------------');
|
||||
// MCSERVER.log('启动命令: ' + dockerCommandPart[0] + " " + execDockerCommande.join(" "));
|
||||
// MCSERVER.log('根:' + stdCwd);
|
||||
// MCSERVER.log('-------------------------------');
|
||||
// this.process = childProcess.spawn(dockerCommandPart[0], execDockerCommande, this.ProcessConfig);
|
||||
// this.send(this.dataModel.highCommande || this.templateStart(true));
|
||||
|
||||
|
||||
// 采用 Docker API 进行启动与监控
|
||||
// 启动命令解析
|
||||
let startCommande = "";
|
||||
if (this.dataModel.highCommande.trim() != "")
|
||||
startCommande = this.dataModel.highCommande;
|
||||
else
|
||||
startCommande = this.templateStart(true);
|
||||
const startCommandeArray = startCommande.split(" ");
|
||||
// 端口解析
|
||||
let portmap = this.dataModel.dockerConfig.dockerPorts;
|
||||
portmap = portmap.split(":");
|
||||
if (portmap.length != 2) {
|
||||
throw new Error("不支持的多端口操作方法");
|
||||
}
|
||||
// 绑定内部暴露端口
|
||||
const protocol = "tcp";
|
||||
const ExposedPortsObj = {};
|
||||
ExposedPortsObj[portmap[0] + "/" + protocol] = {};
|
||||
|
||||
//暂时使用 MCSMERVER.log 目前已弃用,下版本 log4js
|
||||
MCSERVER.infoLog('Minecraft Server start (Docker)', this.dataModel.name);
|
||||
MCSERVER.log('端实例 [' + this.dataModel.name + '] 启动 Docker 容器:');
|
||||
MCSERVER.log('-------------------------------');
|
||||
MCSERVER.log('启动命令: ' + dockerCommandPart[0] + " " + execDockerCommande.join(" "));
|
||||
MCSERVER.log('根:' + stdCwd);
|
||||
MCSERVER.log('-------------------------------');
|
||||
// 绑定内部暴露端口与其对应的宿主机端口
|
||||
const PortBindingsObj = {};
|
||||
PortBindingsObj[portmap[0] + "/" + protocol] = [{
|
||||
HostPort: portmap[1] + ""
|
||||
}];
|
||||
|
||||
this.process = childProcess.spawn(dockerCommandPart[0], execDockerCommande, this.ProcessConfig);
|
||||
console.log("正在使用虚拟化技术启动进程:\n", startCommandeArray, " Port:", portmap)
|
||||
console.log("Pwd:", stdCwd, "端口参数:", ExposedPortsObj, PortBindingsObj)
|
||||
|
||||
this.send(this.dataModel.highCommande || this.templateStart(true));
|
||||
// 模拟一个正常的 Process
|
||||
this.process = new EventEmitter();
|
||||
|
||||
// 基于镜像启动虚拟化容器
|
||||
const docker = new Docker();
|
||||
let auxContainer = null;
|
||||
docker.createContainer({
|
||||
Image: this.dataModel.dockerConfig.dockerImageName,
|
||||
AttachStdin: true,
|
||||
AttachStdout: true,
|
||||
AttachStderr: true,
|
||||
Tty: false,
|
||||
Cmd: startCommandeArray,
|
||||
OpenStdin: true,
|
||||
StdinOnce: false,
|
||||
ExposedPorts: ExposedPortsObj,
|
||||
HostConfig: {
|
||||
Binds: [
|
||||
stdCwd + ":/mcsd/"
|
||||
],
|
||||
Memory: this.dataModel.dockerConfig.dockerXmx * 1024 * 1024 * 1024,
|
||||
PortBindings: PortBindingsObj,
|
||||
}
|
||||
}).then((container) => {
|
||||
auxContainer = container;
|
||||
return auxContainer.start();
|
||||
}).then(() => {
|
||||
// 链接容器的输入输出流
|
||||
auxContainer.attach({
|
||||
stream: true,
|
||||
stdin: true,
|
||||
stdout: true
|
||||
}, (err, stream) => {
|
||||
if (err) throw err;
|
||||
// 赋值进程容器
|
||||
this.process.dockerContainer = auxContainer;
|
||||
// 模拟 pid
|
||||
this.process.pid = 1;
|
||||
// 对接普通进程的输入输出流
|
||||
this.process.stdin = stream;
|
||||
this.process.stdout = stream;
|
||||
this.process.stderr = stream;
|
||||
// 模拟进程杀死功能
|
||||
this.process.kill = (() => {
|
||||
docker.getContainer(auxContainer.id).kill().then(() => {
|
||||
docker.getContainer(auxContainer.id).remove().then(() => {
|
||||
console.log('this.process.kill')
|
||||
return;
|
||||
});
|
||||
});
|
||||
});
|
||||
// 进程事件传递
|
||||
stream.on('exit', (e) => this.process.emit('exit', e));
|
||||
stream.on('error', (e) => this.process.emit('error', e));
|
||||
|
||||
console.log("\nDEBUG Docker process start:", this.process);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
//统一服务端开启
|
||||
|
176
package-lock.json
generated
176
package-lock.json
generated
@ -18,11 +18,27 @@
|
||||
"resolved": "http://registry.npm.taobao.org/array-flatten/download/array-flatten-1.1.1.tgz",
|
||||
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
|
||||
},
|
||||
"asn1": {
|
||||
"version": "0.2.4",
|
||||
"resolved": "https://registry.npm.taobao.org/asn1/download/asn1-0.2.4.tgz",
|
||||
"integrity": "sha1-jSR136tVO7M+d7VOWeiAu4ziMTY=",
|
||||
"requires": {
|
||||
"safer-buffer": "~2.1.0"
|
||||
}
|
||||
},
|
||||
"async-limiter": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npm.taobao.org/async-limiter/download/async-limiter-1.0.1.tgz",
|
||||
"integrity": "sha1-3TeelPDbgxCwgpH51kwyCXZmF/0="
|
||||
},
|
||||
"bcrypt-pbkdf": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npm.taobao.org/bcrypt-pbkdf/download/bcrypt-pbkdf-1.0.2.tgz",
|
||||
"integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
|
||||
"requires": {
|
||||
"tweetnacl": "^0.14.3"
|
||||
}
|
||||
},
|
||||
"bl": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npm.taobao.org/bl/download/bl-1.2.2.tgz?cache=0&sync_timestamp=1568859021404&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fbl%2Fdownload%2Fbl-1.2.2.tgz",
|
||||
@ -80,11 +96,21 @@
|
||||
"resolved": "https://registry.npm.taobao.org/buffer-fill/download/buffer-fill-1.0.0.tgz",
|
||||
"integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw="
|
||||
},
|
||||
"buffer-from": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npm.taobao.org/buffer-from/download/buffer-from-1.1.1.tgz",
|
||||
"integrity": "sha1-MnE7wCj3XAL9txDXx7zsHyxgcO8="
|
||||
},
|
||||
"bytes": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "http://registry.npm.taobao.org/bytes/download/bytes-3.0.0.tgz",
|
||||
"integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg="
|
||||
},
|
||||
"chownr": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npm.taobao.org/chownr/download/chownr-1.1.4.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fchownr%2Fdownload%2Fchownr-1.1.4.tgz",
|
||||
"integrity": "sha1-b8nXtC0ypYNZYzdmbn0ICE2izGs="
|
||||
},
|
||||
"colors": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "http://registry.npm.taobao.org/colors/download/colors-1.1.2.tgz",
|
||||
@ -145,6 +171,29 @@
|
||||
"vary": "~1.1.2"
|
||||
}
|
||||
},
|
||||
"concat-stream": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npm.taobao.org/concat-stream/download/concat-stream-2.0.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fconcat-stream%2Fdownload%2Fconcat-stream-2.0.0.tgz",
|
||||
"integrity": "sha1-QUz1r3kKSMYKub5FJ9VtXkETPLE=",
|
||||
"requires": {
|
||||
"buffer-from": "^1.0.0",
|
||||
"inherits": "^2.0.3",
|
||||
"readable-stream": "^3.0.2",
|
||||
"typedarray": "^0.0.6"
|
||||
},
|
||||
"dependencies": {
|
||||
"readable-stream": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npm.taobao.org/readable-stream/download/readable-stream-3.6.0.tgz",
|
||||
"integrity": "sha1-M3u9o63AcGvT4CRCaihtS0sskZg=",
|
||||
"requires": {
|
||||
"inherits": "^2.0.3",
|
||||
"string_decoder": "^1.1.1",
|
||||
"util-deprecate": "^1.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"connect": {
|
||||
"version": "3.6.5",
|
||||
"resolved": "http://registry.npm.taobao.org/connect/download/connect-3.6.5.tgz",
|
||||
@ -236,6 +285,52 @@
|
||||
"resolved": "http://registry.npm.taobao.org/destroy/download/destroy-1.0.4.tgz",
|
||||
"integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
|
||||
},
|
||||
"docker-modem": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npm.taobao.org/docker-modem/download/docker-modem-2.1.1.tgz",
|
||||
"integrity": "sha1-wtNzULRVduoaqZAjR7eyjKuTPqI=",
|
||||
"requires": {
|
||||
"debug": "^4.1.1",
|
||||
"readable-stream": "^3.5.0",
|
||||
"split-ca": "^1.0.1",
|
||||
"ssh2": "^0.8.7"
|
||||
},
|
||||
"dependencies": {
|
||||
"debug": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npm.taobao.org/debug/download/debug-4.1.1.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdebug%2Fdownload%2Fdebug-4.1.1.tgz",
|
||||
"integrity": "sha1-O3ImAlUQnGtYnO4FDx1RYTlmR5E=",
|
||||
"requires": {
|
||||
"ms": "^2.1.1"
|
||||
}
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npm.taobao.org/ms/download/ms-2.1.2.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fms%2Fdownload%2Fms-2.1.2.tgz",
|
||||
"integrity": "sha1-0J0fNXtEP0kzgqjrPM0YOHKuYAk="
|
||||
},
|
||||
"readable-stream": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npm.taobao.org/readable-stream/download/readable-stream-3.6.0.tgz",
|
||||
"integrity": "sha1-M3u9o63AcGvT4CRCaihtS0sskZg=",
|
||||
"requires": {
|
||||
"inherits": "^2.0.3",
|
||||
"string_decoder": "^1.1.1",
|
||||
"util-deprecate": "^1.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"dockerode": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npm.taobao.org/dockerode/download/dockerode-3.1.0.tgz",
|
||||
"integrity": "sha1-hbl6HqHEgjHtMpdIJxJ7e4CyryA=",
|
||||
"requires": {
|
||||
"concat-stream": "~2.0.0",
|
||||
"docker-modem": "^2.1.0",
|
||||
"tar-fs": "~2.0.0"
|
||||
}
|
||||
},
|
||||
"ee-first": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "http://registry.npm.taobao.org/ee-first/download/ee-first-1.1.1.tgz",
|
||||
@ -759,6 +854,29 @@
|
||||
"resolved": "https://registry.npmjs.org/sorted-array-functions/-/sorted-array-functions-1.1.0.tgz",
|
||||
"integrity": "sha512-zq6fLdGQixb9VZfT/tLgU+LzoedJyTbcf1I/TKETFeUVoWIfcs5HNr+SJSvQJLXRlEZjB1gpILTrxamxAdCcgA=="
|
||||
},
|
||||
"split-ca": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npm.taobao.org/split-ca/download/split-ca-1.0.1.tgz",
|
||||
"integrity": "sha1-bIOv82kvphJW4M0ZfgXp3hV2kaY="
|
||||
},
|
||||
"ssh2": {
|
||||
"version": "0.8.8",
|
||||
"resolved": "https://registry.npm.taobao.org/ssh2/download/ssh2-0.8.8.tgz",
|
||||
"integrity": "sha1-HZgV4of672I64rfbMuZ02tvvRmQ=",
|
||||
"requires": {
|
||||
"ssh2-streams": "~0.4.9"
|
||||
}
|
||||
},
|
||||
"ssh2-streams": {
|
||||
"version": "0.4.9",
|
||||
"resolved": "https://registry.npm.taobao.org/ssh2-streams/download/ssh2-streams-0.4.9.tgz",
|
||||
"integrity": "sha1-09khVUEAAUN9JxGdnAI7MDy+Iwk=",
|
||||
"requires": {
|
||||
"asn1": "~0.2.0",
|
||||
"bcrypt-pbkdf": "^1.0.2",
|
||||
"streamsearch": "~0.1.2"
|
||||
}
|
||||
},
|
||||
"stat-mode": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npm.taobao.org/stat-mode/download/stat-mode-0.2.2.tgz",
|
||||
@ -774,6 +892,11 @@
|
||||
"resolved": "https://registry.npm.taobao.org/streamifier/download/streamifier-0.1.1.tgz",
|
||||
"integrity": "sha1-l+mNj6TRBdYqJpHR3AfoINuN/E8="
|
||||
},
|
||||
"streamsearch": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npm.taobao.org/streamsearch/download/streamsearch-0.1.2.tgz",
|
||||
"integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo="
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npm.taobao.org/string_decoder/download/string_decoder-1.1.1.tgz",
|
||||
@ -782,6 +905,49 @@
|
||||
"safe-buffer": "~5.1.0"
|
||||
}
|
||||
},
|
||||
"tar-fs": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npm.taobao.org/tar-fs/download/tar-fs-2.0.0.tgz",
|
||||
"integrity": "sha1-Z3cA/AyLM3p4vuNiP9wjXyHXr60=",
|
||||
"requires": {
|
||||
"chownr": "^1.1.1",
|
||||
"mkdirp": "^0.5.1",
|
||||
"pump": "^3.0.0",
|
||||
"tar-stream": "^2.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"bl": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npm.taobao.org/bl/download/bl-3.0.0.tgz?cache=0&sync_timestamp=1568859021404&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fbl%2Fdownload%2Fbl-3.0.0.tgz",
|
||||
"integrity": "sha1-NhHsAFef0YVhdUNgsh6feEUA/4g=",
|
||||
"requires": {
|
||||
"readable-stream": "^3.0.1"
|
||||
}
|
||||
},
|
||||
"readable-stream": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npm.taobao.org/readable-stream/download/readable-stream-3.6.0.tgz",
|
||||
"integrity": "sha1-M3u9o63AcGvT4CRCaihtS0sskZg=",
|
||||
"requires": {
|
||||
"inherits": "^2.0.3",
|
||||
"string_decoder": "^1.1.1",
|
||||
"util-deprecate": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"tar-stream": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npm.taobao.org/tar-stream/download/tar-stream-2.1.0.tgz",
|
||||
"integrity": "sha1-0aqjZh8Fs4tazJtwIO/cpReaLMM=",
|
||||
"requires": {
|
||||
"bl": "^3.0.0",
|
||||
"end-of-stream": "^1.4.1",
|
||||
"fs-constants": "^1.0.0",
|
||||
"inherits": "^2.0.3",
|
||||
"readable-stream": "^3.1.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tar-stream": {
|
||||
"version": "1.6.2",
|
||||
"resolved": "https://registry.npm.taobao.org/tar-stream/download/tar-stream-1.6.2.tgz",
|
||||
@ -801,6 +967,11 @@
|
||||
"resolved": "https://registry.npm.taobao.org/to-buffer/download/to-buffer-1.1.1.tgz",
|
||||
"integrity": "sha1-STvUj2LXxD/N7TE6A9ytsuEhOoA="
|
||||
},
|
||||
"tweetnacl": {
|
||||
"version": "0.14.5",
|
||||
"resolved": "https://registry.npm.taobao.org/tweetnacl/download/tweetnacl-0.14.5.tgz?cache=0&sync_timestamp=1581365004105&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ftweetnacl%2Fdownload%2Ftweetnacl-0.14.5.tgz",
|
||||
"integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
|
||||
},
|
||||
"type-is": {
|
||||
"version": "1.6.15",
|
||||
"resolved": "http://registry.npm.taobao.org/type-is/download/type-is-1.6.15.tgz",
|
||||
@ -810,6 +981,11 @@
|
||||
"mime-types": "~2.1.15"
|
||||
}
|
||||
},
|
||||
"typedarray": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npm.taobao.org/typedarray/download/typedarray-0.0.6.tgz",
|
||||
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
|
||||
},
|
||||
"uid-safe": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "http://registry.npm.taobao.org/uid-safe/download/uid-safe-2.1.5.tgz",
|
||||
|
@ -27,6 +27,7 @@
|
||||
"compression": "^1.7.2",
|
||||
"connect": "^3.6.2",
|
||||
"cookie-parser": "^1.4.3",
|
||||
"dockerode": "^3.1.0",
|
||||
"express": "^4.15.3",
|
||||
"express-session": "^1.15.3",
|
||||
"express-ws": "^4.0.0",
|
||||
|
Loading…
Reference in New Issue
Block a user