move to src

This commit is contained in:
Alex Holliday committed 2025-07-25 15:33:48 -07:00
1 parent acc910cbc0
commit 1c47c8ce2c
127 files changed
+71 -19

No files matched your search

+22
View File
@@ -0,0 +1,22 @@
import logger from "../utils/logger.js";
import ServiceRegistry from "../service/system/serviceRegistry.js";
import StringService from "../service/system/stringService.js";
const handleErrors = (error, req, res, next) => {
const status = error.status || 500;
const stringService = ServiceRegistry.get(StringService.SERVICE_NAME);
const message = error.message || stringService.authIncorrectPassword;
const service = error.service || stringService.unknownService;
logger.error({
message: message,
service: service,
method: error.method,
stack: error.stack,
});
res.error({
status,
msg: message,
});
};
export { handleErrors };
+57
View File
@@ -0,0 +1,57 @@
import jwt from "jsonwebtoken";
const TOKEN_PREFIX = "Bearer ";
const SERVICE_NAME = "allowedRoles";
import ServiceRegistry from "../service/system/serviceRegistry.js";
import StringService from "../service/system/stringService.js";
import SettingsService from "../service/system/settingsService.js";
const isAllowed = (allowedRoles) => {
return (req, res, next) => {
const token = req.headers["authorization"];
const stringService = ServiceRegistry.get(StringService.SERVICE_NAME);
// If no token is pressent, return an error
if (!token) {
const error = new Error(stringService.noAuthToken);
error.status = 401;
error.service = SERVICE_NAME;
next(error);
return;
}
// If the token is improperly formatted, return an error
if (!token.startsWith(TOKEN_PREFIX)) {
const error = new Error(stringService.invalidAuthToken);
error.status = 400;
error.service = SERVICE_NAME;
next(error);
return;
}
// Parse the token
try {
const parsedToken = token.slice(TOKEN_PREFIX.length, token.length);
const { jwtSecret } = ServiceRegistry.get(SettingsService.SERVICE_NAME).getSettings();
var decoded = jwt.verify(parsedToken, jwtSecret);
const userRoles = decoded.role;
// Check if the user has the required role
if (userRoles.some((role) => allowedRoles.includes(role))) {
next();
return;
} else {
const error = new Error(stringService.insufficientPermissions);
error.status = 403;
error.service = SERVICE_NAME;
next(error);
return;
}
} catch (error) {
error.status = 401;
error.method = "isAllowed";
error.service = SERVICE_NAME;
next(error);
return;
}
};
};
export { isAllowed };
+27
View File
@@ -0,0 +1,27 @@
import logger from "../utils/logger.js";
const languageMiddleware = (stringService, translationService) => async (req, res, next) => {
try {
const acceptLanguage = req.headers["accept-language"] || "en";
const language = acceptLanguage.split(",")[0].slice(0, 2).toLowerCase();
translationService.setLanguage(language);
stringService.setLanguage(language);
next();
} catch (error) {
logger.error({
message: error.message,
service: "languageMiddleware",
});
const acceptLanguage = req.headers["accept-language"] || "en";
const language = acceptLanguage.split(",")[0].slice(0, 2).toLowerCase();
translationService.setLanguage(language);
stringService.setLanguage(language);
next();
}
};
export default languageMiddleware;
+66
View File
@@ -0,0 +1,66 @@
/**
* Middleware that adds standardized response methods to the Express response object.
* This allows for consistent API responses throughout the application.
*
* @param {import('express').Request} req - Express request object
* @param {import('express').Response} res - Express response object
* @param {import('express').NextFunction} next - Express next middleware function
*/
const responseHandler = (req, res, next) => {
/**
* Sends a standardized success response
*
* @param {Object} options - Success response options
* @param {number} [options.status=200] - HTTP status code
* @param {string} [options.msg="OK"] - Success message
* @param {*} [options.data=null] - Response data payload
* @returns {Object} Express response object
*/
res.success = ({ status = 200, msg = "OK", data = null, headers = {} }) => {
// Set custom headers if provided
Object.entries(headers).forEach(([key, value]) => {
res.set(key, value);
});
return res.status(status).json({
success: true,
msg: msg,
data: data,
});
};
/**
* Sends a standardized error response
*
* @param {Object} options - Error response options
* @param {number} [options.status=500] - HTTP status code
* @param {string} [options.msg="Internal server error"] - Error message
* @param {*} [options.data=null] - Additional error data (if any)
* @returns {Object} Express response object
*/
res.error = ({ status = 500, msg = "Internal server error", data = null }) => {
return res.status(status).json({
success: false,
msg,
data,
});
};
/**
* Sends a raw file response (for CSV, PDF, etc.)
* @param {Object} options
* @param {Buffer|string} options.data - The file content
* @param {Object} options.headers - Headers to set (e.g. Content-Type, Content-Disposition)
* @param {number} [options.status=200] - HTTP status code
*/
res.file = ({ data, headers = {}, status = 200 }) => {
Object.entries(headers).forEach(([key, value]) => {
res.setHeader(key, value);
});
return res.status(status).send(data);
};
next();
};
export { responseHandler };
+57
View File
@@ -0,0 +1,57 @@
import jwt from "jsonwebtoken";
import ServiceRegistry from "../service/system/serviceRegistry.js";
import SettingsService from "../service/system/settingsService.js";
import StringService from "../service/system/stringService.js";
const SERVICE_NAME = "verifyJWT";
const TOKEN_PREFIX = "Bearer ";
/**
* Verifies the JWT token
* @function
* @param {express.Request} req
* @param {express.Response} res
* @param {express.NextFunction} next
* @returns {express.Response}
*/
const verifyJWT = (req, res, next) => {
const stringService = ServiceRegistry.get(StringService.SERVICE_NAME);
const token = req.headers["authorization"];
// Make sure a token is provided
if (!token) {
const error = new Error(stringService.noAuthToken);
error.status = 401;
error.service = SERVICE_NAME;
next(error);
return;
}
// Make sure it is properly formatted
if (!token.startsWith(TOKEN_PREFIX)) {
const error = new Error(stringService.invalidAuthToken); // Instantiate a new Error object for improperly formatted token
error.status = 401;
error.service = SERVICE_NAME;
error.method = "verifyJWT";
next(error);
return;
}
const parsedToken = token.slice(TOKEN_PREFIX.length, token.length);
// Verify the token's authenticity
const { jwtSecret } = ServiceRegistry.get(SettingsService.SERVICE_NAME).getSettings();
jwt.verify(parsedToken, jwtSecret, (err, decoded) => {
if (err) {
const errorMessage = err.name === "TokenExpiredError" ? stringService.expiredAuthToken : stringService.invalidAuthToken;
err.details = { msg: errorMessage };
err.status = 401;
err.service = SERVICE_NAME;
err.method = "verifyJWT";
next(err);
return;
} else {
// Token is valid, carry on
req.user = decoded;
next();
}
});
};
export { verifyJWT };
+59
View File
@@ -0,0 +1,59 @@
import logger from "../utils/logger.js";
import ServiceRegistry from "../service/system/serviceRegistry.js";
import StringService from "../service/system/stringService.js";
import { ObjectId } from "mongodb";
const SERVICE_NAME = "verifyOwnership";
const verifyOwnership = (Model, paramName) => {
const stringService = ServiceRegistry.get(StringService.SERVICE_NAME);
return async (req, res, next) => {
const userId = req.user._id;
let documentId = req.params[paramName];
try {
if (typeof documentId === "string") {
documentId = ObjectId.createFromHexString(documentId);
}
const doc = await Model.findById(documentId);
//If the document is not found, return a 404 error
if (!doc) {
logger.error({
message: stringService.verifyOwnerNotFound,
service: SERVICE_NAME,
method: "verifyOwnership",
});
const error = new Error(stringService.verifyOwnerNotFound);
error.status = 404;
throw error;
}
// Special case for User model, as it will not have a `userId` field as other docs will
if (Model.modelName === "User") {
if (userId.toString() !== doc._id.toString()) {
const error = new Error(stringService.verifyOwnerUnauthorized);
error.status = 403;
throw error;
}
next();
return;
}
// If the userID does not match the document's userID, return a 403 error
if (userId.toString() !== doc.userId.toString()) {
const error = new Error("Unauthorized");
error.status = 403;
throw error;
}
next();
return;
} catch (error) {
error.service = SERVICE_NAME;
error.method = "verifyOwnership";
next(error);
return;
}
};
};
export { verifyOwnership };
+66
View File
@@ -0,0 +1,66 @@
const jwt = require("jsonwebtoken");
const logger = require("../utils/logger");
const SERVICE_NAME = "verifyAdmin";
const TOKEN_PREFIX = "Bearer ";
import ServiceRegistry from "../service/serviceRegistry.js";
import SettingsService from "../service/settingsService.js";
import StringService from "../service/stringService.js";
/**
* Verifies the JWT token
* @function
* @param {express.Request} req
* @param {express.Response} res
* @param {express.NextFunction} next
* @returns {express.Response}
*/
const verifySuperAdmin = (req, res, next) => {
const stringService = ServiceRegistry.get(StringService.SERVICE_NAME);
const token = req.headers["authorization"];
// Make sure a token is provided
if (!token) {
const error = new Error(stringService.noAuthToken);
error.status = 401;
error.service = SERVICE_NAME;
next(error);
return;
}
// Make sure it is properly formatted
if (!token.startsWith(TOKEN_PREFIX)) {
const error = new Error(stringService.invalidAuthToken); // Instantiate a new Error object for improperly formatted token
error.status = 400;
error.service = SERVICE_NAME;
error.method = "verifySuperAdmin";
next(error);
return;
}
const parsedToken = token.slice(TOKEN_PREFIX.length, token.length);
// verify admin role is present
const { jwtSecret } = ServiceRegistry.get(SettingsService.SERVICE_NAME).getSettings();
jwt.verify(parsedToken, jwtSecret, (err, decoded) => {
if (err) {
logger.error({
message: err.message,
service: SERVICE_NAME,
method: "verifySuperAdmin",
stack: err.stack,
details: stringService.invalidAuthToken,
});
return res.status(401).json({ success: false, msg: stringService.invalidAuthToken });
}
if (decoded.role.includes("superadmin") === false) {
logger.error({
message: stringService.invalidAuthToken,
service: SERVICE_NAME,
method: "verifySuperAdmin",
stack: err.stack,
});
return res.status(401).json({ success: false, msg: stringService.unauthorized });
}
next();
});
};
module.exports = { verifySuperAdmin };
+38
View File
@@ -0,0 +1,38 @@
const SERVICE_NAME = "verifyTeamAccess";
const verifyTeamAccess = (Model, paramName) => {
return async (req, res, next) => {
try {
const documentId = req.params[paramName];
const doc = await Model.findById(documentId);
if (!doc) {
const error = new Error("Document not found");
error.status = 404;
throw error;
}
if (!req?.user?.teamId || !doc.teamId) {
const error = new Error("Missing team information");
error.status = 400;
throw error;
}
if (req.user.teamId.toString() === doc.teamId.toString()) {
next();
return;
}
const error = new Error("Unauthorized");
error.status = 403;
throw error;
} catch (error) {
error.service = SERVICE_NAME;
error.method = "verifyTeamAccess";
next(error);
return;
}
};
};
export { verifyTeamAccess };