Skip to content

Commit

Permalink
Merge pull request #34 from meteordefect/Fix-form-cors-9
Browse files Browse the repository at this point in the history
Fixing contact form 9
  • Loading branch information
meteordefect authored Dec 22, 2024
2 parents 1fee3d2 + bd01add commit 6b6c223
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 136 deletions.
70 changes: 31 additions & 39 deletions cipher-infra/lambda/contact-form/handler.ts
Original file line number Diff line number Diff line change
@@ -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" });

Expand All @@ -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<APIGatewayProxyResult> => {
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 = {
Expand Down Expand Up @@ -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'
});
}
};
108 changes: 11 additions & 97 deletions cipher-infra/lib/contact-form-stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,19 @@ 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',
code: lambda.Code.fromAsset(path.join(__dirname, '../lambda/contact-form')),
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,
Expand All @@ -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',
Expand All @@ -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',
Expand All @@ -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',
Expand Down

0 comments on commit 6b6c223

Please sign in to comment.