Przeglądaj źródła

Check TypeScript for backend (#64)

* Check Typescript

* Fix backend typescript issues

* Update
Louis Lam 1 rok temu
rodzic
commit
53b052c1e5

+ 3 - 0
.github/workflows/ci.yml

@@ -53,5 +53,8 @@ jobs:
 
 
       - name: Lint
       - name: Lint
         run: pnpm run lint
         run: pnpm run lint
+
+      - name: Check Typescript
+        run: pnpm run check-ts
       # more things can be add later like tests etc..
       # more things can be add later like tests etc..
 
 

+ 24 - 13
backend/database.ts

@@ -5,6 +5,7 @@ import fs from "fs";
 import path from "path";
 import path from "path";
 import knex from "knex";
 import knex from "knex";
 
 
+// @ts-ignore
 import Dialect from "knex/lib/dialects/sqlite3/index.js";
 import Dialect from "knex/lib/dialects/sqlite3/index.js";
 
 
 import sqlite from "@louislam/sqlite3";
 import sqlite from "@louislam/sqlite3";
@@ -12,6 +13,11 @@ import { sleep } from "./util-common";
 
 
 interface DBConfig {
 interface DBConfig {
     type?: "sqlite" | "mysql";
     type?: "sqlite" | "mysql";
+    hostname?: string;
+    port?: string;
+    database?: string;
+    username?: string;
+    password?: string;
 }
 }
 
 
 export class Database {
 export class Database {
@@ -19,7 +25,7 @@ export class Database {
      * SQLite file path (Default: ./data/dockge.db)
      * SQLite file path (Default: ./data/dockge.db)
      * @type {string}
      * @type {string}
      */
      */
-    static sqlitePath;
+    static sqlitePath : string;
 
 
     static noReject = true;
     static noReject = true;
 
 
@@ -51,7 +57,7 @@ export class Database {
      * @typedef {string|undefined} envString
      * @typedef {string|undefined} envString
      * @returns {{type: "sqlite"} | {type:envString, hostname:envString, port:envString, database:envString, username:envString, password:envString}} Database config
      * @returns {{type: "sqlite"} | {type:envString, hostname:envString, port:envString, database:envString, username:envString, password:envString}} Database config
      */
      */
-    static readDBConfig() {
+    static readDBConfig() : DBConfig {
         const dbConfigString = fs.readFileSync(path.join(this.server.config.dataDir, "db-config.json")).toString("utf-8");
         const dbConfigString = fs.readFileSync(path.join(this.server.config.dataDir, "db-config.json")).toString("utf-8");
         const dbConfig = JSON.parse(dbConfigString);
         const dbConfig = JSON.parse(dbConfigString);
 
 
@@ -67,10 +73,10 @@ export class Database {
 
 
     /**
     /**
      * @typedef {string|undefined} envString
      * @typedef {string|undefined} envString
-     * @param {{type: "sqlite"} | {type:envString, hostname:envString, port:envString, database:envString, username:envString, password:envString}} dbConfig the database configuration that should be written
+     * @param dbConfig the database configuration that should be written
      * @returns {void}
      * @returns {void}
      */
      */
-    static writeDBConfig(dbConfig) {
+    static writeDBConfig(dbConfig : DBConfig) {
         fs.writeFileSync(path.join(this.server.config.dataDir, "db-config.json"), JSON.stringify(dbConfig, null, 4));
         fs.writeFileSync(path.join(this.server.config.dataDir, "db-config.json"), JSON.stringify(dbConfig, null, 4));
     }
     }
 
 
@@ -82,12 +88,15 @@ export class Database {
      */
      */
     static async connect(autoloadModels = true) {
     static async connect(autoloadModels = true) {
         const acquireConnectionTimeout = 120 * 1000;
         const acquireConnectionTimeout = 120 * 1000;
-        let dbConfig;
+        let dbConfig : DBConfig;
         try {
         try {
             dbConfig = this.readDBConfig();
             dbConfig = this.readDBConfig();
             Database.dbConfig = dbConfig;
             Database.dbConfig = dbConfig;
         } catch (err) {
         } catch (err) {
-            log.warn("db", err.message);
+            if (err instanceof Error) {
+                log.warn("db", err.message);
+            }
+
             dbConfig = {
             dbConfig = {
                 type: "sqlite",
                 type: "sqlite",
             };
             };
@@ -176,13 +185,15 @@ export class Database {
                 directory: Database.knexMigrationsPath,
                 directory: Database.knexMigrationsPath,
             });
             });
         } catch (e) {
         } catch (e) {
-            // Allow missing patch files for downgrade or testing pr.
-            if (e.message.includes("the following files are missing:")) {
-                log.warn("db", e.message);
-                log.warn("db", "Database migration failed, you may be downgrading Dockge.");
-            } else {
-                log.error("db", "Database migration failed");
-                throw e;
+            if (e instanceof Error) {
+                // Allow missing patch files for downgrade or testing pr.
+                if (e.message.includes("the following files are missing:")) {
+                    log.warn("db", e.message);
+                    log.warn("db", "Database migration failed, you may be downgrading Dockge.");
+                } else {
+                    log.error("db", "Database migration failed");
+                    throw e;
+                }
             }
             }
         }
         }
     }
     }

+ 12 - 6
backend/dockge-server.ts

@@ -60,7 +60,7 @@ export class DockgeServer {
      */
      */
     needSetup = false;
     needSetup = false;
 
 
-    jwtSecret? : string;
+    jwtSecret : string = "";
 
 
     stacksDir : string = "";
     stacksDir : string = "";
 
 
@@ -129,7 +129,7 @@ export class DockgeServer {
         this.config.sslKey = args.sslKey || process.env.DOCKGE_SSL_KEY || undefined;
         this.config.sslKey = args.sslKey || process.env.DOCKGE_SSL_KEY || undefined;
         this.config.sslCert = args.sslCert || process.env.DOCKGE_SSL_CERT || undefined;
         this.config.sslCert = args.sslCert || process.env.DOCKGE_SSL_CERT || undefined;
         this.config.sslKeyPassphrase = args.sslKeyPassphrase || process.env.DOCKGE_SSL_KEY_PASSPHRASE || undefined;
         this.config.sslKeyPassphrase = args.sslKeyPassphrase || process.env.DOCKGE_SSL_KEY_PASSPHRASE || undefined;
-        this.config.port = args.port || parseInt(process.env.DOCKGE_PORT) || 5001;
+        this.config.port = args.port || Number(process.env.DOCKGE_PORT) || 5001;
         this.config.hostname = args.hostname || process.env.DOCKGE_HOSTNAME || undefined;
         this.config.hostname = args.hostname || process.env.DOCKGE_HOSTNAME || undefined;
         this.config.dataDir = args.dataDir || process.env.DOCKGE_DATA_DIR || "./data/";
         this.config.dataDir = args.dataDir || process.env.DOCKGE_DATA_DIR || "./data/";
         this.config.stacksDir = args.stacksDir || process.env.DOCKGE_STACKS_DIR || defaultStacksDir;
         this.config.stacksDir = args.stacksDir || process.env.DOCKGE_STACKS_DIR || defaultStacksDir;
@@ -218,7 +218,7 @@ export class DockgeServer {
             log.debug("auth", "check auto login");
             log.debug("auth", "check auto login");
             if (await Settings.get("disableAuth")) {
             if (await Settings.get("disableAuth")) {
                 log.info("auth", "Disabled Auth: auto login to admin");
                 log.info("auth", "Disabled Auth: auto login to admin");
-                this.afterLogin(socket as DockgeSocket, await R.findOne("user"));
+                this.afterLogin(socket as DockgeSocket, await R.findOne("user") as User);
                 socket.emit("autoLogin");
                 socket.emit("autoLogin");
             } else {
             } else {
                 log.debug("auth", "need auth");
                 log.debug("auth", "need auth");
@@ -253,7 +253,9 @@ export class DockgeServer {
         try {
         try {
             await Database.init(this);
             await Database.init(this);
         } catch (e) {
         } catch (e) {
-            log.error("server", "Failed to prepare your database: " + e.message);
+            if (e instanceof Error) {
+                log.error("server", "Failed to prepare your database: " + e.message);
+            }
             process.exit(1);
             process.exit(1);
         }
         }
 
 
@@ -376,7 +378,9 @@ export class DockgeServer {
                 return process.env.TZ;
                 return process.env.TZ;
             }
             }
         } catch (e) {
         } catch (e) {
-            log.warn("timezone", e.message + " in process.env.TZ");
+            if (e instanceof Error) {
+                log.warn("timezone", e.message + " in process.env.TZ");
+            }
         }
         }
 
 
         const timezone = await Settings.get("serverTimezone");
         const timezone = await Settings.get("serverTimezone");
@@ -389,7 +393,9 @@ export class DockgeServer {
                 return timezone;
                 return timezone;
             }
             }
         } catch (e) {
         } catch (e) {
-            log.warn("timezone", e.message + " in settings");
+            if (e instanceof Error) {
+                log.warn("timezone", e.message + " in settings");
+            }
         }
         }
 
 
         // Guess
         // Guess

+ 2 - 2
backend/password-hash.ts

@@ -17,7 +17,7 @@ export function generatePasswordHash(password : string) {
  * @param {string} hash Hash to verify against
  * @param {string} hash Hash to verify against
  * @returns {boolean} Does the password match the hash?
  * @returns {boolean} Does the password match the hash?
  */
  */
-export function verifyPassword(password, hash) {
+export function verifyPassword(password : string, hash : string) {
     return bcrypt.compareSync(password, hash);
     return bcrypt.compareSync(password, hash);
 }
 }
 
 
@@ -37,7 +37,7 @@ export const SHAKE256_LENGTH = 16;
  * @param {number} len Output length of the hash
  * @param {number} len Output length of the hash
  * @returns {string} The hashed data in hex format
  * @returns {string} The hashed data in hex format
  */
  */
-export function shake256(data, len) {
+export function shake256(data : string, len : number) {
     if (!data) {
     if (!data) {
         return "";
         return "";
     }
     }

+ 10 - 4
backend/rate-limiter.ts

@@ -1,8 +1,14 @@
 // "limit" is bugged in Typescript, use "limiter-es6-compat" instead
 // "limit" is bugged in Typescript, use "limiter-es6-compat" instead
 // See https://github.com/jhurliman/node-rate-limiter/issues/80
 // See https://github.com/jhurliman/node-rate-limiter/issues/80
-import { RateLimiter } from "limiter-es6-compat";
+import { RateLimiter, RateLimiterOpts } from "limiter-es6-compat";
 import { log } from "./log";
 import { log } from "./log";
 
 
+export interface KumaRateLimiterOpts extends RateLimiterOpts {
+    errorMessage : string;
+}
+
+export type KumaRateLimiterCallback = (err : object) => void;
+
 class KumaRateLimiter {
 class KumaRateLimiter {
 
 
     errorMessage : string;
     errorMessage : string;
@@ -11,7 +17,7 @@ class KumaRateLimiter {
     /**
     /**
      * @param {object} config Rate limiter configuration object
      * @param {object} config Rate limiter configuration object
      */
      */
-    constructor(config) {
+    constructor(config : KumaRateLimiterOpts) {
         this.errorMessage = config.errorMessage;
         this.errorMessage = config.errorMessage;
         this.rateLimiter = new RateLimiter(config);
         this.rateLimiter = new RateLimiter(config);
     }
     }
@@ -24,11 +30,11 @@ class KumaRateLimiter {
 
 
     /**
     /**
      * Should the request be passed through
      * Should the request be passed through
-     * @param {passCB} callback Callback function to call with decision
+     * @param callback Callback function to call with decision
      * @param {number} num Number of tokens to remove
      * @param {number} num Number of tokens to remove
      * @returns {Promise<boolean>} Should the request be allowed?
      * @returns {Promise<boolean>} Should the request be allowed?
      */
      */
-    async pass(callback, num = 1) {
+    async pass(callback : KumaRateLimiterCallback, num = 1) {
         const remainingRequests = await this.removeTokens(num);
         const remainingRequests = await this.removeTokens(num);
         log.info("rate-limit", "remaining requests: " + remainingRequests);
         log.info("rate-limit", "remaining requests: " + remainingRequests);
         if (remainingRequests < 0) {
         if (remainingRequests < 0) {

+ 1 - 1
backend/routers/main-router.ts

@@ -1,4 +1,4 @@
-import { DockgeServer } from "../dockgeServer";
+import { DockgeServer } from "../dockge-server";
 import { Router } from "../router";
 import { Router } from "../router";
 import express, { Express, Router as ExpressRouter } from "express";
 import express, { Express, Router as ExpressRouter } from "express";
 
 

+ 18 - 18
backend/settings.ts

@@ -1,5 +1,6 @@
 import { R } from "redbean-node";
 import { R } from "redbean-node";
 import { log } from "./log";
 import { log } from "./log";
+import { LooseObject } from "./util-common";
 
 
 export class Settings {
 export class Settings {
 
 
@@ -15,20 +16,19 @@ export class Settings {
      *             timestamp: 12345678
      *             timestamp: 12345678
      *         },
      *         },
      *     }
      *     }
-     * @type {{}}
      */
      */
-    static cacheList = {
+    static cacheList : LooseObject = {
 
 
     };
     };
 
 
-    static cacheCleaner = null;
+    static cacheCleaner? : NodeJS.Timeout;
 
 
     /**
     /**
      * Retrieve value of setting based on key
      * Retrieve value of setting based on key
-     * @param {string} key Key of setting to retrieve
-     * @returns {Promise<any>} Value
+     * @param key Key of setting to retrieve
+     * @returns Value
      */
      */
-    static async get(key) {
+    static async get(key : string) {
 
 
         // Start cache clear if not started yet
         // Start cache clear if not started yet
         if (!Settings.cacheCleaner) {
         if (!Settings.cacheCleaner) {
@@ -72,12 +72,12 @@ export class Settings {
 
 
     /**
     /**
      * Sets the specified setting to specified value
      * Sets the specified setting to specified value
-     * @param {string} key Key of setting to set
-     * @param {any} value Value to set to
+     * @param key Key of setting to set
+     * @param value Value to set to
      * @param {?string} type Type of setting
      * @param {?string} type Type of setting
      * @returns {Promise<void>}
      * @returns {Promise<void>}
      */
      */
-    static async set(key, value, type = null) {
+    static async set(key : string, value : object | string | number | boolean, type : string | null = null) {
 
 
         let bean = await R.findOne("setting", " `key` = ? ", [
         let bean = await R.findOne("setting", " `key` = ? ", [
             key,
             key,
@@ -95,15 +95,15 @@ export class Settings {
 
 
     /**
     /**
      * Get settings based on type
      * Get settings based on type
-     * @param {string} type The type of setting
-     * @returns {Promise<Bean>} Settings
+     * @param type The type of setting
+     * @returns Settings
      */
      */
-    static async getSettings(type) {
+    static async getSettings(type : string) {
         const list = await R.getAll("SELECT `key`, `value` FROM setting WHERE `type` = ? ", [
         const list = await R.getAll("SELECT `key`, `value` FROM setting WHERE `type` = ? ", [
             type,
             type,
         ]);
         ]);
 
 
-        const result = {};
+        const result : LooseObject = {};
 
 
         for (const row of list) {
         for (const row of list) {
             try {
             try {
@@ -118,11 +118,11 @@ export class Settings {
 
 
     /**
     /**
      * Set settings based on type
      * Set settings based on type
-     * @param {string} type Type of settings to set
-     * @param {object} data Values of settings
+     * @param type Type of settings to set
+     * @param data Values of settings
      * @returns {Promise<void>}
      * @returns {Promise<void>}
      */
      */
-    static async setSettings(type, data) {
+    static async setSettings(type : string, data : LooseObject) {
         const keyList = Object.keys(data);
         const keyList = Object.keys(data);
 
 
         const promiseList = [];
         const promiseList = [];
@@ -154,7 +154,7 @@ export class Settings {
      * @param {string[]} keyList Keys to remove
      * @param {string[]} keyList Keys to remove
      * @returns {void}
      * @returns {void}
      */
      */
-    static deleteCache(keyList) {
+    static deleteCache(keyList : string[]) {
         for (const key of keyList) {
         for (const key of keyList) {
             delete Settings.cacheList[key];
             delete Settings.cacheList[key];
         }
         }
@@ -167,7 +167,7 @@ export class Settings {
     static stopCacheCleaner() {
     static stopCacheCleaner() {
         if (Settings.cacheCleaner) {
         if (Settings.cacheCleaner) {
             clearInterval(Settings.cacheCleaner);
             clearInterval(Settings.cacheCleaner);
-            Settings.cacheCleaner = null;
+            Settings.cacheCleaner = undefined;
         }
         }
     }
     }
 }
 }

+ 34 - 21
backend/socket-handlers/main-socket-handler.ts

@@ -5,7 +5,7 @@ import { R } from "redbean-node";
 import { loginRateLimiter, twoFaRateLimiter } from "../rate-limiter";
 import { loginRateLimiter, twoFaRateLimiter } from "../rate-limiter";
 import { generatePasswordHash, needRehashPassword, shake256, SHAKE256_LENGTH, verifyPassword } from "../password-hash";
 import { generatePasswordHash, needRehashPassword, shake256, SHAKE256_LENGTH, verifyPassword } from "../password-hash";
 import { User } from "../models/user";
 import { User } from "../models/user";
-import { checkLogin, DockgeSocket, doubleCheckPassword } from "../util-server";
+import { checkLogin, DockgeSocket, doubleCheckPassword, JWTDecoded } from "../util-server";
 import { passwordStrength } from "check-password-strength";
 import { passwordStrength } from "check-password-strength";
 import jwt from "jsonwebtoken";
 import jwt from "jsonwebtoken";
 import { Settings } from "../settings";
 import { Settings } from "../settings";
@@ -42,10 +42,12 @@ export class MainSocketHandler extends SocketHandler {
                 });
                 });
 
 
             } catch (e) {
             } catch (e) {
-                callback({
-                    ok: false,
-                    msg: e.message,
-                });
+                if (e instanceof Error) {
+                    callback({
+                        ok: false,
+                        msg: e.message,
+                    });
+                }
             }
             }
         });
         });
 
 
@@ -56,7 +58,7 @@ export class MainSocketHandler extends SocketHandler {
             log.info("auth", `Login by token. IP=${clientIP}`);
             log.info("auth", `Login by token. IP=${clientIP}`);
 
 
             try {
             try {
-                const decoded = jwt.verify(token, server.jwtSecret);
+                const decoded = jwt.verify(token, server.jwtSecret) as JWTDecoded;
 
 
                 log.info("auth", "Username from JWT: " + decoded.username);
                 log.info("auth", "Username from JWT: " + decoded.username);
 
 
@@ -90,9 +92,13 @@ export class MainSocketHandler extends SocketHandler {
                     });
                     });
                 }
                 }
             } catch (error) {
             } catch (error) {
+                if (!(error instanceof Error)) {
+                    console.error("Unknown error:", error);
+                    return;
+                }
                 log.error("auth", `Invalid token. IP=${clientIP}`);
                 log.error("auth", `Invalid token. IP=${clientIP}`);
                 if (error.message) {
                 if (error.message) {
-                    log.error("auth", error.message, `IP=${clientIP}`);
+                    log.error("auth", error.message + ` IP=${clientIP}`);
                 }
                 }
                 callback({
                 callback({
                     ok: false,
                     ok: false,
@@ -148,6 +154,7 @@ export class MainSocketHandler extends SocketHandler {
                 }
                 }
 
 
                 if (data.token) {
                 if (data.token) {
+                    // @ts-ignore
                     const verify = notp.totp.verify(data.token, user.twofa_secret, twoFAVerifyOptions);
                     const verify = notp.totp.verify(data.token, user.twofa_secret, twoFAVerifyOptions);
 
 
                     if (user.twofa_last_token !== data.token && verify) {
                     if (user.twofa_last_token !== data.token && verify) {
@@ -210,10 +217,12 @@ export class MainSocketHandler extends SocketHandler {
                 });
                 });
 
 
             } catch (e) {
             } catch (e) {
-                callback({
-                    ok: false,
-                    msg: e.message,
-                });
+                if (e instanceof Error) {
+                    callback({
+                        ok: false,
+                        msg: e.message,
+                    });
+                }
             }
             }
         });
         });
 
 
@@ -228,10 +237,12 @@ export class MainSocketHandler extends SocketHandler {
                 });
                 });
 
 
             } catch (e) {
             } catch (e) {
-                callback({
-                    ok: false,
-                    msg: e.message,
-                });
+                if (e instanceof Error) {
+                    callback({
+                        ok: false,
+                        msg: e.message,
+                    });
+                }
             }
             }
         });
         });
 
 
@@ -261,22 +272,24 @@ export class MainSocketHandler extends SocketHandler {
                 server.sendInfo(socket);
                 server.sendInfo(socket);
 
 
             } catch (e) {
             } catch (e) {
-                callback({
-                    ok: false,
-                    msg: e.message,
-                });
+                if (e instanceof Error) {
+                    callback({
+                        ok: false,
+                        msg: e.message,
+                    });
+                }
             }
             }
         });
         });
     }
     }
 
 
-    async login(username : string, password : string) {
+    async login(username : string, password : string) : Promise<User | null> {
         if (typeof username !== "string" || typeof password !== "string") {
         if (typeof username !== "string" || typeof password !== "string") {
             return null;
             return null;
         }
         }
 
 
         const user = await R.findOne("user", " username = ? AND active = 1 ", [
         const user = await R.findOne("user", " username = ? AND active = 1 ", [
             username,
             username,
-        ]);
+        ]) as User;
 
 
         if (user && verifyPassword(password, user.password)) {
         if (user && verifyPassword(password, user.password)) {
             // Upgrade the hash to bcrypt
             // Upgrade the hash to bcrypt

+ 6 - 4
backend/socket-handlers/terminal-socket-handler.ts

@@ -38,10 +38,12 @@ export class TerminalSocketHandler extends SocketHandler {
                     throw new Error("Terminal not found or it is not a Interactive Terminal.");
                     throw new Error("Terminal not found or it is not a Interactive Terminal.");
                 }
                 }
             } catch (e) {
             } catch (e) {
-                errorCallback({
-                    ok: false,
-                    msg: e.message,
-                });
+                if (e instanceof Error) {
+                    errorCallback({
+                        ok: false,
+                        msg: e.message,
+                    });
+                }
             }
             }
         });
         });
 
 

+ 3 - 1
backend/stack.ts

@@ -197,7 +197,9 @@ export class Stack {
                     stack._status = CREATED_FILE;
                     stack._status = CREATED_FILE;
                     stackList.set(filename, stack);
                     stackList.set(filename, stack);
                 } catch (e) {
                 } catch (e) {
-                    log.warn("getStackList", `Failed to get stack ${filename}, error: ${e.message}`);
+                    if (e instanceof Error) {
+                        log.warn("getStackList", `Failed to get stack ${filename}, error: ${e.message}`);
+                    }
                 }
                 }
             }
             }
 
 

+ 7 - 3
backend/terminal.ts

@@ -54,7 +54,9 @@ export class Terminal {
         try {
         try {
             this.ptyProcess?.resize(this.cols, this.rows);
             this.ptyProcess?.resize(this.cols, this.rows);
         } catch (e) {
         } catch (e) {
-            log.debug("Terminal", "Failed to resize terminal: " + e.message);
+            if (e instanceof Error) {
+                log.debug("Terminal", "Failed to resize terminal: " + e.message);
+            }
         }
         }
     }
     }
 
 
@@ -67,7 +69,9 @@ export class Terminal {
         try {
         try {
             this.ptyProcess?.resize(this.cols, this.rows);
             this.ptyProcess?.resize(this.cols, this.rows);
         } catch (e) {
         } catch (e) {
-            log.debug("Terminal", "Failed to resize terminal: " + e.message);
+            if (e instanceof Error) {
+                log.debug("Terminal", "Failed to resize terminal: " + e.message);
+            }
         }
         }
     }
     }
 
 
@@ -85,7 +89,7 @@ export class Terminal {
 
 
         // On Data
         // On Data
         this._ptyProcess.onData((data) => {
         this._ptyProcess.onData((data) => {
-            this.buffer.push(data);
+            this.buffer.pushItem(data);
             if (this.server.io) {
             if (this.server.io) {
                 this.server.io.to(this.name).emit("terminalWrite", this.name, data);
                 this.server.io.to(this.name).emit("terminalWrite", this.name, data);
             }
             }

+ 5 - 0
backend/util-common.ts

@@ -12,6 +12,11 @@ dayjs.extend(utc);
 dayjs.extend(timezone);
 dayjs.extend(timezone);
 dayjs.extend(relativeTime);
 dayjs.extend(relativeTime);
 
 
+export interface LooseObject {
+    // eslint-disable-next-line @typescript-eslint/no-explicit-any
+    [key: string]: any
+}
+
 let randomBytes : (numBytes: number) => Uint8Array;
 let randomBytes : (numBytes: number) => Uint8Array;
 initRandomBytes();
 initRandomBytes();
 
 

+ 5 - 0
backend/util-server.ts

@@ -6,6 +6,11 @@ import { ERROR_TYPE_VALIDATION } from "./util-common";
 import { R } from "redbean-node";
 import { R } from "redbean-node";
 import { verifyPassword } from "./password-hash";
 import { verifyPassword } from "./password-hash";
 
 
+export interface JWTDecoded {
+    username : string;
+    h? : string;
+}
+
 export interface DockgeSocket extends Socket {
 export interface DockgeSocket extends Socket {
     userID: number;
     userID: number;
     consoleTerminal? : Terminal;
     consoleTerminal? : Terminal;

+ 2 - 2
backend/utils/limit-queue.ts

@@ -4,14 +4,14 @@
  */
  */
 export class LimitQueue<T> extends Array<T> {
 export class LimitQueue<T> extends Array<T> {
     __limit;
     __limit;
-    __onExceed = null;
+    __onExceed? : (item : T | undefined) => void;
 
 
     constructor(limit: number) {
     constructor(limit: number) {
         super();
         super();
         this.__limit = limit;
         this.__limit = limit;
     }
     }
 
 
-    push(value : T) {
+    pushItem(value : T) {
         super.push(value);
         super.push(value);
         if (this.length > this.__limit) {
         if (this.length > this.__limit) {
             const item = this.shift();
             const item = this.shift();

+ 1 - 28
frontend/src/util-frontend.ts

@@ -10,7 +10,7 @@ import { POSITION } from "vue-toastification";
  *
  *
  * Generated by Trelent
  * Generated by Trelent
  */
  */
-function getTimezoneOffset(timeZone) {
+function getTimezoneOffset(timeZone : string) {
     const now = new Date();
     const now = new Date();
     const tzString = now.toLocaleString("en-US", {
     const tzString = now.toLocaleString("en-US", {
         timeZone,
         timeZone,
@@ -124,33 +124,6 @@ export function hostNameRegexPattern(mqtt = false) {
     return `${ipRegexPattern}|${hostNameRegexPattern}`;
     return `${ipRegexPattern}|${hostNameRegexPattern}`;
 }
 }
 
 
-/**
- * Get the tag color options
- * Shared between components
- * @param {any} self Component
- * @returns {object[]} Colour options
- */
-export function colorOptions(self) {
-    return [
-        { name: self.$t("Gray"),
-            color: "#4B5563" },
-        { name: self.$t("Red"),
-            color: "#DC2626" },
-        { name: self.$t("Orange"),
-            color: "#D97706" },
-        { name: self.$t("Green"),
-            color: "#059669" },
-        { name: self.$t("Blue"),
-            color: "#2563EB" },
-        { name: self.$t("Indigo"),
-            color: "#4F46E5" },
-        { name: self.$t("Purple"),
-            color: "#7C3AED" },
-        { name: self.$t("Pink"),
-            color: "#DB2777" },
-    ];
-}
-
 /**
 /**
  * Loads the toast timeout settings from storage.
  * Loads the toast timeout settings from storage.
  * @returns {object} The toast plugin options object.
  * @returns {object} The toast plugin options object.

+ 4 - 2
package.json

@@ -5,6 +5,7 @@
     "scripts": {
     "scripts": {
         "fmt": "eslint \"**/*.{ts,vue}\" --fix",
         "fmt": "eslint \"**/*.{ts,vue}\" --fix",
         "lint": "eslint \"**/*.{ts,vue}\"",
         "lint": "eslint \"**/*.{ts,vue}\"",
+        "check-ts": "tsc --noEmit",
         "start": "tsx ./backend/index.ts",
         "start": "tsx ./backend/index.ts",
         "dev:backend": "cross-env NODE_ENV=development tsx watch ./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",
         "dev:frontend": "cross-env NODE_ENV=development vite --host --config ./frontend/vite.config.ts",
@@ -33,8 +34,8 @@
         "jwt-decode": "~3.1.2",
         "jwt-decode": "~3.1.2",
         "knex": "~2.5.1",
         "knex": "~2.5.1",
         "limiter-es6-compat": "~2.1.2",
         "limiter-es6-compat": "~2.1.2",
-        "mysql2": "^3.6.3",
-        "redbean-node": "0.3.2",
+        "mysql2": "~3.6.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",
         "timezones-list": "~3.0.2",
         "timezones-list": "~3.0.2",
@@ -49,6 +50,7 @@
         "@fortawesome/free-regular-svg-icons": "6.4.2",
         "@fortawesome/free-regular-svg-icons": "6.4.2",
         "@fortawesome/free-solid-svg-icons": "6.4.2",
         "@fortawesome/free-solid-svg-icons": "6.4.2",
         "@fortawesome/vue-fontawesome": "3.0.3",
         "@fortawesome/vue-fontawesome": "3.0.3",
+        "@types/bcryptjs": "^2.4.6",
         "@types/bootstrap": "~5.2.9",
         "@types/bootstrap": "~5.2.9",
         "@types/command-exists": "~1.2.3",
         "@types/command-exists": "~1.2.3",
         "@types/express": "~4.17.21",
         "@types/express": "~4.17.21",

+ 12 - 5
pnpm-lock.yaml

@@ -54,11 +54,11 @@ dependencies:
     specifier: ~2.1.2
     specifier: ~2.1.2
     version: 2.1.2
     version: 2.1.2
   mysql2:
   mysql2:
-    specifier: ^3.6.3
+    specifier: ~3.6.3
     version: 3.6.3
     version: 3.6.3
   redbean-node:
   redbean-node:
-    specifier: 0.3.2
-    version: 0.3.2(mysql2@3.6.3)
+    specifier: ~0.3.3
+    version: 0.3.3(mysql2@3.6.3)
   socket.io:
   socket.io:
     specifier: ~4.7.2
     specifier: ~4.7.2
     version: 4.7.2
     version: 4.7.2
@@ -97,6 +97,9 @@ devDependencies:
   '@fortawesome/vue-fontawesome':
   '@fortawesome/vue-fontawesome':
     specifier: 3.0.3
     specifier: 3.0.3
     version: 3.0.3(@fortawesome/fontawesome-svg-core@6.4.2)(vue@3.3.8)
     version: 3.0.3(@fortawesome/fontawesome-svg-core@6.4.2)(vue@3.3.8)
+  '@types/bcryptjs':
+    specifier: ^2.4.6
+    version: 2.4.6
   '@types/bootstrap':
   '@types/bootstrap':
     specifier: ~5.2.9
     specifier: ~5.2.9
     version: 5.2.9
     version: 5.2.9
@@ -1013,6 +1016,10 @@ packages:
     dev: false
     dev: false
     optional: true
     optional: true
 
 
+  /@types/bcryptjs@2.4.6:
+    resolution: {integrity: sha512-9xlo6R2qDs5uixm0bcIqCeMCE6HiQsIyel9KQySStiyqNl2tnj2mP3DX1Nf56MD6KMenNNlBBsy3LJ7gUEQPXQ==}
+    dev: true
+
   /@types/body-parser@1.19.5:
   /@types/body-parser@1.19.5:
     resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==}
     resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==}
     dependencies:
     dependencies:
@@ -3847,8 +3854,8 @@ packages:
       resolve: 1.22.8
       resolve: 1.22.8
     dev: false
     dev: false
 
 
-  /redbean-node@0.3.2(mysql2@3.6.3):
-    resolution: {integrity: sha512-39VMxPWPpPicRlU4FSJJnJuUMoxw5/4envFthHtKnLe+3qWTBje3RMrJTFZcQGLruWQ/s2LgeYzdd+d0O+p+uQ==}
+  /redbean-node@0.3.3(mysql2@3.6.3):
+    resolution: {integrity: sha512-0J59/QlShdWs1h0lsFHRfb8NwjvgIYTQKwYrvq6FykRmeX1cG2u8AgHEIRVBrm56mtKLRASVy/8ykk6fSntLdw==}
     dependencies:
     dependencies:
       '@types/node': 20.3.3
       '@types/node': 20.3.3
       await-lock: 2.2.2
       await-lock: 2.2.2

+ 6 - 2
tsconfig.json

@@ -3,6 +3,10 @@
         "module": "ESNext",
         "module": "ESNext",
         "target": "ESNext",
         "target": "ESNext",
         "strict": true,
         "strict": true,
-        "moduleResolution": "bundler"
-    }
+        "moduleResolution": "bundler",
+        "skipLibCheck": true
+    },
+    "include": [
+        "backend/**/*"
+    ],
 }
 }