Skip to content

Commit

Permalink
feat: add AWS SES support for nodemailer (#613)
Browse files Browse the repository at this point in the history
  • Loading branch information
gogarufi authored Oct 1, 2024
1 parent c1517d4 commit 9585bf6
Show file tree
Hide file tree
Showing 11 changed files with 1,155 additions and 58 deletions.
15 changes: 8 additions & 7 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,14 @@ DATABASE_SCHEMA=public
DATABASE_SSL_SELF=false
PUBLIC_FRONTEND_URL="http://localhost:5173" # Used for emails linking to the frontend

# SMTP Settings
SMTP_HOST="smtp.example.com"
SMTP_PORT=587
SMTP_USERNAME=""
SMTP_PASSWORD=""
MAIL_FROM="[email protected]"
MAIL_REPLY_TO="[email protected]"
# AWS SES Settings
AWS_ACCESS_KEY_ID="EXAMPLE_KEY"
AWS_SECRET_ACCESS_KEY="EXAMPLE_SECRET"
AWS_REGION="eu-north-1"
# `MAIL_FROM` and `MAIL_REPLY_TO` variables will not take effect for emails sent by `user-permissions` Strapi plugin
# (f.e. reset password emails) as they are configured separately via Strapi UI in `Settings > Email Templates`.
MAIL_FROM="OpenVAA Voting Advice Application <[email protected]>"
MAIL_REPLY_TO="OpenVAA Admin <[email protected]>"

# Maildev settings (used only in dev)
MAILDEV_PORT=1080
Expand Down
9 changes: 9 additions & 0 deletions backend/vaa-strapi/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,12 @@ DATABASE_SSL_SELF=false
PUBLIC_FRONTEND_URL="http://localhost:5173" # Used for emails linking to the frontend
GENERATE_MOCK_DATA_ON_INITIALISE=true # Set to true to enable mock data on an empty database
GENERATE_MOCK_DATA_ON_RESTART=false # Used only in development builds

AWS_ACCESS_KEY_ID="EXAMPLE_KEY"
AWS_SECRET_ACCESS_KEY="EXAMPLE_SECRET"
AWS_REGION="eu-north-1"
# `MAIL_FROM` and `MAIL_REPLY_TO` variables will not take effect for emails sent by `user-permissions` Strapi plugin
# (f.e. reset password emails) as they are configured separately via Strapi UI in `Settings > Email Templates`.
MAIL_FROM="OpenVAA Voting Advice Application <[email protected]>"
MAIL_REPLY_TO="OpenVAA Admin <[email protected]>"

92 changes: 55 additions & 37 deletions backend/vaa-strapi/config/plugins.ts
Original file line number Diff line number Diff line change
@@ -1,41 +1,59 @@
export default ({env}) => ({
'users-permissions': {
config: {
jwt: {
expiresIn: '4h'
},
register: {
allowedFields: ['candidate']
const aws = require('@aws-sdk/client-ses');

export default ({env}) => {
/**
* Strapi initilises nodemailer transporter only once using main plugin config.
* It allows to override only transporter specific (SMPT or SES) settings using [env]/plugins.ts, not the transporter type itself.
* So we have to make sure that for development we initilise the transporter as SMTP to be able to use maildev.
*/
const emailProviderOptions =
env('NODE_ENV') === 'development'
? undefined
: {
SES: {
ses: new aws.SES({
apiVersion: '2010-12-01',
region: env('AWS_REGION'),
credentials: {
accessKeyId: env('AWS_ACCESS_KEY_ID'),
secretAccessKey: env('AWS_SECRET_ACCESS_KEY')
}
}),
aws
},
// max 14 messages per second to comply with AWS SES
sendingRate: 14
};
return {
'users-permissions': {
config: {
jwt: {
expiresIn: '4h'
},
register: {
allowedFields: ['candidate']
}
}
}
},
email: {
config: {
provider: 'nodemailer',
providerOptions: {
host: env('SMTP_HOST', 'smtp.example.com'),
port: env('SMTP_PORT', 587),
auth: {
user: env('SMTP_USERNAME'),
pass: env('SMTP_PASSWORD')
},
email: {
config: {
provider: 'nodemailer',
providerOptions: {
...emailProviderOptions
},
settings: {
defaultFrom: env('MAIL_FROM'),
defaultReplyTo: env('MAIL_REPLY_TO')
}
},
settings: {
defaultFrom: env('MAIL_FROM', '[email protected]'),
defaultReplyTo: env('MAIL_REPLY_TO', '[email protected]')
},
ratelimit: env.NODE_ENV === 'development' && {
max: 10000,
interval: 60000
}
},
'import-export-entries': {
enabled: true,
resolve: './strapi-plugin-import-export-entries'
},
'candidate-admin': {
enabled: true,
resolve: './src/plugins/candidate-admin'
}
},
'import-export-entries': {
enabled: true,
resolve: './strapi-plugin-import-export-entries'
},
'candidate-admin': {
enabled: true,
resolve: './src/plugins/candidate-admin'
}
});
};
};
5 changes: 5 additions & 0 deletions backend/vaa-strapi/docker-compose.dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ services:
GENERATE_MOCK_DATA_ON_INITIALISE: ${GENERATE_MOCK_DATA_ON_INITIALISE}
GENERATE_MOCK_DATA_ON_RESTART: ${GENERATE_MOCK_DATA_ON_RESTART}
LOAD_DATA_ON_INITIALISE_FOLDER: ${LOAD_DATA_ON_INITIALISE_FOLDER}
AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID}
AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY}
AWS_REGION: ${AWS_REGION}
MAIL_FROM: ${MAIL_FROM}
MAIL_REPLY_TO: ${MAIL_REPLY_TO}
volumes:
# Creating this volume will make Docker update changes from local automatically
# Note that this only applies to src/, where all the source code is anyway.
Expand Down
5 changes: 5 additions & 0 deletions backend/vaa-strapi/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ services:
DATABASE_SSL_SELF: ${DATABASE_SSL_SELF}
GENERATE_MOCK_DATA_ON_INITIALISE: ${GENERATE_MOCK_DATA_ON_INITIALISE}
LOAD_DATA_ON_INITIALISE_FOLDER: ${LOAD_DATA_ON_INITIALISE_FOLDER}
AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID}
AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY}
AWS_REGION: ${AWS_REGION}
MAIL_FROM: ${MAIL_FROM}
MAIL_REPLY_TO: ${MAIL_REPLY_TO}
healthcheck:
test: wget --no-verbose --tries=1 --spider ${STRAPI_HOST}:${STRAPI_PORT} || exit 1
interval: 30s
Expand Down
1 change: 1 addition & 0 deletions backend/vaa-strapi/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
]
},
"dependencies": {
"@aws-sdk/client-ses": "^3.654.0",
"@strapi/plugin-documentation": "^4.25.1",
"@strapi/plugin-i18n": "^4.25.1",
"@strapi/plugin-users-permissions": "^4.25.1",
Expand Down
5 changes: 5 additions & 0 deletions docker-compose.dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ services:
GENERATE_MOCK_DATA_ON_INITIALISE: ${GENERATE_MOCK_DATA_ON_INITIALISE}
GENERATE_MOCK_DATA_ON_RESTART: ${GENERATE_MOCK_DATA_ON_RESTART}
LOAD_DATA_ON_INITIALISE_FOLDER: ${LOAD_DATA_ON_INITIALISE_FOLDER}
AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID}
AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY}
AWS_REGION: ${AWS_REGION}
MAIL_FROM: ${MAIL_FROM}
MAIL_REPLY_TO: ${MAIL_REPLY_TO}
postgres:
extends:
file: ./backend/vaa-strapi/docker-compose.dev.yml
Expand Down
5 changes: 5 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ services:
DATABASE_SSL_SELF: ${DATABASE_SSL_SELF}
GENERATE_MOCK_DATA_ON_INITIALISE: ${GENERATE_MOCK_DATA_ON_INITIALISE}
LOAD_DATA_ON_INITIALISE_FOLDER: ${LOAD_DATA_ON_INITIALISE_FOLDER}
AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID}
AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY}
AWS_REGION: ${AWS_REGION}
MAIL_FROM: ${MAIL_FROM}
MAIL_REPLY_TO: ${MAIL_REPLY_TO}
postgres:
extends:
file: ./backend/vaa-strapi/docker-compose.yml
Expand Down
11 changes: 6 additions & 5 deletions docs/candidate-app/candidates.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,15 @@ You can create a new user using the "Create new entry" button. The following fie
Then, use the "Save" button and you should be able to log in as the candidate at http://localhost:5173/candidate.

