diff --git a/Server/index.js b/Server/index.js index 996c4fd07..8f7d845ac 100644 --- a/Server/index.js +++ b/Server/index.js @@ -8,16 +8,31 @@ import cors from "cors"; import logger from "./utils/logger.js"; import { verifyJWT } from "./middleware/verifyJWT.js"; import { handleErrors } from "./middleware/handleErrors.js"; -import authRouter from "./routes/authRoute.js"; -import inviteRouter from "./routes/inviteRoute.js"; -import monitorRouter from "./routes/monitorRoute.js"; -import checkRouter from "./routes/checkRoute.js"; -import maintenanceWindowRouter from "./routes/maintenanceWindowRoute.js"; -import settingsRouter from "./routes/settingsRoute.js"; -import statusPageRouter from "./routes/statusPageRoute.js"; import { fileURLToPath } from "url"; -import queueRouter from "./routes/queueRoute.js"; +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"; //JobQueue service and dependencies import JobQueue from "./service/jobQueue.js"; @@ -41,102 +56,68 @@ import mjml2html from "mjml"; 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"; -import db from "./db/mongo/MongoDB.js"; +// Service Registry +import ServiceRegistry from "./service/serviceRegistry.js"; + +import MongoDB from "./db/mongo/MongoDB.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") ); +let server; + const PORT = 5000; +const shutdown = async () => { + if (isShuttingDown) { + return; + } + isShuttingDown = true; + logger.info({ message: "Attempting graceful shutdown" }); + setTimeout(() => { + logger.error({ + message: "Could not shut down in time, forcing shutdown", + service: SERVICE_NAME, + method: "shutdown", + }); + process.exit(1); + }, SHUTDOWN_TIMEOUT); + try { + server.close(); + await ServiceRegistry.get(JobQueue.SERVICE_NAME).obliterate(); + 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(); - // middlewares - app.use( - cors() - //We will add configuration later - ); - app.use(express.json()); - app.use(helmet()); - - // Add db, jobQueue, emailService, and settingsService to request object for easy access - app.use((req, res, next) => { - req.db = db; - req.jobQueue = jobQueue; - req.emailService = emailService; - req.settingsService = settingsService; - next(); - }); - - // Swagger UI - app.use("/api-docs", swaggerUi.serve, swaggerUi.setup(openApiSpec)); - - //routes - app.use("/api/v1/auth", authRouter); - app.use("/api/v1/settings", verifyJWT, settingsRouter); - app.use("/api/v1/invite", inviteRouter); - app.use("/api/v1/monitors", verifyJWT, monitorRouter); - app.use("/api/v1/checks", verifyJWT, checkRouter); - app.use("/api/v1/maintenance-window", verifyJWT, maintenanceWindowRouter); - app.use("/api/v1/queue", verifyJWT, queueRouter); - app.use("/api/v1/status-page", statusPageRouter); - - app.use("/api/v1/dummy-data", async (req, res) => { - try { - const response = await axios.get( - "https://gist.githubusercontent.com/ajhollid/9afa39410c7bbf52cc905f285a2225bf/raw/429a231a3559ebc95f6f488ed2c766bd7d6f46e5/dummyData.json", - { - headers: { - "Content-Type": "application/json", - "Cache-Control": "no-cache", - }, - } - ); - return res.status(200).json(response.data); - } catch (error) { - return res.status(500).json({ message: error.message }); - } - }); - - //health check - app.use("/api/v1/healthy", (req, res) => { - try { - logger.info({ message: "Checking Health of the server." }); - return res.status(200).json({ message: "Healthy" }); - } catch (error) { - logger.error({ - message: error.message, - service: SERVICE_NAME, - method: "healthCheck", - stack: error.stack, - }); - return res.status(500).json({ message: error.message }); - } - }); - - /** - * Error handler middleware - * Should be called last - */ - app.use(handleErrors); + // Create DB + const db = new MongoDB(); + await db.connect(); // Create services - await db.connect(); - const server = app.listen(PORT, () => { - logger.info({ message: `server started on port:${PORT}` }); - }); - const settingsService = new SettingsService(AppSettings); await settingsService.loadSettings(); const emailService = new EmailService( @@ -162,39 +143,94 @@ const startApp = async () => { Worker ); - const shutdown = async () => { - if (isShuttingDown) { - return; - } - isShuttingDown = true; - logger.info({ message: "Attempting graceful shutdown" }); - setTimeout(() => { - logger.error({ - message: "Could not shut down in time, forcing shutdown", - service: SERVICE_NAME, - method: "shutdown", - }); - process.exit(1); - }, SHUTDOWN_TIMEOUT); - try { - server.close(); - await jobQueue.obliterate(); - await db.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, - }); - } - }; + // Register services + ServiceRegistry.register(JobQueue.SERVICE_NAME, jobQueue); + 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(StatusService.SERVICE_NAME, statusService); + ServiceRegistry.register(NotificationService.SERVICE_NAME, notificationService); + 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) + ); + + const monitorController = new MonitorController( + ServiceRegistry.get(MongoDB.SERVICE_NAME), + ServiceRegistry.get(SettingsService.SERVICE_NAME), + ServiceRegistry.get(JobQueue.SERVICE_NAME) + ); + + const settingsController = new SettingsController( + ServiceRegistry.get(MongoDB.SERVICE_NAME), + ServiceRegistry.get(SettingsService.SERVICE_NAME) + ); + + const checkController = new CheckController( + ServiceRegistry.get(MongoDB.SERVICE_NAME), + ServiceRegistry.get(SettingsService.SERVICE_NAME) + ); + + const inviteController = new InviteController( + ServiceRegistry.get(MongoDB.SERVICE_NAME), + ServiceRegistry.get(SettingsService.SERVICE_NAME), + ServiceRegistry.get(EmailService.SERVICE_NAME) + ); + + const maintenanceWindowController = new MaintenanceWindowController( + ServiceRegistry.get(MongoDB.SERVICE_NAME), + ServiceRegistry.get(SettingsService.SERVICE_NAME) + ); + + const queueController = new QueueController(ServiceRegistry.get(JobQueue.SERVICE_NAME)); + + const statusPageController = new StatusPageController( + 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); + // Middleware + app.use( + cors() + //We will add configuration later + ); + app.use(express.json()); + app.use(helmet()); + // 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(handleErrors); }; startApp().catch((error) => {