diff --git a/src/apptask.js b/src/apptask.js index 430e68467..a0a177997 100644 --- a/src/apptask.js +++ b/src/apptask.js @@ -25,7 +25,8 @@ var assert = require('assert'), exports = module.exports = { initialize: initialize, - start: start + start: start, + setNakedDomain: setNakedDomain }; // FIXME: For some reason our selfhost.io certificate doesn't work with @@ -71,8 +72,8 @@ function forwardFromHostToVirtualBox(rulename, port) { } } -function configureNginx(app, freePort, callback) { - var nginxConf = ejs.render(NGINX_APPCONFIG_EJS, { vhost: app.location + '.' + HOSTNAME, port: freePort }); +function configureNginx(app, httpPort, callback) { + var nginxConf = ejs.render(NGINX_APPCONFIG_EJS, { vhost: app.location + '.' + HOSTNAME, port: httpPort }); var nginxConfigFilename = path.join(nginxAppConfigDir, app.location + '.conf'); // TODO: check if app.location is safe debug('writing config to ' + nginxConfigFilename); @@ -84,10 +85,26 @@ function configureNginx(app, freePort, callback) { if (error) return callback(error); return callback(null); - // missing 'return' is intentional }); - forwardFromHostToVirtualBox(app.id + '-http', freePort); + forwardFromHostToVirtualBox(app.id + '-http', httpPort); + }); +} + +function setNakedDomain(app, callback) { + var nginxConf = app ? ejs.render(NGINX_APPCONFIG_EJS, { vhost: HOSTNAME, port: app.httpPort }) : ''; + + var nginxNakedDomainFilename = path.join(nginxAppConfigDir, '../naked_domain.conf'); // TODO: check if app.location is safe + debug('writing naked domain config to ' + nginxNakedDomainFilename); + + fs.writeFile(nginxNakedDomainFilename, nginxConf, function (error) { + if (error) return callback(error); + + child_process.exec("supervisorctl -c supervisor/supervisord.conf restart nginx", { timeout: 10000 }, function (error, stdout, stderr) { + if (error) return callback(error); + + return callback(null); + }); }); } diff --git a/src/routes/index.js b/src/routes/index.js index 76c2c03ca..eedbd7f18 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -7,6 +7,7 @@ exports = module.exports = { sync: require('./sync.js'), fileops: require('./fileops.js'), oauth2: require('./oauth2.js'), + settings: require('./settings.js'), volume: require('./volume.js') }; diff --git a/src/routes/settings.js b/src/routes/settings.js new file mode 100644 index 000000000..7631af754 --- /dev/null +++ b/src/routes/settings.js @@ -0,0 +1,47 @@ +/* jslint node:true */ + +'use strict'; + +var HttpError = require('../httperror.js'), + HttpSuccess = require('../httpsuccess.js'), + debug = require('debug')('box:routes/settings'), + settingsdb = require('../settingsdb.js'), + DatabaseError = require('../databaseerror.js'), + apptask = require('../apptask.js'), + appdb = require('../appdb.js'); + +exports = module.exports = { + getNakedDomain: getNakedDomain, + setNakedDomain: setNakedDomain +}; + +function getNakedDomain(req, res, next) { + settingsdb.get(settingsdb.NAKED_DOMAIN_KEY, function (error, value) { + if (error && error.reason === DatabaseError.NOT_FOUND) return next(new HttpSuccess(200, { appid: '' })); + if (error) return next(new HttpError(500, 'Internal error: ' + error)); + + next(new HttpSuccess(200, { appid: value })); + }); +} + +function setNakedDomain(req, res, next) { + var data = req.body; + if (!data || typeof data.appid !== 'string') return next(new HttpError(400, 'appid is required')); + + function getApp(appid, callback) { return appid !== '' ? appdb.get(appid, callback): callback(null); } + + getApp(data.appid, function (error, app) { + if (error && error.reason === DatabaseError.NOT_FOUND) return next(new HttpError(404, 'No such app')); + + apptask.setNakedDomain(app, function (error) { + if (error) return next(new HttpError(500, 'Error setting naked domain: ' + error)); + + settingsdb.set(settingsdb.NAKED_DOMAIN_KEY, data.appid, function (error) { + if (error) return next(new HttpError(500, 'Internal error: ' + error)); + + next(new HttpSuccess(200, { })); + }); + }); + }); +} + diff --git a/src/server.js b/src/server.js index d7d0dfff2..ea8c8585e 100644 --- a/src/server.js +++ b/src/server.js @@ -273,6 +273,10 @@ Server.prototype._initializeExpressSync = function () { // subdomain routes router.get('/api/v1/subdomain/:subdomain', routes.apps.getAppBySubdomain); // TODO: allow non-authenticated for the appstatus page + + // settings routes + router.get('/api/v1/settings/naked_domain', both, routes.settings.getNakedDomain); + router.post('/api/v1/settings/naked_domain', both, routes.settings.setNakedDomain); }; Server.prototype._initialize2 = function (callback) { diff --git a/src/settingsdb.js b/src/settingsdb.js index b64c6e083..a79d9f9e2 100644 --- a/src/settingsdb.js +++ b/src/settingsdb.js @@ -8,7 +8,9 @@ exports = module.exports = { init: init, get: get, getAll: getAll, - set: set + set: set, + + NAKED_DOMAIN_KEY: 'naked_domain' }; var DatabaseError = require('./databaseerror'), diff --git a/webadmin/js/client.js b/webadmin/js/client.js index e42bf6f64..3220529cb 100644 --- a/webadmin/js/client.js +++ b/webadmin/js/client.js @@ -193,6 +193,28 @@ angular.module('YellowTent') }); }; + Client.prototype.getNakedDomain = function (callback) { + $http.get('/api/v1/settings/naked_domain') + .success(function (data, status, headers, config) { + if (status !== 200) return callback(new ClientError(status, data)); + callback(null, data.appid); + }) + .error(function (data, status, headers, config) { + callback(new ClientError(status, data)); + }); + }; + + Client.prototype.setNakedDomain = function (appid, callback) { + $http.post('/api/v1/settings/naked_domain', { appid: appid }) + .success(function (data, status, headers, config) { + if (status !== 200) return callback(new ClientError(status, data)); + callback(null); + }) + .error(function (data, status, headers, config) { + callback(new ClientError(status, data)); + }); + }; + Client.prototype.createAdmin = function (username, password, email, callback) { var payload = { username: username, diff --git a/webadmin/views/settings.html b/webadmin/views/settings.html index 1ef27d882..e10417026 100644 --- a/webadmin/views/settings.html +++ b/webadmin/views/settings.html @@ -19,6 +19,14 @@
Password
+
+
Set naked domain app
+
+
+ +
+
+
  • diff --git a/webadmin/views/settings.js b/webadmin/views/settings.js index c1e3a32df..43bd21b05 100644 --- a/webadmin/views/settings.js +++ b/webadmin/views/settings.js @@ -2,6 +2,21 @@ var SettingsController = function ($scope, Client) { $scope.user = Client.getUserInfo(); + $scope.nakedDomain = ''; + + Client.getNakedDomain(function (error, appid) { + if (error) return console.error(error); + + $scope.nakedDomain = appid; + }); + + $scope.setNakedDomain = function () { + Client.setNakedDomain($scope.nakedDomain, function (error) { + if (error) return console.error(error); + + console.log('Updated naked domain'); + }); + }; $scope.changePassword = function () { window.location.href = '#/userpassword';