Skip to content

Commit

Permalink
Merge pull request #464 from OneCommunityGlobal/Abdel-streamlined-dat…
Browse files Browse the repository at this point in the history
…a-input-for-new-members

Abdel streamlined data input for new members
  • Loading branch information
one-community authored Sep 9, 2023
2 parents 7724a16 + aab1c26 commit 40e6398
Show file tree
Hide file tree
Showing 9 changed files with 362 additions and 58 deletions.
249 changes: 249 additions & 0 deletions src/controllers/profileInitialSetupController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,249 @@
const mongoose = require('mongoose');
const { v4: uuidv4 } = require('uuid');
const moment = require('moment-timezone');
const jwt = require('jsonwebtoken');
const emailSender = require('../utilities/emailSender');
const config = require('../config');


// returns the email body that includes the setup link for the recipient.
function sendLinkMessage(Link) {
const message = `<p>Hello,</p>
<p>Welcome to the One Community Highest Good Network! We’re excited to have you as a new member of our team.<br>
To work as a member of our volunteer team, you need to complete the following profile setup:</p>
<p><a href="${Link}">Click to Complete Profile</a></p>
<p>Please complete all fields and be accurate. If you have any questions or need assistance during the profile setup process, please contact your manager.</p>
<p>Thank you and welcome!</p>
<p>With Gratitude,<br>
One Community.</p>`;
return message;
}

// returns the email body containing the details of the newly created user.
function informManagerMessage(user) {
const message = `
<p>Hello,</p>
<p>A new user has created their profile on our platform. Below is the information provided by the user:</p>
<table border="1" cellpadding="10">
<tr>
<td><strong>First Name:</strong></td>
<td>${user.firstName}</td>
</tr>
<tr>
<td><strong>Last Name:</strong></td>
<td>${user.lastName}</td>
</tr>
<tr>
<td><strong>Email:</strong></td>
<td>${user.email}</td>
</tr>
<tr>
<td><strong>Phone Number:</strong></td>
<td>${user.phoneNumber}</td>
</tr>
<tr>
<td><strong>Weekly Committed Hours:</strong></td>
<td>${user.weeklycommittedHours}</td>
</tr>
<tr>
<td><strong>Collaboration Preference:</strong></td>
<td>${user.collaborationPreference}</td>
</tr>
<tr>
<td><strong>Job Title:</strong></td>
<td>${user.jobTitle}</td>
</tr>
<tr>
<td><strong>Time Zone:</strong></td>
<td>${user.timeZone}</td>
</tr>
<tr>
<td><strong>Location:</strong></td>
<td>${user.location}</td>
</tr>
</table>
<p>Please check the details provided by the user. If any errors were made, kindly ask them to correct the information accordingly.</p>
<p>Thank you,</p>
<p>One Community.</p>`;
return message;
}

