Skip to content

Commit 709babb

Browse files
Changed emails templates and link from application validation expiration time
Co-authored-by: Daniel Ferreira <dsantosferreira@users.noreply.github.com>
1 parent 0bd0830 commit 709babb

File tree

13 files changed

+82
-83
lines changed

13 files changed

+82
-83
lines changed

.env

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ JWT_SECRET=Lendas contam que o Rui foi membro do IEEE.
2020
# Frontend Password Recovery Base Route
2121
PASSWORD_RECOVERY_LINK=https://localhost:3000/recover
2222

23-
# Froantend Application Confirmation Base Route
24-
APPLICATION_CONFIRMATION_LINK=https://localhost:8087/apply/company/validate/
23+
# Frontend Application Confirmation Base Route
24+
APPLICATION_CONFIRMATION_LINK=https://localhost:3000/apply/company/validate/
2525

2626
# Specifies the port in which the app will be exposed
2727
PORT=8087
@@ -36,17 +36,17 @@ TEST_LOG_REQUESTS=false
3636
ADMIN_EMAIL=ni@aefeup.pt
3737
ADMIN_PASSWORD=n1j0bs_ftw.12345
3838
#CORS allowed origin - OVERRIDE IN PRODUCTION
39-
ACCESS_CONTROL_ALLOW_ORIGIN=
39+
ACCESS_CONTROL_ALLOW_ORIGIN=https://localhost
4040

4141
# Mail service information. If you don't provide a MAIL_FROM, no emails will be sent. The app will execute no-ops and won't crash
4242
# However, if you want to send emails, you need to fill all of the following 2 fields
4343
# 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)
4444

4545
# The email address from which the emails are sent
46-
MAIL_FROM=jacky88@ethereal.email
46+
MAIL_FROM=
4747

4848
# Password for email above
49-
MAIL_FROM_PASSWORD=PgAnnVsAa6Sg8zJp6t
49+
MAIL_FROM_PASSWORD=
5050

5151
# Cloudinary API URL to save images
5252
CLOUDINARY_URL=

src/api/middleware/application.js

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,13 @@
11
import CompanyApplication, { CompanyApplicationRules } from "../../models/CompanyApplication.js";
22
import { APIError, ErrorTypes } from "./errorHandler.js";
33
import { StatusCodes as HTTPStatus } from "http-status-codes/build/cjs/status-codes.js";
4-
export const exceededCreationTimeLimit = async (email) => {
5-
const cursor = await CompanyApplication.findOne({ email, isVerified: false }).exec();
6-
if (cursor !== null && Date.now() - cursor.submittedAt < 5000 * 60) {
7-
throw new APIError(HTTPStatus.FORBIDDEN, ErrorTypes.FORBIDDEN, CompanyApplicationRules.APPLICATION_RECENTLY_CREATED);
8-
}
9-
return true;
10-
};
4+
import { VALIDATION_LINK_EXPIRATION } from "../../models/constants/ApplicationStatus.js";
5+
import { SECOND_IN_MS } from "../../models/constants/TimeConstants.js";
116

