From bd01adda1405e06945a8eb6085109a8529ba5df3 Mon Sep 17 00:00:00 2001 From: Keith Vaughan Date: Sun, 22 Dec 2024 15:20:15 +0700 Subject: [PATCH] Fixing contact form 9 --- cipher-infra/lambda/contact-form/handler.ts | 70 ++++++------- cipher-infra/lib/contact-form-stack.ts | 108 ++------------------ 2 files changed, 42 insertions(+), 136 deletions(-) diff --git a/cipher-infra/lambda/contact-form/handler.ts b/cipher-infra/lambda/contact-form/handler.ts index 2225128..30a6dee 100644 --- a/cipher-infra/lambda/contact-form/handler.ts +++ b/cipher-infra/lambda/contact-form/handler.ts @@ -1,5 +1,5 @@ import { SESClient, SendEmailCommand } from "@aws-sdk/client-ses"; -import { APIGatewayEvent } from 'aws-lambda'; +import { APIGatewayEvent, APIGatewayProxyResult } from 'aws-lambda'; const ses = new SESClient({ region: "ap-southeast-2" }); @@ -11,29 +11,29 @@ const corsHeaders = { 'Content-Type': 'application/json' }; -export const handler = async (event: APIGatewayEvent) => { - // Handle OPTIONS request +const createResponse = (statusCode: number, body: any): APIGatewayProxyResult => ({ + statusCode, + headers: corsHeaders, + body: JSON.stringify(body) +}); + +export const handler = async (event: APIGatewayEvent): Promise => { + console.log('Event:', JSON.stringify(event, null, 2)); + if (event.httpMethod === 'OPTIONS') { - return { - statusCode: 200, - headers: corsHeaders, - body: '' - }; + return createResponse(200, {}); } - console.log('Event received:', JSON.stringify(event, null, 2)); - try { - const body = JSON.parse(event.body || '{}'); + if (!event.body) { + return createResponse(400, { error: 'Missing request body' }); + } + + const body = JSON.parse(event.body); console.log('Parsed body:', body); - // Input validation if (!body.email || !body.message) { - return { - statusCode: 400, - headers: corsHeaders, - body: JSON.stringify({ error: 'Email and message are required' }), - }; + return createResponse(400, { error: 'Email and message are required' }); } const params = { @@ -65,32 +65,24 @@ Submitted at: ${new Date().toISOString()} try { const command = new SendEmailCommand(params); - const result = await ses.send(command); - console.log('Email sent successfully:', result); + await ses.send(command); + return createResponse(200, { + message: 'Message sent successfully', + success: true + }); } catch (sesError) { console.error('SES Error:', sesError); - throw new Error('Failed to send email'); + return createResponse(500, { + error: 'Failed to send email', + success: false + }); } - - return { - statusCode: 200, - headers: corsHeaders, - body: JSON.stringify({ - message: 'Email sent successfully', - success: true - }), - }; - } catch (error) { console.error('Error:', error); - return { - statusCode: 500, - headers: corsHeaders, - body: JSON.stringify({ - error: 'Failed to process request', - success: false, - details: error instanceof Error ? error.message : 'Unknown error' - }), - }; + return createResponse(500, { + error: 'Failed to process request', + success: false, + details: error instanceof Error ? error.message : 'Unknown error' + }); } }; \ No newline at end of file diff --git a/cipher-infra/lib/contact-form-stack.ts b/cipher-infra/lib/contact-form-stack.ts index 4971bcb..ec03232 100644 --- a/cipher-infra/lib/contact-form-stack.ts +++ b/cipher-infra/lib/contact-form-stack.ts @@ -9,7 +9,7 @@ export class ContactFormStack extends cdk.Stack { constructor(scope: Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); - // Create the Lambda function + // Lambda function const contactFormLambda = new lambda.Function(this, 'ContactFormFunction', { runtime: lambda.Runtime.NODEJS_18_X, handler: 'index.handler', @@ -17,11 +17,11 @@ export class ContactFormStack extends cdk.Stack { timeout: cdk.Duration.seconds(30), environment: { NODE_ENV: 'production', - VERSION: '1.0.2', // Increment version to force update + VERSION: '1.0.3', // Increment version to force update }, }); - // Add SES permissions to Lambda + // SES permissions contactFormLambda.addToRolePolicy( new iam.PolicyStatement({ effect: iam.Effect.ALLOW, @@ -30,7 +30,7 @@ export class ContactFormStack extends cdk.Stack { }) ); - // Create API Gateway + // API Gateway const api = new apigateway.RestApi(this, 'ContactFormApi', { restApiName: 'Contact Form API', description: 'API for handling contact form submissions', @@ -48,15 +48,13 @@ export class ContactFormStack extends cdk.Stack { }, }); - // Create API Key - remove the enabled property + // API Key const apiKey = api.addApiKey('ContactFormApiKey', { apiKeyName: `contact-form-key-${Date.now()}`, description: 'API Key for Contact Form' }); - - - // Create Usage Plan + // Usage Plan const usagePlan = api.addUsagePlan('ContactFormUsagePlan', { name: `ContactFormUsagePlan-${Date.now()}`, description: 'Usage plan for the contact form API', @@ -74,106 +72,22 @@ export class ContactFormStack extends cdk.Stack { }, }); - // Associate API key with usage plan usagePlan.addApiKey(apiKey); - // Create contact resource + // Contact Resource const contact = api.root.addResource('contact'); - // Create integration response model - const errorResponseModel = api.addModel('ErrorResponseModel', { - contentType: 'application/json', - modelName: 'ErrorResponse', - schema: { - schema: apigateway.JsonSchemaVersion.DRAFT4, - title: 'errorResponse', - type: apigateway.JsonSchemaType.OBJECT, - properties: { - message: { type: apigateway.JsonSchemaType.STRING } - } - } - }); - - // Add POST method with integration + // Integration const integration = new apigateway.LambdaIntegration(contactFormLambda, { - proxy: false, - integrationResponses: [ - { - statusCode: '200', - responseParameters: { - 'method.response.header.Access-Control-Allow-Origin': "'https://www.cipherprojects.com'", - 'method.response.header.Access-Control-Allow-Headers': "'Content-Type,X-Api-Key'", - 'method.response.header.Access-Control-Allow-Methods': "'OPTIONS,POST'", - 'method.response.header.Access-Control-Allow-Credentials': "'true'" - } - }, - { - selectionPattern: '.*"statusCode":400.*', - statusCode: '400', - responseParameters: { - 'method.response.header.Access-Control-Allow-Origin': "'https://www.cipherprojects.com'", - 'method.response.header.Access-Control-Allow-Headers': "'Content-Type,X-Api-Key'", - 'method.response.header.Access-Control-Allow-Methods': "'OPTIONS,POST'", - 'method.response.header.Access-Control-Allow-Credentials': "'true'" - } - }, - { - selectionPattern: '.*"statusCode":403.*', - statusCode: '403', - responseParameters: { - 'method.response.header.Access-Control-Allow-Origin': "'https://www.cipherprojects.com'", - 'method.response.header.Access-Control-Allow-Headers': "'Content-Type,X-Api-Key'", - 'method.response.header.Access-Control-Allow-Methods': "'OPTIONS,POST'", - 'method.response.header.Access-Control-Allow-Credentials': "'true'" - } - } - ], - requestTemplates: { - 'application/json': '{ "statusCode": 200 }' - } + proxy: true, // Use proxy integration for simplicity }); - // Add POST method with responses for all status codes + // Method contact.addMethod('POST', integration, { apiKeyRequired: true, - methodResponses: [ - { - statusCode: '200', - responseParameters: { - 'method.response.header.Access-Control-Allow-Origin': true, - 'method.response.header.Access-Control-Allow-Headers': true, - 'method.response.header.Access-Control-Allow-Methods': true, - 'method.response.header.Access-Control-Allow-Credentials': true - } - }, - { - statusCode: '400', - responseParameters: { - 'method.response.header.Access-Control-Allow-Origin': true, - 'method.response.header.Access-Control-Allow-Headers': true, - 'method.response.header.Access-Control-Allow-Methods': true, - 'method.response.header.Access-Control-Allow-Credentials': true - }, - responseModels: { - 'application/json': errorResponseModel - } - }, - { - statusCode: '403', - responseParameters: { - 'method.response.header.Access-Control-Allow-Origin': true, - 'method.response.header.Access-Control-Allow-Headers': true, - 'method.response.header.Access-Control-Allow-Methods': true, - 'method.response.header.Access-Control-Allow-Credentials': true - }, - responseModels: { - 'application/json': errorResponseModel - } - } - ] }); - // Output values + // Outputs new cdk.CfnOutput(this, 'ApiEndpoint', { value: `${api.url}contact`, description: 'API Gateway endpoint URL',