diff --git a/.gitignore b/.gitignore index e3d51778..1ba6c42a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,7 @@ +*.deflate *.egg-info *.elc +*.gz *.less.css *.po.js *.pyc @@ -15,30 +17,25 @@ .emacs.d/site/nxhtml .emacs.d/tramp .emacs.desktop -.ipython +.node-version .virtualenvs .zsh/cache TAGS build_* -dist +locale/ node_modules npm-debug.log settings_local.js +settings_local_hosted.js smokealarm/captures/ -tags -yulelog_* -.node-version -settings_local.py - -locale/ src/locales/* src/media/build_id.txt -src/media/locales/* src/media/css/include.css src/media/css/include.css src/media/imgurls.txt src/media/include.css src/media/include.js src/media/js/include.js +src/media/locales/* src/templates.js -settings_local.js +tags diff --git a/README.md b/README.md index 5a26b914..8a9bfd09 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,31 @@ To infinity and beyond. npm install commonplace -g # Copy local configuration into place cp src/media/js/settings_local.js.dist src/media/js/settings_local.js +# Copy production configuration into place +cp src/media/js/settings_local_hosted.js.dist src/media/js/settings_local_hosted.js # Start the server damper ``` + + +## Deployment + +We use stackato: + + stackato push --no-prompt + +To start the instance on stackato: + + stackato start + +To read the logs on stackato: + + stackato logs + +To run shell commands on stackato: + + stackato run cat ../logs/stdout.log + +To access the shell on stackato: + + stackato ssh diff --git a/app.js b/app.js new file mode 100644 index 00000000..3e0ba147 --- /dev/null +++ b/app.js @@ -0,0 +1,135 @@ +var fs = require('fs'); +var http = require('http'); +var zlib = require('zlib'); + +var nodeStatic = require('node-static'); + + +const SERVER_HTML = '/server.html'; +const REWRITES = [ + {from: '^/([\?].*)?$', to: SERVER_HTML}, + {from: '^/(friends|submit|game|genre|user|settings|leaderboard|developer|review|tests|debug)([\?].*)?$', to: SERVER_HTML}, +]; +const STRIP_TRAILING_SLASHES = true; +const TEMPLATE_404 = SERVER_HTML; + + +function patchVaryHeader(headers, header) { + var v = headers['Vary']; + return headers['Vary'] = (v && v !== header ? v + ', ' : '') + header; +} + +/* Check if we should consider sending a deflate version of the file based on the + * file content type and client's Accept-Encoding header value. + */ +nodeStatic.Server.prototype.deflateOk = function(req, contentType) { + var enable = this.options.deflate; + if (enable && + (typeof enable === 'boolean' || + (contentType && enable instanceof RegExp && + enable.test(contentType)))) { + var acceptEncoding = req.headers['accept-encoding']; + return acceptEncoding && acceptEncoding.indexOf('deflate') >= 0; + } + return false; +}; + +/* Monkeypatching `node-static`'s gzip so we can do it on the fly and also + * support deflate (which results in smaller responses). + */ +nodeStatic.Server.prototype.respondGzip = function(pathname, status, + contentType, _headers, + files, stat, req, res, + finish) { + var that = this; + var gzipOk = that.gzipOk(req, contentType); + var deflateOk = that.deflateOk(req, contentType); + if (!(files.length === 1 && (gzipOk || deflateOk))) { + // Client doesn't want deflate/gzip or we're sending multiple files. + return that.respondNoGzip(pathname, status, contentType, _headers, files, + stat, req, res, finish); + } + + var compFile; + var compLib; + + if (deflateOk) { + compFile = files[0] + '.deflate'; + compLib = zlib.createDeflate(); + _headers['Content-Encoding'] = 'deflate'; + } else { + compFile = files[0] + '.gz'; + compLib = zlib.createGzip(); + _headers['Content-Encoding'] = 'gzip'; + } + + var inStr = fs.createReadStream(files[0]); + var outStr = fs.createWriteStream(compFile); + + patchVaryHeader(_headers, 'Accept-Encoding'); + + inStr.pipe(compLib).pipe(outStr); + + outStr.on('close', function() { + fs.stat(compFile, function(e, stat) { + that.respondNoGzip(pathname, status, contentType, _headers, [compFile], + stat, req, res, finish); + }); + }); +}; + +var fileServer = new nodeStatic.Server('./src', { + // TODO: Set a reasonable `max-age` (issue #41). + cache: 0, + deflate: true, + gzip: true +}); + + +function getRewrite(req, stripslashes) { + var data = {headers: {}}; + REWRITES.forEach(function(rewrite) { + var re = new RegExp(rewrite.from); + if (re.test(req.url)) { + if (rewrite.redirect) { + data.headers['Location'] = data.path; + data.statusCode = rewrite.redirect === 'permanent' ? 301 : 302; + } else { + data.statusCode = 200; + } + data.path = rewrite.to; + } else if (stripslashes && req.url.substr(-1) === '/') { + var slashlessUrl = req.url.substr(0, req.url.length - 1); + if (re.test(slashlessUrl)) { + data.headers['Location'] = slashlessUrl; + data.statusCode = 302; + } + } + }); + return data; +} + +http.createServer(function (req, res) { + req.addListener('end', function () { + if (req.url === '/') { + req.url = SERVER_HTML; + } + fileServer.serve(req, res, function(err, fileRes) { + var rewrite = getRewrite(req, STRIP_TRAILING_SLASHES); + if (rewrite.path) { + fileServer.serveFile(rewrite.path, rewrite.statusCode, + rewrite.headers, req, res); + } else if (rewrite.statusCode) { + res.writeHead(rewrite.statusCode, rewrite.headers); + res.end(); + } else if (err && err.status === 404) { + if (TEMPLATE_404) { + fileServer.serveFile(TEMPLATE_404, 404, {}, req, res); + } else { + res.writeHead(404, {'Content-Type': 'text/plain'}); + res.end('404'); + } + } + }); + }).resume(); +}).listen(process.env.PORT || 9000); diff --git a/deploy.sh b/deploy.sh new file mode 100755 index 00000000..e6c8a635 --- /dev/null +++ b/deploy.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +COMMONPLACE=./node_modules/commonplace/bin/commonplace + +find . -name 'src/*.deflate' -delete +find . -name 'src/*.gz' -delete + +npm install +npm install --force commonplace@0.2.6 + +$COMMONPLACE includes +$COMMONPLACE langpacks diff --git a/package.json b/package.json new file mode 100644 index 00000000..7e7d1604 --- /dev/null +++ b/package.json @@ -0,0 +1,18 @@ +{ + "name": "galaxy", + "version": "0.0.1", + "engines": { + "node": ">=0.10.x", + "npm": ">=1.1.x" + }, + "repository": { + "type": "git", + "url": "git://github.com/cvan/galaxy.git" + }, + "dependencies": { + "node-static": "latest" + }, + "scripts": { + "start": "node app.js" + } +} diff --git a/src/media/js/settings_local_hosted.js b/src/media/js/settings_local_hosted.js deleted file mode 100644 index 68798aa4..00000000 --- a/src/media/js/settings_local_hosted.js +++ /dev/null @@ -1,8 +0,0 @@ -define('settings_local', [], function() { - // Use this module for settings to be used in production. - var origin = window.location.origin || ( - window.location.protocol + '//' + window.location.host); - return { - api_url: origin - }; -}); diff --git a/src/media/js/settings_local_hosted.js.dist b/src/media/js/settings_local_hosted.js.dist index 68798aa4..376df29a 100644 --- a/src/media/js/settings_local_hosted.js.dist +++ b/src/media/js/settings_local_hosted.js.dist @@ -1,8 +1,6 @@ define('settings_local', [], function() { // Use this module for settings to be used in production. - var origin = window.location.origin || ( - window.location.protocol + '//' + window.location.host); return { - api_url: origin + api_url: 'https://galaxy-api.paas.allizom.org' }; }); diff --git a/src/server.html b/src/server.html new file mode 100644 index 00000000..a8ece93a --- /dev/null +++ b/src/server.html @@ -0,0 +1,39 @@ + + +
+ + +