2016-04-16 12:49:48 -04:00
|
|
|
var async = require('async');
|
2016-04-15 19:42:57 -04:00
|
|
|
var defaults = require('lodash.defaults');
|
2017-04-25 13:18:13 -04:00
|
|
|
var fastlyConfig = require('./lib/fastly-config-methods');
|
2018-12-13 16:00:41 -05:00
|
|
|
const languages = require('scratch-l10n').default;
|
2016-04-15 19:42:57 -04:00
|
|
|
|
2018-01-30 11:53:12 -05:00
|
|
|
var routeJson = require('../src/routes.json');
|
2016-04-16 10:17:20 -04:00
|
|
|
|
2016-04-18 12:32:20 -04:00
|
|
|
const FASTLY_SERVICE_ID = process.env.FASTLY_SERVICE_ID || '';
|
|
|
|
const S3_BUCKET_NAME = process.env.S3_BUCKET_NAME || '';
|
2016-04-16 13:01:39 -04:00
|
|
|
|
2016-04-18 12:32:20 -04:00
|
|
|
var fastly = require('./lib/fastly-extended')(process.env.FASTLY_API_KEY, FASTLY_SERVICE_ID);
|
|
|
|
|
2016-04-15 19:42:57 -04:00
|
|
|
var extraAppRoutes = [
|
|
|
|
// Homepage with querystring.
|
|
|
|
// TODO: Should this be added for every route?
|
2016-07-22 12:38:24 -04:00
|
|
|
'/\\?',
|
2016-04-15 19:42:57 -04:00
|
|
|
// View html
|
2018-01-30 11:53:12 -05:00
|
|
|
'/[^/]*.html$'
|
2016-04-16 10:17:20 -04:00
|
|
|
];
|
2016-04-15 19:42:57 -04:00
|
|
|
|
2018-01-30 11:53:12 -05:00
|
|
|
var routes = routeJson.map(function (route) {
|
2017-04-25 13:18:13 -04:00
|
|
|
return defaults({}, {pattern: fastlyConfig.expressPatternToRegex(route.pattern)}, route);
|
2016-05-20 10:33:13 -04:00
|
|
|
});
|
|
|
|
|
2016-04-18 12:32:20 -04:00
|
|
|
async.auto({
|
2016-04-18 14:07:27 -04:00
|
|
|
version: function (cb) {
|
2019-09-19 12:36:25 -04:00
|
|
|
fastly.getLatestActiveVersion(function (err, response) {
|
2016-04-18 12:32:20 -04:00
|
|
|
if (err) return cb(err);
|
|
|
|
// Validate latest version before continuing
|
2016-04-19 13:04:45 -04:00
|
|
|
if (response.active || response.locked) {
|
2018-01-30 11:53:12 -05:00
|
|
|
fastly.cloneVersion(response.number, function (e, resp) {
|
|
|
|
if (e) return cb('Failed to clone latest version: ' + e);
|
|
|
|
cb(null, resp.number);
|
2016-04-19 13:04:45 -04:00
|
|
|
});
|
|
|
|
} else {
|
|
|
|
cb(null, response.number);
|
|
|
|
}
|
2016-04-18 12:32:20 -04:00
|
|
|
});
|
|
|
|
},
|
2019-07-12 11:44:52 -04:00
|
|
|
recvCustomVCL: ['version', function (results, cb) {
|
2017-04-25 11:06:57 -04:00
|
|
|
// For all the routes in routes.json, construct a varnish-style regex that matches
|
|
|
|
// on any of those route conditions.
|
2017-04-25 13:18:13 -04:00
|
|
|
var notPassStatement = fastlyConfig.getAppRouteCondition('../build/*', routes, extraAppRoutes, __dirname);
|
2019-07-12 11:44:52 -04:00
|
|
|
|
2017-04-25 11:06:57 -04:00
|
|
|
// For a non-pass condition, point backend at s3
|
2017-11-30 18:03:01 -05:00
|
|
|
var recvCondition = '' +
|
|
|
|
'if (' + notPassStatement + ') {\n' +
|
|
|
|
' set req.backend = F_s3;\n' +
|
2018-01-30 11:53:12 -05:00
|
|
|
' set req.http.host = "' + S3_BUCKET_NAME + '";\n' +
|
2017-11-30 18:03:01 -05:00
|
|
|
'} else {\n' +
|
|
|
|
' if (!req.http.Fastly-FF) {\n' +
|
|
|
|
' if (req.http.X-Forwarded-For) {\n' +
|
2018-01-30 11:53:12 -05:00
|
|
|
' set req.http.Fastly-Temp-XFF = req.http.X-Forwarded-For ", " client.ip;\n' +
|
2017-11-30 18:03:01 -05:00
|
|
|
' } else {\n' +
|
|
|
|
' set req.http.Fastly-Temp-XFF = client.ip;\n' +
|
|
|
|
' }\n' +
|
|
|
|
' } else {\n' +
|
|
|
|
' set req.http.Fastly-Temp-XFF = req.http.X-Forwarded-For;\n' +
|
|
|
|
' }\n' +
|
|
|
|
' set req.grace = 60s;\n' +
|
|
|
|
' if (req.http.Cookie:scratchlanguage) {\n' +
|
|
|
|
' set req.http.Accept-Language = req.http.Cookie:scratchlanguage;\n' +
|
|
|
|
' } else {\n' +
|
|
|
|
' set req.http.Accept-Language = accept.language_lookup("' +
|
|
|
|
Object.keys(languages).join(':') + '", ' +
|
|
|
|
'"en", ' +
|
|
|
|
'std.tolower(req.http.Accept-Language)' +
|
|
|
|
');\n' +
|
|
|
|
' }\n' +
|
2017-12-01 13:50:43 -05:00
|
|
|
' if (req.url ~ "^(/projects/|/fragment/account-nav.json|/session/)" && ' +
|
|
|
|
'!req.http.Cookie:scratchsessionsid) {\n' +
|
2017-12-01 10:04:47 -05:00
|
|
|
' set req.http.Cookie = "scratchlanguage=" req.http.Cookie:scratchlanguage;\n' +
|
2017-11-30 18:03:01 -05:00
|
|
|
' } else {\n' +
|
|
|
|
' return(pass);\n' +
|
|
|
|
' }\n' +
|
|
|
|
'}\n';
|
2019-07-12 11:44:52 -04:00
|
|
|
|
2017-04-25 11:06:57 -04:00
|
|
|
|
|
|
|
fastly.setCustomVCL(
|
|
|
|
results.version,
|
|
|
|
'recv-condition',
|
2017-11-30 18:03:01 -05:00
|
|
|
recvCondition,
|
2017-04-25 11:06:57 -04:00
|
|
|
cb
|
|
|
|
);
|
|
|
|
}],
|
2019-07-12 11:44:52 -04:00
|
|
|
fetchCustomVCL: ['version', function (results, cb) {
|
2017-04-25 13:18:13 -04:00
|
|
|
var passStatement = fastlyConfig.negateConditionStatement(
|
|
|
|
fastlyConfig.getAppRouteCondition('../build/*', routes, extraAppRoutes, __dirname)
|
|
|
|
);
|
|
|
|
var ttlCondition = fastlyConfig.setResponseTTL(passStatement);
|
2017-04-25 11:06:57 -04:00
|
|
|
fastly.setCustomVCL(results.version, 'fetch-condition', ttlCondition, cb);
|
2016-04-18 12:32:20 -04:00
|
|
|
}],
|
2019-07-12 11:44:52 -04:00
|
|
|
appRouteRequestConditions: ['version', function (results, cb) {
|
2016-04-18 12:32:20 -04:00
|
|
|
var conditions = {};
|
2019-08-07 14:03:48 -04:00
|
|
|
async.forEachOf(routes, function (route, id, cb2) {
|
2016-04-18 12:32:20 -04:00
|
|
|
var condition = {
|
2017-04-25 13:18:13 -04:00
|
|
|
name: fastlyConfig.getConditionNameForRoute(route, 'request'),
|
2021-09-15 15:25:30 -04:00
|
|
|
statement: 'req.url.path ~ "' + route.pattern + '"',
|
2016-04-18 12:32:20 -04:00
|
|
|
type: 'REQUEST',
|
2016-04-27 15:27:45 -04:00
|
|
|
// Priority needs to be > 1 to not interact with http->https redirect
|
|
|
|
priority: 10 + id
|
2016-04-18 12:32:20 -04:00
|
|
|
};
|
|
|
|
fastly.setCondition(results.version, condition, function (err, response) {
|
|
|
|
if (err) return cb2(err);
|
|
|
|
conditions[id] = response;
|
|
|
|
cb2(null, response);
|
|
|
|
});
|
|
|
|
}, function (err) {
|
|
|
|
if (err) return cb(err);
|
|
|
|
cb(null, conditions);
|
|
|
|
});
|
|
|
|
}],
|
2019-07-12 11:44:52 -04:00
|
|
|
appRouteHeaders: ['version', 'appRouteRequestConditions', function (results, cb) {
|
2016-04-18 12:32:20 -04:00
|
|
|
var headers = {};
|
2019-08-07 14:03:48 -04:00
|
|
|
async.forEachOf(routes, function (route, id, cb2) {
|
2016-04-19 18:42:03 -04:00
|
|
|
if (route.redirect) {
|
|
|
|
async.auto({
|
|
|
|
responseCondition: function (cb3) {
|
|
|
|
var condition = {
|
2017-04-25 13:18:13 -04:00
|
|
|
name: fastlyConfig.getConditionNameForRoute(route, 'response'),
|
2021-09-15 15:25:30 -04:00
|
|
|
statement: 'req.url.path ~ "' + route.pattern + '"',
|
2016-04-19 18:42:03 -04:00
|
|
|
type: 'RESPONSE',
|
|
|
|
priority: id
|
|
|
|
};
|
|
|
|
fastly.setCondition(results.version, condition, cb3);
|
|
|
|
},
|
|
|
|
responseObject: function (cb3) {
|
|
|
|
var responseObject = {
|
2017-04-25 13:18:13 -04:00
|
|
|
name: fastlyConfig.getResponseNameForRoute(route),
|
2016-04-19 18:42:03 -04:00
|
|
|
status: 301,
|
|
|
|
response: 'Moved Permanently',
|
2017-04-25 13:18:13 -04:00
|
|
|
request_condition: fastlyConfig.getConditionNameForRoute(route, 'request')
|
2016-04-19 18:42:03 -04:00
|
|
|
};
|
|
|
|
fastly.setResponseObject(results.version, responseObject, cb3);
|
|
|
|
},
|
2019-07-12 11:44:52 -04:00
|
|
|
redirectHeader: ['responseCondition', function (redirectResults, cb3) {
|
2016-04-19 18:42:03 -04:00
|
|
|
var header = {
|
2017-04-25 13:18:13 -04:00
|
|
|
name: fastlyConfig.getHeaderNameForRoute(route),
|
2016-04-19 18:42:03 -04:00
|
|
|
action: 'set',
|
|
|
|
ignore_if_set: 0,
|
|
|
|
type: 'RESPONSE',
|
|
|
|
dst: 'http.Location',
|
|
|
|
src: '"' + route.redirect + '"',
|
|
|
|
response_condition: redirectResults.responseCondition.name
|
|
|
|
};
|
|
|
|
fastly.setFastlyHeader(results.version, header, cb3);
|
|
|
|
}]
|
2019-08-07 14:03:48 -04:00
|
|
|
}, function (err, redirectResults) {
|
2016-04-19 18:42:03 -04:00
|
|
|
if (err) return cb2(err);
|
|
|
|
headers[id] = redirectResults.redirectHeader;
|
|
|
|
cb2(null, redirectResults);
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
var header = {
|
2017-04-25 13:18:13 -04:00
|
|
|
name: fastlyConfig.getHeaderNameForRoute(route, 'request'),
|
2016-04-19 18:42:03 -04:00
|
|
|
action: 'set',
|
|
|
|
ignore_if_set: 0,
|
|
|
|
type: 'REQUEST',
|
|
|
|
dst: 'url',
|
2016-04-21 18:16:49 -04:00
|
|
|
src: '"/' + route.name + '.html"',
|
2016-04-19 18:42:03 -04:00
|
|
|
request_condition: results.appRouteRequestConditions[id].name,
|
|
|
|
priority: 10
|
|
|
|
};
|
|
|
|
fastly.setFastlyHeader(results.version, header, function (err, response) {
|
|
|
|
if (err) return cb2(err);
|
|
|
|
headers[id] = response;
|
|
|
|
cb2(null, response);
|
|
|
|
});
|
|
|
|
}
|
2016-04-18 12:32:20 -04:00
|
|
|
}, function (err) {
|
|
|
|
if (err) return cb(err);
|
|
|
|
cb(null, headers);
|
2016-04-18 14:07:27 -04:00
|
|
|
});
|
2018-12-05 14:57:30 -05:00
|
|
|
}],
|
2019-07-12 11:44:52 -04:00
|
|
|
tipbarRedirectHeaders: ['version', function (results, cb) {
|
2018-12-05 14:57:30 -05:00
|
|
|
async.auto({
|
|
|
|
requestCondition: function (cb2) {
|
|
|
|
var condition = {
|
|
|
|
name: 'routes/?tip_bar= (request)',
|
|
|
|
statement: 'req.url ~ "\\?tip_bar="',
|
|
|
|
type: 'REQUEST',
|
|
|
|
priority: 10
|
|
|
|
};
|
|
|
|
fastly.setCondition(results.version, condition, cb2);
|
|
|
|
},
|
|
|
|
responseCondition: function (cb2) {
|
|
|
|
var condition = {
|
|
|
|
name: 'routes/?tip_bar= (response)',
|
|
|
|
statement: 'req.url ~ "\\?tip_bar="',
|
|
|
|
type: 'RESPONSE',
|
|
|
|
priority: 10
|
|
|
|
};
|
|
|
|
fastly.setCondition(results.version, condition, cb2);
|
|
|
|
},
|
2019-07-12 11:44:52 -04:00
|
|
|
responseObject: ['requestCondition', function (redirectResults, cb2) {
|
2018-12-05 14:57:30 -05:00
|
|
|
var responseObject = {
|
|
|
|
name: 'redirects/?tip_bar=',
|
|
|
|
status: 301,
|
|
|
|
response: 'Moved Permanently',
|
|
|
|
request_condition: redirectResults.requestCondition.name
|
|
|
|
};
|
|
|
|
fastly.setResponseObject(results.version, responseObject, cb2);
|
|
|
|
}],
|
2019-07-12 11:44:52 -04:00
|
|
|
redirectHeader: ['responseCondition', function (redirectResults, cb2) {
|
2018-12-05 14:57:30 -05:00
|
|
|
var header = {
|
|
|
|
name: 'redirects/?tip_bar=',
|
|
|
|
action: 'set',
|
|
|
|
ignore_if_set: 0,
|
|
|
|
type: 'RESPONSE',
|
|
|
|
dst: 'http.Location',
|
|
|
|
src: 'regsub(req.url, "tip_bar=", "tutorial=")',
|
|
|
|
response_condition: redirectResults.responseCondition.name
|
|
|
|
};
|
|
|
|
fastly.setFastlyHeader(results.version, header, cb2);
|
|
|
|
}]
|
2019-08-07 14:03:48 -04:00
|
|
|
}, function (err, redirectResults) {
|
2018-12-05 14:57:30 -05:00
|
|
|
if (err) return cb(err);
|
|
|
|
cb(null, redirectResults);
|
|
|
|
});
|
2018-12-04 11:33:37 -05:00
|
|
|
}],
|
2019-07-12 11:44:52 -04:00
|
|
|
embedRedirectHeaders: ['version', function (results, cb) {
|
2018-12-04 11:33:37 -05:00
|
|
|
async.auto({
|
|
|
|
requestCondition: function (cb2) {
|
|
|
|
var condition = {
|
|
|
|
name: 'routes/projects/embed (request)',
|
2021-09-15 15:25:30 -04:00
|
|
|
statement: 'req.url.path ~ "^/projects/embed/(\\d+)"',
|
2018-12-04 11:33:37 -05:00
|
|
|
type: 'REQUEST',
|
|
|
|
priority: 10
|
|
|
|
};
|
|
|
|
fastly.setCondition(results.version, condition, cb2);
|
|
|
|
},
|
|
|
|
responseCondition: function (cb2) {
|
|
|
|
var condition = {
|
|
|
|
name: 'routes/projects/embed (response)',
|
2021-09-15 15:25:30 -04:00
|
|
|
statement: 'req.url.path ~ "^/projects/embed/(\\d+)"',
|
2018-12-04 11:33:37 -05:00
|
|
|
type: 'RESPONSE',
|
|
|
|
priority: 10
|
|
|
|
};
|
|
|
|
fastly.setCondition(results.version, condition, cb2);
|
|
|
|
},
|
2019-07-12 11:44:52 -04:00
|
|
|
responseObject: ['requestCondition', function (redirectResults, cb2) {
|
2018-12-04 11:33:37 -05:00
|
|
|
var responseObject = {
|
|
|
|
name: 'redirects/projects/embed',
|
|
|
|
status: 301,
|
|
|
|
response: 'Moved Permanently',
|
|
|
|
request_condition: redirectResults.requestCondition.name
|
|
|
|
};
|
|
|
|
fastly.setResponseObject(results.version, responseObject, cb2);
|
|
|
|
}],
|
2019-07-12 11:44:52 -04:00
|
|
|
redirectHeader: ['responseCondition', function (redirectResults, cb2) {
|
2018-12-04 11:33:37 -05:00
|
|
|
var header = {
|
|
|
|
name: 'redirects/projects/embed',
|
|
|
|
action: 'set',
|
|
|
|
ignore_if_set: 0,
|
|
|
|
type: 'RESPONSE',
|
|
|
|
dst: 'http.Location',
|
|
|
|
src: '"/projects/" + re.group.1 + "/embed"',
|
|
|
|
response_condition: redirectResults.responseCondition.name
|
|
|
|
};
|
|
|
|
fastly.setFastlyHeader(results.version, header, cb2);
|
|
|
|
}]
|
2019-08-07 14:03:48 -04:00
|
|
|
}, function (err, redirectResults) {
|
2018-12-04 11:33:37 -05:00
|
|
|
if (err) return cb(err);
|
|
|
|
cb(null, redirectResults);
|
|
|
|
});
|
2018-01-30 11:53:12 -05:00
|
|
|
}]
|
2019-08-07 14:03:48 -04:00
|
|
|
}, function (err, results) {
|
2018-01-30 11:53:12 -05:00
|
|
|
if (err) throw new Error(err);
|
|
|
|
if (process.env.FASTLY_ACTIVATE_CHANGES) {
|
|
|
|
fastly.activateVersion(results.version, function (e, resp) {
|
2018-05-02 17:01:52 -04:00
|
|
|
if (e) throw new Error(e);
|
2018-01-30 11:53:12 -05:00
|
|
|
process.stdout.write('Successfully configured and activated version ' + resp.number + '\n');
|
2021-05-14 19:17:22 -04:00
|
|
|
// purge static-assets using surrogate key
|
|
|
|
fastly.purgeKey(FASTLY_SERVICE_ID, 'static-assets', function (error) {
|
2020-12-22 14:18:29 -05:00
|
|
|
if (error) throw new Error(error);
|
|
|
|
process.stdout.write('Purged static assets.\n');
|
|
|
|
});
|
2018-01-30 11:53:12 -05:00
|
|
|
});
|
2016-04-16 12:49:48 -04:00
|
|
|
}
|
2018-01-30 11:53:12 -05:00
|
|
|
});
|