Merge pull request #3163 from ajhollid/fix/app

fix: app -> ts
This commit is contained in:
Alexander Holliday
2026-01-21 11:20:03 -08:00
committed by GitHub
10 changed files with 152 additions and 89 deletions
+47
View File
@@ -45,6 +45,9 @@
},
"devDependencies": {
"@eslint/js": "^9.17.0",
"@types/compression": "1.8.1",
"@types/cookie-parser": "1.4.10",
"@types/cors": "2.8.19",
"@types/dockerode": "^4.0.0",
"@types/express": "5.0.3",
"@types/gamedig": "^5.0.3",
@@ -57,6 +60,7 @@
"@types/nodemailer": "7.0.1",
"@types/papaparse": "^5.5.2",
"@types/ping": "0.4.4",
"@types/swagger-ui-express": "4.1.8",
"c8": "10.1.3",
"eslint": "^9.17.0",
"eslint-plugin-mocha": "^10.5.0",
@@ -4323,6 +4327,17 @@
"@types/node": "*"
}
},
"node_modules/@types/compression": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/@types/compression/-/compression-1.8.1.tgz",
"integrity": "sha512-kCFuWS0ebDbmxs0AXYn6e2r2nrGAb5KwQhknjSPSPgJcGd8+HVSILlUyFhGqML2gk39HcG7D1ydW9/qpYkN00Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/express": "*",
"@types/node": "*"
}
},
"node_modules/@types/connect": {
"version": "3.4.38",
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz",
@@ -4333,6 +4348,26 @@
"@types/node": "*"
}
},
"node_modules/@types/cookie-parser": {
"version": "1.4.10",
"resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.10.tgz",
"integrity": "sha512-B4xqkqfZ8Wek+rCOeRxsjMS9OgvzebEzzLYw7NHYuvzb7IdxOkI0ZHGgeEBX4PUM7QGVvNSK60T3OvWj3YfBRg==",
"dev": true,
"license": "MIT",
"peerDependencies": {
"@types/express": "*"
}
},
"node_modules/@types/cors": {
"version": "2.8.19",
"resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz",
"integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/docker-modem": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/@types/docker-modem/-/docker-modem-3.0.6.tgz",
@@ -4369,6 +4404,7 @@
"integrity": "sha512-wGA0NX93b19/dZC1J18tKWVIYWyyF2ZjT9vin/NRu0qzzvfVzWjs04iq2rQ3H65vCTQYlRqs3YHfY7zjdV+9Kw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@types/body-parser": "*",
"@types/express-serve-static-core": "^5.0.0",
@@ -4649,6 +4685,17 @@
"dev": true,
"license": "MIT"
},
"node_modules/@types/swagger-ui-express": {
"version": "4.1.8",
"resolved": "https://registry.npmjs.org/@types/swagger-ui-express/-/swagger-ui-express-4.1.8.tgz",
"integrity": "sha512-AhZV8/EIreHFmBV5wAs0gzJUNq9JbbSXgJLQubCC0jtIo6prnI9MIRRxnU4MZX9RB9yXxF1V4R7jtLl/Wcj31g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/express": "*",
"@types/serve-static": "*"
}
},
"node_modules/@types/tough-cookie": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz",
+4
View File
@@ -57,6 +57,9 @@
},
"devDependencies": {
"@eslint/js": "^9.17.0",
"@types/compression": "1.8.1",
"@types/cookie-parser": "1.4.10",
"@types/cors": "2.8.19",
"@types/dockerode": "^4.0.0",
"@types/express": "5.0.3",
"@types/gamedig": "^5.0.3",
@@ -69,6 +72,7 @@
"@types/nodemailer": "7.0.1",
"@types/papaparse": "^5.5.2",
"@types/ping": "0.4.4",
"@types/swagger-ui-express": "4.1.8",
"c8": "10.1.3",
"eslint": "^9.17.0",
"eslint-plugin-mocha": "^10.5.0",
+20 -5
View File
@@ -5,12 +5,27 @@ import helmet from "helmet";
import compression from "compression";
import cookieParser from "cookie-parser";
import swaggerUi from "swagger-ui-express";
import { handleErrors } from "./middleware/handleErrors.js";
import { setupRoutes } from "./config/routes.js";
import { generalApiLimiter } from "./middleware/rateLimiter.js";
import { sanitizeBody, sanitizeQuery } from "./middleware/sanitization.js";
import { handleErrors } from "@/middleware/handleErrors.js";
import { generalApiLimiter } from "@/middleware/rateLimiter.js";
import { sanitizeBody, sanitizeQuery } from "@/middleware/sanitization.js";
import { setupRoutes } from "@/config/routes.js";
import { InitializedServices } from "@/config/services.js";
import { InitializedControllers } from "@/config/controllers.js";
import { EnvConfig } from "@/service/system/settingsService.js";
export const createApp = ({ services, controllers, envSettings, frontendPath, openApiSpec }) => {
export const createApp = ({
services,
controllers,
envSettings,
frontendPath,
openApiSpec,
}: {
services: InitializedServices;
controllers: InitializedControllers;
envSettings: EnvConfig;
frontendPath: string;
openApiSpec: any;
}) => {
const allowedOrigin = envSettings.clientHost;
const app = express();
app.use(generalApiLimiter);
+30 -21
View File
@@ -10,26 +10,35 @@ import StatusPageController from "../controllers/statusPageController.js";
import NotificationController from "../controllers/notificationController.js";
import DiagnosticController from "../controllers/diagnosticController.js";
import IncidentController from "../controllers/incidentController.js";
import type { InitializedSerivces } from "@/config/services.js";
export const initializeControllers = (services: InitializedSerivces) => {
const controllers: Record<string, any> = {};
import type { InitializedServices } from "@/config/services.js";
controllers.authController = new AuthController(services.userService);
controllers.monitorController = new MonitorController(services.monitorService);
controllers.settingsController = new SettingsController(services.settingsService, services.emailService, services.db);
controllers.checkController = new CheckController(services.checkService);
controllers.inviteController = new InviteController(services.inviteService);
controllers.maintenanceWindowController = new MaintenanceWindowController(services.maintenanceWindowService);
controllers.queueController = new QueueController(services.jobQueue);
controllers.logController = new LogController(services.logger);
controllers.statusPageController = new StatusPageController(services.db);
controllers.notificationController = new NotificationController(services.notificationsService, services.db, services.monitorsRepository);
controllers.diagnosticController = new DiagnosticController(services.diagnosticService);
controllers.incidentController = new IncidentController(services.incidentService);
return controllers;
export interface InitializedControllers {
authController: AuthController;
monitorController: MonitorController;
settingsController: SettingsController;
checkController: CheckController;
inviteController: InviteController;
maintenanceWindowController: MaintenanceWindowController;
queueController: QueueController;
logController: LogController;
statusPageController: StatusPageController;
notificationController: NotificationController;
diagnosticController: DiagnosticController;
incidentController: IncidentController;
}
export const initializeControllers = (services: InitializedServices): InitializedControllers => {
return {
authController: new AuthController(services.userService),
monitorController: new MonitorController(services.monitorService),
settingsController: new SettingsController(services.settingsService, services.emailService, services.db),
checkController: new CheckController(services.checkService),
inviteController: new InviteController(services.inviteService),
maintenanceWindowController: new MaintenanceWindowController(services.maintenanceWindowService),
queueController: new QueueController(services.jobQueue),
logController: new LogController(services.logger),
statusPageController: new StatusPageController(services.db),
notificationController: new NotificationController(services.notificationsService, services.db, services.monitorsRepository),
diagnosticController: new DiagnosticController(services.diagnosticService),
incidentController: new IncidentController(services.incidentService),
};
};
-25
View File
@@ -1,25 +0,0 @@
import dotenv from "dotenv";
export interface IEevConfig {
NODE_ENV: string;
JWT_SECRET: string;
PORT: number;
PAGESPEED_API_KEY: string;
SMTP_HOST: string;
SMTP_PORT: number;
SMTP_USER: string;
SMTP_PASS: string;
}
dotenv.config();
export const config: IEevConfig = {
NODE_ENV: process.env.NODE_ENV || "development",
JWT_SECRET: process.env.JWT_SECRET || "your_jwt_secret",
PORT: process.env.PORT ? parseInt(process.env.PORT, 10) : 3000,
PAGESPEED_API_KEY: process.env.PAGESPEED_API_KEY || "",
SMTP_HOST: process.env.SMTP_HOST || "smtp.example.com",
SMTP_PORT: process.env.SMTP_PORT ? parseInt(process.env.SMTP_PORT, 10) : 587,
SMTP_USER: process.env.SMTP_USER || "user@example.com",
SMTP_PASS: process.env.SMTP_PASS || "your_smtp_password",
};
+7 -5
View File
@@ -92,8 +92,10 @@ import {
INotificationsRepository,
IIncidentsRepository,
} from "@/repositories/index.js";
import { ILogger } from "@/utils/logger.js";
import { EnvConfig } from "@/service/system/settingsService.js";
export type InitializedSerivces = {
export type InitializedServices = {
//v1
settingsService: any;
translationService: any;
@@ -132,10 +134,10 @@ export const initializeServices = async ({
envSettings,
settingsService,
}: {
logger: any;
envSettings: any;
logger: ILogger;
envSettings: EnvConfig;
settingsService: any;
}): Promise<InitializedSerivces> => {
}): Promise<InitializedServices> => {
const translationService = new TranslationService(logger);
await translationService.initialize();
@@ -205,7 +207,7 @@ export const initializeServices = async ({
checksRepository,
});
const bufferService = new BufferService({ logger, checkService });
const bufferService = new BufferService({ logger, checkService, settingsService });
const statusService = new StatusService({ db, logger, buffer: bufferService, monitorsRepository });
+3 -3
View File
@@ -7,12 +7,12 @@ import path from "path";
import fs from "fs";
import { runMigrations } from "./db/migration/index.js";
import Logger from "./utils/logger.js";
import SettingsService from "./service/system/settingsService.js";
import Logger, { ILogger } from "@/utils/logger.js";
import SettingsService from "@/service/system/settingsService.js";
import AppSettings from "./db/models/AppSettings.js";
const SERVICE_NAME = "Server";
let logger;
let logger: ILogger;
const startApp = async () => {
// FE path
@@ -1,4 +1,3 @@
import { config } from "@/config/index.js";
import type { Check } from "@/types/index.js";
const SERVICE_NAME = "BufferService";
@@ -12,8 +11,9 @@ class BufferService {
private bufferTimer: NodeJS.Timeout | null = null;
private checksService: any;
constructor({ logger, checkService }: { logger: any; checkService: any }) {
this.BUFFER_TIMEOUT = config.NODE_ENV === "development" ? 10 : 1000 * 60 * 1; // 1 minute
constructor({ logger, checkService, settingsService }: { logger: any; checkService: any; settingsService: any }) {
this.BUFFER_TIMEOUT = settingsService.getSettings().nodeEnv === "development" ? 10 : 1000 * 60 * 1; // 1 minute
console.log(this.BUFFER_TIMEOUT);
this.logger = logger;
this.checksService = checkService;
this.SERVICE_NAME = SERVICE_NAME;
@@ -1,6 +1,17 @@
const SERVICE_NAME = "SettingsService";
const envConfig = {
export type EnvConfig = {
jwtSecret: string | undefined;
jwtTTL: string | undefined;
systemEmailHost: string | undefined;
nodeEnv: string | undefined;
logLevel: string | undefined;
clientHost: string | undefined;
dbConnectionString: string | undefined;
port: string | undefined;
};
const envConfig: EnvConfig = {
jwtSecret: process.env.JWT_SECRET,
jwtTTL: process.env.TOKEN_TTL,
systemEmailHost: process.env.SYSTEM_EMAIL_HOST,
@@ -10,19 +21,20 @@ const envConfig = {
dbConnectionString: process.env.DB_CONNECTION_STRING,
port: process.env.PORT,
};
/**
* SettingsService
*
* This service is responsible for loading and managing the application settings.
*/
class SettingsService {
static SERVICE_NAME = "SettingsService";
/**
* Constructs a new SettingsService
* @constructor
* @throws {Error}
*/ constructor(AppSettings) {
export interface ISettingsService {
readonly serviceName: string;
loadSettings(): EnvConfig;
getSettings(): EnvConfig;
getDBSettings(): Promise<Record<string, any>>;
}
class SettingsService implements ISettingsService {
static SERVICE_NAME = "SettingsService";
private AppSettings: any;
private settings: EnvConfig;
constructor(AppSettings: any) {
this.AppSettings = AppSettings;
this.settings = { ...envConfig };
}
@@ -31,19 +43,10 @@ class SettingsService {
return SettingsService.SERVICE_NAME;
}
/**
* Load settings from env settings
* @returns {Object>} The settings.
*/
loadSettings() {
return this.settings;
}
/**
* Get the current settings.
* @returns {Object} The current settings.
* @throws Will throw an error if settings have not been loaded.
*/
getSettings() {
if (!this.settings) {
throw new Error("Settings have not been loaded");
+13 -5
View File
@@ -1,6 +1,7 @@
import { createLogger, format, transports, Logger as WinstonLogger } from "winston";
import type { Logform } from "winston";
import dotenv from "dotenv";
import { EnvConfig } from "@/service/system/settingsService.js";
dotenv.config();
const SERVICE_NAME = "Logger";
@@ -18,19 +19,26 @@ interface LogEntry extends LogConfig {
timestamp: string;
}
interface EnvSettings {
logLevel?: string;
export interface ILogger {
readonly serviceName: string;
info(config: LogConfig): void;
warn(config: LogConfig): void;
error(config: LogConfig): void;
debug(config: LogConfig): void;
cacheLog(entry: LogEntry): void;
getLogs(): LogEntry[];
buildLogEntry(level: string, config: LogConfig): LogEntry;
}
class Logger {
class Logger implements ILogger {
static SERVICE_NAME = SERVICE_NAME;
private logger: WinstonLogger;
private envSettings: EnvSettings;
private envSettings: Partial<EnvConfig>;
private logCache: LogEntry[];
private maxCacheSize: number;
constructor({ envSettings }: { envSettings: EnvSettings }) {
constructor({ envSettings }: { envSettings: Partial<EnvConfig> }) {
this.envSettings = envSettings;
this.logCache = [];
this.maxCacheSize = 1000;