-
Notifications
You must be signed in to change notification settings - Fork 50
/
Copy pathws-lambda-at-edge-generate-card-page.js
99 lines (86 loc) · 3.45 KB
/
ws-lambda-at-edge-generate-card-page.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
'use strict';
const http = require('https');
const AWS = require('aws-sdk');
const ddb = new AWS.DynamoDB.DocumentClient({apiVersion: '2012-10-08', region: 'us-east-1'});
// Copy DynamoDB table name here, for example, 'AlienCards-1201c610'
const ddbTableName = 'FIXME';
// The generated page contains some dynamic data, so we don't want
// it to stay in cache for long
const cacheControlMaxAge = 3;
const cardTemplate = '/templates/card.html';
exports.handler = async (event) => {
console.log('Event: ', JSON.stringify(event, null, 2));
const cf = event.Records[0].cf;
const request = cf.request;
// Get Id from URI (pass through if failed to parse)
const m = request.uri.match(/^\/card\/([a-z0-9]+)$/i);
const id = (Array.isArray(m) && m.length > 1) ? m[1] : null;
if (!id) {
// Couldn't get id from URI, don't generate any response.
// Just let the request go to the origin
return request;
}
let response = '';
try {
// Get HTML template from the CloudFront cache
// and data from the DynamoDB table
const responses = await Promise.all([
httpGet({ hostname: cf.config.distributionDomainName, path: cardTemplate }),
ddbGet({ TableName: ddbTableName, Key: { CardId: id } }),
]);
const template = responses[0];
const data = responses[1];
if (!data.Item) {
throw new Error(`No item found, id: + ${id}`);
}
// Replace the placeholders in the template with actual data
const html = template
.replace(/{{message}}/g, 'HTML Generated by Lambda@Edge')
.replace(/{{id}}/g, id)
.replace(/{{description}}/g, data.Item.Description)
.replace(/{{likes}}/g, data.Item.Likes);
response = {
status: '200',
headers: addSecurityHeaders({
'cache-control': [{ value: `max-age=${cacheControlMaxAge}` }],
'content-type': [{ value: 'text/html;charset=UTF-8' }]
}),
body: html
};
} catch (error) {
response = {
status: '500',
headers: addSecurityHeaders({
'content-type': [{ value: 'application/json' }]
}),
body: JSON.stringify(error, null, 2)
};
};
console.log('response: ' + JSON.stringify(response));
return response;
};
function httpGet(params) {
return new Promise((resolve, reject) => {
http.get(params, (resp) => {
console.log(`Fetching ${params.hostname}${params.path}, status code : ${resp.statusCode}`);
let data = '';
resp.on('data', (chunk) => { data += chunk; });
resp.on('end', () => { resolve(data); });
}).on('error', (err) => {
console.log(`Couldn't fetch ${params.hostname}${params.path} : ${err.message}`);
reject(err, null);
});
});
}
function ddbGet(params) {
console.log('DynamoDB get params: ' + JSON.stringify(params, null, 2));
return ddb.get(params).promise();
}
function addSecurityHeaders(headers) {
headers['strict-transport-security'] = [{ value: 'max-age=31536000; includeSubDomains' }];
headers['content-security-policy'] = [{ value: "default-src 'self'" }];
headers['x-xss-protection'] = [{ value: '1; mode=block' }];
headers['x-content-type-options'] = [{ value: 'nosniff' }];
headers['x-frame-options'] = [{ value: 'DENY' }];
return headers;
}