Skip to content

Commit

Permalink
Changed emails templates and link from application validation expirat…
Browse files Browse the repository at this point in the history
…ion time

Co-authored-by: Daniel Ferreira <[email protected]>
  • Loading branch information
FranciscoCardoso913 and dsantosferreira committed Mar 7, 2023
1 parent 0bd0830 commit 441c78e
Show file tree
Hide file tree
Showing 12 changed files with 45 additions and 52 deletions.
8 changes: 4 additions & 4 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ JWT_SECRET=Lendas contam que o Rui foi membro do IEEE.
# Frontend Password Recovery Base Route
PASSWORD_RECOVERY_LINK=https://localhost:3000/recover

# Froantend Application Confirmation Base Route
APPLICATION_CONFIRMATION_LINK=https://localhost:8087/apply/company/validate/
# Frontend Application Confirmation Base Route
APPLICATION_CONFIRMATION_LINK=https://localhost:3000/apply/company/validate/

# Specifies the port in which the app will be exposed
PORT=8087
Expand All @@ -43,10 +43,10 @@ ACCESS_CONTROL_ALLOW_ORIGIN=
# Check this for details on how to configure your personal account for testing (https://support.google.com/accounts/answer/185833?p=InvalidSecondFactor&visit_id=637446218993181653-2339409452&rd=1)

# The email address from which the emails are sent
MAIL_FROM=[email protected]
MAIL_FROM=

# Password for email above
MAIL_FROM_PASSWORD=PgAnnVsAa6Sg8zJp6t
MAIL_FROM_PASSWORD=

