Louis Lam 1 год назад
Родитель
Сommit
49fb055da4

+ 85 - 0
backend/dockge-instance-manager.ts

@@ -0,0 +1,85 @@
+import { DockgeSocket } from "./util-server";
+import { io } from "socket.io-client";
+import { log } from "./log";
+
+/**
+ * Dockge Instance Manager
+ */
+export class DockgeInstanceManager {
+    protected static instance: DockgeInstanceManager;
+
+    protected constructor() {
+    }
+
+    public static getInstance(): DockgeInstanceManager {
+        if (!DockgeInstanceManager.instance) {
+            DockgeInstanceManager.instance = new DockgeInstanceManager();
+        }
+        return DockgeInstanceManager.instance;
+    }
+
+    connect(socket: DockgeSocket) {
+
+        let list : Record<string, { username : string, password : string}> = {
+            "ws://louis-twister-pi:5001": {
+                username: "admin",
+                password: process.env.DOCKGE_PW || "",
+            }
+        };
+
+        if (Object.keys(list).length !== 0) {
+            log.info("INSTANCEMANAGER", "Connecting to all instance socket server(s)...");
+        }
+
+        for (let url in list) {
+            let item = list[url];
+
+            let client = io(url, {
+                transports: [ "websocket", "polling" ],
+            });
+
+            client.on("connect", () => {
+                log.info("INSTANCEMANAGER", "Connected to the socket server: " + url);
+
+                client.emit("login", {
+                    username: item.username,
+                    password: item.password,
+                }, (res) => {
+                    if (res.ok) {
+                        log.info("INSTANCEMANAGER", "Logged in to the socket server: " + url);
+                    } else {
+                        log.error("INSTANCEMANAGER", "Failed to login to the socket server: " + url);
+                    }
+                });
+            });
+
+            // Catch all events
+            client.onAny((eventName, ...args) => {
+                log.debug("INSTANCEMANAGER", "Received event: " + eventName);
+
+                let proxyEventList = [
+                    "stackList",
+                ];
+
+                if (proxyEventList.includes(eventName)) {
+                    // Add the socket url in the res object to determine which socket server it is from
+                    if (args.length > 0 && typeof args[0] === "object") {
+                        args[0].instanceURL = url;
+                    }
+                    socket.emit(eventName, ...args);
+                } else {
+                    log.debug("INSTANCEMANAGER", "Event not in the proxy list: " + eventName);
+                }
+            });
+
+            socket.instanceSocketList[url] = client;
+        }
+    }
+
+    disconnect(socket: DockgeSocket) {
+        for (let url in socket.instanceSocketList) {
+            let client = socket.instanceSocketList[url];
+            client.disconnect();
+        }
+    }
+}

+ 22 - 2
backend/dockge-server.ts

@@ -31,6 +31,9 @@ import gracefulShutdown from "http-graceful-shutdown";
 import User from "./models/user";
 import childProcess from "child_process";
 import { Terminal } from "./terminal";
