Skip to content
This repository has been archived by the owner on Apr 19, 2023. It is now read-only.

Commit

Permalink
✨ Resend email verification link
Browse files Browse the repository at this point in the history
  • Loading branch information
AnandChowdhary committed Apr 28, 2019
1 parent b51c0bf commit cf70881
Show file tree
Hide file tree
Showing 9 changed files with 82 additions and 12 deletions.
18 changes: 16 additions & 2 deletions src/crud/email.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { query, tableValues, setValues, removeReadOnlyValues } from "../helpers/mysql";
import {
query,
tableValues,
setValues,
removeReadOnlyValues
} from "../helpers/mysql";
import { Email } from "../interfaces/tables/emails";
import { dateToDateTime } from "../helpers/utils";
import { KeyValue } from "../interfaces/general";
Expand Down Expand Up @@ -41,6 +46,15 @@ export const sendEmailVerification = async (
return;
};

export const resendEmailVerification = async (id: number) => {
const token = await emailVerificationToken(id);
const emailObject = await getEmail(id);
const email = emailObject.email;
const user = await getUser(emailObject.userId);
await mail(email, Templates.EMAIL_VERIFY, { name: user.name, email, token });
return;
};

export const updateEmail = async (id: number, email: KeyValue) => {
email.updatedAt = dateToDateTime(new Date());
email = removeReadOnlyValues(email);
Expand Down Expand Up @@ -87,7 +101,7 @@ export const getEmailObject = async (email: string) => {
};

export const getUserVerifiedEmails = async (userId: number) => {
return <Email>(
return <Email[]>(
await query("SELECT * FROM emails WHERE userId = ? AND isVerified = 1", [
userId
])
Expand Down
7 changes: 6 additions & 1 deletion src/crud/membership.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { query, tableValues, setValues, removeReadOnlyValues } from "../helpers/mysql";
import {
query,
tableValues,
setValues,
removeReadOnlyValues
} from "../helpers/mysql";
import { Membership } from "../interfaces/tables/memberships";
import { dateToDateTime } from "../helpers/utils";
import { KeyValue } from "../interfaces/general";
Expand Down
7 changes: 6 additions & 1 deletion src/crud/organization.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { query, tableValues, setValues, removeReadOnlyValues } from "../helpers/mysql";
import {
query,
tableValues,
setValues,
removeReadOnlyValues
} from "../helpers/mysql";
import { Organization } from "../interfaces/tables/organization";
import { capitalizeFirstAndLastLetter, dateToDateTime } from "../helpers/utils";
import { KeyValue } from "../interfaces/general";
Expand Down
7 changes: 6 additions & 1 deletion src/crud/user.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { query, tableValues, setValues, removeReadOnlyValues } from "../helpers/mysql";
import {
query,
tableValues,
setValues,
removeReadOnlyValues
} from "../helpers/mysql";
import { User } from "../interfaces/tables/user";
import {
capitalizeFirstAndLastLetter,
Expand Down
2 changes: 1 addition & 1 deletion src/helpers/mysql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,4 +97,4 @@ export const removeReadOnlyValues = (object: KeyValue) => {
if (object[value]) delete object[value];
});
return object;
}
};
3 changes: 2 additions & 1 deletion src/interfaces/enum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ export enum EventType {
EMAIL_CREATED = "email.created",
EMAIL_UPDATED = "email.updated",
EMAIL_DELETED = "email.deleted",
EMAIL_VERIFIED = "email.verified"
EMAIL_VERIFIED = "email.verified",
EMAIL_CANNOT_DELETE = "email.cannotDelete"
}

export enum ErrorCode {
Expand Down
23 changes: 21 additions & 2 deletions src/rest/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,19 @@ import {
UserRole,
EventType
} from "../interfaces/enum";
import { getUser } from "../crud/user";
import { getUser, updateUser } from "../crud/user";
import {
getUserOrganizationId,
getUserMembershipObject
} from "../crud/membership";
import { createEmail, deleteEmail, getEmail } from "../crud/email";
import {
createEmail,
deleteEmail,
getEmail,
getUserVerifiedEmails,
getUserPrimaryEmail,
getUserPrimaryEmailObject
} from "../crud/email";
import { Locals } from "../interfaces/general";
import { createEvent } from "../crud/event";

Expand Down Expand Up @@ -55,6 +62,18 @@ export const deleteEmailFromUser = async (
const email = await getEmail(emailId);
if (email.userId != userId)
throw new Error(ErrorCode.INSUFFICIENT_PERMISSION);
const verifiedEmails = await getUserVerifiedEmails(userId);
if (verifiedEmails.length > 1) {
const currentPrimaryEmailId = (await getUserPrimaryEmailObject(userId)).id;
if (currentPrimaryEmailId == emailId) {
const nextVerifiedEmail = verifiedEmails.filter(
emailObject => emailObject.id != emailId
)[0];
await updateUser(userId, { primaryEmail: nextVerifiedEmail });
}
} else {
throw new Error(EventType.EMAIL_CANNOT_DELETE);
}
await deleteEmail(emailId);
await createEvent(
{ userId, type: EventType.EMAIL_DELETED, data: { email: email.email } },
Expand Down
8 changes: 8 additions & 0 deletions src/routes/emails.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Request, Response } from "express";
import { verifyEmail } from "../rest/auth";
import { ErrorCode } from "../interfaces/enum";
import { addEmailToUser, deleteEmailFromUser } from "../rest/user";
import { resendEmailVerification } from "../crud/email";

export const routeEmailAdd = async (req: Request, res: Response) => {
const email = req.body.email;
Expand All @@ -21,3 +22,10 @@ export const routeEmailVerify = async (req: Request, res: Response) => {
await verifyEmail(req.body.token || req.params.token, res.locals);
res.json({ success: true });
};

export const routeEmailVerifyResend = async (req: Request, res: Response) => {
const emailId = req.params.id || req.body.id;
if (!emailId) throw new Error(ErrorCode.MISSING_FIELD);
await resendEmailVerification(emailId);
res.json({ success: true });
};
19 changes: 16 additions & 3 deletions src/routes/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
import { Application } from "express";
import asyncHandler from "express-async-handler";
import { routeUserPut, routeUserId } from "./users";
import { routeEmailVerify, routeEmailAdd, routeEmailDelete } from "./emails";
import { routeOrganizationCreate, routeOrganizationUpdate } from "./organizations";
import {
routeEmailVerify,
routeEmailAdd,
routeEmailDelete,
routeEmailVerifyResend
} from "./emails";
import {
routeOrganizationCreate,
routeOrganizationUpdate
} from "./organizations";
import { authHandler } from "../helpers/middleware";
import {
routeAuthVerifyToken,
Expand Down Expand Up @@ -43,10 +51,15 @@ const routesUser = (app: Application) => {
const routesEmail = (app: Application) => {
app.put("/emails", authHandler, asyncHandler(routeEmailAdd));
app.delete("/emails/:id", authHandler, asyncHandler(routeEmailDelete));
app.post("/emails/:id/resend", asyncHandler(routeEmailVerifyResend));
app.post("/emails/verify", asyncHandler(routeEmailVerify));
};

const routesOrganization = (app: Application) => {
app.put("/organizations", authHandler, asyncHandler(routeOrganizationCreate));
app.patch("/organizations/:id", authHandler, asyncHandler(routeOrganizationUpdate));
app.patch(
"/organizations/:id",
authHandler,
asyncHandler(routeOrganizationUpdate)
);
};

0 comments on commit cf70881

Please sign in to comment.