Postman.gov.sg enables public officers to send templated messages to many recipients.
- Postman.gov.sg
- Table of Contents
- Features
- Development
- Deployment
- Releasing
- Serverless
- Downtime procedure
- Infrastructure customizations
- Local node module
- How messages are sent
- Forking and configuring this product
- Contributions
- Passwordless login: login with your .gov.sg email
- Easily customize messages to reach a wide audience: create a message template, upload a file containing customization parameters and we will handle the customization for you
- Send emails: Just click send and Postman will send those messages out to your intended audience via email
- Send SMSes: Enter your twilio credentials, and Postman will send those messages via SMS. No integration with twilio is necessary
- View stats: Keep track of your campaign's progress as it's sending, and check back when it's done.
Set up a postgresql@11 database, and redis cache. PostgreSQL and Redis using Docker.
npm run dev:servicesThis project makes of detect-secrets to prevent secrets and credentials from being committed to the repository.
It runs as a pre-commit hook and it needs to be installed if you intend to make commits to the repo.
Note: The reason we're running detect-secrets through detect-secrets:precommit instead of using lint-staged is because detect-secrets-hook doesn't work well with the combination of output of staged files by lint-staged and baseline supplied.
Run the following to install:
pip install detect-secrets==1.2.0Upon blockage by detect-secrets-hook, please take these steps:
- Go into each of the locations pointed out by
detect-secrets-hookand remove accidentally added secrets - If some of these detections are false positive (please be super sure about this, when not sure check with teammates), update the secrets baseline by running `npm run detect-secrets:update
Example environment variables can be found in
Set the environment variables in a file named .env in each folder. If you're a developer at OGP, ask your friendly colleague for their env variables. (Please help to ensure the .env-example file stays up-to-date though!)
npm installThis step needs to be run if you have made a change to the database schema, or if you are setting up the project for the first time.
cd backend
npm run db:migrate # run all pending migrations
npm run db:seed # seed database with dummy dataIf you run into errors at the db:migrate step, this is likely because you have created a new model in TypeScript that has not been compiled to JavaScript. Run npm run build to fix this error.
If you need to undo any database migrations:
cd backend
npm run db:undo # undo most recent migrationYou can find more info on undoing migrations using Sequelize here.
If you wish to create a new migration, run:
cd backend
npx sequelize-cli migration:create --name migration-name-in-kebab-caselingui is used for internationalization. Read this for more info.
cd frontend
npm run extract
npm run compilenpm run devYou should find the
- React frontend at localhost:3000
- Express backend at localhost:4000
Alternatively, if you would like to develop locally against staging database and workers, ensure that you have set up the necessary variables in ./backend/.env and run either:
npm run dev:connectstagingdb(to make a read + write connection with the staging db); ornpm run dev:connectstagingdbreadonly(to make a readonly connection with the staging db.).
Your frontend and backend will still be on localhost but you will be able to use staging database and workers.
As we're relying on external service provider like AWS, Twilio to delivery our messages, we have endpoints to handle webhooks from these providers about statuses of the sent messages. To be able to develop these locally, we have to direct these webhooks to the local instance. This section will outline how to achieve that for the default service providers
We will be using ngrok. Run this command to create a tunnel to port 4000 of your machine (i.e. where the Postman backend server is listening)
ngrok http 4000You will get a public URL that looks something like https://d564-101-78-115-134.ap.ngrok.io. Keep this as we will be using it for later sections.
AWS SES webhooks are configured through configuration set with SNS topics.
Creating your config set:
- Go to the corresponding SES account & region that you are using >
Configuration sets - Create a new config set and open its dashboard page
- Go to
Event destinationstab >Add destination - Choose
Sends,Deliveries,Hard bounces,Complaints,Opens(Those are the events that we're handling) Next>Amazon SNS>Create SNS topicand chose it as the destinationNext> Confirm withAdd destination- Update your
.envfiles to set the values ofBACKEND_SES_CONFIGURATION_SETandWORKER_SES_CONFIGURATION_SETto the newly config set's name
Configure the endpoint to receive webhooks:
- Go to the SNS destination that you've just created
Create subscription> Protocol:HTTPS- Endpoint value will be
https://{env.CALLBACK_SECRET}@{NGROK_TUNNEL_URL}/v1/callback/email - Make sure your local backend server is running (this is so we can get the subscription URL confirmation)
- We can leave the rest of the configs to default as this is only used locally >
Create subscription - SES will send a request for confirmation with
subscribeUrlvalue in the request body - Go back to the dashboard page of your SNS topic
- Choose the correct subscription and click
Confirm subscription - Paste in the
subscribeUrlvalue and voila!
Note: After this initial setup, for future usage, the ngrok url might not be the same so we will have to delete the old one and create a new subscription under the same SNS topic.
SMS webhook URL is set on every request we make to Twilio and the base URL is determined by BACKEND_URL env variable. Hence, to receive webhooks from Twilio about message statuses, we just need to set BACKEND_URL value in .env to the ngrok provided URL suffixed with /v1/callback/sms.
Telegram webhook URL will be set when a credential added to the Postman platform. Our backend sets this value to the value of TELEGRAM_WEBHOOK_URL env variable. Hence, to receive webhooks from Telegram about message statuses, we need to set TELEGRAM_WEBOOK_URL in the .env file to https://{NGROK_TUNNEL_URL}/v1/callback/telegram.
Note: Because this URL is only set to the bot when the credentials are added, we will need to re-add the credentials when the ngrok URL changes between development sessions.
We use Github Actions to simplify our deployment process:
backendis deployed on Elastic Beanstalkfrontendis deployed on AWS Amplifyworkeris deployed on Elastic Container Serviceserverlessis deployed on AWS Lambda
When a pull request is merged to master, it will be deployed automatically.
We make use of AWS lambda to handle the callbacks from Twilio, as well as updating email delivery status.
See serverless-docs for details
See downtime-procedure for steps on how to bring the app down in the event that we need to make database changes
[
{
"source": "</^[^.]+$|\\\\\\\\.(?!(css|gif|ico|jpg|js|png|txt|svg|woff|ttf)$)([^.]+$)/>",
"target": "/index.html",
"status": "200",
"condition": null
}
]
Create a cluster with four services. These names are currently hardcoded for deployment in .travis.yml
| Cluster Name: postmangovsg-workers |
|---|
| Service Name | LaunchType | Platform version |
|---|---|---|
| staging-sending | FARGATE | 1.4.0 |
| staging-logger | FARGATE | 1.4.0 |
| prod-sending | FARGATE | 1.4.0 |
| prod-logger | FARGATE | 1.4.0 |
See local-module.md for details
See sending.md for details
Disclaimer of Liability. This product is pending Vulnerability Assessment and Penetration Testing (VAPT). You should conduct your own security assessment prior to using code provided in this repository. Open Government Products (OGP) makes no representations or warranties of any kind, expressed or implied about the completeness, accuracy, reliability, suitability or availability of this codebase. Any usage is at your own risk.
See configure/backend for details
See configure/frontend for details
See configure/worker for details
We currently have created two Grafana product metrics dashboards hosted on an EC2 instance. You can access them by connecting to the OGP VPN and following the URLs below. In order to SSH into the EC2 instance:
- Update the following env variables on
/backend/.env:GRAFANA_KEY_FOLDER(get the.pemfile from 1Password and store it in your~/.ssh/folder) andGRAFANA_EC2_HOST_URL(this should beec2-user@<EC2_instance_Public_IPv4_DNS>, which you can find from 1Password too). - Connect to the OGP VPN first, then run
npm run grafana.
The URLs of the Grafana dashboards are:
The production branch is master and each PR is deployed when it is merged into master.
If you have write access to this repository
- Check out your feature branch from
master - Make changes, and commit those changes
- Push these changes to Github
- Submit a pull request against
master, filling in the standard template
If you do not have write access to this repository
- Fork this repository
- Clone the forked repository to your machine
- Create a branch, make changes and commit those changes.
- Push these changes to Github
- Submit a pull request against
basefork/master(that's us!) - Describe the issue as thoroughly as possible, and with screenshots if applicable. A picture speaks a thousand words!
For more information, see CONTRIBUTING.md