const profileInitialSetupController = function (ProfileInitialSetupToken, userProfile, Project) {
const { JWT_SECRET } = config;

/*
Function to handle token generation and email process:
- Generates a new token and saves it to the database.
- If the email already has a token, the old one is deleted.
- Sets the token expiration to one week.
- Generates a link using the token and emails it to the recipient.
*/
const getSetupToken = async (req, res) => {
let { email, baseUrl } = req.body
email = email.toLowerCase()
const token = uuidv4();
const expiration = moment().tz('America/Los_Angeles').add(1, 'week')
try {
await ProfileInitialSetupToken.findOneAndDelete({ email });

const newToken = new ProfileInitialSetupToken({
token,
email,
expiration: expiration.toDate(),
});

const savedToken = await newToken.save();
const link = `${baseUrl}/ProfileInitialSetup/${savedToken.token}`

emailSender(
email,
'NEEDED: Complete your One Community profile setup',
sendLinkMessage(link),
null,
null,
);

res.status(200).send(link);

} catch (error) {
res.status(400).send(`Error: ${error}`);
}

}

/*
Function to validate a token:
- Checks if the token exists in the database.
- Verifies that the token's expiration date has not passed yet.
*/
const validateSetupToken = async (req, res) => {
const { token } = req.body
const currentMoment = moment.tz('America/Los_Angeles');
try {
const foundToken = await ProfileInitialSetupToken.findOne({ token });
if (foundToken) {
const expirationMoment = moment(foundToken.expiration);

if (expirationMoment.isAfter(currentMoment)) {
res.status(200).send("Valid token");
} else {
res.status(400).send("Invalid token");
}
} else {
res.status(404).send("Token not found")
}
} catch (error) {
res.status(500).send(`Error finding token: ${error}`);
}

}

/*
Function for creating and authenticating a new user:
- Validates the token used to submit the form.
- Creates a new user using the information received through req.body.
- Sends an email to the manager to inform them of the new user creation.
- Deletes the token used for user creation from the database.
- Generates a JWT token using the newly created user information.
- Sends the JWT as a response.
*/
const setUpNewUser = async (req, res) => {
const { token } = req.body;
const currentMoment = moment.tz('America/Los_Angeles');
try {
const foundToken = await ProfileInitialSetupToken.findOne({ token });
if (foundToken) {
const expirationMoment = moment(foundToken.expiration);

if (expirationMoment.isAfter(currentMoment)) {

const defaultProject = await Project.findOne({ projectName: "Orientation and Initial Setup" })

const newUser = new userProfile();
newUser.password = req.body.password
newUser.role = "Volunteer";
newUser.firstName = req.body.firstName;
newUser.lastName = req.body.lastName;
newUser.jobTitle = req.body.jobTitle;
newUser.phoneNumber = req.body.phoneNumber;
newUser.bio = "";
newUser.weeklycommittedHours = req.body.weeklycommittedHours;
newUser.personalLinks = [];
newUser.adminLinks = [];
newUser.teams = Array.from(new Set([]));
newUser.projects = Array.from(new Set([defaultProject]));
newUser.createdDate = Date.now();
newUser.email = req.body.email;
newUser.weeklySummaries = [{ summary: '' }];
newUser.weeklySummariesCount = 0;
newUser.weeklySummaryOption = 'Required';
newUser.mediaUrl = '';
newUser.collaborationPreference = req.body.collaborationPreference;
newUser.timeZone = req.body.timeZone || 'America/Los_Angeles';
newUser.location = req.body.location;
newUser.bioPosted = 'default';
newUser.privacySettings.email = req.body.privacySettings.email
newUser.privacySettings.phoneNumber = req.body.privacySettings.phoneNumber
const savedUser = await newUser.save();

emailSender(
process.env.MANAGER_EMAIL,
'New User Profile Created',
informManagerMessage(savedUser),
null,
null,
);
await ProfileInitialSetupToken.findByIdAndDelete(foundToken._id);

const jwtPayload = {
userid: savedUser._id,
role: savedUser.role,
permissions: savedUser.permissions,
expiryTimestamp: moment().add(config.TOKEN.Lifetime, config.TOKEN.Units),
};

const token = jwt.sign(jwtPayload, JWT_SECRET);

res.send({ token }).status(200);
} else {
res.status(400).send("Token is expired");
}
} else {
res.status(400).send("Invalid token");
}
} catch (error) {
res.status(500).send(`Error: ${error}`);
}

}

/*
Function for sending https://opencagedata.com API key:
- Checks if token used in the request is valid.
- sends the API Key as response
*/
const getTimeZoneAPIKeyByToken = async (req, res) => {
const token = req.body.token;
const premiumKey = process.env.TIMEZONE_PREMIUM_KEY;

const foundToken = await ProfileInitialSetupToken.findOne({ token });

if (foundToken) {
res.status(200).send({ userAPIKey: premiumKey });
return;
} else {
res.status(403).send('Unauthorized Request');
return;
}

};

return {
getSetupToken,
setUpNewUser,
validateSetupToken,
getTimeZoneAPIKeyByToken
}
};

module.exports = profileInitialSetupController;
2 changes: 1 addition & 1 deletion src/controllers/userProfileController.js
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ const userProfileController = function (UserProfile) {
const isRequestorAuthorized = !!(
canRequestorUpdateUser(req.body.requestor.requestorId, userid) && (
await hasPermission(req.body.requestor.role, 'putUserProfile')
|| req.body.requestor.requestorId === userid
|| req.body.requestor.requestorId === userid
)
);

Expand Down
1 change: 1 addition & 0 deletions src/cronjobs/userProfileJobs.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const userProfileJobs = () => {
await userhelper.applyMissedHourForCoreTeam();
await userhelper.emailWeeklySummariesForAllUsers();
await userhelper.deleteBlueSquareAfterYear();
await userhelper.deleteExpiredTokens();
}
await userhelper.awardNewBadges();
await userhelper.reActivateUser();
Expand Down
Loading

0 comments on commit 40e6398

Please sign in to comment.