Skip to content

Commit

Permalink
Initial Commit. Implemented send mail function.
Browse files Browse the repository at this point in the history
  • Loading branch information
jnpco98 committed Jun 22, 2020
0 parents commit e8fb0f2
Show file tree
Hide file tree
Showing 8 changed files with 2,674 additions and 0 deletions.
16 changes: 16 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
.idea/
.vscode/
.vs/
.next/
node_modules/
jspm_packages
build/
.build/
dist/
tmp/
temp/
logs/
.env
out/
.serverless
.netlify
20 changes: 20 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"scripts": {
"dev": "serverless offline start --printOutput",
"deploy:dev": "serverless deploy --stage dev function --function",
"deploy:prod": "serverless deploy --stage production function --function",
"full-deploy:dev": "serverless deploy --stage dev",
"full-deploy:prod": "serverless deploy --stage production"
},
"devDependencies": {
"@types/aws-lambda": "^8.10.56",
"@types/nodemailer": "^6.4.0",
"aws-sdk": "^2.701.0",
"serverless-offline": "^6.4.0",
"serverless-plugin-typescript": "^1.1.9",
"typescript": "^3.9.5"
},
"dependencies": {
"nodemailer": "^6.4.10"
}
}
31 changes: 31 additions & 0 deletions serverless.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
service: portfolio-mail-service

provider:
name: aws
runtime: nodejs12.x
stage: dev
region: ${self:custom.region}
stackTags:
STACK: ${self:service}
REGION: ${self:custom.region}
profile: serverless-admin

custom:
region: us-east-1
serverless-offline:
useChildProcesses: true

plugins:
- serverless-plugin-typescript
- serverless-offline

functions:
ascii-text:
handler: src/lambdas/endpoints/send-mail.handler
memorySize: 256
timeout: 30
events:
- http:
path: send-mail
method: POST
cors: true
61 changes: 61 additions & 0 deletions src/lambdas/endpoints/send-mail.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import AWS from 'aws-sdk';
import { APIGatewayEvent, Context } from 'aws-lambda';
import nodemailer from 'nodemailer';
import Mail from 'nodemailer/lib/mailer';

import { Response, createResponse } from '../utilities';
import { createMailOutput } from '../output';

interface SendConfig {
companyName: string;
responseBot: string;
receiver: string;
}

interface MessageConfig {
firstname: string;
lastname: string;
email: string;
message: string;
subject: string;
}

interface RequestBody {
sendConfig: SendConfig;
messageConfig: MessageConfig;
}

const DEFAULT_SUCCESS_MSG = 'Message has been sent.';
const DEFAULT_ERROR_PARAM_MSG = 'Invalid parameters.';
const DEFAULT_ERROR_MSG = 'Something went wrong with your email. Please try again later.';

const transporter = nodemailer.createTransport({
SES: new AWS.SES({ apiVersion: '2010-12-01' })
});

export async function handler(event: APIGatewayEvent, context: Context): Promise<Response> {
const body = JSON.parse(event.body || '{}') as RequestBody;
const { sendConfig, messageConfig } = body;

if(!sendConfig || !messageConfig) return createResponse(422, { error: DEFAULT_ERROR_PARAM_MSG });

const { companyName = 'N/A', receiver, responseBot = 'DEFAULT_BOT' } = sendConfig;
const { email = 'N/A', firstname = 'N/A', lastname = 'N/A', message = 'Empty message', subject = `[Contact Request] ${email}` } = messageConfig;

if(!receiver) return createResponse(422, { error: DEFAULT_ERROR_PARAM_MSG });

const mailOptions: Mail.Options = {
from: `${companyName} ${responseBot}`,
to: receiver,
subject,
html: createMailOutput({ email, message, name: `${firstname} ${lastname}` })
};

try {
const data = await transporter.sendMail(mailOptions);
return createResponse(200, { message: DEFAULT_SUCCESS_MSG, data });
} catch (e) {
console.error(e);
return createResponse(422, { error: DEFAULT_ERROR_MSG });
}
}
20 changes: 20 additions & 0 deletions src/lambdas/output.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
const DEFAULT_OUTPUT = (name: string, email: string, message: string) =>
`<p>You have a new contact request</p><h3>Contact Details</h3> <ul> <li>Name: ${name}</li><li>Email: ${email}</li></ul> <h3>Message</h3> <p>${message}</p>`;

type MailOutputType = 'default';

interface MailParams {
name: string;
email: string;
message: string;
}

export function createMailOutput(params: MailParams, type?: MailOutputType) {
const { name, email, message } = params;

switch(type) {
default:
case 'default':
return DEFAULT_OUTPUT(name, email, message);
}
}
24 changes: 24 additions & 0 deletions src/lambdas/utilities.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
export interface Response {
statusCode: number;
body: string;
headers?: { [name: string]: string | boolean };
}

export type ResponseCode = 200 | 400 | 401 | 403 | 404 | 422 | 500 | 502 | 504;

export const DEFAULT_HEADERS = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Credentials': true,
};

export function createResponse(
code: ResponseCode,
body: { [key: string]: any },
headers?: { [key: string]: string | boolean }
): Response {
return {
body: JSON.stringify(body),
headers: { ...DEFAULT_HEADERS, ...headers },
statusCode: code,
};
}
29 changes: 29 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"compilerOptions": {
"target": "ES5",
"lib": ["ES2015"],
"sourceMap": true,
"outDir": "./build",
"moduleResolution": "node",
"preserveConstEnums": true,
"removeComments": true,
"esModuleInterop": true,
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"noImplicitThis": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"allowSyntheticDefaultImports": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"plugins": [
{
"name": "typescript-tslint-plugin"
}
]
},
"exclude": ["node_modules", ".vscode"],
"include": ["./src/**/*.ts"],
"typeRoots": ["./src/@types/", "./node_modules/@types"]
}
Loading

0 comments on commit e8fb0f2

Please sign in to comment.