# Cloudinary API URL to save images
CLOUDINARY_URL=
Expand Down
20 changes: 9 additions & 11 deletions src/api/middleware/application.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
import CompanyApplication, { CompanyApplicationRules } from "../../models/CompanyApplication.js";
import { APIError, ErrorTypes } from "./errorHandler.js";
import { StatusCodes as HTTPStatus } from "http-status-codes/build/cjs/status-codes.js";
export const exceededCreationTimeLimit = async (email) => {
const cursor = await CompanyApplication.findOne({ email, isVerified: false }).exec();
if (cursor !== null && Date.now() - cursor.submittedAt < 5000 * 60) {
throw new APIError(HTTPStatus.FORBIDDEN, ErrorTypes.FORBIDDEN, CompanyApplicationRules.APPLICATION_RECENTLY_CREATED);
import { VALIDATION_LINK_EXPIRATION } from "../../models/constants/ApplicationStatus.js";
import { SECOND_IN_MS } from "../../models/constants/TimeConstants.js";

export const exceededCreationTimeLimit = async (req, res, next) => {
const application = await CompanyApplication.findOne({ email:req.body.email, isVerified: false });
if (application !== null && Date.now() < application.submittedAt.getTime() + VALIDATION_LINK_EXPIRATION * SECOND_IN_MS) {
return next( new APIError(HTTPStatus.FORBIDDEN, ErrorTypes.FORBIDDEN, CompanyApplicationRules.APPLICATION_RECENTLY_CREATED.msg));
}
return true;
return next();
};

export const deleteApplications = async (email) => {
await CompanyApplication.deleteMany({ email: email, isVerified: false }).catch(function(error) {
console.error(error);
throw (error); // Failure
});
};

15 changes: 5 additions & 10 deletions src/api/middleware/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,16 +78,11 @@ export const hasAdminPrivileges = async (req, res, next) => {
};

export const validToken = (req, res, next) => {
try {
const decoded = verifyAndDecodeToken(req.params.token, config.jwt_secret, next);
const decoded = verifyAndDecodeToken(req.params.token, config.jwt_secret, next);

storeInLocals(req, {
token: decoded,
});
storeInLocals(req, {
token: decoded,
});

return next();
} catch (err) {
console.log(err);
return next(err);
}
return next();
};
1 change: 0 additions & 1 deletion src/api/middleware/validators/validationReasons.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ const ValidationReasons = Object.freeze({
IMAGE_FORMAT: "formats-supported-png-jpeg-jpg",
OFFER_BLOCKED_ADMIN: "offer-blocked-by-admin",
OFFER_HIDDEN: "offer-is-hidden",
ALREADY_VALIDATED: "application-already-validated",
NON_EXISTING_APPLICATION: "application-does-not-exist",
FILE_TOO_LARGE: (max) => `file-cant-be-larger-than-${max}MB`
});
Expand Down
5 changes: 2 additions & 3 deletions src/api/routes/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,10 @@ export default (app) => {
/**
* Creates a new Company Application
*/
router.post("/", validators.create, async (req, res, next) => {
router.post("/", validators.create, applicationMiddleware.exceededCreationTimeLimit, async (req, res, next) => {
try {
await applicationMiddleware.exceededCreationTimeLimit(req.body.email);
await applicationMiddleware.deleteApplications(req.body.email);
const applicationService = new ApplicationService();
await applicationService.deleteApplications(req.body.email);
// This is safe since the service is destructuring the passed object and the fields have been validated
const application = await applicationService.create(req.body);
return res.json(application);
Expand Down
1 change: 0 additions & 1 deletion src/email-templates/companyApplicationApproval.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,3 @@ export const REJECTION_NOTIFICATION = (companyName) => ({
template: "rejection_notification",
context: { companyName },
});

3 changes: 2 additions & 1 deletion src/email-templates/confirm-application.handlebars
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<h1>Confirm your NIJobs application</h1>
<p>Please follow this <a href="{{link}}" target="_blank">link</a> to finish the process. Note that the link will be expired in 5 minutes.</p>
<p>We have successfully received your application!</p>
<p>Please follow this <a href="{{link}}" target="_blank">link</a> to finish the process. Note that the link will expire in 10 minutes.</p>
<br>
<p>If you did not request this or need anything else, please contact us at <a href="mailto:[email protected]">[email protected]</a>!</p><br>
<p>Sincerely,</p>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
<h1>We have successfully received your application!</h1>
<p>We will now review your application, and in case you're approved, you will receive another email with further instructions in order to complete your registration.</p>
<h1>Your application has been validated!</h1>
<p>We will now review your application, and in case you're approved, you will receive another email with further instructions in order to complete your registration.</p>
<p>Your Application ID is {{applicationId}} and you registered {{companyName}}</p>
<p>Once you're approved, you will receive an email, and then you can log into NIJobs! Do not forget your password, you will need it on the first login.</p>
<br>
<p>If you did not request this or if you need anything else, don't hesitate to contact us at <a href="mailto:[email protected]">[email protected]</a>!</p>
<br>
<p>Sincerely,</p>
<p>NIJobs team at NIAEFEUP</p>
<p>NIJobs team at NIAEFEUP</p>
11 changes: 1 addition & 10 deletions src/lib/emailService.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export class EmailService {

async init({ email: user, password: pass }) {
this.email = user;
/* const transporter = await nodemailer.createTransport({
const transporter = await nodemailer.createTransport({
pool: true,
host: "smtp.gmail.com",
port: 465,
Expand All @@ -17,15 +17,6 @@ export class EmailService {
},
connectionTimeout: 30000
});
console.log("transporter");*/
const transporter = nodemailer.createTransport({
host: "smtp.ethereal.email",
port: 587,
auth: {
user: "[email protected]",
pass: "NGEVbMnTZzyA3MQD3V"
}
});

transporter.use("compile", hbs({
viewEngine: {
Expand Down
12 changes: 7 additions & 5 deletions src/models/CompanyApplication.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,11 @@ export const CompanyApplicationRules = Object.freeze({
msg: "company-application-already-reviewed",
},
APPLICATION_RECENTLY_CREATED: {
msg: "company-application-already-created-less-than-5-minutes-ago",
msg: "company-application-recently-created",
},
APPLICATION_ALREADY_VALIDATED: {
msg: "application-already-validated",
}
});

export const CompanyApplicationProps = {
Expand Down Expand Up @@ -127,9 +130,8 @@ function validateMutuallyExclusiveEvents(field) {
export const applicationUniqueness = async (email) => {
const existingApplications = await CompanyApplication.find({ email });
if (existingApplications.some((application) =>
(application.state === ApplicationStatus.PENDING ||
application.state === ApplicationStatus.APPROVED) &&
application.isVerified)
application.state === ApplicationStatus.PENDING ||
application.state === ApplicationStatus.APPROVED)
) {
throw new Error(CompanyApplicationRules.ONLY_ONE_APPLICATION_ACTIVE_PER_EMAIL.msg);
}
Expand Down Expand Up @@ -164,7 +166,7 @@ export const isRejectable = (application) => {


CompanyApplicationSchema.methods.companyValidation = function() {
if (this.isVerified) throw new APIError(HTTPStatus.FORBIDDEN, ErrorTypes.FORBIDDEN, ValidationReasons.ALREADY_VALIDATED);
if (this.isVerified) throw new Error(HTTPStatus.FORBIDDEN, ErrorTypes.FORBIDDEN, CompanyApplicationRules.APPLICATION_ALREADY_VALIDATED);
this.isVerified = true;
return this.save({ validateModifiedOnly: true });
};
Expand Down
2 changes: 1 addition & 1 deletion src/models/constants/ApplicationStatus.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ const ApplicationStatus = Object.freeze({

export default ApplicationStatus;

export const RECOVERY_LINK_EXPIRATION = 300;
export const VALIDATION_LINK_EXPIRATION = 600;
12 changes: 10 additions & 2 deletions src/services/application.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import CompanyApplication, { CompanyApplicationRules } from "../models/CompanyApplication.js";
import { generateToken } from "../lib/token.js";
import hash from "../lib/passwordHashing.js";
import { RECOVERY_LINK_EXPIRATION } from "../models/constants/ApplicationStatus.js";
import { VALIDATION_LINK_EXPIRATION } from "../models/constants/ApplicationStatus.js";
import { APPLICATION_CONFIRMATION } from "../email-templates/companyApplicationConfirmation.js";
import AccountService from "./account.js";
import EmailService from "../lib/emailService.js";
Expand Down Expand Up @@ -219,7 +219,7 @@ class CompanyApplicationService {
}

buildConfirmationLink(id) {
const token = generateToken({ _id: id }, config.jwt_secret, RECOVERY_LINK_EXPIRATION);
const token = generateToken({ _id: id }, config.jwt_secret, VALIDATION_LINK_EXPIRATION);
return `${config.application_confirmation_link}/${token}/confirm`;
}

Expand Down Expand Up @@ -247,6 +247,14 @@ class CompanyApplicationService {
...NEW_COMPANY_APPLICATION_COMPANY(application.companyName, application._id.toString())
});
}
async deleteApplications(email) {
try{
await CompanyApplication.deleteMany({ email: email, isVerified: false });
} catch (error){
throw error;
}
}
}


export default CompanyApplicationService;

0 comments on commit 441c78e

Please sign in to comment.