diff --git a/Dockerfile b/Dockerfile index 2438298e..d057241e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,15 +1,21 @@ -FROM mhart/alpine-node:5.8.0 +FROM node:6-alpine -# Switch to /app +# Setup Container WORKDIR /app +ENTRYPOINT ["/usr/local/bin/dumb-init", "--"] +CMD ["npm", "start"] +EXPOSE 80 + +# Dumb-init, proper signal handling, and zombie reaping +ADD https://github.com/Yelp/dumb-init/releases/download/v1.2.1/dumb-init_1.2.1_amd64 /usr/local/bin/dumb-init +RUN chmod +x /usr/local/bin/dumb-init + # Install deps -COPY package.json ./ +COPY package.json /app/package.json RUN npm install --production -# Copy source -COPY . ./ + +# Copy Source +COPY . /app # Ports ENV PORT 80 -EXPOSE 80 - -ENTRYPOINT ["npm", "start"] diff --git a/app.json b/app.json index fa7b54dd..239c959a 100644 --- a/app.json +++ b/app.json @@ -16,6 +16,9 @@ }, "API_PASSWORD": { "description": "Password for private API access" + }, + "ROUTE_PREFIX": { + "description": "Custom prefix for all routes, defaults to /" } } -} \ No newline at end of file +} diff --git a/bin/web.js b/bin/web.js index 9e7c0aab..38e54bfc 100644 --- a/bin/web.js +++ b/bin/web.js @@ -18,6 +18,7 @@ if (process.env.ANALYTICS_TOKEN) { } var myNuts = nuts.Nuts({ + routePrefix: process.env.ROUTE_PREFIX, repository: process.env.GITHUB_REPO, token: process.env.GITHUB_TOKEN, endpoint: process.env.GITHUB_ENDPOINT, diff --git a/lib/nuts.js b/lib/nuts.js index 1642158b..9219d790 100644 --- a/lib/nuts.js +++ b/lib/nuts.js @@ -35,9 +35,16 @@ function Nuts(opts) { preFetch: true, // Secret for GitHub webhook - refreshSecret: 'secret' + refreshSecret: 'secret', + + // Prefix for all routes + routePrefix: '/' }); + if (this.opts.routePrefix.substr(this.opts.routePrefix.length - 1, 1) !== '/') { + throw new Error('ROUTE_PREIX must end with a slash'); + } + // .init() is now a memoized version of ._init() this.init = _.memoize(this._init); @@ -51,26 +58,26 @@ function Nuts(opts) { // Bind routes this.router.use(useragent.express()); - this.router.get('/', this.onDownload); - this.router.get('/download/channel/:channel/:platform?', this.onDownload); - this.router.get('/download/version/:tag/:platform?', this.onDownload); - this.router.get('/download/:tag/:filename', this.onDownload); - this.router.get('/download/:platform?', this.onDownload); + this.router.get(`${that.opts.routePrefix}`, this.onDownload); + this.router.get(`${that.opts.routePrefix}download/channel/:channel/:platform?`, this.onDownload); + this.router.get(`${that.opts.routePrefix}download/version/:tag/:platform?`, this.onDownload); + this.router.get(`${that.opts.routePrefix}download/:tag/:filename`, this.onDownload); + this.router.get(`${that.opts.routePrefix}download/:platform?`, this.onDownload); - this.router.get('/feed/channel/:channel.atom', this.onServeVersionsFeed); + this.router.get(`${that.opts.routePrefix}feed/channel/:channel.atom`, this.onServeVersionsFeed); - this.router.get('/update', this.onUpdateRedirect); - this.router.get('/update/:platform/:version', this.onUpdate); - this.router.get('/update/channel/:channel/:platform/:version', this.onUpdate); - this.router.get('/update/:platform/:version/RELEASES', this.onUpdateWin); - this.router.get('/update/channel/:channel/:platform/:version/RELEASES', this.onUpdateWin); + this.router.get(`${that.opts.routePrefix}update`, this.onUpdateRedirect); + this.router.get(`${that.opts.routePrefix}update/:platform/:version`, this.onUpdate); + this.router.get(`${that.opts.routePrefix}update/channel/:channel/:platform/:version`, this.onUpdate); + this.router.get(`${that.opts.routePrefix}update/:platform/:version/RELEASES`, this.onUpdateWin); + this.router.get(`${that.opts.routePrefix}update/channel/:channel/:platform/:version/RELEASES`, this.onUpdateWin); - this.router.get('/notes/:version?', this.onServeNotes); + this.router.get(`${that.opts.routePrefix}notes/:version?`, this.onServeNotes); // Bind API - this.router.use('/api', this.onAPIAccessControl); + this.router.use(`${that.opts.routePrefix}api`, this.onAPIAccessControl); _.each(API_METHODS, function(method, route) { - this.router.get('/api/' + route, function(req, res, next) { + this.router.get(`${that.opts.routePrefix}api/${route}`, function(req, res, next) { return Q() .then(function() { return method.call(that, req); @@ -197,12 +204,14 @@ Nuts.prototype.onDownload = function(req, res, next) { // Request to update Nuts.prototype.onUpdateRedirect = function(req, res, next) { + var that = this; + Q() .then(function() { if (!req.query.version) throw new Error('Requires "version" parameter'); if (!req.query.platform) throw new Error('Requires "platform" parameter'); - return res.redirect('/update/'+req.query.platform+'/'+req.query.version); + return res.redirect(`${that.opts.routePrefix}update/${req.query.platform}/${req.query.version}`); }) .fail(next); }; @@ -241,7 +250,7 @@ Nuts.prototype.onUpdate = function(req, res, next) { console.error(latest.tag); var gitFilePath = (channel === '*' ? '/../../../' : '/../../../../../'); res.status(200).send({ - "url": urljoin(fullUrl, gitFilePath, '/download/version/'+latest.tag+'/'+platform+'?filetype='+filetype), + "url": urljoin(fullUrl, gitFilePath, `download/version/${latest.tag}/${platform}?filetype=${filetype}`), "name": latest.tag, "notes": releaseNotes, "pub_date": latest.published_at.toISOString() @@ -291,7 +300,7 @@ Nuts.prototype.onUpdateWin = function(req, res, next) { // Change filename to use download proxy .map(function(entry) { var gitFilePath = (channel === '*' ? '../../../../' : '../../../../../../'); - entry.filename = urljoin(fullUrl, gitFilePath, '/download/'+entry.semver+'/'+entry.filename); + entry.filename = urljoin(fullUrl, gitFilePath, `download/${entry.semver}/${entry.filename}`); return entry; }) @@ -366,7 +375,7 @@ Nuts.prototype.onServeVersionsFeed = function(req, res, next) { _.each(versions, function(version) { feed.addItem({ title: version.tag, - link: urljoin(fullUrl, '/../../../', '/download/version/'+version.tag), + link: urljoin(fullUrl, '/../../../', `download/version/${version.tag}`), description: version.notes, date: version.published_at, author: []