Skip to content

Commit

Permalink
Draft SES code
Browse files Browse the repository at this point in the history
  • Loading branch information
MikkoKauhanen committed Jan 16, 2025
1 parent 6fec176 commit 454bee8
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 20 deletions.
16 changes: 15 additions & 1 deletion aoe-infra/bin/infra.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import { DocumentdbStack } from '../lib/documentdb-stack'
import { MskStack } from '../lib/msk-stack'
import { GithubActionsStack } from '../lib/githubActionsStack'
import { UtilityStack } from '../lib/utility-stack'
import { SesStack } from "../lib/ses-stack";

const app = new cdk.App()

Expand Down Expand Up @@ -96,6 +97,11 @@ if (environmentName === 'dev' || environmentName === 'qa' || environmentName ===
vpc: Network.vpc
})

const SESStack = new SesStack(app, 'SesStack', {
env: { region: 'eu-west-1' },
hostedZone: HostedZones.publicHostedZone
});

const SecurityGroups = new SecurityGroupStack(app, 'SecurityGroupStack', {
env: { region: 'eu-west-1' },
stackName: `${environmentName}-security-groups`,
Expand Down Expand Up @@ -422,6 +428,13 @@ if (environmentName === 'dev' || environmentName === 'qa' || environmentName ===
resources: [efs.fileSystem.fileSystemArn]
})

const sesIamPolicy = new iam.PolicyStatement({
actions: ['ses:SendEmail'],
resources: [
'*'
]
});

new EcsServiceStack(app, 'WebBackendEcsService', {
env: { region: 'eu-west-1' },
stackName: `${environmentName}-web-backend-service`,
Expand Down Expand Up @@ -472,7 +485,8 @@ if (environmentName === 'dev' || environmentName === 'qa' || environmentName ===
s3PolicyStatement,
efsPolicyStatement,
kafkaClusterIamPolicy,
kafkaTopicIamPolicy
kafkaTopicIamPolicy,
sesIamPolicy
],
privateDnsNamespace: namespace.privateDnsNamespace,
efs: {
Expand Down
11 changes: 8 additions & 3 deletions aoe-infra/environments/dev.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@
"memory_limit": "1024",
"min_count": 1,
"max_count": 1,
"image_tag": "ga-281",
"image_tag": "ga-285",
"allow_ecs_exec": true,
"env_vars": {
"NODE_ENV": "production",
Expand Down Expand Up @@ -151,11 +151,16 @@
"REDIS_USERNAME": "app",
"REDIS_USE_TLS": "true",
"BASE_URL": "https://dev.aoe.fi/api/v1/",
"EMAIL_FROM": "oppimateriaalivaranto@aoe.fi",
"EMAIL_FROM": "admin@dev.aoe.fi",
"TRANSPORT_AUTH_USER": "[email protected]",
"TRANSPORT_AUTH_HOST": "XXXX",
"TRANSPORT_PORT": "25",
"SEND_EMAIL": "0",

"SEND_SYSTEM_NOTIFICATION_EMAIL" : "0",
"SEND_EXPIRATION_NOTIFICATION_EMAIL" : "0",
"SEND_RATING_NOTIFICATION:EMAIL": "0",
"SEND_VERIFICATION_EMAIL": "1",

"VERIFY_EMAIL_REDIRECT_URL": "/",

"CLOUD_STORAGE_ENABLED": "1",
Expand Down
26 changes: 26 additions & 0 deletions aoe-infra/lib/ses-stack.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as ses from 'aws-cdk-lib/aws-ses'
import { HostedZone } from 'aws-cdk-lib/aws-route53';
import * as cdk from 'aws-cdk-lib';
import { EmailIdentity } from "aws-cdk-lib/aws-ses";

interface sesProps extends StackProps {
hostedZone: HostedZone
}

export class SesStack extends Stack {
public emailIdentity: EmailIdentity;

constructor(scope: Construct, id: string, props: sesProps) {
super(scope, id, props);
this.emailIdentity = new ses.EmailIdentity(this, 'EmailIdentity', {
identity: ses.Identity.publicHostedZone(props.hostedZone)
})

new cdk.CfnOutput(this, 'EmailIdentityArn', {
value: this.emailIdentity.emailIdentityArn,
description: 'The ARN of the SES Email Identity',
});
}
}
69 changes: 53 additions & 16 deletions aoe-web-backend/src/services/mailService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,45 @@ import { sign, verify } from 'jsonwebtoken';
import Mail from 'nodemailer/lib/mailer';
import { createTransport, Transporter } from 'nodemailer';
import winstonLogger from '@util/winstonLogger';

import { db } from '@resource/postgresClient';
import AWS from 'aws-sdk';

AWS.config.update({ region: process.env.AWS_REGION || 'eu-west-1' });
const ses = new AWS.SES();

const sendEmail = async (subject: string, from: string, to: string, html: string) => {
const params = {
Destination: {
ToAddresses: [to],
},
Message: {
Body: {
/* required */
Html: {
Charset: 'UTF-8',
Data: html,
},
// Text: {
// Charset: 'UTF-8',
// Data: 'Test email content',
// },
},
Subject: {
Charset: 'UTF-8',
Data: subject,
},
},
Source: from,
};

try {
const res = await ses.sendEmail(params).promise();
winstonLogger.info(`Email sent successfully ${res.MessageId}`);
return res;
} catch (err) {
winstonLogger.error('Error sending email:', err);
}
};

/**
* Initialize Nodemailer Transporter
Expand Down Expand Up @@ -36,7 +73,7 @@ export const sendSystemNotification = async (content: string): Promise<void> =>
};
try {
// If environment variable SEND_MAIL is true (1), not false (0).
if (parseInt(process.env.SEND_EMAIL, 10)) {
if (parseInt(process.env.SEND_SYSTEM_NOTIFICATION_EMAIL, 10)) {
const info: Record<string, unknown> = await transporter.sendMail(mailOptions);
winstonLogger.debug('System email notification delivery completed: ' + info);
} else {
Expand All @@ -58,8 +95,8 @@ export async function sendExpirationMail() {
const materials = await getExpiredMaterials();
const emailArray = materials.filter((m) => m.email != undefined).map((m) => m.email);
mailOptions.to = emailArray;
if (!(process.env.SEND_EMAIL === '1')) {
winstonLogger.debug('Email sending disabled');
if (!(process.env.SEND_EXPIRATION_NOTIFICATION_EMAIL === '1')) {
winstonLogger.debug('Material expiration email sending disabled');
} else {
for (const element of emailArray) {
mailOptions.to = element;
Expand All @@ -84,8 +121,8 @@ export async function sendRatingNotificationMail() {
holder[d.email] = d.materialname;
}
});
if (!(process.env.SEND_EMAIL === '1')) {
winstonLogger.debug('Email sending disabled');
if (!(process.env.SEND_RATING_NOTIFICATION === '1')) {
winstonLogger.debug('Rating notification email sending disabled');
} else {
for (const element of emailArray) {
const mailOptions = {
Expand Down Expand Up @@ -141,16 +178,16 @@ export async function sendVerificationEmail(user: string, email: string) {
const token_mail_verification = sign(mail, jwtSecret, { expiresIn: '1d' });

const url = process.env.BASE_URL + 'verify?id=' + token_mail_verification;
winstonLogger.debug(await verificationEmailText(url));
const mailOptions = {
from: process.env.EMAIL_FROM,
to: email,
subject: 'Sähköpostin vahvistus - Avointen oppimateriaalien kirjasto (aoe.fi)',
html: await verificationEmailText(url),
};
if (process.env.SEND_EMAIL === '1') {
const info = await transporter.sendMail(mailOptions);
winstonLogger.debug('Message sent: %s', info.messageId);
const content = await verificationEmailText(url);
winstonLogger.debug(content);

if (process.env.SEND_VERIFICATION_EMAIL === '1') {
await sendEmail(
'Sähköpostin vahvistus - Avointen oppimateriaalien kirjasto (aoe.fi)',
process.env.EMAIL_FROM,
email,
content,
);
}
return url;
}
Expand Down

0 comments on commit 454bee8

Please sign in to comment.