Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Check TypeScript for backend #64

Merged
merged 3 commits into from
Nov 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,5 +53,8 @@ jobs:

- name: Lint
run: pnpm run lint

- name: Check Typescript
run: pnpm run check-ts
# more things can be add later like tests etc..

37 changes: 24 additions & 13 deletions backend/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,27 @@ import fs from "fs";
import path from "path";
import knex from "knex";

// @ts-ignore
import Dialect from "knex/lib/dialects/sqlite3/index.js";

import sqlite from "@louislam/sqlite3";
import { sleep } from "./util-common";

interface DBConfig {
type?: "sqlite" | "mysql";
hostname?: string;
port?: string;
database?: string;
username?: string;
password?: string;
}

export class Database {
/**
* SQLite file path (Default: ./data/dockge.db)
* @type {string}
*/
static sqlitePath;
static sqlitePath : string;

static noReject = true;

Expand Down Expand Up @@ -51,7 +57,7 @@ export class Database {
* @typedef {string|undefined} envString
* @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 dbConfig = JSON.parse(dbConfigString);

Expand All @@ -67,10 +73,10 @@ export class Database {

/**
* @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}
*/
static writeDBConfig(dbConfig) {
static writeDBConfig(dbConfig : DBConfig) {
fs.writeFileSync(path.join(this.server.config.dataDir, "db-config.json"), JSON.stringify(dbConfig, null, 4));
}

Expand All @@ -82,12 +88,15 @@ export class Database {
*/
static async connect(autoloadModels = true) {
const acquireConnectionTimeout = 120 * 1000;
let dbConfig;
let dbConfig : DBConfig;
try {
dbConfig = this.readDBConfig();
Database.dbConfig = dbConfig;
} catch (err) {
log.warn("db", err.message);
if (err instanceof Error) {
log.warn("db", err.message);
}

dbConfig = {
type: "sqlite",
};
Expand Down Expand Up @@ -176,13 +185,15 @@ export class Database {
directory: Database.knexMigrationsPath,
});
} 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;
}
}
}
}
Expand Down
18 changes: 12 additions & 6 deletions backend/dockge-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export class DockgeServer {
*/
needSetup = false;

jwtSecret? : string;
jwtSecret : string = "";

stacksDir : string = "";

Expand Down Expand Up @@ -129,7 +129,7 @@ export class DockgeServer {
this.config.sslKey = args.sslKey || process.env.DOCKGE_SSL_KEY || 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.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.dataDir = args.dataDir || process.env.DOCKGE_DATA_DIR || "./data/";
this.config.stacksDir = args.stacksDir || process.env.DOCKGE_STACKS_DIR || defaultStacksDir;
Expand Down Expand Up @@ -218,7 +218,7 @@ 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"));
this.afterLogin(socket as DockgeSocket, await R.findOne("user") as User);
socket.emit("autoLogin");
} else {
log.debug("auth", "need auth");
Expand Down Expand Up @@ -253,7 +253,9 @@ export class DockgeServer {
try {
await Database.init(this);
} 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);
}

Expand Down Expand Up @@ -376,7 +378,9 @@ export class DockgeServer {
return process.env.TZ;
}
} 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");
Expand All @@ -389,7 +393,9 @@ export class DockgeServer {
return timezone;
}
} catch (e) {
log.warn("timezone", e.message + " in settings");
if (e instanceof Error) {
log.warn("timezone", e.message + " in settings");
}
}

// Guess
Expand Down
4 changes: 2 additions & 2 deletions backend/password-hash.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export function generatePasswordHash(password : string) {
* @param {string} hash Hash to verify against
* @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);
}

Expand All @@ -37,7 +37,7 @@ export const SHAKE256_LENGTH = 16;
* @param {number} len Output length of the hash
* @returns {string} The hashed data in hex format
*/
export function shake256(data, len) {
export function shake256(data : string, len : number) {
if (!data) {
return "";
}
Expand Down
14 changes: 10 additions & 4 deletions backend/rate-limiter.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
// "limit" is bugged in Typescript, use "limiter-es6-compat" instead
// 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";

export interface KumaRateLimiterOpts extends RateLimiterOpts {
errorMessage : string;
}

export type KumaRateLimiterCallback = (err : object) => void;

class KumaRateLimiter {

errorMessage : string;
Expand All @@ -11,7 +17,7 @@ class KumaRateLimiter {
/**
* @param {object} config Rate limiter configuration object
*/
constructor(config) {
constructor(config : KumaRateLimiterOpts) {
this.errorMessage = config.errorMessage;
this.rateLimiter = new RateLimiter(config);
}
Expand All @@ -24,11 +30,11 @@ class KumaRateLimiter {

/**
* 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
* @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);
log.info("rate-limit", "remaining requests: " + remainingRequests);
if (remainingRequests < 0) {
Expand Down
2 changes: 1 addition & 1 deletion backend/routers/main-router.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { DockgeServer } from "../dockgeServer";
import { DockgeServer } from "../dockge-server";
import { Router } from "../router";
import express, { Express, Router as ExpressRouter } from "express";

Expand Down
36 changes: 18 additions & 18 deletions backend/settings.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { R } from "redbean-node";
import { log } from "./log";
import { LooseObject } from "./util-common";

export class Settings {

Expand All @@ -15,20 +16,19 @@ export class Settings {
* timestamp: 12345678
* },
* }
* @type {{}}
*/
static cacheList = {
static cacheList : LooseObject = {

};

static cacheCleaner = null;
static cacheCleaner? : NodeJS.Timeout;

/**
* 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
if (!Settings.cacheCleaner) {
Expand Down Expand Up @@ -72,12 +72,12 @@ export class Settings {

/**
* 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
* @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` = ? ", [
key,
Expand All @@ -95,15 +95,15 @@ export class Settings {

/**
* 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` = ? ", [
type,
]);

const result = {};
const result : LooseObject = {};

for (const row of list) {
try {
Expand All @@ -118,11 +118,11 @@ export class Settings {

/**
* 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>}
*/
static async setSettings(type, data) {
static async setSettings(type : string, data : LooseObject) {
const keyList = Object.keys(data);

const promiseList = [];
Expand Down Expand Up @@ -154,7 +154,7 @@ export class Settings {
* @param {string[]} keyList Keys to remove
* @returns {void}
*/
static deleteCache(keyList) {
static deleteCache(keyList : string[]) {
for (const key of keyList) {
delete Settings.cacheList[key];
}
Expand All @@ -167,7 +167,7 @@ export class Settings {
static stopCacheCleaner() {
if (Settings.cacheCleaner) {
clearInterval(Settings.cacheCleaner);
Settings.cacheCleaner = null;
Settings.cacheCleaner = undefined;
}
}
}
Expand Down
Loading
Loading