mirror of
https://github.com/scratchfoundation/scratch-www.git
synced 2024-11-26 17:16:11 -05:00
32611c9cba
The order of the rewrite conditions needs to be internally consistent so we can control specificity with routes.json
176 lines
No EOL
6.4 KiB
JavaScript
176 lines
No EOL
6.4 KiB
JavaScript
var async = require('async');
|
|
var defaults = require('lodash.defaults');
|
|
var glob = require('glob');
|
|
var path = require('path');
|
|
|
|
var routes = require('../server/routes.json');
|
|
var serviceId = process.env.FASTLY_SERVICE_ID
|
|
var s3Bucket = process.env.S3_BUCKET_NAME;
|
|
|
|
var fastly = require('./lib/fastly-extended')(process.env.FASTLY_API_KEY, serviceId);
|
|
|
|
const PASS_REQUEST_CONDITION_NAME = 'Pass';
|
|
const NOT_PASS_REQUEST_CONDITION_NAME = '!(Pass)'
|
|
const PASS_CACHE_CONDITION_NAME = 'Cache Pass';
|
|
const BUCKET_NAME_HEADER_NAME = 'Bucket name';
|
|
|
|
var extraAppRoutes = [
|
|
// Homepage with querystring.
|
|
// TODO: Should this be added for every route?
|
|
'^/\\?',
|
|
// Version output by build
|
|
'/version\.txt$',
|
|
// View html
|
|
'^/[^\/]*\.html'
|
|
];
|
|
|
|
/*
|
|
* Given the relative path to the static directory, return an array of
|
|
* patterns matching the files and directories there.
|
|
*/
|
|
var getStaticPaths = function (pathToStatic) {
|
|
var staticPaths = glob.sync(path.resolve(__dirname, pathToStatic));
|
|
return staticPaths.map( function (pathName) {
|
|
// Reduce absolute path to relative paths like '/js'
|
|
var base = path.dirname(path.resolve(__dirname, pathToStatic));
|
|
return '^' + pathName.replace(base, '');
|
|
});
|
|
}
|
|
|
|
/*
|
|
* Given a list of express routes, return a list of patterns to match
|
|
* the express route and a static view file associated with the route
|
|
*/
|
|
var getViewPaths = function (routes) {
|
|
return routes.map(function (route) {
|
|
return route.pattern;
|
|
});
|
|
}
|
|
|
|
/*
|
|
* Given a list of patterns for paths, OR all of them together into one
|
|
* string suitable for a Fastly condition
|
|
*/
|
|
var pathsToCondition = function (paths) {
|
|
return paths.reduce(function(conditionString, pattern) {
|
|
var patternCondition = 'req.url ~ "' + pattern + '"';
|
|
return conditionString + (conditionString ? ' || ' : '') + patternCondition;
|
|
}, '');
|
|
}
|
|
|
|
/*
|
|
* Combine static paths, routes, and any additional paths to a single
|
|
* fastly condition to match req.url
|
|
*/
|
|
var getAppRouteCondition = function (pathToStatic, routes, additionalPaths) {
|
|
var staticPaths = getStaticPaths(pathToStatic);
|
|
var viewPaths = getViewPaths(routes);
|
|
var allPaths = [].concat(staticPaths, viewPaths, additionalPaths);
|
|
return pathsToCondition(allPaths);
|
|
}
|
|
|
|
var getConditionNameForView = function (view) {
|
|
return 'routes/' + view;
|
|
};
|
|
|
|
var getHeaderNameForView = function (view) {
|
|
return 'rewrites/' + view;
|
|
}
|
|
|
|
var notPassCondition = {
|
|
name: NOT_PASS_REQUEST_CONDITION_NAME,
|
|
statement: getAppRouteCondition('../static/*', routes, extraAppRoutes),
|
|
type: 'REQUEST',
|
|
priority: 10
|
|
};
|
|
var passCondition = {
|
|
name: PASS_REQUEST_CONDITION_NAME,
|
|
statement: fastly.negateConditionStatement(notPassCondition.statement),
|
|
type: 'REQUEST',
|
|
priority: 10
|
|
};
|
|
var passCacheCondition = defaults({name: PASS_CACHE_CONDITION_NAME, type: 'CACHE'}, passCondition);
|
|
var bucketNameHeader = {
|
|
name: BUCKET_NAME_HEADER_NAME,
|
|
action: 'set',
|
|
ignore_if_set: 0,
|
|
type: 'REQUEST',
|
|
dst: 'http.host',
|
|
src: '"' + s3Bucket + '"',
|
|
request_condition: NOT_PASS_REQUEST_CONDITION_NAME,
|
|
priority: 1
|
|
};
|
|
|
|
async.waterfall([
|
|
fastly.getLatestVersion.bind(fastly),
|
|
function (version, next) {
|
|
if (version.active) return next('Latest version is active. Will not modify.');
|
|
if (version.locked) return next('Latest version is locked. Cannot modify.');
|
|
async.parallel([
|
|
function (cb) {
|
|
async.series([
|
|
async.apply(fastly.setCondition.bind(fastly), version.number, notPassCondition),
|
|
async.apply(fastly.setFastlyHeader.bind(fastly), version.number, bucketNameHeader)
|
|
], cb)
|
|
},
|
|
function (cb) {
|
|
async.series([
|
|
async.apply(fastly.setCondition.bind(fastly), version.number, passCondition),
|
|
function (cb) {
|
|
async.parallel([
|
|
async.apply(
|
|
fastly.request.bind(fastly), 'PUT',
|
|
fastly.getFastlyAPIPrefix(serviceId, version.number) + '/request_settings/Pass',
|
|
{request_condition: PASS_REQUEST_CONDITION_NAME}
|
|
),
|
|
async.apply(
|
|
fastly.request.bind(fastly), 'PUT',
|
|
fastly.getFastlyAPIPrefix(serviceId, version.number) + '/backend/femto',
|
|
{request_condition: PASS_REQUEST_CONDITION_NAME}
|
|
)
|
|
], cb)
|
|
}
|
|
], cb)
|
|
},
|
|
function (cb) {
|
|
async.series([
|
|
async.apply(fastly.setCondition.bind(fastly), version.number, passCacheCondition),
|
|
async.apply(
|
|
fastly.request.bind(fastly), 'PUT',
|
|
fastly.getFastlyAPIPrefix(serviceId, version.number) + '/cache_settings/Pass',
|
|
{cache_condition: PASS_CACHE_CONDITION_NAME}
|
|
),
|
|
], cb)
|
|
},
|
|
function (cb) {
|
|
async.forEachOf(routes, function (route, id, cb) {
|
|
var condition = {
|
|
name: getConditionNameForView(route.view),
|
|
statement: 'req.url ~ "' + route.pattern + '"',
|
|
type: 'REQUEST',
|
|
priority: id
|
|
};
|
|
var header = {
|
|
name: getHeaderNameForView(route.view),
|
|
action: 'set',
|
|
ignore_if_set: 0,
|
|
type: 'request',
|
|
dst: 'url',
|
|
src: '"/' + route.view + '.html"',
|
|
request_condition: getConditionNameForView(route.view),
|
|
priority: 10
|
|
};
|
|
async.series([
|
|
async.apply(fastly.setCondition.bind(fastly), version.number, condition),
|
|
async.apply(fastly.setFastlyHeader.bind(fastly), version.number, header)
|
|
], cb);
|
|
}, cb)
|
|
}
|
|
], next);
|
|
}], function (err, responses) {
|
|
if (err) {
|
|
console.log(err);
|
|
process.exit(1);
|
|
}
|
|
}
|
|
); |