Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SES first iteration (no templates yet) #95

Merged
merged 5 commits into from
Jul 19, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
},
"dependencies": {
"@aws-sdk/client-s3": "^3.496.0",
"@aws-sdk/client-ses": "^3.609.0",
"@aws-sdk/s3-presigned-post": "^3.499.0",
"@aws-sdk/s3-request-presigner": "^3.496.0",
"@paralleldrive/cuid2": "^2.2.2",
Expand Down
9 changes: 9 additions & 0 deletions src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { StatusCodes } from "http-status-codes";
import { Config } from "./config";
import { rateLimiter } from "./middleware/rateLimiter";
import { isTest } from "./utilities";
import AWS from "aws-sdk";

import databaseMiddleware from "./middleware/database-middleware";
import customCors from "./middleware/cors-middleware";
Expand All @@ -16,10 +17,17 @@ import eventsRouter from "./services/events/events-router";
import notificationsRouter from "./services/notifications/notifications-router";
import registrationRouter from "./services/registration/registration-router";
import s3Router from "./services/s3/s3-router";
import sesRouter from "./services/ses/ses-router";
import statsRouter from "./services/stats/stats-router";
import sponsorRouter from "./services/sponsor/sponsor-router";
import subscriptionRouter from "./services/subscription/subscription-router";

AWS.config.update({
region: Config.S3_REGION,
accessKeyId: Config.S3_ACCESS_KEY,
secretAccessKey: Config.S3_SECRET_KEY,
});

const app = express();

// to prevent server-side caching/returning status code 200
Expand All @@ -42,6 +50,7 @@ app.use("/events", databaseMiddleware, eventsRouter);
app.use("/notifications", databaseMiddleware, notificationsRouter);
app.use("/registration", databaseMiddleware, registrationRouter);
app.use("/s3", databaseMiddleware, s3Router);
app.use("/ses", sesRouter);
app.use("/stats", databaseMiddleware, statsRouter);
app.use("/sponsor", databaseMiddleware, sponsorRouter);
app.use("/subscription", databaseMiddleware, subscriptionRouter);
Expand Down
8 changes: 8 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import dotenv from "dotenv";
import { z } from "zod";
import { getEnv } from "./utilities";

import AWS from "aws-sdk";

dotenv.config();

export const Environment = z.enum(["PRODUCTION", "DEVELOPMENT", "TESTING"]);
Expand Down Expand Up @@ -79,6 +81,8 @@ export const Config = {
// QR Scanning
QR_HASH_ITERATIONS: 10000,
QR_HASH_SECRET: getEnv("QR_HASH_SECRET"),

OUTGOING_EMAIL_ADDRESSES: "[email protected]",
aletya marked this conversation as resolved.
Show resolved Hide resolved
};

export const DeviceRedirects: Record<string, string> = {
Expand All @@ -87,4 +91,8 @@ export const DeviceRedirects: Record<string, string> = {
mobile: "exp://192.168.86.24:8081/--/Main",
};

export const ses = new AWS.SES({
region: Config.S3_REGION,
});

aletya marked this conversation as resolved.
Show resolved Hide resolved
export default Config;
16 changes: 16 additions & 0 deletions src/services/ses/ses-formats.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export interface sendEmailParams {
Destination: {
ToAddresses: string[];
};
Message: {
Body: {
Text: {
Data: string;
};
};
Subject: {
Data: string;
};
};
Source: string;
}
52 changes: 52 additions & 0 deletions src/services/ses/ses-router.ts
aletya marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { Request, Response, Router } from "express";
import RoleChecker from "../../middleware/role-checker";
import { StatusCodes } from "http-status-codes";
import { Role } from "../auth/auth-models";
import { sendEmail } from "./ses-utils";

const sesRouter: Router = Router();

//Takes in a list of emails and sends a test email to each of them
//Will change this to take in a template_id later
//The template_id and substitutions will all be done locally
sesRouter.post(
"/email",
RoleChecker([Role.enum.STAFF], true),
async (req: Request, res: Response) => {
if (!req.body.emailList) {
return res
.status(StatusCodes.BAD_REQUEST)
.json({ error: "Invalid Params" });
}

const emailList: string[] = req.body.emailList;
const emailPromises: Promise<void>[] = [];
for (let i = 0; i < emailList.length; i++) {
emailPromises.push(
sendEmail(emailList[i], "Hello from SES!", `Test Email ${i}`)
.then(() => {})
.catch((err) => {
console.error(
`Error sending email ${i + 1}:`,
err.message
);
})
);
}

Promise.all(emailPromises)
.then(() => {
return res
.status(StatusCodes.OK)
.send("All emails sent successfully");
})
.catch((err) => {
console.error("Error sending emails:", err.message);
return res
.status(StatusCodes.INTERNAL_SERVER_ERROR)
.send("Internal Server Error");
});
}
);

export default sesRouter;
10 changes: 10 additions & 0 deletions src/services/ses/ses-schema.ts
aletya marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { z } from "zod";

// Zod schema for email requests
const NotificationsValidator = z.object({
recipients: z.string(),
template: z.string(),
//should be able to work out subs from template
});

export { NotificationsValidator };
26 changes: 26 additions & 0 deletions src/services/ses/ses-utils.ts
aletya marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { ses, Config } from "../../config";

export function sendEmail(
emailId: string,
subject: string,
emailBody: string
): Promise<AWS.SES.SendEmailResponse> {
return ses
.sendEmail({
Destination: {
ToAddresses: [emailId],
},
Message: {
Body: {
Text: {
Data: emailBody,
},
},
Subject: {
Data: subject,
},
},
Source: Config.OUTGOING_EMAIL_ADDRESSES,
})
.promise();
}
Loading