├── .gitignore
├── LICENSE
├── README.md
├── api
├── .gitignore
├── authorizer
│ ├── handler.js
│ ├── package-lock.json
│ └── package.json
├── create-booking
│ ├── handler.js
│ ├── package-lock.json
│ └── package.json
├── list-bookings
│ ├── handler.js
│ ├── package-lock.json
│ └── package.json
├── login
│ ├── handler.js
│ ├── package-lock.json
│ └── package.json
├── register
│ ├── handler.js
│ ├── package-lock.json
│ └── package.json
└── serverless.yml
├── assets
└── logo-terraform.png
├── bookings-consumer
├── .gitignore
├── handler.js
├── package-lock.json
├── package.json
└── serverless.yml
├── deploy.sh
├── destroy.sh
├── email-notification
├── .gitignore
├── handler.js
├── package-lock.json
├── package.json
└── serverless.yml
├── sms-notification
├── .gitignore
├── handler.js
├── package-lock.json
├── package.json
└── serverless.yml
└── terraform
├── environments
├── dev
│ ├── main.tf
│ ├── provider.tf
│ ├── secrets.auto.tfvars
│ ├── variables.auto.tfvars
│ └── variables.tf
└── prod
│ ├── main.tf
│ ├── provider.tf
│ ├── secrets.auto.tfvars
│ ├── variables.auto.tfvars
│ └── variables.tf
└── infra
├── bookings
├── dynamodb-bookings.tf
├── iam-policy-attachment-bookings-stream-consumer.tf
├── iam-policy-attachment-create-booking.tf
├── iam-policy-attachment-list-bookings.tf
├── iam-policy-bookings-stream-consumer.tf
├── iam-policy-create-booking.tf
├── iam-policy-list-bookings.tf
├── iam-role-bookings-stream-consumer.tf
├── iam-role-create-booking.tf
├── iam-role-list-bookings.tf
├── templates
│ ├── dynamodb-policy.tpl
│ └── lambda-base-policy.tpl
└── variables.tf
├── notifications
├── iam-policy-attachment-email.tf
├── iam-policy-attachment-sms.tf
├── iam-policy-email.tf
├── iam-policy-sms.tf
├── iam-role-email.tf
├── iam-role-sms.tf
├── sns-subscriptions.tf
├── sns.tf
├── sqs-email.tf
├── sqs-sms.tf
├── templates
│ ├── lambda-base-policy.tpl
│ ├── lambda-sqs-policy.tpl
│ └── sqs-sns-policy.tpl
└── variables.tf
├── system
├── ssm.tf
└── variables.tf
└── users
├── dynamodb-users.tf
├── iam-policy-attachment-login.tf
├── iam-policy-attachment-register.tf
├── iam-policy-login.tf
├── iam-policy-register.tf
├── iam-role-login.tf
├── iam-role-register.tf
├── ssm-jwt-secret.tf
├── templates
├── dynamodb-policy.tpl
└── lambda-base-policy.tpl
└── variables.tf
/.gitignore:
--------------------------------------------------------------------------------
1 | .terraform*
2 | terraform.*
3 | node_modules
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Eduardo Santana
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Serverless booking software
7 |
8 |
9 | Cloud-Native Serverless application developed with Hashicorp Terraform and Serverless Framework.
10 |
11 | ## Introduction
12 |
13 | The main purpose of this small event-driven project is to show how to use Terraform to provision immutable Cloud-Native infrastructure, along with the Serverless Framework.
14 |
15 | ## Infrastructure as Code (IaC) 👷🏻
16 |
17 | Terraform is an open-source Infrastructure as Code tool to manage cloud resources. It was mainly used to provision the core infrastructure at AWS, which was the chosen cloud provider.
18 |
19 | ### Resources
20 |
21 | These are the main infrastructure resources provisioned by Terraform to create a fully event-driven serverless architecture:
22 |
23 | - DynamoDB
24 | - Tables
25 | - Indexes
26 | - Streams
27 | - IAM
28 | - Roles
29 | - Policies
30 | - SNS topics
31 | - SQS
32 | - Queues
33 | - DQLs
34 | - Systems Manager
35 | - Parameter Store: this service is the key-point to make it possible to Serverless and Terraform communicate to each other. All the resources names and ARNs (IAM roles, policies, queues, tables, etc.) are exported to the Parameter Store as key-values by Terraform. Then, it is possible to access these keys via Serverless Framework and referecence IAM role statements or create environment variables for each Lambda function.
36 |
37 | ## Serverless ☁️
38 |
39 | It is also possible to provision the resources mentioned previously with the Serverless Framework, however, in this project, I chose to separate responsabilities and only Terraform is used to manage infrastructure.
40 |
41 | Therefore, the main usage of Serverless in this project is to create the API gateway endpoints and Lambda functions triggered by API Gateway's events.
42 |
43 | ### Endpoints
44 |
45 | The API is really simple and has only four endpoints:
46 |
47 | - POST/users: register a new user.
48 | - POST/login: user authentication (returns a JWT token).
49 | - POST/bookings: register a new booking.
50 | - GET/bookings: list all the bookings (restricted to ADMIN users).
51 |
52 | Click on the button below and import the Insomnia workspace to do the API requests after the deployment step:
53 |
54 | [](https://insomnia.rest/run/?label=Terraform%20Serverless&uri=https%3A%2F%2Fgist.github.com%2Feduardo3g%2F0bc07cdc507e62b90c620b3eab50c1f2)
55 |
56 | ## Deploy
57 |
58 | ### Prerequisites
59 |
60 | First of all, you need an AWS account and create a user with programmatic admin access. Then, configure the AWS CLI on your workstation.
61 |
62 |
63 | Once you're done, clone this repository and move yourself to the root directory.
64 |
65 | ### Environment variables
66 |
67 | The API depends on two external services to send e-mails and SMSs when a new booking is registed in the DynamoDB table. Create accounts on each of them:
68 | - Zoho
69 | - Messagebird
70 |
71 |
72 |
73 | The last thing before the deployment is to define your environment variables of the `dev` stage.
74 |
75 | ```bash
76 | # Move to the Terraform development directory
77 | cd terraform/environments/dev
78 |
79 | # Open the file that contains the environment variables and update the values
80 | nano secrets.auto.tfvars
81 | ```
82 |
83 | In order to keep things simple, I recommend only changing the following keys:
84 | - email_from: use the e-mail you created at Zoho (the one containing the domain @zohomail.com)
85 | - email_from_password: your password from Zoho
86 | - email_to: create a temporary e-mail box at TempMail (leave the window opened while you're testing)
87 | - message_bird_api_key: your test API Key from Messagebird (No SMSs will be sent with this key. Use the production one if you want to receive them.)
88 | - sms_phone_from: your cellphone number (e.g: "+55119...")
89 | - sms_phone_to: your cellphone number (e.g: "+55119...")
90 |
91 | ### Deploy
92 |
93 | Luckly, now you just need to run a single command and Terraform will do the magic for you 😛
94 | ```bash
95 | # Run the command bellow to avoid lack of permission to run the deploy shell script
96 | chmod u=rwx,g=r,o=r deploy.sh
97 |
98 | # Deploy everything to a development stage at AWS
99 | ./deploy.sh dev
100 | ```
101 |
102 | You're supposed to see 53 resources created by Terraform, that is, all the resources I mentioned in previous sections (IAM roles, tables, queues, etc).
103 |
104 |
105 |
106 | In the other hand, the Serverless Framework will create the `dev-api` and 8 Lambda functions.
107 |
108 | ### Destroy
109 |
110 | Now that you've tested everything, feel free to delete all the resources at AWS. It's as simple as the deployment script:
111 | ```bash
112 | # Run the command bellow to avoid lack of permission to run the destroy shell script
113 | chmod u=rwx,g=r,o=r deploy.sh
114 |
115 | # Remove everything from the development stage at AWS
116 | ./destroy.sh dev
117 | ```
118 |
--------------------------------------------------------------------------------
/api/.gitignore:
--------------------------------------------------------------------------------
1 | # package directories
2 | node_modules
3 | jspm_packages
4 |
5 | # Serverless directories
6 | .serverless
--------------------------------------------------------------------------------
/api/authorizer/handler.js:
--------------------------------------------------------------------------------
1 | const jwt = require('jsonwebtoken');
2 |
3 | exports.authorizer = function(event, context, callback) {
4 | const authHeader = event.authorizationToken;
5 |
6 | try {
7 | const [, token] = authHeader.split(' ');
8 |
9 | const user = jwt.verify(token, process.env.JWT_SECRET);
10 | callback(null, generatePolicy('user', 'Allow', event.methodArn, user));
11 | } catch (exception) {
12 | console.log(exception);
13 | callback(null, generatePolicy('user', 'Deny', event.methodArn));
14 | }
15 | };
16 |
17 | const generatePolicy = function(principalId, effect, resource, user) {
18 | let authResponse = {};
19 |
20 | authResponse.principalId = principalId;
21 | if (effect && resource) {
22 | let policyDocument = {};
23 | policyDocument.Version = '2012-10-17';
24 | policyDocument.Statement = [];
25 | let statementOne = {};
26 | statementOne.Action = 'execute-api:Invoke';
27 | statementOne.Effect = effect;
28 | statementOne.Resource = resource;
29 | policyDocument.Statement[0] = statementOne;
30 | authResponse.policyDocument = policyDocument;
31 | }
32 |
33 | if (user) {
34 | authResponse.context = user;
35 | }
36 | return authResponse;
37 | }
--------------------------------------------------------------------------------
/api/authorizer/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "authorizer",
3 | "version": "1.0.0",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "buffer-equal-constant-time": {
8 | "version": "1.0.1",
9 | "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
10 | "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk="
11 | },
12 | "ecdsa-sig-formatter": {
13 | "version": "1.0.11",
14 | "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
15 | "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
16 | "requires": {
17 | "safe-buffer": "^5.0.1"
18 | }
19 | },
20 | "jsonwebtoken": {
21 | "version": "8.5.1",
22 | "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz",
23 | "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==",
24 | "requires": {
25 | "jws": "^3.2.2",
26 | "lodash.includes": "^4.3.0",
27 | "lodash.isboolean": "^3.0.3",
28 | "lodash.isinteger": "^4.0.4",
29 | "lodash.isnumber": "^3.0.3",
30 | "lodash.isplainobject": "^4.0.6",
31 | "lodash.isstring": "^4.0.1",
32 | "lodash.once": "^4.0.0",
33 | "ms": "^2.1.1",
34 | "semver": "^5.6.0"
35 | }
36 | },
37 | "jwa": {
38 | "version": "1.4.1",
39 | "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
40 | "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==",
41 | "requires": {
42 | "buffer-equal-constant-time": "1.0.1",
43 | "ecdsa-sig-formatter": "1.0.11",
44 | "safe-buffer": "^5.0.1"
45 | }
46 | },
47 | "jws": {
48 | "version": "3.2.2",
49 | "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
50 | "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
51 | "requires": {
52 | "jwa": "^1.4.1",
53 | "safe-buffer": "^5.0.1"
54 | }
55 | },
56 | "lodash.includes": {
57 | "version": "4.3.0",
58 | "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
59 | "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8="
60 | },
61 | "lodash.isboolean": {
62 | "version": "3.0.3",
63 | "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
64 | "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY="
65 | },
66 | "lodash.isinteger": {
67 | "version": "4.0.4",
68 | "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
69 | "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M="
70 | },
71 | "lodash.isnumber": {
72 | "version": "3.0.3",
73 | "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
74 | "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w="
75 | },
76 | "lodash.isplainobject": {
77 | "version": "4.0.6",
78 | "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
79 | "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs="
80 | },
81 | "lodash.isstring": {
82 | "version": "4.0.1",
83 | "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
84 | "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE="
85 | },
86 | "lodash.once": {
87 | "version": "4.1.1",
88 | "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
89 | "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w="
90 | },
91 | "ms": {
92 | "version": "2.1.3",
93 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
94 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
95 | },
96 | "safe-buffer": {
97 | "version": "5.2.1",
98 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
99 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
100 | },
101 | "semver": {
102 | "version": "5.7.1",
103 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
104 | "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
105 | }
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/api/authorizer/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "authorizer",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "handler.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "keywords": [],
10 | "author": "",
11 | "license": "ISC",
12 | "dependencies": {
13 | "jsonwebtoken": "^8.5.1"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/api/create-booking/handler.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const AWS = require('aws-sdk');
4 | AWS.config.update({
5 | region: process.env.AWS_REGION,
6 | });
7 | const documentClient = new AWS.DynamoDB.DocumentClient();
8 |
9 | const { v4: uuidv4 } = require('uuid');
10 |
11 | module.exports.create = async event => {
12 | const body = JSON.parse(event.body);
13 |
14 | await documentClient.put({
15 | TableName: process.env.DYNAMODB_BOOKINGS,
16 | Item: {
17 | id: uuidv4(),
18 | date: body.date,
19 | user: event.requestContext.authorizer,
20 | }
21 | }).promise();
22 |
23 | return {
24 | statusCode: 200,
25 | body: JSON.stringify({ message: 'The booking was created successfully' }),
26 | }
27 | };
--------------------------------------------------------------------------------
/api/create-booking/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "create-booking",
3 | "version": "1.0.0",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "aws-sdk": {
8 | "version": "2.817.0",
9 | "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.817.0.tgz",
10 | "integrity": "sha512-DZIdWpkcqbqsCz0MEskHsyFaqc6Tk9XIFqXAg1AKHbOgC8nU45bz+Y2osX77pU01JkS/G7OhGtGmlKDrOPvFwg==",
11 | "dev": true,
12 | "requires": {
13 | "buffer": "4.9.2",
14 | "events": "1.1.1",
15 | "ieee754": "1.1.13",
16 | "jmespath": "0.15.0",
17 | "querystring": "0.2.0",
18 | "sax": "1.2.1",
19 | "url": "0.10.3",
20 | "uuid": "3.3.2",
21 | "xml2js": "0.4.19"
22 | },
23 | "dependencies": {
24 | "uuid": {
25 | "version": "3.3.2",
26 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
27 | "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==",
28 | "dev": true
29 | }
30 | }
31 | },
32 | "base64-js": {
33 | "version": "1.5.1",
34 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
35 | "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
36 | "dev": true
37 | },
38 | "buffer": {
39 | "version": "4.9.2",
40 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz",
41 | "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==",
42 | "dev": true,
43 | "requires": {
44 | "base64-js": "^1.0.2",
45 | "ieee754": "^1.1.4",
46 | "isarray": "^1.0.0"
47 | }
48 | },
49 | "events": {
50 | "version": "1.1.1",
51 | "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz",
52 | "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=",
53 | "dev": true
54 | },
55 | "ieee754": {
56 | "version": "1.1.13",
57 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
58 | "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==",
59 | "dev": true
60 | },
61 | "isarray": {
62 | "version": "1.0.0",
63 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
64 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
65 | "dev": true
66 | },
67 | "jmespath": {
68 | "version": "0.15.0",
69 | "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz",
70 | "integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=",
71 | "dev": true
72 | },
73 | "punycode": {
74 | "version": "1.3.2",
75 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
76 | "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=",
77 | "dev": true
78 | },
79 | "querystring": {
80 | "version": "0.2.0",
81 | "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
82 | "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=",
83 | "dev": true
84 | },
85 | "sax": {
86 | "version": "1.2.1",
87 | "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz",
88 | "integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o=",
89 | "dev": true
90 | },
91 | "url": {
92 | "version": "0.10.3",
93 | "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz",
94 | "integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=",
95 | "dev": true,
96 | "requires": {
97 | "punycode": "1.3.2",
98 | "querystring": "0.2.0"
99 | }
100 | },
101 | "uuid": {
102 | "version": "8.3.2",
103 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
104 | "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
105 | },
106 | "xml2js": {
107 | "version": "0.4.19",
108 | "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz",
109 | "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==",
110 | "dev": true,
111 | "requires": {
112 | "sax": ">=0.6.0",
113 | "xmlbuilder": "~9.0.1"
114 | }
115 | },
116 | "xmlbuilder": {
117 | "version": "9.0.7",
118 | "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz",
119 | "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=",
120 | "dev": true
121 | }
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/api/create-booking/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "create-booking",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "handler.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "keywords": [],
10 | "author": "",
11 | "license": "ISC",
12 | "devDependencies": {
13 | "aws-sdk": "^2.817.0"
14 | },
15 | "dependencies": {
16 | "uuid": "^8.3.2"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/api/list-bookings/handler.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const AWS = require('aws-sdk');
4 | AWS.config.update({
5 | region: process.env.AWS_REGION,
6 | });
7 | const documentClient = new AWS.DynamoDB.DocumentClient();
8 |
9 | module.exports.list = async event => {
10 | const body = JSON.parse(event.body);
11 |
12 | if (event.requestContext.authorizer.role === 'ADMIN') {
13 | const data = await documentClient.scan({
14 | TableName: process.env.DYNAMODB_BOOKINGS,
15 | }).promise();
16 |
17 | return {
18 | statusCode: 200,
19 | body: JSON.stringify(data.Items),
20 | };
21 | }
22 |
23 | return {
24 | statusCode: 403,
25 | body: JSON.stringify({
26 | message: 'Only administrators are allowed to access this resource'
27 | }),
28 | };
29 | };
--------------------------------------------------------------------------------
/api/list-bookings/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "list-bookings",
3 | "version": "1.0.0",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "aws-sdk": {
8 | "version": "2.817.0",
9 | "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.817.0.tgz",
10 | "integrity": "sha512-DZIdWpkcqbqsCz0MEskHsyFaqc6Tk9XIFqXAg1AKHbOgC8nU45bz+Y2osX77pU01JkS/G7OhGtGmlKDrOPvFwg==",
11 | "dev": true,
12 | "requires": {
13 | "buffer": "4.9.2",
14 | "events": "1.1.1",
15 | "ieee754": "1.1.13",
16 | "jmespath": "0.15.0",
17 | "querystring": "0.2.0",
18 | "sax": "1.2.1",
19 | "url": "0.10.3",
20 | "uuid": "3.3.2",
21 | "xml2js": "0.4.19"
22 | }
23 | },
24 | "base64-js": {
25 | "version": "1.5.1",
26 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
27 | "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
28 | "dev": true
29 | },
30 | "buffer": {
31 | "version": "4.9.2",
32 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz",
33 | "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==",
34 | "dev": true,
35 | "requires": {
36 | "base64-js": "^1.0.2",
37 | "ieee754": "^1.1.4",
38 | "isarray": "^1.0.0"
39 | }
40 | },
41 | "events": {
42 | "version": "1.1.1",
43 | "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz",
44 | "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=",
45 | "dev": true
46 | },
47 | "ieee754": {
48 | "version": "1.1.13",
49 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
50 | "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==",
51 | "dev": true
52 | },
53 | "isarray": {
54 | "version": "1.0.0",
55 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
56 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
57 | "dev": true
58 | },
59 | "jmespath": {
60 | "version": "0.15.0",
61 | "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz",
62 | "integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=",
63 | "dev": true
64 | },
65 | "punycode": {
66 | "version": "1.3.2",
67 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
68 | "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=",
69 | "dev": true
70 | },
71 | "querystring": {
72 | "version": "0.2.0",
73 | "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
74 | "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=",
75 | "dev": true
76 | },
77 | "sax": {
78 | "version": "1.2.1",
79 | "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz",
80 | "integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o=",
81 | "dev": true
82 | },
83 | "url": {
84 | "version": "0.10.3",
85 | "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz",
86 | "integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=",
87 | "dev": true,
88 | "requires": {
89 | "punycode": "1.3.2",
90 | "querystring": "0.2.0"
91 | }
92 | },
93 | "uuid": {
94 | "version": "3.3.2",
95 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
96 | "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==",
97 | "dev": true
98 | },
99 | "xml2js": {
100 | "version": "0.4.19",
101 | "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz",
102 | "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==",
103 | "dev": true,
104 | "requires": {
105 | "sax": ">=0.6.0",
106 | "xmlbuilder": "~9.0.1"
107 | }
108 | },
109 | "xmlbuilder": {
110 | "version": "9.0.7",
111 | "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz",
112 | "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=",
113 | "dev": true
114 | }
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/api/list-bookings/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "list-bookings",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "handler.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "keywords": [],
10 | "author": "",
11 | "license": "ISC",
12 | "devDependencies": {
13 | "aws-sdk": "^2.817.0"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/api/login/handler.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const AWS = require('aws-sdk');
4 | AWS.config.update({
5 | region: process.env.AWS_REGION,
6 | });
7 | const documentClient = new AWS.DynamoDB.DocumentClient();
8 | const bcrypt = require('bcryptjs');
9 | const jwt = require('jsonwebtoken');
10 |
11 | module.exports.login = async event => {
12 | const body = JSON.parse(event.body);
13 |
14 | const params = {
15 | TableName: process.env.DYNAMODB_USERS,
16 | IndexName: process.env.EMAIL_GSI,
17 | KeyConditionExpression: 'email = :email',
18 | ExpressionAttributeValues: {
19 | ':email': body.email,
20 | },
21 | };
22 |
23 | const data = await documentClient.query(params).promise();
24 | const user = data.Items[0];
25 |
26 | if (user) {
27 | if (bcrypt.compareSync(body.password, user.password)) {
28 | delete user.password;
29 |
30 | return {
31 | statusCode: 200,
32 | body: JSON.stringify({ token: jwt.sign(user, process.env.JWT_SECRET) }),
33 | };
34 | }
35 |
36 | return {
37 | statusCode: 401,
38 | body: JSON.stringify({ message: 'The user credentials are incorrect' }),
39 | };
40 | }
41 |
42 | return {
43 | statusCode: 401,
44 | body: JSON.stringify({ message: 'The user credentials are incorrect' }),
45 | };
46 | };
47 |
--------------------------------------------------------------------------------
/api/login/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "login",
3 | "version": "1.0.0",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "aws-sdk": {
8 | "version": "2.817.0",
9 | "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.817.0.tgz",
10 | "integrity": "sha512-DZIdWpkcqbqsCz0MEskHsyFaqc6Tk9XIFqXAg1AKHbOgC8nU45bz+Y2osX77pU01JkS/G7OhGtGmlKDrOPvFwg==",
11 | "dev": true,
12 | "requires": {
13 | "buffer": "4.9.2",
14 | "events": "1.1.1",
15 | "ieee754": "1.1.13",
16 | "jmespath": "0.15.0",
17 | "querystring": "0.2.0",
18 | "sax": "1.2.1",
19 | "url": "0.10.3",
20 | "uuid": "3.3.2",
21 | "xml2js": "0.4.19"
22 | }
23 | },
24 | "base64-js": {
25 | "version": "1.5.1",
26 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
27 | "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
28 | "dev": true
29 | },
30 | "bcryptjs": {
31 | "version": "2.4.3",
32 | "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz",
33 | "integrity": "sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms="
34 | },
35 | "buffer": {
36 | "version": "4.9.2",
37 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz",
38 | "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==",
39 | "dev": true,
40 | "requires": {
41 | "base64-js": "^1.0.2",
42 | "ieee754": "^1.1.4",
43 | "isarray": "^1.0.0"
44 | }
45 | },
46 | "buffer-equal-constant-time": {
47 | "version": "1.0.1",
48 | "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
49 | "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk="
50 | },
51 | "ecdsa-sig-formatter": {
52 | "version": "1.0.11",
53 | "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
54 | "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
55 | "requires": {
56 | "safe-buffer": "^5.0.1"
57 | }
58 | },
59 | "events": {
60 | "version": "1.1.1",
61 | "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz",
62 | "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=",
63 | "dev": true
64 | },
65 | "ieee754": {
66 | "version": "1.1.13",
67 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
68 | "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==",
69 | "dev": true
70 | },
71 | "isarray": {
72 | "version": "1.0.0",
73 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
74 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
75 | "dev": true
76 | },
77 | "jmespath": {
78 | "version": "0.15.0",
79 | "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz",
80 | "integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=",
81 | "dev": true
82 | },
83 | "jsonwebtoken": {
84 | "version": "8.5.1",
85 | "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz",
86 | "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==",
87 | "requires": {
88 | "jws": "^3.2.2",
89 | "lodash.includes": "^4.3.0",
90 | "lodash.isboolean": "^3.0.3",
91 | "lodash.isinteger": "^4.0.4",
92 | "lodash.isnumber": "^3.0.3",
93 | "lodash.isplainobject": "^4.0.6",
94 | "lodash.isstring": "^4.0.1",
95 | "lodash.once": "^4.0.0",
96 | "ms": "^2.1.1",
97 | "semver": "^5.6.0"
98 | }
99 | },
100 | "jwa": {
101 | "version": "1.4.1",
102 | "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
103 | "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==",
104 | "requires": {
105 | "buffer-equal-constant-time": "1.0.1",
106 | "ecdsa-sig-formatter": "1.0.11",
107 | "safe-buffer": "^5.0.1"
108 | }
109 | },
110 | "jws": {
111 | "version": "3.2.2",
112 | "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
113 | "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
114 | "requires": {
115 | "jwa": "^1.4.1",
116 | "safe-buffer": "^5.0.1"
117 | }
118 | },
119 | "lodash.includes": {
120 | "version": "4.3.0",
121 | "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
122 | "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8="
123 | },
124 | "lodash.isboolean": {
125 | "version": "3.0.3",
126 | "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
127 | "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY="
128 | },
129 | "lodash.isinteger": {
130 | "version": "4.0.4",
131 | "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
132 | "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M="
133 | },
134 | "lodash.isnumber": {
135 | "version": "3.0.3",
136 | "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
137 | "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w="
138 | },
139 | "lodash.isplainobject": {
140 | "version": "4.0.6",
141 | "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
142 | "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs="
143 | },
144 | "lodash.isstring": {
145 | "version": "4.0.1",
146 | "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
147 | "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE="
148 | },
149 | "lodash.once": {
150 | "version": "4.1.1",
151 | "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
152 | "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w="
153 | },
154 | "ms": {
155 | "version": "2.1.3",
156 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
157 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
158 | },
159 | "punycode": {
160 | "version": "1.3.2",
161 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
162 | "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=",
163 | "dev": true
164 | },
165 | "querystring": {
166 | "version": "0.2.0",
167 | "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
168 | "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=",
169 | "dev": true
170 | },
171 | "safe-buffer": {
172 | "version": "5.2.1",
173 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
174 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
175 | },
176 | "sax": {
177 | "version": "1.2.1",
178 | "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz",
179 | "integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o=",
180 | "dev": true
181 | },
182 | "semver": {
183 | "version": "5.7.1",
184 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
185 | "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
186 | },
187 | "url": {
188 | "version": "0.10.3",
189 | "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz",
190 | "integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=",
191 | "dev": true,
192 | "requires": {
193 | "punycode": "1.3.2",
194 | "querystring": "0.2.0"
195 | }
196 | },
197 | "uuid": {
198 | "version": "3.3.2",
199 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
200 | "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==",
201 | "dev": true
202 | },
203 | "xml2js": {
204 | "version": "0.4.19",
205 | "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz",
206 | "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==",
207 | "dev": true,
208 | "requires": {
209 | "sax": ">=0.6.0",
210 | "xmlbuilder": "~9.0.1"
211 | }
212 | },
213 | "xmlbuilder": {
214 | "version": "9.0.7",
215 | "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz",
216 | "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=",
217 | "dev": true
218 | }
219 | }
220 | }
221 |
--------------------------------------------------------------------------------
/api/login/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "login",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "handler.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "keywords": [],
10 | "author": "",
11 | "license": "ISC",
12 | "devDependencies": {
13 | "aws-sdk": "^2.817.0"
14 | },
15 | "dependencies": {
16 | "bcryptjs": "^2.4.3",
17 | "jsonwebtoken": "^8.5.1"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/api/register/handler.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const AWS = require('aws-sdk');
4 | AWS.config.update({
5 | region: process.env.AWS_REGION,
6 | });
7 | const documentClient = new AWS.DynamoDB.DocumentClient();
8 | const bcrypt = require('bcryptjs');
9 | const { v4: uuidv4 } = require('uuid');
10 |
11 | module.exports.register = async event => {
12 | const body = JSON.parse(event.body);
13 |
14 | await documentClient.put({
15 | TableName: process.env.DYNAMODB_USERS,
16 | Item: {
17 | id: uuidv4(),
18 | name: body.name,
19 | email: body.email,
20 | password: bcrypt.hashSync(body.password, 10),
21 | },
22 | }).promise();
23 |
24 | return {
25 | statusCode: 201,
26 | body: JSON.stringify({ message: 'The user was registered successfully' }),
27 | };
28 | };
29 |
--------------------------------------------------------------------------------
/api/register/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "api",
3 | "version": "1.0.0",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "aws-sdk": {
8 | "version": "2.817.0",
9 | "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.817.0.tgz",
10 | "integrity": "sha512-DZIdWpkcqbqsCz0MEskHsyFaqc6Tk9XIFqXAg1AKHbOgC8nU45bz+Y2osX77pU01JkS/G7OhGtGmlKDrOPvFwg==",
11 | "dev": true,
12 | "requires": {
13 | "buffer": "4.9.2",
14 | "events": "1.1.1",
15 | "ieee754": "1.1.13",
16 | "jmespath": "0.15.0",
17 | "querystring": "0.2.0",
18 | "sax": "1.2.1",
19 | "url": "0.10.3",
20 | "uuid": "3.3.2",
21 | "xml2js": "0.4.19"
22 | },
23 | "dependencies": {
24 | "uuid": {
25 | "version": "3.3.2",
26 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
27 | "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==",
28 | "dev": true
29 | }
30 | }
31 | },
32 | "base64-js": {
33 | "version": "1.5.1",
34 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
35 | "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
36 | "dev": true
37 | },
38 | "bcryptjs": {
39 | "version": "2.4.3",
40 | "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz",
41 | "integrity": "sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms="
42 | },
43 | "buffer": {
44 | "version": "4.9.2",
45 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz",
46 | "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==",
47 | "dev": true,
48 | "requires": {
49 | "base64-js": "^1.0.2",
50 | "ieee754": "^1.1.4",
51 | "isarray": "^1.0.0"
52 | }
53 | },
54 | "events": {
55 | "version": "1.1.1",
56 | "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz",
57 | "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=",
58 | "dev": true
59 | },
60 | "ieee754": {
61 | "version": "1.1.13",
62 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
63 | "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==",
64 | "dev": true
65 | },
66 | "isarray": {
67 | "version": "1.0.0",
68 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
69 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
70 | "dev": true
71 | },
72 | "jmespath": {
73 | "version": "0.15.0",
74 | "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz",
75 | "integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=",
76 | "dev": true
77 | },
78 | "punycode": {
79 | "version": "1.3.2",
80 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
81 | "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=",
82 | "dev": true
83 | },
84 | "querystring": {
85 | "version": "0.2.0",
86 | "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
87 | "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=",
88 | "dev": true
89 | },
90 | "sax": {
91 | "version": "1.2.1",
92 | "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz",
93 | "integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o=",
94 | "dev": true
95 | },
96 | "url": {
97 | "version": "0.10.3",
98 | "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz",
99 | "integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=",
100 | "dev": true,
101 | "requires": {
102 | "punycode": "1.3.2",
103 | "querystring": "0.2.0"
104 | }
105 | },
106 | "uuid": {
107 | "version": "8.3.2",
108 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
109 | "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
110 | },
111 | "xml2js": {
112 | "version": "0.4.19",
113 | "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz",
114 | "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==",
115 | "dev": true,
116 | "requires": {
117 | "sax": ">=0.6.0",
118 | "xmlbuilder": "~9.0.1"
119 | }
120 | },
121 | "xmlbuilder": {
122 | "version": "9.0.7",
123 | "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz",
124 | "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=",
125 | "dev": true
126 | }
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/api/register/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "api",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "keywords": [],
10 | "author": "",
11 | "license": "ISC",
12 | "dependencies": {
13 | "bcryptjs": "^2.4.3",
14 | "uuid": "^8.3.2"
15 | },
16 | "devDependencies": {
17 | "aws-sdk": "^2.817.0"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/api/serverless.yml:
--------------------------------------------------------------------------------
1 | service: api
2 |
3 | frameworkVersion: '2'
4 |
5 | provider:
6 | name: aws
7 | runtime: nodejs12.x
8 |
9 | stage: dev
10 | region: us-east-1
11 |
12 | functions:
13 | register:
14 | role: ${ssm:${self:custom.stage}-register-iam-role}
15 | handler: register/handler.register
16 | environment:
17 | DYNAMODB_USERS: ${ssm:${self:custom.stage}-dynamodb-users-table}
18 | events:
19 | - http:
20 | path: users
21 | method: post
22 | login:
23 | role: ${ssm:${self:custom.stage}-login-iam-role}
24 | handler: login/handler.login
25 | environment:
26 | DYNAMODB_USERS: ${ssm:${self:custom.stage}-dynamodb-users-table}
27 | JWT_SECRET: ${ssm:${self:custom.stage}-jwt-secret}
28 | EMAIL_GSI: ${ssm:${self:custom.stage}-email-gsi}
29 | events:
30 | - http:
31 | path: login
32 | method: post
33 | create_booking:
34 | role: ${ssm:${self:custom.stage}-create-booking-iam-role}
35 | handler: create-booking/handler.create
36 | environment:
37 | DYNAMODB_BOOKINGS: ${ssm:${self:custom.stage}-dynamodb-bookings-table}
38 | events:
39 | - http:
40 | path: bookings
41 | method: post
42 | authorizer: authorizer
43 | list_bookings:
44 | role: ${ssm:${self:custom.stage}-list-bookings-iam-role}
45 | handler: list-bookings/handler.list
46 | environment:
47 | DYNAMODB_BOOKINGS: ${ssm:${self:custom.stage}-dynamodb-bookings-table}
48 | events:
49 | - http:
50 | path: bookings
51 | method: get
52 | authorizer: authorizer
53 | authorizer:
54 | handler: authorizer/handler.authorizer
55 | environment:
56 | JWT_SECRET: ${ssm:${self:custom.stage}-jwt-secret}
57 |
58 | custom:
59 | stage: ${opt:stage, self:provider.stage}
--------------------------------------------------------------------------------
/assets/logo-terraform.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eduardo3g/terraform-serverless/70d093a97e50ff23f61aedd68cb6704ac98f482d/assets/logo-terraform.png
--------------------------------------------------------------------------------
/bookings-consumer/.gitignore:
--------------------------------------------------------------------------------
1 | # package directories
2 | node_modules
3 | jspm_packages
4 |
5 | # Serverless directories
6 | .serverless
--------------------------------------------------------------------------------
/bookings-consumer/handler.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const AWS = require('aws-sdk');
4 | AWS.config.update({
5 | region: process.env.AWS_REGION,
6 | });
7 | const SNS = new AWS.SNS();
8 | const converter = AWS.DynamoDB.Converter;
9 | const moment = require('moment');
10 |
11 | moment.locale('pt-br');
12 |
13 | module.exports.listen = async event => {
14 | const snsPromises = [];
15 |
16 | for (let record of event.Records) {
17 | if (record.eventName === 'INSERT') {
18 | const booking = converter.unmarshall(record.dynamodb.NewImage);
19 |
20 | snsPromises.push(SNS.publish({
21 | TopicArn: process.env.SNS_NOTIFICATIONS_TOPIC,
22 | Message: `Booking created: the user ${booking.user.name} (${booking.user.email}) created a booking on: ${moment(booking.date).format('LLLL')}`,
23 | }).promise());
24 | }
25 | }
26 |
27 | await Promise.all(snsPromises);
28 |
29 | console.log('Message(s) sent successfully to SNS topic');
30 |
31 | return { message: 'Go Serverless v1.0! Your function executed successfully!' };
32 | };
33 |
--------------------------------------------------------------------------------
/bookings-consumer/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bookings-consumer",
3 | "version": "1.0.0",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "aws-sdk": {
8 | "version": "2.817.0",
9 | "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.817.0.tgz",
10 | "integrity": "sha512-DZIdWpkcqbqsCz0MEskHsyFaqc6Tk9XIFqXAg1AKHbOgC8nU45bz+Y2osX77pU01JkS/G7OhGtGmlKDrOPvFwg==",
11 | "dev": true,
12 | "requires": {
13 | "buffer": "4.9.2",
14 | "events": "1.1.1",
15 | "ieee754": "1.1.13",
16 | "jmespath": "0.15.0",
17 | "querystring": "0.2.0",
18 | "sax": "1.2.1",
19 | "url": "0.10.3",
20 | "uuid": "3.3.2",
21 | "xml2js": "0.4.19"
22 | }
23 | },
24 | "base64-js": {
25 | "version": "1.5.1",
26 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
27 | "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
28 | "dev": true
29 | },
30 | "buffer": {
31 | "version": "4.9.2",
32 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz",
33 | "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==",
34 | "dev": true,
35 | "requires": {
36 | "base64-js": "^1.0.2",
37 | "ieee754": "^1.1.4",
38 | "isarray": "^1.0.0"
39 | }
40 | },
41 | "events": {
42 | "version": "1.1.1",
43 | "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz",
44 | "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=",
45 | "dev": true
46 | },
47 | "ieee754": {
48 | "version": "1.1.13",
49 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
50 | "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==",
51 | "dev": true
52 | },
53 | "isarray": {
54 | "version": "1.0.0",
55 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
56 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
57 | "dev": true
58 | },
59 | "jmespath": {
60 | "version": "0.15.0",
61 | "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz",
62 | "integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=",
63 | "dev": true
64 | },
65 | "moment": {
66 | "version": "2.29.1",
67 | "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz",
68 | "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ=="
69 | },
70 | "punycode": {
71 | "version": "1.3.2",
72 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
73 | "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=",
74 | "dev": true
75 | },
76 | "querystring": {
77 | "version": "0.2.0",
78 | "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
79 | "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=",
80 | "dev": true
81 | },
82 | "sax": {
83 | "version": "1.2.1",
84 | "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz",
85 | "integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o=",
86 | "dev": true
87 | },
88 | "url": {
89 | "version": "0.10.3",
90 | "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz",
91 | "integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=",
92 | "dev": true,
93 | "requires": {
94 | "punycode": "1.3.2",
95 | "querystring": "0.2.0"
96 | }
97 | },
98 | "uuid": {
99 | "version": "3.3.2",
100 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
101 | "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==",
102 | "dev": true
103 | },
104 | "xml2js": {
105 | "version": "0.4.19",
106 | "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz",
107 | "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==",
108 | "dev": true,
109 | "requires": {
110 | "sax": ">=0.6.0",
111 | "xmlbuilder": "~9.0.1"
112 | }
113 | },
114 | "xmlbuilder": {
115 | "version": "9.0.7",
116 | "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz",
117 | "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=",
118 | "dev": true
119 | }
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/bookings-consumer/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bookings-consumer",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "handler.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "keywords": [],
10 | "author": "",
11 | "license": "ISC",
12 | "devDependencies": {
13 | "aws-sdk": "^2.817.0"
14 | },
15 | "dependencies": {
16 | "moment": "^2.29.1"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/bookings-consumer/serverless.yml:
--------------------------------------------------------------------------------
1 | service: bookings-consumer
2 |
3 | frameworkVersion: '2'
4 |
5 | provider:
6 | name: aws
7 | runtime: nodejs12.x
8 |
9 | stage: dev
10 | region: us-east-1
11 |
12 | functions:
13 | stream_listener:
14 | handler: handler.listen
15 | role: ${ssm:${self:custom.stage}-bookings-stream-consumer-iam-role}
16 | events:
17 | - stream: ${ssm:${self:custom.stage}-dynamodb-bookings-stream}
18 | environment:
19 | SNS_NOTIFICATIONS_TOPIC: ${ssm:${self:custom.stage}-notifications-topic}
20 |
21 | custom:
22 | stage: ${opt:stage, self:provider.stage}
--------------------------------------------------------------------------------
/deploy.sh:
--------------------------------------------------------------------------------
1 | cd terraform/environments/$1
2 |
3 | terraform init && terraform apply -auto-approve
4 |
5 | cd ../../../
6 | pwd
7 |
8 | executeSls () {
9 | pwd
10 | sls deploy --stage $1
11 | }
12 |
13 | cd api/
14 | executeSls $1
15 | cd ../bookings-consumer/
16 | executeSls $1
17 | cd ../sms-notification/
18 | executeSls $1
19 | cd ../email-notification
20 | executeSls $1
--------------------------------------------------------------------------------
/destroy.sh:
--------------------------------------------------------------------------------
1 | executeSls () {
2 | pwd
3 | sls remove --stage $1
4 | }
5 |
6 | cd api/
7 | executeSls $1
8 | cd ../bookings-consumer/
9 | executeSls $1
10 | cd ../sms-notification/
11 | executeSls $1
12 | cd ../email-notification
13 | executeSls $1
14 |
15 | cd ../terraform/environments/$1
16 | pwd
17 | terraform destroy -auto-approve
--------------------------------------------------------------------------------
/email-notification/.gitignore:
--------------------------------------------------------------------------------
1 | # package directories
2 | node_modules
3 | jspm_packages
4 |
5 | # Serverless directories
6 | .serverless
--------------------------------------------------------------------------------
/email-notification/handler.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const nodemailer = require('nodemailer');
4 |
5 | const transporter = nodemailer.createTransport({
6 | host: process.env.SMTP_SERVER,
7 | port: 465,
8 | secure: true,
9 | auth: {
10 | user: process.env.EMAIL_FROM,
11 | pass: process.env.EMAIL_FROM_PASSWORD,
12 | },
13 | });
14 |
15 | module.exports.send = async event => {
16 | let emailPromises = [];
17 |
18 | for (let record of event.Records) {
19 | const message = JSON.parse(record.body).Message;
20 |
21 | emailPromises.push(transporter.sendMail({
22 | from: `"Reservations 👻" <${process.env.EMAIL_FROM}>`,
23 | to: process.env.EMAIL_TO,
24 | subject: "Reservation confirmed ✔",
25 | text: message,
26 | html: message,
27 | }));
28 | }
29 |
30 | await Promise.all(emailPromises);
31 |
32 | console.log('All the email were sent successfully');
33 |
34 | return { message: 'Go Serverless v1.0! Your function executed successfully!', event };
35 | };
36 |
--------------------------------------------------------------------------------
/email-notification/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "email-notification",
3 | "version": "1.0.0",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "nodemailer": {
8 | "version": "6.4.17",
9 | "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.4.17.tgz",
10 | "integrity": "sha512-89ps+SBGpo0D4Bi5ZrxcrCiRFaMmkCt+gItMXQGzEtZVR3uAD3QAQIDoxTWnx3ky0Dwwy/dhFrQ+6NNGXpw/qQ=="
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/email-notification/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "email-notification",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "handler.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "keywords": [],
10 | "author": "",
11 | "license": "ISC",
12 | "dependencies": {
13 | "nodemailer": "^6.4.17"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/email-notification/serverless.yml:
--------------------------------------------------------------------------------
1 | service: email-notification
2 |
3 | frameworkVersion: '2'
4 |
5 | provider:
6 | name: aws
7 | runtime: nodejs12.x
8 |
9 | stage: dev
10 | region: us-east-1
11 |
12 | functions:
13 | send_email:
14 | handler: handler.send
15 | role: ${ssm:${self:custom.stage}-email-iam-role}
16 | events:
17 | - sqs: ${ssm:${self:custom.stage}-email-sqs}
18 | environment:
19 | SMTP_SERVER: ${ssm:${self:custom.stage}-smtp-server}
20 | EMAIL_FROM: ${ssm:${self:custom.stage}-email-from}
21 | EMAIL_FROM_PASSWORD: ${ssm:${self:custom.stage}-email-from-password}
22 | EMAIL_TO: ${ssm:${self:custom.stage}-email-to}
23 |
24 | custom:
25 | stage: ${opt:stage, self:provider.stage}
--------------------------------------------------------------------------------
/sms-notification/.gitignore:
--------------------------------------------------------------------------------
1 | # package directories
2 | node_modules
3 | jspm_packages
4 |
5 | # Serverless directories
6 | .serverless
--------------------------------------------------------------------------------
/sms-notification/handler.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const messagebird = require('messagebird')(process.env.MESSAGE_BIRD_API_KEY);
4 |
5 | module.exports.send = async event => {
6 | const smsPromises = [];
7 |
8 | for (let record of event.Records) {
9 | const message = JSON.parse(record.body).Message;
10 |
11 | smsPromises.push(sendMessagePromise(message))
12 | }
13 |
14 | await Promise.all(smsPromises);
15 |
16 | console.log('SMSs sent successfully');
17 |
18 | return {
19 | message: 'Go Serverless v1.0! Your function executed successfully!',
20 | event,
21 | };
22 | };
23 |
24 | const sendMessagePromise = message => {
25 | return new Promise((res, rej) => {
26 | messagebird.messages.create({
27 | originator: process.env.SMS_PHONE_FROM,
28 | recipients: [process.env.SMS_PHONE_TO],
29 | body: message,
30 | }, (err, callback) => {
31 | if (!err) {
32 | console.log(JSON.stringify(callback));
33 | return res({});
34 | }
35 | return (rej(err));
36 | });
37 | });
38 | };
--------------------------------------------------------------------------------
/sms-notification/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sms-notification",
3 | "version": "1.0.0",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "messagebird": {
8 | "version": "3.5.0",
9 | "resolved": "https://registry.npmjs.org/messagebird/-/messagebird-3.5.0.tgz",
10 | "integrity": "sha512-LNzMPr6PuZBHFjDdrAQR5aRad2QHv4C4BcumySyE3DKvsd0k2p8oYX2mFHzk8uMO5KstDJVe6Yz2/bNDADo4SQ==",
11 | "requires": {
12 | "safe-buffer": "^5.1.2",
13 | "scmp": "^2.0.0"
14 | }
15 | },
16 | "safe-buffer": {
17 | "version": "5.2.1",
18 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
19 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
20 | },
21 | "scmp": {
22 | "version": "2.1.0",
23 | "resolved": "https://registry.npmjs.org/scmp/-/scmp-2.1.0.tgz",
24 | "integrity": "sha512-o/mRQGk9Rcer/jEEw/yw4mwo3EU/NvYvp577/Btqrym9Qy5/MdWGBqipbALgd2lrdWTJ5/gqDusxfnQBxOxT2Q=="
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/sms-notification/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sms-notification",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "handler.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "keywords": [],
10 | "author": "",
11 | "license": "ISC",
12 | "dependencies": {
13 | "messagebird": "^3.5.0"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/sms-notification/serverless.yml:
--------------------------------------------------------------------------------
1 | service: sms-notification
2 |
3 | frameworkVersion: '2'
4 |
5 | provider:
6 | name: aws
7 | runtime: nodejs12.x
8 |
9 | stage: dev
10 | region: us-east-1
11 |
12 | functions:
13 | send_sms:
14 | memorySize: ${self:custom.memorySize.${self:custom.stage}}
15 | handler: handler.send
16 | role: ${ssm:${self:custom.stage}-sms-iam-role}
17 | events:
18 | - sqs: ${ssm:${self:custom.stage}-sms-sqs}
19 | environment:
20 | MESSAGE_BIRD_API_KEY: ${ssm:${self:custom.stage}-message-bird-api-key}
21 | SMS_PHONE_FROM: ${ssm:${self:custom.stage}-sms-phone-from}
22 | SMS_PHONE_TO: ${ssm:${self:custom.stage}-sms-phone-to}
23 |
24 | custom:
25 | stage: ${opt:stage, self:provider.stage}
26 | memorySize:
27 | dev: 128
28 | prod: 2048
--------------------------------------------------------------------------------
/terraform/environments/dev/main.tf:
--------------------------------------------------------------------------------
1 | data "aws_caller_identity" "current" {}
2 |
3 | module "users" {
4 | source = "../../infra/users"
5 | environment = var.environment
6 | write_capacity = 1
7 | read_capacity = 1
8 | jwt_secret = var.jwt_secret
9 | admin_id = var.admin_id
10 | admin_name = var.admin_name
11 | admin_password = var.admin_password
12 | admin_email = var.admin_email
13 | }
14 |
15 | module "bookings" {
16 | source = "../../infra/bookings"
17 | environment = var.environment
18 | write_capacity = 1
19 | read_capacity = 1
20 | sns_notifications_arn = module.notifications.notifications_topic_arn
21 | }
22 |
23 | module "notifications" {
24 | source = "../../infra/notifications"
25 | environment = var.environment
26 | account_id = data.aws_caller_identity.current.account_id
27 | region = var.region
28 | }
29 |
30 | module "system" {
31 | source = "../../infra/system"
32 | environment = var.environment
33 | email_from = var.email_from
34 | email_from_password = var.email_from_password
35 | email_to = var.email_to
36 | smtp_server = var.smtp_server
37 | message_bird_api_key = var.message_bird_api_key
38 | sms_phone_from = var.sms_phone_from
39 | sms_phone_to = var.sms_phone_to
40 | }
--------------------------------------------------------------------------------
/terraform/environments/dev/provider.tf:
--------------------------------------------------------------------------------
1 | provider "aws" {
2 | region = var.region
3 | }
--------------------------------------------------------------------------------
/terraform/environments/dev/secrets.auto.tfvars:
--------------------------------------------------------------------------------
1 | jwt_secret = "0YfU3LDpD-7f9hhtY1rmhmTdqCrmoeHRzARBlOjZqME"
2 | admin_id = "d1ad9c81-ed4f-41bc-82a1-d7d966706b52"
3 | admin_email = "admin@email.com"
4 | admin_password = "$2a$10$..k73TdFFOpbIjYqk8AI5eTZYj0ulyq/QyYD22ij3.kEeJ5Mu5OMu" #admin@123
5 | admin_name = "Admin"
6 | email_from = "yourmail@zohomail.com"
7 | email_from_password = "yousecretzohopassword"
8 | email_to = "joxil80150@mmgaklan.com"
9 | smtp_server = "smtp.zoho.com"
10 | message_bird_api_key = "test_messagebird_apikey"
11 | sms_phone_from = "+5511900000000"
12 | sms_phone_to = "+5511900000000"
13 |
--------------------------------------------------------------------------------
/terraform/environments/dev/variables.auto.tfvars:
--------------------------------------------------------------------------------
1 | environment = "dev"
2 | region = "us-east-1"
--------------------------------------------------------------------------------
/terraform/environments/dev/variables.tf:
--------------------------------------------------------------------------------
1 | variable "environment" {
2 |
3 | }
4 |
5 | variable "jwt_secret" {
6 |
7 | }
8 |
9 | variable "admin_id" {
10 |
11 | }
12 |
13 | variable "admin_email" {
14 |
15 | }
16 |
17 | variable "admin_password" {
18 |
19 | }
20 |
21 | variable "admin_name" {
22 |
23 | }
24 |
25 | variable "region" {
26 |
27 | }
28 |
29 | variable "email_from" {
30 |
31 | }
32 |
33 | variable "email_from_password" {
34 |
35 | }
36 |
37 | variable "email_to" {
38 |
39 | }
40 |
41 | variable "smtp_server" {
42 |
43 | }
44 |
45 | variable "message_bird_api_key" {
46 |
47 | }
48 |
49 | variable "sms_phone_from" {
50 |
51 | }
52 |
53 | variable "sms_phone_to" {
54 |
55 | }
--------------------------------------------------------------------------------
/terraform/environments/prod/main.tf:
--------------------------------------------------------------------------------
1 | data "aws_caller_identity" "current" {}
2 |
3 | module "users" {
4 | source = "../../infra/users"
5 | environment = var.environment
6 | write_capacity = 10
7 | read_capacity = 10
8 | jwt_secret = var.jwt_secret
9 | admin_id = var.admin_id
10 | admin_name = var.admin_name
11 | admin_password = var.admin_password
12 | admin_email = var.admin_email
13 | }
14 |
15 | module "bookings" {
16 | source = "../../infra/bookings"
17 | environment = var.environment
18 | write_capacity = 1
19 | read_capacity = 1
20 | sns_notifications_arn = module.notifications.notifications_topic_arn
21 | }
22 |
23 | module "notifications" {
24 | source = "../../infra/notifications"
25 | environment = var.environment
26 | account_id = data.aws_caller_identity.current.account_id
27 | region = var.region
28 | }
29 |
30 | module "system" {
31 | source = "../../infra/system"
32 | environment = var.environment
33 | email_from = var.email_from
34 | email_from_password = var.email_from_password
35 | email_to = var.email_to
36 | smtp_server = var.smtp_server
37 | message_bird_api_key = var.message_bird_api_key
38 | sms_phone_from = var.sms_phone_from
39 | sms_phone_to = var.sms_phone_to
40 | }
--------------------------------------------------------------------------------
/terraform/environments/prod/provider.tf:
--------------------------------------------------------------------------------
1 | provider "aws" {
2 | region = var.region
3 | }
--------------------------------------------------------------------------------
/terraform/environments/prod/secrets.auto.tfvars:
--------------------------------------------------------------------------------
1 | jwt_secret = "f4f8f3BkbRGdsRsRWimxvfS5OJ5hibkTxH5yESDOR3c="
2 | admin_id = "d1ad9c81-ed4f-41bc-82a1-da73faw706b52"
3 | admin_email = "admin@email.com"
4 | admin_password = "$2a$10$brvPOhBESQrOO96VWdQSI.ek3BDslJ9tfiJHhEfGpmxMygYBxxVsq" #adminprod@123
5 | admin_name = "Admin Production"
6 | email_from = "yourmail@zohomail.com"
7 | email_from_password = "mysuperstrongpassword"
8 | email_to = "lacemi1426@nowdigit.com"
9 | smtp_server = "smtp.zoho.com"
10 | message_bird_api_key = "prod_messagebird_apikey"
11 | sms_phone_from = "+5511900000000"
12 | sms_phone_to = "+5511900000000"
13 |
--------------------------------------------------------------------------------
/terraform/environments/prod/variables.auto.tfvars:
--------------------------------------------------------------------------------
1 | environment = "prod"
2 | region = "us-east-1"
--------------------------------------------------------------------------------
/terraform/environments/prod/variables.tf:
--------------------------------------------------------------------------------
1 | variable "environment" {
2 |
3 | }
4 |
5 | variable "jwt_secret" {
6 |
7 | }
8 |
9 | variable "admin_id" {
10 |
11 | }
12 |
13 | variable "admin_email" {
14 |
15 | }
16 |
17 | variable "admin_password" {
18 |
19 | }
20 |
21 | variable "admin_name" {
22 |
23 | }
24 |
25 | variable "region" {
26 |
27 | }
28 |
29 | variable "email_from" {
30 |
31 | }
32 |
33 | variable "email_from_password" {
34 |
35 | }
36 |
37 | variable "email_to" {
38 |
39 | }
40 |
41 | variable "smtp_server" {
42 |
43 | }
44 |
45 | variable "message_bird_api_key" {
46 |
47 | }
48 |
49 | variable "sms_phone_from" {
50 |
51 | }
52 |
53 | variable "sms_phone_to" {
54 |
55 | }
--------------------------------------------------------------------------------
/terraform/infra/bookings/dynamodb-bookings.tf:
--------------------------------------------------------------------------------
1 | resource "aws_dynamodb_table" "bookings" {
2 | name = "${var.environment}-bookings"
3 | hash_key = "id"
4 | attribute {
5 | name = "id"
6 | type = "S"
7 | }
8 | write_capacity = var.write_capacity
9 | read_capacity = var.read_capacity
10 | stream_enabled = true
11 | stream_view_type = "NEW_IMAGE"
12 | }
13 |
14 | resource "aws_ssm_parameter" "dynamodb_bookings_table" {
15 | name = "${var.environment}-dynamodb-bookings-table"
16 | type = "String"
17 | value = aws_dynamodb_table.bookings.name
18 | }
19 |
20 | resource "aws_ssm_parameter" "dynamodb_bookings_stream" {
21 | name = "${var.environment}-dynamodb-bookings-stream"
22 | type = "String"
23 | value = aws_dynamodb_table.bookings.stream_arn
24 | }
--------------------------------------------------------------------------------
/terraform/infra/bookings/iam-policy-attachment-bookings-stream-consumer.tf:
--------------------------------------------------------------------------------
1 | resource "aws_iam_policy_attachment" "bookings_stream_consumer_policy_attachment" {
2 | name = "${var.environment}-bookings-stream-consumer-attachment"
3 | roles = [aws_iam_role.bookings_stream_consumer_iam_role.name]
4 | policy_arn = aws_iam_policy.bookings_stream_consumer_policy.arn
5 | }
--------------------------------------------------------------------------------
/terraform/infra/bookings/iam-policy-attachment-create-booking.tf:
--------------------------------------------------------------------------------
1 | resource "aws_iam_policy_attachment" "create_booking_policy_attachment" {
2 | name = "${var.environment}-create-booking-attachment"
3 | roles = [aws_iam_role.create_booking_iam_role.name]
4 | policy_arn = aws_iam_policy.create_booking_policy.arn
5 | }
--------------------------------------------------------------------------------
/terraform/infra/bookings/iam-policy-attachment-list-bookings.tf:
--------------------------------------------------------------------------------
1 | resource "aws_iam_policy_attachment" "list_bookings_policy_attachment" {
2 | name = "${var.environment}-list-bookings-attachment"
3 | roles = [aws_iam_role.list_bookings_iam_role.name]
4 | policy_arn = aws_iam_policy.list_bookings_policy.arn
5 | }
--------------------------------------------------------------------------------
/terraform/infra/bookings/iam-policy-bookings-stream-consumer.tf:
--------------------------------------------------------------------------------
1 | resource "aws_iam_policy" "bookings_stream_consumer_policy" {
2 | name = "${var.environment}-bookings-stream-consumer-policy"
3 | policy = templatefile("${path.module}/templates/dynamodb-policy.tpl", {
4 | action = join("\",\"", [
5 | "dynamodb:DescribeStream",
6 | "dynamodb:GetRecords",
7 | "dynamodb:GetShardIterator",
8 | "dynamodb:ListStreams"
9 | ]
10 | ),
11 | resource = aws_dynamodb_table.bookings.stream_arn
12 | sns_topic = var.sns_notifications_arn
13 | })
14 | }
--------------------------------------------------------------------------------
/terraform/infra/bookings/iam-policy-create-booking.tf:
--------------------------------------------------------------------------------
1 | resource "aws_iam_policy" "create_booking_policy" {
2 | name = "${var.environment}-create-booking-policy"
3 | policy = templatefile("${path.module}/templates/dynamodb-policy.tpl", {
4 | action = "dynamodb:PutItem",
5 | resource = aws_dynamodb_table.bookings.arn,
6 | sns_topic = ""
7 | })
8 | }
--------------------------------------------------------------------------------
/terraform/infra/bookings/iam-policy-list-bookings.tf:
--------------------------------------------------------------------------------
1 | resource "aws_iam_policy" "list_bookings_policy" {
2 | name = "${var.environment}-list-bookings-policy"
3 | policy = templatefile("${path.module}/templates/dynamodb-policy.tpl", {
4 | action = "dynamodb:Scan",
5 | resource = aws_dynamodb_table.bookings.arn,
6 | sns_topic = ""
7 | })
8 | }
--------------------------------------------------------------------------------
/terraform/infra/bookings/iam-role-bookings-stream-consumer.tf:
--------------------------------------------------------------------------------
1 | resource "aws_iam_role" "bookings_stream_consumer_iam_role" {
2 | name = "${var.environment}-bookings-stream-consumer-iam-role"
3 |
4 | assume_role_policy = templatefile("${path.module}/templates/lambda-base-policy.tpl", {})
5 | }
6 |
7 | resource "aws_ssm_parameter" "bookings_stream_consumer_iam_role" {
8 | name = "${var.environment}-bookings-stream-consumer-iam-role"
9 | type = "String"
10 | value = aws_iam_role.bookings_stream_consumer_iam_role.arn
11 | }
--------------------------------------------------------------------------------
/terraform/infra/bookings/iam-role-create-booking.tf:
--------------------------------------------------------------------------------
1 | resource "aws_iam_role" "create_booking_iam_role" {
2 | name = "${var.environment}-create-booking-iam-role"
3 |
4 | assume_role_policy = templatefile("${path.module}/templates/lambda-base-policy.tpl", {})
5 | }
6 |
7 | resource "aws_ssm_parameter" "create_booking_iam_role" {
8 | name = "${var.environment}-create-booking-iam-role"
9 | type = "String"
10 | value = aws_iam_role.create_booking_iam_role.arn
11 | }
--------------------------------------------------------------------------------
/terraform/infra/bookings/iam-role-list-bookings.tf:
--------------------------------------------------------------------------------
1 | resource "aws_iam_role" "list_bookings_iam_role" {
2 | name = "${var.environment}-list-bookings-iam-role"
3 |
4 | assume_role_policy = templatefile("${path.module}/templates/lambda-base-policy.tpl", {})
5 | }
6 |
7 | resource "aws_ssm_parameter" "list-bookings_iam_role" {
8 | name = "${var.environment}-list-bookings-iam-role"
9 | type = "String"
10 | value = aws_iam_role.list_bookings_iam_role.arn
11 | }
--------------------------------------------------------------------------------
/terraform/infra/bookings/templates/dynamodb-policy.tpl:
--------------------------------------------------------------------------------
1 | {
2 | "Version": "2012-10-17",
3 | "Statement": [
4 | {
5 | "Action": [
6 | "${action}"
7 | ],
8 | "Effect": "Allow",
9 | "Resource": "${resource}"
10 | },
11 | {
12 | "Effect": "Allow",
13 | "Action": [
14 | "logs:CreateLogGroup",
15 | "logs:CreateLogStream",
16 | "logs:PutLogEvents"
17 | ],
18 | "Resource": "*"
19 | }
20 | %{ if sns_topic != "" }
21 | , {
22 | "Effect": "Allow",
23 | "Action": [
24 | "sns:Publish"
25 | ],
26 | "Resource": "${sns_topic}"
27 | }
28 | %{ endif }
29 | ]
30 | }
--------------------------------------------------------------------------------
/terraform/infra/bookings/templates/lambda-base-policy.tpl:
--------------------------------------------------------------------------------
1 | {
2 | "Version": "2012-10-17",
3 | "Statement": [
4 | {
5 | "Action": "sts:AssumeRole",
6 | "Principal": {
7 | "Service": "lambda.amazonaws.com"
8 | },
9 | "Effect": "Allow",
10 | "Sid": ""
11 | }
12 | ]
13 | }
--------------------------------------------------------------------------------
/terraform/infra/bookings/variables.tf:
--------------------------------------------------------------------------------
1 | variable "environment" {
2 |
3 | }
4 |
5 | variable "write_capacity" {
6 |
7 | }
8 |
9 | variable "read_capacity" {
10 |
11 | }
12 |
13 | variable "sns_notifications_arn" {
14 |
15 | }
--------------------------------------------------------------------------------
/terraform/infra/notifications/iam-policy-attachment-email.tf:
--------------------------------------------------------------------------------
1 | resource "aws_iam_policy_attachment" "email_policy_attachment" {
2 | name = "${var.environment}-email-attachment"
3 | roles = [aws_iam_role.email_iam_role.name]
4 | policy_arn = aws_iam_policy.email_policy.arn
5 | }
--------------------------------------------------------------------------------
/terraform/infra/notifications/iam-policy-attachment-sms.tf:
--------------------------------------------------------------------------------
1 | resource "aws_iam_policy_attachment" "sms_policy_attachment" {
2 | name = "${var.environment}-sms-attachment"
3 | roles = [aws_iam_role.sms_iam_role.name]
4 | policy_arn = aws_iam_policy.sms_policy.arn
5 | }
--------------------------------------------------------------------------------
/terraform/infra/notifications/iam-policy-email.tf:
--------------------------------------------------------------------------------
1 | resource "aws_iam_policy" "email_policy" {
2 | name = "${var.environment}-email-policy"
3 | policy = templatefile("${path.module}/templates/lambda-sqs-policy.tpl", {
4 | action = join("\",\"", [
5 | "sqs:ReceiveMessage",
6 | "sqs:DeleteMessage",
7 | "sqs:GetQueueAttributes"
8 | ]
9 | ),
10 | resource = aws_sqs_queue.email.arn
11 | })
12 | }
--------------------------------------------------------------------------------
/terraform/infra/notifications/iam-policy-sms.tf:
--------------------------------------------------------------------------------
1 | resource "aws_iam_policy" "sms_policy" {
2 | name = "${var.environment}-sms-policy"
3 | policy = templatefile("${path.module}/templates/lambda-sqs-policy.tpl", {
4 | action = join("\",\"", [
5 | "sqs:ReceiveMessage",
6 | "sqs:DeleteMessage",
7 | "sqs:GetQueueAttributes"
8 | ]
9 | ),
10 | resource = aws_sqs_queue.sms.arn
11 | })
12 | }
--------------------------------------------------------------------------------
/terraform/infra/notifications/iam-role-email.tf:
--------------------------------------------------------------------------------
1 | resource "aws_iam_role" "email_iam_role" {
2 | name = "${var.environment}-email-iam-role"
3 |
4 | assume_role_policy = templatefile("${path.module}/templates/lambda-base-policy.tpl", {})
5 | }
6 |
7 | resource "aws_ssm_parameter" "email_iam_role" {
8 | name = "${var.environment}-email-iam-role"
9 | type = "String"
10 | value = aws_iam_role.email_iam_role.arn
11 | }
--------------------------------------------------------------------------------
/terraform/infra/notifications/iam-role-sms.tf:
--------------------------------------------------------------------------------
1 | resource "aws_iam_role" "sms_iam_role" {
2 | name = "${var.environment}-sms-iam-role"
3 |
4 | assume_role_policy = templatefile("${path.module}/templates/lambda-base-policy.tpl", {})
5 | }
6 |
7 | resource "aws_ssm_parameter" "sms_iam_role" {
8 | name = "${var.environment}-sms-iam-role"
9 | type = "String"
10 | value = aws_iam_role.sms_iam_role.arn
11 | }
--------------------------------------------------------------------------------
/terraform/infra/notifications/sns-subscriptions.tf:
--------------------------------------------------------------------------------
1 | resource "aws_sns_topic_subscription" "email_subscription" {
2 | topic_arn = aws_sns_topic.notifications.arn
3 | protocol = "sqs"
4 | endpoint = aws_sqs_queue.email.arn
5 | }
6 |
7 | resource "aws_sns_topic_subscription" "sms_subscription" {
8 | topic_arn = aws_sns_topic.notifications.arn
9 | protocol = "sqs"
10 | endpoint = aws_sqs_queue.sms.arn
11 | }
--------------------------------------------------------------------------------
/terraform/infra/notifications/sns.tf:
--------------------------------------------------------------------------------
1 | resource "aws_sns_topic" "notifications" {
2 | name = "${var.environment}-notifications"
3 | }
4 |
5 | resource "aws_ssm_parameter" "notifications_topic" {
6 | name = "${var.environment}-notifications-topic"
7 | type = "String"
8 | value = aws_sns_topic.notifications.arn
9 | }
10 |
11 | output "notifications_topic_arn" {
12 | value = aws_sns_topic.notifications.arn
13 | }
--------------------------------------------------------------------------------
/terraform/infra/notifications/sqs-email.tf:
--------------------------------------------------------------------------------
1 | resource "aws_sqs_queue" "email" {
2 | name = "${var.environment}-email-queue"
3 | redrive_policy = jsonencode({
4 | deadLetterTargetArn = aws_sqs_queue.email_dlq.arn
5 | maxReceiveCount = 3
6 | })
7 | policy = templatefile("${path.module}/templates/sqs-sns-policy.tpl", {
8 | resource = "arn:aws:sqs:${var.region}:${var.account_id}:${var.environment}-email-queue"
9 | source_arn = aws_sns_topic.notifications.arn
10 | })
11 | }
12 |
13 | resource "aws_ssm_parameter" "email_sqs" {
14 | name = "${var.environment}-email-sqs"
15 | type = "String"
16 | value = aws_sqs_queue.email.arn
17 | }
18 |
19 | resource "aws_sqs_queue" "email_dlq" {
20 | name = "${var.environment}-email-queue-dlq"
21 | }
--------------------------------------------------------------------------------
/terraform/infra/notifications/sqs-sms.tf:
--------------------------------------------------------------------------------
1 | resource "aws_sqs_queue" "sms" {
2 | name = "${var.environment}-sms-queue"
3 | redrive_policy = jsonencode({
4 | deadLetterTargetArn = aws_sqs_queue.sms_dlq.arn
5 | maxReceiveCount = 3
6 | })
7 | policy = templatefile("${path.module}/templates/sqs-sns-policy.tpl", {
8 | resource = "arn:aws:sqs:${var.region}:${var.account_id}:${var.environment}-sms-queue"
9 | source_arn = aws_sns_topic.notifications.arn
10 | })
11 | }
12 |
13 | resource "aws_ssm_parameter" "sms_sqs" {
14 | name = "${var.environment}-sms-sqs"
15 | type = "String"
16 | value = aws_sqs_queue.sms.arn
17 | }
18 |
19 | resource "aws_sqs_queue" "sms_dlq" {
20 | name = "${var.environment}-sms-queue-dlq"
21 | }
--------------------------------------------------------------------------------
/terraform/infra/notifications/templates/lambda-base-policy.tpl:
--------------------------------------------------------------------------------
1 | {
2 | "Version": "2012-10-17",
3 | "Statement": [
4 | {
5 | "Action": "sts:AssumeRole",
6 | "Principal": {
7 | "Service": "lambda.amazonaws.com"
8 | },
9 | "Effect": "Allow",
10 | "Sid": ""
11 | }
12 | ]
13 | }
--------------------------------------------------------------------------------
/terraform/infra/notifications/templates/lambda-sqs-policy.tpl:
--------------------------------------------------------------------------------
1 | {
2 | "Version": "2012-10-17",
3 | "Statement": [
4 | {
5 | "Action": [
6 | "${action}"
7 | ],
8 | "Effect": "Allow",
9 | "Resource": "${resource}"
10 | },
11 | {
12 | "Effect": "Allow",
13 | "Action": [
14 | "logs:CreateLogGroup",
15 | "logs:CreateLogStream",
16 | "logs:PutLogEvents"
17 | ],
18 | "Resource": "*"
19 | }
20 | ]
21 | }
--------------------------------------------------------------------------------
/terraform/infra/notifications/templates/sqs-sns-policy.tpl:
--------------------------------------------------------------------------------
1 | {
2 | "Version": "2012-10-17",
3 | "Statement": [
4 | {
5 | "Effect": "Allow",
6 | "Principal": "*",
7 | "Action": "sqs:SendMessage",
8 | "Resource": "${resource}",
9 | "Condition": {
10 | "ArnEquals": {
11 | "aws:SourceArn": "${source_arn}"
12 | }
13 | }
14 | }
15 | ]
16 | }
--------------------------------------------------------------------------------
/terraform/infra/notifications/variables.tf:
--------------------------------------------------------------------------------
1 | variable "environment" {
2 |
3 | }
4 |
5 | variable "account_id" {
6 |
7 | }
8 |
9 | variable "region" {
10 |
11 | }
--------------------------------------------------------------------------------
/terraform/infra/system/ssm.tf:
--------------------------------------------------------------------------------
1 | resource "aws_ssm_parameter" "email_from" {
2 | name = "${var.environment}-email-from"
3 | type = "String"
4 | value = var.email_from
5 | }
6 |
7 | resource "aws_ssm_parameter" "email_from_password" {
8 | name = "${var.environment}-email-from-password"
9 | type = "String"
10 | value = var.email_from_password
11 | }
12 |
13 | resource "aws_ssm_parameter" "email_to" {
14 | name = "${var.environment}-email-to"
15 | type = "String"
16 | value = var.email_to
17 | }
18 |
19 | resource "aws_ssm_parameter" "smtp_server" {
20 | name = "${var.environment}-smtp-server"
21 | type = "String"
22 | value = var.smtp_server
23 | }
24 |
25 | resource "aws_ssm_parameter" "message_bird_api_key" {
26 | name = "${var.environment}-message-bird-api-key"
27 | type = "String"
28 | value = var.message_bird_api_key
29 | }
30 |
31 | resource "aws_ssm_parameter" "sms_phone_from" {
32 | name = "${var.environment}-sms-phone-from"
33 | type = "String"
34 | value = var.sms_phone_from
35 | }
36 |
37 | resource "aws_ssm_parameter" "sms_phone_to" {
38 | name = "${var.environment}-sms-phone-to"
39 | type = "String"
40 | value = var.sms_phone_to
41 | }
--------------------------------------------------------------------------------
/terraform/infra/system/variables.tf:
--------------------------------------------------------------------------------
1 | variable "environment" {
2 |
3 | }
4 |
5 | variable "email_from" {
6 |
7 | }
8 |
9 | variable "email_from_password" {
10 |
11 | }
12 |
13 | variable "email_to" {
14 |
15 | }
16 |
17 | variable "smtp_server" {
18 |
19 | }
20 |
21 | variable "message_bird_api_key" {
22 |
23 | }
24 |
25 | variable "sms_phone_from" {
26 |
27 | }
28 |
29 | variable "sms_phone_to" {
30 |
31 | }
--------------------------------------------------------------------------------
/terraform/infra/users/dynamodb-users.tf:
--------------------------------------------------------------------------------
1 | resource "aws_dynamodb_table" "users" {
2 | name = "${var.environment}-users"
3 | hash_key = "id"
4 | attribute {
5 | name = "id"
6 | type = "S"
7 | }
8 | write_capacity = var.write_capacity
9 | read_capacity = var.read_capacity
10 |
11 | attribute {
12 | name = "email"
13 | type = "S"
14 | }
15 |
16 | global_secondary_index {
17 | name = "${var.environment}-email-gsi"
18 | projection_type = "ALL"
19 | hash_key = "email"
20 | write_capacity = var.write_capacity
21 | read_capacity = var.read_capacity
22 | }
23 | }
24 |
25 | resource "aws_dynamodb_table_item" "admin" {
26 | table_name = aws_dynamodb_table.users.name
27 | hash_key = aws_dynamodb_table.users.hash_key
28 |
29 | item = <