Merge pull request #3379 from bluewave-labs/fix/network-providers

fix: network providers
This commit is contained in:
Alexander Holliday
2026-03-05 14:52:33 -08:00
committed by GitHub
20 changed files with 242 additions and 244 deletions
+72 -51
View File
@@ -1,31 +1,65 @@
import MongoDB from "../db/MongoDB.js";
import NetworkService from "../service/infrastructure/networkService.js";
import EmailService from "../service/infrastructure/emailService.js";
import BufferService from "../service/infrastructure/bufferService.js";
import { IDb } from "@/db/IDb.js";
import {
// Service classes
NetworkService,
EmailService,
BufferService,
GlobalPingService,
SuperSimpleQueue,
SuperSimpleQueueHelper,
NotificationsService,
StatusService,
NotificationMessageBuilder,
MonitorService,
StatusPageService,
UserService,
CheckService,
GeoChecksService,
DiagnosticService,
InviteService,
MaintenanceWindowService,
IncidentService,
// Notification providers
WebhookProvider,
SlackProvider,
EmailProvider,
DiscordProvider,
PagerDutyProvider,
MatrixProvider,
// Interfaces
INetworkService,
IEmailService,
IBufferService,
ISuperSimpleQueue,
INotificationsService,
IStatusService,
IMonitorService,
IUserService,
ICheckService,
IGeoChecksService,
IDiagnosticService,
IInviteService,
IMaintenanceWindowService,
IStatusPageService,
IIncidentService,
INotificationMessageBuilder,
ISettingsService,
EnvConfig,
} from "@/service/index.js";
import SuperSimpleQueueHelper from "../service/infrastructure/SuperSimpleQueue/SuperSimpleQueueHelper.js";
import SuperSimpleQueue from "../service/infrastructure/SuperSimpleQueue/SuperSimpleQueue.js";
import UserService from "../service/business/userService.js";
import CheckService from "../service/business/checkService.js";
import GeoChecksService from "../service/business/geoChecksService.js";
import GlobalPingService from "../service/infrastructure/globalPingService.js";
import DiagnosticService from "../service/business/diagnosticService.js";
import InviteService from "../service/business/inviteService.js";
import MaintenanceWindowService from "../service/business/maintenanceWindowService.js";
import { MonitorService } from "@/service/index.js";
import { StatusPageService, IStatusPageService } from "../service/business/statusPageService.js";
import IncidentService from "../service/business/incidentService.js";
import { NotificationMessageBuilder, INotificationMessageBuilder } from "../service/infrastructure/notificationMessageBuilder.js";
// Network providers
import { PingProvider } from "@/service/infrastructure/network/PingProvider.js";
import { HttpProvider } from "@/service/infrastructure/network/HttpProvider.js";
import { AdvancedMatcher } from "@/service/infrastructure/network/AdvancedMatcher.js";
import { PageSpeedProvider } from "@/service/infrastructure/network/PageSpeedProvider.js";
import { HardwareProvider } from "@/service/infrastructure/network/HardwareProvider.js";
import { DockerProvider } from "@/service/infrastructure/network/DockerProvider.js";
import { PortProvider } from "@/service/infrastructure/network/PortProvider.js";
import { GameProvider } from "@/service/infrastructure/network/GameProvider.js";
import { GrpcProvider } from "@/service/infrastructure/network/GrpcProvider.js";
// Third-party
import axios from "axios";
import got from "got";
import ping from "ping";
@@ -44,7 +78,7 @@ import jmespath from "jmespath";
import * as grpc from "@grpc/grpc-js";
import * as protoLoader from "@grpc/proto-loader";
// repositories
// Repositories
import {
MongoMonitorsRepository,
MongoChecksRepository,
@@ -73,33 +107,23 @@ import {
IMaintenanceWindowsRepository,
} from "@/repositories/index.js";
import { ILogger } from "@/utils/logger.js";
import { EnvConfig } from "@/service/system/settingsService.js";
import { PingProvider } from "@/service/infrastructure/network/PingProvider.js";
import { HttpProvider } from "@/service/infrastructure/network/HttpProvider.js";
import { AdvancedMatcher } from "@/service/infrastructure/network/AdvancedMatcher.js";
import { PageSpeedProvider } from "@/service/infrastructure/network/PageSpeedProvider.js";
import { HardwareProvider } from "@/service/infrastructure/network/HardwareProvider.js";
import { DockerProvider } from "@/service/infrastructure/network/DockerProvider.js";
import { PortProvider } from "@/service/infrastructure/network/PortProvider.js";
import { GameProvider } from "@/service/infrastructure/network/GameProvider.js";
import { GrpcProvider } from "@/service/infrastructure/network/GrpcProvider.js";
export type InitializedServices = {
settingsService: any;
db: any;
networkService: any;
emailService: any;
bufferService: any;
statusService: any;
jobQueue: any;
userService: any;
checkService: any;
geoChecksService: any;
diagnosticService: any;
inviteService: any;
maintenanceWindowService: any;
monitorService: any;
incidentService: any;
settingsService: ISettingsService;
db: IDb;
networkService: INetworkService;
emailService: IEmailService;
bufferService: IBufferService;
statusService: IStatusService;
jobQueue: ISuperSimpleQueue;
userService: IUserService;
checkService: ICheckService;
geoChecksService: IGeoChecksService;
diagnosticService: IDiagnosticService;
inviteService: IInviteService;
maintenanceWindowService: IMaintenanceWindowService;
monitorService: IMonitorService;
incidentService: IIncidentService;
logger: ILogger;
notificationsService: INotificationsService;
statusPageService: IStatusPageService;
@@ -129,7 +153,7 @@ export const initializeServices = async ({
}: {
logger: ILogger;
envSettings: EnvConfig;
settingsService: any;
settingsService: ISettingsService;
settingsRepository: ISettingsRepository;
}): Promise<InitializedServices> => {
// Create DB
@@ -152,8 +176,7 @@ export const initializeServices = async ({
const teamsRepository = new MongoTeamsRepository();
const maintenanceWindowsRepository = new MongoMaintenanceWindowsRepository();
// Providers
// Network providers
const pingProvider = new PingProvider(ping);
const httpProvider = new HttpProvider(got, new AdvancedMatcher(jmespath));
const pageSpeedProvider = new PageSpeedProvider(httpProvider, settingsService, logger);
@@ -163,9 +186,7 @@ export const initializeServices = async ({
const gameProvider = new GameProvider(logger, GameDig);
const grpcProvider = new GrpcProvider(grpc, protoLoader);
const networkService = new NetworkService(
axios,
logger,
const networkService = new NetworkService(axios, logger, [
pingProvider,
httpProvider,
pageSpeedProvider,
@@ -173,8 +194,8 @@ export const initializeServices = async ({
dockerProvider,
portProvider,
gameProvider,
grpcProvider
);
grpcProvider,
]);
const emailService = new EmailService(settingsService, fs, path, compile, mjml2html, nodemailer, logger);
const notificationMessageBuilder = new NotificationMessageBuilder();
@@ -196,6 +217,7 @@ export const initializeServices = async ({
const statusService = new StatusService(logger, bufferService, monitorsRepository, monitorStatsRepository, checksRepository);
// Notification providers
const webhookProvider = new WebhookProvider(logger);
const slackProvider = new SlackProvider(logger);
const emailProvider = new EmailProvider(emailService, logger);
@@ -279,7 +301,6 @@ export const initializeServices = async ({
const statusPageService = new StatusPageService(statusPagesRepository);
const services = {
//v1
settingsService,
db,
networkService,
+4
View File
@@ -0,0 +1,4 @@
export interface IDb {
connect(): Promise<void>;
disconnect(): Promise<void>;
}
+11 -9
View File
@@ -1,10 +1,12 @@
import mongoose from "mongoose";
import AppSettings from "./models/AppSettings.js";
import { runMigrations } from "./migration/index.js";
import AppSettings from "@/db/models/AppSettings.js";
import { runMigrations } from "@/db/migration/index.js";
import { ILogger } from "@/utils/logger.js";
import { EnvConfig } from "@/service/system/settingsService.js";
const SERVICE_NAME = "MongoDB";
class MongoDB {
import { IDb } from "@/db/IDb.js";
class MongoDB implements IDb {
static SERVICE_NAME = SERVICE_NAME;
private logger: ILogger;
@@ -46,12 +48,12 @@ class MongoDB {
});
await runMigrations();
} catch (error: any) {
} catch (error: unknown) {
this.logger.error({
message: error.message,
message: error instanceof Error ? error.message : "Unknown error",
service: SERVICE_NAME,
method: "connect",
stack: error.stack,
stack: error instanceof Error ? error.stack : undefined,
});
throw error;
}
@@ -63,12 +65,12 @@ class MongoDB {
await mongoose.disconnect();
this.logger.info({ message: "Disconnected from MongoDB", service: SERVICE_NAME, method: "disconnect" });
return;
} catch (error: any) {
} catch (error: unknown) {
this.logger.error({
message: error.message,
message: error instanceof Error ? error.message : "Unknown error",
service: SERVICE_NAME,
method: "disconnect",
stack: error.stack,
stack: error instanceof Error ? error.stack : undefined,
});
}
};
+1 -1
View File
@@ -9,7 +9,7 @@ import fs from "fs";
import { runMigrations } from "./db/migration/index.js";
import Logger, { ILogger } from "@/utils/logger.js";
import SettingsService from "@/service/system/settingsService.js";
import { SettingsService } from "@/service/index.js";
import { MongoSettingsRepository } from "./repositories/index.js";
const SERVICE_NAME = "Server";
+1 -2
View File
@@ -19,7 +19,7 @@ export interface ICheckService {
updateChecksTTL(params: { teamId: string; ttl: string }): Promise<void>;
}
class CheckService implements ICheckService {
export class CheckService implements ICheckService {
static SERVICE_NAME = SERVICE_NAME;
private monitorsRepository: IMonitorsRepository;
@@ -195,4 +195,3 @@ class CheckService implements ICheckService {
};
}
export default CheckService;
@@ -3,15 +3,19 @@ import os from "os";
const SERVICE_NAME = "diagnosticService";
interface MemoryUsageMetrics {
rss: number;
heapTotal: number;
heapUsed: number;
external: number;
arrayBuffers: number;
export interface IDiagnosticService {
getCPUUsage(): Promise<{ userUsageMs: number; systemUsageMs: number; usagePercentage: number }>;
getSystemStats(): Promise<{
osStats: { freeMemoryBytes: number; totalMemoryBytes: number };
memoryUsage: Record<keyof NodeJS.MemoryUsage, number>;
cpuUsage: { userUsageMs: number; systemUsageMs: number; usagePercentage: number };
v8HeapStats: { totalHeapSizeBytes: number; usedHeapSizeBytes: number; heapSizeLimitBytes: number };
eventLoopDelayMs: number;
uptimeMs: number;
}>;
}
class DiagnosticService {
export class DiagnosticService implements IDiagnosticService {
static SERVICE_NAME = SERVICE_NAME;
constructor() {
@@ -104,4 +108,3 @@ class DiagnosticService {
};
}
export default DiagnosticService;
@@ -16,7 +16,7 @@ export interface IGeoChecksService {
getGeoChecksByMonitor(args: { monitorId: string; query: any; teamId: string }): Promise<any>;
}
class GeoChecksService implements IGeoChecksService {
export class GeoChecksService implements IGeoChecksService {
static SERVICE_NAME = SERVICE_NAME;
private logger: ILogger;
@@ -189,4 +189,3 @@ class GeoChecksService implements IGeoChecksService {
};
}
export default GeoChecksService;
+24 -3
View File
@@ -5,12 +5,34 @@ import { AppError } from "@/utils/AppError.js";
import { ParseBoolean } from "@/utils/utils.js";
import { getDateForRange } from "@/utils/dataUtils.js";
import type { IIncidentsRepository, IMonitorsRepository, IUsersRepository } from "@/repositories/index.js";
import type { Incident } from "@/types/index.js";
import type { Incident, IncidentSummary, User } from "@/types/index.js";
import type { MonitorActionDecision } from "@/service/infrastructure/SuperSimpleQueue/SuperSimpleQueueHelper.js";
import type { INotificationMessageBuilder } from "@/service/infrastructure/notificationMessageBuilder.js";
import type { ILogger } from "@/utils/logger.js";
class IncidentService {
export interface IIncidentService {
handleIncident(
monitor: Monitor,
code: number,
decision: MonitorActionDecision,
monitorStatusResponse?: MonitorStatusResponse
): Promise<Incident | null>;
resolveIncident(incidentId: string, userId: string, teamId: string, comment?: string, userEmail?: string): Promise<Incident>;
getIncidentsByTeam(
teamId: string,
sortOrder: string,
dateRange: string,
page: string,
rowsPerPage: string,
status: string,
monitorId: string,
resolutionType: string
): Promise<{ incidents: Incident[]; count: number }>;
getIncidentSummary(teamId: string, limit?: string): Promise<IncidentSummary>;
getIncidentById(incidentId: string, teamId: string): Promise<{ incident: Incident; monitor: Monitor; user: User | null }>;
}
export class IncidentService implements IIncidentService {
static SERVICE_NAME = SERVICE_NAME;
private logger: ILogger;
@@ -245,4 +267,3 @@ class IncidentService {
};
}
export default IncidentService;
+7 -2
View File
@@ -5,7 +5,13 @@ import { AppError } from "@/utils/AppError.js";
const SERVICE_NAME = "inviteService";
class InviteService {
export interface IInviteService {
getInviteToken(params: { invite: Partial<Invite>; teamId: string; userRoles: UserRole[] }): Promise<Invite>;
sendInviteEmail(params: { invite: Partial<Invite>; firstName: string; userRoles: UserRole[] }): Promise<void>;
verifyInviteToken(params: { inviteToken: string }): Promise<Invite>;
}
export class InviteService implements IInviteService {
static SERVICE_NAME = SERVICE_NAME;
private settingsService: any;
@@ -89,4 +95,3 @@ class InviteService {
};
}
export default InviteService;
@@ -1,10 +1,23 @@
import { IMaintenanceWindowsRepository, IMonitorsRepository } from "@/repositories/index.js";
import type { MaintenanceWindow } from "@/types/index.js";
import { ParseBoolean } from "@/utils/utils.js";
import { AppError } from "@/utils/AppError.js";
const SERVICE_NAME = "maintenanceWindowService";
class MaintenanceWindowService {
export interface IMaintenanceWindowService {
createMaintenanceWindow(params: { teamId: string; body: Partial<MaintenanceWindow> & { monitors: string[] } }): Promise<void>;
getMaintenanceWindowById(params: { id: string; teamId: string }): Promise<MaintenanceWindow>;
getMaintenanceWindowsByTeamId(params: {
teamId: string;
query: Record<string, string>;
}): Promise<{ maintenanceWindows: MaintenanceWindow[]; maintenanceWindowCount: number }>;
getMaintenanceWindowsByMonitorId(params: { monitorId: string; teamId: string }): Promise<MaintenanceWindow[]>;
deleteMaintenanceWindow(params: { id: string; teamId: string }): Promise<MaintenanceWindow>;
editMaintenanceWindow(params: { id: string; teamId: string; body: Partial<MaintenanceWindow> }): Promise<MaintenanceWindow>;
}
export class MaintenanceWindowService implements IMaintenanceWindowService {
static SERVICE_NAME = SERVICE_NAME;
private monitorsRepository: IMonitorsRepository;
private maintenanceWindowsRepository: IMaintenanceWindowsRepository;
@@ -84,4 +97,3 @@ class MaintenanceWindowService {
};
}
export default MaintenanceWindowService;
+44 -21
View File
@@ -11,10 +11,31 @@ import { canManageRole, type UserRole } from "@/types/user.js";
import bcrypt from "bcryptjs";
import { AppError } from "@/utils/AppError.js";
import { ISuperSimpleQueue } from "@/service/infrastructure/SuperSimpleQueue/SuperSimpleQueue.js";
import { IEmailService } from "@/service/infrastructure/emailService.js";
import { ISettingsService } from "@/service/system/settingsService.js";
import { ILogger } from "@/utils/logger.js";
import jwt from "jsonwebtoken";
type JwtType = typeof jwt;
const SERVICE_NAME = "userService";
class UserService {
export interface IUserService {
issueToken(payload: Partial<User>, appSettings: { jwtTTL?: string; jwtSecret?: string }): string;
registerUser(user: Partial<User>, inviteToken: string, file: unknown): Promise<{ user: User; token: string }>;
createUser(userData: Partial<User>, teamId: string, actorRoles: UserRole[], file: unknown): Promise<User>;
loginUser(email: string, password: string): Promise<{ user: User; token: string }>;
editUser(updates: Partial<User & { newPassword?: string }>, file: unknown, currentUser: User): Promise<User>;
checkSuperadminExists(): Promise<boolean>;
requestRecovery(email: string): Promise<string | false | undefined>;
validateRecovery(recoveryToken: string): Promise<void>;
resetPassword(password: string, recoveryToken: string): Promise<{ user: User; token: string }>;
deleteUser(user: User): Promise<void>;
getAllUsers(): Promise<User[]>;
getUserById(roles: UserRole[], userId: string): Promise<User>;
editUserById(userId: string, patch: Partial<User>): Promise<void>;
setPasswordByUserId(userId: string, password: string): Promise<User>;
}
export class UserService implements IUserService {
static SERVICE_NAME = SERVICE_NAME;
private hashPassword = (password: string): string => {
@@ -22,10 +43,10 @@ class UserService {
return bcrypt.hashSync(password, salt);
};
private emailService: any;
private settingsService: any;
private logger: any;
private jwt: any;
private emailService: IEmailService;
private settingsService: ISettingsService;
private logger: ILogger;
private jwt: JwtType;
private jobQueue: ISuperSimpleQueue;
private crypto: any;
private monitorsRepository: IMonitorsRepository;
@@ -50,10 +71,10 @@ class UserService {
teamsRepository,
}: {
crypto: any;
emailService: any;
settingsService: any;
logger: any;
jwt: any;
emailService: IEmailService;
settingsService: ISettingsService;
logger: ILogger;
jwt: JwtType;
jobQueue: ISuperSimpleQueue;
monitorsRepository: IMonitorsRepository;
usersRepository: IUsersRepository;
@@ -134,20 +155,23 @@ class UserService {
const html = await this.emailService.buildEmail("welcomeEmailTemplate", {
name: newUser.firstName,
});
this.emailService.sendEmail(newUser.email, "Welcome to Uptime Monitor", html).catch((error: any) => {
if (!html) {
throw new Error("Failed to build welcome email HTML");
}
this.emailService.sendEmail(newUser.email, "Welcome to Uptime Monitor", html).catch((error: unknown) => {
this.logger.warn({
message: error.message,
message: error instanceof Error ? error.message : "Unknown error",
service: SERVICE_NAME,
method: "registerUser",
stack: error.stack,
stack: error instanceof Error ? error.stack : undefined,
});
});
} catch (error: any) {
} catch (error: unknown) {
this.logger.warn({
message: error.message,
message: error instanceof Error ? error.message : "Unknown error",
service: SERVICE_NAME,
method: "registerUser",
stack: error.stack,
stack: error instanceof Error ? error.stack : undefined,
});
}
@@ -313,13 +337,13 @@ class UserService {
if (roles.includes("superadmin")) {
// 2. Remove all jobs, delete checks and alerts
res &&
res?.length > 0 &&
(await Promise.all(
if (res && res.length > 0) {
await Promise.all(
res.map(async (monitor) => {
await this.jobQueue.deleteJob(monitor.id);
})
));
);
}
}
// 6. Delete the user by id
await this.usersRepository.deleteById(userId);
@@ -348,4 +372,3 @@ class UserService {
return updatedUser;
};
}
export default UserService;
+25 -6
View File
@@ -1,14 +1,33 @@
// Business services
export * from "@/service/business/checkService.js";
export * from "@/service/business/diagnosticService.js";
export * from "@/service/business/geoChecksService.js";
export * from "@/service/business/incidentService.js";
export * from "@/service/business/inviteService.js";
export * from "@/service/business/maintenanceWindowService.js";
export * from "@/service/business/monitorService.js";
export * from "@/service/business/statusPageService.js";
export * from "@/service/business/userService.js";
// Infrastructure services
export * from "@/service/infrastructure/SuperSimpleQueue/SuperSimpleQueue.js";
export * from "@/service/infrastructure/SuperSimpleQueue/SuperSimpleQueueHelper.js";
export * from "@/service/infrastructure/notificationMessageBuilder.js";
export * from "@/service/infrastructure/bufferService.js";
export * from "@/service/infrastructure/emailService.js";
export * from "@/service/infrastructure/globalPingService.js";
export * from "@/service/infrastructure/networkService.js";
export * from "@/service/infrastructure/notificationsService.js";
export * from "@/service/infrastructure/statusService.js";
// Notification providers
export * from "@/service/infrastructure/notificationProviders/discord.js";
export * from "@/service/infrastructure/notificationProviders/email.js";
export * from "@/service/infrastructure/notificationProviders/INotificationProvider.js";
export * from "@/service/infrastructure/notificationProviders/pagerduty.js";
export * from "@/service/infrastructure/notificationProviders/matrix.js";
export * from "@/service/infrastructure/notificationProviders/pagerduty.js";
export * from "@/service/infrastructure/notificationProviders/slack.js";
export * from "@/service/infrastructure/notificationProviders/webhook.js";
export * from "@/service/infrastructure/notificationsService.js";
export * from "@/service/infrastructure/statusService.js";
export * from "@/service/infrastructure/notificationProviders/webhook.js";
export * from "@/service/infrastructure/notificationProviders/matrix.js";
export * from "@/service/business/statusPageService.js";
// System services
export * from "@/service/system/settingsService.js";
@@ -53,7 +53,7 @@ export interface ISuperSimpleQueue {
obliterate(): Promise<void>;
}
class SuperSimpleQueue implements ISuperSimpleQueue {
export class SuperSimpleQueue implements ISuperSimpleQueue {
static SERVICE_NAME = SERVICE_NAME;
private logger: ILogger;
@@ -290,4 +290,3 @@ class SuperSimpleQueue implements ISuperSimpleQueue {
};
}
export default SuperSimpleQueue;
@@ -1,10 +1,8 @@
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 { INetworkService, INotificationsService, IStatusService, IncidentService, type IGeoChecksService } from "@/service/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 {
IMaintenanceWindowsRepository,
IMonitorsRepository,
@@ -15,7 +13,7 @@ import {
IGeoChecksRepository,
} from "@/repositories/index.js";
import { ILogger } from "@/utils/logger.js";
import { IBufferService } from "../bufferService.js";
import { IBufferService } from "@/service/index.js";
export interface ISuperSimpleQueueHelper {
readonly serviceName: string;
@@ -39,7 +37,7 @@ export interface MonitorActionDecision {
};
}
class SuperSimpleQueueHelper implements ISuperSimpleQueueHelper {
export class SuperSimpleQueueHelper implements ISuperSimpleQueueHelper {
static SERVICE_NAME = SERVICE_NAME;
private logger: any;
@@ -415,4 +413,3 @@ class SuperSimpleQueueHelper implements ISuperSimpleQueueHelper {
}
}
export default SuperSimpleQueueHelper;
@@ -15,7 +15,7 @@ export interface IBufferService {
flushGeoBuffer(): Promise<void>;
}
class BufferService implements IBufferService {
export class BufferService implements IBufferService {
static SERVICE_NAME = SERVICE_NAME;
private BUFFER_TIMEOUT: number;
private logger: ILogger;
@@ -178,4 +178,3 @@ class BufferService implements IBufferService {
}
}
export default BufferService;
@@ -23,7 +23,7 @@ export interface IEmailService {
sendEmail(to: string, subject: string, html: string, transportConfig?: EmailTransportConfig): Promise<string | false | undefined>;
}
class EmailService implements IEmailService {
export class EmailService implements IEmailService {
static SERVICE_NAME = SERVICE_NAME;
private settingsService: ISettingsService;
@@ -179,4 +179,3 @@ class EmailService implements IEmailService {
};
}
export default EmailService;
@@ -54,7 +54,7 @@ export interface IGlobalPingService {
pollForResults(measurementId: string, timeoutMs?: number): Promise<GeoCheckResult[]>;
}
class GlobalPingService implements IGlobalPingService {
export class GlobalPingService implements IGlobalPingService {
static SERVICE_NAME = SERVICE_NAME;
private logger: ILogger;
@@ -207,4 +207,3 @@ class GlobalPingService implements IGlobalPingService {
}
}
export default GlobalPingService;
@@ -51,7 +51,6 @@ export class PortProvider implements IStatusProvider<PortStatusPayload> {
});
});
if (error) {
console.log(error);
const errorMessage = error instanceof Error ? error.message : "Port check failed";
return {
monitorId: monitor.id,
@@ -1,38 +1,14 @@
import type {
Monitor,
MonitorStatusResponse,
GrpcStatusPayload,
PageSpeedStatusPayload,
HttpStatusPayload,
HardwareStatusPayload,
PingStatusPayload,
DockerStatusPayload,
PortStatusPayload,
GameStatusPayload,
} from "@/types/index.js";
import type { Monitor, MonitorStatusResponse } from "@/types/index.js";
import type { AxiosStatic } from "axios";
import { AppError } from "@/utils/AppError.js";
import { NETWORK_ERROR } from "@/service/infrastructure/network/utils.js";
import { ILogger } from "@/utils/logger.js";
import { IStatusProvider } from "./network/IStatusProvider.js";
const SERVICE_NAME = "NetworkService";
export interface INetworkService {
readonly serviceName: string;
requestStatus(
monitor: Monitor
): Promise<
MonitorStatusResponse<
| PingStatusPayload
| HttpStatusPayload
| PageSpeedStatusPayload
| HardwareStatusPayload
| DockerStatusPayload
| PortStatusPayload
| GameStatusPayload
| GrpcStatusPayload
>
>;
requestStatus(monitor: Monitor): Promise<MonitorStatusResponse<unknown>>;
requestWebhook(
type: string,
url: string,
@@ -47,66 +23,19 @@ export interface INetworkService {
}>;
}
class NetworkService implements INetworkService {
export class NetworkService implements INetworkService {
static SERVICE_NAME = SERVICE_NAME;
private TYPE_PING: string;
private TYPE_HTTP: string;
private TYPE_PAGESPEED: string;
private TYPE_HARDWARE: string;
private TYPE_DOCKER: string;
private TYPE_PORT: string;
private TYPE_GAME: string;
private TYPE_GRPC: string;
private SERVICE_NAME: string;
private NETWORK_ERROR: number;
private PING_ERROR: number;
private axios: AxiosStatic;
private logger: ILogger;
private pingProvider;
private httpProvider;
private pageSpeedProvider;
private hardwareProvider;
private dockerProvider;
private portProvider;
private gameProvider;
private grpcProvider;
constructor(
axios: AxiosStatic,
logger: ILogger,
pingProvider: IStatusProvider<PingStatusPayload>,
httpProvider: IStatusProvider<HttpStatusPayload>,
pagespeedProvider: IStatusProvider<PageSpeedStatusPayload>,
hardwareProvider: IStatusProvider<HardwareStatusPayload>,
dockerProvider: IStatusProvider<DockerStatusPayload>,
portProvider: IStatusProvider<PortStatusPayload>,
gameProvider: IStatusProvider<GameStatusPayload>,
grpcProvider: IStatusProvider<GrpcStatusPayload>
private providers: IStatusProvider<unknown>[]
) {
this.TYPE_PING = "ping";
this.TYPE_HTTP = "http";
this.TYPE_PAGESPEED = "pagespeed";
this.TYPE_HARDWARE = "hardware";
this.TYPE_DOCKER = "docker";
this.TYPE_PORT = "port";
this.TYPE_GAME = "game";
this.TYPE_GRPC = "grpc";
this.SERVICE_NAME = SERVICE_NAME;
this.NETWORK_ERROR = 5000;
this.PING_ERROR = 5001;
this.axios = axios;
this.logger = logger;
this.pingProvider = pingProvider;
this.httpProvider = httpProvider;
this.pageSpeedProvider = pagespeedProvider;
this.hardwareProvider = hardwareProvider;
this.dockerProvider = dockerProvider;
this.portProvider = portProvider;
this.gameProvider = gameProvider;
this.grpcProvider = grpcProvider;
}
get serviceName(): string {
@@ -114,41 +43,12 @@ class NetworkService implements INetworkService {
}
// Main entry point
async requestStatus(
monitor: Monitor
): Promise<
MonitorStatusResponse<
| PingStatusPayload
| HttpStatusPayload
| PageSpeedStatusPayload
| HardwareStatusPayload
| DockerStatusPayload
| PortStatusPayload
| GameStatusPayload
| GrpcStatusPayload
>
> {
const type = monitor?.type || "unknown";
switch (type) {
case this.TYPE_PING:
return await this.pingProvider.handle(monitor);
case this.TYPE_HTTP:
return await this.httpProvider.handle(monitor);
case this.TYPE_PAGESPEED:
return await this.pageSpeedProvider.handle(monitor);
case this.TYPE_HARDWARE:
return await this.hardwareProvider.handle(monitor);
case this.TYPE_DOCKER:
return await this.dockerProvider.handle(monitor);
case this.TYPE_PORT:
return await this.portProvider.handle(monitor);
case this.TYPE_GAME:
return await this.gameProvider.handle(monitor);
case this.TYPE_GRPC:
return await this.grpcProvider.handle(monitor);
default:
return this.handleUnsupportedType(type);
async requestStatus(monitor: Monitor) {
const provider = this.providers.find((p) => p.supports(monitor.type));
if (!provider) {
return this.handleUnsupportedType(monitor.type);
}
return provider.handle(monitor);
}
private async handleUnsupportedType(type: string): Promise<MonitorStatusResponse> {
@@ -157,7 +57,7 @@ class NetworkService implements INetworkService {
teamId: "unknown",
type: "unknown",
status: false,
code: this.NETWORK_ERROR,
code: NETWORK_ERROR,
message: `Unsupported type: ${type}`,
};
}
@@ -181,7 +81,7 @@ class NetworkService implements INetworkService {
} catch (err: unknown) {
this.logger.warn({
message: err instanceof Error ? err.message : String(err),
service: this.SERVICE_NAME,
service: SERVICE_NAME,
method: "requestWebhook",
});
@@ -190,7 +90,7 @@ class NetworkService implements INetworkService {
return {
type: "webhook",
status: false,
code: axiosError.response?.status ?? this.NETWORK_ERROR,
code: axiosError.response?.status ?? NETWORK_ERROR,
message: `Failed to send ${type} notification`,
payload: axiosError.response?.data,
};
@@ -199,7 +99,7 @@ class NetworkService implements INetworkService {
return {
type: "webhook",
status: false,
code: this.NETWORK_ERROR,
code: NETWORK_ERROR,
message: `Failed to send ${type} notification`,
};
}
@@ -225,7 +125,7 @@ class NetworkService implements INetworkService {
throw new AppError({
message: originalMessage || "Error sending PagerDuty notification",
service: this.SERVICE_NAME,
service: SERVICE_NAME,
method: "requestPagerDuty",
details: {
responseData: err && typeof err === "object" && "response" in err ? (err as { response?: { data?: unknown } }).response?.data : undefined,
@@ -268,7 +168,7 @@ class NetworkService implements INetworkService {
if (err instanceof Error) {
this.logger.warn({
message: err.message,
service: this.SERVICE_NAME,
service: SERVICE_NAME,
method: "requestMatrix",
});
@@ -276,7 +176,7 @@ class NetworkService implements INetworkService {
const axiosError = err as { response?: { status?: number; data?: unknown } };
return {
status: false,
code: axiosError.response?.status || this.NETWORK_ERROR,
code: axiosError.response?.status || NETWORK_ERROR,
message: "Failed to send Matrix notification",
payload: axiosError.response?.data,
};
@@ -285,17 +185,16 @@ class NetworkService implements INetworkService {
this.logger.warn({
message: String(err),
service: this.SERVICE_NAME,
service: SERVICE_NAME,
method: "requestMatrix",
});
return {
status: false,
code: this.NETWORK_ERROR,
code: NETWORK_ERROR,
message: "Failed to send Matrix notification",
};
}
}
}
export default NetworkService;
+1 -2
View File
@@ -21,7 +21,7 @@ export interface ISettingsService {
getDBSettings(): Promise<Settings>;
}
class SettingsService implements ISettingsService {
export class SettingsService implements ISettingsService {
static SERVICE_NAME = SERVICE_NAME;
private settings: EnvConfig;
private settingsRepository: ISettingsRepository;
@@ -75,4 +75,3 @@ class SettingsService implements ISettingsService {
};
}
export default SettingsService;