12-
export const deleteApplications = async (email) => {
13-
await CompanyApplication.deleteMany({ email: email, isVerified: false }).catch(function(error) {
14-
console.error(error);
15-
throw (error); // Failure
16-
});
7+
export const exceededCreationTimeLimit = async (req, res, next) => {
8+
const application = await CompanyApplication.findOne({ email: req.body.email, isVerified: false });
9+
if (application !== null && Date.now() < application.submittedAt.getTime() + (VALIDATION_LINK_EXPIRATION * SECOND_IN_MS)) {
10+
return next(new APIError(HTTPStatus.FORBIDDEN, ErrorTypes.FORBIDDEN, CompanyApplicationRules.APPLICATION_RECENTLY_CREATED.msg));
11+
}
12+
return next();
1713
};

src/api/middleware/auth.js

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -79,15 +79,19 @@ export const hasAdminPrivileges = async (req, res, next) => {
7979

8080
export const validToken = (req, res, next) => {
8181
try {
82-
const decoded = verifyAndDecodeToken(req.params.token, config.jwt_secret, next);
82+
const decoded = verifyAndDecodeToken(req.params.token, config.jwt_secret);
8383

8484
storeInLocals(req, {
8585
token: decoded,
8686
});
8787

8888
return next();
89-
} catch (err) {
90-
console.log(err);
91-
return next(err);
89+
} catch (jwtErr) {
90+
console.log(jwtErr);
91+
if (jwtErr.name === "TokenExpiredError") {
92+
return next(new APIError(HTTPStatus.FORBIDDEN, ErrorTypes.FORBIDDEN, ValidationReasons.EXPIRED_TOKEN));
93+
} else {
94+
return next(new APIError(HTTPStatus.FORBIDDEN, ErrorTypes.FORBIDDEN, ValidationReasons.INVALID_TOKEN));
95+
}
9296
}
9397
};

src/api/middleware/validators/validationReasons.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@ const ValidationReasons = Object.freeze({
4949
IMAGE_FORMAT: "formats-supported-png-jpeg-jpg",
5050
OFFER_BLOCKED_ADMIN: "offer-blocked-by-admin",
5151
OFFER_HIDDEN: "offer-is-hidden",
52-
ALREADY_VALIDATED: "application-already-validated",
5352
NON_EXISTING_APPLICATION: "application-does-not-exist",
5453
FILE_TOO_LARGE: (max) => `file-cant-be-larger-than-${max}MB`
5554
});

src/api/routes/application.js

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import { Router } from "express";
22
import * as validators from "../middleware/validators/application.js";
3-
import ApplicationService from "../../services/application.js";
3+
import ApplicationService, { CompanyApplicationAlreadyValidated } from "../../services/application.js";
44
import * as applicationMiddleware from "../middleware/application.js";
55
import { validToken } from "../middleware/auth.js";
66
import { StatusCodes as HTTPStatus } from "http-status-codes/build/cjs/status-codes.js";
7+
import { buildErrorResponse, ErrorTypes } from "../middleware/errorHandler.js";
78

89
const router = Router();
910

@@ -13,11 +14,10 @@ export default (app) => {
1314
/**
1415
* Creates a new Company Application
1516
*/
16-
router.post("/", validators.create, async (req, res, next) => {
17+
router.post("/", validators.create, applicationMiddleware.exceededCreationTimeLimit, async (req, res, next) => {
1718
try {
18-
await applicationMiddleware.exceededCreationTimeLimit(req.body.email);
19-
await applicationMiddleware.deleteApplications(req.body.email);
2019
const applicationService = new ApplicationService();
20+
await applicationService.deleteApplications(req.body.email);
2121
// This is safe since the service is destructuring the passed object and the fields have been validated
2222
const application = await applicationService.create(req.body);
2323
return res.json(application);
@@ -32,6 +32,11 @@ export default (app) => {
3232
await new ApplicationService().applicationValidation(id);
3333
return res.status(HTTPStatus.OK).json({});
3434
} catch (err) {
35+
if (err instanceof CompanyApplicationAlreadyValidated) {
36+
return res
37+
.status(HTTPStatus.CONFLICT)
38+
.json(buildErrorResponse(ErrorTypes.FORBIDDEN, [{ msg: err.message }]));
39+
}
3540
console.error(err);
3641
return next(err);
3742
}

src/email-templates/companyApplicationApproval.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,3 @@ export const REJECTION_NOTIFICATION = (companyName) => ({
2323
template: "rejection_notification",
2424
context: { companyName },
2525
});
26-

src/email-templates/confirm-application.handlebars

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<h1>Confirm your NIJobs application</h1>
2-
<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>
2+
<p>We have successfully received your application!</p>
3+
<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>
34
<br>
45
<p>If you did not request this or need anything else, please contact us at <a href="mailto:nijobs@aefeup.pt">nijobs@aefeup.pt</a>!</p><br>
56
<p>Sincerely,</p>
Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
<h1>We have successfully received your application!</h1>
2-
<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>
1+
<h1>Your application has been validated!</h1>
2+
<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>
33
<p>Your Application ID is {{applicationId}} and you registered {{companyName}}</p>
4+
<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>
45
<br>
56
<p>If you did not request this or if you need anything else, don't hesitate to contact us at <a href="mailto:nijobs@aefeup.pt">nijobs@aefeup.pt</a>!</p>
67
<br>
78
<p>Sincerely,</p>
8-
<p>NIJobs team at NIAEFEUP</p>
9+
<p>NIJobs team at NIAEFEUP</p>

src/lib/emailService.js

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,13 @@ export class EmailService {
66

77
async init({ email: user, password: pass }) {
88
this.email = user;
9-
/* const transporter = await nodemailer.createTransport({
10-
pool: true,
11-
host: "smtp.gmail.com",
12-
port: 465,
13-
secure: true,
14-
auth: {
15-
user,
16-
pass
17-
},
18-
connectionTimeout: 30000
19-
});
20-
console.log("transporter");*/
219
const transporter = nodemailer.createTransport({
22-
host: "smtp.ethereal.email",
10+
host: 'smtp.ethereal.email',
2311
port: 587,
12+
secure: false, // true for 465, false for other ports
2413
auth: {
25-
user: "naomie.koelpin2@ethereal.email",
26-
pass: "NGEVbMnTZzyA3MQD3V"
14+
user: "magdalen.labadie@ethereal.email", // generated ethereal user
15+
pass: "GjdJdafFyj5svQBzJJ"// generated ethereal password
2716
}
2817
});
2918

src/lib/token.js

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,9 @@
11
import jwt from "jsonwebtoken";
2-
import { APIError, ErrorTypes } from "../api/middleware/errorHandler.js";
3-
import { StatusCodes as HTTPStatus } from "http-status-codes/build/cjs/status-codes.js";
4-
import ValidationReasons from "../api/middleware/validators/validationReasons.js";
52

63
export const generateToken = (data, secret, expiresInSeconds) => jwt.sign(
74
{ ...data },
85
secret,
96
{ expiresIn: `${expiresInSeconds} seconds`, algorithm: "HS256" }
107
);
118

12-
export const verifyAndDecodeToken = (token, secret, next) => {
13-
try {
14-
return jwt.verify(token, secret, { algorithm: "HS256" });
15-
} catch (jwtErr) {
16-
if (jwtErr.name === "TokenExpiredError") {
17-
return next(new APIError(HTTPStatus.FORBIDDEN, ErrorTypes.FORBIDDEN, ValidationReasons.EXPIRED_TOKEN));
18-
} else {
19-
return next(new APIError(HTTPStatus.FORBIDDEN, ErrorTypes.FORBIDDEN, ValidationReasons.INVALID_TOKEN));
20-
}
21-
}
22-
};
9+
export const verifyAndDecodeToken = (token, secret) => jwt.verify(token, secret, { algorithm: "HS256" });

0 commit comments

Comments
 (0)