Skip to content

Commit 8bbdcba

Browse files
committed
Implement static asset path validation, and fallback fast boot route logic
1 parent de75d2f commit 8bbdcba

File tree

1 file changed

+139
-111
lines changed

1 file changed

+139
-111
lines changed

assets/lambda-package/index.js

Lines changed: 139 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,44 @@
11
// ember-cli-deploy-fastboot-api-lambda
22

3-
var config = require('./config.json');
4-
var mime = require('mime');
5-
var fs = require('fs-promise');
6-
var FastBoot = require('fastboot');
3+
var config = require('./config.json');
4+
var mime = require('mime');
5+
var nodePath = require('path');
6+
var fs = require('fs-promise');
7+
var FastBoot = require('fastboot');
78

89
var fancyACacheYeh = {
9-
yes: 'max-age=63072000, public',
10-
no: 'max-age=0, public'
10+
yes: 'max-age=63072000, public',
11+
no: 'max-age=0, public'
1112
};
1213

1314
var defaults = {
14-
distPath: 'dist',
15-
path: '/',
16-
host: 'localhost',
17-
assetsPath: '/assets/',
18-
standardExtensions: [
19-
'html',
20-
'css',
21-
'js',
22-
'json',
23-
'xml',
24-
'ico',
25-
'txt',
26-
'map'
27-
],
28-
headers: {
29-
'Content-Type': 'text/html;charset=UTF-8',
30-
'Cache-Control': fancyACacheYeh.no
31-
},
32-
fastBootOptions: {
15+
distPath: 'dist',
16+
path: '/',
17+
protocol: 'https',
18+
host: 'localhost:4200',
19+
assetsPath: '/assets/',
20+
standardExtensions: [
21+
'html',
22+
'css',
23+
'js',
24+
'json',
25+
'xml',
26+
'ico',
27+
'txt',
28+
'map'
29+
],
30+
validAssetPaths: [
31+
'/assets/',
32+
'/robots.txt',
33+
'/humans.txt',
34+
'/crossdomain.xml',
35+
'/sitemap.xml'
36+
],
37+
headers: {
38+
'Content-Type': 'text/html;charset=UTF-8',
39+
'Cache-Control': fancyACacheYeh.no
40+
},
41+
fastBootOptions: {
3342
request: {
3443
headers: {},
3544
get: function() {}
@@ -38,106 +47,125 @@ var defaults = {
3847
}
3948
};
4049

41-
// Merge defaults with config overrides
42-
var standardExtensions = defaults.standardExtensions.concat(config.standardExtensions || []);
50+
// Merge config: start
51+
var distPath = config.distPath || defaults.distPath;
4352
var fallbackPath = config.defaultPath || defaults.path;
53+
var protocol = config.protocol || defaults.protocol;
54+
var host = config.host || defaults.host;
55+
var validAssetPaths = defaults.validAssetPaths.concat(config.validAssetPaths || []);
56+
var standardExtensions = defaults.standardExtensions.concat(config.standardExtensions || []);
57+
// Merge config: end
4458

4559
// Instantiate Fastboot server
46-
var app = new FastBoot({ distPath: defaults.distPath });
47-
48-
exports.handler = function(event, context, callback) {
49-
console.log('INFO event:', event);
50-
51-
var path = event.path || fallbackPath;
52-
var staticPath = defaults.distPath + '/' + path;
53-
54-
console.log('INFO path:', path);
55-
console.log('INFO staticPath:', staticPath);
56-
57-
return fs.readFile(staticPath)
58-
59-
// STATIC FILE LOGIC
60-
.then(function(fileBuffer) {
61-
62-
// 1. Look up files content type.
63-
var contentType = mime.lookup(staticPath);
64-
65-
//2. Get file extension.
66-
var extension = mime.extension(contentType);
67-
68-
//3. If it isn't a standard file, then base64 encode it.
69-
var shouldEncode = standardExtensions.indexOf(extension) < 0;
70-
71-
//4. Determine if the item is fingerprinted/cacheable
72-
var shouldCache = staticPath.includes(defaults.assetsPath);
73-
74-
//5. Set encoding value
75-
var encoding = shouldEncode ? 'base64' : 'utf8';
76-
77-
//6. Create headers
78-
var headers = {
79-
'Content-Type': contentType,
80-
'Cache-Control': shouldCache ? fancyACacheYeh.yes : fancyACacheYeh.no
81-
};
82-
83-
//7. Create body
84-
var body = fileBuffer.toString(encoding);
60+
var app = new FastBoot({ distPath: distPath });
61+
62+
var serveACheekyFile = (path, staticPath, fileBuffer) => {
63+
// 1. Early exit bail
64+
var isAssetValidPath = validAssetPaths.find(p => p.includes(path));
65+
console.log('INFO isAssetValidPath:', isAssetValidPath);
66+
if (!isAssetValidPath) { throw true; }
67+
68+
// 1. Look up files content type.
69+
var contentType = mime.lookup(staticPath);
70+
71+
//2. Get file extension.
72+
var extension = mime.extension(contentType);
73+
74+
//3. If it isn't a standard file, then base64 encode it.
75+
var shouldEncode = standardExtensions.indexOf(extension) < 0;
76+
77+
//4. Determine if the item is fingerprinted/cacheable
78+
var shouldCache = staticPath.includes(defaults.assetsPath);
79+
80+
//5. Set encoding value
81+
var encoding = shouldEncode ? 'base64' : 'utf8';
82+
83+
//6. Create headers
84+
var headers = {
85+
'Content-Type': contentType,
86+
'Cache-Control': shouldCache ? fancyACacheYeh.yes : fancyACacheYeh.no
87+
};
88+
89+
//7. Create body
90+
var body = fileBuffer.toString(encoding);
91+
92+
//8. Create final output
93+
var payload = {
94+
statusCode: 200,
95+
headers: headers,
96+
body: body,
97+
isBase64Encoded: shouldEncode
98+
};
99+
100+
console.log('INFO: contentType:', contentType);
101+
console.log('INFO: extension:', extension);
102+
console.log('INFO: standardExtensions:', standardExtensions);
103+
console.log('INFO: shouldEncode:', shouldEncode);
104+
console.log('INFO: shouldCache:', shouldCache);
105+
console.log('INFO: encoding:', encoding);
106+
107+
return payload;
108+
};
85109

86-
//8. Create final output
87-
var payload = {
88-
statusCode: 200,
89-
headers: headers,
90-
body: body,
91-
isBase64Encoded: shouldEncode
92-
};
93110

94-
console.log('INFO: contentType:', contentType);
95-
console.log('INFO: extension:', extension);
96-
console.log('INFO: standardExtensions:', standardExtensions);
97-
console.log('INFO: shouldEncode:', shouldEncode);
98-
console.log('INFO: shouldCache:', shouldCache);
99-
console.log('INFO: encoding:', encoding);
111+
var doSomeFastBoot = (event, path) => {
112+
113+
// 1. Create options
114+
var options = defaults.fastBootOptions;
115+
options.request.headers = event.headers || {};
116+
options.request.protocol = (event.headers || {})['X-Forwarded-Proto'] || protocol;
117+
options.request.headers.host = (event.headers || {}).Host || host;
118+
if (event.cookie) {
119+
options.request.headers.cookie = event.cookie;
120+
}
100121

101-
return callback(null, payload);
102-
})
122+
console.log('INFO: options:', options);
103123

104-
// GO FASTBOOT GO!
105-
.catch(function() {
124+
// 2. Fire up fastboot server
125+
return app.visit(path, options)
126+
.then(function(result) {
106127

107-
// 1. Create options
108-
var options = defaults.fastBootOptions;
109-
options.request.headers = event.headers || {};
110-
options.request.headers.host = (event.headers || {}).Host || defaults.host;
111-
if (event.cookie) {
112-
options.request.headers.cookie = event.cookie;
113-
}
128+
var statusCode = result.statusCode;
114129

115-
console.log('INFO: options:', options);
130+
// Not interested yo. Wheres that sweet 200's at?
131+
if (statusCode !== 200) { throw true; }
116132

117-
// 2. Fire up fastboot server
118-
return app.visit(path, options)
119-
.then(function(result) {
120-
return result.html()
121-
.then(function(html) {
133+
return result.html()
134+
.then(function(html) {
122135

123-
// 3. Create headers object
124-
var headers = Object.assign(result.headers.headers, defaults.headers);
136+
// 3. Create headers object
137+
var headers = Object.assign(result.headers.headers, defaults.headers);
125138

126-
console.log('INFO: headers:', headers);
139+
console.log('INFO: headers:', headers);
127140

128-
// 4. Create payload
129-
var payload = {
130-
statusCode: result.statusCode,
131-
headers: headers,
132-
body: html
133-
};
141+
// 4. Create payload
142+
var payload = {
143+
statusCode: statusCode,
144+
headers: headers,
145+
body: html
146+
};
134147

135-
// 5. Profit ???
136-
return callback(null, payload);
137-
});
138-
})
139-
.catch(err => callback(err));
148+
return payload;
149+
});
150+
});
140151

141-
});
152+
};
142153

154+
exports.handler = function(event, context, callback) {
155+
console.log('INFO event:', event);
156+
157+
var path = event.path || fallbackPath;
158+
var staticPath = nodePath.join(distPath, path);
159+
160+
console.log('INFO path:', path);
161+
console.log('INFO staticPath:', staticPath);
162+
163+
return fs.readFile(staticPath)
164+
.then(fileBuffer => serveACheekyFile(path, staticPath, fileBuffer), () => doSomeFastBoot(event, path))
165+
.then(r => callback(null, r), () => doSomeFastBoot(event, fallbackPath))
166+
.then(r => callback(null, r))
167+
.catch(error => {
168+
console.log('INFO: ERROR:', error);
169+
return callback(error);
170+
});
143171
};

0 commit comments

Comments
 (0)