+import { DockgeInstanceManager } from "./dockge-instance-manager";
+
+import "dotenv/config";
 
 export class DockgeServer {
     app : Express;
@@ -65,10 +68,13 @@ export class DockgeServer {
 
     stacksDir : string = "";
 
+    dockgeInstanceManager : DockgeInstanceManager;
+
     /**
      *
      */
     constructor() {
+
         // Catch unexpected errors here
         let unexpectedErrorHandler = (error : unknown) => {
             console.trace(error);
@@ -184,6 +190,8 @@ export class DockgeServer {
             response.send(this.indexHTML);
         });
 
+        this.dockgeInstanceManager = DockgeInstanceManager.getInstance();
+
         // Allow all CORS origins in development
         let cors = undefined;
         if (isDev) {
@@ -200,6 +208,9 @@ export class DockgeServer {
         this.io.on("connection", async (socket: Socket) => {
             log.info("server", "Socket connected!");
 
+            let dockgeSocket = socket as DockgeSocket;
+            dockgeSocket.instanceSocketList = {};
+
             this.sendInfo(socket, true);
 
             if (this.needSetup) {
@@ -209,7 +220,7 @@ export class DockgeServer {
 
             // Create socket handlers
             for (const socketHandler of this.socketHandlerList) {
-                socketHandler.create(socket as DockgeSocket, this);
+                socketHandler.create(dockgeSocket, this);
             }
 
             // ***************************
@@ -219,12 +230,18 @@ export class DockgeServer {
             log.debug("auth", "check auto login");
             if (await Settings.get("disableAuth")) {
                 log.info("auth", "Disabled Auth: auto login to admin");
-                this.afterLogin(socket as DockgeSocket, await R.findOne("user") as User);
+                this.afterLogin(dockgeSocket, await R.findOne("user") as User);
                 socket.emit("autoLogin");
             } else {
                 log.debug("auth", "need auth");
             }
 
+            // Socket disconnect
+            socket.on("disconnect", () => {
+                log.info("server", "Socket disconnected!");
+                this.dockgeInstanceManager.disconnect(dockgeSocket);
+            });
+
         });
 
         this.io.on("disconnect", () => {
@@ -249,6 +266,9 @@ export class DockgeServer {
         } catch (e) {
             log.error("server", e);
         }
+
+        // Also connect to other dockge instances
+        this.dockgeInstanceManager.connect(socket);
     }
 
     /**

+ 0 - 10
backend/socket-handlers/terminal-socket-handler.ts

@@ -2,16 +2,6 @@ import { SocketHandler } from "../socket-handler.js";
 import { DockgeServer } from "../dockge-server";
 import { callbackError, checkLogin, DockgeSocket, ValidationError } from "../util-server";
 import { log } from "../log";
-import yaml from "yaml";
-import path from "path";
-import fs from "fs";
-import {
-    allowedCommandList,
-    allowedRawKeys,
-    getComposeTerminalName, getContainerExecTerminalName,
-    isDev,
-    PROGRESS_TERMINAL_ROWS
-} from "../util-common";
 import { InteractiveTerminal, MainTerminal, Terminal } from "../terminal";
 import { Stack } from "../stack";
 

+ 2 - 1
backend/util-server.ts

@@ -1,6 +1,6 @@
 import { Socket } from "socket.io";
+import { Socket as SocketClient } from "socket.io-client";
 import { Terminal } from "./terminal";
-import { randomBytes } from "crypto";
 import { log } from "./log";
 import { ERROR_TYPE_VALIDATION } from "./util-common";
 import { R } from "redbean-node";
@@ -14,6 +14,7 @@ export interface JWTDecoded {
 export interface DockgeSocket extends Socket {
     userID: number;
     consoleTerminal? : Terminal;
+    instanceSocketList: Record<string, SocketClient>;
 }
 
 // For command line arguments, so they are nullable

+ 1 - 0
package.json

@@ -31,6 +31,7 @@
         "composerize": "~1.4.1",
         "croner": "~7.0.5",
         "dayjs": "~1.11.10",
+        "dotenv": "^16.3.1",
         "express": "~4.18.2",
         "express-static-gzip": "~2.1.7",
         "http-graceful-shutdown": "~3.1.13",

+ 8 - 0
pnpm-lock.yaml

@@ -32,6 +32,9 @@ dependencies:
   dayjs:
     specifier: ~1.11.10
     version: 1.11.10
+  dotenv:
+    specifier: ^16.3.1
+    version: 16.3.1
   express:
     specifier: ~4.18.2
     version: 4.18.2
@@ -2151,6 +2154,11 @@ packages:
       esutils: 2.0.3
     dev: true
 
+  /dotenv@16.3.1:
+    resolution: {integrity: sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==}
+    engines: {node: '>=12'}
+    dev: false
+
   /eastasianwidth@0.2.0:
     resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
     dev: false