2015-07-20 00:09:47 -07:00
'use strict' ;
exports = module . exports = {
get : get ,
getByHttpPort : getByHttpPort ,
2015-09-14 17:01:04 -07:00
getByContainerId : getByContainerId ,
2015-07-20 00:09:47 -07:00
add : add ,
exists : exists ,
del : del ,
update : update ,
getAll : getAll ,
getPortBindings : getPortBindings ,
2017-10-23 22:05:43 +02:00
delPortBinding : delPortBinding ,
2015-07-20 00:09:47 -07:00
setAddonConfig : setAddonConfig ,
getAddonConfig : getAddonConfig ,
getAddonConfigByAppId : getAddonConfigByAppId ,
2017-03-26 19:06:36 -07:00
getAddonConfigByName : getAddonConfigByName ,
2015-07-20 00:09:47 -07:00
unsetAddonConfig : unsetAddonConfig ,
unsetAddonConfigByAppId : unsetAddonConfigByAppId ,
2018-12-06 21:08:19 -08:00
getAppIdByAddonConfigValue : getAppIdByAddonConfigValue ,
2015-07-20 00:09:47 -07:00
setHealth : setHealth ,
setInstallationCommand : setInstallationCommand ,
setRunCommand : setRunCommand ,
getAppStoreIds : getAppStoreIds ,
2018-06-28 16:48:04 -07:00
setOwner : setOwner ,
transferOwnership : transferOwnership ,
2015-07-20 00:09:47 -07:00
// installation codes (keep in sync in UI)
ISTATE _PENDING _INSTALL : 'pending_install' , // installs and fresh reinstalls
2016-06-17 16:56:15 -05:00
ISTATE _PENDING _CLONE : 'pending_clone' , // clone
2015-07-20 00:09:47 -07:00
ISTATE _PENDING _CONFIGURE : 'pending_configure' , // config (location, port) changes and on infra update
ISTATE _PENDING _UNINSTALL : 'pending_uninstall' , // uninstallation
ISTATE _PENDING _RESTORE : 'pending_restore' , // restore to previous backup or on upgrade
ISTATE _PENDING _UPDATE : 'pending_update' , // update from installed state preserving data
ISTATE _PENDING _FORCE _UPDATE : 'pending_force_update' , // update from any state preserving data
ISTATE _PENDING _BACKUP : 'pending_backup' , // backup the app
ISTATE _ERROR : 'error' , // error executing last pending_* command
ISTATE _INSTALLED : 'installed' , // app is installed
RSTATE _RUNNING : 'running' ,
RSTATE _PENDING _START : 'pending_start' ,
RSTATE _PENDING _STOP : 'pending_stop' ,
RSTATE _STOPPED : 'stopped' , // app stopped by use
2015-08-24 21:58:04 -07:00
// run codes (keep in sync in UI)
2015-07-20 00:09:47 -07:00
HEALTH _HEALTHY : 'healthy' ,
HEALTH _UNHEALTHY : 'unhealthy' ,
HEALTH _ERROR : 'error' ,
HEALTH _DEAD : 'dead' ,
2018-06-29 11:04:14 +02:00
// subdomain table types
SUBDOMAIN _TYPE _PRIMARY : 'primary' ,
SUBDOMAIN _TYPE _REDIRECT : 'redirect' ,
2015-07-20 00:09:47 -07:00
_clear : clear
} ;
var assert = require ( 'assert' ) ,
async = require ( 'async' ) ,
database = require ( './database.js' ) ,
DatabaseError = require ( './databaseerror' ) ,
safe = require ( 'safetydance' ) ,
util = require ( 'util' ) ;
var APPS _FIELDS _PREFIXED = [ 'apps.id' , 'apps.appStoreId' , 'apps.installationState' , 'apps.installationProgress' , 'apps.runState' ,
2018-06-29 22:25:34 +02:00
'apps.health' , 'apps.containerId' , 'apps.manifestJson' , 'apps.httpPort' , 'subdomains.subdomain AS location' , 'subdomains.domain' ,
2018-01-20 23:40:59 -08:00
'apps.accessRestrictionJson' , 'apps.restoreConfigJson' , 'apps.oldConfigJson' , 'apps.updateConfigJson' , 'apps.memoryLimit' ,
2018-02-07 20:54:43 +01:00
'apps.xFrameOptions' , 'apps.sso' , 'apps.debugModeJson' , 'apps.robotsTxt' , 'apps.enableBackup' ,
2018-12-06 21:08:19 -08:00
'apps.creationTime' , 'apps.updateTime' , 'apps.ownerId' , 'apps.mailboxName' , 'apps.ts' ] . join ( ',' ) ;
2015-07-20 00:09:47 -07:00
2018-08-12 22:08:19 -07:00
var PORT _BINDINGS _FIELDS = [ 'hostPort' , 'type' , 'environmentVariable' , 'appId' ] . join ( ',' ) ;
2015-07-20 00:09:47 -07:00
2018-08-24 10:39:59 -07:00
const SUBDOMAIN _FIELDS = [ 'appId' , 'domain' , 'subdomain' , 'type' ] . join ( ',' ) ;
2015-07-20 00:09:47 -07:00
function postProcess ( result ) {
assert . strictEqual ( typeof result , 'object' ) ;
assert ( result . manifestJson === null || typeof result . manifestJson === 'string' ) ;
result . manifest = safe . JSON . parse ( result . manifestJson ) ;
delete result . manifestJson ;
assert ( result . oldConfigJson === null || typeof result . oldConfigJson === 'string' ) ;
result . oldConfig = safe . JSON . parse ( result . oldConfigJson ) ;
delete result . oldConfigJson ;
2017-11-16 12:36:07 -08:00
assert ( result . updateConfigJson === null || typeof result . updateConfigJson === 'string' ) ;
result . updateConfig = safe . JSON . parse ( result . updateConfigJson ) ;
delete result . updateConfigJson ;
2017-10-12 17:46:15 -07:00
2017-11-16 14:47:05 -08:00
assert ( result . restoreConfigJson === null || typeof result . restoreConfigJson === 'string' ) ;
result . restoreConfig = safe . JSON . parse ( result . restoreConfigJson ) ;
delete result . restoreConfigJson ;
2015-07-20 00:09:47 -07:00
assert ( result . hostPorts === null || typeof result . hostPorts === 'string' ) ;
assert ( result . environmentVariables === null || typeof result . environmentVariables === 'string' ) ;
result . portBindings = { } ;
2018-08-12 22:08:19 -07:00
let hostPorts = result . hostPorts === null ? [ ] : result . hostPorts . split ( ',' ) ;
let environmentVariables = result . environmentVariables === null ? [ ] : result . environmentVariables . split ( ',' ) ;
let portTypes = result . portTypes === null ? [ ] : result . portTypes . split ( ',' ) ;
2015-07-20 00:09:47 -07:00
delete result . hostPorts ;
delete result . environmentVariables ;
2018-08-12 22:08:19 -07:00
delete result . portTypes ;
2015-07-20 00:09:47 -07:00
2018-10-11 14:07:43 -07:00
for ( let i = 0 ; i < environmentVariables . length ; i ++ ) {
2018-08-12 22:08:19 -07:00
result . portBindings [ environmentVariables [ i ] ] = { hostPort : parseInt ( hostPorts [ i ] , 10 ) , type : portTypes [ i ] } ;
2015-07-20 00:09:47 -07:00
}
2015-10-13 12:29:40 +02:00
2015-10-16 15:11:54 +02:00
assert ( result . accessRestrictionJson === null || typeof result . accessRestrictionJson === 'string' ) ;
result . accessRestriction = safe . JSON . parse ( result . accessRestrictionJson ) ;
if ( result . accessRestriction && ! result . accessRestriction . users ) result . accessRestriction . users = [ ] ;
delete result . accessRestrictionJson ;
2016-07-15 11:08:11 +02:00
// TODO remove later once all apps have this attribute
result . xFrameOptions = result . xFrameOptions || 'SAMEORIGIN' ;
2016-09-06 21:56:36 -07:00
2016-11-11 10:53:41 +05:30
result . sso = ! ! result . sso ; // make it bool
2017-08-16 14:12:07 -07:00
result . enableBackup = ! ! result . enableBackup ; // make it bool
2017-01-20 05:48:25 -08:00
assert ( result . debugModeJson === null || typeof result . debugModeJson === 'string' ) ;
result . debugMode = safe . JSON . parse ( result . debugModeJson ) ;
delete result . debugModeJson ;
2018-06-29 15:07:20 +02:00
2018-06-29 15:50:18 +02:00
result . alternateDomains = result . alternateDomains || [ ] ;
result . alternateDomains . forEach ( function ( d ) {
delete d . appId ;
delete d . type ;
} ) ;
2018-10-11 14:07:43 -07:00
let envNames = JSON . parse ( result . envNames ) , envValues = JSON . parse ( result . envValues ) ;
delete result . envNames ;
delete result . envValues ;
result . env = { } ;
for ( let i = 0 ; i < envNames . length ; i ++ ) { // NOTE: envNames is [ null ] when env of an app is empty
if ( envNames [ i ] ) result . env [ envNames [ i ] ] = envValues [ i ] ;
}
2015-07-20 00:09:47 -07:00
}
function get ( id , callback ) {
assert . strictEqual ( typeof id , 'string' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
database . query ( 'SELECT ' + APPS _FIELDS _PREFIXED + ','
2018-10-11 14:07:43 -07:00
+ 'GROUP_CONCAT(CAST(appPortBindings.hostPort AS CHAR(6))) AS hostPorts, GROUP_CONCAT(appPortBindings.environmentVariable) AS environmentVariables, GROUP_CONCAT(appPortBindings.type) AS portTypes, '
+ 'JSON_ARRAYAGG(appEnvVars.name) AS envNames, JSON_ARRAYAGG(appEnvVars.value) AS envValues'
2018-06-29 11:04:14 +02:00
+ ' FROM apps'
+ ' LEFT OUTER JOIN appPortBindings ON apps.id = appPortBindings.appId'
2018-10-11 14:07:43 -07:00
+ ' LEFT OUTER JOIN appEnvVars ON apps.id = appEnvVars.appId'
2018-08-12 22:08:19 -07:00
+ ' LEFT OUTER JOIN subdomains ON apps.id = subdomains.appId AND subdomains.type = ?'
2018-06-29 11:04:14 +02:00
+ ' WHERE apps.id = ? GROUP BY apps.id' , [ exports . SUBDOMAIN _TYPE _PRIMARY , id ] , function ( error , result ) {
2015-07-20 00:09:47 -07:00
if ( error ) return callback ( new DatabaseError ( DatabaseError . INTERNAL _ERROR , error ) ) ;
if ( result . length === 0 ) return callback ( new DatabaseError ( DatabaseError . NOT _FOUND ) ) ;
2018-08-24 10:39:59 -07:00
database . query ( 'SELECT ' + SUBDOMAIN _FIELDS + ' FROM subdomains WHERE appId = ? AND type = ?' , [ id , exports . SUBDOMAIN _TYPE _REDIRECT ] , function ( error , alternateDomains ) {
2018-06-29 15:50:18 +02:00
if ( error ) return callback ( new DatabaseError ( DatabaseError . INTERNAL _ERROR , error ) ) ;
result [ 0 ] . alternateDomains = alternateDomains ;
2015-07-20 00:09:47 -07:00
2018-06-29 15:50:18 +02:00
postProcess ( result [ 0 ] ) ;
callback ( null , result [ 0 ] ) ;
2018-10-11 14:07:43 -07:00
} ) ;
2015-07-20 00:09:47 -07:00
} ) ;
}
function getByHttpPort ( httpPort , callback ) {
assert . strictEqual ( typeof httpPort , 'number' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
database . query ( 'SELECT ' + APPS _FIELDS _PREFIXED + ','
2018-10-11 14:07:43 -07:00
+ 'GROUP_CONCAT(CAST(appPortBindings.hostPort AS CHAR(6))) AS hostPorts, GROUP_CONCAT(appPortBindings.environmentVariable) AS environmentVariables, GROUP_CONCAT(appPortBindings.type) AS portTypes,'
+ 'JSON_ARRAYAGG(appEnvVars.name) AS envNames, JSON_ARRAYAGG(appEnvVars.value) AS envValues'
2018-06-29 11:04:14 +02:00
+ ' FROM apps'
+ ' LEFT OUTER JOIN appPortBindings ON apps.id = appPortBindings.appId'
2018-10-11 14:07:43 -07:00
+ ' LEFT OUTER JOIN appEnvVars ON apps.id = appEnvVars.appId'
2018-08-12 22:08:19 -07:00
+ ' LEFT OUTER JOIN subdomains ON apps.id = subdomains.appId AND subdomains.type = ?'
2018-06-29 11:04:14 +02:00
+ ' WHERE httpPort = ? GROUP BY apps.id' , [ exports . SUBDOMAIN _TYPE _PRIMARY , httpPort ] , function ( error , result ) {
2015-07-20 00:09:47 -07:00
if ( error ) return callback ( new DatabaseError ( DatabaseError . INTERNAL _ERROR , error ) ) ;
if ( result . length === 0 ) return callback ( new DatabaseError ( DatabaseError . NOT _FOUND ) ) ;
2018-08-24 10:39:59 -07:00
database . query ( 'SELECT ' + SUBDOMAIN _FIELDS + ' FROM subdomains WHERE appId = ? AND type = ?' , [ result [ 0 ] . id , exports . SUBDOMAIN _TYPE _REDIRECT ] , function ( error , alternateDomains ) {
2018-06-29 15:50:18 +02:00
if ( error ) return callback ( new DatabaseError ( DatabaseError . INTERNAL _ERROR , error ) ) ;
result [ 0 ] . alternateDomains = alternateDomains ;
postProcess ( result [ 0 ] ) ;
2015-07-20 00:09:47 -07:00
2018-06-29 15:50:18 +02:00
callback ( null , result [ 0 ] ) ;
} ) ;
2015-07-20 00:09:47 -07:00
} ) ;
}
2015-09-14 17:01:04 -07:00
function getByContainerId ( containerId , callback ) {
assert . strictEqual ( typeof containerId , 'string' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
database . query ( 'SELECT ' + APPS _FIELDS _PREFIXED + ','
2018-10-11 14:07:43 -07:00
+ 'GROUP_CONCAT(CAST(appPortBindings.hostPort AS CHAR(6))) AS hostPorts, GROUP_CONCAT(appPortBindings.environmentVariable) AS environmentVariables, GROUP_CONCAT(appPortBindings.type) AS portTypes,'
+ 'JSON_ARRAYAGG(appEnvVars.name) AS envNames, JSON_ARRAYAGG(appEnvVars.value) AS envValues'
2018-06-29 11:04:14 +02:00
+ ' FROM apps'
+ ' LEFT OUTER JOIN appPortBindings ON apps.id = appPortBindings.appId'
2018-10-11 14:07:43 -07:00
+ ' LEFT OUTER JOIN appEnvVars ON apps.id = appEnvVars.appId'
2018-08-12 22:08:19 -07:00
+ ' LEFT OUTER JOIN subdomains ON apps.id = subdomains.appId AND subdomains.type = ?'
2018-06-29 11:04:14 +02:00
+ ' WHERE containerId = ? GROUP BY apps.id' , [ exports . SUBDOMAIN _TYPE _PRIMARY , containerId ] , function ( error , result ) {
2015-09-14 17:01:04 -07:00
if ( error ) return callback ( new DatabaseError ( DatabaseError . INTERNAL _ERROR , error ) ) ;
if ( result . length === 0 ) return callback ( new DatabaseError ( DatabaseError . NOT _FOUND ) ) ;
2018-08-24 10:39:59 -07:00
database . query ( 'SELECT ' + SUBDOMAIN _FIELDS + ' FROM subdomains WHERE appId = ? AND type = ?' , [ result [ 0 ] . id , exports . SUBDOMAIN _TYPE _REDIRECT ] , function ( error , alternateDomains ) {
2018-06-29 15:50:18 +02:00
if ( error ) return callback ( new DatabaseError ( DatabaseError . INTERNAL _ERROR , error ) ) ;
result [ 0 ] . alternateDomains = alternateDomains ;
postProcess ( result [ 0 ] ) ;
2015-09-14 17:01:04 -07:00
2018-06-29 15:50:18 +02:00
callback ( null , result [ 0 ] ) ;
} ) ;
2015-09-14 17:01:04 -07:00
} ) ;
}
2015-07-20 00:09:47 -07:00
function getAll ( callback ) {
assert . strictEqual ( typeof callback , 'function' ) ;
database . query ( 'SELECT ' + APPS _FIELDS _PREFIXED + ','
2018-10-11 14:07:43 -07:00
+ 'GROUP_CONCAT(CAST(appPortBindings.hostPort AS CHAR(6))) AS hostPorts, GROUP_CONCAT(appPortBindings.environmentVariable) AS environmentVariables, GROUP_CONCAT(appPortBindings.type) AS portTypes,'
+ 'JSON_ARRAYAGG(appEnvVars.name) AS envNames, JSON_ARRAYAGG(appEnvVars.value) AS envValues'
2018-06-29 11:04:14 +02:00
+ ' FROM apps'
+ ' LEFT OUTER JOIN appPortBindings ON apps.id = appPortBindings.appId'
2018-10-11 14:07:43 -07:00
+ ' LEFT OUTER JOIN appEnvVars ON apps.id = appEnvVars.appId'
2018-08-12 22:08:19 -07:00
+ ' LEFT OUTER JOIN subdomains ON apps.id = subdomains.appId AND subdomains.type = ?'
2018-06-29 11:04:14 +02:00
+ ' GROUP BY apps.id ORDER BY apps.id' , [ exports . SUBDOMAIN _TYPE _PRIMARY ] , function ( error , results ) {
2015-07-20 00:09:47 -07:00
if ( error ) return callback ( new DatabaseError ( DatabaseError . INTERNAL _ERROR , error ) ) ;
2018-08-24 10:39:59 -07:00
database . query ( 'SELECT ' + SUBDOMAIN _FIELDS + ' FROM subdomains WHERE type = ?' , [ exports . SUBDOMAIN _TYPE _REDIRECT ] , function ( error , alternateDomains ) {
2018-06-29 15:50:18 +02:00
if ( error ) return callback ( new DatabaseError ( DatabaseError . INTERNAL _ERROR , error ) ) ;
2015-07-20 00:09:47 -07:00
2018-06-29 15:50:18 +02:00
alternateDomains . forEach ( function ( d ) {
var domain = results . find ( function ( a ) { return d . appId === a . id ; } ) ;
if ( ! domain ) return ;
domain . alternateDomains = domain . alternateDomains || [ ] ;
domain . alternateDomains . push ( d ) ;
} ) ;
results . forEach ( postProcess ) ;
callback ( null , results ) ;
} ) ;
2015-07-20 00:09:47 -07:00
} ) ;
}
2018-05-13 21:02:57 -07:00
function add ( id , appStoreId , manifest , location , domain , ownerId , portBindings , data , callback ) {
2015-07-20 00:09:47 -07:00
assert . strictEqual ( typeof id , 'string' ) ;
assert . strictEqual ( typeof appStoreId , 'string' ) ;
assert ( manifest && typeof manifest === 'object' ) ;
assert . strictEqual ( typeof manifest . version , 'string' ) ;
assert . strictEqual ( typeof location , 'string' ) ;
2017-11-02 22:16:42 +01:00
assert . strictEqual ( typeof domain , 'string' ) ;
2018-05-13 21:02:57 -07:00
assert . strictEqual ( typeof ownerId , 'string' ) ;
2015-07-20 00:09:47 -07:00
assert . strictEqual ( typeof portBindings , 'object' ) ;
2016-06-17 16:43:35 -05:00
assert ( data && typeof data === 'object' ) ;
2015-07-20 00:09:47 -07:00
assert . strictEqual ( typeof callback , 'function' ) ;
portBindings = portBindings || { } ;
var manifestJson = JSON . stringify ( manifest ) ;
2016-06-17 16:43:35 -05:00
var accessRestriction = data . accessRestriction || null ;
2016-06-17 16:56:15 -05:00
var accessRestrictionJson = JSON . stringify ( accessRestriction ) ;
2016-06-17 16:43:35 -05:00
var memoryLimit = data . memoryLimit || 0 ;
2016-07-14 15:47:20 +02:00
var xFrameOptions = data . xFrameOptions || '' ;
2016-06-17 16:56:15 -05:00
var installationState = data . installationState || exports . ISTATE _PENDING _INSTALL ;
2017-11-17 22:29:13 -08:00
var restoreConfigJson = data . restoreConfig ? JSON . stringify ( data . restoreConfig ) : null ; // used when cloning
2016-11-19 21:59:06 +05:30
var sso = 'sso' in data ? data . sso : null ;
2018-03-05 16:14:23 -08:00
var robotsTxt = 'robotsTxt' in data ? data . robotsTxt : null ;
2017-01-20 05:48:25 -08:00
var debugModeJson = data . debugMode ? JSON . stringify ( data . debugMode ) : null ;
2018-10-11 14:07:43 -07:00
var env = data . env || { } ;
2018-12-06 21:08:19 -08:00
const mailboxName = data . mailboxName || null ;
2015-07-20 00:09:47 -07:00
2017-02-08 23:50:26 +01:00
var queries = [ ] ;
2018-06-29 11:04:14 +02:00
2015-07-20 00:09:47 -07:00
queries . push ( {
2018-12-06 21:08:19 -08:00
query : 'INSERT INTO apps (id, appStoreId, manifestJson, installationState, accessRestrictionJson, memoryLimit, xFrameOptions, restoreConfigJson, sso, debugModeJson, robotsTxt, ownerId, mailboxName) ' +
' VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)' ,
args : [ id , appStoreId , manifestJson , installationState , accessRestrictionJson , memoryLimit , xFrameOptions , restoreConfigJson , sso , debugModeJson , robotsTxt , ownerId , mailboxName ]
2015-07-20 00:09:47 -07:00
} ) ;
2018-06-29 11:04:14 +02:00
queries . push ( {
query : 'INSERT INTO subdomains (appId, domain, subdomain, type) VALUES (?, ?, ?, ?)' ,
args : [ id , domain , location , exports . SUBDOMAIN _TYPE _PRIMARY ]
} ) ;
2015-07-20 00:09:47 -07:00
Object . keys ( portBindings ) . forEach ( function ( env ) {
queries . push ( {
2018-08-12 22:08:19 -07:00
query : 'INSERT INTO appPortBindings (environmentVariable, hostPort, type, appId) VALUES (?, ?, ?, ?)' ,
args : [ env , portBindings [ env ] . hostPort , portBindings [ env ] . type , id ]
2015-07-20 00:09:47 -07:00
} ) ;
} ) ;
2018-10-11 14:07:43 -07:00
Object . keys ( env ) . forEach ( function ( name ) {
queries . push ( {
query : 'INSERT INTO appEnvVars (appId, name, value) VALUES (?, ?, ?)' ,
args : [ id , name , env [ name ] ]
} ) ;
} ) ;
2018-06-29 15:50:18 +02:00
if ( data . alternateDomains ) {
data . alternateDomains . forEach ( function ( d ) {
queries . push ( {
query : 'INSERT INTO subdomains (appId, domain, subdomain, type) VALUES (?, ?, ?, ?)' ,
args : [ id , d . domain , d . subdomain , exports . SUBDOMAIN _TYPE _REDIRECT ]
} ) ;
} ) ;
}
2015-07-20 00:09:47 -07:00
database . transaction ( queries , function ( error ) {
if ( error && error . code === 'ER_DUP_ENTRY' ) return callback ( new DatabaseError ( DatabaseError . ALREADY _EXISTS , error . message ) ) ;
2018-01-20 23:40:59 -08:00
if ( error && error . code === 'ER_NO_REFERENCED_ROW_2' ) return callback ( new DatabaseError ( DatabaseError . NOT _FOUND , 'no such domain' ) ) ;
2015-07-20 00:09:47 -07:00
if ( error ) return callback ( new DatabaseError ( DatabaseError . INTERNAL _ERROR , error ) ) ;
callback ( null ) ;
} ) ;
}
function exists ( id , callback ) {
assert . strictEqual ( typeof id , 'string' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
database . query ( 'SELECT 1 FROM apps WHERE id=?' , [ id ] , function ( error , result ) {
if ( error ) return callback ( new DatabaseError ( DatabaseError . INTERNAL _ERROR , error ) ) ;
return callback ( null , result . length !== 0 ) ;
} ) ;
}
function getPortBindings ( id , callback ) {
assert . strictEqual ( typeof id , 'string' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
database . query ( 'SELECT ' + PORT _BINDINGS _FIELDS + ' FROM appPortBindings WHERE appId = ?' , [ id ] , function ( error , results ) {
if ( error ) return callback ( new DatabaseError ( DatabaseError . INTERNAL _ERROR , error ) ) ;
var portBindings = { } ;
for ( var i = 0 ; i < results . length ; i ++ ) {
2018-08-12 22:08:19 -07:00
portBindings [ results [ i ] . environmentVariable ] = { hostPort : results [ i ] . hostPort , type : results [ i ] . type } ;
2015-07-20 00:09:47 -07:00
}
callback ( null , portBindings ) ;
} ) ;
}
2018-08-12 22:08:19 -07:00
function delPortBinding ( hostPort , type , callback ) {
2017-10-23 22:05:43 +02:00
assert . strictEqual ( typeof hostPort , 'number' ) ;
2018-08-12 22:08:19 -07:00
assert . strictEqual ( typeof type , 'string' ) ;
2017-10-23 22:05:43 +02:00
assert . strictEqual ( typeof callback , 'function' ) ;
2018-08-12 22:08:19 -07:00
database . query ( 'DELETE FROM appPortBindings WHERE hostPort=? AND type=?' , [ hostPort , type ] , function ( error , result ) {
2017-10-23 22:05:43 +02:00
if ( error ) return callback ( new DatabaseError ( DatabaseError . INTERNAL _ERROR , error ) ) ;
if ( result . affectedRows !== 1 ) return callback ( new DatabaseError ( DatabaseError . NOT _FOUND ) ) ;
callback ( null ) ;
} ) ;
}
2015-07-20 00:09:47 -07:00
function del ( id , callback ) {
assert . strictEqual ( typeof id , 'string' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
var queries = [
2018-06-29 11:04:14 +02:00
{ query : 'DELETE FROM subdomains WHERE appId = ?' , args : [ id ] } ,
2015-07-20 00:09:47 -07:00
{ query : 'DELETE FROM appPortBindings WHERE appId = ?' , args : [ id ] } ,
2018-10-11 14:07:43 -07:00
{ query : 'DELETE FROM appEnvVars WHERE appId = ?' , args : [ id ] } ,
2015-07-20 00:09:47 -07:00
{ query : 'DELETE FROM apps WHERE id = ?' , args : [ id ] }
] ;
database . transaction ( queries , function ( error , results ) {
if ( error ) return callback ( new DatabaseError ( DatabaseError . INTERNAL _ERROR , error ) ) ;
2018-12-06 21:08:19 -08:00
if ( results [ 3 ] . affectedRows !== 1 ) return callback ( new DatabaseError ( DatabaseError . NOT _FOUND ) ) ;
2015-07-20 00:09:47 -07:00
callback ( null ) ;
} ) ;
}
function clear ( callback ) {
assert . strictEqual ( typeof callback , 'function' ) ;
async . series ( [
2018-06-29 11:04:14 +02:00
database . query . bind ( null , 'DELETE FROM subdomains' ) ,
2015-07-20 00:09:47 -07:00
database . query . bind ( null , 'DELETE FROM appPortBindings' ) ,
database . query . bind ( null , 'DELETE FROM appAddonConfigs' ) ,
2018-10-11 14:07:43 -07:00
database . query . bind ( null , 'DELETE FROM appEnvVars' ) ,
2015-07-20 00:09:47 -07:00
database . query . bind ( null , 'DELETE FROM apps' )
] , function ( error ) {
if ( error ) return callback ( new DatabaseError ( DatabaseError . INTERNAL _ERROR , error ) ) ;
return callback ( null ) ;
} ) ;
}
function update ( id , app , callback ) {
updateWithConstraints ( id , app , '' , callback ) ;
}
function updateWithConstraints ( id , app , constraints , callback ) {
assert . strictEqual ( typeof id , 'string' ) ;
assert . strictEqual ( typeof app , 'object' ) ;
assert . strictEqual ( typeof constraints , 'string' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
assert ( ! ( 'portBindings' in app ) || typeof app . portBindings === 'object' ) ;
2016-02-18 17:28:00 +01:00
assert ( ! ( 'accessRestriction' in app ) || typeof app . accessRestriction === 'object' || app . accessRestriction === '' ) ;
2018-06-29 16:50:09 +02:00
assert ( ! ( 'alternateDomains' in app ) || Array . isArray ( app . alternateDomains ) ) ;
2018-10-11 14:07:43 -07:00
assert ( ! ( 'env' in app ) || typeof app . env === 'object' ) ;
2015-07-20 00:09:47 -07:00
var queries = [ ] ;
if ( 'portBindings' in app ) {
var portBindings = app . portBindings || { } ;
// replace entries by app id
queries . push ( { query : 'DELETE FROM appPortBindings WHERE appId = ?' , args : [ id ] } ) ;
Object . keys ( portBindings ) . forEach ( function ( env ) {
2018-08-12 22:08:19 -07:00
var values = [ portBindings [ env ] . hostPort , portBindings [ env ] . type , env , id ] ;
queries . push ( { query : 'INSERT INTO appPortBindings (hostPort, type, environmentVariable, appId) VALUES(?, ?, ?, ?)' , args : values } ) ;
2015-07-20 00:09:47 -07:00
} ) ;
}
2018-10-11 14:07:43 -07:00
if ( 'env' in app ) {
queries . push ( { query : 'DELETE FROM appEnvVars WHERE appId = ?' , args : [ id ] } ) ;
Object . keys ( app . env ) . forEach ( function ( name ) {
queries . push ( {
query : 'INSERT INTO appEnvVars (appId, name, value) VALUES (?, ?, ?)' ,
args : [ id , name , app . env [ name ] ]
} ) ;
} ) ;
}
2018-06-29 11:04:14 +02:00
if ( 'location' in app ) {
queries . push ( { query : 'UPDATE subdomains SET subdomain = ? WHERE appId = ? AND type = ?' , args : [ app . location , id , exports . SUBDOMAIN _TYPE _PRIMARY ] } ) ;
}
if ( 'domain' in app ) {
queries . push ( { query : 'UPDATE subdomains SET domain = ? WHERE appId = ? AND type = ?' , args : [ app . domain , id , exports . SUBDOMAIN _TYPE _PRIMARY ] } ) ;
}
2018-06-29 15:03:36 +02:00
if ( 'alternateDomains' in app ) {
queries . push ( { query : 'DELETE FROM subdomains WHERE appId = ? AND type = ?' , args : [ id , exports . SUBDOMAIN _TYPE _REDIRECT ] } ) ;
app . alternateDomains . forEach ( function ( d ) {
queries . push ( { query : 'INSERT INTO subdomains (appId, domain, subdomain, type) VALUES (?, ?, ?, ?)' , args : [ id , d . domain , d . subdomain , exports . SUBDOMAIN _TYPE _REDIRECT ] } ) ;
} ) ;
}
2015-07-20 00:09:47 -07:00
var fields = [ ] , values = [ ] ;
for ( var p in app ) {
2017-11-16 14:47:05 -08:00
if ( p === 'manifest' || p === 'oldConfig' || p === 'updateConfig' || p === 'restoreConfig' || p === 'accessRestriction' || p === 'debugMode' ) {
2017-10-12 17:46:15 -07:00
fields . push ( ` ${ p } Json = ? ` ) ;
2017-01-20 09:40:11 -08:00
values . push ( JSON . stringify ( app [ p ] ) ) ;
2018-10-11 14:07:43 -07:00
} else if ( p !== 'portBindings' && p !== 'location' && p !== 'domain' && p !== 'alternateDomains' && p !== 'env' ) {
2015-07-20 00:09:47 -07:00
fields . push ( p + ' = ?' ) ;
values . push ( app [ p ] ) ;
}
}
if ( values . length !== 0 ) {
values . push ( id ) ;
queries . push ( { query : 'UPDATE apps SET ' + fields . join ( ', ' ) + ' WHERE id = ? ' + constraints , args : values } ) ;
}
database . transaction ( queries , function ( error , results ) {
if ( error && error . code === 'ER_DUP_ENTRY' ) return callback ( new DatabaseError ( DatabaseError . ALREADY _EXISTS , error . message ) ) ;
if ( error ) return callback ( new DatabaseError ( DatabaseError . INTERNAL _ERROR , error ) ) ;
if ( results [ results . length - 1 ] . affectedRows !== 1 ) return callback ( new DatabaseError ( DatabaseError . NOT _FOUND ) ) ;
return callback ( null ) ;
} ) ;
}
// not sure if health should influence runState
function setHealth ( appId , health , callback ) {
assert . strictEqual ( typeof appId , 'string' ) ;
assert . strictEqual ( typeof health , 'string' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
var values = { health : health } ;
var constraints = 'AND runState NOT LIKE "pending_%" AND installationState = "installed"' ;
updateWithConstraints ( appId , values , constraints , callback ) ;
}
function setInstallationCommand ( appId , installationState , values , callback ) {
assert . strictEqual ( typeof appId , 'string' ) ;
assert . strictEqual ( typeof installationState , 'string' ) ;
if ( typeof values === 'function' ) {
callback = values ;
values = { } ;
} else {
assert . strictEqual ( typeof values , 'object' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
}
values . installationState = installationState ;
values . installationProgress = '' ;
// Rules are:
// uninstall is allowed in any state
2015-08-17 21:30:56 -07:00
// force update is allowed in any state including pending_uninstall! (for better or worse)
2017-11-13 18:43:36 -08:00
// restore is allowed from installed or error state or currently restoring
2016-06-14 13:18:37 -07:00
// configure is allowed in installed state or currently configuring or in error state
// update and backup are allowed only in installed state
2015-07-20 00:09:47 -07:00
if ( installationState === exports . ISTATE _PENDING _UNINSTALL || installationState === exports . ISTATE _PENDING _FORCE _UPDATE ) {
updateWithConstraints ( appId , values , '' , callback ) ;
} else if ( installationState === exports . ISTATE _PENDING _RESTORE ) {
2017-11-13 18:43:36 -08:00
updateWithConstraints ( appId , values , 'AND (installationState = "installed" OR installationState = "error" OR installationState = "pending_restore")' , callback ) ;
2016-06-20 09:43:09 -05:00
} else if ( installationState === exports . ISTATE _PENDING _UPDATE || installationState === exports . ISTATE _PENDING _BACKUP ) {
2015-07-20 00:09:47 -07:00
updateWithConstraints ( appId , values , 'AND installationState = "installed"' , callback ) ;
2016-06-14 13:18:37 -07:00
} else if ( installationState === exports . ISTATE _PENDING _CONFIGURE ) {
2016-06-20 09:43:09 -05:00
updateWithConstraints ( appId , values , 'AND (installationState = "installed" OR installationState = "pending_configure" OR installationState = "error")' , callback ) ;
2015-07-20 00:09:47 -07:00
} else {
callback ( new DatabaseError ( DatabaseError . INTERNAL _ERROR , 'invalid installationState' ) ) ;
}
}
function setRunCommand ( appId , runState , callback ) {
assert . strictEqual ( typeof appId , 'string' ) ;
assert . strictEqual ( typeof runState , 'string' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
var values = { runState : runState } ;
updateWithConstraints ( appId , values , 'AND runState NOT LIKE "pending_%" AND installationState = "installed"' , callback ) ;
}
function getAppStoreIds ( callback ) {
assert . strictEqual ( typeof callback , 'function' ) ;
database . query ( 'SELECT id, appStoreId FROM apps' , function ( error , results ) {
if ( error ) return callback ( new DatabaseError ( DatabaseError . INTERNAL _ERROR , error ) ) ;
callback ( null , results ) ;
} ) ;
}
function setAddonConfig ( appId , addonId , env , callback ) {
assert . strictEqual ( typeof appId , 'string' ) ;
assert . strictEqual ( typeof addonId , 'string' ) ;
assert ( util . isArray ( env ) ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
unsetAddonConfig ( appId , addonId , function ( error ) {
if ( error ) return callback ( error ) ;
if ( env . length === 0 ) return callback ( null ) ;
2017-03-25 14:14:57 -07:00
var query = 'INSERT INTO appAddonConfigs(appId, addonId, name, value) VALUES ' ;
2015-07-20 00:09:47 -07:00
var args = [ ] , queryArgs = [ ] ;
for ( var i = 0 ; i < env . length ; i ++ ) {
2017-03-25 14:14:57 -07:00
args . push ( appId , addonId , env [ i ] . name , env [ i ] . value ) ;
queryArgs . push ( '(?, ?, ?, ?)' ) ;
2015-07-20 00:09:47 -07:00
}
database . query ( query + queryArgs . join ( ',' ) , args , function ( error ) {
if ( error ) return callback ( new DatabaseError ( DatabaseError . INTERNAL _ERROR , error ) ) ;
return callback ( null ) ;
} ) ;
} ) ;
}
function unsetAddonConfig ( appId , addonId , callback ) {
assert . strictEqual ( typeof appId , 'string' ) ;
assert . strictEqual ( typeof addonId , 'string' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
database . query ( 'DELETE FROM appAddonConfigs WHERE appId = ? AND addonId = ?' , [ appId , addonId ] , function ( error ) {
if ( error ) return callback ( new DatabaseError ( DatabaseError . INTERNAL _ERROR , error ) ) ;
callback ( null ) ;
} ) ;
}
function unsetAddonConfigByAppId ( appId , callback ) {
assert . strictEqual ( typeof appId , 'string' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
database . query ( 'DELETE FROM appAddonConfigs WHERE appId = ?' , [ appId ] , function ( error ) {
if ( error ) return callback ( new DatabaseError ( DatabaseError . INTERNAL _ERROR , error ) ) ;
callback ( null ) ;
} ) ;
}
function getAddonConfig ( appId , addonId , callback ) {
assert . strictEqual ( typeof appId , 'string' ) ;
assert . strictEqual ( typeof addonId , 'string' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
2017-03-25 14:14:57 -07:00
database . query ( 'SELECT name, value FROM appAddonConfigs WHERE appId = ? AND addonId = ?' , [ appId , addonId ] , function ( error , results ) {
2015-07-20 00:09:47 -07:00
if ( error ) return callback ( new DatabaseError ( DatabaseError . INTERNAL _ERROR , error ) ) ;
2017-03-25 14:14:57 -07:00
callback ( null , results ) ;
2015-07-20 00:09:47 -07:00
} ) ;
}
function getAddonConfigByAppId ( appId , callback ) {
assert . strictEqual ( typeof appId , 'string' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
2017-03-25 14:14:57 -07:00
database . query ( 'SELECT name, value FROM appAddonConfigs WHERE appId = ?' , [ appId ] , function ( error , results ) {
2015-07-20 00:09:47 -07:00
if ( error ) return callback ( new DatabaseError ( DatabaseError . INTERNAL _ERROR , error ) ) ;
2017-03-25 14:14:57 -07:00
callback ( null , results ) ;
2015-07-20 00:09:47 -07:00
} ) ;
}
2017-03-26 19:06:36 -07:00
2018-12-06 21:08:19 -08:00
function getAppIdByAddonConfigValue ( addonId , name , value , callback ) {
assert . strictEqual ( typeof addonId , 'string' ) ;
assert . strictEqual ( typeof name , 'string' ) ;
assert . strictEqual ( typeof value , 'string' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
database . query ( 'SELECT appId FROM appAddonConfigs WHERE addonId = ? AND name = ? AND value = ?' , [ addonId , name , value ] , function ( error , results ) {
if ( error ) return callback ( new DatabaseError ( DatabaseError . INTERNAL _ERROR , error ) ) ;
if ( results . length === 0 ) return callback ( new DatabaseError ( DatabaseError . NOT _FOUND ) ) ;
callback ( null , results [ 0 ] . appId ) ;
} ) ;
}
2017-03-26 19:06:36 -07:00
function getAddonConfigByName ( appId , addonId , name , callback ) {
assert . strictEqual ( typeof appId , 'string' ) ;
assert . strictEqual ( typeof addonId , 'string' ) ;
assert . strictEqual ( typeof name , 'string' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
database . query ( 'SELECT value FROM appAddonConfigs WHERE appId = ? AND addonId = ? AND name = ?' , [ appId , addonId , name ] , function ( error , results ) {
if ( error ) return callback ( new DatabaseError ( DatabaseError . INTERNAL _ERROR , error ) ) ;
if ( results . length === 0 ) return callback ( new DatabaseError ( DatabaseError . NOT _FOUND ) ) ;
callback ( null , results [ 0 ] . value ) ;
} ) ;
}
2018-06-28 16:48:04 -07:00
function setOwner ( appId , ownerId , callback ) {
assert . strictEqual ( typeof appId , 'string' ) ;
assert . strictEqual ( typeof ownerId , 'string' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
database . query ( 'UPDATE apps SET ownerId=? WHERE appId=?' , [ ownerId , appId ] , function ( error , results ) {
if ( error && error . code === 'ER_NO_REFERENCED_ROW_2' ) return callback ( new DatabaseError ( DatabaseError . NOT _FOUND , 'No such user' ) ) ;
if ( error ) return callback ( new DatabaseError ( DatabaseError . INTERNAL _ERROR , error ) ) ;
if ( results . length === 0 ) return callback ( new DatabaseError ( DatabaseError . NOT _FOUND , 'No such app' ) ) ;
callback ( null ) ;
} ) ;
}
function transferOwnership ( oldOwnerId , newOwnerId , callback ) {
assert . strictEqual ( typeof oldOwnerId , 'string' ) ;
assert . strictEqual ( typeof newOwnerId , 'string' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
2018-10-11 14:07:43 -07:00
database . query ( 'UPDATE apps SET ownerId=? WHERE ownerId=?' , [ newOwnerId , oldOwnerId ] , function ( error ) {
2018-06-28 16:48:04 -07:00
if ( error && error . code === 'ER_NO_REFERENCED_ROW_2' ) return callback ( new DatabaseError ( DatabaseError . NOT _FOUND , 'No such user' ) ) ;
if ( error ) return callback ( new DatabaseError ( DatabaseError . INTERNAL _ERROR , error ) ) ;
callback ( null ) ;
} ) ;
}