Skip to content

Commit

Permalink
Use service registry instead of adding services to request
Browse files Browse the repository at this point in the history
  • Loading branch information
ajhollid committed Dec 24, 2024
1 parent 139c1fd commit 83e489c
Showing 1 changed file with 149 additions and 113 deletions.
262 changes: 149 additions & 113 deletions Server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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 services
// Create DB
const db = new MongoDB();
await db.connect();
const server = app.listen(PORT, () => {
logger.info({ message: `server started on port:${PORT}` });
});

// Create services
const settingsService = new SettingsService(AppSettings);
await settingsService.loadSettings();
const emailService = new EmailService(
Expand All @@ -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());

Check failure

Code scanning / CodeQL

Missing rate limiting High

This route handler performs
authorization
, but is not rate-limited.
app.use("/api/v1/invite", inviteRoutes.getRouter());
app.use("/api/v1/monitors", verifyJWT, monitorRoutes.getRouter());

Check failure

Code scanning / CodeQL

Missing rate limiting High

This route handler performs
authorization
, but is not rate-limited.
app.use("/api/v1/checks", verifyJWT, checkRoutes.getRouter());

Check failure

Code scanning / CodeQL

Missing rate limiting High

This route handler performs
authorization
, but is not rate-limited.
app.use("/api/v1/maintenance-window", verifyJWT, maintenanceWindowRoutes.getRouter());

Check failure

Code scanning / CodeQL

Missing rate limiting High

This route handler performs
authorization
, but is not rate-limited.
app.use("/api/v1/queue", verifyJWT, queueRoutes.getRouter());

Check failure

Code scanning / CodeQL

Missing rate limiting High

This route handler performs
authorization
, but is not rate-limited.
app.use("/api/v1/status-page", statusPageRoutes.getRouter());
app.use(handleErrors);
};

startApp().catch((error) => {
Expand Down

0 comments on commit 83e489c

Please sign in to comment.