2018-01-29 15:47:26 -08:00
'use strict' ;
exports = module . exports = {
2018-12-11 15:29:47 -08:00
setup : setup ,
2018-01-29 15:47:26 -08:00
restore : restore ,
activate : activate ,
2020-06-03 12:43:32 -07:00
getStatus : getStatus
2018-01-29 15:47:26 -08:00
} ;
2020-06-16 14:37:06 +02:00
var assert = require ( 'assert' ) ,
2018-01-29 15:47:26 -08:00
async = require ( 'async' ) ,
backups = require ( './backups.js' ) ,
2019-10-22 20:36:20 -07:00
BoxError = require ( './boxerror.js' ) ,
2020-10-18 10:15:36 -07:00
branding = require ( './branding.js' ) ,
2018-01-29 15:47:26 -08:00
constants = require ( './constants.js' ) ,
cloudron = require ( './cloudron.js' ) ,
2018-12-11 15:29:47 -08:00
debug = require ( 'debug' ) ( 'box:provision' ) ,
2018-01-29 15:47:26 -08:00
domains = require ( './domains.js' ) ,
eventlog = require ( './eventlog.js' ) ,
mail = require ( './mail.js' ) ,
semver = require ( 'semver' ) ,
settings = require ( './settings.js' ) ,
2019-11-11 16:05:53 -08:00
sysinfo = require ( './sysinfo.js' ) ,
2018-04-29 10:58:45 -07:00
users = require ( './users.js' ) ,
2018-01-29 15:47:26 -08:00
tld = require ( 'tldjs' ) ,
2020-02-06 16:57:33 +01:00
tokens = require ( './tokens.js' ) ,
2018-12-15 15:27:16 -08:00
_ = require ( 'underscore' ) ;
const NOOP _CALLBACK = function ( error ) { if ( error ) debug ( error ) ; } ;
// we cannot use tasks since the tasks table gets overwritten when db is imported
let gProvisionStatus = {
setup : {
active : false ,
message : '' ,
errorMessage : null
} ,
restore : {
active : false ,
message : '' ,
errorMessage : null
}
} ;
2018-01-29 15:47:26 -08:00
2018-12-15 15:27:16 -08:00
function setProgress ( task , message , callback ) {
2018-12-22 20:48:03 -08:00
debug ( ` setProgress: ${ task } - ${ message } ` ) ;
2018-12-15 15:27:16 -08:00
gProvisionStatus [ task ] . message = message ;
callback ( ) ;
}
2018-12-07 14:35:04 -08:00
function unprovision ( callback ) {
2018-01-29 15:47:26 -08:00
assert . strictEqual ( typeof callback , 'function' ) ;
2018-12-07 14:35:04 -08:00
debug ( 'unprovision' ) ;
2018-01-29 15:47:26 -08:00
2018-12-07 14:35:04 -08:00
// TODO: also cancel any existing configureWebadmin task
async . series ( [
2020-08-15 19:24:32 -07:00
settings . setAdminLocation . bind ( null , '' , '' ) ,
2018-12-07 14:35:04 -08:00
mail . clearDomains ,
domains . clear
] , callback ) ;
}
2018-02-09 12:43:03 +01:00
2019-02-26 12:32:35 -08:00
2019-10-29 20:12:37 -07:00
function setup ( dnsConfig , sysinfoConfig , auditSource , callback ) {
2018-12-07 14:35:04 -08:00
assert . strictEqual ( typeof dnsConfig , 'object' ) ;
2019-10-29 20:12:37 -07:00
assert . strictEqual ( typeof sysinfoConfig , 'object' ) ;
2018-12-07 14:35:04 -08:00
assert . strictEqual ( typeof auditSource , 'object' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
2018-01-29 15:47:26 -08:00
2019-10-23 15:45:09 -07:00
if ( gProvisionStatus . setup . active || gProvisionStatus . restore . active ) return callback ( new BoxError ( BoxError . BAD _STATE , 'Already setting up or restoring' ) ) ;
2018-01-29 15:47:26 -08:00
2018-12-15 15:27:16 -08:00
gProvisionStatus . setup = { active : true , errorMessage : '' , message : 'Adding domain' } ;
2018-01-29 15:47:26 -08:00
2018-12-15 15:27:16 -08:00
function done ( error ) {
gProvisionStatus . setup . active = false ;
gProvisionStatus . setup . errorMessage = error ? error . message : '' ;
callback ( error ) ;
}
2018-03-08 15:10:38 -08:00
2018-12-15 15:27:16 -08:00
users . isActivated ( function ( error , activated ) {
2019-10-24 20:31:45 -07:00
if ( error ) return done ( error ) ;
2019-10-23 15:45:09 -07:00
if ( activated ) return done ( new BoxError ( BoxError . CONFLICT , 'Already activated' , { activate : true } ) ) ;
2018-12-15 15:27:16 -08:00
unprovision ( function ( error ) {
2019-10-23 15:45:09 -07:00
if ( error ) return done ( error ) ;
2018-11-10 00:43:46 -08:00
2018-12-07 14:35:04 -08:00
const domain = dnsConfig . domain . toLowerCase ( ) ;
const zoneName = dnsConfig . zoneName ? dnsConfig . zoneName : ( tld . getDomain ( domain ) || domain ) ;
2018-10-30 18:43:52 -07:00
2018-12-15 15:27:16 -08:00
debug ( ` provision: Setting up Cloudron with domain ${ domain } and zone ${ zoneName } ` ) ;
2018-11-10 00:43:46 -08:00
2018-12-07 14:35:04 -08:00
let data = {
zoneName : zoneName ,
provider : dnsConfig . provider ,
config : dnsConfig . config ,
fallbackCertificate : dnsConfig . fallbackCertificate || null ,
2020-03-31 12:04:46 -07:00
tlsConfig : dnsConfig . tlsConfig || { provider : 'letsencrypt-prod' } ,
dkimSelector : 'cloudron'
2018-12-07 14:35:04 -08:00
} ;
2018-10-30 18:43:52 -07:00
2020-08-15 23:17:47 -07:00
async . series ( [
settings . setMailLocation . bind ( null , domain , ` ${ constants . ADMIN _LOCATION } . ${ domain } ` ) , // default mail location. do this before we add the domain for upserting mail DNS
domains . add . bind ( null , domain , data , auditSource ) ,
sysinfo . testConfig . bind ( null , sysinfoConfig )
] , function ( error ) {
2019-10-23 15:45:09 -07:00
if ( error ) return done ( error ) ;
2018-12-15 15:27:16 -08:00
2020-08-15 23:17:47 -07:00
callback ( ) ; // now that args are validated run the task in the background
async . series ( [
settings . setSysinfoConfig . bind ( null , sysinfoConfig ) ,
cloudron . setupDnsAndCert . bind ( null , constants . ADMIN _LOCATION , domain , auditSource , ( progress ) => setProgress ( 'setup' , progress . message , NOOP _CALLBACK ) ) ,
cloudron . setDashboardDomain . bind ( null , domain , auditSource ) ,
setProgress . bind ( null , 'setup' , 'Done' ) ,
eventlog . add . bind ( null , eventlog . ACTION _PROVISION , auditSource , { } )
] , function ( error ) {
gProvisionStatus . setup . active = false ;
gProvisionStatus . setup . errorMessage = error ? error . message : '' ;
2018-12-15 15:27:16 -08:00
} ) ;
2018-12-07 14:35:04 -08:00
} ) ;
2018-10-30 18:43:52 -07:00
} ) ;
2018-01-29 15:47:26 -08:00
} ) ;
}
function activate ( username , password , email , displayName , ip , auditSource , callback ) {
assert . strictEqual ( typeof username , 'string' ) ;
assert . strictEqual ( typeof password , 'string' ) ;
assert . strictEqual ( typeof email , 'string' ) ;
assert . strictEqual ( typeof displayName , 'string' ) ;
assert . strictEqual ( typeof ip , 'string' ) ;
assert . strictEqual ( typeof auditSource , 'object' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
debug ( 'activating user:%s email:%s' , username , email ) ;
2018-04-29 10:58:45 -07:00
users . createOwner ( username , password , email , displayName , auditSource , function ( error , userObject ) {
2019-10-24 20:31:45 -07:00
if ( error && error . reason === BoxError . ALREADY _EXISTS ) return callback ( new BoxError ( BoxError . CONFLICT , 'Already activated' ) ) ;
if ( error ) return callback ( error ) ;
2018-01-29 15:47:26 -08:00
2020-02-06 17:26:06 +01:00
tokens . add ( tokens . ID _WEBADMIN , userObject . id , Date . now ( ) + constants . DEFAULT _TOKEN _EXPIRATION , { } , function ( error , result ) {
2019-10-23 15:45:09 -07:00
if ( error ) return callback ( error ) ;
2018-01-29 15:47:26 -08:00
2018-06-27 23:17:04 -07:00
eventlog . add ( eventlog . ACTION _ACTIVATE , auditSource , { } ) ;
2018-01-29 15:47:26 -08:00
2018-06-29 08:27:33 -07:00
callback ( null , {
userId : userObject . id ,
token : result . accessToken ,
expires : result . expires
} ) ;
2018-01-29 15:47:26 -08:00
2021-02-24 14:56:09 -08:00
setImmediate ( cloudron . onActivated . bind ( null , { } , NOOP _CALLBACK ) ) ; // hack for now to not block the above http response
2018-01-29 15:47:26 -08:00
} ) ;
} ) ;
}
2021-02-24 14:56:09 -08:00
function restore ( backupConfig , backupId , version , sysinfoConfig , options , auditSource , callback ) {
2018-01-29 15:47:26 -08:00
assert . strictEqual ( typeof backupConfig , 'object' ) ;
assert . strictEqual ( typeof backupId , 'string' ) ;
assert . strictEqual ( typeof version , 'string' ) ;
2019-11-11 09:49:18 -08:00
assert . strictEqual ( typeof sysinfoConfig , 'object' ) ;
2021-02-24 14:56:09 -08:00
assert . strictEqual ( typeof options , 'object' ) ;
2018-11-10 00:43:46 -08:00
assert . strictEqual ( typeof auditSource , 'object' ) ;
2018-01-29 15:47:26 -08:00
assert . strictEqual ( typeof callback , 'function' ) ;
2019-10-23 15:45:09 -07:00
if ( ! semver . valid ( version ) ) return callback ( new BoxError ( BoxError . BAD _FIELD , 'version is not a valid semver' , { field : 'version' } ) ) ;
2020-08-14 10:27:23 -07:00
if ( constants . VERSION !== version ) return callback ( new BoxError ( BoxError . BAD _STATE , ` Run "cloudron-setup --version ${ version } " on a fresh Ubuntu installation to restore from this backup ` ) ) ;
2018-01-29 15:47:26 -08:00
2019-10-23 15:45:09 -07:00
if ( gProvisionStatus . setup . active || gProvisionStatus . restore . active ) return callback ( new BoxError ( BoxError . BAD _STATE , 'Already setting up or restoring' ) ) ;
2018-11-11 09:29:11 -08:00
2018-12-15 15:27:16 -08:00
gProvisionStatus . restore = { active : true , errorMessage : '' , message : 'Testing backup config' } ;
function done ( error ) {
gProvisionStatus . restore . active = false ;
gProvisionStatus . restore . errorMessage = error ? error . message : '' ;
callback ( error ) ;
}
2018-02-04 15:08:46 -08:00
2018-11-10 18:08:08 -08:00
users . isActivated ( function ( error , activated ) {
2019-10-23 15:45:09 -07:00
if ( error ) return done ( error ) ;
if ( activated ) return done ( new BoxError ( BoxError . CONFLICT , 'Already activated. Restore with a fresh Cloudron installation.' ) ) ;
2018-01-29 15:47:26 -08:00
2020-05-12 15:49:43 -07:00
backups . testProviderConfig ( backupConfig , function ( error ) {
2019-10-23 15:45:09 -07:00
if ( error ) return done ( error ) ;
2018-01-29 15:47:26 -08:00
2020-05-12 15:49:43 -07:00
if ( 'password' in backupConfig ) {
backupConfig . encryption = backups . generateEncryptionKeysSync ( backupConfig . password ) ;
delete backupConfig . password ;
2020-05-18 09:07:18 -07:00
} else {
backupConfig . encryption = null ;
2020-05-12 15:49:43 -07:00
}
2019-11-11 16:05:53 -08:00
sysinfo . testConfig ( sysinfoConfig , function ( error ) {
if ( error ) return done ( error ) ;
debug ( ` restore: restoring from ${ backupId } from provider ${ backupConfig . provider } with format ${ backupConfig . format } ` ) ;
2018-01-29 15:47:26 -08:00
2019-11-11 16:05:53 -08:00
callback ( ) ; // now that the fields are validated, continue task in the background
2018-01-29 15:47:26 -08:00
2019-11-11 16:05:53 -08:00
async . series ( [
setProgress . bind ( null , 'restore' , 'Downloading backup' ) ,
backups . restore . bind ( null , backupConfig , backupId , ( progress ) => setProgress ( 'restore' , progress . message , NOOP _CALLBACK ) ) ,
settings . setSysinfoConfig . bind ( null , sysinfoConfig ) ,
2020-08-15 23:00:39 -07:00
( done ) => {
const adminDomain = settings . adminDomain ( ) ; // load this fresh from after the backup.restore
async . series ( [
2021-02-24 14:56:09 -08:00
( next ) => {
if ( options . skipDnsSetup ) return next ( ) ;
cloudron . setupDnsAndCert ( constants . ADMIN _LOCATION , adminDomain , auditSource , ( progress ) => setProgress ( 'restore' , progress . message , NOOP _CALLBACK ) , next ) ;
} ,
2020-08-15 23:00:39 -07:00
cloudron . setDashboardDomain . bind ( null , adminDomain , auditSource )
] , done ) ;
} ,
2020-02-27 12:38:17 -08:00
settings . setBackupCredentials . bind ( null , backupConfig ) , // update just the credentials and not the policy and flags
2019-11-11 16:05:53 -08:00
eventlog . add . bind ( null , eventlog . ACTION _RESTORE , auditSource , { backupId } ) ,
] , function ( error ) {
gProvisionStatus . restore . active = false ;
gProvisionStatus . restore . errorMessage = error ? error . message : '' ;
2018-12-15 15:27:16 -08:00
2021-02-24 14:56:09 -08:00
if ( ! error ) cloudron . onActivated ( options , NOOP _CALLBACK ) ;
2019-11-11 16:05:53 -08:00
} ) ;
2018-01-29 15:47:26 -08:00
} ) ;
} ) ;
} ) ;
}
2018-12-15 15:27:16 -08:00
function getStatus ( callback ) {
assert . strictEqual ( typeof callback , 'function' ) ;
users . isActivated ( function ( error , activated ) {
2019-10-23 15:45:09 -07:00
if ( error ) return callback ( error ) ;
2018-12-15 15:27:16 -08:00
2020-03-06 01:16:27 -08:00
settings . getAll ( function ( error , allSettings ) {
2019-10-23 15:45:09 -07:00
if ( error ) return callback ( error ) ;
2018-12-15 15:27:16 -08:00
callback ( null , _ . extend ( {
2019-07-25 14:40:52 -07:00
version : constants . VERSION ,
2019-07-26 10:49:29 -07:00
apiServerOrigin : settings . apiServerOrigin ( ) , // used by CaaS tool
2019-12-23 17:15:45 +01:00
webServerOrigin : settings . webServerOrigin ( ) , // used by CaaS tool
2020-03-06 01:16:27 -08:00
cloudronName : allSettings [ settings . CLOUDRON _NAME _KEY ] ,
2020-10-18 10:15:36 -07:00
footer : branding . renderFooter ( allSettings [ settings . FOOTER _KEY ] || constants . FOOTER ) ,
2019-07-26 10:49:29 -07:00
adminFqdn : settings . adminDomain ( ) ? settings . adminFqdn ( ) : null ,
2020-11-18 23:45:16 +01:00
language : allSettings [ settings . LANGUAGE _KEY ] ,
2018-12-15 15:27:16 -08:00
activated : activated ,
2020-07-13 08:45:24 -07:00
provider : settings . provider ( ) // used by setup wizard of marketplace images
2018-12-15 15:27:16 -08:00
} , gProvisionStatus ) ) ;
} ) ;
} ) ;
}