Ver Fonte

Merge branch 'master' into feat-multiple-docker-endpoint

Muhammed Hussein karimi há 1 ano atrás
pai
commit
aed0f5a93d

+ 41 - 18
README.md

@@ -4,15 +4,19 @@
 
 # Dockge
 
-A fancy, easy-to-use and reactive docker `compose.yaml` stack-oriented manager.
+A fancy, easy-to-use and reactive self-hosted docker compose.yaml stack-oriented manager.
+
+![GitHub Repo stars](https://img.shields.io/github/stars/louislam/dockge?logo=github) ![GitHub issues](https://img.shields.io/github/issues/louislam/dockge?logo=github) ![GitHub pull requests](https://img.shields.io/github/issues-pr/louislam/dockge?logo=github) ![Docker Pulls](https://img.shields.io/docker/pulls/louislam/dockge?logo=docker) ![Docker Image Version (latest semver)](https://img.shields.io/docker/v/louislam/dockge?logo=docker) ![GitHub last commit (branch)](https://img.shields.io/github/last-commit/louislam/dockge/master?logo=github) ![GitHub](https://img.shields.io/github/license/louislam/dockge?logo=github)
 
 <img src="https://github.com/louislam/dockge/assets/1336778/26a583e1-ecb1-4a8d-aedf-76157d714ad7" width="900" alt="" />
 
-[View Video](https://youtu.be/AWAlOQeNpgU?t=48)
+View Video: https://youtu.be/AWAlOQeNpgU?t=48
 
 ## ⭐ Features
 
 - Manage `compose.yaml`
+  - Create/Edit/Start/Stop/Restart/Delete
+  - Update Docker Images
 - Interactive Editor for `compose.yaml`
 - Interactive Web Terminal
 - Reactive
@@ -20,20 +24,28 @@ A fancy, easy-to-use and reactive docker `compose.yaml` stack-oriented manager.
 - Easy-to-use & fancy UI
    - If you love Uptime Kuma's UI/UX, you will love this too
 - Convert `docker run ...` commands into `compose.yaml`
+- File based structure
+   - Dockge won't kidnap your compose files, they stored on your drive as usual. You can interact with them using normal `docker compose` commands   
+   <img src="https://github.com/louislam/dockge/assets/1336778/cc071864-592e-4909-b73a-343a57494002" width=300 />
+
+
+![](https://github.com/louislam/dockge/assets/1336778/89fc1023-b069-42c0-a01c-918c495f1a6a)
 
 ## 🔧 How to Install
 
 Requirements:
-- [Docker CE](https://docs.docker.com/engine/install/) 20+ is recommended
-- [Docker Compose V2](https://docs.docker.com/compose/install/linux/)
+- [Docker CE](https://docs.docker.com/engine/install/) 20+ is recommended / Podman
+- (Docker only) [Docker Compose Plugin](https://docs.docker.com/compose/install/linux/)
+- (Podman only) podman-docker (Debian: `apt install podman-docker`)
 - OS: 
-  - As long as you can run Docker CE, it should be fine, but:
-  - Debian/Raspbian Buster or lower is not supported, please upgrade to Bullseye
+  - As long as you can run Docker CE / Podman, it should be fine, but:
+  - Debian/Raspbian Buster or lower is not supported, please upgrade to Bullseye or higher
 - Arch: armv7, arm64, amd64 (a.k.a x86_64)
 
 ### Basic
 
-Default stacks directory is `/opt/stacks`.
+- Default Stacks Directory: `/opt/stacks`
+- Default Port: 5001
 
 ```
 # Create a directory that stores your stacks and stores dockge's compose.yaml
@@ -46,16 +58,16 @@ curl https://raw.githubusercontent.com/louislam/dockge/master/compose.yaml --out
 # Start Server
 docker compose up -d
 
-# If you are using docker-compose V1
+# If you are using docker-compose V1 or Podman
 # docker-compose up -d 
 ```
 
+Dockge is now running on http://localhost:5001
+
 ### Advanced
 
 If you want to store your stacks in another directory, you can change the `DOCKGE_STACKS_DIR` environment variable and volumes.
 
-For example, if you want to store your stacks in `/my-stacks`:
-
 ```yaml
 version: "3.8"
 services:
@@ -72,24 +84,29 @@ services:
       # If you want to use private registries, you need to share the auth file with Dockge:
       # - /root/.docker/:/root/.docker
 
-      # Your stacks directory in the host
-      # (The paths inside container must be the same as the host)
-      - /my-stacks:/my-stacks
+      # Your stacks directory in the host (The paths inside container must be the same as the host)
+      # ⚠️⚠️ If you did it wrong, your data could end up be written into a wrong path.
+      # ✔️✔️✔️✔️ CORRECT EXAMPLE: - /my-stacks:/my-stacks (Both paths match)
+      # ❌❌❌❌ WRONG EXAMPLE: - /docker:/my-stacks (Both paths do not match)
+      - /opt/stacks:/opt/stacks
     environment:
       # Tell Dockge where is your stacks directory
-      - DOCKGE_STACKS_DIR=/my-stacks
+      - DOCKGE_STACKS_DIR=/opt/stacks
 ```
 
 ## How to Update
 
 ```bash
-cd /opt/stacks
+cd /opt/dockge
 docker compose pull
 docker compose up -d
 ```
 
 ## Screenshots
 
+![](https://github.com/louislam/dockge/assets/1336778/e7ff0222-af2e-405c-b533-4eab04791b40)
+
+
 ![](https://github.com/louislam/dockge/assets/1336778/7139e88c-77ed-4d45-96e3-00b66d36d871)
 
 ![](https://github.com/louislam/dockge/assets/1336778/f019944c-0e87-405b-a1b8-625b35de1eeb)
@@ -105,16 +122,22 @@ docker compose up -d
 If you love this project, please consider giving this project a ⭐.
 
 
+## 🗣️
+
+### Bug Report
+https://github.com/louislam/dockge/issues
+
+### Ask for Help / Discussions
+https://github.com/louislam/dockge/discussions
+
 ## FAQ
 
 #### "Dockge"?
 
-"Dockge" is a coinage word which is created by myself. I hope it sounds like `Badge` but replacing with `Dock` - `Dock-ge`.
+"Dockge" is a coinage word which is created by myself. I hope it sounds like `Dodge`.
 
 The naming idea was coming from Twitch emotes like `sadge`, `bedge` or `wokege`. They are all ending with `-ge`.
 
-If you are not comfortable with the pronunciation, you can call it `Dockage`
-
 #### Can I manage a single container without `compose.yaml`?
 
 The main objective of Dockge is that try to use docker `compose.yaml` for everything. If you want to manage a single container, you can just use Portainer or Docker CLI.

+ 9 - 4
backend/stack.ts

@@ -72,9 +72,9 @@ export class Stack {
     }
 
     validate() {
-        // Check name, allows [a-z][A-Z][0-9] _ - only
-        if (!this.name.match(/^[a-zA-Z0-9_-]+$/)) {
-            throw new ValidationError("Stack name can only contain [a-z][A-Z][0-9] _ - only");
+        // Check name, allows [a-z][0-9] _ - only
+        if (!this.name.match(/^[a-z0-9_-]+$/)) {
+            throw new ValidationError("Stack name can only contain [a-z][0-9] _ - only");
         }
 
         // Check YAML format
@@ -149,7 +149,7 @@ export class Stack {
 
     async delete(socket?: DockgeSocket) : Promise<number> {
         const terminalName = getComposeTerminalName(this.name);
-        let exitCode = await Terminal.exec(this.server, socket, terminalName, "docker", [ "compose", "down", "--remove-orphans", "--rmi", "all" ], this.path);
+        let exitCode = await Terminal.exec(this.server, socket, terminalName, "docker", [ "compose", "down", "--remove-orphans", "all" ], this.path);
         if (exitCode !== 0) {
             throw new Error("Failed to delete, please check the terminal output for more information.");
         }
@@ -177,6 +177,11 @@ export class Stack {
 
             for (let filename of filenameList) {
                 try {
+                    // Check if it is a directory
+                    let stat = fs.statSync(path.join(stacksDir, filename));
+                    if (!stat.isDirectory()) {
+                        continue;
+                    }
                     let stack = this.getStack(server, filename);
                     stack._status = CREATED_FILE;
                     stackList.set(filename, stack);

+ 2 - 2
backend/util-common.ts

@@ -86,8 +86,8 @@ export const TERMINAL_COLS = 105;
 export const TERMINAL_ROWS = 10;
 export const PROGRESS_TERMINAL_ROWS = 8;
 
-export const COMBINED_TERMINAL_COLS = 56;
-export const COMBINED_TERMINAL_ROWS = 15;
+export const COMBINED_TERMINAL_COLS = 58;
+export const COMBINED_TERMINAL_ROWS = 20;
 
 export const ERROR_TYPE_VALIDATION = 1;
 

+ 6 - 4
compose.yaml

@@ -7,14 +7,16 @@ services:
       # Host Port : Container Port
       - 5001:5001
     volumes:
+      - /var/run/docker.sock:/var/run/docker.sock
+      - ./data:/app/data
+        
       # If you want to use private registries, you need to share the auth file with Dockge:
       # - /root/.docker/:/root/.docker
 
-      # Docker Socket
-      - /var/run/docker.sock:/var/run/docker.sock
-      # Dockge Config
-      - ./data:/app/data
       # Your stacks directory in the host (The paths inside container must be the same as the host)
+      # ⚠️⚠️ If you did it wrong, your data could end up be written into a wrong path.
+      # ✔️✔️✔️✔️ CORRECT: - /my-stacks:/my-stacks (Both paths match)
+      # ❌❌❌❌ WRONG: - /docker:/my-stacks (Both paths do not match)
       - /opt/stacks:/opt/stacks
     environment:
       # Tell Dockge where is your stacks directory

+ 20 - 0
extra/env2arg.js

@@ -0,0 +1,20 @@
+#!/usr/bin/env node
+
+import childProcess from "child_process";
+
+let env = process.env;
+
+let cmd = process.argv[2];
+let args = process.argv.slice(3);
+let replacedArgs = [];
+
+for (let arg of args) {
+    for (let key in env) {
+        arg = arg.replaceAll(`$${key}`, env[key]);
+    }
+    replacedArgs.push(arg);
+}
+
+let child = childProcess.spawn(cmd, replacedArgs);
+child.stdout.pipe(process.stdout);
+child.stderr.pipe(process.stderr);

+ 9 - 0
extra/test-docker.ts

@@ -0,0 +1,9 @@
+// Check if docker is running
+import { exec } from "child_process";
+
+exec("docker ps", (err, stdout, stderr) => {
+    if (err) {
+        console.error("Docker is not running. Please start docker and try again.");
+        process.exit(1);
+    }
+});

+ 64 - 0
extra/update-version.ts

@@ -0,0 +1,64 @@
+import pkg from "../package.json";
+import childProcess from "child_process";
+import fs from "fs";
+
+const newVersion = process.env.VERSION;
+
+console.log("New Version: " + newVersion);
+
+if (! newVersion) {
+    console.error("invalid version");
+    process.exit(1);
+}
+
+const exists = tagExists(newVersion);
+
+if (! exists) {
+    // Process package.json
+    pkg.version = newVersion;
+    fs.writeFileSync("package.json", JSON.stringify(pkg, null, 4) + "\n");
+    commit(newVersion);
+    tag(newVersion);
+} else {
+    console.log("version exists");
+}
+
+/**
+ * Commit updated files
+ * @param {string} version Version to update to
+ */
+function commit(version) {
+    let msg = "Update to " + version;
+
+    let res = childProcess.spawnSync("git", [ "commit", "-m", msg, "-a" ]);
+    let stdout = res.stdout.toString().trim();
+    console.log(stdout);
+
+    if (stdout.includes("no changes added to commit")) {
+        throw new Error("commit error");
+    }
+}
+
+/**
+ * Create a tag with the specified version
+ * @param {string} version Tag to create
+ */
+function tag(version) {
+    let res = childProcess.spawnSync("git", [ "tag", version ]);
+    console.log(res.stdout.toString().trim());
+}
+
+/**
+ * Check if a tag exists for the specified version
+ * @param {string} version Version to check
+ * @returns {boolean} Does the tag already exist
+ */
+function tagExists(version) {
+    if (! version) {
+        throw new Error("invalid version");
+    }
+
+    let res = childProcess.spawnSync("git", [ "tag", "-l", version ]);
+
+    return res.stdout.toString().trim() === version;
+}

+ 2 - 2
frontend/src/components/Terminal.vue

@@ -77,8 +77,8 @@ export default {
         }
 
         this.terminal = new Terminal({
-            fontSize: 16,
-            fontFamily: "monospace",
+            fontSize: 14,
+            fontFamily: "'JetBrains Mono', monospace",
             cursorBlink,
             cols: this.cols,
             rows: this.rows,

+ 4 - 0
frontend/src/main.ts

@@ -13,6 +13,7 @@ import Toast, { POSITION, useToast } from "vue-toastification";
 import "xterm/lib/xterm.js";
 
 // CSS
+import "@fontsource/jetbrains-mono";
 import "vue-toastification/dist/index.css";
 import "xterm/css/xterm.css";
 import "./styles/main.scss";
@@ -22,6 +23,9 @@ import socket from "./mixins/socket";
 import lang from "./mixins/lang";
 import theme from "./mixins/theme";
 
+// Set Title
+document.title = document.title + " - " + location.host;
+
 const app = createApp(rootApp());
 
 app.use(Toast, {

+ 9 - 2
frontend/src/pages/Compose.vue

@@ -68,9 +68,10 @@
                         <h4 class="mb-3">{{ $t("general") }}</h4>
                         <div class="shadow-box big-padding mb-3">
                             <!-- Stack Name -->
-                            <div class="mb-3">
+                            <div>
                                 <label for="name" class="form-label">{{ $t("stackName") }}</label>
-                                <input id="name" v-model="stack.name" type="text" class="form-control" required>
+                                <input id="name" v-model="stack.name" type="text" class="form-control" required @blur="stackNameToLowercase">
+                                <div class="form-text">Lowercase only</div>
                             </div>
                         </div>
                     </div>
@@ -582,6 +583,10 @@ export default {
             });
         },
 
+        stackNameToLowercase() {
+            this.stack.name = this.stack?.name?.toLowerCase();
+        },
+
     }
 };
 </script>
@@ -592,6 +597,8 @@ export default {
 }
 
 .editor-box {
+    font-family: 'JetBrains Mono', monospace;
+    font-size: 14px;
     &.edit-mode {
         background-color: #2c2f38 !important;
     }

+ 2 - 0
frontend/src/pages/DashboardHome.vue

@@ -227,5 +227,7 @@ table {
 .docker-run {
     background-color: $dark-bg !important;
     border: none;
+    font-family: 'JetBrains Mono', monospace;
+    font-size: 15px;
 }
 </style>

+ 4 - 0
frontend/src/styles/main.scss

@@ -680,6 +680,10 @@ code {
     }
 }
 
+.form-text {
+    color: $dark-font-color3;
+}
+
 // Vue Prism Editor bug - workaround
 // https://github.com/koca/vue-prism-editor/issues/87
 /*

+ 8 - 6
package.json

@@ -1,6 +1,6 @@
 {
     "name": "dockge",
-    "version": "1.0.0",
+    "version": "1.0.3",
     "type": "module",
     "scripts": {
         "fmt": "eslint \"**/*.{ts,vue}\" --fix",
@@ -8,14 +8,16 @@
         "start": "tsx ./backend/index.ts",
         "dev:backend": "cross-env NODE_ENV=development tsx watch ./backend/index.ts",
         "dev:frontend": "cross-env NODE_ENV=development vite --host --config ./frontend/vite.config.ts",
+        "release-final": "tsx ./extra/test-docker.ts && tsx extra/update-version.ts && pnpm run build:frontend && npm run build:docker",
         "build:frontend": "vite build --config ./frontend/vite.config.ts",
         "build:docker-base": "docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/dockge:base -f ./docker/Base.Dockerfile . --push",
-        "build:docker": "pnpm run build:frontend && docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/dockge:latest -t louislam/dockge:1 -t louislam/dockge:1.0.0 --target release -f ./docker/Dockerfile . --push",
+        "build:docker": "node ./extra/env2arg.js docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/dockge:latest -t louislam/dockge:1 -t louislam/dockge:$VERSION --target release -f ./docker/Dockerfile . --push",
         "build:docker-nightly": "pnpm run build:frontend && docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/dockge:nightly --target nightly -f ./docker/Dockerfile . --push",
         "start-docker": "docker run --rm -p 5001:5001 --name dockge louislam/dockge:latest",
         "mark-as-nightly": "tsx ./extra/mark-as-nightly.ts"
     },
     "dependencies": {
+        "@fontsource/jetbrains-mono": "^5.0.17",
         "@homebridge/node-pty-prebuilt-multiarch": "~0.11.10",
         "@louislam/sqlite3": "~15.1.6",
         "@vueform/multiselect": "^2.6.6",
@@ -24,7 +26,7 @@
         "command-exists": "~1.2.9",
         "compare-versions": "~6.1.0",
         "composerize": "~1.4.1",
-        "croner": "~7.0.4",
+        "croner": "~7.0.5",
         "dayjs": "~1.11.10",
         "express": "~4.18.2",
         "express-static-gzip": "~2.1.7",
@@ -48,10 +50,10 @@
         "@fortawesome/free-regular-svg-icons": "6.4.2",
         "@fortawesome/free-solid-svg-icons": "6.4.2",
         "@fortawesome/vue-fontawesome": "3.0.3",
-        "@types/bootstrap": "~5.2.8",
+        "@types/bootstrap": "~5.2.9",
         "@types/command-exists": "~1.2.3",
         "@types/express": "~4.17.21",
-        "@types/jsonwebtoken": "~9.0.4",
+        "@types/jsonwebtoken": "~9.0.5",
         "@typescript-eslint/eslint-plugin": "~6.8.0",
         "@typescript-eslint/parser": "~6.8.0",
         "@vitejs/plugin-vue": "~4.3.4",
@@ -74,7 +76,7 @@
         "vue-qrcode": "~2.2.0",
         "vue-router": "~4.2.5",
         "vue-toastification": "2.0.0-rc.5",
-        "xterm": "~5.4.0-beta.37",
+        "xterm": "5.4.0-beta.37",
         "xterm-addon-web-links": "~0.9.0"
     }
 }

+ 93 - 84
pnpm-lock.yaml

@@ -5,6 +5,9 @@ settings:
   excludeLinksFromLockfile: false
 
 dependencies:
+  '@fontsource/jetbrains-mono':
+    specifier: ^5.0.17
+    version: 5.0.17
   '@homebridge/node-pty-prebuilt-multiarch':
     specifier: ~0.11.10
     version: 0.11.10
@@ -30,8 +33,8 @@ dependencies:
     specifier: ~1.4.1
     version: 1.4.1
   croner:
-    specifier: ~7.0.4
-    version: 7.0.4
+    specifier: ~7.0.5
+    version: 7.0.5
   dayjs:
     specifier: ~1.11.10
     version: 1.11.10
@@ -98,8 +101,8 @@ devDependencies:
     specifier: 3.0.3
     version: 3.0.3(@fortawesome/fontawesome-svg-core@6.4.2)(vue@3.3.8)
   '@types/bootstrap':
-    specifier: ~5.2.8
-    version: 5.2.8
+    specifier: ~5.2.9
+    version: 5.2.9
   '@types/command-exists':
     specifier: ~1.2.3
     version: 1.2.3
@@ -107,8 +110,8 @@ devDependencies:
     specifier: ~4.17.21
     version: 4.17.21
   '@types/jsonwebtoken':
-    specifier: ~9.0.4
-    version: 9.0.4
+    specifier: ~9.0.5
+    version: 9.0.5
   '@typescript-eslint/eslint-plugin':
     specifier: ~6.8.0
     version: 6.8.0(@typescript-eslint/parser@6.8.0)(eslint@8.50.0)(typescript@5.2.2)
@@ -176,7 +179,7 @@ devDependencies:
     specifier: 2.0.0-rc.5
     version: 2.0.0-rc.5(vue@3.3.8)
   xterm:
-    specifier: ~5.4.0-beta.37
+    specifier: 5.4.0-beta.37
     version: 5.4.0-beta.37
   xterm-addon-web-links:
     specifier: ~0.9.0
@@ -203,16 +206,16 @@ packages:
     engines: {node: '>=6.9.0'}
     dev: true
 
-  /@babel/parser@7.23.0:
-    resolution: {integrity: sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==}
+  /@babel/parser@7.23.3:
+    resolution: {integrity: sha512-uVsWNvlVsIninV2prNz/3lHCb+5CJ+e+IUBfbjToAHODtfGYLfCFuY4AU7TskI+dAKk+njsPiBjq1gKTvZOBaw==}
     engines: {node: '>=6.0.0'}
     hasBin: true
     dependencies:
-      '@babel/types': 7.23.0
+      '@babel/types': 7.23.3
     dev: true
 
-  /@babel/types@7.23.0:
-    resolution: {integrity: sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==}
+  /@babel/types@7.23.3:
+    resolution: {integrity: sha512-OZnvoH2l8PK5eUvEcUyCt/sXgr/h+UWpVuBbOljwcrAgUl6lpchoQ++PHGyQy1AtYnVA6CEq3y5xeEI10brpXw==}
     engines: {node: '>=6.9.0'}
     dependencies:
       '@babel/helper-string-parser': 7.22.5
@@ -469,6 +472,10 @@ packages:
       - vue
     dev: true
 
+  /@fontsource/jetbrains-mono@5.0.17:
+    resolution: {integrity: sha512-Y/EtdbwKwNQTGpnMrexX8SVW6Jqlh0nX2bNHI9Z9m6FsyjbocZIFNJqwSY9bDUoi7irGtz8nuidAN7FF8wYuJA==}
+    dev: false
+
   /@fortawesome/fontawesome-common-types@6.4.2:
     resolution: {integrity: sha512-1DgP7f+XQIJbLFCTX1V2QnxVmpLdKdzzo2k8EmvDOePfchaIGQ9eCHj2up3/jNEbZuBqel5OxiaOJf37TWauRA==}
     engines: {node: '>=6'}
@@ -697,11 +704,11 @@ packages:
     resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==}
     dependencies:
       '@types/connect': 3.4.38
-      '@types/node': 20.8.10
+      '@types/node': 20.9.0
     dev: true
 
-  /@types/bootstrap@5.2.8:
-    resolution: {integrity: sha512-14do+aWZPc1w3G+YevSsy8eas1XEPhTOUNBhQX/r12YKn7ySssATJusBQ/HCQAd2nq54U8vvrftHSb1YpeJUXg==}
+  /@types/bootstrap@5.2.9:
+    resolution: {integrity: sha512-Fcg4nORBKaVUAG4F0ePWcatWQVfr3NAT9XIN+hl1PaiAwb4tq55+iua9R3exsbB3yyfhyQlHYg2foTlW86J+RA==}
     dependencies:
       '@popperjs/core': 2.11.8
     dev: true
@@ -713,7 +720,7 @@ packages:
   /@types/connect@3.4.38:
     resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==}
     dependencies:
-      '@types/node': 20.8.10
+      '@types/node': 20.9.0
     dev: true
 
   /@types/cookie@0.4.1:
@@ -723,7 +730,7 @@ packages:
   /@types/cors@2.8.16:
     resolution: {integrity: sha512-Trx5or1Nyg1Fq138PCuWqoApzvoSLWzZ25ORBiHMbbUT42g578lH1GT4TwYDbiUOLFuDsCkfLneT2105fsFWGg==}
     dependencies:
-      '@types/node': 20.8.10
+      '@types/node': 20.9.0
     dev: false
 
   /@types/estree@1.0.5:
@@ -733,10 +740,10 @@ packages:
   /@types/express-serve-static-core@4.17.41:
     resolution: {integrity: sha512-OaJ7XLaelTgrvlZD8/aa0vvvxZdUmlCn6MtWeB7TkiKW70BQLc9XEPpDLPdbo52ZhXUCrznlWdCHWxJWtdyajA==}
     dependencies:
-      '@types/node': 20.8.10
-      '@types/qs': 6.9.9
-      '@types/range-parser': 1.2.6
-      '@types/send': 0.17.3
+      '@types/node': 20.9.0
+      '@types/qs': 6.9.10
+      '@types/range-parser': 1.2.7
+      '@types/send': 0.17.4
     dev: true
 
   /@types/express@4.17.21:
@@ -744,70 +751,70 @@ packages:
     dependencies:
       '@types/body-parser': 1.19.5
       '@types/express-serve-static-core': 4.17.41
-      '@types/qs': 6.9.9
-      '@types/serve-static': 1.15.4
+      '@types/qs': 6.9.10
+      '@types/serve-static': 1.15.5
     dev: true
 
-  /@types/http-errors@2.0.3:
-    resolution: {integrity: sha512-pP0P/9BnCj1OVvQR2lF41EkDG/lWWnDyA203b/4Fmi2eTyORnBtcDoKDwjWQthELrBvWkMOrvSOnZ8OVlW6tXA==}
+  /@types/http-errors@2.0.4:
+    resolution: {integrity: sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==}
     dev: true
 
-  /@types/json-schema@7.0.14:
-    resolution: {integrity: sha512-U3PUjAudAdJBeC2pgN8uTIKgxrb4nlDF3SF0++EldXQvQBGkpFZMSnwQiIoDU77tv45VgNkl/L4ouD+rEomujw==}
+  /@types/json-schema@7.0.15:
+    resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
     dev: true
 
-  /@types/jsonwebtoken@9.0.4:
-    resolution: {integrity: sha512-8UYapdmR0QlxgvJmyE8lP7guxD0UGVMfknsdtCFZh4ovShdBl3iOI4zdvqBHrB/IS+xUj3PSx73Qkey1fhWz+g==}
+  /@types/jsonwebtoken@9.0.5:
+    resolution: {integrity: sha512-VRLSGzik+Unrup6BsouBeHsf4d1hOEgYWTm/7Nmw1sXoN1+tRly/Gy/po3yeahnP4jfnQWWAhQAqcNfH7ngOkA==}
     dependencies:
-      '@types/node': 20.8.10
+      '@types/node': 20.9.0
     dev: true
 
-  /@types/mime@1.3.4:
-    resolution: {integrity: sha512-1Gjee59G25MrQGk8bsNvC6fxNiRgUlGn2wlhGf95a59DrprnnHk80FIMMFG9XHMdrfsuA119ht06QPDXA1Z7tw==}
+  /@types/mime@1.3.5:
+    resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==}
     dev: true
 
-  /@types/mime@3.0.3:
-    resolution: {integrity: sha512-i8MBln35l856k5iOhKk2XJ4SeAWg75mLIpZB4v6imOagKL6twsukBZGDMNhdOVk7yRFTMPpfILocMos59Q1otQ==}
+  /@types/mime@3.0.4:
+    resolution: {integrity: sha512-iJt33IQnVRkqeqC7PzBHPTC6fDlRNRW8vjrgqtScAhrmMwe8c4Eo7+fUGTa+XdWrpEgpyKWMYmi2dIwMAYRzPw==}
     dev: true
 
   /@types/node@20.3.3:
     resolution: {integrity: sha512-wheIYdr4NYML61AjC8MKj/2jrR/kDQri/CIpVoZwldwhnIrD/j9jIU5bJ8yBKuB2VhpFV7Ab6G2XkBjv9r9Zzw==}
     dev: false
 
-  /@types/node@20.8.10:
-    resolution: {integrity: sha512-TlgT8JntpcbmKUFzjhsyhGfP2fsiz1Mv56im6enJ905xG1DAYesxJaeSbGqQmAw8OWPdhyJGhGSQGKRNJ45u9w==}
+  /@types/node@20.9.0:
+    resolution: {integrity: sha512-nekiGu2NDb1BcVofVcEKMIwzlx4NjHlcjhoxxKBNLtz15Y1z7MYf549DFvkHSId02Ax6kGwWntIBPC3l/JZcmw==}
     dependencies:
       undici-types: 5.26.5
 
-  /@types/qs@6.9.9:
-    resolution: {integrity: sha512-wYLxw35euwqGvTDx6zfY1vokBFnsK0HNrzc6xNHchxfO2hpuRg74GbkEW7e3sSmPvj0TjCDT1VCa6OtHXnubsg==}
+  /@types/qs@6.9.10:
+    resolution: {integrity: sha512-3Gnx08Ns1sEoCrWssEgTSJs/rsT2vhGP+Ja9cnnk9k4ALxinORlQneLXFeFKOTJMOeZUFD1s7w+w2AphTpvzZw==}
     dev: true
 
-  /@types/range-parser@1.2.6:
-    resolution: {integrity: sha512-+0autS93xyXizIYiyL02FCY8N+KkKPhILhcUSA276HxzreZ16kl+cmwvV2qAM/PuCCwPXzOXOWhiPcw20uSFcA==}
+  /@types/range-parser@1.2.7:
+    resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==}
     dev: true
 
-  /@types/semver@7.5.4:
-    resolution: {integrity: sha512-MMzuxN3GdFwskAnb6fz0orFvhfqi752yjaXylr0Rp4oDg5H0Zn1IuyRhDVvYOwAXoJirx2xuS16I3WjxnAIHiQ==}
+  /@types/semver@7.5.5:
+    resolution: {integrity: sha512-+d+WYC1BxJ6yVOgUgzK8gWvp5qF8ssV5r4nsDcZWKRWcDQLQ619tvWAxJQYGgBrO1MnLJC7a5GtiYsAoQ47dJg==}
     dev: true
 
-  /@types/send@0.17.3:
-    resolution: {integrity: sha512-/7fKxvKUoETxjFUsuFlPB9YndePpxxRAOfGC/yJdc9kTjTeP5kRCTzfnE8kPUKCeyiyIZu0YQ76s50hCedI1ug==}
+  /@types/send@0.17.4:
+    resolution: {integrity: sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==}
     dependencies:
-      '@types/mime': 1.3.4
-      '@types/node': 20.8.10
+      '@types/mime': 1.3.5
+      '@types/node': 20.9.0
     dev: true
 
-  /@types/serve-static@1.15.4:
-    resolution: {integrity: sha512-aqqNfs1XTF0HDrFdlY//+SGUxmdSUbjeRXb5iaZc3x0/vMbYmdw9qvOgHWOyyLFxSSRnUuP5+724zBgfw8/WAw==}
+  /@types/serve-static@1.15.5:
+    resolution: {integrity: sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==}
     dependencies:
-      '@types/http-errors': 2.0.3
-      '@types/mime': 3.0.3
-      '@types/node': 20.8.10
+      '@types/http-errors': 2.0.4
+      '@types/mime': 3.0.4
+      '@types/node': 20.9.0
     dev: true
 
-  /@types/web-bluetooth@0.0.18:
-    resolution: {integrity: sha512-v/ZHEj9xh82usl8LMR3GarzFY1IrbXJw5L4QfQhokjRV91q+SelFqxQWSep1ucXEZ22+dSTwLFkXeur25sPIbw==}
+  /@types/web-bluetooth@0.0.20:
+    resolution: {integrity: sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==}
     dev: true
 
   /@typescript-eslint/eslint-plugin@6.8.0(@typescript-eslint/parser@6.8.0)(eslint@8.50.0)(typescript@5.2.2):
@@ -921,8 +928,8 @@ packages:
       eslint: ^7.0.0 || ^8.0.0
     dependencies:
       '@eslint-community/eslint-utils': 4.4.0(eslint@8.50.0)
-      '@types/json-schema': 7.0.14
-      '@types/semver': 7.5.4
+      '@types/json-schema': 7.0.15
+      '@types/semver': 7.5.5
       '@typescript-eslint/scope-manager': 6.8.0
       '@typescript-eslint/types': 6.8.0
       '@typescript-eslint/typescript-estree': 6.8.0(typescript@5.2.2)
@@ -955,7 +962,7 @@ packages:
   /@vue/compiler-core@3.3.8:
     resolution: {integrity: sha512-hN/NNBUECw8SusQvDSqqcVv6gWq8L6iAktUR0UF3vGu2OhzRqcOiAno0FmBJWwxhYEXRlQJT5XnoKsVq1WZx4g==}
     dependencies:
-      '@babel/parser': 7.23.0
+      '@babel/parser': 7.23.3
       '@vue/shared': 3.3.8
       estree-walker: 2.0.2
       source-map-js: 1.0.2
@@ -971,7 +978,7 @@ packages:
   /@vue/compiler-sfc@3.3.8:
     resolution: {integrity: sha512-WMzbUrlTjfYF8joyT84HfwwXo+8WPALuPxhy+BZ6R4Aafls+jDBnSz8PDz60uFhuqFbl3HxRfxvDzrUf3THwpA==}
     dependencies:
-      '@babel/parser': 7.23.0
+      '@babel/parser': 7.23.3
       '@vue/compiler-core': 3.3.8
       '@vue/compiler-dom': 3.3.8
       '@vue/compiler-ssr': 3.3.8
@@ -997,7 +1004,7 @@ packages:
   /@vue/reactivity-transform@3.3.8:
     resolution: {integrity: sha512-49CvBzmZNtcHua0XJ7GdGifM8GOXoUMOX4dD40Y5DxI3R8OUhMlvf2nvgUAcPxaXiV5MQQ1Nwy09ADpnLQUqRw==}
     dependencies:
-      '@babel/parser': 7.23.0
+      '@babel/parser': 7.23.3
       '@vue/compiler-core': 3.3.8
       '@vue/shared': 3.3.8
       estree-walker: 2.0.2
@@ -1043,24 +1050,24 @@ packages:
     resolution: {integrity: sha512-JDWesVRmyGz9HmHp2Ooy1cb8XgKohiztwMDtjm8c0/Th+7wEZENZuYa0iY5CTvaJNANl3LVqh9BNnCc/YlM/Bg==}
     dev: false
 
-  /@vueuse/core@10.5.0(vue@3.3.8):
-    resolution: {integrity: sha512-z/tI2eSvxwLRjOhDm0h/SXAjNm8N5ld6/SC/JQs6o6kpJ6Ya50LnEL8g5hoYu005i28L0zqB5L5yAl8Jl26K3A==}
+  /@vueuse/core@10.6.0(vue@3.3.8):
+    resolution: {integrity: sha512-+Yee+g9+9BEbvkyGdn4Bf4yZx9EfocAytpV2ZlrlP7xcz+qznLmZIDqDroTvc5vtMkWZicisgEv8dt3+jL+HQg==}
     dependencies:
-      '@types/web-bluetooth': 0.0.18
-      '@vueuse/metadata': 10.5.0
-      '@vueuse/shared': 10.5.0(vue@3.3.8)
+      '@types/web-bluetooth': 0.0.20
+      '@vueuse/metadata': 10.6.0
+      '@vueuse/shared': 10.6.0(vue@3.3.8)
       vue-demi: 0.14.6(vue@3.3.8)
     transitivePeerDependencies:
       - '@vue/composition-api'
       - vue
     dev: true
 
-  /@vueuse/metadata@10.5.0:
-    resolution: {integrity: sha512-fEbElR+MaIYyCkeM0SzWkdoMtOpIwO72x8WsZHRE7IggiOlILttqttM69AS13nrDxosnDBYdyy3C5mR1LCxHsw==}
+  /@vueuse/metadata@10.6.0:
+    resolution: {integrity: sha512-mzKHkHoiK6xVz01VzQjM2l6ofUanEaofgEGPgDHcAzlvOTccPRTIdEuzneOUTYxgfm1vkDikS6rtrEw/NYlaTQ==}
     dev: true
 
-  /@vueuse/shared@10.5.0(vue@3.3.8):
-    resolution: {integrity: sha512-18iyxbbHYLst9MqU1X1QNdMHIjks6wC7XTVf0KNOv5es/Ms6gjVFCAAWTVP2JStuGqydg3DT+ExpFORUEi9yhg==}
+  /@vueuse/shared@10.6.0(vue@3.3.8):
+    resolution: {integrity: sha512-0t4MVE18sO+/4Gh0jfeOXBTjKeV4606N9kIrDOLPjFl8Rwnlodn+QC5A4LfJuysK7aOsTMjF3KnzNeueaI0xlQ==}
     dependencies:
       vue-demi: 0.14.6(vue@3.3.8)
     transitivePeerDependencies:
@@ -1287,7 +1294,7 @@ packages:
       vue: ^3.3.4
     dependencies:
       '@floating-ui/vue': 1.0.2(vue@3.3.8)
-      '@vueuse/core': 10.5.0(vue@3.3.8)
+      '@vueuse/core': 10.6.0(vue@3.3.8)
       vue: 3.3.8(typescript@5.2.2)
     transitivePeerDependencies:
       - '@vue/composition-api'
@@ -1577,8 +1584,8 @@ packages:
       vary: 1.1.2
     dev: false
 
-  /croner@7.0.4:
-    resolution: {integrity: sha512-P8Zd88km8oQ0xH8Es0u75GtOnFyCNopuAhlFv5kAnbcTuXd0xNvRTgnxnJEs63FicCOsHTL7rpu4BHzY3cMq4w==}
+  /croner@7.0.5:
+    resolution: {integrity: sha512-15HLCD7iXnMe5km54yc4LN5BH+Cg9uCQvbkJ0acHxFffE29w3Uvgb9s/l310UCVUgMwGSBNw9BAHsEb5uMgj1g==}
     engines: {node: '>=6.0'}
     dev: false
 
@@ -1755,8 +1762,8 @@ packages:
       once: 1.4.0
     dev: false
 
-  /engine.io-client@6.5.2:
-    resolution: {integrity: sha512-CQZqbrpEYnrpGqC07a9dJDz4gePZUgTPMU3NKJPSeQOyw27Tst4Pl3FemKoFGAlHzgZmKjoRmiJvbWfhCXUlIg==}
+  /engine.io-client@6.5.3:
+    resolution: {integrity: sha512-9Z0qLB0NIisTRt1DZ/8U2k12RJn8yls/nXMZLn+/N8hANT3TcYjKFKcwbw5zFQiN4NTde3TSY9zb79e1ij6j9Q==}
     dependencies:
       '@socket.io/component-emitter': 3.1.0
       debug: 4.3.4
@@ -1774,13 +1781,13 @@ packages:
     engines: {node: '>=10.0.0'}
     dev: false
 
-  /engine.io@6.5.3:
-    resolution: {integrity: sha512-IML/R4eG/pUS5w7OfcDE0jKrljWS9nwnEfsxWCIJF5eO6AHo6+Hlv+lQbdlAYsiJPHzUthLm1RUjnBzWOs45cw==}
+  /engine.io@6.5.4:
+    resolution: {integrity: sha512-KdVSDKhVKyOi+r5uEabrDLZw2qXStVvCsEB/LN3mw4WFi6Gx50jTyuxYVCwAAC0U46FdnzP/ScKRBTXb/NiEOg==}
     engines: {node: '>=10.2.0'}
     dependencies:
       '@types/cookie': 0.4.1
       '@types/cors': 2.8.16
-      '@types/node': 20.8.10
+      '@types/node': 20.9.0
       accepts: 1.3.8
       base64id: 2.0.0
       cookie: 0.4.2
@@ -2084,7 +2091,7 @@ packages:
     resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==}
     engines: {node: ^10.12.0 || >=12.0.0}
     dependencies:
-      flat-cache: 3.1.1
+      flat-cache: 3.2.0
     dev: true
 
   /fill-range@7.0.1:
@@ -2132,9 +2139,9 @@ packages:
       path-exists: 4.0.0
     dev: true
 
-  /flat-cache@3.1.1:
-    resolution: {integrity: sha512-/qM2b3LUIaIgviBQovTLvijfyOQXPtSRnRK26ksj2J7rzPIecePUIpJsZ4T02Qg+xiAEKIs5K8dsHEd+VaKa/Q==}
-    engines: {node: '>=12.0.0'}
+  /flat-cache@3.2.0:
+    resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==}
+    engines: {node: ^10.12.0 || >=12.0.0}
     dependencies:
       flatted: 3.2.9
       keyv: 4.5.4
@@ -2845,9 +2852,11 @@ packages:
       js-tokens: 4.0.0
     dev: false
 
-  /lru-cache@10.0.1:
-    resolution: {integrity: sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g==}
+  /lru-cache@10.0.2:
+    resolution: {integrity: sha512-Yj9mA8fPiVgOUpByoTZO5pNrcl5Yk37FcSHsUINpAsaBIEZIuqcCclDZJCVxqQShDsmYX8QG63svJiTbOATZwg==}
     engines: {node: 14 || >=16.14}
+    dependencies:
+      semver: 7.5.4
     dev: false
 
   /lru-cache@6.0.0:
@@ -3306,7 +3315,7 @@ packages:
     resolution: {integrity: sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==}
     engines: {node: '>=16 || 14 >=14.17'}
     dependencies:
-      lru-cache: 10.0.1
+      lru-cache: 10.0.2
       minipass: 7.0.4
     dev: false
 
@@ -3736,7 +3745,7 @@ packages:
     dependencies:
       '@socket.io/component-emitter': 3.1.0
       debug: 4.3.4
-      engine.io-client: 6.5.2
+      engine.io-client: 6.5.3
       socket.io-parser: 4.2.4
     transitivePeerDependencies:
       - bufferutil
@@ -3762,7 +3771,7 @@ packages:
       base64id: 2.0.0
       cors: 2.8.5
       debug: 4.3.4
-      engine.io: 6.5.3
+      engine.io: 6.5.4
       socket.io-adapter: 2.5.2
       socket.io-parser: 4.2.4
     transitivePeerDependencies: