mirror of
https://github.com/bluewave-labs/Checkmate.git
synced 2026-01-07 18:29:41 -06:00
add invite service
This commit is contained in:
@@ -1,41 +1,46 @@
|
||||
import { inviteRoleValidation, inviteBodyValidation, inviteVerificationBodyValidation } from "../validation/joi.js";
|
||||
import jwt from "jsonwebtoken";
|
||||
import { getTokenFromHeaders } from "../utils/utils.js";
|
||||
import { asyncHandler, createServerError } from "../utils/errorUtils.js";
|
||||
import { inviteBodyValidation, inviteVerificationBodyValidation } from "../validation/joi.js";
|
||||
import { asyncHandler } from "../utils/errorUtils.js";
|
||||
|
||||
const SERVICE_NAME = "inviteController";
|
||||
|
||||
/**
|
||||
* Controller for handling user invitation operations
|
||||
* Manages invite token generation, email sending, and token verification
|
||||
*/
|
||||
class InviteController {
|
||||
constructor(db, settingsService, emailService, stringService) {
|
||||
/**
|
||||
* Creates a new InviteController instance
|
||||
* @param {Object} dependencies - Dependencies injected into the controller
|
||||
* @param {Object} dependencies.db - Database connection instance
|
||||
* @param {Object} dependencies.settingsService - Service for managing application settings
|
||||
* @param {Object} dependencies.emailService - Service for sending emails
|
||||
* @param {Object} dependencies.stringService - Service for internationalized strings
|
||||
* @param {Object} dependencies.inviteService - Service for invite-related operations
|
||||
*/
|
||||
constructor({ db, settingsService, emailService, stringService, inviteService }) {
|
||||
this.db = db;
|
||||
this.settingsService = settingsService;
|
||||
this.emailService = emailService;
|
||||
this.stringService = stringService;
|
||||
this.inviteService = inviteService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Issues an invitation to a new user. Only admins can invite new users. An invitation token is created and sent via email.
|
||||
* @async
|
||||
* @param {Object} req - The Express request object.
|
||||
* @property {Object} req.headers - The headers of the request.
|
||||
* @property {string} req.headers.authorization - The authorization header containing the JWT token.
|
||||
* @property {Object} req.body - The body of the request.
|
||||
* @property {string} req.body.email - The email of the user to be invited.
|
||||
* @param {Object} res - The Express response object.
|
||||
* @param {function} next - The next middleware function.
|
||||
* @returns {Object} The response object with a success status, a message indicating the sending of the invitation, and the invitation token.
|
||||
* @throws {Error} If there is an error during the process, especially if there is a validation error (422).
|
||||
* Generates an invite token for a user invitation
|
||||
* @param {Object} req - Express request object
|
||||
* @param {Object} req.body - Request body containing invite details
|
||||
* @param {Object} req.user - Authenticated user object
|
||||
* @param {string} req.user.teamId - Team ID of the authenticated user
|
||||
* @param {Object} res - Express response object
|
||||
* @returns {Promise<Object>} Response with invite token data
|
||||
*/
|
||||
getInviteToken = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
// Only admins can invite
|
||||
const token = getTokenFromHeaders(req.headers);
|
||||
const { role, teamId } = jwt.decode(token);
|
||||
req.body.teamId = teamId;
|
||||
await inviteRoleValidation.validateAsync({ roles: role });
|
||||
await inviteBodyValidation.validateAsync(req.body);
|
||||
|
||||
const inviteToken = await this.db.requestInviteToken({ ...req.body });
|
||||
async (req, res) => {
|
||||
const invite = req.body;
|
||||
const teamId = req?.user?.teamId;
|
||||
invite.teamId = teamId;
|
||||
await inviteBodyValidation.validateAsync(invite);
|
||||
const inviteToken = await this.inviteService.getInviteToken({ invite, teamId });
|
||||
return res.success({
|
||||
msg: this.stringService.inviteIssued,
|
||||
data: inviteToken,
|
||||
@@ -45,27 +50,26 @@ class InviteController {
|
||||
"getInviteToken"
|
||||
);
|
||||
|
||||
/**
|
||||
* Sends an invitation email to a user
|
||||
* @param {Object} req - Express request object
|
||||
* @param {Object} req.body - Request body containing invite details
|
||||
* @param {Object} req.user - Authenticated user object
|
||||
* @param {string} req.user.teamId - Team ID of the authenticated user
|
||||
* @param {string} req.user.firstName - First name of the authenticated user
|
||||
* @param {Object} res - Express response object
|
||||
* @returns {Promise<Object>} Response with invite token data
|
||||
*/
|
||||
sendInviteEmail = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
// Only admins can invite
|
||||
const token = getTokenFromHeaders(req.headers);
|
||||
const { role, firstname, teamId } = jwt.decode(token);
|
||||
req.body.teamId = teamId;
|
||||
await inviteRoleValidation.validateAsync({ roles: role });
|
||||
await inviteBodyValidation.validateAsync(req.body);
|
||||
async (req, res) => {
|
||||
const inviteRequest = req.body;
|
||||
inviteRequest.teamId = req?.user?.teamId;
|
||||
await inviteBodyValidation.validateAsync(inviteRequest);
|
||||
|
||||
const inviteToken = await this.db.requestInviteToken({ ...req.body });
|
||||
const { clientHost } = this.settingsService.getSettings();
|
||||
|
||||
const html = await this.emailService.buildEmail("employeeActivationTemplate", {
|
||||
name: firstname,
|
||||
link: `${clientHost}/register/${inviteToken.token}`,
|
||||
const inviteToken = await this.inviteService.sendInviteEmail({
|
||||
inviteRequest,
|
||||
firstName: req?.user?.firstName,
|
||||
});
|
||||
const result = await this.emailService.sendEmail(req.body.email, "Welcome to Uptime Monitor", html);
|
||||
if (!result) {
|
||||
throw createServerError("Failed to send invite e-mail... Please verify your settings.");
|
||||
}
|
||||
|
||||
return res.success({
|
||||
msg: this.stringService.inviteIssued,
|
||||
data: inviteToken,
|
||||
@@ -75,17 +79,25 @@ class InviteController {
|
||||
"sendInviteEmail"
|
||||
);
|
||||
|
||||
inviteVerifyController = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
/**
|
||||
* Verifies an invite token and returns invite details
|
||||
* @param {Object} req - Express request object
|
||||
* @param {Object} req.body - Request body containing the invite token
|
||||
* @param {string} req.body.token - The invite token to verify
|
||||
* @param {Object} res - Express response object
|
||||
* @returns {Promise<Object>} Response with verified invite data
|
||||
*/
|
||||
verifyInviteToken = asyncHandler(
|
||||
async (req, res) => {
|
||||
await inviteVerificationBodyValidation.validateAsync(req.body);
|
||||
const invite = await this.db.getInviteToken(req.body.token);
|
||||
const invite = await this.inviteService.verifyInviteToken({ inviteToken: req?.body?.token });
|
||||
return res.success({
|
||||
msg: this.stringService.inviteVerified,
|
||||
data: invite,
|
||||
});
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"inviteVerifyController"
|
||||
"verifyInviteToken"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -61,6 +61,7 @@ import SuperSimpleQueueHelper from "./service/infrastructure/SuperSimpleQueue/Su
|
||||
import UserService from "./service/business/userService.js";
|
||||
import CheckService from "./service/business/checkService.js";
|
||||
import DiagnosticService from "./service/business/diagnosticService.js";
|
||||
import InviteService from "./service/business/inviteService.js";
|
||||
|
||||
//Network service and dependencies
|
||||
import NetworkService from "./service/infrastructure/networkService.js";
|
||||
@@ -198,7 +199,12 @@ const startApp = async () => {
|
||||
stringService,
|
||||
});
|
||||
const diagnosticService = new DiagnosticService();
|
||||
|
||||
const inviteService = new InviteService({
|
||||
db,
|
||||
settingsService,
|
||||
emailService,
|
||||
stringService,
|
||||
});
|
||||
// const jobQueueHelper = new JobQueueHelper({
|
||||
// redisService,
|
||||
// Queue,
|
||||
@@ -304,12 +310,13 @@ const startApp = async () => {
|
||||
checkService: ServiceRegistry.get(CheckService.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 inviteController = new InviteController({
|
||||
db: ServiceRegistry.get(MongoDB.SERVICE_NAME),
|
||||
settingsService: ServiceRegistry.get(SettingsService.SERVICE_NAME),
|
||||
emailService: ServiceRegistry.get(EmailService.SERVICE_NAME),
|
||||
stringService: ServiceRegistry.get(StringService.SERVICE_NAME),
|
||||
inviteService,
|
||||
});
|
||||
|
||||
const maintenanceWindowController = new MaintenanceWindowController(
|
||||
ServiceRegistry.get(MongoDB.SERVICE_NAME),
|
||||
|
||||
@@ -10,8 +10,8 @@ class InviteRoutes {
|
||||
}
|
||||
|
||||
initRoutes() {
|
||||
this.router.post("/send", this.inviteController.sendInviteEmail);
|
||||
this.router.post("/verify", this.inviteController.inviteVerifyController);
|
||||
this.router.post("/send", verifyJWT, isAllowed(["admin", "superadmin"]), this.inviteController.sendInviteEmail);
|
||||
this.router.post("/verify", this.inviteController.verifyInviteToken);
|
||||
this.router.post("/", verifyJWT, isAllowed(["admin", "superadmin"]), this.inviteController.getInviteToken);
|
||||
}
|
||||
|
||||
|
||||
40
server/service/business/inviteService.js
Normal file
40
server/service/business/inviteService.js
Normal file
@@ -0,0 +1,40 @@
|
||||
const SERVICE_NAME = "inviteService";
|
||||
import { createServerError } from "../../utils/errorUtils.js";
|
||||
|
||||
class InviteService {
|
||||
static SERVICE_NAME = SERVICE_NAME;
|
||||
|
||||
constructor({ db, settingsService, emailService, stringService }) {
|
||||
this.db = db;
|
||||
this.settingsService = settingsService;
|
||||
this.emailService = emailService;
|
||||
this.stringService = stringService;
|
||||
}
|
||||
|
||||
getInviteToken = async ({ invite, teamId }) => {
|
||||
invite.teamId = teamId;
|
||||
const inviteToken = await this.db.requestInviteToken(invite);
|
||||
return inviteToken;
|
||||
};
|
||||
|
||||
sendInviteEmail = async ({ inviteRequest, firstName }) => {
|
||||
const inviteToken = await this.db.requestInviteToken({ ...inviteRequest });
|
||||
const { clientHost } = this.settingsService.getSettings();
|
||||
|
||||
const html = await this.emailService.buildEmail("employeeActivationTemplate", {
|
||||
name: firstName,
|
||||
link: `${clientHost}/register/${inviteToken.token}`,
|
||||
});
|
||||
const result = await this.emailService.sendEmail(inviteRequest.email, "Welcome to Uptime Monitor", html);
|
||||
if (!result) {
|
||||
throw createServerError("Failed to send invite e-mail... Please verify your settings.");
|
||||
}
|
||||
};
|
||||
|
||||
verifyInviteToken = async ({ inviteToken }) => {
|
||||
const invite = await this.db.getInviteToken(inviteToken);
|
||||
return invite;
|
||||
};
|
||||
}
|
||||
|
||||
export default InviteService;
|
||||
@@ -87,10 +87,6 @@ const deleteUserParamValidation = joi.object({
|
||||
email: joi.string().email().required(),
|
||||
});
|
||||
|
||||
const inviteRoleValidation = joi.object({
|
||||
roles: joi.custom(roleValidatior(["admin", "superadmin"])).required(),
|
||||
});
|
||||
|
||||
const inviteBodyValidation = joi.object({
|
||||
email: joi.string().trim().email().required().messages({
|
||||
"string.empty": "Email is required",
|
||||
@@ -652,7 +648,6 @@ export {
|
||||
recoveryValidation,
|
||||
recoveryTokenBodyValidation,
|
||||
newPasswordValidation,
|
||||
inviteRoleValidation,
|
||||
inviteBodyValidation,
|
||||
inviteVerificationBodyValidation,
|
||||
createMonitorBodyValidation,
|
||||
|
||||
Reference in New Issue
Block a user