diff --git a/client/vite.config.ts b/client/vite.config.ts index ffa4f544d..7408636dd 100644 --- a/client/vite.config.ts +++ b/client/vite.config.ts @@ -7,7 +7,7 @@ const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); export default defineConfig(({}) => { - let version = "3.4.0"; + let version = "3.5.0"; return { base: "/", diff --git a/server/scripts/generate-checks.js b/server/scripts/generate-checks.js index 544770bb6..2e336274c 100644 --- a/server/scripts/generate-checks.js +++ b/server/scripts/generate-checks.js @@ -3,8 +3,6 @@ import { MonitorModel } from "../dist/db/models/Monitor.js"; import { CheckModel } from "../dist/db/models/Check.js"; const DEFAULT_MONITOR_ID = "000000000000000000000001"; -const DEFAULT_TEAM_ID = "0000000000000000000000aa"; -const DEFAULT_USER_ID = "0000000000000000000000bb"; const DEFAULT_MONITOR_TYPE = "http"; const DEFAULT_TOTAL = 1_000_000; const DEFAULT_BATCH_SIZE = 5_000; @@ -12,7 +10,7 @@ const DEFAULT_BATCH_SIZE = 5_000; const parseObjectId = (value, fallback) => { try { return new mongoose.Types.ObjectId(value || fallback); - } catch (error) { + } catch { console.warn(`Invalid ObjectId '${value}', falling back to '${fallback}'.`); return new mongoose.Types.ObjectId(fallback); } diff --git a/server/src/config/services.ts b/server/src/config/services.ts index 664f9b2c5..56aef2f19 100644 --- a/server/src/config/services.ts +++ b/server/src/config/services.ts @@ -29,7 +29,6 @@ import { NotificationMessageBuilder, INotificationMessageBuilder } from "../serv import axios from "axios"; import got from "got"; import ping from "ping"; -import http from "http"; import https from "https"; import Docker from "dockerode"; import net from "net"; diff --git a/server/src/controllers/statusPageController.ts b/server/src/controllers/statusPageController.ts index fa6cd3288..8593cd53f 100644 --- a/server/src/controllers/statusPageController.ts +++ b/server/src/controllers/statusPageController.ts @@ -11,7 +11,6 @@ import { requireTeamId, requireUserId } from "@/controllers/controllerUtils.js"; import { IStatusPageService } from "@/service/business/statusPageService.js"; import { IMonitorsRepository } from "@/repositories/index.js"; import { ISettingsService } from "@/service/system/settingsService.js"; -import { ParseBoolean } from "@/utils/utils.js"; import { NormalizeData } from "@/utils/dataUtils.js"; const SERVICE_NAME = "statusPageController"; @@ -110,6 +109,7 @@ class StatusPageController { const normalizedMonitors = sortedMonitors.map((monitor) => { const normalizedChecks = NormalizeData(monitor.recentChecks, 10, 100); if (!showURL) { + // eslint-disable-next-line @typescript-eslint/no-unused-vars const { url, port, secret, notifications, ...rest } = monitor; return { ...rest, checks: normalizedChecks }; } diff --git a/server/src/repositories/invites/MongoInviteRepository.ts b/server/src/repositories/invites/MongoInviteRepository.ts index e9243e96e..a8187d09a 100644 --- a/server/src/repositories/invites/MongoInviteRepository.ts +++ b/server/src/repositories/invites/MongoInviteRepository.ts @@ -36,7 +36,7 @@ class MongoInvitesRepository implements IInvitesRepository { create = async (invite: Partial) => { await InviteModel.deleteMany({ email: invite.email }); invite.token = crypto.randomBytes(32).toString("hex"); - let inviteToken = await InviteModel.create(invite); + const inviteToken = await InviteModel.create(invite); return this.toEntity(inviteToken); }; diff --git a/server/src/repositories/maintenance-windows/MongoMaintenanceWindowsRepository.ts b/server/src/repositories/maintenance-windows/MongoMaintenanceWindowsRepository.ts index a8910d0df..4aadf9cc2 100644 --- a/server/src/repositories/maintenance-windows/MongoMaintenanceWindowsRepository.ts +++ b/server/src/repositories/maintenance-windows/MongoMaintenanceWindowsRepository.ts @@ -92,7 +92,7 @@ class MongoMaintenanceWindowsRepository implements IMaintenanceWindowsRepository } // Sorting - let sort: Record = {}; + const sort: Record = {}; if (field !== undefined && order !== undefined) { sort[field] = order === "asc" ? 1 : -1; } diff --git a/server/src/repositories/settings/MongoSettingsRepository.ts b/server/src/repositories/settings/MongoSettingsRepository.ts index 91a087423..f803ae6a9 100644 --- a/server/src/repositories/settings/MongoSettingsRepository.ts +++ b/server/src/repositories/settings/MongoSettingsRepository.ts @@ -52,7 +52,7 @@ class MongoSettingsRepository implements ISettingsRepository { }; findSingleton = async () => { - let settings = await AppSettingsModel.findOne({ singleton: true }).select("-__v -_id -createdAt -updatedAt -singleton").lean(); + const settings = await AppSettingsModel.findOne({ singleton: true }).select("-__v -_id -createdAt -updatedAt -singleton").lean(); if (!settings) { return null; } diff --git a/server/src/service/business/geoChecksService.ts b/server/src/service/business/geoChecksService.ts index c2fabdcfa..1e5720656 100644 --- a/server/src/service/business/geoChecksService.ts +++ b/server/src/service/business/geoChecksService.ts @@ -170,7 +170,7 @@ class GeoChecksService implements IGeoChecksService { }); } - let { sortOrder, dateRange, page, rowsPerPage, continent } = query; + const { sortOrder, dateRange, page, rowsPerPage, continent } = query; const continents = continent ? (Array.isArray(continent) ? continent : [continent]) : undefined; this.logger.debug({ diff --git a/server/src/service/infrastructure/SuperSimpleQueue/SuperSimpleQueueHelper.ts b/server/src/service/infrastructure/SuperSimpleQueue/SuperSimpleQueueHelper.ts index 454b52b0d..3946f620a 100644 --- a/server/src/service/infrastructure/SuperSimpleQueue/SuperSimpleQueueHelper.ts +++ b/server/src/service/infrastructure/SuperSimpleQueue/SuperSimpleQueueHelper.ts @@ -2,7 +2,7 @@ const SERVICE_NAME = "JobQueueHelper"; import type { Monitor } from "@/types/monitor.js"; import { AppError } from "@/utils/AppError.js"; import { INetworkService, INotificationsService, IStatusService } from "@/service/index.js"; -import type { StatusChangeResult, MonitorStatusResponse, HardwareStatusPayload, MonitorStatus } from "@/types/index.js"; +import type { StatusChangeResult } from "@/types/index.js"; import IncidentService from "@/service/business/incidentService.js"; import type { IGeoChecksService } from "@/service/business/geoChecksService.js"; import { diff --git a/server/src/service/infrastructure/emailService.ts b/server/src/service/infrastructure/emailService.ts index 9de3999a4..0840d5a4c 100755 --- a/server/src/service/infrastructure/emailService.ts +++ b/server/src/service/infrastructure/emailService.ts @@ -1,11 +1,21 @@ import { fileURLToPath } from "url"; -import path from "path"; import { EmailTransportConfig } from "@/types/index.js"; +import { ISettingsService } from "@/service/system/settingsService.js"; +import { ILogger } from "@/utils/logger.js"; +import fs from "node:fs"; +import path from "node:path"; +import nodemailer from "nodemailer"; +import mjml2html from "mjml"; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const SERVICE_NAME = "EmailService"; +type MjmlFn = typeof mjml2html; +type FileSystem = typeof fs; +type PathModule = typeof path; +type Mailer = typeof nodemailer; +type TemplateCompiler = (template: string) => (context: Record) => string; export interface IEmailService { init(): void; @@ -16,18 +26,26 @@ export interface IEmailService { class EmailService implements IEmailService { static SERVICE_NAME = SERVICE_NAME; - private settingsService: any; - private fs: any; - private path: any; - private compile: any; - private mjml2html: any; - private nodemailer: any; - private logger: any; - private transporter: any; - private templateLookup: Record; - private loadTemplate: (templateName: string) => Function; + private settingsService: ISettingsService; + private fs: FileSystem; + private path: PathModule; + private compile: TemplateCompiler; + private mjml2html: MjmlFn; + private nodemailer: Mailer; + private logger: ILogger; + private transporter: ReturnType | null = null; + private templateLookup: Record) => string) | undefined>; + private loadTemplate: (templateName: string) => ((context: Record) => string) | undefined; - constructor(settingsService: any, fs: any, path: any, compile: any, mjml2html: any, nodemailer: any, logger: any) { + constructor( + settingsService: ISettingsService, + fs: FileSystem, + path: PathModule, + compile: TemplateCompiler, + mjml2html: MjmlFn, + nodemailer: Mailer, + logger: ILogger + ) { this.settingsService = settingsService; this.fs = fs; this.path = path; @@ -36,9 +54,7 @@ class EmailService implements IEmailService { this.nodemailer = nodemailer; this.logger = logger; this.templateLookup = {}; - this.loadTemplate = () => { - return () => {}; - }; + this.loadTemplate = () => undefined; this.init(); } @@ -52,12 +68,12 @@ class EmailService implements IEmailService { const templatePath = this.path.join(__dirname, `../../templates/${templateName}.mjml`); const templateContent = this.fs.readFileSync(templatePath, "utf8"); return this.compile(templateContent); - } catch (error: any) { + } catch (error: unknown) { this.logger.error({ - message: error.message, + message: error instanceof Error ? error.message : "Unknown error", service: SERVICE_NAME, method: "loadTemplate", - stack: error.stack, + stack: error instanceof Error ? error.stack : undefined, }); } }; @@ -72,17 +88,20 @@ class EmailService implements IEmailService { }; }; - buildEmail = async (template: string, context: Record) => { + buildEmail = async (template: string, context: Record) => { try { const mjml = this.templateLookup[template]?.(context); - const html = await this.mjml2html(mjml); + if (!mjml) { + throw new Error(`Template ${template} not found`); + } + const html = this.mjml2html(mjml); return html.html; - } catch (error: any) { + } catch (error: unknown) { this.logger.error({ - message: error.message, + message: error instanceof Error ? error.message : "Unknown error", service: SERVICE_NAME, method: "buildEmail", - stack: error.stack, + stack: error instanceof Error ? error.stack : undefined, }); } }; @@ -131,11 +150,12 @@ class EmailService implements IEmailService { try { await this.transporter.verify(); - } catch (error) { + } catch (error: unknown) { this.logger.warn({ message: "Email transporter verification failed", service: SERVICE_NAME, method: "verifyTransporter", + stack: error instanceof Error ? error.stack : undefined, }); return false; } @@ -148,12 +168,12 @@ class EmailService implements IEmailService { html: html, }); return info?.messageId; - } catch (error: any) { + } catch (error: unknown) { this.logger.error({ - message: error.message, + message: error instanceof Error ? error.message : "Unknown error", service: SERVICE_NAME, method: "sendEmail", - stack: error.stack, + stack: error instanceof Error ? error.stack : undefined, }); } }; diff --git a/server/src/service/infrastructure/globalPingService.ts b/server/src/service/infrastructure/globalPingService.ts index 1d49c6679..883b9f76b 100644 --- a/server/src/service/infrastructure/globalPingService.ts +++ b/server/src/service/infrastructure/globalPingService.ts @@ -95,12 +95,12 @@ class GlobalPingService implements IGlobalPingService { }); return measurementId; - } catch (error: any) { + } catch (error: unknown) { this.logger.error({ message: "GlobalPing API unavailable, skipping geo check", service: SERVICE_NAME, method: "createMeasurement", - details: error, + stack: error instanceof Error ? error.stack : undefined, }); return null; } @@ -140,12 +140,12 @@ class GlobalPingService implements IGlobalPingService { // Still in-progress, wait and poll again await this.sleep(POLL_INTERVAL_MS); - } catch (error: any) { + } catch (error: unknown) { this.logger.error({ message: "Error polling GlobalPing API", service: SERVICE_NAME, method: "pollForResults", - details: error.message, + stack: error instanceof Error ? error.stack : undefined, }); return []; }