mirror of
https://github.com/louislam/dockge.git
synced 2024-11-24 03:44:03 +00:00
parent
a3c4082800
commit
05b79ba50e
6 changed files with 89 additions and 49 deletions
|
@ -29,7 +29,7 @@ import { Stack } from "./stack";
|
||||||
import { Cron } from "croner";
|
import { Cron } from "croner";
|
||||||
import gracefulShutdown from "http-graceful-shutdown";
|
import gracefulShutdown from "http-graceful-shutdown";
|
||||||
import User from "./models/user";
|
import User from "./models/user";
|
||||||
import childProcess from "child_process";
|
import childProcessAsync from "promisify-child-process";
|
||||||
import { Terminal } from "./terminal";
|
import { Terminal } from "./terminal";
|
||||||
|
|
||||||
export class DockgeServer {
|
export class DockgeServer {
|
||||||
|
@ -483,7 +483,7 @@ export class DockgeServer {
|
||||||
return jwtSecretBean;
|
return jwtSecretBean;
|
||||||
}
|
}
|
||||||
|
|
||||||
sendStackList(useCache = false) {
|
async sendStackList(useCache = false) {
|
||||||
let roomList = this.io.sockets.adapter.rooms.keys();
|
let roomList = this.io.sockets.adapter.rooms.keys();
|
||||||
let map : Map<string, object> | undefined;
|
let map : Map<string, object> | undefined;
|
||||||
|
|
||||||
|
@ -494,7 +494,7 @@ export class DockgeServer {
|
||||||
// Get the list only if there is a room
|
// Get the list only if there is a room
|
||||||
if (!map) {
|
if (!map) {
|
||||||
map = new Map();
|
map = new Map();
|
||||||
let stackList = Stack.getStackList(this, useCache);
|
let stackList = await Stack.getStackList(this, useCache);
|
||||||
|
|
||||||
for (let [ stackName, stack ] of stackList) {
|
for (let [ stackName, stack ] of stackList) {
|
||||||
map.set(stackName, stack.toSimpleJSON());
|
map.set(stackName, stack.toSimpleJSON());
|
||||||
|
@ -510,8 +510,8 @@ export class DockgeServer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sendStackStatusList() {
|
async sendStackStatusList() {
|
||||||
let statusList = Stack.getStatusList();
|
let statusList = await Stack.getStatusList();
|
||||||
|
|
||||||
let roomList = this.io.sockets.adapter.rooms.keys();
|
let roomList = this.io.sockets.adapter.rooms.keys();
|
||||||
|
|
||||||
|
@ -529,8 +529,15 @@ export class DockgeServer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getDockerNetworkList() : string[] {
|
async getDockerNetworkList() : Promise<string[]> {
|
||||||
let res = childProcess.spawnSync("docker", [ "network", "ls", "--format", "{{.Name}}" ]);
|
let res = await childProcessAsync.spawn("docker", [ "network", "ls", "--format", "{{.Name}}" ], {
|
||||||
|
encoding: "utf-8",
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!res.stdout) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
let list = res.stdout.toString().split("\n");
|
let list = res.stdout.toString().split("\n");
|
||||||
|
|
||||||
// Remove empty string item
|
// Remove empty string item
|
||||||
|
|
|
@ -45,7 +45,7 @@ export class DockerSocketHandler extends SocketHandler {
|
||||||
if (typeof(name) !== "string") {
|
if (typeof(name) !== "string") {
|
||||||
throw new ValidationError("Name must be a string");
|
throw new ValidationError("Name must be a string");
|
||||||
}
|
}
|
||||||
const stack = Stack.getStack(server, name);
|
const stack = await Stack.getStack(server, name);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await stack.delete(socket);
|
await stack.delete(socket);
|
||||||
|
@ -65,7 +65,7 @@ export class DockerSocketHandler extends SocketHandler {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on("getStack", (stackName : unknown, callback) => {
|
socket.on("getStack", async (stackName : unknown, callback) => {
|
||||||
try {
|
try {
|
||||||
checkLogin(socket);
|
checkLogin(socket);
|
||||||
|
|
||||||
|
@ -73,7 +73,7 @@ export class DockerSocketHandler extends SocketHandler {
|
||||||
throw new ValidationError("Stack name must be a string");
|
throw new ValidationError("Stack name must be a string");
|
||||||
}
|
}
|
||||||
|
|
||||||
const stack = Stack.getStack(server, stackName);
|
const stack = await Stack.getStack(server, stackName);
|
||||||
|
|
||||||
if (stack.isManagedByDockge) {
|
if (stack.isManagedByDockge) {
|
||||||
stack.joinCombinedTerminal(socket);
|
stack.joinCombinedTerminal(socket);
|
||||||
|
@ -111,7 +111,7 @@ export class DockerSocketHandler extends SocketHandler {
|
||||||
throw new ValidationError("Stack name must be a string");
|
throw new ValidationError("Stack name must be a string");
|
||||||
}
|
}
|
||||||
|
|
||||||
const stack = Stack.getStack(server, stackName);
|
const stack = await Stack.getStack(server, stackName);
|
||||||
await stack.start(socket);
|
await stack.start(socket);
|
||||||
callback({
|
callback({
|
||||||
ok: true,
|
ok: true,
|
||||||
|
@ -135,7 +135,7 @@ export class DockerSocketHandler extends SocketHandler {
|
||||||
throw new ValidationError("Stack name must be a string");
|
throw new ValidationError("Stack name must be a string");
|
||||||
}
|
}
|
||||||
|
|
||||||
const stack = Stack.getStack(server, stackName);
|
const stack = await Stack.getStack(server, stackName);
|
||||||
await stack.stop(socket);
|
await stack.stop(socket);
|
||||||
callback({
|
callback({
|
||||||
ok: true,
|
ok: true,
|
||||||
|
@ -156,7 +156,7 @@ export class DockerSocketHandler extends SocketHandler {
|
||||||
throw new ValidationError("Stack name must be a string");
|
throw new ValidationError("Stack name must be a string");
|
||||||
}
|
}
|
||||||
|
|
||||||
const stack = Stack.getStack(server, stackName);
|
const stack = await Stack.getStack(server, stackName);
|
||||||
await stack.restart(socket);
|
await stack.restart(socket);
|
||||||
callback({
|
callback({
|
||||||
ok: true,
|
ok: true,
|
||||||
|
@ -177,7 +177,7 @@ export class DockerSocketHandler extends SocketHandler {
|
||||||
throw new ValidationError("Stack name must be a string");
|
throw new ValidationError("Stack name must be a string");
|
||||||
}
|
}
|
||||||
|
|
||||||
const stack = Stack.getStack(server, stackName);
|
const stack = await Stack.getStack(server, stackName);
|
||||||
await stack.update(socket);
|
await stack.update(socket);
|
||||||
callback({
|
callback({
|
||||||
ok: true,
|
ok: true,
|
||||||
|
@ -198,7 +198,7 @@ export class DockerSocketHandler extends SocketHandler {
|
||||||
throw new ValidationError("Stack name must be a string");
|
throw new ValidationError("Stack name must be a string");
|
||||||
}
|
}
|
||||||
|
|
||||||
const stack = Stack.getStack(server, stackName);
|
const stack = await Stack.getStack(server, stackName);
|
||||||
await stack.down(socket);
|
await stack.down(socket);
|
||||||
callback({
|
callback({
|
||||||
ok: true,
|
ok: true,
|
||||||
|
@ -219,7 +219,7 @@ export class DockerSocketHandler extends SocketHandler {
|
||||||
throw new ValidationError("Stack name must be a string");
|
throw new ValidationError("Stack name must be a string");
|
||||||
}
|
}
|
||||||
|
|
||||||
const stack = Stack.getStack(server, stackName, true);
|
const stack = await Stack.getStack(server, stackName, true);
|
||||||
const serviceStatusList = Object.fromEntries(await stack.getServiceStatusList());
|
const serviceStatusList = Object.fromEntries(await stack.getServiceStatusList());
|
||||||
callback({
|
callback({
|
||||||
ok: true,
|
ok: true,
|
||||||
|
|
|
@ -101,7 +101,7 @@ export class TerminalSocketHandler extends SocketHandler {
|
||||||
log.debug("interactiveTerminal", "Service name: " + serviceName);
|
log.debug("interactiveTerminal", "Service name: " + serviceName);
|
||||||
|
|
||||||
// Get stack
|
// Get stack
|
||||||
const stack = Stack.getStack(server, stackName);
|
const stack = await Stack.getStack(server, stackName);
|
||||||
stack.joinContainerTerminal(socket, serviceName, shell);
|
stack.joinContainerTerminal(socket, serviceName, shell);
|
||||||
|
|
||||||
callback({
|
callback({
|
||||||
|
@ -151,7 +151,7 @@ export class TerminalSocketHandler extends SocketHandler {
|
||||||
throw new ValidationError("Stack name must be a string.");
|
throw new ValidationError("Stack name must be a string.");
|
||||||
}
|
}
|
||||||
|
|
||||||
const stack = Stack.getStack(server, stackName);
|
const stack = await Stack.getStack(server, stackName);
|
||||||
await stack.leaveCombinedTerminal(socket);
|
await stack.leaveCombinedTerminal(socket);
|
||||||
|
|
||||||
callback({
|
callback({
|
||||||
|
|
|
@ -16,7 +16,7 @@ import {
|
||||||
UNKNOWN
|
UNKNOWN
|
||||||
} from "./util-common";
|
} from "./util-common";
|
||||||
import { InteractiveTerminal, Terminal } from "./terminal";
|
import { InteractiveTerminal, Terminal } from "./terminal";
|
||||||
import childProcess from "child_process";
|
import childProcessAsync from "promisify-child-process";
|
||||||
|
|
||||||
export class Stack {
|
export class Stack {
|
||||||
|
|
||||||
|
@ -72,11 +72,15 @@ export class Stack {
|
||||||
/**
|
/**
|
||||||
* Get the status of the stack from `docker compose ps --format json`
|
* Get the status of the stack from `docker compose ps --format json`
|
||||||
*/
|
*/
|
||||||
ps() : object {
|
async ps() : Promise<object> {
|
||||||
let res = childProcess.execSync("docker compose ps --format json", {
|
let res = await childProcessAsync.spawn("docker", [ "compose", "ps", "--format", "json" ], {
|
||||||
cwd: this.path
|
cwd: this.path,
|
||||||
|
encoding: "utf-8",
|
||||||
});
|
});
|
||||||
return JSON.parse(res.toString());
|
if (!res.stdout) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return JSON.parse(res.stdout.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
get isManagedByDockge() : boolean {
|
get isManagedByDockge() : boolean {
|
||||||
|
@ -192,8 +196,8 @@ export class Stack {
|
||||||
return exitCode;
|
return exitCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateStatus() {
|
async updateStatus() {
|
||||||
let statusList = Stack.getStatusList();
|
let statusList = await Stack.getStatusList();
|
||||||
let status = statusList.get(this.name);
|
let status = statusList.get(this.name);
|
||||||
|
|
||||||
if (status) {
|
if (status) {
|
||||||
|
@ -203,7 +207,7 @@ export class Stack {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static getStackList(server : DockgeServer, useCacheForManaged = false) : Map<string, Stack> {
|
static async getStackList(server : DockgeServer, useCacheForManaged = false) : Promise<Map<string, Stack>> {
|
||||||
let stacksDir = server.stacksDir;
|
let stacksDir = server.stacksDir;
|
||||||
let stackList : Map<string, Stack>;
|
let stackList : Map<string, Stack>;
|
||||||
|
|
||||||
|
@ -223,7 +227,7 @@ export class Stack {
|
||||||
if (!stat.isDirectory()) {
|
if (!stat.isDirectory()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let stack = this.getStack(server, filename);
|
let stack = await this.getStack(server, filename);
|
||||||
stack._status = CREATED_FILE;
|
stack._status = CREATED_FILE;
|
||||||
stackList.set(filename, stack);
|
stackList.set(filename, stack);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -238,8 +242,15 @@ export class Stack {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get status from docker compose ls
|
// Get status from docker compose ls
|
||||||
let res = childProcess.execSync("docker compose ls --all --format json");
|
let res = await childProcessAsync.spawn("docker", [ "compose", "ls", "--all", "--format", "json" ], {
|
||||||
let composeList = JSON.parse(res.toString());
|
encoding: "utf-8",
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!res.stdout) {
|
||||||
|
return stackList;
|
||||||
|
}
|
||||||
|
|
||||||
|
let composeList = JSON.parse(res.stdout.toString());
|
||||||
|
|
||||||
for (let composeStack of composeList) {
|
for (let composeStack of composeList) {
|
||||||
let stack = stackList.get(composeStack.Name);
|
let stack = stackList.get(composeStack.Name);
|
||||||
|
@ -265,10 +276,12 @@ export class Stack {
|
||||||
* Get the status list, it will be used to update the status of the stacks
|
* Get the status list, it will be used to update the status of the stacks
|
||||||
* Not all status will be returned, only the stack that is deployed or created to `docker compose` will be returned
|
* Not all status will be returned, only the stack that is deployed or created to `docker compose` will be returned
|
||||||
*/
|
*/
|
||||||
static getStatusList() : Map<string, number> {
|
static async getStatusList() : Promise<Map<string, number>> {
|
||||||
let statusList = new Map<string, number>();
|
let statusList = new Map<string, number>();
|
||||||
|
|
||||||
let res = childProcess.execSync("docker compose ls --all --format json");
|
let res = await childProcessAsync.spawn("docker", [ "compose", "ls", "--all", "--format", "json" ], {
|
||||||
|
encoding: "utf-8",
|
||||||
|
});
|
||||||
let composeList = JSON.parse(res.toString());
|
let composeList = JSON.parse(res.toString());
|
||||||
|
|
||||||
for (let composeStack of composeList) {
|
for (let composeStack of composeList) {
|
||||||
|
@ -297,13 +310,13 @@ export class Stack {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static getStack(server: DockgeServer, stackName: string, skipFSOperations = false) : Stack {
|
static async getStack(server: DockgeServer, stackName: string, skipFSOperations = false) : Promise<Stack> {
|
||||||
let dir = path.join(server.stacksDir, stackName);
|
let dir = path.join(server.stacksDir, stackName);
|
||||||
|
|
||||||
if (!skipFSOperations) {
|
if (!skipFSOperations) {
|
||||||
if (!fs.existsSync(dir) || !fs.statSync(dir).isDirectory()) {
|
if (!fs.existsSync(dir) || !fs.statSync(dir).isDirectory()) {
|
||||||
// Maybe it is a stack managed by docker compose directly
|
// Maybe it is a stack managed by docker compose directly
|
||||||
let stackList = this.getStackList(server, true);
|
let stackList = await this.getStackList(server, true);
|
||||||
let stack = stackList.get(stackName);
|
let stack = stackList.get(stackName);
|
||||||
|
|
||||||
if (stack) {
|
if (stack) {
|
||||||
|
@ -374,7 +387,7 @@ export class Stack {
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the stack is not running, we don't need to restart it
|
// If the stack is not running, we don't need to restart it
|
||||||
this.updateStatus();
|
await this.updateStatus();
|
||||||
log.debug("update", "Status: " + this.status);
|
log.debug("update", "Status: " + this.status);
|
||||||
if (this.status !== RUNNING) {
|
if (this.status !== RUNNING) {
|
||||||
return exitCode;
|
return exitCode;
|
||||||
|
@ -422,11 +435,17 @@ export class Stack {
|
||||||
async getServiceStatusList() {
|
async getServiceStatusList() {
|
||||||
let statusList = new Map<string, number>();
|
let statusList = new Map<string, number>();
|
||||||
|
|
||||||
let res = childProcess.spawnSync("docker", [ "compose", "ps", "--format", "json" ], {
|
try {
|
||||||
|
let res = await childProcessAsync.spawn("docker", [ "compose", "ps", "--format", "json" ], {
|
||||||
cwd: this.path,
|
cwd: this.path,
|
||||||
|
encoding: "utf-8",
|
||||||
});
|
});
|
||||||
|
|
||||||
let lines = res.stdout.toString().split("\n");
|
if (!res.stdout) {
|
||||||
|
return statusList;
|
||||||
|
}
|
||||||
|
|
||||||
|
let lines = res.stdout?.toString().split("\n");
|
||||||
|
|
||||||
for (let line of lines) {
|
for (let line of lines) {
|
||||||
try {
|
try {
|
||||||
|
@ -441,5 +460,10 @@ export class Stack {
|
||||||
}
|
}
|
||||||
|
|
||||||
return statusList;
|
return statusList;
|
||||||
|
} catch (e) {
|
||||||
|
log.error("getServiceStatusList", e);
|
||||||
|
return statusList;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,6 +41,7 @@
|
||||||
"knex": "~2.5.1",
|
"knex": "~2.5.1",
|
||||||
"limiter-es6-compat": "~2.1.2",
|
"limiter-es6-compat": "~2.1.2",
|
||||||
"mysql2": "~3.6.3",
|
"mysql2": "~3.6.3",
|
||||||
|
"promisify-child-process": "~4.1.2",
|
||||||
"redbean-node": "~0.3.3",
|
"redbean-node": "~0.3.3",
|
||||||
"socket.io": "~4.7.2",
|
"socket.io": "~4.7.2",
|
||||||
"socket.io-client": "~4.7.2",
|
"socket.io-client": "~4.7.2",
|
||||||
|
|
|
@ -56,6 +56,9 @@ dependencies:
|
||||||
mysql2:
|
mysql2:
|
||||||
specifier: ~3.6.3
|
specifier: ~3.6.3
|
||||||
version: 3.6.3
|
version: 3.6.3
|
||||||
|
promisify-child-process:
|
||||||
|
specifier: ~4.1.2
|
||||||
|
version: 4.1.2
|
||||||
redbean-node:
|
redbean-node:
|
||||||
specifier: ~0.3.3
|
specifier: ~0.3.3
|
||||||
version: 0.3.3(mysql2@3.6.3)
|
version: 0.3.3(mysql2@3.6.3)
|
||||||
|
@ -3887,6 +3890,11 @@ packages:
|
||||||
dev: false
|
dev: false
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
/promisify-child-process@4.1.2:
|
||||||
|
resolution: {integrity: sha512-APnkIgmaHNJpkAn7k+CrJSi9WMuff5ctYFbD0CO2XIPkM8yO7d/ShouU2clywbpHV/DUsyc4bpJCsNgddNtx4g==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/proxy-addr@2.0.7:
|
/proxy-addr@2.0.7:
|
||||||
resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==}
|
resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==}
|
||||||
engines: {node: '>= 0.10'}
|
engines: {node: '>= 0.10'}
|
||||||
|
|
Loading…
Reference in a new issue