Updates and added SES client.
This commit is contained in:
parent
ae4c215e5e
commit
a4a4f55d08
@ -3,7 +3,7 @@ import * as cdk from 'aws-cdk-lib/core';
|
||||
import { JbApiAwsStack } from '../lib/jb-api-aws-stack';
|
||||
|
||||
const app = new cdk.App();
|
||||
new JbApiAwsStack(app, 'JbApiAwsStack', {
|
||||
new JbApiAwsStack(app, 'JbApi', {
|
||||
/* For more information, see https://docs.aws.amazon.com/cdk/latest/guide/environments.html */
|
||||
env: {
|
||||
account: process.env.CDK_DEFAULT_ACCOUNT,
|
||||
|
||||
@ -1,10 +1,19 @@
|
||||
import * as cdk from 'aws-cdk-lib/core';
|
||||
import { Construct } from 'constructs';
|
||||
import { ContactConstruct } from './lambda/contact/ContactConstruct';
|
||||
import { DomainName } from 'aws-cdk-lib/aws-apigateway';
|
||||
|
||||
export class JbApiAwsStack extends cdk.Stack {
|
||||
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
|
||||
super(scope, id, props);
|
||||
new ContactConstruct(this, id);
|
||||
|
||||
const apiDomainName = DomainName.fromDomainNameAttributes(this, id, {
|
||||
domainName: 'api.jessebrault.com',
|
||||
domainNameAliasTarget:
|
||||
'd-fax16c4l5l.execute-api.us-east-2.amazonaws.com',
|
||||
domainNameAliasHostedZoneId: 'ZOJJZC49E0EPZ'
|
||||
});
|
||||
|
||||
new ContactConstruct(this, 'ContactConstruct', { apiDomainName });
|
||||
}
|
||||
}
|
||||
|
||||
132
lib/lambda/contact/ContactConstruct.Contact.ts
Normal file
132
lib/lambda/contact/ContactConstruct.Contact.ts
Normal file
@ -0,0 +1,132 @@
|
||||
import {
|
||||
APIGatewayProxyEvent,
|
||||
APIGatewayProxyResult,
|
||||
Context
|
||||
} from 'aws-lambda';
|
||||
import {
|
||||
SendEmailCommand,
|
||||
SESClient,
|
||||
SESClientConfig
|
||||
} from '@aws-sdk/client-ses';
|
||||
|
||||
interface ContactRequest {
|
||||
name: string;
|
||||
email: string;
|
||||
institution: string;
|
||||
message: string;
|
||||
}
|
||||
|
||||
interface ValidationError {
|
||||
field: keyof ContactRequest;
|
||||
message: string;
|
||||
}
|
||||
|
||||
interface ValidationErrorsResponse {
|
||||
errors: ValidationError[];
|
||||
}
|
||||
|
||||
const sesClient = new SESClient({
|
||||
region: 'us-east-2'
|
||||
} satisfies SESClientConfig);
|
||||
|
||||
export async function handler(
|
||||
event: APIGatewayProxyEvent,
|
||||
context: Context
|
||||
): Promise<APIGatewayProxyResult> {
|
||||
if (event.body === null) {
|
||||
return {
|
||||
statusCode: 400,
|
||||
body: JSON.stringify({
|
||||
message: 'ContactRequest body required.'
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
const { name, email, institution, message } = JSON.parse(
|
||||
event.body
|
||||
) as ContactRequest;
|
||||
const errors: ValidationError[] = [];
|
||||
|
||||
// name
|
||||
if (!name) {
|
||||
errors.push({
|
||||
field: 'name',
|
||||
message: 'Name is required.'
|
||||
});
|
||||
} else if (name.trim().length === 0) {
|
||||
errors.push({
|
||||
field: 'name',
|
||||
message: 'Name may not be blank.'
|
||||
});
|
||||
}
|
||||
|
||||
// email
|
||||
if (!email) {
|
||||
errors.push({
|
||||
field: 'email',
|
||||
message: 'Email is required.'
|
||||
});
|
||||
} else if (email.trim().length === 0) {
|
||||
errors.push({
|
||||
field: 'email',
|
||||
message: 'Email may not be blank.'
|
||||
});
|
||||
}
|
||||
|
||||
// message
|
||||
if (!message) {
|
||||
errors.push({
|
||||
field: 'message',
|
||||
message: 'Message is required.'
|
||||
});
|
||||
} else if (message.trim().length === 0) {
|
||||
errors.push({
|
||||
field: 'message',
|
||||
message: 'Message may not be blank.'
|
||||
});
|
||||
}
|
||||
|
||||
if (errors.length) {
|
||||
return {
|
||||
statusCode: 400,
|
||||
headers: {
|
||||
'Content-type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ errors } satisfies ValidationErrorsResponse)
|
||||
};
|
||||
} else {
|
||||
const sendEmailCommand = new SendEmailCommand({
|
||||
Source: 'noreply@api.jessebrault.com',
|
||||
Destination: {
|
||||
ToAddresses: ['jesse@jessebrault.com']
|
||||
},
|
||||
Message: {
|
||||
Subject: {
|
||||
Charset: 'UTF-8',
|
||||
Data: 'Contact request'
|
||||
},
|
||||
Body: {
|
||||
Text: {
|
||||
Charset: 'utf-8',
|
||||
Data: `
|
||||
Contact Request from jessebrault.com
|
||||
From ${name}
|
||||
Email ${email}
|
||||
Institution ${institution ?? '<none>'}
|
||||
--- Message ---
|
||||
|
||||
${message}
|
||||
`.trim()
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
await sesClient.send(sendEmailCommand);
|
||||
return {
|
||||
statusCode: 200,
|
||||
body: JSON.stringify({
|
||||
message: 'Success'
|
||||
})
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -1,16 +0,0 @@
|
||||
import { APIGatewayEvent, Context, APIGatewayProxyResult } from 'aws-lambda';
|
||||
|
||||
export async function handler(
|
||||
event: APIGatewayEvent,
|
||||
context: Context
|
||||
): Promise<APIGatewayProxyResult> {
|
||||
return {
|
||||
statusCode: 200,
|
||||
headers: {
|
||||
'Content-type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
message: 'Hello, World!'
|
||||
})
|
||||
};
|
||||
}
|
||||
@ -1,22 +1,38 @@
|
||||
import { Construct } from 'constructs';
|
||||
import * as nodejs from 'aws-cdk-lib/aws-lambda-nodejs';
|
||||
import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs';
|
||||
import * as lambda from 'aws-cdk-lib/aws-lambda';
|
||||
import * as apigateway from 'aws-cdk-lib/aws-apigateway';
|
||||
import {
|
||||
BasePathMapping,
|
||||
IDomainName,
|
||||
LambdaIntegration,
|
||||
RestApi
|
||||
} from 'aws-cdk-lib/aws-apigateway';
|
||||
|
||||
export interface ContactConstructProps {
|
||||
apiDomainName: IDomainName;
|
||||
}
|
||||
|
||||
export class ContactConstruct extends Construct {
|
||||
constructor(scope: Construct, id: string) {
|
||||
constructor(scope: Construct, id: string, props: ContactConstructProps) {
|
||||
super(scope, id);
|
||||
|
||||
const contactLambda = new nodejs.NodejsFunction(this, 'contact', {
|
||||
const contactServiceRestApi = new RestApi(this, 'ContactService', {
|
||||
description: 'The ContactService rest api.'
|
||||
});
|
||||
new BasePathMapping(this, 'ContactServiceMapping', {
|
||||
domainName: props.apiDomainName,
|
||||
restApi: contactServiceRestApi,
|
||||
basePath: 'contact'
|
||||
});
|
||||
|
||||
// contact endpoint
|
||||
const contactLambda = new NodejsFunction(this, 'Contact', {
|
||||
description: 'The lambda for the contact endpoint.',
|
||||
runtime: lambda.Runtime.NODEJS_24_X
|
||||
});
|
||||
|
||||
const api = new apigateway.LambdaRestApi(this, 'ContactApi', {
|
||||
handler: contactLambda,
|
||||
proxy: false
|
||||
});
|
||||
|
||||
const contactResource = api.root.addResource('contact');
|
||||
contactResource.addMethod('GET');
|
||||
const contactIntegration = new LambdaIntegration(contactLambda);
|
||||
const contactResource =
|
||||
contactServiceRestApi.root.addResource('contact');
|
||||
contactResource.addMethod('POST', contactIntegration);
|
||||
}
|
||||
}
|
||||
|
||||
1288
package-lock.json
generated
1288
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -23,6 +23,7 @@
|
||||
"typescript": "~5.9.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-ses": "^3.966.0",
|
||||
"aws-cdk-lib": "^2.232.2",
|
||||
"constructs": "^10.0.0"
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user