2015-07-20 00:09:47 -07:00
'use strict' ;
exports = module . exports = {
activate : activate ,
2017-01-05 11:52:25 +01:00
dnsSetup : dnsSetup ,
2015-07-20 00:09:47 -07:00
setupTokenAuth : setupTokenAuth ,
getStatus : getStatus ,
reboot : reboot ,
2016-06-27 22:24:30 -05:00
migrate : migrate ,
2015-07-20 00:09:47 -07:00
getProgress : getProgress ,
getConfig : getConfig ,
update : update ,
2016-06-07 20:24:41 -07:00
feedback : feedback ,
checkForUpdates : checkForUpdates
2015-07-20 00:09:47 -07:00
} ;
var assert = require ( 'assert' ) ,
2016-06-07 20:24:41 -07:00
async = require ( 'async' ) ,
2015-07-20 00:09:47 -07:00
cloudron = require ( '../cloudron.js' ) ,
CloudronError = cloudron . CloudronError ,
2016-04-30 13:56:03 -07:00
config = require ( '../config.js' ) ,
2015-07-20 00:09:47 -07:00
debug = require ( 'debug' ) ( 'box:routes/cloudron' ) ,
HttpError = require ( 'connect-lastmile' ) . HttpError ,
HttpSuccess = require ( 'connect-lastmile' ) . HttpSuccess ,
2016-04-30 13:56:03 -07:00
progress = require ( '../progress.js' ) ,
mailer = require ( '../mailer.js' ) ,
2016-06-07 20:24:41 -07:00
superagent = require ( 'superagent' ) ,
2016-07-02 16:03:21 -05:00
updateChecker = require ( '../updatechecker.js' ) ,
_ = require ( 'underscore' ) ;
2015-07-20 00:09:47 -07:00
2016-05-01 13:15:30 -07:00
function auditSource ( req ) {
2016-05-01 13:29:11 -07:00
var ip = req . headers [ 'x-forwarded-for' ] || req . connection . remoteAddress || null ;
2016-05-01 13:15:30 -07:00
return { ip : ip , username : req . user ? req . user . username : null , userId : req . user ? req . user . id : null } ;
}
2015-07-20 00:09:47 -07:00
function activate ( req , res , next ) {
assert . strictEqual ( typeof req . body , 'object' ) ;
if ( typeof req . body . username !== 'string' ) return next ( new HttpError ( 400 , 'username must be string' ) ) ;
if ( typeof req . body . password !== 'string' ) return next ( new HttpError ( 400 , 'password must be string' ) ) ;
if ( typeof req . body . email !== 'string' ) return next ( new HttpError ( 400 , 'email must be string' ) ) ;
2016-01-20 16:14:21 +01:00
if ( 'displayName' in req . body && typeof req . body . displayName !== 'string' ) return next ( new HttpError ( 400 , 'displayName must be string' ) ) ;
2015-07-20 00:09:47 -07:00
var username = req . body . username ;
var password = req . body . password ;
var email = req . body . email ;
2016-01-19 23:34:49 -08:00
var displayName = req . body . displayName || '' ;
2015-07-20 00:09:47 -07:00
var ip = req . headers [ 'x-forwarded-for' ] || req . connection . remoteAddress ;
debug ( 'activate: username:%s ip:%s' , username , ip ) ;
2016-05-01 13:27:57 -07:00
cloudron . activate ( username , password , email , displayName , ip , auditSource ( req ) , function ( error , info ) {
2015-07-20 00:09:47 -07:00
if ( error && error . reason === CloudronError . ALREADY _PROVISIONED ) return next ( new HttpError ( 409 , 'Already setup' ) ) ;
2016-06-02 00:06:54 -07:00
if ( error && error . reason === CloudronError . BAD _FIELD ) return next ( new HttpError ( 400 , error . message ) ) ;
2015-07-20 00:09:47 -07:00
if ( error ) return next ( new HttpError ( 500 , error ) ) ;
2015-12-29 15:56:37 +01:00
// only in caas case do we have to notify the api server about activation
if ( config . provider ( ) !== 'caas' ) return next ( new HttpSuccess ( 201 , info ) ) ;
2015-07-20 00:09:47 -07:00
// Now let the api server know we got activated
2016-09-12 12:53:51 -07:00
superagent . post ( config . apiServerOrigin ( ) + '/api/v1/boxes/' + config . fqdn ( ) + '/setup/done' ) . query ( { setupToken : req . query . setupToken } )
. timeout ( 30 * 1000 )
. end ( function ( error , result ) {
2015-12-15 09:12:52 -08:00
if ( error && ! error . response ) return next ( new HttpError ( 500 , error ) ) ;
2015-07-20 00:09:47 -07:00
if ( result . statusCode === 403 ) return next ( new HttpError ( 403 , 'Invalid token' ) ) ;
if ( result . statusCode === 409 ) return next ( new HttpError ( 409 , 'Already setup' ) ) ;
2015-12-29 16:07:04 +01:00
if ( result . statusCode !== 201 ) return next ( new HttpError ( 500 , result . text || 'Internal error' ) ) ;
2015-07-20 00:09:47 -07:00
next ( new HttpSuccess ( 201 , info ) ) ;
} ) ;
} ) ;
}
2017-01-05 11:52:25 +01:00
function dnsSetup ( req , res , next ) {
assert . strictEqual ( typeof req . body , 'object' ) ;
if ( typeof req . body . provider !== 'string' ) return next ( new HttpError ( 400 , 'provider is required' ) ) ;
2017-01-10 15:55:31 -08:00
if ( typeof req . body . domain !== 'string' || ! req . body . domain ) return next ( new HttpError ( 400 , 'domain is required' ) ) ;
2017-01-12 11:00:46 -08:00
cloudron . dnsSetup ( req . body , req . body . domain , function ( error ) {
if ( error && error . reason === CloudronError . ALREADY _SETUP ) return next ( new HttpError ( 409 , error . message ) ) ;
if ( error && error . reason === CloudronError . BAD _FIELD ) return next ( new HttpError ( 400 , error . message ) ) ;
2017-01-05 11:52:25 +01:00
if ( error ) return next ( new HttpError ( 500 , error ) ) ;
2017-01-05 17:17:47 -08:00
next ( new HttpSuccess ( 200 ) ) ;
2017-01-05 11:52:25 +01:00
} ) ;
}
2015-07-20 00:09:47 -07:00
function setupTokenAuth ( req , res , next ) {
assert . strictEqual ( typeof req . query , 'object' ) ;
2017-03-14 13:45:04 +01:00
if ( config . provider ( ) === 'caas' ) {
if ( typeof req . query . setupToken !== 'string' || ! req . query . setupToken ) return next ( new HttpError ( 400 , 'setupToken must be a non empty string' ) ) ;
2015-12-29 11:24:45 +01:00
2017-03-14 13:45:04 +01:00
superagent . get ( config . apiServerOrigin ( ) + '/api/v1/boxes/' + config . fqdn ( ) + '/setup/verify' ) . query ( { setupToken : req . query . setupToken } )
2016-09-12 12:53:51 -07:00
. timeout ( 30 * 1000 )
. end ( function ( error , result ) {
2017-03-14 13:45:04 +01:00
if ( error && ! error . response ) return next ( new HttpError ( 500 , error ) ) ;
if ( result . statusCode === 403 ) return next ( new HttpError ( 403 , 'Invalid token' ) ) ;
if ( result . statusCode === 409 ) return next ( new HttpError ( 409 , 'Already setup' ) ) ;
if ( result . statusCode !== 200 ) return next ( new HttpError ( 500 , result . text || 'Internal error' ) ) ;
next ( ) ;
} ) ;
} else if ( config . provider ( ) === 'ami' ) {
if ( typeof req . query . setupToken !== 'string' || ! req . query . setupToken ) return next ( new HttpError ( 400 , 'setupToken must be a non empty string' ) ) ;
2015-07-20 00:09:47 -07:00
2017-03-14 13:45:04 +01:00
superagent . get ( 'http://169.254.169.254/latest/meta-data/instance-id' ) . timeout ( 30 * 1000 ) . end ( function ( error , result ) {
if ( error && ! error . response ) return next ( new HttpError ( 500 , error ) ) ;
if ( result . statusCode !== 200 ) return next ( new HttpError ( 500 , 'Unable to get meta data' ) ) ;
if ( result . text !== req . query . setupToken ) return next ( new HttpError ( 403 , 'Invalid token' ) ) ;
next ( ) ;
} ) ;
} else {
2015-07-20 00:09:47 -07:00
next ( ) ;
2017-03-14 13:45:04 +01:00
}
2015-07-20 00:09:47 -07:00
}
function getStatus ( req , res , next ) {
cloudron . getStatus ( function ( error , status ) {
if ( error ) return next ( new HttpError ( 500 , error ) ) ;
next ( new HttpSuccess ( 200 , status ) ) ;
} ) ;
}
function getProgress ( req , res , next ) {
return next ( new HttpSuccess ( 200 , progress . get ( ) ) ) ;
}
function reboot ( req , res , next ) {
// Finish the request, to let the appstore know we triggered the restore it
next ( new HttpSuccess ( 202 , { } ) ) ;
2016-10-03 15:49:47 -07:00
cloudron . reboot ( function ( ) { } ) ;
2015-07-20 00:09:47 -07:00
}
2016-06-27 22:24:30 -05:00
function migrate ( req , res , next ) {
2016-07-04 23:08:51 -05:00
if ( config . provider ( ) !== 'caas' ) return next ( new HttpError ( 422 , 'Cannot use migrate API with this provider' ) ) ;
2016-06-27 22:24:30 -05:00
2016-07-04 23:08:51 -05:00
if ( 'size' in req . body && typeof req . body . size !== 'string' ) return next ( new HttpError ( 400 , 'size must be string' ) ) ;
if ( 'region' in req . body && typeof req . body . region !== 'string' ) return next ( new HttpError ( 400 , 'region must be string' ) ) ;
if ( 'domain' in req . body ) {
if ( typeof req . body . domain !== 'string' ) return next ( new HttpError ( 400 , 'domain must be string' ) ) ;
if ( typeof req . body . provider !== 'string' ) return next ( new HttpError ( 400 , 'provider must be string' ) ) ;
}
debug ( 'Migration requested domain:%s size:%s region:%s' , req . body . domain , req . body . size , req . body . region ) ;
2016-06-27 22:24:30 -05:00
2016-07-02 16:03:21 -05:00
var options = _ . pick ( req . body , 'domain' , 'size' , 'region' ) ;
2016-07-04 23:08:51 -05:00
if ( Object . keys ( options ) . length === 0 ) return next ( new HttpError ( 400 , 'no migrate option provided' ) ) ;
2016-07-02 16:03:21 -05:00
2016-07-04 23:28:03 -05:00
cloudron . migrate ( req . body , function ( error ) { // pass req.body because 'domain' can have arbitrary options
2016-06-27 22:24:30 -05:00
if ( error && error . reason === CloudronError . BAD _STATE ) return next ( new HttpError ( 409 , error . message ) ) ;
2016-07-04 23:32:27 -05:00
if ( error && error . reason === CloudronError . BAD _FIELD ) return next ( new HttpError ( 400 , error . message ) ) ;
2016-06-27 22:24:30 -05:00
if ( error ) return next ( new HttpError ( 500 , error ) ) ;
next ( new HttpSuccess ( 202 , { } ) ) ;
} ) ;
}
2015-07-20 00:09:47 -07:00
function getConfig ( req , res , next ) {
cloudron . getConfig ( function ( error , cloudronConfig ) {
if ( error ) return next ( new HttpError ( 500 , error ) ) ;
next ( new HttpSuccess ( 200 , cloudronConfig ) ) ;
} ) ;
}
function update ( req , res , next ) {
// this only initiates the update, progress can be checked via the progress route
2016-05-01 13:15:30 -07:00
cloudron . updateToLatest ( auditSource ( req ) , function ( error ) {
2016-01-14 11:13:00 -08:00
if ( error && error . reason === CloudronError . ALREADY _UPTODATE ) return next ( new HttpError ( 422 , error . message ) ) ;
2015-07-20 00:09:47 -07:00
if ( error && error . reason === CloudronError . BAD _STATE ) return next ( new HttpError ( 409 , error . message ) ) ;
2016-06-20 14:06:58 +02:00
if ( error && error . reason === CloudronError . SELF _UPGRADE _NOT _SUPPORTED ) return next ( new HttpError ( 412 , error . message ) ) ;
2015-07-20 00:09:47 -07:00
if ( error ) return next ( new HttpError ( 500 , error ) ) ;
next ( new HttpSuccess ( 202 , { } ) ) ;
} ) ;
}
2016-06-07 20:24:41 -07:00
function checkForUpdates ( req , res , next ) {
async . series ( [
updateChecker . checkAppUpdates ,
updateChecker . checkBoxUpdates
] , function ( ) {
next ( new HttpSuccess ( 200 , { update : updateChecker . getUpdateInfo ( ) } ) ) ;
} ) ;
}
2015-08-04 14:31:40 +02:00
function feedback ( req , res , next ) {
assert . strictEqual ( typeof req . user , 'object' ) ;
2016-02-25 21:38:37 +01:00
if ( req . body . type !== mailer . FEEDBACK _TYPE _FEEDBACK &&
req . body . type !== mailer . FEEDBACK _TYPE _TICKET &&
req . body . type !== mailer . FEEDBACK _TYPE _APP _MISSING &&
2016-04-18 17:11:36 +02:00
req . body . type !== mailer . FEEDBACK _TYPE _UPGRADE _REQUEST &&
2016-04-18 17:12:47 +02:00
req . body . type !== mailer . FEEDBACK _TYPE _APP _ERROR ) return next ( new HttpError ( 400 , 'type must be either "ticket", "feedback", "app_missing", "app_error" or "upgrade_request"' ) ) ;
2015-08-04 16:59:35 +02:00
if ( typeof req . body . subject !== 'string' || ! req . body . subject ) return next ( new HttpError ( 400 , 'subject must be string' ) ) ;
if ( typeof req . body . description !== 'string' || ! req . body . description ) return next ( new HttpError ( 400 , 'description must be string' ) ) ;
2015-08-04 14:31:40 +02:00
2015-08-04 15:39:14 +02:00
mailer . sendFeedback ( req . user , req . body . type , req . body . subject , req . body . description ) ;
next ( new HttpSuccess ( 201 , { } ) ) ;
2015-08-04 14:31:40 +02:00
}