split services to api, worker, and shared
This commit is contained in:
1 parent
f59a7c3113
commit
71d3e789ff
10 files changed
+375
-353
No files matched your search
+23
-1
@@ -386,6 +386,10 @@
|
||||
"type": "string",
|
||||
"enum": ["equal", "include", "regex"]
|
||||
},
|
||||
"method": {
|
||||
"type": "string",
|
||||
"enum": ["GET", "HEAD"]
|
||||
},
|
||||
"notifications": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
@@ -466,6 +470,9 @@
|
||||
},
|
||||
"updatedAt": {
|
||||
"type": "string"
|
||||
},
|
||||
"lastEvaluatedAt": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
@@ -480,6 +487,7 @@
|
||||
"statusWindowThreshold",
|
||||
"ignoreTlsErrors",
|
||||
"useAdvancedMatching",
|
||||
"method",
|
||||
"notifications",
|
||||
"tags",
|
||||
"cpuAlertThreshold",
|
||||
@@ -493,7 +501,8 @@
|
||||
"teamId",
|
||||
"userId",
|
||||
"createdAt",
|
||||
"updatedAt"
|
||||
"updatedAt",
|
||||
"lastEvaluatedAt"
|
||||
],
|
||||
"additionalProperties": {
|
||||
"nullable": true
|
||||
@@ -7066,6 +7075,10 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"method": {
|
||||
"type": "string",
|
||||
"enum": ["GET", "HEAD"]
|
||||
},
|
||||
"gameId": {
|
||||
"anyOf": [
|
||||
{
|
||||
@@ -7666,6 +7679,11 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"method": {
|
||||
"type": "string",
|
||||
"enum": ["GET", "HEAD"],
|
||||
"default": "GET"
|
||||
},
|
||||
"url": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
@@ -8278,6 +8296,10 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"method": {
|
||||
"type": "string",
|
||||
"enum": ["GET", "HEAD"]
|
||||
},
|
||||
"port": {
|
||||
"type": "number",
|
||||
"minimum": 1,
|
||||
|
||||
+3
-3
@@ -9,20 +9,20 @@ import { handleErrors } from "@/api/middleware/handleErrors.js";
|
||||
import { generalApiLimiter } from "@/api/middleware/rateLimiter.js";
|
||||
import { sanitizeBody, sanitizeQuery } from "@/api/middleware/sanitization.js";
|
||||
import { setupRoutes } from "@/config/routes.js";
|
||||
import { InitializedServices } from "@/config/services.js";
|
||||
import { InitializedControllers } from "@/config/controllers.js";
|
||||
import { EnvConfig } from "@/domain/app-settings/app-settings.service.js";
|
||||
import { createStatusPageCorsOrigin } from "@/api/middleware/statusPageCorsOrigin.js";
|
||||
import { isPublicStatusPageApiPath } from "@/api/middleware/statusPagePublicApiPath.js";
|
||||
import { ApiServices } from "@/config/services.api.js";
|
||||
|
||||
export const createApp = ({
|
||||
services,
|
||||
apiServices: services,
|
||||
controllers,
|
||||
envSettings,
|
||||
frontendPath,
|
||||
openApiSpec,
|
||||
}: {
|
||||
services: InitializedServices;
|
||||
apiServices: ApiServices;
|
||||
controllers: InitializedControllers;
|
||||
envSettings: EnvConfig;
|
||||
frontendPath: string;
|
||||
|
||||
@@ -12,7 +12,7 @@ import NotificationController from "../api/controllers/notificationController.js
|
||||
import TagsController from "../api/controllers/tagController.js";
|
||||
import DiagnosticController from "../api/controllers/diagnosticController.js";
|
||||
import IncidentController from "../api/controllers/incidentController.js";
|
||||
import type { InitializedServices } from "@/config/services.js";
|
||||
import { ApiServices } from "@/config/services.api.js";
|
||||
|
||||
export interface InitializedControllers {
|
||||
authController: AuthController;
|
||||
@@ -30,21 +30,21 @@ export interface InitializedControllers {
|
||||
diagnosticController: DiagnosticController;
|
||||
incidentController: IncidentController;
|
||||
}
|
||||
export const initializeControllers = (services: InitializedServices): InitializedControllers => {
|
||||
export const initializeControllers = (apiServices: ApiServices): InitializedControllers => {
|
||||
return {
|
||||
authController: new AuthController(services.userService),
|
||||
monitorController: new MonitorController(services.monitorService, services.notificationsService),
|
||||
settingsController: new SettingsController(services.settingsService, services.emailService),
|
||||
checkController: new CheckController(services.checkService),
|
||||
geoCheckController: new GeoCheckController(services.geoChecksService),
|
||||
inviteController: new InviteController(services.inviteService),
|
||||
maintenanceWindowController: new MaintenanceWindowController(services.maintenanceWindowService),
|
||||
queueController: new QueueController(services.worker),
|
||||
logController: new LogController(services.logger),
|
||||
statusPageController: new StatusPageController(services.statusPageService, services.monitorsRepository, services.settingsService),
|
||||
notificationController: new NotificationController(services.notificationsService, services.monitorsRepository),
|
||||
tagController: new TagsController(services.tagsService),
|
||||
diagnosticController: new DiagnosticController(services.diagnosticService),
|
||||
incidentController: new IncidentController(services.incidentService),
|
||||
authController: new AuthController(apiServices.userService),
|
||||
monitorController: new MonitorController(apiServices.monitorService, apiServices.notificationsService),
|
||||
settingsController: new SettingsController(apiServices.settingsService, apiServices.emailService),
|
||||
checkController: new CheckController(apiServices.checkService),
|
||||
geoCheckController: new GeoCheckController(apiServices.geoChecksService),
|
||||
inviteController: new InviteController(apiServices.inviteService),
|
||||
maintenanceWindowController: new MaintenanceWindowController(apiServices.maintenanceWindowService),
|
||||
queueController: new QueueController(apiServices.worker),
|
||||
logController: new LogController(apiServices.logger),
|
||||
statusPageController: new StatusPageController(apiServices.statusPageService, apiServices.monitorsRepository, apiServices.settingsService),
|
||||
notificationController: new NotificationController(apiServices.notificationsService, apiServices.monitorsRepository),
|
||||
tagController: new TagsController(apiServices.tagsService),
|
||||
diagnosticController: new DiagnosticController(apiServices.diagnosticService),
|
||||
incidentController: new IncidentController(apiServices.incidentService),
|
||||
};
|
||||
};
|
||||
@@ -3,7 +3,7 @@ import { createVerifyJWT } from "../api/middleware/verifyJWT.js";
|
||||
import { createVerifyStatusPageAccess } from "../api/middleware/verifyStatusPageAccess.js";
|
||||
import { authApiLimiter } from "../api/middleware/rateLimiter.js";
|
||||
import type { InitializedControllers } from "./controllers.js";
|
||||
import type { InitializedServices } from "./services.js";
|
||||
import type { ApiServices } from "@/config/services.api.js";
|
||||
|
||||
import AuthRoutes from "../api/routes/authRoute.js";
|
||||
import InviteRoutes from "../api/routes/inviteRoute.js";
|
||||
@@ -21,8 +21,8 @@ import TagRoutes from "../api/routes/tagRoutes.js";
|
||||
|
||||
import IncidentRoutes from "../api/routes/incidentRoute.js";
|
||||
|
||||
export const setupRoutes = (app: Application, controllers: InitializedControllers, services: InitializedServices) => {
|
||||
const verifyJWT = createVerifyJWT(services.settingsService);
|
||||
export const setupRoutes = (app: Application, controllers: InitializedControllers, apiServices: ApiServices) => {
|
||||
const verifyJWT = createVerifyJWT(apiServices.settingsService);
|
||||
const authRoutes = new AuthRoutes(controllers.authController, verifyJWT);
|
||||
const monitorRoutes = new MonitorRoutes(controllers.monitorController);
|
||||
const settingsRoutes = new SettingsRoutes(controllers.settingsController);
|
||||
@@ -32,7 +32,7 @@ export const setupRoutes = (app: Application, controllers: InitializedController
|
||||
const maintenanceWindowRoutes = new MaintenanceWindowRoutes(controllers.maintenanceWindowController);
|
||||
const queueRoutes = new QueueRoutes(controllers.queueController);
|
||||
const logRoutes = new LogRoutes(controllers.logController);
|
||||
const verifyStatusPageAccess = createVerifyStatusPageAccess(services.statusPagesRepository, verifyJWT);
|
||||
const verifyStatusPageAccess = createVerifyStatusPageAccess(apiServices.statusPagesRepository, verifyJWT);
|
||||
const statusPageRoutes = new StatusPageRoutes(controllers.statusPageController, verifyJWT, verifyStatusPageAccess);
|
||||
const notificationRoutes = new NotificationRoutes(controllers.notificationController);
|
||||
const tagRoutes = new TagRoutes(controllers.tagController);
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
import { DiagnosticService, IDiagnosticService } from "@/domain/diagnostics/diagnostic.service.js";
|
||||
import { IInviteService, InviteService } from "@/domain/invites/invite.service.js";
|
||||
import { IMaintenanceWindowService, MaintenanceWindowService } from "@/domain/maintenance-windows/maintenance-window.service.js";
|
||||
import { IMonitorService, MonitorService } from "@/domain/monitors/monitor.service.js";
|
||||
import { IStatusPageService, StatusPageService } from "@/domain/status-pages/status-page.service.js";
|
||||
import { ITagsService, TagsService } from "@/domain/tags/tag.service.js";
|
||||
import { IUserService, UserService } from "@/domain/users/user.service.js";
|
||||
import { IJobScheduler } from "@/worker/worker.interface.js";
|
||||
|
||||
// Third-party
|
||||
import jwt from "jsonwebtoken";
|
||||
import crypto from "crypto";
|
||||
import { games } from "gamedig";
|
||||
|
||||
import { SharedServices } from "@/config/services.shared.js";
|
||||
|
||||
export interface ApiServices extends SharedServices {
|
||||
worker: IJobScheduler; // control-plane handle only (DBQueueWorker in all-in-one, bare JobScheduler in API-only)
|
||||
userService: IUserService;
|
||||
monitorService: IMonitorService;
|
||||
maintenanceWindowService: IMaintenanceWindowService;
|
||||
inviteService: IInviteService;
|
||||
statusPageService: IStatusPageService;
|
||||
tagsService: ITagsService;
|
||||
diagnosticService: IDiagnosticService;
|
||||
}
|
||||
|
||||
export const buildApi = (shared: SharedServices, jobScheduler: IJobScheduler): ApiServices => {
|
||||
const {
|
||||
logger,
|
||||
db,
|
||||
settingsService,
|
||||
emailService,
|
||||
monitorsRepository,
|
||||
checksRepository,
|
||||
geoChecksRepository,
|
||||
monitorStatsRepository,
|
||||
statusPagesRepository,
|
||||
usersRepository,
|
||||
invitesRepository,
|
||||
recoveryTokensRepository,
|
||||
settingsRepository,
|
||||
tagsRepository,
|
||||
incidentsRepository,
|
||||
teamsRepository,
|
||||
maintenanceWindowsRepository,
|
||||
jobsRepository,
|
||||
} = shared;
|
||||
|
||||
const userService = new UserService({
|
||||
crypto,
|
||||
emailService,
|
||||
settingsService,
|
||||
logger,
|
||||
jwt,
|
||||
scheduler: jobScheduler,
|
||||
monitorsRepository,
|
||||
usersRepository,
|
||||
invitesRepository,
|
||||
recoveryTokensRepository,
|
||||
settingsRepository,
|
||||
teamsRepository,
|
||||
});
|
||||
|
||||
// ***********************
|
||||
// Business services
|
||||
// ***********************
|
||||
|
||||
const monitorService = new MonitorService({
|
||||
scheduler: jobScheduler,
|
||||
logger,
|
||||
games,
|
||||
monitorsRepository,
|
||||
checksRepository,
|
||||
geoChecksRepository,
|
||||
monitorStatsRepository,
|
||||
statusPagesRepository,
|
||||
incidentsRepository,
|
||||
});
|
||||
|
||||
const maintenanceWindowService = new MaintenanceWindowService({
|
||||
monitorsRepository,
|
||||
maintenanceWindowsRepository,
|
||||
jobsRepository,
|
||||
scheduler: jobScheduler,
|
||||
});
|
||||
|
||||
const inviteService = new InviteService({
|
||||
invitesRepository,
|
||||
settingsService,
|
||||
emailService,
|
||||
});
|
||||
|
||||
const statusPageService = new StatusPageService(statusPagesRepository, settingsService);
|
||||
const tagsService = new TagsService(tagsRepository, monitorsRepository);
|
||||
const diagnosticService = new DiagnosticService(db);
|
||||
|
||||
return {
|
||||
...shared,
|
||||
worker: jobScheduler,
|
||||
userService,
|
||||
monitorService,
|
||||
maintenanceWindowService,
|
||||
inviteService,
|
||||
statusPageService,
|
||||
tagsService,
|
||||
diagnosticService,
|
||||
};
|
||||
};
|
||||
@@ -1,309 +0,0 @@
|
||||
import { Mongoose } from "mongoose";
|
||||
import { buildShared } from "@/config/services.shared.js";
|
||||
import { IDb } from "@/db/db.interface.js";
|
||||
import { EnvConfig, ISettingsService } from "@/domain/app-settings/app-settings.service.js";
|
||||
import { ICheckService } from "@/domain/checks/check.service.js";
|
||||
import { DiagnosticService, IDiagnosticService } from "@/domain/diagnostics/diagnostic.service.js";
|
||||
import { IGeoChecksService } from "@/domain/geo-checks/geo-check.service.js";
|
||||
import { IIncidentService } from "@/domain/incidents/incident.service.js";
|
||||
import { IInviteService, InviteService } from "@/domain/invites/invite.service.js";
|
||||
import { IMaintenanceWindowService, MaintenanceWindowService } from "@/domain/maintenance-windows/maintenance-window.service.js";
|
||||
import { IMonitorService, MonitorService } from "@/domain/monitors/monitor.service.js";
|
||||
import { INotificationMessageBuilder } from "@/domain/notifications/notification.message-builder.js";
|
||||
import { INotificationsService } from "@/domain/notifications/notification.service.js";
|
||||
import { IStatusPageService, StatusPageService } from "@/domain/status-pages/status-page.service.js";
|
||||
import { ITagsService, TagsService } from "@/domain/tags/tag.service.js";
|
||||
import { IUserService, UserService } from "@/domain/users/user.service.js";
|
||||
import { BufferService, IBufferService } from "@/service/bufferService.js";
|
||||
import { IEmailService } from "@/service/emailService.js";
|
||||
import { WorkerHelper } from "@/worker/worker.helper.js";
|
||||
import { IQueueWorker } from "@/worker/worker.interface.js";
|
||||
import { INetworkService, NetworkService } from "@/service/networkService.js";
|
||||
import { IStatusService, StatusService } from "@/service/statusService.js";
|
||||
import { MonitorStatusPolicy } from "@/worker/worker.monitor-status-policy.js";
|
||||
|
||||
// Network providers
|
||||
import { PingProvider } from "@/service/network/PingProvider.js";
|
||||
import { HttpProvider } from "@/service/network/HttpProvider.js";
|
||||
import { AdvancedMatcher } from "@/service/network/AdvancedMatcher.js";
|
||||
import { PageSpeedProvider } from "@/service/network/PageSpeedProvider.js";
|
||||
import { HardwareProvider } from "@/service/network/HardwareProvider.js";
|
||||
import { DockerProvider } from "@/service/network/DockerProvider.js";
|
||||
import { PortProvider } from "@/service/network/PortProvider.js";
|
||||
import { GameProvider } from "@/service/network/GameProvider.js";
|
||||
import { GrpcProvider } from "@/service/network/GrpcProvider.js";
|
||||
import { WebSocketProvider } from "@/service/network/WebSocketProvider.js";
|
||||
import { DNSProvider } from "@/service/network/DNSProvider.js";
|
||||
|
||||
// Third-party
|
||||
import { Resolver } from "dns/promises";
|
||||
import axios from "axios";
|
||||
import got from "got";
|
||||
import ping from "ping";
|
||||
import Docker from "dockerode";
|
||||
import net from "net";
|
||||
import jwt from "jsonwebtoken";
|
||||
import crypto from "crypto";
|
||||
import { games, GameDig } from "gamedig";
|
||||
import jmespath from "jmespath";
|
||||
import * as grpc from "@grpc/grpc-js";
|
||||
import * as protoLoader from "@grpc/proto-loader";
|
||||
import WebSocket from "ws";
|
||||
|
||||
// Repositories
|
||||
import { ISettingsRepository } from "@/domain/app-settings/app-settings-repository.interface.js";
|
||||
import { IChecksRepository } from "@/domain/checks/check.repository.interface.js";
|
||||
import { IGeoChecksRepository } from "@/domain/geo-checks/geo-check.repository.interface.js";
|
||||
import { IIncidentsRepository } from "@/domain/incidents/incident.repository.interface.js";
|
||||
import { IInvitesRepository } from "@/domain/invites/invite.repository.interface.js";
|
||||
import { IMaintenanceWindowsRepository } from "@/domain/maintenance-windows/maintenance-window.repository.interface.js";
|
||||
import { IMonitorStatsRepository } from "@/domain/monitor-stats/monitor-stats.repository.interface.js";
|
||||
import { IMonitorsRepository } from "@/domain/monitors/monitor.repository.interface.js";
|
||||
import { INotificationsRepository } from "@/domain/notifications/notification.repository.interface.js";
|
||||
import { IRecoveryTokensRepository } from "@/domain/recovery-tokens/recovery-token.repository.interface.js";
|
||||
import { IStatusPagesRepository } from "@/domain/status-pages/status-page-repository.interface.js";
|
||||
import { ITagsRepository } from "@/domain/tags/tag.repository.interface.js";
|
||||
import { ITeamsRepository } from "@/domain/teams/team.repository.interface.js";
|
||||
import { IUsersRepository } from "@/domain/users/user.repository.interface.js";
|
||||
import { ILogger } from "@/utils/logger.js";
|
||||
import { NotificationReactor } from "@/worker/reactors/reactor.notification.js";
|
||||
import { ReactorDispatcher } from "@/worker/reactors/reactor.dispatcher.js";
|
||||
import { IncidentReactor } from "@/worker/reactors/reactor.incident.js";
|
||||
import { GeoChecksPipeline } from "@/worker/worker.check-pipeline.js";
|
||||
import { CheckProducer } from "@/worker/worker.check-producer.js";
|
||||
import { CheckEvaluator } from "@/worker/worker.check-evaluator.js";
|
||||
import { DBQueueWorker } from "@/worker/worker.db-queue.js";
|
||||
|
||||
export type InitializedServices = {
|
||||
settingsService: ISettingsService;
|
||||
db: IDb<Mongoose>;
|
||||
networkService: INetworkService;
|
||||
emailService: IEmailService;
|
||||
bufferService: IBufferService;
|
||||
statusService: IStatusService;
|
||||
worker: IQueueWorker;
|
||||
userService: IUserService;
|
||||
checkService: ICheckService;
|
||||
geoChecksService: IGeoChecksService;
|
||||
diagnosticService: IDiagnosticService;
|
||||
inviteService: IInviteService;
|
||||
maintenanceWindowService: IMaintenanceWindowService;
|
||||
monitorService: IMonitorService;
|
||||
incidentService: IIncidentService;
|
||||
logger: ILogger;
|
||||
notificationsService: INotificationsService;
|
||||
tagsService: ITagsService;
|
||||
statusPageService: IStatusPageService;
|
||||
notificationMessageBuilder: INotificationMessageBuilder;
|
||||
|
||||
// Repositories
|
||||
monitorsRepository: IMonitorsRepository;
|
||||
checksRepository: IChecksRepository;
|
||||
geoChecksRepository: IGeoChecksRepository;
|
||||
monitorStatsRepository: IMonitorStatsRepository;
|
||||
statusPagesRepository: IStatusPagesRepository;
|
||||
usersRepository: IUsersRepository;
|
||||
invitesRepository: IInvitesRepository;
|
||||
recoveryTokensRepository: IRecoveryTokensRepository;
|
||||
settingsRepository: ISettingsRepository;
|
||||
notificationsRepository: INotificationsRepository;
|
||||
tagsRepository: ITagsRepository;
|
||||
incidentsRepository: IIncidentsRepository;
|
||||
teamsRepository: ITeamsRepository;
|
||||
maintenanceWindowsRepository: IMaintenanceWindowsRepository;
|
||||
};
|
||||
|
||||
export const initializeServices = async ({
|
||||
logger,
|
||||
envSettings,
|
||||
settingsService,
|
||||
}: {
|
||||
logger: ILogger;
|
||||
envSettings: EnvConfig;
|
||||
settingsService: ISettingsService;
|
||||
}): Promise<InitializedServices> => {
|
||||
const shared = await buildShared({ logger, envSettings, settingsService });
|
||||
const {
|
||||
db,
|
||||
emailService,
|
||||
notificationMessageBuilder,
|
||||
incidentService,
|
||||
checkService,
|
||||
geoChecksService,
|
||||
notificationsService,
|
||||
workerId,
|
||||
jobsRepository,
|
||||
monitorsRepository,
|
||||
queueWorkersRepository,
|
||||
checksRepository,
|
||||
geoChecksRepository,
|
||||
monitorStatsRepository,
|
||||
statusPagesRepository,
|
||||
usersRepository,
|
||||
invitesRepository,
|
||||
recoveryTokensRepository,
|
||||
settingsRepository,
|
||||
notificationsRepository,
|
||||
tagsRepository,
|
||||
incidentsRepository,
|
||||
teamsRepository,
|
||||
maintenanceWindowsRepository,
|
||||
} = shared;
|
||||
|
||||
// Network providers
|
||||
const pingProvider = new PingProvider(ping);
|
||||
const httpProvider = new HttpProvider(got, new AdvancedMatcher(jmespath));
|
||||
const pageSpeedProvider = new PageSpeedProvider(httpProvider, settingsService, logger);
|
||||
const hardwareProvider = new HardwareProvider(httpProvider);
|
||||
const dockerProvider = new DockerProvider(logger, Docker);
|
||||
const portProvider = new PortProvider(net);
|
||||
const gameProvider = new GameProvider(logger, GameDig);
|
||||
const grpcProvider = new GrpcProvider(grpc, protoLoader);
|
||||
const webSocketProvider = new WebSocketProvider(WebSocket);
|
||||
const dnsProvider = new DNSProvider(() => new Resolver());
|
||||
|
||||
const networkService = new NetworkService(axios, logger, [
|
||||
pingProvider,
|
||||
httpProvider,
|
||||
pageSpeedProvider,
|
||||
hardwareProvider,
|
||||
dockerProvider,
|
||||
portProvider,
|
||||
gameProvider,
|
||||
grpcProvider,
|
||||
webSocketProvider,
|
||||
dnsProvider,
|
||||
]);
|
||||
|
||||
const bufferService = new BufferService(logger, checkService, geoChecksService, settingsService, jobsRepository);
|
||||
|
||||
const statusService = new StatusService(logger, monitorsRepository, monitorStatsRepository);
|
||||
|
||||
const tagsService = new TagsService(tagsRepository, monitorsRepository);
|
||||
|
||||
const monitorStatusPolicy = new MonitorStatusPolicy();
|
||||
// reactors and dispatcher
|
||||
const notificationReactor = new NotificationReactor(notificationsService);
|
||||
const incidentReactor = new IncidentReactor(incidentService);
|
||||
const reactorDispatcher = new ReactorDispatcher(logger, [notificationReactor, incidentReactor]);
|
||||
|
||||
// Check producer/evaluator
|
||||
const checkProducer = new CheckProducer(monitorsRepository, maintenanceWindowsRepository, checkService, networkService, bufferService, logger);
|
||||
const checkEvaluator = new CheckEvaluator(statusService, monitorStatusPolicy);
|
||||
|
||||
// pipelines
|
||||
const geoCheckPipeline = new GeoChecksPipeline(maintenanceWindowsRepository, geoChecksService, bufferService, logger);
|
||||
|
||||
const workerHelper = new WorkerHelper(
|
||||
logger,
|
||||
checkService,
|
||||
settingsService,
|
||||
monitorsRepository,
|
||||
jobsRepository,
|
||||
teamsRepository,
|
||||
monitorStatsRepository,
|
||||
checksRepository,
|
||||
incidentsRepository,
|
||||
geoChecksRepository
|
||||
);
|
||||
|
||||
const worker = await DBQueueWorker.create(
|
||||
logger,
|
||||
jobsRepository,
|
||||
monitorsRepository,
|
||||
checksRepository,
|
||||
checkService,
|
||||
checkProducer,
|
||||
checkEvaluator,
|
||||
geoCheckPipeline,
|
||||
reactorDispatcher,
|
||||
workerHelper,
|
||||
queueWorkersRepository,
|
||||
envSettings.queueMode,
|
||||
envSettings.queuePrimaryProcesses,
|
||||
workerId
|
||||
);
|
||||
|
||||
// Business services
|
||||
const userService = new UserService({
|
||||
crypto,
|
||||
emailService,
|
||||
settingsService,
|
||||
logger,
|
||||
jwt,
|
||||
scheduler: worker,
|
||||
monitorsRepository,
|
||||
usersRepository,
|
||||
invitesRepository,
|
||||
recoveryTokensRepository,
|
||||
settingsRepository,
|
||||
teamsRepository,
|
||||
});
|
||||
|
||||
const diagnosticService = new DiagnosticService(db);
|
||||
const inviteService = new InviteService({
|
||||
invitesRepository,
|
||||
settingsService,
|
||||
emailService,
|
||||
});
|
||||
const maintenanceWindowService = new MaintenanceWindowService({
|
||||
monitorsRepository,
|
||||
maintenanceWindowsRepository,
|
||||
jobsRepository,
|
||||
scheduler: worker,
|
||||
});
|
||||
const monitorService = new MonitorService({
|
||||
scheduler: worker,
|
||||
logger,
|
||||
games,
|
||||
monitorsRepository,
|
||||
checksRepository,
|
||||
geoChecksRepository,
|
||||
monitorStatsRepository,
|
||||
statusPagesRepository,
|
||||
incidentsRepository,
|
||||
});
|
||||
|
||||
const statusPageService = new StatusPageService(statusPagesRepository, settingsService);
|
||||
|
||||
const services = {
|
||||
settingsService,
|
||||
db,
|
||||
networkService,
|
||||
emailService,
|
||||
bufferService,
|
||||
statusService,
|
||||
worker,
|
||||
userService,
|
||||
checkService,
|
||||
geoChecksService,
|
||||
diagnosticService,
|
||||
inviteService,
|
||||
maintenanceWindowService,
|
||||
monitorService,
|
||||
incidentService,
|
||||
logger,
|
||||
notificationsService,
|
||||
tagsService,
|
||||
statusPageService,
|
||||
notificationMessageBuilder,
|
||||
|
||||
// Repositories
|
||||
monitorsRepository,
|
||||
checksRepository,
|
||||
geoChecksRepository,
|
||||
monitorStatsRepository,
|
||||
statusPagesRepository,
|
||||
usersRepository,
|
||||
invitesRepository,
|
||||
recoveryTokensRepository,
|
||||
settingsRepository,
|
||||
notificationsRepository,
|
||||
tagsRepository,
|
||||
incidentsRepository,
|
||||
teamsRepository,
|
||||
maintenanceWindowsRepository,
|
||||
};
|
||||
|
||||
return services;
|
||||
};
|
||||
@@ -0,0 +1,151 @@
|
||||
import { Resolver } from "dns/promises";
|
||||
import axios from "axios";
|
||||
import got from "got";
|
||||
import ping from "ping";
|
||||
import Docker from "dockerode";
|
||||
import net from "net";
|
||||
import { GameDig } from "gamedig";
|
||||
import jmespath from "jmespath";
|
||||
import * as grpc from "@grpc/grpc-js";
|
||||
import * as protoLoader from "@grpc/proto-loader";
|
||||
import WebSocket from "ws";
|
||||
|
||||
import { EnvConfig } from "@/domain/app-settings/app-settings.service.js";
|
||||
import { SharedServices } from "@/config/services.shared.js";
|
||||
import { INetworkService, NetworkService } from "@/service/networkService.js";
|
||||
import { IBufferService, BufferService } from "@/service/bufferService.js";
|
||||
import { IStatusService, StatusService } from "@/service/statusService.js";
|
||||
import { IQueueWorker } from "@/worker/worker.interface.js";
|
||||
import { MonitorStatusPolicy } from "@/worker/worker.monitor-status-policy.js";
|
||||
import { WorkerHelper } from "@/worker/worker.helper.js";
|
||||
import { CheckProducer } from "@/worker/worker.check-producer.js";
|
||||
import { CheckEvaluator } from "@/worker/worker.check-evaluator.js";
|
||||
import { GeoChecksPipeline } from "@/worker/worker.check-pipeline.js";
|
||||
import { NotificationReactor } from "@/worker/reactors/reactor.notification.js";
|
||||
import { IncidentReactor } from "@/worker/reactors/reactor.incident.js";
|
||||
import { ReactorDispatcher } from "@/worker/reactors/reactor.dispatcher.js";
|
||||
import { DBQueueWorker } from "@/worker/worker.db-queue.js";
|
||||
|
||||
// Network providers
|
||||
import { PingProvider } from "@/service/network/PingProvider.js";
|
||||
import { HttpProvider } from "@/service/network/HttpProvider.js";
|
||||
import { AdvancedMatcher } from "@/service/network/AdvancedMatcher.js";
|
||||
import { PageSpeedProvider } from "@/service/network/PageSpeedProvider.js";
|
||||
import { HardwareProvider } from "@/service/network/HardwareProvider.js";
|
||||
import { DockerProvider } from "@/service/network/DockerProvider.js";
|
||||
import { PortProvider } from "@/service/network/PortProvider.js";
|
||||
import { GameProvider } from "@/service/network/GameProvider.js";
|
||||
import { GrpcProvider } from "@/service/network/GrpcProvider.js";
|
||||
import { WebSocketProvider } from "@/service/network/WebSocketProvider.js";
|
||||
import { DNSProvider } from "@/service/network/DNSProvider.js";
|
||||
export interface WorkerServices {
|
||||
worker: IQueueWorker;
|
||||
networkService: INetworkService;
|
||||
bufferService: IBufferService;
|
||||
statusService: IStatusService;
|
||||
}
|
||||
|
||||
export const buildWorker = async (shared: SharedServices, envSettings: EnvConfig): Promise<WorkerServices> => {
|
||||
const {
|
||||
logger,
|
||||
settingsService,
|
||||
checkService,
|
||||
geoChecksService,
|
||||
notificationsService,
|
||||
incidentService,
|
||||
workerId,
|
||||
jobsRepository,
|
||||
queueWorkersRepository,
|
||||
monitorsRepository,
|
||||
checksRepository,
|
||||
geoChecksRepository,
|
||||
monitorStatsRepository,
|
||||
incidentsRepository,
|
||||
teamsRepository,
|
||||
maintenanceWindowsRepository,
|
||||
} = shared;
|
||||
|
||||
// ***********************
|
||||
// Network providers
|
||||
// ***********************
|
||||
const pingProvider = new PingProvider(ping);
|
||||
const httpProvider = new HttpProvider(got, new AdvancedMatcher(jmespath));
|
||||
const pageSpeedProvider = new PageSpeedProvider(httpProvider, settingsService, logger);
|
||||
const hardwareProvider = new HardwareProvider(httpProvider);
|
||||
const dockerProvider = new DockerProvider(logger, Docker);
|
||||
const portProvider = new PortProvider(net);
|
||||
const gameProvider = new GameProvider(logger, GameDig);
|
||||
const grpcProvider = new GrpcProvider(grpc, protoLoader);
|
||||
const webSocketProvider = new WebSocketProvider(WebSocket);
|
||||
const dnsProvider = new DNSProvider(() => new Resolver());
|
||||
|
||||
const networkService = new NetworkService(axios, logger, [
|
||||
pingProvider,
|
||||
httpProvider,
|
||||
pageSpeedProvider,
|
||||
hardwareProvider,
|
||||
dockerProvider,
|
||||
portProvider,
|
||||
gameProvider,
|
||||
grpcProvider,
|
||||
webSocketProvider,
|
||||
dnsProvider,
|
||||
]);
|
||||
|
||||
const bufferService = new BufferService(logger, checkService, geoChecksService, settingsService, jobsRepository);
|
||||
const statusService = new StatusService(logger, monitorsRepository, monitorStatsRepository);
|
||||
const monitorStatusPolicy = new MonitorStatusPolicy();
|
||||
|
||||
// ***********************
|
||||
// Reactors and dispatcher
|
||||
// Handles notifications and incidents
|
||||
// ***********************
|
||||
|
||||
const notificationReactor = new NotificationReactor(notificationsService);
|
||||
const incidentReactor = new IncidentReactor(incidentService);
|
||||
const reactorDispatcher = new ReactorDispatcher(logger, [notificationReactor, incidentReactor]);
|
||||
|
||||
// ***********************
|
||||
// Check producer/evaluator
|
||||
// Handles creating and evaluatiog checks
|
||||
// ***********************
|
||||
const checkProducer = new CheckProducer(monitorsRepository, maintenanceWindowsRepository, checkService, networkService, bufferService, logger);
|
||||
const checkEvaluator = new CheckEvaluator(statusService, monitorStatusPolicy);
|
||||
const geoCheckPipeline = new GeoChecksPipeline(maintenanceWindowsRepository, geoChecksService, bufferService, logger);
|
||||
|
||||
// ***********************
|
||||
// Worker
|
||||
// ***********************
|
||||
|
||||
const workerHelper = new WorkerHelper(
|
||||
logger,
|
||||
checkService,
|
||||
settingsService,
|
||||
monitorsRepository,
|
||||
jobsRepository,
|
||||
teamsRepository,
|
||||
monitorStatsRepository,
|
||||
checksRepository,
|
||||
incidentsRepository,
|
||||
geoChecksRepository
|
||||
);
|
||||
|
||||
const worker = await DBQueueWorker.create(
|
||||
logger,
|
||||
jobsRepository,
|
||||
monitorsRepository,
|
||||
checksRepository,
|
||||
checkService,
|
||||
checkProducer,
|
||||
checkEvaluator,
|
||||
geoCheckPipeline,
|
||||
reactorDispatcher,
|
||||
workerHelper,
|
||||
queueWorkersRepository,
|
||||
envSettings.queueMode,
|
||||
envSettings.queuePrimaryProcesses,
|
||||
workerId
|
||||
);
|
||||
|
||||
return { worker, networkService, bufferService, statusService };
|
||||
};
|
||||
@@ -241,6 +241,7 @@ const CheckSchema = new Schema<CheckDocument>(
|
||||
},
|
||||
errors: {
|
||||
type: [errorSchema],
|
||||
suppressReservedKeysWarning: true,
|
||||
default: () => [],
|
||||
},
|
||||
capture: {
|
||||
|
||||
+60
-18
@@ -1,4 +1,7 @@
|
||||
import { initializeServices } from "./config/services.js";
|
||||
import { buildShared } from "@/config/services.shared.js";
|
||||
import { buildApi } from "@/config/services.api.js";
|
||||
import { buildWorker } from "@/config/services.worker.js";
|
||||
|
||||
import { initializeControllers } from "./config/controllers.js";
|
||||
import { createApp } from "./app.js";
|
||||
import { initShutdownListener } from "@/shutdown.js";
|
||||
@@ -9,44 +12,83 @@ import fs from "fs";
|
||||
|
||||
import Logger, { ILogger } from "@/utils/logger.js";
|
||||
import { SettingsService } from "@/domain/app-settings/app-settings.service.js";
|
||||
|
||||
import { JobScheduler } from "@/worker/worker.job-scheduler.js";
|
||||
import { IJobScheduler } from "@/worker/worker.interface.js";
|
||||
const SERVICE_NAME = "Server";
|
||||
let logger: ILogger;
|
||||
|
||||
const startApp = async () => {
|
||||
// Validate environment variables first
|
||||
const env = validateEnv();
|
||||
// ***********************
|
||||
// Basic setup
|
||||
// ***********************
|
||||
|
||||
// Create settings service (env only — DB repository injected after connect)
|
||||
const env = validateEnv();
|
||||
const settingsService = new SettingsService(env);
|
||||
const envSettings = settingsService.loadSettings();
|
||||
// Create logger
|
||||
logger = new Logger({ envSettings });
|
||||
const { queueMode, queuePrimaryProcesses } = envSettings;
|
||||
|
||||
// Initialize services (connects DB, creates repositories, injects settingsRepository)
|
||||
const services = await initializeServices({ logger, envSettings, settingsService });
|
||||
logger.info({
|
||||
message: `Queue mode: ${queueMode}`,
|
||||
service: SERVICE_NAME,
|
||||
method: "startApp",
|
||||
});
|
||||
|
||||
// If this is a worker instance, we're done. No need for express
|
||||
if (envSettings.queueMode === "worker") {
|
||||
logger.info({
|
||||
message: "Worker instance started. API will not be started",
|
||||
service: SERVICE_NAME,
|
||||
});
|
||||
initShutdownListener(null, services);
|
||||
logger.info({
|
||||
message: `Process: ${queuePrimaryProcesses}`,
|
||||
service: SERVICE_NAME,
|
||||
method: "startApp",
|
||||
});
|
||||
|
||||
// ***********************
|
||||
// Build shared services
|
||||
// ***********************
|
||||
const shared = await buildShared({ logger, envSettings, settingsService });
|
||||
|
||||
// ***********************
|
||||
// Worker node path, don't need API
|
||||
// ***********************
|
||||
if (queueMode === "worker") {
|
||||
const { worker } = await buildWorker(shared, envSettings);
|
||||
logger.info({ message: "Worker instance started. API will not be started", service: SERVICE_NAME });
|
||||
initShutdownListener(null, { worker, db: shared.db });
|
||||
return;
|
||||
}
|
||||
|
||||
// FE path
|
||||
// ***********************
|
||||
// Primary node path
|
||||
// ***********************
|
||||
let scheduler: IJobScheduler;
|
||||
|
||||
// ***********************
|
||||
//Primary node processes jobs, need full worker
|
||||
// ***********************
|
||||
if (queuePrimaryProcesses === true) {
|
||||
const { worker } = await buildWorker(shared, envSettings);
|
||||
scheduler = worker;
|
||||
}
|
||||
// ***********************
|
||||
// Primary node does not process jobs, only need scheduler
|
||||
// ***********************
|
||||
else {
|
||||
scheduler = new JobScheduler(shared.jobsRepository, shared.queueWorkersRepository, shared.workerId);
|
||||
}
|
||||
|
||||
const services = buildApi(shared, scheduler);
|
||||
|
||||
// ***********************
|
||||
// FE assets always needed for primary node
|
||||
// ***********************
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
const openApiSpec = JSON.parse(fs.readFileSync(path.join(__dirname, "../openapi.json"), "utf8"));
|
||||
const frontendPath = path.join(__dirname, "..", "public");
|
||||
|
||||
// Initialize controllers
|
||||
const controllers = initializeControllers(services);
|
||||
|
||||
const app = createApp({
|
||||
services,
|
||||
apiServices: services,
|
||||
controllers,
|
||||
envSettings,
|
||||
frontendPath,
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
import { InitializedServices } from "./config/services.js";
|
||||
import { IDb } from "@/db/db.interface.js";
|
||||
import { logger } from "./utils/logger.js";
|
||||
import type { Server } from "http";
|
||||
import { Mongoose } from "mongoose";
|
||||
import { IJobScheduler } from "@/worker/worker.interface.js";
|
||||
|
||||
export const initShutdownListener = (server: Server | null, services: InitializedServices) => {
|
||||
type ShutdownTargets = {
|
||||
worker: IJobScheduler;
|
||||
db: IDb<Mongoose>;
|
||||
};
|
||||
export const initShutdownListener = (server: Server | null, services: ShutdownTargets) => {
|
||||
const SERVICE_NAME = "Server";
|
||||
|
||||
let isShuttingDown = false;
|
||||
|
||||
Reference in new issue
Block a user