## Resetting Password
The user gets an email with a link to reset their password using the forgot password functionality on the login page. The frontend URL in the emails is configured in `.env` with the PUBLIC_FRONTEND_URL variable, and the SMTP can be configured using the following variables:
- `SMTP_HOST`: the hostname the SMTP server runs on
- `SMTP_PORT`: the port of the SMTP server
- `SMTP_USERNAME`: the username used for authentication
- `SMTP_PASSWORD`: the password used for authentication
The user gets an email with a link to reset their password using the forgot password functionality on the login page. The frontend URL in the emails is configured in `.env` with the PUBLIC_FRONTEND_URL variable, and the email service (AWS SES) can be configured using the following variables:
- `AWS_ACCESS_KEY_ID`: AWS SES user access key
- `AWS_SECRET_ACCESS_KEY`: AWS SES user secret access key
- `AWS_REGION`: AWS SES region
- `MAIL_FROM`: the email address the emails are sent from
- `MAIL_REPLY_TO`: the email address replies should be sent to

`MAIL_FROM` and `MAIL_REPLY_TO` variables will not take effect for emails sent by `user-permissions` Strapi plugin (f.e. reset password emails) as they are configured separately via Strapi UI in `Settings > Email Templates`.

In a development environment, there is a local instance of [maildev](https://github.com/maildev/maildev) running so that you do not need to set up SMTP. The development configuration automatically enforces the use of maildev through the `backend/vaa-strapi/config/env/development/plugins.ts` file. The user interface of maildev can be found at [http://localhost:1080](http://localhost:1080), where you'll find any emails sent by Strapi.

## Technical Documentation
Expand Down
2 changes: 1 addition & 1 deletion docs/troubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,4 @@ If Strapi gives an error dealing with creating a table with the message that a r

## Candidate registration fails

The `email` property is required for a `Candidate`. If it is not set, regisration will result in a 'Bad Request' error.
The `email` property is required for a `Candidate`. If it is not set, registration will result in a 'Bad Request' error.
Loading

0 comments on commit 9585bf6

Please sign in to comment.