mirror of
https://github.com/bluewave-labs/Checkmate.git
synced 2026-01-05 01:10:36 -06:00
391 lines
12 KiB
JavaScript
Executable File
391 lines
12 KiB
JavaScript
Executable File
import path from "path";
|
|
import fs from "fs";
|
|
import swaggerUi from "swagger-ui-express";
|
|
|
|
import express from "express";
|
|
import helmet from "helmet";
|
|
import cors from "cors";
|
|
import compression from "compression";
|
|
import logger from "./utils/logger.js";
|
|
import { verifyJWT } from "./middleware/verifyJWT.js";
|
|
import { handleErrors } from "./middleware/handleErrors.js";
|
|
import { responseHandler } from "./middleware/responseHandler.js";
|
|
import { fileURLToPath } from "url";
|
|
|
|
import AuthRoutes from "./routes/authRoute.js";
|
|
import AuthController from "./controllers/authController.js";
|
|
|
|
import InviteRoutes from "./routes/inviteRoute.js";
|
|
import InviteController from "./controllers/inviteController.js";
|
|
|
|
import MonitorRoutes from "./routes/monitorRoute.js";
|
|
import MonitorController from "./controllers/monitorController.js";
|
|
|
|
import CheckRoutes from "./routes/checkRoute.js";
|
|
import CheckController from "./controllers/checkController.js";
|
|
|
|
import MaintenanceWindowRoutes from "./routes/maintenanceWindowRoute.js";
|
|
import MaintenanceWindowController from "./controllers/maintenanceWindowController.js";
|
|
|
|
import SettingsRoutes from "./routes/settingsRoute.js";
|
|
import SettingsController from "./controllers/settingsController.js";
|
|
|
|
import StatusPageRoutes from "./routes/statusPageRoute.js";
|
|
import StatusPageController from "./controllers/statusPageController.js";
|
|
|
|
import QueueRoutes from "./routes/queueRoute.js";
|
|
import QueueController from "./controllers/queueController.js";
|
|
|
|
import NotificationRoutes from "./routes/notificationRoute.js";
|
|
import NotificationController from "./controllers/notificationController.js";
|
|
|
|
import DiagnosticRoutes from "./routes/diagnosticRoute.js";
|
|
import DiagnosticController from "./controllers/diagnosticController.js";
|
|
|
|
//JobQueue service and dependencies
|
|
import JobQueue from "./service/JobQueue/JobQueue.js";
|
|
import JobQueueHelper from "./service/JobQueue/JobQueueHelper.js";
|
|
import { Queue, Worker } from "bullmq";
|
|
|
|
import PulseQueue from "./service/PulseQueue/PulseQueue.js";
|
|
import PulseQueueHelper from "./service/PulseQueue/PulseQueueHelper.js";
|
|
|
|
//Network service and dependencies
|
|
import NetworkService from "./service/networkService.js";
|
|
import axios from "axios";
|
|
import ping from "ping";
|
|
import http from "http";
|
|
import Docker from "dockerode";
|
|
import net from "net";
|
|
// Email service and dependencies
|
|
import EmailService from "./service/emailService.js";
|
|
import nodemailer from "nodemailer";
|
|
import pkg from "handlebars";
|
|
const { compile } = pkg;
|
|
import mjml2html from "mjml";
|
|
|
|
// Settings Service and dependencies
|
|
import SettingsService from "./service/settingsService.js";
|
|
import AppSettings from "./db/models/AppSettings.js";
|
|
|
|
// Status Service and dependencies
|
|
import StatusService from "./service/statusService.js";
|
|
|
|
// Notification Service and dependencies
|
|
import NotificationService from "./service/notificationService.js";
|
|
|
|
// Buffer Service and dependencies
|
|
import BufferService from "./service/bufferService.js";
|
|
|
|
// Service Registry
|
|
import ServiceRegistry from "./service/serviceRegistry.js";
|
|
|
|
import MongoDB from "./db/mongo/MongoDB.js";
|
|
|
|
// Redis Service and dependencies
|
|
import IORedis from "ioredis";
|
|
import RedisService from "./service/redisService.js";
|
|
|
|
import TranslationService from "./service/translationService.js";
|
|
import languageMiddleware from "./middleware/languageMiddleware.js";
|
|
import StringService from "./service/stringService.js";
|
|
|
|
const SERVICE_NAME = "Server";
|
|
const SHUTDOWN_TIMEOUT = 1000;
|
|
let isShuttingDown = false;
|
|
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");
|
|
|
|
let server;
|
|
|
|
const shutdown = async () => {
|
|
if (isShuttingDown) {
|
|
return;
|
|
}
|
|
isShuttingDown = true;
|
|
logger.info({ message: "Attempting graceful shutdown" });
|
|
setTimeout(async () => {
|
|
logger.error({
|
|
message: "Could not shut down in time, forcing shutdown",
|
|
service: SERVICE_NAME,
|
|
method: "shutdown",
|
|
});
|
|
await ServiceRegistry.get(RedisService.SERVICE_NAME).flushRedis();
|
|
process.exit(1);
|
|
}, SHUTDOWN_TIMEOUT);
|
|
try {
|
|
server.close();
|
|
await ServiceRegistry.get(JobQueue.SERVICE_NAME).shutdown();
|
|
await ServiceRegistry.get(MongoDB.SERVICE_NAME).disconnect();
|
|
logger.info({ message: "Graceful shutdown complete" });
|
|
process.exit(0);
|
|
} catch (error) {
|
|
logger.error({
|
|
message: error.message,
|
|
service: SERVICE_NAME,
|
|
method: "shutdown",
|
|
stack: error.stack,
|
|
});
|
|
}
|
|
};
|
|
// Need to wrap server setup in a function to handle async nature of JobQueue
|
|
const startApp = async () => {
|
|
const app = express();
|
|
// Create and Register Primary services
|
|
const translationService = new TranslationService(logger);
|
|
const stringService = new StringService(translationService);
|
|
ServiceRegistry.register(StringService.SERVICE_NAME, stringService);
|
|
|
|
// Create services
|
|
const settingsService = new SettingsService(AppSettings);
|
|
const appSettings = settingsService.loadSettings();
|
|
|
|
// Create DB
|
|
const db = new MongoDB({ appSettings });
|
|
await db.connect();
|
|
|
|
// Set allowed origin
|
|
const allowedOrigin = appSettings.clientHost;
|
|
|
|
const networkService = new NetworkService(
|
|
axios,
|
|
ping,
|
|
logger,
|
|
http,
|
|
Docker,
|
|
net,
|
|
stringService,
|
|
settingsService
|
|
);
|
|
const emailService = new EmailService(
|
|
settingsService,
|
|
fs,
|
|
path,
|
|
compile,
|
|
mjml2html,
|
|
nodemailer,
|
|
logger
|
|
);
|
|
const bufferService = new BufferService({ db, logger });
|
|
const statusService = new StatusService({ db, logger, buffer: bufferService });
|
|
const notificationService = new NotificationService(
|
|
emailService,
|
|
db,
|
|
logger,
|
|
networkService,
|
|
stringService
|
|
);
|
|
|
|
const redisService = new RedisService({ Redis: IORedis, logger });
|
|
|
|
// const jobQueueHelper = new JobQueueHelper({
|
|
// redisService,
|
|
// Queue,
|
|
// Worker,
|
|
// logger,
|
|
// db,
|
|
// networkService,
|
|
// statusService,
|
|
// notificationService,
|
|
// });
|
|
// const jobQueue = await JobQueue.create({
|
|
// db,
|
|
// jobQueueHelper,
|
|
// logger,
|
|
// stringService,
|
|
// });
|
|
|
|
const pulseQueueHelper = new PulseQueueHelper({
|
|
db,
|
|
logger,
|
|
networkService,
|
|
statusService,
|
|
notificationService,
|
|
});
|
|
const pulseQueue = await PulseQueue.create({
|
|
appSettings,
|
|
db,
|
|
pulseQueueHelper,
|
|
logger,
|
|
});
|
|
|
|
// Register services
|
|
// ServiceRegistry.register(JobQueue.SERVICE_NAME, jobQueue);
|
|
ServiceRegistry.register(JobQueue.SERVICE_NAME, pulseQueue);
|
|
ServiceRegistry.register(MongoDB.SERVICE_NAME, db);
|
|
ServiceRegistry.register(SettingsService.SERVICE_NAME, settingsService);
|
|
ServiceRegistry.register(EmailService.SERVICE_NAME, emailService);
|
|
ServiceRegistry.register(NetworkService.SERVICE_NAME, networkService);
|
|
ServiceRegistry.register(BufferService.SERVICE_NAME, bufferService);
|
|
ServiceRegistry.register(StatusService.SERVICE_NAME, statusService);
|
|
ServiceRegistry.register(NotificationService.SERVICE_NAME, notificationService);
|
|
ServiceRegistry.register(TranslationService.SERVICE_NAME, translationService);
|
|
ServiceRegistry.register(RedisService.SERVICE_NAME, redisService);
|
|
|
|
await translationService.initialize();
|
|
|
|
const port = appSettings.port || 52345;
|
|
server = app.listen(port, () => {
|
|
logger.info({ message: `Server started on port:${port}` });
|
|
});
|
|
|
|
process.on("SIGUSR2", shutdown);
|
|
process.on("SIGINT", shutdown);
|
|
process.on("SIGTERM", shutdown);
|
|
|
|
//Create controllers
|
|
const authController = new AuthController(
|
|
ServiceRegistry.get(MongoDB.SERVICE_NAME),
|
|
ServiceRegistry.get(SettingsService.SERVICE_NAME),
|
|
ServiceRegistry.get(EmailService.SERVICE_NAME),
|
|
ServiceRegistry.get(JobQueue.SERVICE_NAME),
|
|
ServiceRegistry.get(StringService.SERVICE_NAME)
|
|
);
|
|
|
|
const monitorController = new MonitorController(
|
|
ServiceRegistry.get(MongoDB.SERVICE_NAME),
|
|
ServiceRegistry.get(SettingsService.SERVICE_NAME),
|
|
ServiceRegistry.get(JobQueue.SERVICE_NAME),
|
|
ServiceRegistry.get(StringService.SERVICE_NAME),
|
|
ServiceRegistry.get(EmailService.SERVICE_NAME)
|
|
);
|
|
|
|
const settingsController = new SettingsController({
|
|
db: ServiceRegistry.get(MongoDB.SERVICE_NAME),
|
|
settingsService: ServiceRegistry.get(SettingsService.SERVICE_NAME),
|
|
stringService: ServiceRegistry.get(StringService.SERVICE_NAME),
|
|
emailService: ServiceRegistry.get(EmailService.SERVICE_NAME),
|
|
});
|
|
|
|
const checkController = new CheckController(
|
|
ServiceRegistry.get(MongoDB.SERVICE_NAME),
|
|
ServiceRegistry.get(SettingsService.SERVICE_NAME),
|
|
ServiceRegistry.get(StringService.SERVICE_NAME)
|
|
);
|
|
|
|
const inviteController = new InviteController(
|
|
ServiceRegistry.get(MongoDB.SERVICE_NAME),
|
|
ServiceRegistry.get(SettingsService.SERVICE_NAME),
|
|
ServiceRegistry.get(EmailService.SERVICE_NAME),
|
|
ServiceRegistry.get(StringService.SERVICE_NAME)
|
|
);
|
|
|
|
const maintenanceWindowController = new MaintenanceWindowController(
|
|
ServiceRegistry.get(MongoDB.SERVICE_NAME),
|
|
ServiceRegistry.get(SettingsService.SERVICE_NAME),
|
|
ServiceRegistry.get(StringService.SERVICE_NAME)
|
|
);
|
|
|
|
const queueController = new QueueController(
|
|
ServiceRegistry.get(JobQueue.SERVICE_NAME),
|
|
ServiceRegistry.get(StringService.SERVICE_NAME)
|
|
);
|
|
|
|
const statusPageController = new StatusPageController(
|
|
ServiceRegistry.get(MongoDB.SERVICE_NAME),
|
|
ServiceRegistry.get(StringService.SERVICE_NAME)
|
|
);
|
|
|
|
const notificationController = new NotificationController({
|
|
notificationService: ServiceRegistry.get(NotificationService.SERVICE_NAME),
|
|
stringService: ServiceRegistry.get(StringService.SERVICE_NAME),
|
|
statusService: ServiceRegistry.get(StatusService.SERVICE_NAME),
|
|
db: ServiceRegistry.get(MongoDB.SERVICE_NAME),
|
|
});
|
|
|
|
const diagnosticController = new DiagnosticController(
|
|
ServiceRegistry.get(MongoDB.SERVICE_NAME)
|
|
);
|
|
|
|
//Create routes
|
|
const authRoutes = new AuthRoutes(authController);
|
|
const monitorRoutes = new MonitorRoutes(monitorController);
|
|
const settingsRoutes = new SettingsRoutes(settingsController);
|
|
const checkRoutes = new CheckRoutes(checkController);
|
|
const inviteRoutes = new InviteRoutes(inviteController);
|
|
const maintenanceWindowRoutes = new MaintenanceWindowRoutes(
|
|
maintenanceWindowController
|
|
);
|
|
const queueRoutes = new QueueRoutes(queueController);
|
|
const statusPageRoutes = new StatusPageRoutes(statusPageController);
|
|
|
|
const notificationRoutes = new NotificationRoutes(notificationController);
|
|
const diagnosticRoutes = new DiagnosticRoutes(diagnosticController);
|
|
// Middleware
|
|
app.use(express.static(frontendPath));
|
|
app.use(responseHandler);
|
|
app.use(
|
|
cors({
|
|
origin: allowedOrigin,
|
|
methods: "GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS",
|
|
allowedHeaders: "*",
|
|
credentials: true,
|
|
})
|
|
);
|
|
app.use(express.json());
|
|
app.use(
|
|
helmet({
|
|
hsts: false,
|
|
contentSecurityPolicy: {
|
|
useDefaults: true,
|
|
directives: {
|
|
upgradeInsecureRequests: null,
|
|
},
|
|
},
|
|
})
|
|
);
|
|
app.use(
|
|
compression({
|
|
level: 6,
|
|
threshold: 1024,
|
|
filter: (req, res) => {
|
|
if (req.headers["x-no-compression"]) {
|
|
return false;
|
|
}
|
|
return compression.filter(req, res);
|
|
},
|
|
})
|
|
);
|
|
|
|
app.use(languageMiddleware(stringService, translationService, settingsService));
|
|
// Swagger UI
|
|
app.use("/api-docs", swaggerUi.serve, swaggerUi.setup(openApiSpec));
|
|
|
|
//routes
|
|
app.use("/api/v1/auth", authRoutes.getRouter());
|
|
app.use("/api/v1/settings", verifyJWT, settingsRoutes.getRouter());
|
|
app.use("/api/v1/invite", inviteRoutes.getRouter());
|
|
app.use("/api/v1/monitors", verifyJWT, monitorRoutes.getRouter());
|
|
app.use("/api/v1/checks", verifyJWT, checkRoutes.getRouter());
|
|
app.use("/api/v1/maintenance-window", verifyJWT, maintenanceWindowRoutes.getRouter());
|
|
app.use("/api/v1/queue", verifyJWT, queueRoutes.getRouter());
|
|
app.use("/api/v1/status-page", statusPageRoutes.getRouter());
|
|
app.use("/api/v1/notifications", verifyJWT, notificationRoutes.getRouter());
|
|
app.use("/api/v1/diagnostic", verifyJWT, diagnosticRoutes.getRouter());
|
|
app.use("/api/v1/health", (req, res) => {
|
|
res.json({
|
|
status: "OK",
|
|
});
|
|
});
|
|
// FE routes
|
|
app.get("*", (req, res) => {
|
|
res.sendFile(path.join(frontendPath, "index.html"));
|
|
});
|
|
app.use(handleErrors);
|
|
};
|
|
|
|
startApp().catch((error) => {
|
|
logger.error({
|
|
message: error.message,
|
|
service: SERVICE_NAME,
|
|
method: "startApp",
|
|
stack: error.stack,
|
|
});
|
|
process.exit(1);
|
|
});
|