diff --git a/backend/dockge-server.ts b/backend/dockge-server.ts index a15e215..7aaba63 100644 --- a/backend/dockge-server.ts +++ b/backend/dockge-server.ts @@ -234,7 +234,7 @@ export class DockgeServer { if (isDev) { setInterval(() => { log.debug("terminal", "Terminal count: " + Terminal.getTerminalCount()); - }, 10000); + }, 5000); } } @@ -298,11 +298,11 @@ export class DockgeServer { log.info("server", `Listening on ${this.config.port}`); } - // Run every 5 seconds - Cron("*/2 * * * * *", { + // Run every 10 seconds + Cron("*/10 * * * * *", { protect: true, // Enabled over-run protection. }, () => { - log.debug("server", "Cron job running"); + //log.debug("server", "Cron job running"); this.sendStackList(true); }); diff --git a/backend/socket-handlers/terminal-socket-handler.ts b/backend/socket-handlers/terminal-socket-handler.ts index 0a0485b..bb27a66 100644 --- a/backend/socket-handlers/terminal-socket-handler.ts +++ b/backend/socket-handlers/terminal-socket-handler.ts @@ -140,9 +140,26 @@ export class TerminalSocketHandler extends SocketHandler { } }); - // Close Terminal - socket.on("terminalClose", async (terminalName : unknown, callback : unknown) => { + // Leave Combined Terminal + socket.on("leaveCombinedTerminal", async (stackName : unknown, callback) => { + try { + checkLogin(socket); + log.debug("leaveCombinedTerminal", "Stack name: " + stackName); + + if (typeof(stackName) !== "string") { + throw new ValidationError("Stack name must be a string."); + } + + const stack = Stack.getStack(server, stackName); + await stack.leaveCombinedTerminal(socket); + + callback({ + ok: true, + }); + } catch (e) { + callbackError(e, callback); + } }); // TODO: Resize Terminal diff --git a/backend/stack.ts b/backend/stack.ts index bf7657c..a9af442 100644 --- a/backend/stack.ts +++ b/backend/stack.ts @@ -298,7 +298,7 @@ export class Stack { } } } else { - log.debug("getStack", "Skip FS operations"); + //log.debug("getStack", "Skip FS operations"); } let stack : Stack; @@ -374,12 +374,21 @@ export class Stack { async joinCombinedTerminal(socket: DockgeSocket) { const terminalName = getCombinedTerminalName(this.name); const terminal = Terminal.getOrCreateTerminal(this.server, terminalName, "docker", [ "compose", "logs", "-f", "--tail", "100" ], this.path); + terminal.enableKeepAlive = true; terminal.rows = COMBINED_TERMINAL_ROWS; terminal.cols = COMBINED_TERMINAL_COLS; terminal.join(socket); terminal.start(); } + async leaveCombinedTerminal(socket: DockgeSocket) { + const terminalName = getCombinedTerminalName(this.name); + const terminal = Terminal.getTerminal(terminalName); + if (terminal) { + terminal.leave(socket); + } + } + async joinContainerTerminal(socket: DockgeSocket, serviceName: string, shell : string = "sh", index: number = 0) { const terminalName = getContainerExecTerminalName(this.name, serviceName, index); let terminal = Terminal.getTerminal(terminalName); diff --git a/backend/terminal.ts b/backend/terminal.ts index 3146e33..2b22e69 100644 --- a/backend/terminal.ts +++ b/backend/terminal.ts @@ -46,25 +46,6 @@ export class Terminal { this.cwd = cwd; Terminal.terminalMap.set(this.name, this); - - if (this.enableKeepAlive) { - log.debug("Terminal", "Keep alive enabled for terminal " + this.name); - - // Close if there is no clients - this.keepAliveInterval = setInterval(() => { - const clients = this.server.io.sockets.adapter.rooms.get(this.name); - const numClients = clients ? clients.size : 0; - - if (numClients === 0) { - log.debug("Terminal", "Terminal " + this.name + " has no client, closing..."); - this.close(); - } else { - log.debug("Terminal", "Terminal " + this.name + " has " + numClients + " client(s)"); - } - }, 60 * 1000); - } else { - log.debug("Terminal", "Keep alive disabled for terminal " + this.name); - } } get rows() { @@ -102,6 +83,25 @@ export class Terminal { return; } + if (this.enableKeepAlive) { + log.debug("Terminal", "Keep alive enabled for terminal " + this.name); + + // Close if there is no clients + this.keepAliveInterval = setInterval(() => { + const clients = this.server.io.sockets.adapter.rooms.get(this.name); + const numClients = clients ? clients.size : 0; + + if (numClients === 0) { + log.debug("Terminal", "Terminal " + this.name + " has no client, closing..."); + this.close(); + } else { + log.debug("Terminal", "Terminal " + this.name + " has " + numClients + " client(s)"); + } + }, 60 * 1000); + } else { + log.debug("Terminal", "Keep alive disabled for terminal " + this.name); + } + try { this._ptyProcess = pty.spawn(this.file, this.args, { name: this.name, @@ -122,6 +122,8 @@ export class Terminal { this._ptyProcess.onExit(this.exit); } catch (error) { if (error instanceof Error) { + clearInterval(this.keepAliveInterval); + log.error("Terminal", "Failed to start terminal: " + error.message); const exitCode = Number(error.message.split(" ").pop()); this.exit({ @@ -182,6 +184,7 @@ export class Terminal { } close() { + clearInterval(this.keepAliveInterval); // Send Ctrl+C to the terminal this.ptyProcess?.write("\x03"); } diff --git a/frontend/src/pages/Compose.vue b/frontend/src/pages/Compose.vue index afcab69..025ed0a 100644 --- a/frontend/src/pages/Compose.vue +++ b/frontend/src/pages/Compose.vue @@ -319,6 +319,12 @@ export default { }, deep: true, }, + + $route(to, from) { + // Leave Combined Terminal + console.debug("leaveCombinedTerminal", from.params.stackName); + this.$root.getSocket().emit("leaveCombinedTerminal", this.stack.name, () => {}); + } }, mounted() { if (this.isAdd) { @@ -361,7 +367,7 @@ export default { clearTimeout(serviceStatusTimeout); serviceStatusTimeout = setTimeout(async () => { this.requestServiceStatus(); - }, 2000); + }, 5000); }, requestServiceStatus() {