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

Commit 759e14f

Browse files
✨ Send emails
1 parent b4086e7 commit 759e14f

File tree

12 files changed

+159
-8
lines changed

12 files changed

+159
-8
lines changed

package.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616
"@types/bcrypt": "^3.0.0",
1717
"@types/dotenv": "^6.1.1",
1818
"@types/express": "^4.16.1",
19+
"@types/fs-extra": "^5.0.5",
1920
"@types/jest": "^24.0.11",
21+
"@types/marked": "^0.6.5",
2022
"@types/mysql": "^2.15.5",
2123
"@types/node": "^11.13.6",
2224
"concurrently": "^4.1.0",
@@ -31,8 +33,13 @@
3133
"typescript": "^3.4.4"
3234
},
3335
"dependencies": {
36+
"@types/mustache": "^0.8.32",
3437
"bcrypt": "^3.0.6",
38+
"fs-extra": "^7.0.1",
39+
"marked": "^0.6.2",
40+
"mustache": "^3.0.1",
3541
"mysql": "^2.17.1",
42+
"node-ses": "^2.2.0",
3643
"reflect-metadata": "^0.1.13"
3744
}
3845
}

src/config.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { config } from "dotenv";
22
config();
33

44
// Server
5-
export const PORT = process.env.PORT || 7008;
5+
export const PORT = process.env.PORT || 7007;
66

77
// Database
88
export const DB_HOST = process.env.DB_HOST || "localhost";
@@ -12,3 +12,8 @@ export const DB_PORT = process.env.DB_PORT
1212
export const DB_USERNAME = process.env.DB_USERNAME || "root";
1313
export const DB_PASSWORD = process.env.DB_PASSWORD || "";
1414
export const DB_DATABASE = process.env.DB_DATABASE || "database";
15+
16+
// Email
17+
export const SES_EMAIL = process.env.SES_EMAIL || "";
18+
export const SES_ACCESS = process.env.SES_ACCESS || "";
19+
export const SES_SECRET = process.env.SES_SECRET || "";

src/crud/email.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { query, tableValues } from "../helpers/mysql";
22
import { Email } from "../interfaces/tables/emails";
33

4-
export const createEmail = async (email: Email) => {
4+
export const createEmail = async (email: Email, sendVerification = true) => {
55
// Clean up values
66
email.email = email.email.toLowerCase();
77
email.isVerified = false;

src/crud/user.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ interface KV {
3131
[index: string]: any;
3232
}
3333
export const updateUser = async (id: number, user: KV) => {
34-
user.updatedAt = user.createdAt;
34+
user.updatedAt = new Date();
3535
// Create user
3636
return await query(`UPDATE users SET ${setValues(user)} WHERE id = ?`, [
3737
...Object.values(user),

src/helpers/mail.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import * as ses from "node-ses";
2+
import { Mail } from "../interfaces/mail";
3+
import { SES_SECRET, SES_ACCESS, SES_EMAIL } from "../config";
4+
import { readFile } from "fs-extra";
5+
import { join } from "path";
6+
import { render } from "mustache";
7+
import marked from "marked";
8+
import i18n from "../i18n";
9+
10+
const client = ses.createClient({ key: SES_ACCESS, secret: SES_SECRET });
11+
12+
export const sendMail = (mail: Mail) =>
13+
new Promise((resolve, reject) => {
14+
client.sendEmail(mail, (error: Error, data: any, response: any) => {
15+
if (error) return reject(error);
16+
resolve(response);
17+
});
18+
});
19+
20+
export const mail = async (
21+
to: number | string,
22+
template: string,
23+
data: any = {}
24+
) => {
25+
const altText = render(
26+
(await readFile(
27+
join(__dirname, "..", "..", "src", "templates", `${template}.md`)
28+
)).toString(),
29+
data
30+
);
31+
const message = marked(altText);
32+
return await sendMail({
33+
from: SES_EMAIL,
34+
to: to.toString(),
35+
subject: i18n.en.emails["verify-email"],
36+
message,
37+
altText
38+
});
39+
};

src/i18n.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export default {
2+
en: {
3+
emails: {
4+
"verify-email": "Verify your new email"
5+
}
6+
}
7+
};

src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ app.get("/create-account", async (req, res) => {
99
try {
1010
const users = await register(
1111
{ name: "Anand Chowdhary" },
12-
"anandchowdhary@gmail.com"
12+
"anand@oswaldlabs.com"
1313
);
1414
res.json({ success: true, users });
1515
} catch (error) {

src/interfaces/mail.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
export interface Mail {
2+
from?: string;
3+
to: string | string[];
4+
cc?: string | string[];
5+
bcc?: string | string[];
6+
subject: string;
7+
message: string;
8+
altText?: string;
9+
replyTo?: string;
10+
}

src/rest/auth.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { User } from "../interfaces/tables/user";
22
import { createUser, updateUser } from "../crud/user";
33
import { InsertResult } from "../interfaces/mysql";
44
import { createEmail } from "../crud/email";
5+
import { mail } from "../helpers/mail";
56

67
export const register = async (
78
user: User,
@@ -20,5 +21,7 @@ export const register = async (
2021
});
2122
const emailId = newEmail.insertId;
2223
await updateUser(userId, { primaryEmail: emailId });
24+
await mail(email, "verify-email", { name: user.name, email });
2325
}
26+
return { created: true };
2427
};

src/templates/verify-email.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Verify your email
2+
3+
Dear {{name}},
4+
5+
Click here to verify your email address: https://example.com?email={{email}}.
6+
7+
If you didn't request this email, you can safely ignore it.
8+

0 commit comments

Comments
 (0)