Skip to content

Commit 84e74db

Browse files
author
Chris Raynor
committed
Merge pull request #30 from firebase/v1.1.2
v1.1.2
2 parents 50fdebc + 17bf760 commit 84e74db

File tree

3 files changed

+131
-8
lines changed

3 files changed

+131
-8
lines changed

CHANGELOG.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
## v1.1.2
2+
- Adds client-side validation of new advanced features with better error messaging
3+
14
## v1.1.1
25
- Adds a brief description of each template inline when running `firebase bootstrap`
36

@@ -26,4 +29,4 @@
2629
## v1.0.1
2730
- Fixes bug with `firebase bootstrap` on windows
2831
- Adds 'ignore' to `firebase.json` to allow files to be ignored on deploy
29-
- Prioritizes `--email` and `--password` command line arguments over current auth token if both passed
32+
- Prioritizes `--email` and `--password` command line arguments over current auth token if both passed

lib/app.js

Lines changed: 126 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,7 @@ function updateRules(settings, tokens) {
7575

7676
function updateRedirects(firebaseRef, settings) {
7777
return _when.promise(function(resolve, reject, notify) {
78-
var redirects = settings.redirects || null;
79-
firebaseRef.child('hosting/path-redirects').child(settings.firebase).set(redirects, function(err) {
78+
firebaseRef.child('hosting/path-redirects').child(settings.firebase).set(settings.redirects, function(err) {
8079
if (err) {
8180
console.log(chalk.red('Settings Error') + ' - Incorrectly formatted "redirects" entry in the firebase.json');
8281
process.exit(1);
@@ -88,8 +87,7 @@ function updateRedirects(firebaseRef, settings) {
8887

8988
function updateRewrites(firebaseRef, settings) {
9089
return _when.promise(function(resolve, reject, notify) {
91-
var rewrites = settings.rewrites || null;
92-
firebaseRef.child('hosting/rewrites').child(settings.firebase).set(rewrites, function(err) {
90+
firebaseRef.child('hosting/rewrites').child(settings.firebase).set(settings.rewrites, function(err) {
9391
if (err) {
9492
console.log(chalk.red('Settings Error') + ' - Incorrectly formatted "rewrites" entry in the firebase.json');
9593
process.exit(1);
@@ -101,8 +99,7 @@ function updateRewrites(firebaseRef, settings) {
10199

102100
function updateHeaders(firebaseRef, settings) {
103101
return _when.promise(function(resolve, reject, notify) {
104-
var headers = settings.headers || null;
105-
firebaseRef.child('hosting/headers').child(settings.firebase).set(headers, function(err) {
102+
firebaseRef.child('hosting/headers').child(settings.firebase).set(settings.headers, function(err) {
106103
if (err) {
107104
console.log(chalk.red('Settings Error') + ' - Incorrectly formatted "headers" entry in the firebase.json');
108105
process.exit(1);
@@ -112,6 +109,122 @@ function updateHeaders(firebaseRef, settings) {
112109
});
113110
}
114111

112+
function validateRedirects(redirects) {
113+
var error = null;
114+
if (redirects) {
115+
if (!Array.isArray(redirects)) {
116+
error = 'Redirects entry in the firebase.json must be an Array.';
117+
} else {
118+
for (var i = 0; i < redirects.length; i++) {
119+
var redirect = redirects[i];
120+
if (typeof redirect !== 'object') {
121+
error = 'Redirect rule: ' + JSON.stringify(redirect) + ' Must be an object.';
122+
} else if (!redirect.source || typeof redirect.source !== 'string' || redirect.source.length == 0) {
123+
error = 'Redirect rule: ' + JSON.stringify(redirect) + ' Must contain a "source" attribute that\'s a non-empty string.';
124+
} else if (!redirect.destination || typeof redirect.destination !== 'string' || redirect.destination.length == 0) {
125+
error = 'Redirect rule: ' + JSON.stringify(redirect) + ' Must contain a "destination" attribute that\'s a non-empty string.';
126+
} else if (!/^(\/[^\s]*|https?:\/\/[^\s]+)$/.test(redirect.destination)) {
127+
error = 'Redirect destination: "' + redirect.destination + '" Must be a remote or absolute url and start with "http", "https", or a "/".';
128+
} else if (redirect.type !== 301 && redirect.type !== 302) {
129+
error = 'Redirect rule: ' + JSON.stringify(redirect) + ' Must have a redirect "type" that\'s either 301 for a permanent redirect or 302 for a temporary redirect.';
130+
} else if (Object.keys(redirect).length > 3) {
131+
error = 'Redirect rule: ' + JSON.stringify(redirect) + ' Must not contain any keys other than "source", "destination", or "type".';
132+
}
133+
if (error) {
134+
break;
135+
}
136+
}
137+
}
138+
}
139+
if (error) {
140+
console.log(chalk.red('Settings Error') + ' - ' + error);
141+
process.exit(1);
142+
}
143+
}
144+
145+
function validateRewrites(rewrites) {
146+
var error = null;
147+
if (rewrites) {
148+
if (!Array.isArray(rewrites)) {
149+
error = 'Rewrites entry in the firebase.json must be an Array.';
150+
} else {
151+
for (var i = 0; i < rewrites.length; i++) {
152+
var rewrite = rewrites[i];
153+
if (typeof rewrite !== 'object') {
154+
error = 'Rewrite rule: ' + JSON.stringify(rewrite) + ' Must be an object.';
155+
} else if (!rewrite.source || typeof rewrite.source !== 'string' || rewrite.source.length == 0) {
156+
error = 'Rewrite rule: ' + JSON.stringify(rewrite) + ' Must contain a "source" attribute that\'s a non-empty string.';
157+
} else if (!rewrite.destination || typeof rewrite.destination !== 'string' || rewrite.destination.length == 0) {
158+
error = 'Rewrite rule: ' + JSON.stringify(rewrite) + ' Must contain a "destination" attribute that\'s a non-empty string.';
159+
} else if (!/^\/[^\s]*[^\/\s]$/.test(rewrite.destination)) {
160+
error = 'Rewrite destination: "' + rewrite.destination + '" Must be an absolute path to a file that starts with a "/" and does not end in a "/".';
161+
} else if (Object.keys(rewrite).length > 2) {
162+
error = 'Rewrite rule: ' + JSON.stringify(rewrite) + ' Must not contain any keys other than "source" or "destination".';
163+
}
164+
if (error) {
165+
break;
166+
}
167+
}
168+
}
169+
}
170+
if (error) {
171+
console.log(chalk.red('Settings Error') + ' - ' + error);
172+
process.exit(1);
173+
}
174+
}
175+
176+
function validateHeaders(headers) {
177+
var error = null,
178+
supportedHeaders = [
179+
'Access-Control-Allow-Origin',
180+
'Cache-Control'
181+
];
182+
if (headers) {
183+
if (!Array.isArray(headers)) {
184+
error = 'Headers entry in the firebase.json must be an Array.';
185+
} else {
186+
for (var i = 0; i < headers.length; i++) {
187+
var header = headers[i];
188+
if (typeof header !== 'object') {
189+
error = 'Header rule: ' + JSON.stringify(header) + ' Must be an object.';
190+
} else if (!header.source || typeof header.source !== 'string' || header.source.length === 0) {
191+
error = 'Header rule: ' + JSON.stringify(header) + ' Must contain a "source" attribute that\'s a non-empty string.';
192+
} else if (!header.headers || !Array.isArray(header.headers)) {
193+
error = 'Header rule: ' + JSON.stringify(header) + ' Must contain a "headers" attribute that\'s an array.';
194+
} else if (Object.keys(header).length > 2) {
195+
error = 'Header rule: ' + JSON.stringify(header) + ' Must not contain any keys other than "source" or "headers".';
196+
}
197+
if (!error) {
198+
for (var j = 0; j < header.headers.length; j++) {
199+
var individualHeader = header.headers[j];
200+
if (typeof individualHeader !== 'object') {
201+
error = 'Header: ' + JSON.stringify(individualHeader) + ' Must be an object';
202+
} else if (!individualHeader['key'] || typeof individualHeader['key'] !== 'string' || individualHeader['key'].length === 0) {
203+
error = 'Header: ' + JSON.stringify(individualHeader) + ' Must contain a "key" field that\'s one of: "' + supportedHeaders.join('", "') + '"';
204+
} else if (!individualHeader['value'] || typeof individualHeader['value'] !== 'string' || individualHeader['value'].length === 0) {
205+
error = 'Header: ' + JSON.stringify(individualHeader) + ' Must contain a "value" field that\'s a non-empty string.';
206+
} else if (supportedHeaders.indexOf(individualHeader['key']) < 0) {
207+
error = 'Header key: "' + individualHeader['key'] + '" is not supported. Supported keys are: "' + supportedHeaders.join('", "') + '"';
208+
} else if (Object.keys(individualHeader).length > 2) {
209+
error = 'Header: ' + JSON.stringify(individualHeader) + ' Must not contain any keys other than "key" or "value".';
210+
}
211+
if (error) {
212+
break;
213+
}
214+
}
215+
}
216+
if (error) {
217+
break;
218+
}
219+
}
220+
}
221+
}
222+
if (error) {
223+
console.log(chalk.red('Settings Error') + ' - ' + error);
224+
process.exit(1);
225+
}
226+
}
227+
115228
function uploadSite(settings, directoryRef, argv) {
116229
return function() {
117230
var bar = null;
@@ -520,6 +633,13 @@ module.exports = {
520633
process.exit(1);
521634
}
522635

636+
settings.rewrites = settings.rewrites || null;
637+
validateRewrites(settings.rewrites);
638+
settings.redirects = settings.redirects || null;
639+
validateRedirects(settings.redirects);
640+
settings.headers = settings.headers || null;
641+
validateHeaders(settings.headers);
642+
523643
var firebaseRef = new Firebase(api.realtimeUrl.replace(/\/\//, '//firebase.'));
524644
firebaseRef.auth(tokens.firebaseToken, function(error, result) {
525645
if (error) {

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "firebase-tools",
33
"preferGlobal": true,
4-
"version": "1.1.1",
4+
"version": "1.1.2",
55
"description": "The Firebase Command Line Tools",
66
"keywords": [
77
"firebase",

0 commit comments

Comments
 (0)