move to src
This commit is contained in:
1 parent
acc910cbc0
commit
1c47c8ce2c
127 files changed
+71
-19
No files matched your search
Executable
+22
@@ -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 };
|
||||
Executable
+57
@@ -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 };
|
||||
Executable
+27
@@ -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;
|
||||
Executable
+66
@@ -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 };
|
||||
Executable
+57
@@ -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 };
|
||||
Executable
+59
@@ -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 };
|
||||
Executable
+66
@@ -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 };
|
||||
@@ -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 };
|
||||
Reference in new issue
Block a user