2019-09-10 19:21:30 +02:00
'use strict' ;
2024-12-10 20:52:29 +01:00
/* global angular */
2019-09-10 19:21:30 +02:00
/* global $ */
2020-06-03 23:08:05 +02:00
/* global async */
2019-09-22 12:21:39 +02:00
/* global RSTATES */
/* global ISTATES */
2019-09-24 18:50:52 +02:00
/* global ERROR */
2020-05-13 00:42:27 +02:00
/* global Chart */
2020-05-25 21:47:58 +02:00
/* global Clipboard */
2020-05-16 11:19:47 -07:00
/* global SECRET_PLACEHOLDER */
2022-12-24 11:10:04 +01:00
/* global APP_TYPES, STORAGE_PROVIDERS, BACKUP_FORMATS */
2024-09-25 12:21:42 +02:00
/* global REGIONS_S3, REGIONS_WASABI, REGIONS_DIGITALOCEAN, REGIONS_EXOSCALE, REGIONS_SCALEWAY, REGIONS_LINODE, REGIONS_OVH, REGIONS_IONOS, REGIONS_UPCLOUD, REGIONS_VULTR, REGIONS_HETZNER */
2024-01-16 13:54:42 +01:00
/* global onAppClick */
2019-09-10 19:21:30 +02:00
2021-09-28 20:40:36 +02:00
angular . module ( 'Application' ) . controller ( 'AppController' , [ '$scope' , '$location' , '$translate' , '$timeout' , '$interval' , '$route' , '$routeParams' , 'Client' , function ( $scope , $location , $translate , $timeout , $interval , $route , $routeParams , Client ) {
2022-09-27 13:31:13 +02:00
$scope . s3Regions = REGIONS _S3 ;
$scope . wasabiRegions = REGIONS _WASABI ;
$scope . doSpacesRegions = REGIONS _DIGITALOCEAN ;
$scope . exoscaleSosRegions = REGIONS _EXOSCALE ;
$scope . scalewayRegions = REGIONS _SCALEWAY ;
$scope . linodeRegions = REGIONS _LINODE ;
$scope . ovhRegions = REGIONS _OVH ;
$scope . ionosRegions = REGIONS _IONOS ;
$scope . upcloudRegions = REGIONS _UPCLOUD ;
$scope . vultrRegions = REGIONS _VULTR ;
2023-08-25 07:59:40 +05:30
$scope . contaboRegions = REGIONS _VULTR ;
2024-09-25 12:21:42 +02:00
$scope . hetznerRegions = REGIONS _HETZNER ;
2022-09-27 13:31:13 +02:00
2022-12-24 11:10:04 +01:00
$scope . storageProviders = STORAGE _PROVIDERS ;
2022-09-27 13:31:13 +02:00
$scope . formats = BACKUP _FORMATS ;
2020-02-06 16:08:22 -08:00
2019-09-18 17:45:13 +02:00
// Avoid full reload on path change
// https://stackoverflow.com/a/22614334
// reloadOnUrl: false in $routeProvider did not work!
var lastRoute = $route . current ;
$scope . $on ( '$locationChangeSuccess' , function ( /* event */ ) {
if ( lastRoute . $$route . originalPath === $route . current . $$route . originalPath ) {
$route . current = lastRoute ;
}
} ) ;
var appId = $routeParams . appId ;
if ( ! appId ) return $location . path ( '/apps' ) ;
2019-09-10 19:21:30 +02:00
2019-09-18 17:45:13 +02:00
$scope . view = '' ;
2019-09-10 19:21:30 +02:00
$scope . app = null ;
$scope . config = Client . getConfig ( ) ;
$scope . user = Client . getUserInfo ( ) ;
2021-09-22 12:45:28 -07:00
// note: these variables will remain empty for operators
2019-09-10 19:21:30 +02:00
$scope . domains = [ ] ;
2020-10-28 22:11:05 -07:00
$scope . volumes = [ ] ;
2019-09-10 19:21:30 +02:00
$scope . groups = [ ] ;
$scope . users = [ ] ;
2020-05-16 11:19:47 -07:00
$scope . backupConfig = null ;
2022-11-29 12:36:15 +01:00
$scope . diskUsage = - 1 ;
2022-11-15 14:54:07 +01:00
$scope . diskUsageDate = 0 ;
2019-09-19 18:41:03 +02:00
2022-09-09 09:29:14 +02:00
$scope . APP _TYPES = APP _TYPES ;
2024-08-20 18:16:57 +02:00
$scope . HOST _PORT _MIN = 1 ;
2019-09-19 18:41:03 +02:00
$scope . HOST _PORT _MAX = 65535 ;
$scope . ROBOTS _DISABLE _INDEXING _TEMPLATE = '# Disable search engine indexing\n\nUser-agent: *\nDisallow: /' ;
2019-09-10 19:21:30 +02:00
2019-12-20 17:05:45 -08:00
$scope . setView = function ( view , skipViewShow ) {
2019-09-17 14:52:22 +02:00
if ( $scope . view === view ) return ;
2019-09-18 17:45:13 +02:00
$route . updateParams ( { view : view } ) ;
2019-12-20 17:05:45 -08:00
if ( ! skipViewShow ) $scope [ view ] . show ( ) ;
2019-09-17 14:52:22 +02:00
$scope . view = view ;
} ;
2020-03-26 00:19:06 +01:00
$scope . stopAppTask = function ( taskId ) {
Client . stopTask ( taskId , function ( error ) {
// we can ignore a call trying to cancel an already done task
if ( error && error . statusCode !== 409 ) Client . error ( error ) ;
} ) ;
2021-02-18 16:16:42 +01:00
} ;
2020-03-26 00:19:06 +01:00
2024-01-16 13:54:42 +01:00
$scope . appPostInstallConfirm = {
app : { } ,
message : '' ,
show : function ( app ) {
$scope . appPostInstallConfirm . app = app ;
$scope . appPostInstallConfirm . message = app . manifest . postInstallMessage ;
$ ( '#appPostInstallConfirmModal' ) . modal ( 'show' ) ;
return false ; // prevent propagation and default
} ,
submit : function ( ) {
$scope . appPostInstallConfirm . app . pendingPostInstallConfirmation = false ;
delete localStorage [ 'confirmPostInstall_' + $scope . appPostInstallConfirm . app . id ] ;
$ ( '#appPostInstallConfirmModal' ) . modal ( 'hide' ) ;
}
} ;
2019-09-27 19:43:03 +02:00
$scope . postInstallMessage = {
openApp : false ,
show : function ( openApp ) {
$scope . postInstallMessage . openApp = ! ! openApp ;
if ( ! $scope . app . manifest . postInstallMessage ) return ;
$ ( '#postInstallModal' ) . modal ( 'show' ) ;
} ,
submit : function ( ) {
$scope . app . pendingPostInstallConfirmation = false ;
delete localStorage [ 'confirmPostInstall_' + $scope . app . id ] ;
$ ( '#postInstallModal' ) . modal ( 'hide' ) ;
}
2019-09-26 20:48:04 +02:00
} ;
2022-11-04 10:18:12 +01:00
$scope . getAppBackupDownloadLink = function ( backup ) {
return Client . getAppBackupDownloadLink ( $scope . app . id , backup . id ) ;
} ;
2022-09-13 12:39:56 +02:00
$scope . onAppClick = function ( app , $event ) { onAppClick ( app , $event , true /* always operator */ , $scope ) ; } ;
2021-09-22 22:28:05 +02:00
$scope . sftpInfo = {
show : function ( ) {
$ ( '#sftpInfoModal' ) . modal ( 'show' ) ;
}
} ;
2024-04-10 17:02:32 +02:00
$scope . info = {
2024-04-19 12:40:35 +02:00
showDoneChecklist : false ,
2024-05-11 11:26:46 +02:00
hasOldChecklist : false ,
2024-04-19 12:40:35 +02:00
2024-04-10 17:02:32 +02:00
notes : {
2024-06-13 15:48:35 +02:00
busy : true ,
2024-06-18 15:49:52 +02:00
busySave : false ,
2024-04-10 17:02:32 +02:00
editing : false ,
content : '' ,
2024-06-13 15:48:35 +02:00
placeholder : 'Add admin notes here...' ,
2024-04-10 17:02:32 +02:00
edit : function ( ) {
2024-06-27 16:20:19 +02:00
$scope . info . notes . content = $scope . app . notes === null ? $scope . app . manifest . postInstallMessage : $scope . app . notes ;
2024-04-10 17:02:32 +02:00
$scope . info . notes . editing = true ;
2024-06-13 15:48:35 +02:00
setTimeout ( function ( ) { document . getElementById ( 'adminNotesTextarea' ) . focus ( ) ; } , 1 ) ;
2024-04-10 17:02:32 +02:00
} ,
dismiss : function ( ) {
2024-06-27 16:20:19 +02:00
$scope . info . notes . content = $scope . app . notes === null ? $scope . app . manifest . postInstallMessage : $scope . app . notes ;
2024-04-10 17:02:32 +02:00
$scope . info . notes . editing = false ;
} ,
submit : function ( ) {
2024-06-18 15:49:52 +02:00
$scope . info . notes . busySave = true ;
2024-04-10 17:02:32 +02:00
2024-06-24 19:59:02 +02:00
// skip saving if unchanged from postInstall
if ( $scope . info . notes . content === $scope . app . manifest . postInstallMessage ) {
$scope . info . notes . busySave = false ;
$scope . info . notes . editing = false ;
return ;
}
2024-04-10 17:02:32 +02:00
Client . configureApp ( $scope . app . id , 'notes' , { notes : $scope . info . notes . content } , function ( error ) {
if ( error ) return console . error ( 'Failed to save notes.' , error ) ;
2024-04-15 17:38:02 +02:00
refreshApp ( $scope . app . id , function ( error ) {
if ( error ) return Client . error ( error ) ;
2024-04-10 18:48:20 +02:00
2024-06-27 16:20:19 +02:00
$scope . info . notes . content = $scope . app . notes === null ? $scope . app . manifest . postInstallMessage : $scope . app . notes ;
2024-06-18 15:49:52 +02:00
$scope . info . notes . busySave = false ;
2024-04-15 17:38:02 +02:00
$scope . info . notes . editing = false ;
} ) ;
2024-04-10 17:02:32 +02:00
} ) ;
}
} ,
show : function ( ) {
2024-05-11 11:26:46 +02:00
$scope . info . hasOldChecklist = ! ! Object . keys ( $scope . app . checklist ) . find ( ( k ) => { return $scope . app . checklist [ k ] . acknowledged ; } ) ;
2024-06-27 16:20:19 +02:00
$scope . info . notes . content = $scope . app . notes === null ? $scope . app . manifest . postInstallMessage : $scope . app . notes ;
2024-04-10 17:02:32 +02:00
$scope . info . notes . editing = false ;
2024-04-19 12:40:35 +02:00
$scope . info . notes . busy = false ;
2024-04-18 17:35:01 +02:00
} ,
2024-06-24 18:39:37 +02:00
checklistAck ( item , key ) {
2024-04-19 14:32:31 +02:00
item . acknowledged = true ;
2024-06-24 18:39:37 +02:00
// item.acknowledged = !item.acknowledged;
2024-04-18 17:35:01 +02:00
2024-06-24 18:39:37 +02:00
Client . ackAppChecklistItem ( $scope . app . id , key , item . acknowledged , function ( error ) {
if ( error ) return console . error ( 'Failed to ack checklist item.' , error ) ;
2024-05-11 11:26:46 +02:00
$scope . info . hasOldChecklist = true ;
2024-06-25 18:01:31 +02:00
refreshApp ( $scope . app . id ) ;
2024-04-19 12:40:35 +02:00
} ) ;
2024-04-10 17:02:32 +02:00
}
} ;
2019-09-10 19:21:30 +02:00
$scope . display = {
busy : false ,
error : { } ,
success : false ,
tags : '' ,
label : '' ,
icon : { data : null } ,
iconUrl : function ( ) {
if ( ! $scope . app ) return '' ;
if ( $scope . display . icon . data === '__original__' ) { // user clicked reset
return $scope . app . iconUrl + '&original=true' ;
} else if ( $scope . display . icon . data ) { // user uploaded icon
return $scope . display . icon . data ;
} else { // current icon
return $scope . app . iconUrl ;
}
} ,
resetCustomIcon : function ( ) {
$scope . display . icon . data = '__original__' ;
} ,
showCustomIconSelector : function ( ) {
$ ( '#iconFileInput' ) . click ( ) ;
} ,
show : function ( ) {
var app = $scope . app ;
2019-09-19 18:31:11 +02:00
$scope . display . error = { } ;
2019-09-10 19:21:30 +02:00
// translate for tag-input
2020-03-28 16:46:06 -07:00
$scope . display . tags = app . tags ? app . tags . join ( ' ' ) : '' ;
2019-09-10 19:21:30 +02:00
$scope . display . label = $scope . app . label || '' ;
$scope . display . icon = { data : null } ;
} ,
submit : function ( ) {
$scope . display . busy = true ;
$scope . display . error = { } ;
2019-10-11 15:18:48 -07:00
function done ( error ) {
if ( error ) Client . error ( error ) ;
2019-12-20 19:09:17 -08:00
$scope . displayForm . $setPristine ( ) ;
$scope . display . success = true ;
2019-09-17 14:49:26 +02:00
2019-12-20 19:09:17 -08:00
refreshApp ( $scope . app . id , function ( error ) {
if ( error ) Client . error ( error ) ;
$scope . display . show ( ) ; // "refresh" view with latest data
$timeout ( function ( ) { $scope . display . busy = false ; } , 1000 ) ;
} ) ;
2019-09-12 16:28:21 +02:00
}
2019-09-27 13:38:30 -07:00
var NOOP = function ( next ) { return next ( ) ; } ;
var configureLabel = $scope . display . label === $scope . app . label ? NOOP : Client . configureApp . bind ( null , $scope . app . id , 'label' , { label : $scope . display . label } ) ;
configureLabel ( function ( error ) {
2019-10-11 15:18:48 -07:00
if ( error ) return done ( error ) ;
2019-09-10 19:21:30 +02:00
2020-03-28 16:46:06 -07:00
var tags = $scope . display . tags . split ( ' ' ) . map ( function ( t ) { return t . trim ( ) ; } ) . filter ( function ( t ) { return ! ! t ; } ) ;
2019-09-10 19:21:30 +02:00
2019-09-27 13:38:30 -07:00
var configureTags = angular . equals ( tags , $scope . app . tags ) ? NOOP : Client . configureApp . bind ( null , $scope . app . id , 'tags' , { tags : tags } ) ;
configureTags ( function ( error ) {
2019-10-11 15:18:48 -07:00
if ( error ) return done ( error ) ;
2019-09-10 19:21:30 +02:00
// skip if icon is unchanged
2019-09-12 16:28:21 +02:00
if ( $scope . display . icon . data === null ) return done ( ) ;
2019-09-10 19:21:30 +02:00
var icon ;
if ( $scope . display . icon . data === '__original__' ) { // user reset the icon
icon = '' ;
} else if ( $scope . display . icon . data ) { // user loaded custom icon
icon = $scope . display . icon . data . replace ( /^data:image\/[a-z]+;base64,/ , '' ) ;
}
Client . configureApp ( $scope . app . id , 'icon' , { icon : icon } , function ( error ) {
2019-10-11 15:18:48 -07:00
if ( error ) return done ( error ) ;
2019-09-12 16:28:21 +02:00
done ( ) ;
2019-09-10 19:21:30 +02:00
} ) ;
} ) ;
} ) ;
}
} ;
$scope . location = {
busy : false ,
error : { } ,
2019-09-20 01:55:45 +02:00
domainCollisions : [ ] ,
2019-09-10 19:21:30 +02:00
2022-01-20 16:58:00 -08:00
domain : null , // object and not the string
2022-01-16 18:29:32 -08:00
subdomain : '' ,
2022-01-20 16:58:00 -08:00
secondaryDomains : { } ,
2022-01-14 22:32:41 -08:00
redirectDomains : [ ] ,
2021-01-18 17:55:48 -08:00
aliasDomains : [ ] ,
2024-07-16 22:21:36 +02:00
ports : { } ,
portsEnabled : { } ,
portInfo : { } ,
2019-09-10 19:21:30 +02:00
2022-01-14 22:32:41 -08:00
addRedirectDomain : function ( event ) {
2019-09-10 19:21:30 +02:00
event . preventDefault ( ) ;
2022-01-14 22:32:41 -08:00
$scope . location . redirectDomains . push ( {
2020-10-04 16:39:59 -07:00
domain : $scope . domains . filter ( function ( d ) { return d . domain === $scope . app . domain ; } ) [ 0 ] , // pre-select app's domain by default
2019-09-10 19:21:30 +02:00
subdomain : ''
} ) ;
2021-07-06 19:05:42 +02:00
setTimeout ( function ( ) {
2022-01-14 22:32:41 -08:00
document . getElementById ( 'redirectDomainsInput-' + ( $scope . location . redirectDomains . length - 1 ) ) . focus ( ) ;
2021-07-06 19:05:42 +02:00
} , 200 ) ;
2019-09-10 19:21:30 +02:00
} ,
2022-01-14 22:32:41 -08:00
delRedirectDomain : function ( event , index ) {
2019-09-10 19:21:30 +02:00
event . preventDefault ( ) ;
2022-01-14 22:32:41 -08:00
$scope . location . redirectDomains . splice ( index , 1 ) ;
2019-09-10 19:21:30 +02:00
} ,
2021-01-18 17:55:48 -08:00
addAliasDomain : function ( event ) {
event . preventDefault ( ) ;
$scope . location . aliasDomains . push ( {
domain : $scope . domains . filter ( function ( d ) { return d . domain === $scope . app . domain ; } ) [ 0 ] , // pre-select app's domain by default
subdomain : ''
} ) ;
2021-07-06 19:05:42 +02:00
setTimeout ( function ( ) {
document . getElementById ( 'aliasDomainsInput-' + ( $scope . location . aliasDomains . length - 1 ) ) . focus ( ) ;
} , 200 ) ;
2021-01-18 17:55:48 -08:00
} ,
delAliasDomain : function ( event , index ) {
event . preventDefault ( ) ;
$scope . location . aliasDomains . splice ( index , 1 ) ;
} ,
2019-09-10 19:21:30 +02:00
show : function ( ) {
var app = $scope . app ;
2019-09-19 18:31:11 +02:00
$scope . location . error = { } ;
2019-09-20 01:55:45 +02:00
$scope . location . domainCollisions = [ ] ;
2022-01-16 18:29:32 -08:00
$scope . location . subdomain = app . subdomain ;
2019-09-10 19:21:30 +02:00
$scope . location . domain = $scope . domains . filter ( function ( d ) { return d . domain === app . domain ; } ) [ 0 ] ;
2022-01-20 16:58:00 -08:00
// for compat, secondary domain can be empty after an upgrade. so it may not exist in app.secondaryDomains
$scope . location . secondaryDomains = { } ;
var httpPorts = app . manifest . httpPorts || { } ;
for ( var env2 in httpPorts ) {
$scope . location . secondaryDomains [ env2 ] = {
2022-02-07 09:27:07 -08:00
subdomain : httpPorts [ env2 ] . defaultValue || '' ,
2022-01-20 16:58:00 -08:00
domain : $scope . location . domain
} ;
}
// now fill secondaryDomains with real values, if it exists
app . secondaryDomains . forEach ( function ( sd ) {
$scope . location . secondaryDomains [ sd . environmentVariable ] = {
subdomain : sd . subdomain ,
domain : $scope . domains . filter ( function ( d ) { return d . domain === sd . domain ; } ) [ 0 ]
} ;
} ) ;
2024-07-16 22:21:36 +02:00
$scope . location . portInfo = angular . extend ( { } , app . manifest . tcpPorts , app . manifest . udpPorts ) ; // Portbinding map only for information
2022-01-14 22:32:41 -08:00
$scope . location . redirectDomains = app . redirectDomains . map ( function ( a ) { return { subdomain : a . subdomain , domain : $scope . domains . filter ( function ( d ) { return d . domain === a . domain ; } ) [ 0 ] } ; } ) ;
2021-01-18 17:55:48 -08:00
$scope . location . aliasDomains = app . aliasDomains . map ( function ( a ) { return { subdomain : a . subdomain , domain : $scope . domains . filter ( function ( d ) { return d . domain === a . domain ; } ) [ 0 ] } ; } ) ;
2019-09-10 19:21:30 +02:00
// fill the portBinding structures. There might be holes in the app.portBindings, which signalizes a disabled port
2024-07-16 22:21:36 +02:00
for ( var env in $scope . location . portInfo ) {
2019-09-10 19:21:30 +02:00
if ( app . portBindings && app . portBindings [ env ] ) {
2024-07-16 22:21:36 +02:00
$scope . location . ports [ env ] = app . portBindings [ env ] . hostPort ;
$scope . location . portsEnabled [ env ] = true ;
2019-09-10 19:21:30 +02:00
} else {
2024-07-16 22:21:36 +02:00
$scope . location . ports [ env ] = $scope . location . portInfo [ env ] . defaultValue || 0 ;
$scope . location . portsEnabled [ env ] = false ;
2019-09-10 19:21:30 +02:00
}
}
} ,
2019-09-20 01:55:45 +02:00
submit : function ( overwriteDns ) {
$ ( '#domainCollisionsModal' ) . modal ( 'hide' ) ;
2019-09-10 19:21:30 +02:00
$scope . location . busy = true ;
$scope . location . error = { } ;
2019-09-20 01:55:45 +02:00
$scope . location . domainCollisions = [ ] ;
2019-09-10 19:21:30 +02:00
2022-01-20 16:58:00 -08:00
var secondaryDomains = { } ;
for ( var env2 in $scope . location . secondaryDomains ) {
secondaryDomains [ env2 ] = {
subdomain : $scope . location . secondaryDomains [ env2 ] . subdomain ,
domain : $scope . location . secondaryDomains [ env2 ] . domain . domain
} ;
}
2024-07-16 22:21:36 +02:00
// only use enabled ports
var ports = { } ;
for ( var env in $scope . location . ports ) {
if ( $scope . location . portsEnabled [ env ] ) {
ports [ env ] = $scope . location . ports [ env ] ;
2019-09-10 19:21:30 +02:00
}
}
var data = {
2019-09-20 01:55:45 +02:00
overwriteDns : ! ! overwriteDns ,
2022-01-16 18:29:32 -08:00
subdomain : $scope . location . subdomain ,
2019-09-10 19:21:30 +02:00
domain : $scope . location . domain . domain ,
2024-07-16 22:21:36 +02:00
ports : ports ,
2022-01-20 16:58:00 -08:00
secondaryDomains : secondaryDomains ,
2022-01-14 22:32:41 -08:00
redirectDomains : $scope . location . redirectDomains . map ( function ( a ) { return { subdomain : a . subdomain , domain : a . domain . domain } ; } ) ,
2021-01-18 17:55:48 -08:00
aliasDomains : $scope . location . aliasDomains . map ( function ( a ) { return { subdomain : a . subdomain , domain : a . domain . domain } ; } )
2019-09-10 19:21:30 +02:00
} ;
2019-09-20 01:55:45 +02:00
// pre-flight only for changed domains
var domains = [ ] ;
2022-02-07 17:23:17 -08:00
if ( $scope . app . domain !== data . domain || $scope . app . subdomain !== data . subdomain ) domains . push ( { subdomain : data . subdomain , domain : data . domain , type : 'primary' } ) ;
2022-02-07 22:22:10 -08:00
Object . keys ( data . secondaryDomains ) . forEach ( function ( env ) {
var subdomain = data . secondaryDomains [ env ] . subdomain , domain = data . secondaryDomains [ env ] . domain ;
if ( $scope . app . secondaryDomains . some ( function ( d ) { return d . domain === domain && d . subdomain === subdomain ; } ) ) return ;
domains . push ( { subdomain : subdomain , domain : domain , type : 'secondary' } ) ;
} ) ;
2022-01-14 22:32:41 -08:00
data . redirectDomains . forEach ( function ( a ) {
if ( $scope . app . redirectDomains . some ( function ( d ) { return d . domain === a . domain && d . subdomain === a . subdomain ; } ) ) return ;
2021-01-18 17:55:48 -08:00
domains . push ( { subdomain : a . subdomain , domain : a . domain , type : 'redirect' } ) ;
} ) ;
data . aliasDomains . forEach ( function ( a ) {
if ( $scope . app . aliasDomains . some ( function ( d ) { return d . domain === a . domain && d . subdomain === a . subdomain ; } ) ) return ;
domains . push ( { subdomain : a . subdomain , domain : a . domain , type : 'alias' } ) ;
2019-09-20 01:55:45 +02:00
} ) ;
2022-02-07 22:22:10 -08:00
var canConfigure = true ;
2020-06-03 23:17:06 +02:00
async . eachSeries ( domains , function ( domain , callback ) {
2019-09-20 01:55:45 +02:00
if ( overwriteDns ) return callback ( ) ;
2019-09-24 00:04:31 -07:00
Client . checkDNSRecords ( domain . domain , domain . subdomain , function ( error , result ) {
2019-09-23 23:47:33 +02:00
if ( error ) return callback ( error ) ;
if ( result . error ) {
2022-02-07 17:23:17 -08:00
if ( domain . type === 'primary' ) {
2019-09-23 23:47:33 +02:00
$scope . location . error . location = domain . domain + ' ' + result . error . message ;
2021-01-18 17:55:48 -08:00
} else if ( domain . type === 'alias' ) {
$scope . location . error . aliasDomains = domain . domain + ' ' + result . error . message ;
2019-09-23 19:23:00 +02:00
} else {
2022-01-14 22:32:41 -08:00
$scope . location . error . redirectDomains = domain . domain + ' ' + result . error . message ;
2019-09-23 19:23:00 +02:00
}
$scope . location . busy = false ;
2022-02-07 22:22:10 -08:00
canConfigure = false ;
} else if ( result . needsOverwrite ) {
$scope . location . domainCollisions . push ( domain ) ;
canConfigure = false ;
2019-09-23 19:23:00 +02:00
}
2019-09-20 01:55:45 +02:00
callback ( ) ;
} ) ;
} , function ( error ) {
if ( error ) {
$scope . location . busy = false ;
return Client . error ( error ) ;
}
2019-09-16 19:58:15 +02:00
2022-02-07 22:22:10 -08:00
if ( ! canConfigure ) {
2019-09-16 19:58:15 +02:00
$scope . location . busy = false ;
2019-09-20 01:55:45 +02:00
return $ ( '#domainCollisionsModal' ) . modal ( 'show' ) ;
2019-09-16 19:58:15 +02:00
}
2019-09-10 19:21:30 +02:00
2019-09-20 01:55:45 +02:00
Client . configureApp ( $scope . app . id , 'location' , data , function ( error ) {
if ( error && ( error . statusCode === 409 || error . statusCode === 400 ) ) {
2022-02-10 15:41:41 +01:00
var errorMessage = error . message . toLowerCase ( ) ;
2022-02-07 16:11:57 -08:00
if ( errorMessage . indexOf ( 'location' ) !== - 1 ) {
if ( errorMessage . indexOf ( 'primary' ) !== - 1 ) {
2019-09-27 14:42:37 -07:00
$scope . location . error . location = error . message ;
$scope . locationForm . $setPristine ( ) ;
2022-02-07 16:11:57 -08:00
} else if ( errorMessage . indexOf ( 'secondary' ) !== - 1 ) {
2022-02-07 13:44:26 -08:00
$scope . location . error . secondaryDomain = error . message ;
2022-02-07 16:11:57 -08:00
} else if ( errorMessage . indexOf ( 'redirect' ) !== - 1 ) {
2022-01-14 22:32:41 -08:00
$scope . location . error . redirectDomains = error . message ;
2022-02-07 16:11:57 -08:00
} else if ( errorMessage . indexOf ( 'alias' ) !== - 1 ) {
2022-02-07 13:44:26 -08:00
$scope . location . error . aliasDomains = error . message ;
2019-09-27 14:42:37 -07:00
}
2022-02-07 16:11:57 -08:00
} else if ( errorMessage . indexOf ( 'port' ) !== - 1 ) {
2019-09-27 14:42:37 -07:00
$scope . location . error . port = error . message ;
2022-02-07 16:11:57 -08:00
} else {
$scope . location . error . location = error . message ; // fallback
2019-09-20 01:55:45 +02:00
}
$scope . location . busy = false ;
return ;
}
if ( error ) return Client . error ( error ) ;
2019-12-20 19:09:17 -08:00
refreshApp ( $scope . app . id , function ( error ) {
if ( error ) return Client . error ( error ) ;
2019-09-11 21:24:25 +02:00
2019-12-20 19:09:17 -08:00
$scope . locationForm . $setPristine ( ) ;
$timeout ( function ( ) { $scope . location . busy = false ; } , 1000 ) ;
} ) ;
2019-09-20 01:55:45 +02:00
} ) ;
2019-09-10 19:21:30 +02:00
} ) ;
}
} ;
$scope . access = {
busy : false ,
error : { } ,
success : false ,
ftp : false ,
ssoAuth : false ,
accessRestrictionOption : 'any' ,
2021-09-24 13:19:07 +02:00
accessRestrictionOptionCur : 'any' ,
2019-09-10 19:21:30 +02:00
accessRestriction : { users : [ ] , groups : [ ] } ,
2021-09-21 15:26:05 -07:00
operators : { users : [ ] , groups : [ ] } ,
2019-09-10 19:21:30 +02:00
isAccessRestrictionValid : function ( ) {
var tmp = $scope . access . accessRestriction ;
return ! ! ( tmp . users . length || tmp . groups . length ) ;
} ,
show : function ( ) {
var app = $scope . app ;
2019-09-19 18:31:11 +02:00
$scope . access . error = { } ;
2019-09-10 19:21:30 +02:00
$scope . access . ftp = app . manifest . addons . localstorage && app . manifest . addons . localstorage . ftp ;
2023-04-25 19:52:14 +02:00
$scope . access . ssoAuth = ( app . manifest . addons [ 'ldap' ] || app . manifest . addons [ 'oidc' ] || app . manifest . addons [ 'proxyAuth' ] ) && app . sso ;
2019-09-10 19:21:30 +02:00
$scope . access . accessRestrictionOption = app . accessRestriction ? 'groups' : 'any' ;
2021-09-24 13:19:07 +02:00
$scope . access . accessRestrictionOptionCur = app . accessRestriction ? 'groups' : 'any' ;
2019-09-10 19:21:30 +02:00
$scope . access . accessRestriction = { users : [ ] , groups : [ ] } ;
2021-09-21 15:26:05 -07:00
$scope . access . operators = { users : [ ] , groups : [ ] } ;
var userSet , groupSet ;
2019-09-10 19:21:30 +02:00
if ( app . accessRestriction ) {
2021-09-21 15:26:05 -07:00
userSet = { } ;
2019-09-10 19:21:30 +02:00
app . accessRestriction . users . forEach ( function ( uid ) { userSet [ uid ] = true ; } ) ;
$scope . users . forEach ( function ( u ) { if ( userSet [ u . id ] === true ) $scope . access . accessRestriction . users . push ( u ) ; } ) ;
2021-09-21 15:26:05 -07:00
groupSet = { } ;
if ( app . accessRestriction . groups ) app . accessRestriction . groups . forEach ( function ( gid ) { groupSet [ gid ] = true ; } ) ;
2019-09-10 19:21:30 +02:00
$scope . groups . forEach ( function ( g ) { if ( groupSet [ g . id ] === true ) $scope . access . accessRestriction . groups . push ( g ) ; } ) ;
}
2021-09-21 15:26:05 -07:00
if ( app . operators ) {
userSet = { } ;
app . operators . users . forEach ( function ( uid ) { userSet [ uid ] = true ; } ) ;
$scope . users . forEach ( function ( u ) { if ( userSet [ u . id ] === true ) $scope . access . operators . users . push ( u ) ; } ) ;
groupSet = { } ;
if ( app . operators . groups ) app . operators . groups . forEach ( function ( gid ) { groupSet [ gid ] = true ; } ) ;
$scope . groups . forEach ( function ( g ) { if ( groupSet [ g . id ] === true ) $scope . access . operators . groups . push ( g ) ; } ) ;
}
2019-09-10 19:21:30 +02:00
} ,
submit : function ( ) {
$scope . access . busy = true ;
$scope . access . error = { } ;
var accessRestriction = null ;
if ( $scope . access . accessRestrictionOption === 'groups' ) {
accessRestriction = { users : [ ] , groups : [ ] } ;
accessRestriction . users = $scope . access . accessRestriction . users . map ( function ( u ) { return u . id ; } ) ;
accessRestriction . groups = $scope . access . accessRestriction . groups . map ( function ( g ) { return g . id ; } ) ;
}
2021-09-21 15:26:05 -07:00
var operators = null ;
if ( $scope . access . operators . users . length || $scope . access . operators . groups . length ) {
operators = { users : [ ] , groups : [ ] } ;
operators . users = $scope . access . operators . users . map ( function ( u ) { return u . id ; } ) ;
operators . groups = $scope . access . operators . groups . map ( function ( g ) { return g . id ; } ) ;
}
2021-09-24 13:19:07 +02:00
async . series ( [
function ( callback ) {
if ( $scope . access . accessRestrictionOption === $scope . access . accessRestrictionOptionCur && ! $scope . accessForm . accessUsersSelect . $dirty && ! $scope . accessForm . accessGroupsSelect . $dirty ) return callback ( ) ;
Client . configureApp ( $scope . app . id , 'access_restriction' , { accessRestriction : accessRestriction } , callback ) ;
} ,
function ( callback ) {
if ( ! $scope . accessForm . operatorsUsersSelect . $dirty && ! $scope . accessForm . operatorsGroupsSelect . $dirty ) return callback ( ) ;
Client . configureApp ( $scope . app . id , 'operators' , { operators : operators } , callback ) ;
}
] , function ( error ) {
2019-09-10 19:21:30 +02:00
if ( error ) return Client . error ( error ) ;
2021-09-24 13:19:07 +02:00
$scope . accessForm . $setPristine ( ) ;
2021-09-21 15:26:05 -07:00
2021-09-24 13:19:07 +02:00
$scope . access . accessRestrictionOptionCur = $scope . access . accessRestrictionOption ;
2021-10-19 20:16:39 -07:00
$timeout ( function ( ) {
$scope . access . success = true ;
$scope . access . busy = false ;
} , 3000 ) ;
2019-09-10 19:21:30 +02:00
} ) ;
}
} ;
$scope . resources = {
error : { } ,
2020-04-29 22:18:44 -07:00
busy : false ,
2019-09-13 11:05:34 +02:00
currentMemoryLimit : 0 ,
2024-04-10 10:01:11 +02:00
memoryLimit : 0 , // RAM
2019-09-10 19:21:30 +02:00
memoryTicks : [ ] ,
2020-01-28 22:05:06 -08:00
2024-04-10 17:38:49 +02:00
currentCpuQuota : 0 ,
cpuQuota : 0 ,
2024-12-05 14:49:36 +01:00
devices : '' ,
2020-01-28 22:05:06 -08:00
2019-09-10 19:21:30 +02:00
show : function ( ) {
var app = $scope . app ;
2024-03-11 19:20:38 +01:00
$scope . resources . busy = true ;
2019-09-10 19:21:30 +02:00
2019-09-19 18:31:11 +02:00
$scope . resources . error = { } ;
2019-09-13 11:05:34 +02:00
$scope . resources . currentMemoryLimit = app . memoryLimit || app . manifest . memoryLimit || ( 256 * 1024 * 1024 ) ;
2019-09-10 19:21:30 +02:00
2024-04-10 10:01:11 +02:00
Client . memory ( function ( error , result ) {
if ( error ) console . error ( error ) ;
2019-12-12 12:13:06 +01:00
2024-04-10 10:01:11 +02:00
// create ticks starting from manifest memory limit. the memory limit here is just RAM
2019-12-12 12:13:06 +01:00
$scope . resources . memoryTicks = [ ] ;
2024-04-10 10:01:11 +02:00
// we max system memory and current app memory for the case where the user configured the app on another server with more resources
var nearest256m = Math . ceil ( Math . max ( result . memory , $scope . resources . currentMemoryLimit ) / ( 256 * 1024 * 1024 ) ) * 256 * 1024 * 1024 ;
var startTick = app . manifest . memoryLimit || ( 256 * 1024 * 1024 ) ;
2024-08-30 12:07:31 +02:00
// code below ensure we atleast have 2 ticks to keep the slider usable
$scope . resources . memoryTicks . push ( startTick ) ; // start tick
for ( var i = startTick * 2 ; i < nearest256m ; i *= 2 ) {
2024-04-10 10:01:11 +02:00
$scope . resources . memoryTicks . push ( i ) ;
2019-12-12 12:13:06 +01:00
}
2024-08-30 12:07:31 +02:00
$scope . resources . memoryTicks . push ( nearest256m ) ; // end tick
2019-12-12 12:13:06 +01:00
} ) ;
2024-03-11 19:20:38 +01:00
// for firefox widget update
$timeout ( function ( ) {
2024-04-10 17:38:49 +02:00
$scope . resources . currentCpuQuota = $scope . resources . cpuQuota = app . cpuQuota ;
2024-03-11 19:20:38 +01:00
$scope . resources . memoryLimit = $scope . resources . currentMemoryLimit ;
$scope . resources . busy = false ;
} , 500 ) ;
2024-12-05 14:49:36 +01:00
$scope . resources . devices = Object . keys ( app . devices ) . join ( ', ' ) ;
2019-09-10 19:21:30 +02:00
} ,
2019-09-18 17:12:10 +02:00
submitMemoryLimit : function ( ) {
2019-09-10 19:21:30 +02:00
$scope . resources . busy = true ;
$scope . resources . error = { } ;
2024-03-11 19:20:38 +01:00
const tmp = parseInt ( $scope . resources . memoryLimit ) ;
const memoryLimit = tmp === $scope . resources . memoryTicks [ 0 ] ? 0 : tmp ;
Client . configureApp ( $scope . app . id , 'memory_limit' , { memoryLimit } , function ( error ) {
2020-10-21 13:27:31 +02:00
if ( error && error . statusCode === 400 ) {
$scope . resources . busy = false ;
$scope . resources . error . memoryLimit = true ;
return ;
}
2019-09-10 19:21:30 +02:00
if ( error ) return Client . error ( error ) ;
2019-09-13 11:12:11 +02:00
$scope . resources . currentMemoryLimit = $scope . resources . memoryLimit ;
2019-09-10 19:21:30 +02:00
2019-12-20 19:09:17 -08:00
refreshApp ( $scope . app . id , function ( error ) {
if ( error ) return Client . error ( error ) ;
$timeout ( function ( ) { $scope . resources . busy = false ; } , 1000 ) ;
} ) ;
2019-09-18 17:12:10 +02:00
} ) ;
2020-01-28 22:05:06 -08:00
} ,
2024-04-10 17:38:49 +02:00
submitCpuQuota : function ( ) {
2024-03-11 19:20:38 +01:00
$scope . resources . busy = true ;
2020-01-28 22:05:06 -08:00
$scope . resources . error = { } ;
2024-04-10 17:38:49 +02:00
Client . configureApp ( $scope . app . id , 'cpu_quota' , { cpuQuota : parseInt ( $scope . resources . cpuQuota ) } , function ( error ) {
2020-01-28 22:05:06 -08:00
if ( error ) return Client . error ( error ) ;
2024-04-10 17:38:49 +02:00
$scope . resources . currentCpuQuota = $scope . resources . cpuQuota ;
2020-01-28 22:05:06 -08:00
refreshApp ( $scope . app . id , function ( error ) {
if ( error ) return Client . error ( error ) ;
2024-03-11 19:20:38 +01:00
$timeout ( function ( ) { $scope . resources . busy = false ; } , 1000 ) ;
2020-01-28 22:05:06 -08:00
} ) ;
} ) ;
2019-09-18 17:12:10 +02:00
} ,
2024-12-05 14:49:36 +01:00
submitDevices : function ( ) {
$scope . resources . busy = true ;
$scope . resources . error = { } ;
const devices = { } ;
$scope . resources . devices . split ( ',' ) . forEach ( d => {
if ( ! d . trim ( ) ) return ;
devices [ d . trim ( ) ] = { } ;
} ) ;
Client . configureApp ( $scope . app . id , 'devices' , { devices } , function ( error ) {
2024-12-05 15:27:10 +01:00
if ( error && error . statusCode === 400 ) {
$scope . resources . error . devices = error . message ;
return $scope . resources . busy = false ;
} else if ( error ) {
return Client . error ( error ) ;
}
2024-12-05 14:49:36 +01:00
refreshApp ( $scope . app . id , function ( error ) {
if ( error ) return Client . error ( error ) ;
$timeout ( function ( ) { $scope . resources . busy = false ; } , 1000 ) ;
} ) ;
} ) ;
} ,
2020-10-28 22:11:05 -07:00
} ;
2023-07-13 15:06:07 +05:30
$scope . services = {
error : { } ,
busy : false ,
enableTurn : '1' , // curse of radio buttons
2023-07-14 09:03:23 +05:30
enableRedis : '1' ,
2023-07-13 15:06:07 +05:30
show : function ( ) {
var app = $scope . app ;
$scope . services . error = { } ;
$scope . services . enableTurn = app . enableTurn ? '1' : '0' ;
2023-07-14 09:03:23 +05:30
$scope . services . enableRedis = app . enableRedis ? '1' : '0' ;
2023-07-13 15:06:07 +05:30
} ,
submitTurn : function ( ) {
$scope . services . busy = true ;
$scope . services . error = { } ;
Client . configureApp ( $scope . app . id , 'turn' , { enable : $scope . services . enableTurn === '1' } , function ( error ) {
if ( error && error . statusCode === 400 ) {
$scope . services . busy = false ;
$scope . services . error . turn = true ;
return ;
}
if ( error ) return Client . error ( error ) ;
$timeout ( function ( ) { $scope . services . busy = false ; } , 1000 ) ;
} ) ;
} ,
2023-07-14 09:03:23 +05:30
submitRedis : function ( ) {
$scope . services . busy = true ;
$scope . services . error = { } ;
Client . configureApp ( $scope . app . id , 'redis' , { enable : $scope . services . enableRedis === '1' } , function ( error ) {
if ( error && error . statusCode === 400 ) {
$scope . services . busy = false ;
$scope . services . error . redis = true ;
return ;
}
if ( error ) return Client . error ( error ) ;
$timeout ( function ( ) { $scope . services . busy = false ; } , 1000 ) ;
} ) ;
} ,
2023-07-13 15:06:07 +05:30
} ;
2020-10-28 22:11:05 -07:00
$scope . storage = {
error : { } ,
busy : false ,
busyDataDir : false ,
2022-06-03 09:10:16 -07:00
storageVolumeId : null ,
storageVolumePrefix : '' ,
2022-05-23 16:50:46 +02:00
2022-06-03 09:10:16 -07:00
location : null ,
2022-05-23 16:50:46 +02:00
locationOptions : [ ] ,
2020-10-28 22:11:05 -07:00
busyBinds : false ,
mounts : [ ] , // { volume, readOnly }
show : function ( ) {
var app = $scope . app ;
$scope . storage . error = { } ;
2022-06-03 09:10:16 -07:00
$scope . storage . storageVolumeId = app . storageVolumeId ;
2022-06-03 10:44:13 -07:00
$scope . storage . storageVolumePrefix = app . storageVolumePrefix || '' ;
2020-10-28 22:11:05 -07:00
$scope . storage . mounts = [ ] ;
2022-05-23 16:50:46 +02:00
$scope . storage . locationOptions = [
2022-06-03 09:10:16 -07:00
{ id : 'default' , type : 'default' , displayName : 'Default - /home/yellowtent/appsdata/' + app . id } ,
2022-05-23 16:50:46 +02:00
] ;
$scope . volumes . forEach ( function ( volume ) {
2023-07-11 20:52:46 +05:30
$scope . storage . locationOptions . push ( { id : volume . id , type : 'volume' , value : volume . id , displayName : 'Volume - ' + volume . name , mountType : volume . mountType } ) ;
2022-05-23 16:50:46 +02:00
} ) ;
2022-06-03 09:10:16 -07:00
$scope . storage . location = $scope . storage . locationOptions . find ( function ( l ) { return l . id === ( app . storageVolumeId || 'default' ) ; } ) ;
2022-05-23 16:50:46 +02:00
2020-10-29 21:58:25 -07:00
app . mounts . forEach ( function ( mount ) { // { volumeId, readOnly }
2020-10-28 22:11:05 -07:00
var volume = $scope . volumes . find ( function ( v ) { return v . id === mount . volumeId ; } ) ;
2022-11-12 20:49:19 +01:00
$scope . storage . mounts . push ( { volume : volume , readOnly : mount . readOnly ? 'true' : 'false' } ) ;
2020-10-28 22:11:05 -07:00
} ) ;
} ,
2019-09-18 17:12:10 +02:00
submitDataDir : function ( ) {
2020-10-28 22:11:05 -07:00
$scope . storage . busyDataDir = true ;
$scope . storage . error = { } ;
2019-09-18 17:12:10 +02:00
2022-06-03 09:10:16 -07:00
var data = { storageVolumeId : null , storageVolumePrefix : null } ;
if ( $scope . storage . location . id !== 'default' ) {
data . storageVolumeId = $scope . storage . location . id ;
data . storageVolumePrefix = $scope . storage . storageVolumePrefix ;
}
Client . configureApp ( $scope . app . id , 'storage' , data , function ( error ) {
2019-09-18 17:12:10 +02:00
if ( error && error . statusCode === 400 ) {
2022-06-03 09:10:16 -07:00
$scope . storage . error . storageVolumePrefix = error . message ;
2020-10-28 22:11:05 -07:00
$scope . storage . busyDataDir = false ;
2019-09-18 17:12:10 +02:00
return ;
2024-06-06 15:22:33 +02:00
} else if ( error ) {
Client . error ( error ) ;
$scope . storage . busyDataDir = false ;
return ;
2019-09-18 17:12:10 +02:00
}
2020-10-28 22:11:05 -07:00
$scope . storageDataDirForm . $setPristine ( ) ;
2019-09-18 17:12:10 +02:00
2019-12-20 19:09:17 -08:00
refreshApp ( $scope . app . id , function ( error ) {
if ( error ) return Client . error ( error ) ;
2020-10-28 22:11:05 -07:00
$timeout ( function ( ) { $scope . storage . busyDataDir = false ; } , 1000 ) ;
2019-12-20 19:09:17 -08:00
} ) ;
2019-09-10 19:21:30 +02:00
} ) ;
2020-04-29 22:18:44 -07:00
} ,
2020-10-28 22:11:05 -07:00
addMount : function ( event ) {
2020-04-29 22:18:44 -07:00
event . preventDefault ( ) ;
2020-10-28 22:11:05 -07:00
$scope . storage . mounts . push ( {
volume : $scope . volumes [ 0 ] ,
2020-04-29 22:18:44 -07:00
readOnly : true
} ) ;
} ,
2020-10-28 22:11:05 -07:00
delMount : function ( event , index ) {
2020-04-29 22:18:44 -07:00
event . preventDefault ( ) ;
2020-10-28 22:11:05 -07:00
$scope . storage . mounts . splice ( index , 1 ) ;
2020-04-29 22:18:44 -07:00
} ,
2020-10-28 22:11:05 -07:00
submitMounts : function ( ) {
$scope . storage . busyMounts = true ;
$scope . storage . error = { } ;
2020-04-29 22:18:44 -07:00
2020-10-28 22:11:05 -07:00
var data = [ ] ;
$scope . storage . mounts . forEach ( function ( mount ) {
2022-11-12 20:49:19 +01:00
data . push ( { volumeId : mount . volume . id , readOnly : mount . readOnly === 'true' } ) ;
2020-04-29 22:18:44 -07:00
} ) ;
2020-10-28 22:11:05 -07:00
Client . configureApp ( $scope . app . id , 'mounts' , { mounts : data } , function ( error ) {
2020-04-29 22:18:44 -07:00
if ( error && error . statusCode === 400 ) {
2020-10-28 22:11:05 -07:00
$scope . storage . error . mounts = error . message ;
$scope . storage . busyMounts = false ;
2020-04-29 22:18:44 -07:00
return ;
}
if ( error ) return Client . error ( error ) ;
refreshApp ( $scope . app . id , function ( error ) {
if ( error ) return Client . error ( error ) ;
2020-10-28 22:11:05 -07:00
$timeout ( function ( ) { $scope . storage . busyMounts = false ; } , 1000 ) ;
2020-04-29 22:18:44 -07:00
} ) ;
} ) ;
2019-09-10 19:21:30 +02:00
}
} ;
2020-05-13 00:42:27 +02:00
$scope . graphs = {
error : { } ,
2022-10-11 21:36:30 +02:00
busy : true ,
2020-05-13 00:42:27 +02:00
2022-10-15 10:28:52 +02:00
period : 6 ,
2020-05-13 00:42:27 +02:00
memoryChart : null ,
diskChart : null ,
2022-10-14 12:00:41 +02:00
blockReadTotal : 0 ,
blockWriteTotal : 0 ,
networkReadTotal : 0 ,
networkWriteTotal : 0 ,
2021-04-01 16:05:13 +02:00
setPeriod : function ( hours ) {
2020-05-13 00:42:27 +02:00
$scope . graphs . period = hours ;
$scope . graphs . show ( ) ;
} ,
show : function ( ) {
2022-10-11 21:36:30 +02:00
$scope . graphs . busy = true ;
2022-09-14 13:03:24 +02:00
// in minutes
2020-05-13 01:12:13 +02:00
var timePeriod = $scope . graphs . period * 60 ;
2020-05-13 00:42:27 +02:00
2022-10-11 18:31:03 +02:00
// keep in sync with graphs.js
var timeBucketSizeMinutes = timePeriod > ( 24 * 60 ) ? ( 6 * 60 ) : 5 ;
var steps = Math . floor ( timePeriod / timeBucketSizeMinutes ) ;
var labels = new Array ( steps ) . fill ( 0 ) ;
labels = labels . map ( function ( v , index ) {
var dateTime = new Date ( Date . now ( ) - ( ( timePeriod - ( index * timeBucketSizeMinutes ) ) * 60 * 1000 ) ) ;
2020-05-22 17:16:37 +02:00
2022-10-11 18:31:03 +02:00
if ( $scope . graphs . period > 24 ) {
return dateTime . toLocaleDateString ( ) ;
} else {
return dateTime . toLocaleTimeString ( ) ;
}
} ) ;
2020-05-22 17:16:37 +02:00
2022-10-11 18:31:03 +02:00
var borderColors = [ '#2196F3' , '#FF6384' ] ;
var backgroundColors = [ '#82C4F844' , '#FF63844F' ] ;
2020-09-02 18:53:46 +02:00
2022-10-14 22:22:34 +02:00
function fillGraph ( canvasId , contents , chartPropertyName , divisor , max , format , formatDivisor , stepSize ) {
2022-10-11 18:31:03 +02:00
if ( ! contents || ! contents [ 0 ] ) return ; // no data available yet
var datasets = [ ] ;
contents . forEach ( function ( content , index ) {
// fill holes with previous value
var cur = 0 ;
2022-10-14 11:28:00 +02:00
content . data . forEach ( function ( d ) {
2022-10-11 18:31:03 +02:00
if ( d [ 0 ] === null ) d [ 0 ] = cur ;
else cur = d [ 0 ] ;
} ) ;
var datapoints = Array ( steps ) . map ( function ( ) { return '0' ; } ) ;
// walk backwards and fill up the datapoints
2022-10-14 11:28:00 +02:00
content . data . reverse ( ) . forEach ( function ( d , index ) {
2022-10-11 18:31:03 +02:00
datapoints [ datapoints . length - 1 - index ] = ( d [ 0 ] / divisor ) . toFixed ( 2 ) ;
// return parseInt((d[0] / divisor).toFixed(2));
} ) ;
datasets . push ( {
label : content . label ,
backgroundColor : backgroundColors [ index ] ,
borderColor : borderColors [ index ] ,
borderWidth : 1 ,
radius : 0 ,
data : datapoints ,
cubicInterpolationMode : 'monotone' ,
tension : 0.4
} ) ;
2020-05-13 00:42:27 +02:00
} ) ;
2020-05-22 14:52:23 -07:00
var graphData = {
2020-05-13 00:42:27 +02:00
labels : labels ,
2022-10-11 18:31:03 +02:00
datasets : datasets
2020-05-13 00:42:27 +02:00
} ;
var options = {
2022-10-11 18:31:03 +02:00
responsive : true ,
2020-05-13 01:12:13 +02:00
maintainAspectRatio : true ,
aspectRatio : 2.5 ,
2022-10-11 21:36:30 +02:00
animation : false ,
2022-10-11 20:00:19 +02:00
plugins : {
legend : {
display : false
}
2020-05-13 00:42:27 +02:00
} ,
2022-10-11 19:25:18 +02:00
interaction : {
intersect : false ,
mode : 'index' ,
2020-05-13 00:42:27 +02:00
} ,
scales : {
2022-10-11 18:31:03 +02:00
x : {
2022-10-11 19:25:18 +02:00
ticks : { autoSkipPadding : 50 , maxRotation : 0 }
2022-10-11 18:31:03 +02:00
} ,
y : {
2022-10-14 22:22:34 +02:00
ticks : { maxTicksLimit : 6 } ,
2022-10-11 18:31:03 +02:00
min : 0 ,
beginAtZero : true
}
2020-05-13 00:42:27 +02:00
}
} ;
2022-10-21 13:27:31 +02:00
if ( format ) options . scales . y . ticks . callback = function ( value ) {
if ( ! formatDivisor ) return value + ' ' + format ;
return ( value / formatDivisor ) . toLocaleString ( 'en-US' , { maximumFractionDigits : 6 } ) + ' ' + format ;
} ;
2022-10-11 18:31:03 +02:00
if ( max ) options . scales . y . max = max ;
2022-10-14 22:22:34 +02:00
if ( stepSize ) options . scales . y . ticks . stepSize = stepSize ;
2022-10-11 18:31:03 +02:00
2020-05-13 00:42:27 +02:00
var ctx = $ ( canvasId ) . get ( 0 ) . getContext ( '2d' ) ;
if ( $scope . graphs [ chartPropertyName ] ) $scope . graphs [ chartPropertyName ] . destroy ( ) ;
2020-05-22 14:52:23 -07:00
$scope . graphs [ chartPropertyName ] = new Chart ( ctx , { type : 'line' , data : graphData , options : options } ) ;
2020-05-13 00:42:27 +02:00
}
2022-09-14 13:03:24 +02:00
Client . getAppGraphs ( appId , timePeriod , function ( error , result ) {
2020-05-13 00:42:27 +02:00
if ( error ) return console . error ( error ) ;
2022-10-14 21:39:34 +02:00
var currentMemoryLimit = $scope . app . memoryLimit || $scope . app . manifest . memoryLimit || 0 ;
var maxGraphMemory = currentMemoryLimit < ( 512 * 1024 * 1024 ) ? ( 512 * 1024 * 1024 ) : currentMemoryLimit ;
2022-10-13 23:05:10 +02:00
var cpuCount = result . cpuCount ;
2022-10-14 12:00:41 +02:00
var ioDivisor = 1000 * 1000 ;
$scope . graphs . blockReadTotal = ( result . blockReadTotal / ioDivisor / 1000 ) . toFixed ( 2 ) + ' MB' ;
$scope . graphs . blockWriteTotal = ( result . blockWriteTotal / ioDivisor / 1000 ) . toFixed ( 2 ) + ' MB' ;
$scope . graphs . networkReadTotal = ( result . networkReadTotal / ioDivisor / 1000 ) . toFixed ( 2 ) + ' MB' ;
$scope . graphs . networkWriteTotal = ( result . networkWriteTotal / ioDivisor / 1000 ) . toFixed ( 2 ) + ' MB' ;
2020-05-13 23:38:32 +02:00
2022-10-14 22:22:34 +02:00
fillGraph ( '#graphsMemoryChart' , [ { data : result . memory , label : 'Memory' } ] , 'memoryChart' , 1024 * 1024 , maxGraphMemory / 1024 / 1024 , 'GiB' , 1024 , ( maxGraphMemory / 1024 / 1024 ) <= 1024 ? 256 : 512 ) ;
2022-10-13 23:05:10 +02:00
fillGraph ( '#graphsCpuChart' , [ { data : result . cpu , label : 'CPU' } ] , 'cpuChart' , 1 , cpuCount * 100 , '%' ) ;
2022-10-14 12:22:05 +02:00
fillGraph ( '#graphsDiskChart' , [ { data : result . blockRead , label : 'read' } , { data : result . blockWrite , label : 'write' } ] , 'diskChart' , ioDivisor , null , 'kB/s' ) ;
fillGraph ( '#graphsNetworkChart' , [ { data : result . networkRead , label : 'inbound' } , { data : result . networkWrite , label : 'outbound' } ] , 'networkChart' , ioDivisor , null , 'kB/s' ) ;
2022-10-11 21:36:30 +02:00
$scope . graphs . busy = false ;
2020-05-13 00:42:27 +02:00
} ) ;
}
} ;
2021-12-03 11:23:25 +01:00
function findInbox ( inboxes , app ) {
return inboxes . find ( function ( i ) { return i . name === app . inboxName && i . domain === ( app . inboxDomain || app . domain ) ; } ) ;
}
2019-09-10 19:21:30 +02:00
$scope . email = {
2021-03-16 22:41:25 -07:00
enableMailbox : true ,
2019-09-10 19:21:30 +02:00
mailboxName : '' ,
2021-10-02 03:03:15 -07:00
mailboxDomain : null ,
2022-06-01 01:36:59 -07:00
mailboxDisplayName : '' ,
2020-02-27 16:04:11 +01:00
currentMailboxName : '' ,
currentMailboxDomainName : '' ,
2021-10-02 03:03:15 -07:00
mailboxError : { } ,
2021-12-01 20:37:44 -08:00
mailboxBusy : false ,
2021-10-02 03:03:15 -07:00
inboxError : { } ,
2021-12-01 20:37:44 -08:00
inboxBusy : false ,
2021-12-03 11:23:25 +01:00
enableInbox : true ,
inboxes : [ ] ,
currentInbox : null ,
inbox : null ,
2019-09-10 19:21:30 +02:00
show : function ( ) {
var app = $scope . app ;
2019-09-19 18:00:18 -07:00
$scope . emailForm . $setPristine ( ) ;
2021-10-02 03:03:15 -07:00
$scope . email . mailboxError = { } ;
2021-03-18 18:26:33 -07:00
$scope . email . enableMailbox = app . enableMailbox ? '1' : '0' ;
2019-09-10 19:21:30 +02:00
$scope . email . mailboxName = app . mailboxName || '' ;
2022-07-21 10:32:08 +02:00
$scope . email . mailboxDisplayName = app . mailboxDisplayName || '' ;
2021-10-02 03:03:15 -07:00
$scope . email . mailboxDomain = $scope . domains . filter ( function ( d ) { return d . domain === ( app . mailboxDomain || app . domain ) ; } ) [ 0 ] ;
2020-02-27 16:04:11 +01:00
$scope . email . currentMailboxName = app . mailboxName || '' ;
2021-10-02 03:03:15 -07:00
$scope . email . currentMailboxDomainName = $scope . email . mailboxDomain ? $scope . email . mailboxDomain . domain : '' ;
$scope . email . inboxError = { } ;
2021-12-03 11:23:25 +01:00
$scope . email . enableInbox = app . enableInbox ? true : false ;
Client . getAllMailboxes ( function ( error , mailboxes ) {
if ( error ) console . error ( 'Failed to list mailboxes.' , error ) ;
$scope . email . inboxes = mailboxes . map ( function ( m ) { return { display : m . name + '@' + m . domain , name : m . name , domain : m . domain } ; } ) ;
$scope . email . currentInbox = findInbox ( $scope . email . inboxes , app ) ;
$scope . email . inbox = findInbox ( $scope . email . inboxes , app ) ;
} ) ;
2019-09-10 19:21:30 +02:00
} ,
2021-10-02 03:03:15 -07:00
submitMailbox : function ( ) {
2019-09-17 15:09:39 +02:00
$scope . email . error = { } ;
2021-12-01 20:37:44 -08:00
$scope . email . mailboxBusy = true ;
2019-09-11 21:24:25 +02:00
2021-10-02 03:03:15 -07:00
var data = {
enable : $scope . email . enableMailbox === '1'
} ;
if ( data . enable ) {
data . mailboxName = $scope . email . mailboxName || null ;
data . mailboxDomain = $scope . email . mailboxDomain . domain ;
2022-06-01 01:36:59 -07:00
data . mailboxDisplayName = $scope . email . mailboxDisplayName ;
2021-10-02 03:03:15 -07:00
}
Client . configureApp ( $scope . app . id , 'mailbox' , data , function ( error ) {
2019-09-17 15:09:39 +02:00
if ( error && error . statusCode === 400 ) {
2021-12-01 20:37:44 -08:00
$scope . email . mailboxBusy = false ;
2019-09-17 15:09:39 +02:00
$scope . email . error . mailboxName = error . message ;
$scope . emailForm . $setPristine ( ) ;
return ;
}
if ( error ) return Client . error ( error ) ;
2019-09-11 21:24:25 +02:00
2019-09-19 18:00:18 -07:00
$scope . emailForm . $setPristine ( ) ;
2019-12-16 16:08:49 -08:00
refreshApp ( $scope . app . id , function ( error ) {
2019-12-20 19:09:17 -08:00
if ( error ) return Client . error ( error ) ;
2019-09-19 18:00:18 -07:00
// when the mailboxName is 'reset', this will fill it up with the default again
2021-03-18 18:26:33 -07:00
$scope . email . enableMailbox = $scope . app . enableMailbox ? '1' : '0' ;
2019-09-19 18:00:18 -07:00
$scope . email . mailboxName = $scope . app . mailboxName || '' ;
2021-10-02 03:03:15 -07:00
$scope . email . mailboxDomain = $scope . domains . filter ( function ( d ) { return d . domain === ( $scope . app . mailboxDomain || $scope . app . domain ) ; } ) [ 0 ] ;
2020-02-27 16:04:11 +01:00
$scope . email . currentMailboxName = $scope . app . mailboxName || '' ;
2021-10-02 03:03:15 -07:00
$scope . email . currentMailboxDomainName = $scope . email . mailboxDomain ? $scope . email . mailboxDomain . domain : '' ;
2021-12-01 20:37:44 -08:00
$timeout ( function ( ) { $scope . email . mailboxBusy = false ; } , 1000 ) ;
2021-10-02 03:03:15 -07:00
} ) ;
} ) ;
} ,
submitInbox : function ( ) {
$scope . email . error = { } ;
2021-12-01 20:37:44 -08:00
$scope . email . inboxBusy = true ;
2021-10-02 03:03:15 -07:00
var data = {
2021-12-03 11:23:25 +01:00
enable : $scope . email . enableInbox
2021-10-02 03:03:15 -07:00
} ;
if ( data . enable ) {
2021-12-03 11:23:25 +01:00
data . inboxName = $scope . email . inbox . name ;
data . inboxDomain = $scope . email . inbox . domain ;
2021-10-02 03:03:15 -07:00
}
Client . configureApp ( $scope . app . id , 'inbox' , data , function ( error ) {
if ( error && error . statusCode === 400 ) {
2021-12-01 20:37:44 -08:00
$scope . email . inboxBusy = false ;
2021-10-02 03:03:15 -07:00
$scope . email . error . inboxName = error . message ;
return ;
}
if ( error ) return Client . error ( error ) ;
refreshApp ( $scope . app . id , function ( error ) {
if ( error ) return Client . error ( error ) ;
// when the mailboxName is 'reset', this will fill it up with the default again
2021-12-03 11:23:25 +01:00
$scope . email . enableInbox = $scope . app . enableInbox ? true : false ;
$scope . email . currentInbox = findInbox ( $scope . email . inboxes , $scope . app ) ;
$scope . email . inbox = findInbox ( $scope . email . inboxes , $scope . app ) ;
2019-12-20 19:09:17 -08:00
2021-12-01 20:37:44 -08:00
$timeout ( function ( ) { $scope . email . inboxBusy = false ; } , 1000 ) ;
2019-09-19 18:00:18 -07:00
} ) ;
2019-09-17 15:09:39 +02:00
} ) ;
2019-09-10 19:21:30 +02:00
}
} ;
2021-09-14 12:17:38 +02:00
$scope . eventlog = {
busy : false ,
2021-10-19 16:07:59 +02:00
eventLogs : [ ] ,
2021-09-14 12:17:38 +02:00
activeEventLog : null ,
2021-09-23 12:09:05 +02:00
currentPage : 1 ,
2021-10-19 09:49:53 -07:00
perPage : 15 ,
2021-09-14 12:17:38 +02:00
show : function ( ) {
2021-09-23 12:09:05 +02:00
$scope . eventlog . refresh ( ) ;
} ,
2021-09-14 12:17:38 +02:00
2021-09-23 12:09:05 +02:00
refresh : function ( ) {
$scope . eventlog . busy = true ;
2021-09-14 12:17:38 +02:00
2021-09-23 12:09:05 +02:00
Client . getAppEventLog ( $scope . app . id , $scope . eventlog . currentPage , $scope . eventlog . perPage , function ( error , result ) {
2021-09-14 12:17:38 +02:00
if ( error ) return console . error ( 'Failed to get events:' , error ) ;
$scope . eventlog . eventLogs = [ ] ;
result . forEach ( function ( e ) {
2021-10-19 09:49:53 -07:00
$scope . eventlog . eventLogs . push ( { raw : e , details : Client . eventLogDetails ( e , $scope . app . id ) , source : Client . eventLogSource ( e ) } ) ;
2021-09-14 12:17:38 +02:00
} ) ;
$scope . eventlog . busy = false ;
} ) ;
} ,
showDetails : function ( eventLog ) {
if ( $scope . eventlog . activeEventLog === eventLog ) $scope . eventlog . activeEventLog = null ;
else $scope . eventlog . activeEventLog = eventLog ;
2021-09-23 12:09:05 +02:00
} ,
showNextPage : function ( ) {
$scope . eventlog . currentPage ++ ;
$scope . eventlog . refresh ( ) ;
} ,
showPrevPage : function ( ) {
if ( $scope . eventlog . currentPage > 1 ) $scope . eventlog . currentPage -- ;
else $scope . eventlog . currentPage = 1 ;
$scope . eventlog . refresh ( ) ;
2021-09-14 12:17:38 +02:00
}
} ;
2021-09-27 15:32:44 -07:00
$scope . cron = {
busy : false ,
error : { } ,
2021-09-28 19:58:41 +02:00
commonPatterns : [
2021-09-28 20:40:36 +02:00
{ value : '* * * * *' , label : $translate . instant ( 'app.cron.commonPattern.everyMinute' ) } ,
{ value : '0 * * * *' , label : $translate . instant ( 'app.cron.commonPattern.everyHour' ) } ,
{ value : '*/30 * * * *' , label : $translate . instant ( 'app.cron.commonPattern.twicePerHour' ) } ,
{ value : '0 0 * * *' , label : $translate . instant ( 'app.cron.commonPattern.everyDay' ) } ,
{ value : '0 */12 * * *' , label : $translate . instant ( 'app.cron.commonPattern.twicePerDay' ) } ,
2022-05-20 10:18:11 -07:00
{ value : '0 0 * * 0' , label : $translate . instant ( 'app.cron.commonPattern.everySunday' ) } ,
{ value : '@daily' , label : $translate . instant ( 'app.cron.commonPattern.daily' ) } ,
{ value : '@hourly' , label : $translate . instant ( 'app.cron.commonPattern.hourly' ) } ,
{ value : '@service' , label : $translate . instant ( 'app.cron.commonPattern.service' ) }
2021-09-28 19:58:41 +02:00
] ,
2021-09-27 15:32:44 -07:00
crontab : '' ,
2021-09-28 10:13:40 -07:00
crontabDefault : ''
2021-09-28 19:58:41 +02:00
+ '# +------------------------ minute (0 - 59)\n'
+ '# | +------------------- hour (0 - 23)\n'
+ '# | | +-------------- day of month (1 - 31)\n'
+ '# | | | +--------- month (1 - 12)\n'
+ '# | | | | +---- day of week (0 - 6) (Sunday=0 or 7)\n'
+ '# | | | | |\n'
+ '# * * * * * command to be executed\n\n' ,
2021-09-28 10:13:40 -07:00
2021-09-27 15:32:44 -07:00
show : function ( ) {
$scope . cronForm . $setPristine ( ) ;
$scope . cron . error = { } ;
$scope . cron . crontab = $scope . app . crontab ;
2021-09-28 10:13:40 -07:00
if ( $scope . cron . crontab === null ) $scope . cron . crontab = $scope . cron . crontabDefault ; // only when null, not when ''
2021-09-27 15:32:44 -07:00
} ,
submit : function ( ) {
$scope . cron . error = { } ;
$scope . cron . busy = true ;
2021-09-27 21:42:01 -07:00
Client . configureApp ( $scope . app . id , 'crontab' , { crontab : $scope . cron . crontab } , function ( error ) {
2021-09-27 15:32:44 -07:00
if ( error && error . statusCode === 400 ) {
$scope . cron . busy = false ;
$scope . cron . error . crontab = error . message ;
$scope . cronForm . $setPristine ( ) ;
return ;
}
if ( error ) return Client . error ( error ) ;
$scope . cronForm . $setPristine ( ) ;
$timeout ( function ( ) { $scope . cron . busy = false ; } , 1000 ) ;
} ) ;
2021-09-28 19:58:41 +02:00
} ,
addCommonPattern : function ( pattern ) {
2021-10-19 11:21:09 -07:00
$scope . cron . crontab += pattern + ' /path/to/command\n' ;
2021-09-27 15:32:44 -07:00
}
} ;
2019-09-10 19:21:30 +02:00
$scope . security = {
busy : false ,
error : { } ,
success : false ,
robotsTxt : '' ,
2019-10-14 16:50:15 -07:00
csp : '' ,
2023-03-06 11:15:55 +01:00
hstsPreload : false ,
2019-09-10 19:21:30 +02:00
show : function ( ) {
2019-09-19 18:31:11 +02:00
$scope . security . error = { } ;
2019-10-14 15:20:48 -07:00
$scope . security . robotsTxt = $scope . app . reverseProxyConfig . robotsTxt || '' ;
2019-10-14 16:50:15 -07:00
$scope . security . csp = $scope . app . reverseProxyConfig . csp || '' ;
2023-03-06 11:15:55 +01:00
$scope . security . hstsPreload = $scope . app . reverseProxyConfig . hstsPreload || false ;
2019-09-10 19:21:30 +02:00
} ,
submit : function ( ) {
$scope . security . busy = true ;
$scope . security . error = { } ;
2019-10-14 15:20:48 -07:00
var reverseProxyConfig = {
2019-10-14 16:50:15 -07:00
robotsTxt : $scope . security . robotsTxt || null , // empty string resets
2023-03-06 11:15:55 +01:00
csp : $scope . security . csp || null , // empty string resets
hstsPreload : $scope . security . hstsPreload
2019-10-14 15:20:48 -07:00
} ;
Client . configureApp ( $scope . app . id , 'reverse_proxy' , reverseProxyConfig , function ( error ) {
if ( error ) return Client . error ( error ) ;
2019-09-17 14:49:26 +02:00
$timeout ( function ( ) {
$scope . security . success = true ;
$scope . security . busy = false ;
} , 1000 ) ;
2019-09-10 19:21:30 +02:00
} ) ;
}
} ;
2022-06-08 11:41:57 +02:00
$scope . proxy = {
busy : false ,
2022-09-29 19:27:22 +02:00
error : null ,
2022-06-08 11:41:57 +02:00
success : false ,
upstreamUri : '' ,
show : function ( ) {
2022-09-29 19:27:22 +02:00
$scope . proxyForm . $setPristine ( ) ;
$scope . proxy . error = null ;
2022-06-08 11:41:57 +02:00
$scope . proxy . upstreamUri = $scope . app . upstreamUri || '' ;
} ,
submit : function ( ) {
$scope . proxy . busy = true ;
2022-09-29 19:27:22 +02:00
$scope . proxy . error = null ;
2022-06-08 11:41:57 +02:00
2022-09-29 18:45:38 +02:00
var upstreamUri = $scope . proxy . upstreamUri . replace ( /\/$/ , '' ) ;
Client . configureApp ( $scope . app . id , 'upstream_uri' , { upstreamUri : upstreamUri } , function ( error ) {
2022-09-29 19:27:22 +02:00
$scope . proxy . busy = false ;
if ( error && error . statusCode === 400 ) {
$scope . proxy . error = error . message ;
$scope . proxyForm . $setPristine ( ) ;
return ;
}
2022-06-08 11:41:57 +02:00
if ( error ) return Client . error ( error ) ;
2022-09-29 19:27:22 +02:00
$scope . proxyForm . $setPristine ( ) ;
2022-06-08 11:41:57 +02:00
$timeout ( function ( ) {
$scope . proxy . success = true ;
} , 1000 ) ;
} ) ;
}
} ;
2019-09-10 19:21:30 +02:00
$scope . updates = {
busy : false ,
2019-09-17 16:16:48 +02:00
busyCheck : false ,
2019-09-17 17:14:40 +02:00
busyUpdate : false ,
2020-05-20 12:16:35 +02:00
busyAutomaticUpdates : false ,
2019-09-26 20:10:25 -07:00
skipBackup : false ,
2024-07-15 16:23:37 +02:00
enableAutomaticUpdate : true ,
2019-09-10 19:21:30 +02:00
show : function ( ) {
2021-12-14 10:21:52 +01:00
$scope . updates . skipBackup = false ;
2024-07-15 16:23:37 +02:00
$scope . updates . enableAutomaticUpdate = $scope . app . enableAutomaticUpdate ;
2019-09-10 19:21:30 +02:00
} ,
2019-09-17 16:16:48 +02:00
toggleAutomaticUpdates : function ( ) {
2020-05-20 12:16:35 +02:00
$scope . updates . busyAutomaticUpdates = true ;
2019-09-10 19:21:30 +02:00
2024-07-15 16:23:37 +02:00
Client . configureApp ( $scope . app . id , 'automatic_update' , { enable : ! $scope . updates . enableAutomaticUpdate } , function ( error ) {
2019-09-10 19:21:30 +02:00
if ( error ) return Client . error ( error ) ;
2022-10-06 16:17:25 +02:00
refreshApp ( $scope . app . id , function ( error ) {
if ( error ) console . error ( error ) ;
2024-07-15 16:23:37 +02:00
$timeout ( function ( ) {
console . log ( $scope . updates . enableAutomaticUpdate , $scope . app . enableAutomaticUpdate ) ;
$scope . updates . enableAutomaticUpdate = $scope . app . enableAutomaticUpdate ;
$scope . updates . busyAutomaticUpdates = false ;
} , 2000 ) ;
2022-10-06 16:17:25 +02:00
} ) ;
2019-09-17 16:16:48 +02:00
} ) ;
} ,
check : function ( ) {
$scope . updates . busyCheck = true ;
2021-09-21 19:55:48 -07:00
Client . checkForAppUpdates ( $scope . app . id , function ( error ) {
2019-09-17 16:16:48 +02:00
if ( error ) Client . error ( error ) ;
$scope . updates . busyCheck = false ;
2019-09-10 19:21:30 +02:00
} ) ;
2019-09-17 17:14:40 +02:00
} ,
askUpdate : function ( ) {
$scope . updates . busyUpdate = false ;
$ ( '#updateModal' ) . modal ( 'show' ) ;
} ,
confirmUpdate : function ( ) {
$scope . updates . busyUpdate = true ;
2020-12-21 12:49:21 -08:00
Client . updateApp ( $scope . app . id , $scope . config . update [ $scope . app . id ] . manifest , { skipBackup : $scope . updates . skipBackup } , function ( error ) {
2019-09-17 17:14:40 +02:00
$scope . updates . busyUpdate = false ;
if ( error ) return Client . error ( error ) ;
$ ( '#updateModal' ) . modal ( 'hide' ) ;
2019-12-16 16:08:49 -08:00
refreshApp ( $scope . app . id ) ;
2019-09-17 17:14:40 +02:00
} ) ;
2019-09-10 19:21:30 +02:00
}
} ;
2023-09-05 09:15:12 +05:30
$scope . backupDetails = {
backup : null ,
show : function ( backup ) {
$scope . backupDetails . backup = backup ;
$ ( '#backupDetailsModal' ) . modal ( 'show' ) ;
}
} ;
2019-09-10 19:21:30 +02:00
$scope . backups = {
busy : false ,
2019-09-20 00:03:52 +02:00
busyCreate : false ,
2020-05-20 12:16:35 +02:00
busyAutomaticBackups : false ,
2019-09-10 19:21:30 +02:00
error : { } ,
enableBackup : false ,
2019-09-13 17:07:45 +02:00
backups : [ ] ,
2019-09-20 00:03:52 +02:00
createBackup : function ( ) {
$scope . backups . busyCreate = true ;
Client . backupApp ( $scope . app . id , function ( error ) {
if ( error ) Client . error ( error ) ;
2019-12-16 16:17:13 -08:00
refreshApp ( $scope . app . id , function ( ) {
$scope . backups . busyCreate = false ;
waitForAppTask ( function ( error ) {
if ( error ) return Client . error ( error ) ;
$scope . backups . show ( ) ; // refresh backup listing
} ) ;
} ) ;
2019-09-20 00:03:52 +02:00
} ) ;
} ,
2019-09-10 19:21:30 +02:00
show : function ( ) {
var app = $scope . app ;
2019-09-19 18:31:11 +02:00
$scope . backups . error = { } ;
2019-09-10 19:21:30 +02:00
$scope . backups . enableBackup = app . enableBackup ;
2019-09-13 17:07:45 +02:00
Client . getAppBackups ( app . id , function ( error , backups ) {
if ( error ) return Client . error ( error ) ;
$scope . backups . backups = backups ;
2021-09-30 13:50:42 -07:00
Client . getAppEventLog ( app . id , 1 , 1 , function ( error , result ) {
if ( error ) return console . error ( 'Failed to get events:' , error ) ;
if ( result . length !== 0 && result [ 0 ] . action == 'app.backup.finish' ) {
$scope . backups . error . message = result [ 0 ] . data . errorMessage ;
}
} ) ;
2019-09-13 17:07:45 +02:00
} ) ;
2019-09-10 19:21:30 +02:00
} ,
2022-04-08 10:57:45 -07:00
refresh : function ( ) {
Client . getAppBackups ( $scope . app . id , function ( error , backups ) {
if ( error ) return Client . error ( error ) ;
$scope . backups . backups = backups ;
} ) ;
} ,
2019-09-17 16:16:48 +02:00
toggleAutomaticBackups : function ( ) {
2020-05-20 12:16:35 +02:00
$scope . backups . busyAutomaticBackups = true ;
2019-09-10 19:21:30 +02:00
$scope . backups . error = { } ;
2019-09-17 16:16:48 +02:00
Client . configureApp ( $scope . app . id , 'automatic_backup' , { enable : ! $scope . backups . enableBackup } , function ( error ) {
2019-09-10 19:21:30 +02:00
if ( error ) return Client . error ( error ) ;
2019-09-17 16:16:48 +02:00
$timeout ( function ( ) {
$scope . backups . enableBackup = ! $scope . backups . enableBackup ;
2020-05-20 12:16:35 +02:00
$scope . backups . busyAutomaticBackups = false ;
2019-09-17 16:16:48 +02:00
} , 1000 ) ;
2019-09-10 19:21:30 +02:00
} ) ;
}
} ;
2019-09-13 11:18:43 +02:00
2020-02-06 16:08:22 -08:00
$scope . s3like = function ( provider ) {
2022-09-27 19:40:58 +02:00
return provider === 's3' || provider === 'minio' || provider === 's3-v4-compat'
2024-09-25 12:21:42 +02:00
|| provider === 'exoscale-sos' || provider === 'digitalocean-spaces' || provider === 'hetzner-objectstorage'
2022-09-27 19:40:58 +02:00
|| provider === 'scaleway-objectstorage' || provider === 'wasabi' || provider === 'backblaze-b2' || provider === 'cloudflare-r2'
|| provider === 'linode-objectstorage' || provider === 'ovh-objectstorage' || provider === 'ionos-objectstorage'
2023-08-25 07:59:40 +05:30
|| provider === 'vultr-objectstorage' || provider === 'upcloud-objectstorage' || provider === 'idrive-e2'
|| provider === 'contabo-objectstorage' ;
2020-02-06 16:08:22 -08:00
} ;
2021-01-07 18:28:30 +01:00
$scope . mountlike = function ( provider ) {
2022-10-02 10:09:08 +02:00
return provider === 'sshfs' || provider === 'cifs' || provider === 'nfs' || provider === 'mountpoint' || provider === 'ext4' || provider === 'xfs' ;
2021-01-07 18:28:30 +01:00
} ;
2020-02-06 16:08:22 -08:00
$scope . importBackup = {
busy : false ,
error : { } ,
2020-05-16 11:19:47 -07:00
// variables here have to match the import config logic!
2020-02-06 16:08:22 -08:00
provider : '' ,
bucket : '' ,
prefix : '' ,
2024-06-25 13:35:28 +02:00
mountPoint : '' , // for mountpoint
2020-02-06 16:08:22 -08:00
accessKeyId : '' ,
secretAccessKey : '' ,
gcsKey : { keyFileName : '' , content : '' } ,
region : '' ,
endpoint : '' ,
acceptSelfSignedCerts : false ,
format : 'tgz' ,
2022-04-05 09:41:09 -07:00
remotePath : '' ,
2020-05-12 10:54:15 -07:00
password : '' ,
2022-06-27 09:02:44 -07:00
encryptedFilenames : true ,
2024-04-15 22:19:51 +02:00
mountOptions : {
host : '' ,
remoteDir : '' ,
username : '' ,
password : '' ,
diskPath : '' ,
user : '' ,
seal : true ,
port : 22 ,
privateKey : ''
} ,
2022-02-22 16:34:53 +01:00
encrypted : false , // helps with ng-required when backupConfig is read from file
2020-02-06 16:08:22 -08:00
2020-02-07 10:22:52 -08:00
clearForm : function ( ) {
2020-05-24 18:34:53 -07:00
// $scope.importBackup.provider = ''; // do not clear since we call this function on provider change
2020-02-07 10:22:52 -08:00
$scope . importBackup . bucket = '' ;
2021-01-07 18:28:30 +01:00
$scope . importBackup . mountPoint = '' ;
2020-02-07 10:22:52 -08:00
$scope . importBackup . accessKeyId = '' ;
$scope . importBackup . secretAccessKey = '' ;
$scope . importBackup . gcsKey . keyFileName = '' ;
$scope . importBackup . gcsKey . content = '' ;
$scope . importBackup . endpoint = '' ;
$scope . importBackup . region = '' ;
$scope . importBackup . format = 'tgz' ;
$scope . importBackup . acceptSelfSignedCerts = false ;
2020-05-12 10:54:15 -07:00
$scope . importBackup . password = '' ;
2022-06-27 09:02:44 -07:00
$scope . importBackup . encryptedFilenames = true ;
2022-04-05 09:41:09 -07:00
$scope . importBackup . remotePath = '' ;
2024-04-15 22:19:51 +02:00
$scope . importBackup . mountOptions = { host : '' , remoteDir : '' , username : '' , password : '' , diskPath : '' , seal : true , user : '' , port : 22 , privateKey : '' } ;
2020-02-07 10:22:52 -08:00
} ,
2020-02-06 16:08:22 -08:00
submit : function ( ) {
2020-02-07 10:22:52 -08:00
$scope . importBackup . error = { } ;
2020-02-06 16:08:22 -08:00
$scope . importBackup . busy = true ;
var backupConfig = {
provider : $scope . importBackup . provider ,
} ;
2022-06-27 09:02:44 -07:00
if ( $scope . importBackup . password ) {
backupConfig . password = $scope . importBackup . password ;
backupConfig . encryptedFilenames = $scope . importBackup . encryptedFilenames ;
}
2020-02-06 16:08:22 -08:00
2022-04-05 09:41:09 -07:00
var remotePath = $scope . importBackup . remotePath ;
2020-02-06 16:08:22 -08:00
// only set provider specific fields, this will clear them in the db
if ( $scope . s3like ( backupConfig . provider ) ) {
backupConfig . bucket = $scope . importBackup . bucket ;
2024-06-25 13:35:28 +02:00
backupConfig . prefix = $scope . importBackup . prefix ;
2020-02-06 16:08:22 -08:00
backupConfig . accessKeyId = $scope . importBackup . accessKeyId ;
backupConfig . secretAccessKey = $scope . importBackup . secretAccessKey ;
if ( $scope . importBackup . endpoint ) backupConfig . endpoint = $scope . importBackup . endpoint ;
if ( backupConfig . provider === 's3' ) {
if ( $scope . importBackup . region ) backupConfig . region = $scope . importBackup . region ;
delete backupConfig . endpoint ;
} else if ( backupConfig . provider === 'minio' || backupConfig . provider === 's3-v4-compat' ) {
2020-07-05 10:58:20 -07:00
backupConfig . region = backupConfig . region || 'us-east-1' ;
2020-02-06 16:08:22 -08:00
backupConfig . acceptSelfSignedCerts = $scope . importBackup . acceptSelfSignedCerts ;
2020-07-05 10:58:20 -07:00
backupConfig . s3ForcePathStyle = true ; // might want to expose this in the UI
2020-02-06 16:08:22 -08:00
} else if ( backupConfig . provider === 'exoscale-sos' ) {
backupConfig . region = 'us-east-1' ;
backupConfig . signatureVersion = 'v4' ;
} else if ( backupConfig . provider === 'wasabi' ) {
2020-05-19 14:52:40 +02:00
backupConfig . region = $scope . wasabiRegions . find ( function ( x ) { return x . value === $scope . importBackup . endpoint ; } ) . region ;
2020-02-06 16:08:22 -08:00
backupConfig . signatureVersion = 'v4' ;
} else if ( backupConfig . provider === 'scaleway-objectstorage' ) {
backupConfig . region = $scope . scalewayRegions . find ( function ( x ) { return x . value === $scope . importBackup . endpoint ; } ) . region ;
backupConfig . signatureVersion = 'v4' ;
2020-03-05 11:24:42 -08:00
} else if ( backupConfig . provider === 'linode-objectstorage' ) {
backupConfig . region = $scope . linodeRegions . find ( function ( x ) { return x . value === $scope . importBackup . endpoint ; } ) . region ;
2020-04-29 12:54:19 -07:00
backupConfig . signatureVersion = 'v4' ;
} else if ( backupConfig . provider === 'ovh-objectstorage' ) {
backupConfig . region = $scope . ovhRegions . find ( function ( x ) { return x . value === $scope . importBackup . endpoint ; } ) . region ;
2020-03-05 11:24:42 -08:00
backupConfig . signatureVersion = 'v4' ;
2021-02-04 10:14:42 -08:00
} else if ( backupConfig . provider === 'ionos-objectstorage' ) {
backupConfig . region = $scope . ionosRegions . find ( function ( x ) { return x . value === $scope . importBackup . endpoint ; } ) . region ;
backupConfig . signatureVersion = 'v4' ;
2021-06-16 22:35:46 -07:00
} else if ( backupConfig . provider === 'vultr-objectstorage' ) {
backupConfig . region = $scope . vultrRegions . find ( function ( x ) { return x . value === $scope . importBackup . endpoint ; } ) . region ;
backupConfig . signatureVersion = 'v4' ;
2023-08-25 07:59:40 +05:30
} else if ( backupConfig . provider === 'contabo-objectstorage' ) {
backupConfig . region = $scope . contaboRegions . find ( function ( x ) { return x . value === $scope . importBackup . endpoint ; } ) . region ;
backupConfig . signatureVersion = 'v4' ;
backupConfig . s3ForcePathStyle = true ; // https://docs.contabo.com/docs/products/Object-Storage/technical-description (no virtual buckets)
2021-09-27 10:01:09 -07:00
} else if ( backupConfig . provider === 'upcloud-objectstorage' ) {
var m = /^.*\.(.*)\.upcloudobjects.com$/ . exec ( backupConfig . endpoint ) ;
backupConfig . region = m ? m [ 1 ] : 'us-east-1' ; // let it fail in validation phase if m is not valid
backupConfig . signatureVersion = 'v4' ;
2020-02-06 16:08:22 -08:00
} else if ( backupConfig . provider === 'digitalocean-spaces' ) {
backupConfig . region = 'us-east-1' ;
2024-09-25 12:21:42 +02:00
} else if ( backupConfig . provider === 'hetzner-objectstorage' ) {
backupConfig . region = 'us-east-1' ;
backupConfig . signatureVersion = 'v4' ;
2020-02-06 16:08:22 -08:00
}
} else if ( backupConfig . provider === 'gcs' ) {
backupConfig . bucket = $scope . importBackup . bucket ;
2024-06-25 13:35:28 +02:00
backupConfig . prefix = $scope . importBackup . prefix ;
2020-02-06 16:08:22 -08:00
try {
var serviceAccountKey = JSON . parse ( $scope . importBackup . gcsKey . content ) ;
backupConfig . projectId = serviceAccountKey . project _id ;
backupConfig . credentials = {
client _email : serviceAccountKey . client _email ,
private _key : serviceAccountKey . private _key
} ;
if ( ! backupConfig . projectId || ! backupConfig . credentials || ! backupConfig . credentials . client _email || ! backupConfig . credentials . private _key ) {
throw 'fields_missing' ;
}
} catch ( e ) {
$scope . importBackup . error . generic = 'Cannot parse Google Service Account Key: ' + e . message ;
$scope . importBackup . error . gcsKeyInput = true ;
$scope . importBackup . busy = false ;
return ;
}
2022-10-02 10:09:08 +02:00
} else if ( backupConfig . provider === 'sshfs' || backupConfig . provider === 'cifs' || backupConfig . provider === 'nfs' || backupConfig . provider === 'ext4' || backupConfig . provider === 'xfs' ) {
2021-09-14 11:37:36 +02:00
backupConfig . mountOptions = $scope . importBackup . mountOptions ;
2024-06-25 13:35:28 +02:00
backupConfig . prefix = $scope . importBackup . prefix ;
2022-10-02 10:09:08 +02:00
} else if ( backupConfig . provider === 'mountpoint' ) {
2024-06-25 13:35:28 +02:00
backupConfig . prefix = $scope . importBackup . prefix ;
2024-04-08 18:44:44 +02:00
backupConfig . mountPoint = $scope . importBackup . mountPoint ;
2020-02-06 16:08:22 -08:00
} else if ( backupConfig . provider === 'filesystem' ) {
2022-04-05 09:41:09 -07:00
var parts = remotePath . split ( '/' ) ;
remotePath = parts . pop ( ) || parts . pop ( ) ; // removes any trailing slash. this is basename()
2020-02-06 16:08:22 -08:00
backupConfig . backupFolder = parts . join ( '/' ) ; // this is dirname()
2020-03-06 02:18:01 -08:00
}
2020-02-06 16:08:22 -08:00
2020-03-06 02:18:01 -08:00
if ( $scope . importBackup . format === 'tgz' ) {
2022-04-05 09:41:09 -07:00
if ( remotePath . substring ( remotePath . length - '.tar.gz' . length , remotePath . length ) === '.tar.gz' ) { // endsWith
remotePath = remotePath . replace ( /.tar.gz$/ , '' ) ;
} else if ( remotePath . substring ( remotePath . length - '.tar.gz.enc' . length , remotePath . length ) === '.tar.gz.enc' ) { // endsWith
remotePath = remotePath . replace ( /.tar.gz.enc$/ , '' ) ;
2020-02-06 16:08:22 -08:00
}
}
2022-04-05 09:41:09 -07:00
Client . importBackup ( $scope . app . id , remotePath , $scope . importBackup . format , backupConfig , function ( error ) {
2020-02-06 16:08:22 -08:00
if ( error ) {
2020-02-07 10:22:52 -08:00
$scope . importBackup . busy = false ;
if ( error . statusCode === 424 ) {
$scope . importBackup . error . generic = error . message ;
if ( error . message . indexOf ( 'AWS Access Key Id' ) !== - 1 ) {
$scope . importBackup . error . accessKeyId = true ;
$scope . importBackupForm . accessKeyId . $setPristine ( ) ;
$ ( '#inputImportBackupAccessKeyId' ) . focus ( ) ;
2020-02-07 11:16:14 -08:00
} else if ( error . message . indexOf ( 'not match the signature' ) !== - 1 || error . message . indexOf ( 'Signature' ) !== - 1 ) {
2020-02-07 10:22:52 -08:00
$scope . importBackup . error . secretAccessKey = true ;
$scope . importBackupForm . secretAccessKey . $setPristine ( ) ;
$ ( '#inputImportBackupSecretAccessKey' ) . focus ( ) ;
} else if ( error . message . toLowerCase ( ) === 'access denied' ) {
2020-02-07 11:16:14 -08:00
$scope . importBackup . error . accessKeyId = true ;
$scope . importBackupForm . accessKeyId . $setPristine ( ) ;
2020-02-07 10:22:52 -08:00
$ ( '#inputImportBackupBucket' ) . focus ( ) ;
} else if ( error . message . indexOf ( 'ECONNREFUSED' ) !== - 1 ) {
$scope . importBackup . error . generic = 'Unknown region' ;
$scope . importBackup . error . region = true ;
$scope . importBackupForm . region . $setPristine ( ) ;
$ ( '#inputImportBackupDORegion' ) . focus ( ) ;
} else if ( error . message . toLowerCase ( ) === 'wrong region' ) {
$scope . importBackup . error . generic = 'Wrong S3 Region' ;
$scope . importBackup . error . region = true ;
$scope . importBackupForm . region . $setPristine ( ) ;
$ ( '#inputImportBackupS3Region' ) . focus ( ) ;
} else {
$scope . importBackup . error . bucket = true ;
$ ( '#inputImportBackupBucket' ) . focus ( ) ;
$scope . importBackupForm . bucket . $setPristine ( ) ;
}
} else if ( error . statusCode === 400 ) {
$scope . importBackup . error . generic = error . message ;
if ( $scope . importBackup . provider === 'filesystem' ) {
$scope . importBackup . error . backupFolder = true ;
}
} else {
Client . error ( error ) ;
}
2020-02-06 16:08:22 -08:00
return ;
}
$ ( '#importBackupModal' ) . modal ( 'hide' ) ;
2023-09-11 15:11:53 +02:00
// clear potential post-install flag
$scope . app . pendingPostInstallConfirmation = false ;
delete localStorage [ 'confirmPostInstall_' + $scope . app . id ] ;
2020-02-07 10:22:52 -08:00
refreshApp ( $scope . app . id , function ( error ) {
if ( error ) return Client . error ( error ) ;
$timeout ( function ( ) { $scope . importBackup . busy = false ; } , 1000 ) ;
} ) ;
2020-02-06 16:08:22 -08:00
} ) ;
} ,
show : function ( ) {
2020-02-07 10:22:52 -08:00
$scope . importBackup . clearForm ( ) ;
2020-02-06 16:08:22 -08:00
$ ( '#importBackupModal' ) . modal ( 'show' ) ;
2020-05-16 11:19:47 -07:00
} ,
2020-02-06 16:08:22 -08:00
} ;
2022-04-08 10:57:45 -07:00
$scope . editBackup = {
busy : false ,
error : null ,
backup : null ,
label : '' ,
persist : false ,
show : function ( backup ) {
$scope . editBackup . backup = backup ;
$scope . editBackup . label = backup . label ;
$scope . editBackup . persist = backup . preserveSecs === - 1 ;
$scope . editBackup . error = null ;
$scope . editBackup . busy = false ;
$ ( '#editBackupModal' ) . modal ( 'show' ) ;
} ,
submit : function ( ) {
$scope . editBackup . error = null ;
$scope . editBackup . busy = true ;
Client . editAppBackup ( $scope . app . id , $scope . editBackup . backup . id , $scope . editBackup . label , $scope . editBackup . persist ? - 1 : 0 , function ( error ) {
$scope . editBackup . busy = false ;
if ( error ) return $scope . editBackup . error = error . message ;
$scope . backups . refresh ( ) ;
$ ( '#editBackupModal' ) . modal ( 'hide' ) ;
} ) ;
}
} ;
$scope . backupDetails = {
backup : null ,
show : function ( backup ) {
$scope . backupDetails . backup = backup ;
$ ( '#backupDetailsModal' ) . modal ( 'show' ) ;
}
} ;
2019-09-13 11:18:43 +02:00
$scope . uninstall = {
busy : false ,
error : { } ,
2020-11-16 14:42:02 +01:00
busyRunState : false ,
startButton : false ,
2024-12-09 18:28:35 +01:00
latestBackup : null ,
2020-11-16 14:42:02 +01:00
2021-04-13 13:22:49 +02:00
toggleRunState : function ( confirmStop ) {
if ( confirmStop && $scope . app . runState !== RSTATES . STOPPED ) {
$ ( '#stopModal' ) . modal ( 'show' ) ;
return ;
}
$ ( '#stopModal' ) . modal ( 'hide' ) ;
2020-11-16 14:42:02 +01:00
var func = $scope . app . runState === RSTATES . STOPPED ? Client . startApp : Client . stopApp ;
$scope . uninstall . busyRunState = true ;
func ( $scope . app . id , function ( error ) {
if ( error ) return Client . error ( error ) ;
refreshApp ( $scope . app . id , function ( error ) {
if ( error ) return Client . error ( error ) ;
$timeout ( function ( ) { $scope . uninstall . busyRunState = false ; } , 1000 ) ;
} ) ;
} ) ;
} ,
2019-09-13 11:18:43 +02:00
show : function ( ) {
2019-09-19 18:31:11 +02:00
$scope . uninstall . error = { } ;
2024-12-09 18:28:35 +01:00
$scope . uninstall . latestBackup = null ;
Client . getAppBackups ( $scope . app . id , function ( error , backups ) {
if ( ! error && backups . length ) $scope . uninstall . latestBackup = backups [ 0 ] ;
} ) ;
2019-09-17 15:40:04 +02:00
} ,
2024-12-09 20:53:53 +01:00
ask : function ( what ) {
if ( what === 'uninstall' ) {
$ ( '#uninstallModal' ) . modal ( 'show' ) ;
} else {
$ ( '#archiveModal' ) . modal ( 'show' ) ;
}
2024-12-09 18:28:35 +01:00
} ,
2024-12-09 20:53:53 +01:00
submit : function ( what ) {
2019-09-13 11:18:43 +02:00
$scope . uninstall . busy = true ;
2019-09-29 16:52:57 -07:00
var NOOP = function ( next ) { return next ( ) ; } ;
var stopAppTask = $scope . app . taskId ? Client . stopTask . bind ( null , $scope . app . taskId ) : NOOP ;
stopAppTask ( function ( ) { // ignore error
2024-12-09 20:53:53 +01:00
const func = what === 'uninstall' ?
Client . uninstallApp . bind ( null , $scope . app . id ) :
Client . archiveApp . bind ( Client , $scope . app . id , $scope . uninstall . latestBackup . id ) ;
func ( function ( error ) {
2019-09-29 16:52:57 -07:00
if ( error && error . statusCode === 402 ) { // unpurchase failed
Client . error ( 'Relogin to Cloudron App Store' ) ;
} else if ( error ) {
Client . error ( error ) ;
} else {
2024-12-09 20:53:53 +01:00
if ( what === 'uninstall' ) {
$ ( '#uninstallModal' ) . modal ( 'hide' ) ;
} else {
$ ( '#archiveModal' ) . modal ( 'hide' ) ;
}
2019-09-29 16:52:57 -07:00
$location . path ( '/apps' ) ;
}
2019-09-13 11:18:43 +02:00
2019-09-29 16:52:57 -07:00
$scope . uninstall . busy = false ;
} ) ;
2019-09-13 11:18:43 +02:00
} ) ;
}
} ;
2019-09-10 19:21:30 +02:00
2019-10-24 10:01:23 -07:00
$scope . restore = {
busy : false ,
error : { } ,
backup : null ,
show : function ( backup ) {
$scope . restore . error = { } ;
$scope . restore . backup = backup ;
$ ( '#restoreModal' ) . modal ( 'show' ) ;
} ,
submit : function ( ) {
$scope . restore . busy = true ;
Client . restoreApp ( $scope . app . id , $scope . restore . backup . id , function ( error ) {
2022-05-12 09:51:36 -07:00
$scope . restore . busy = false ;
2019-10-24 10:01:23 -07:00
if ( error ) {
Client . error ( error ) ;
return ;
}
$ ( '#restoreModal' ) . modal ( 'hide' ) ;
2019-12-16 16:08:49 -08:00
refreshApp ( $scope . app . id ) ;
2019-10-24 10:01:23 -07:00
} ) ;
}
} ;
2019-09-13 17:18:37 +02:00
$scope . clone = {
2019-09-24 18:50:52 +02:00
busy : false ,
2019-09-13 17:18:37 +02:00
error : { } ,
backup : null ,
2022-01-16 18:29:32 -08:00
subdomain : '' ,
2019-09-13 17:18:37 +02:00
domain : null ,
2022-02-07 22:56:34 -08:00
secondaryDomains : { } ,
2022-02-08 21:52:03 +01:00
needsOverwrite : false ,
overwriteDns : false ,
2024-07-16 22:21:36 +02:00
ports : { } ,
portsEnabled : { } ,
portInfo : { } ,
2019-09-13 17:18:37 +02:00
show : function ( backup ) {
2022-05-12 09:26:12 -07:00
var app = $scope . app ;
2019-09-13 17:18:37 +02:00
2019-09-19 18:31:11 +02:00
$scope . clone . error = { } ;
2019-09-13 17:18:37 +02:00
$scope . clone . backup = backup ;
$scope . clone . domain = $scope . domains . find ( function ( d ) { return app . domain === d . domain ; } ) ; // pre-select the app's domain
2022-02-07 22:56:34 -08:00
2022-02-08 21:52:03 +01:00
$scope . clone . needsOverwrite = false ;
$scope . clone . overwriteDns = false ;
2022-02-07 22:56:34 -08:00
$scope . clone . secondaryDomains = { } ;
2022-05-12 09:26:12 -07:00
var httpPorts = backup . manifest . httpPorts || { } ;
for ( var env2 in httpPorts ) {
$scope . clone . secondaryDomains [ env2 ] = {
subdomain : httpPorts [ env2 ] . defaultValue || '' ,
domain : $scope . clone . domain
2022-02-07 22:56:34 -08:00
} ;
2022-05-12 09:26:12 -07:00
}
2022-02-07 22:56:34 -08:00
2024-07-16 22:21:36 +02:00
$scope . clone . portInfo = angular . extend ( { } , backup . manifest . tcpPorts , backup . manifest . udpPorts ) ; // Portbinding map only for information
2019-09-13 17:18:37 +02:00
// set default ports
2024-07-16 22:21:36 +02:00
for ( var env in $scope . clone . portInfo ) {
$scope . clone . ports [ env ] = $scope . clone . portInfo [ env ] . defaultValue || 0 ;
$scope . clone . portsEnabled [ env ] = true ;
2019-09-13 17:18:37 +02:00
}
2021-08-12 09:59:56 +02:00
$ ( '#appCloneModal' ) . modal ( 'show' ) ;
2019-09-13 17:18:37 +02:00
} ,
submit : function ( ) {
$scope . clone . busy = true ;
2022-02-07 22:56:34 -08:00
var secondaryDomains = { } ;
for ( var env2 in $scope . clone . secondaryDomains ) {
secondaryDomains [ env2 ] = {
subdomain : $scope . clone . secondaryDomains [ env2 ] . subdomain ,
domain : $scope . clone . secondaryDomains [ env2 ] . domain . domain
} ;
}
2024-07-16 22:21:36 +02:00
// only use enabled ports
var finalPorts = { } ;
for ( var env in $scope . clone . ports ) {
if ( $scope . clone . portsEnabled [ env ] ) {
finalPorts [ env ] = $scope . clone . ports [ env ] ;
2019-09-13 17:18:37 +02:00
}
}
var data = {
2022-01-16 18:29:32 -08:00
subdomain : $scope . clone . subdomain ,
2019-09-13 17:18:37 +02:00
domain : $scope . clone . domain . domain ,
2022-02-07 22:56:34 -08:00
secondaryDomains : secondaryDomains ,
2024-07-16 22:21:36 +02:00
ports : finalPorts ,
2022-02-08 21:52:03 +01:00
backupId : $scope . clone . backup . id ,
overwriteDns : $scope . clone . overwriteDns
2019-09-13 17:18:37 +02:00
} ;
2022-02-08 21:52:03 +01:00
var allDomains = [ { domain : data . domain , subdomain : data . subdomain } ] . concat ( Object . keys ( secondaryDomains ) . map ( function ( k ) {
return {
domain : secondaryDomains [ k ] . domain ,
subdomain : secondaryDomains [ k ] . subdomain
} ;
} ) ) ;
async . eachSeries ( allDomains , function ( domain , callback ) {
if ( $scope . clone . overwriteDns ) return callback ( ) ;
Client . checkDNSRecords ( domain . domain , domain . subdomain , function ( error , result ) {
if ( error ) return callback ( error ) ;
var fqdn = domain . subdomain + '.' + domain . domain ;
if ( result . error ) {
if ( result . error . reason === ERROR . ACCESS _DENIED ) return callback ( { type : 'provider' , fqdn : fqdn , message : 'DNS credentials for ' + domain . domain + ' are invalid. Update it in Domains & Certs view' } ) ;
return callback ( { type : 'provider' , fqdn : fqdn , message : result . error . message } ) ;
}
if ( result . needsOverwrite ) {
$scope . clone . needsOverwrite = true ;
$scope . clone . overwriteDns = true ;
2022-02-08 22:11:41 +01:00
return callback ( { type : 'externally_exists' , fqdn : fqdn , message : 'DNS Record already exists. Confirm that the domain is not in use for services external to Cloudron' } ) ;
2022-02-08 21:52:03 +01:00
}
callback ( ) ;
} ) ;
} , function ( error ) {
2019-09-13 17:18:37 +02:00
if ( error ) {
2022-02-08 21:52:03 +01:00
if ( error . type ) {
$scope . clone . error . location = error ;
$scope . clone . busy = false ;
2019-09-13 17:18:37 +02:00
} else {
2022-02-08 21:52:03 +01:00
Client . error ( error ) ;
2019-09-13 17:18:37 +02:00
}
2022-02-08 21:52:03 +01:00
$scope . clone . error . location = error ;
2019-09-24 18:50:52 +02:00
$scope . clone . busy = false ;
2019-09-13 17:18:37 +02:00
return ;
}
2019-09-24 18:50:52 +02:00
Client . cloneApp ( $scope . app . id , data , function ( error /*, clonedApp */ ) {
$scope . clone . busy = false ;
if ( error ) {
2022-02-07 16:11:57 -08:00
var errorMessage = error . message . toLowerCase ( ) ;
2022-02-07 23:02:31 -08:00
if ( errorMessage . indexOf ( 'port' ) !== - 1 ) {
$scope . clone . error . port = error . message ;
2022-02-08 22:11:41 +01:00
} else if ( error . message . indexOf ( 'location' ) !== - 1 || error . message . indexOf ( 'subdomain' ) !== - 1 ) {
// TODO extract fqdn from error message, currently we just set it always to the main location
$scope . clone . error . location = { type : 'internally_exists' , fqdn : data . subdomain + '.' + data . domain , message : error . message } ;
2022-02-07 23:02:31 -08:00
$ ( '#cloneLocationInput' ) . focus ( ) ;
2019-09-24 18:50:52 +02:00
} else {
Client . error ( error ) ;
}
return ;
}
2021-08-12 09:59:56 +02:00
$ ( '#appCloneModal' ) . modal ( 'hide' ) ;
2019-09-13 17:18:37 +02:00
2019-09-24 18:50:52 +02:00
$location . path ( '/apps' ) ;
} ) ;
2019-09-13 17:18:37 +02:00
} ) ;
}
2019-09-23 10:16:19 -07:00
} ;
2019-09-13 17:18:37 +02:00
2019-09-21 22:45:26 +02:00
$scope . repair = {
2019-12-20 11:18:48 -08:00
retryBusy : false ,
2019-09-21 22:45:26 +02:00
error : { } ,
2022-01-16 18:29:32 -08:00
subdomain : null ,
2019-09-23 22:45:45 +02:00
domain : null ,
2022-01-14 22:32:41 -08:00
redirectDomains : [ ] ,
2021-01-18 17:55:48 -08:00
aliasDomains : [ ] ,
2019-09-21 22:45:26 +02:00
backups : [ ] ,
backupId : '' ,
2019-12-16 13:30:51 -08:00
show : function ( ) { } ,
2019-09-23 15:01:44 +02:00
// this prepares the repair dialog with whatever is required for repair action
2019-12-16 13:30:51 -08:00
confirm : function ( ) {
2019-09-21 22:45:26 +02:00
$scope . repair . error = { } ;
2019-12-20 11:18:48 -08:00
$scope . repair . retryBusy = false ;
2022-01-16 18:29:32 -08:00
$scope . repair . subdomain = null ;
2019-09-23 22:45:45 +02:00
$scope . repair . domain = null ;
2022-01-14 22:32:41 -08:00
$scope . repair . redirectDomains = [ ] ;
2021-01-18 17:55:48 -08:00
$scope . repair . aliasDomains = [ ] ;
2019-09-23 15:01:44 +02:00
$scope . repair . backupId = '' ;
2019-09-21 22:45:26 +02:00
2019-09-23 15:01:44 +02:00
var app = $scope . app ;
2019-09-21 22:45:26 +02:00
2019-12-06 10:13:30 -08:00
var errorState = ( $scope . app . error && $scope . app . error . installationState ) || ISTATES . PENDING _CONFIGURE ;
if ( errorState === ISTATES . PENDING _LOCATION _CHANGE ) {
2022-01-16 18:29:32 -08:00
$scope . repair . subdomain = app . subdomain ;
2019-09-23 22:45:45 +02:00
$scope . repair . domain = $scope . domains . filter ( function ( d ) { return d . domain === app . domain ; } ) [ 0 ] ;
2021-01-18 17:55:48 -08:00
$scope . repair . aliasDomains = $scope . app . aliasDomains ;
$scope . repair . aliasDomains = $scope . app . aliasDomains . map ( function ( aliasDomain ) {
return {
subdomain : aliasDomain . subdomain ,
enabled : true ,
domain : $scope . domains . filter ( function ( d ) { return d . domain === aliasDomain . domain ; } ) [ 0 ]
} ;
} ) ;
2022-01-14 22:32:41 -08:00
$scope . repair . redirectDomains = $scope . app . redirectDomains ;
$scope . repair . redirectDomains = $scope . app . redirectDomains . map ( function ( altDomain ) {
2019-09-27 15:34:54 -07:00
return {
subdomain : altDomain . subdomain ,
enabled : true ,
domain : $scope . domains . filter ( function ( d ) { return d . domain === altDomain . domain ; } ) [ 0 ]
} ;
2019-09-23 15:01:44 +02:00
} ) ;
2019-09-23 22:45:45 +02:00
}
2019-09-21 22:45:26 +02:00
2021-05-26 09:32:17 -07:00
if ( errorState === ISTATES . PENDING _RESTORE || errorState === ISTATES . PENDING _IMPORT ) {
2019-09-23 15:01:44 +02:00
Client . getAppBackups ( $scope . app . id , function ( error , backups ) {
if ( error ) return Client . error ( error ) ;
$scope . repair . backups = backups ;
$scope . repair . backupId = '' ;
2019-09-21 22:45:26 +02:00
2019-09-23 15:01:44 +02:00
$ ( '#repairModal' ) . modal ( 'show' ) ;
} ) ;
2019-09-23 22:45:45 +02:00
return ;
2019-09-23 15:01:44 +02:00
}
2019-09-23 22:45:45 +02:00
$ ( '#repairModal' ) . modal ( 'show' ) ;
2019-09-21 22:45:26 +02:00
} ,
submit : function ( ) {
$scope . repair . error = { } ;
2019-12-20 11:18:48 -08:00
$scope . repair . retryBusy = true ;
2019-09-21 22:45:26 +02:00
2019-12-06 10:13:30 -08:00
var errorState = ( $scope . app . error && $scope . app . error . installationState ) || ISTATES . PENDING _CONFIGURE ;
2019-09-23 22:45:45 +02:00
var data = { } ;
2019-12-06 10:13:30 -08:00
var repairFunc ;
switch ( errorState ) {
case ISTATES . PENDING _INSTALL :
case ISTATES . PENDING _CLONE : // if manifest or bad image, use CLI to provide new manifest
repairFunc = Client . repairApp . bind ( null , $scope . app . id , { } ) ; // this will trigger a re-install
break ;
case ISTATES . PENDING _LOCATION _CHANGE :
2022-01-16 18:29:32 -08:00
data . subdomain = $scope . repair . subdomain ;
2019-09-23 22:45:45 +02:00
data . domain = $scope . repair . domain . domain ;
2021-01-18 17:55:48 -08:00
data . aliasDomains = $scope . repair . aliasDomains . filter ( function ( a ) { return a . enabled ; } )
. map ( function ( d ) { return { subdomain : d . subdomain , domain : d . domain . domain } ; } ) ;
2022-01-14 22:32:41 -08:00
data . redirectDomains = $scope . repair . redirectDomains . filter ( function ( a ) { return a . enabled ; } )
2019-09-27 15:39:11 -07:00
. map ( function ( d ) { return { subdomain : d . subdomain , domain : d . domain . domain } ; } ) ;
data . overwriteDns = true ; // always overwriteDns. user can anyway check and uncheck above
2019-12-06 10:13:30 -08:00
repairFunc = Client . configureApp . bind ( null , $scope . app . id , 'location' , data ) ;
break ;
case ISTATES . PENDING _DATA _DIR _MIGRATION :
2022-06-08 10:48:52 -07:00
repairFunc = Client . configureApp . bind ( null , $scope . app . id , 'storage' , { storageVolumeId : null , storageVolumePrefix : null } ) ;
2019-12-06 10:13:30 -08:00
break ;
// this also happens for import faliures. this UI can only show backup listing. use CLI for arbit id/config
case ISTATES . PENDING _RESTORE :
2021-05-26 09:32:17 -07:00
case ISTATES . PENDING _IMPORT :
2021-04-05 10:22:43 -07:00
if ( $scope . repair . backups . length === 0 ) { // this can happen when you give some invalid backup via CLI and restore via UI
repairFunc = Client . repairApp . bind ( null , $scope . app . id , { } ) ; // this will trigger a re-install
} else {
repairFunc = Client . restoreApp . bind ( null , $scope . app . id , $scope . repair . backupId ) ;
}
2019-12-06 10:13:30 -08:00
break ;
case ISTATES . PENDING _UNINSTALL :
repairFunc = Client . uninstallApp . bind ( null , $scope . app . id ) ;
break ;
case ISTATES . PENDING _START :
case ISTATES . PENDING _STOP :
2019-12-20 11:18:48 -08:00
case ISTATES . PENDING _RESTART :
2019-12-06 10:13:30 -08:00
case ISTATES . PENDING _RESIZE :
case ISTATES . PENDING _DEBUG :
case ISTATES . PENDING _RECREATE _CONTAINER :
case ISTATES . PENDING _CONFIGURE :
2020-02-11 21:27:16 -08:00
case ISTATES . PENDING _BACKUP : // can happen if the backup task was killed/rebooted
2019-12-06 10:13:30 -08:00
case ISTATES . PENDING _UPDATE : // when update failed, just bring it back to current state and user can click update again
default :
repairFunc = Client . repairApp . bind ( null , $scope . app . id , { } ) ;
break ;
2019-09-23 22:45:45 +02:00
}
2019-12-06 10:13:30 -08:00
repairFunc ( function ( error ) {
2020-03-31 17:45:34 -07:00
$scope . repair . retryBusy = false ;
2019-09-21 22:45:26 +02:00
if ( error ) return Client . error ( error ) ;
2019-12-20 11:18:48 -08:00
$scope . repair . retryBusy = false ;
2019-09-21 22:45:26 +02:00
$ ( '#repairModal' ) . modal ( 'hide' ) ;
} ) ;
2019-12-16 13:30:51 -08:00
} ,
2019-12-20 11:18:48 -08:00
restartBusy : false ,
restartApp : function ( ) {
$scope . repair . restartBusy = true ;
Client . restartApp ( $scope . app . id , function ( error ) {
if ( error ) return console . error ( error ) ;
2019-12-20 19:09:17 -08:00
refreshApp ( $scope . app . id , function ( error ) {
if ( error ) return Client . error ( error ) ;
2019-12-20 11:18:48 -08:00
2019-12-20 19:09:17 -08:00
$timeout ( function ( ) { $scope . repair . restartBusy = false ; } , 1000 ) ;
2019-12-20 11:18:48 -08:00
} ) ;
} ) ;
} ,
2019-12-16 18:18:22 -08:00
pauseBusy : false ,
pauseAppBegin : function ( ) {
$scope . repair . pauseBusy = true ;
Client . debugApp ( $scope . app . id , true , function ( error ) {
if ( error ) return console . error ( error ) ;
2019-12-20 19:09:17 -08:00
refreshApp ( $scope . app . id , function ( error ) {
if ( error ) return Client . error ( error ) ;
2019-12-16 18:18:22 -08:00
2019-12-20 19:09:17 -08:00
$timeout ( function ( ) { $scope . repair . pauseBusy = false ; } , 1000 ) ;
2019-12-16 18:18:22 -08:00
} ) ;
} ) ;
} ,
pauseAppDone : function ( ) {
$scope . repair . pauseBusy = true ;
Client . debugApp ( $scope . app . id , false , function ( error ) {
if ( error ) return console . error ( error ) ;
2019-12-20 19:09:17 -08:00
refreshApp ( $scope . app . id , function ( error ) {
if ( error ) return Client . error ( error ) ;
2019-12-16 18:18:22 -08:00
2019-12-20 19:09:17 -08:00
$timeout ( function ( ) { $scope . repair . pauseBusy = false ; } , 1000 ) ;
2019-12-16 18:18:22 -08:00
} ) ;
} ) ;
}
2019-09-23 10:16:19 -07:00
} ;
2019-09-21 22:45:26 +02:00
2019-09-10 19:21:30 +02:00
function fetchUsers ( callback ) {
2022-02-14 14:55:04 +01:00
Client . getAllUsers ( function ( error , users ) {
2019-09-10 19:21:30 +02:00
if ( error ) return callback ( error ) ;
$scope . users = users ;
callback ( ) ;
} ) ;
}
function fetchGroups ( callback ) {
Client . getGroups ( function ( error , groups ) {
if ( error ) return callback ( error ) ;
$scope . groups = groups ;
callback ( ) ;
} ) ;
}
2022-11-15 14:54:07 +01:00
function fetchDiskUsage ( callback ) {
2022-11-29 12:36:15 +01:00
$scope . diskUsage = - 1 ;
2022-11-29 12:26:33 +01:00
$scope . diskUsageDate = 0 ;
2022-11-15 14:54:07 +01:00
Client . diskUsage ( function ( error , result ) {
if ( error ) return callback ( error ) ;
2022-11-29 12:26:33 +01:00
if ( ! result . usage ) return callback ( ) ; // no usage date yet
2022-11-15 14:54:07 +01:00
$scope . diskUsageDate = result . usage . ts ;
for ( var diskName in result . usage . disks ) {
var disk = result . usage . disks [ diskName ] ;
var content = disk . contents . find ( function ( c ) { return c . id === appId ; } ) ;
if ( content ) {
$scope . diskUsage = content . usage ;
break ;
}
}
callback ( ) ;
} ) ;
}
2019-09-10 19:21:30 +02:00
function getDomains ( callback ) {
Client . getDomains ( function ( error , result ) {
if ( error ) return callback ( error ) ;
$scope . domains = result ;
callback ( ) ;
} ) ;
}
2020-10-28 22:11:05 -07:00
function getVolumes ( callback ) {
Client . getVolumes ( function ( error , result ) {
if ( error ) return callback ( error ) ;
$scope . volumes = result ;
callback ( ) ;
} ) ;
}
2019-09-10 19:21:30 +02:00
function getBackupConfig ( callback ) {
Client . getBackupConfig ( function ( error , backupConfig ) {
if ( error ) return callback ( error ) ;
2020-05-16 11:19:47 -07:00
$scope . backupConfig = backupConfig ;
2019-09-10 19:21:30 +02:00
callback ( ) ;
} ) ;
}
2019-12-16 16:08:49 -08:00
function refreshApp ( appId , callback ) {
2019-09-20 00:51:16 +02:00
callback = callback || function ( ) { } ;
2019-09-18 15:53:57 +02:00
2020-06-14 13:35:28 +02:00
Client . getAppWithTask ( appId , function ( error , app ) {
2019-10-01 20:04:28 +02:00
if ( error && error . statusCode === 404 ) return $location . path ( '/apps' ) ;
2019-09-12 16:28:21 +02:00
if ( error ) return callback ( error ) ;
2019-09-24 21:27:49 +02:00
$scope . app = app ;
2019-12-16 16:27:24 -08:00
// show 'Start App' if app is starting or is stopped
if ( app . installationState === ISTATES . PENDING _START || app . installationState === ISTATES . PENDING _STOP ) {
2020-11-16 14:42:02 +01:00
$scope . uninstall . startButton = app . installationState === ISTATES . PENDING _START ;
2019-12-16 16:27:24 -08:00
} else {
2020-11-16 14:42:02 +01:00
$scope . uninstall . startButton = app . runState === RSTATES . STOPPED ;
2019-12-16 16:27:24 -08:00
}
2020-06-08 17:53:29 -07:00
callback ( ) ;
2019-09-12 16:28:21 +02:00
} ) ;
}
2019-09-24 21:08:42 +02:00
function waitForAppTask ( callback ) {
2019-09-20 00:51:16 +02:00
callback = callback || function ( ) { } ;
2019-09-17 16:16:48 +02:00
2019-09-20 00:03:52 +02:00
if ( ! $scope . app . taskId ) return callback ( ) ;
2019-09-24 21:08:42 +02:00
// app will be refreshed on interval
$timeout ( waitForAppTask . bind ( null , callback ) , 2000 ) ; // not yet done
2019-09-11 21:24:25 +02:00
}
2020-05-16 11:19:47 -07:00
// https://stackoverflow.com/questions/3665115/how-to-create-a-file-in-memory-for-user-to-download-but-not-through-server#18197341
function download ( filename , text ) {
var element = document . createElement ( 'a' ) ;
element . setAttribute ( 'href' , 'data:text/plain;charset=utf-8,' + encodeURIComponent ( text ) ) ;
element . setAttribute ( 'download' , filename ) ;
element . style . display = 'none' ;
document . body . appendChild ( element ) ;
element . click ( ) ;
document . body . removeChild ( element ) ;
}
$scope . downloadConfig = function ( backup ) {
// secrets and tokens already come with placeholder characters we remove them
var tmp = {
2022-04-05 09:41:09 -07:00
remotePath : backup . remotePath ,
2020-05-16 11:19:47 -07:00
encrypted : ! ! $scope . backupConfig . password // we add this just to help the import UI
} ;
Object . keys ( $scope . backupConfig ) . forEach ( function ( k ) {
2023-12-07 13:38:38 +01:00
var v = $scope . backupConfig [ k ] ;
if ( v && typeof v === 'object' ) { // to hide mountOptions.password and the likes
tmp [ k ] = { } ;
Object . keys ( v ) . forEach ( function ( j ) {
if ( v [ j ] !== SECRET _PLACEHOLDER ) tmp [ k ] [ j ] = v [ j ] ;
} ) ;
} else {
if ( $scope . backupConfig [ k ] !== SECRET _PLACEHOLDER ) tmp [ k ] = v ;
}
2020-05-16 11:19:47 -07:00
} ) ;
2024-12-11 10:54:51 +01:00
const filename = ` ${ $scope . app . fqdn } -backup-config- ${ ( new Date ( backup . creationTime ) ) . toISOString ( ) . split ( 'T' ) [ 0 ] } .json ` ;
2022-02-22 16:28:14 +01:00
download ( filename , JSON . stringify ( tmp , null , 4 ) ) ;
2020-05-16 11:19:47 -07:00
} ;
document . getElementById ( 'backupConfigFileInput' ) . onchange = function ( event ) {
var reader = new FileReader ( ) ;
reader . onload = function ( result ) {
if ( ! result . target || ! result . target . result ) return console . error ( 'Unable to read backup config' ) ;
var backupConfig ;
try {
backupConfig = JSON . parse ( result . target . result ) ;
2024-06-25 13:35:28 +02:00
if ( backupConfig . provider === 'filesystem' ) { // this allows a user to upload a backup to server and import easily with an absolute path
backupConfig . remotePath = backupConfig . backupFolder + '/' + backupConfig . remotePath ;
2020-09-21 21:58:11 -07:00
delete backupConfig . backupFolder ;
}
2020-05-16 11:19:47 -07:00
} catch ( e ) {
2024-07-16 10:32:37 +02:00
console . error ( 'Unable to parse backup config' , e ) ;
2020-05-16 11:19:47 -07:00
return ;
}
$scope . $apply ( function ( ) {
// we assume property names match here, this does not yet work for gcs keys
Object . keys ( backupConfig ) . forEach ( function ( k ) {
2020-09-21 21:58:11 -07:00
if ( k in $scope . importBackup ) {
$scope . importBackup [ k ] = backupConfig [ k ] ;
}
2020-05-16 11:19:47 -07:00
} ) ;
} ) ;
} ;
reader . readAsText ( event . target . files [ 0 ] ) ;
} ;
2019-09-10 19:21:30 +02:00
Client . onReady ( function ( ) {
2019-12-16 16:22:29 -08:00
refreshApp ( appId , function ( error ) {
2019-09-10 19:21:30 +02:00
if ( error ) return Client . error ( error ) ;
2021-09-21 15:26:05 -07:00
if ( $scope . app . accessLevel !== 'admin' && $scope . app . accessLevel !== 'operator' ) return $location . path ( '/' ) ;
2019-12-20 17:05:45 -08:00
// skipViewShow because we don't have all the values like domains/users to init the view yet
if ( $routeParams . view ) { // explicit route in url bar
$scope . setView ( $routeParams . view , true /* skipViewShow */ ) ;
} else { // default
2024-04-11 13:45:34 +02:00
$scope . setView ( $scope . app . error ? 'repair' : 'info' , true /* skipViewShow */ ) ;
2019-12-20 17:05:45 -08:00
}
2019-09-18 17:45:13 +02:00
2021-09-21 15:26:05 -07:00
function done ( ) {
$scope [ $scope . view ] . show ( ) ; // initialize now that we have all the values
var refreshTimer = $interval ( function ( ) { refreshApp ( $scope . app . id ) ; } , 5000 ) ; // call with inline function to avoid iteration argument passed see $interval docs
$scope . $on ( '$destroy' , function ( ) {
$interval . cancel ( refreshTimer ) ;
} ) ;
}
if ( $scope . app . accessLevel !== 'admin' ) return done ( ) ;
2020-06-03 23:08:05 +02:00
async . series ( [
2019-09-10 19:21:30 +02:00
fetchUsers ,
fetchGroups ,
2022-11-15 14:54:07 +01:00
fetchDiskUsage ,
2019-09-10 19:21:30 +02:00
getDomains ,
2020-10-28 22:11:05 -07:00
getVolumes ,
2019-09-10 19:21:30 +02:00
getBackupConfig
] , function ( error ) {
if ( error ) return Client . error ( error ) ;
2022-06-23 12:59:14 -07:00
// check for updates, if the app has a pending update. this handles two cases:
// 1. user got a valid subscription. this will make the updates get the manifest field
// 2. user has not refreshed the ui in a while or updated via cli tool. this will ensure we are not holding to a dangling update
if ( $scope . config . update [ $scope . app . id ] ) Client . checkForUpdates ( ) ;
2021-09-21 15:26:05 -07:00
done ( ) ;
2019-09-10 19:21:30 +02:00
} ) ;
} ) ;
} ) ;
2019-09-12 16:28:21 +02:00
$ ( '#iconFileInput' ) . get ( 0 ) . onchange = function ( event ) {
var fr = new FileReader ( ) ;
fr . onload = function ( ) {
$scope . $apply ( function ( ) {
// var file = event.target.files[0];
$scope . display . icon . data = fr . result ;
} ) ;
} ;
fr . readAsDataURL ( event . target . files [ 0 ] ) ;
} ;
2019-09-10 19:21:30 +02:00
// setup all the dialog focus handling
2022-04-08 10:57:45 -07:00
[ 'appUninstallModal' , 'appUpdateModal' , 'appRestoreModal' , 'appCloneModal' , 'editBackupModal' ] . forEach ( function ( id ) {
2019-09-10 19:21:30 +02:00
$ ( '#' + id ) . on ( 'shown.bs.modal' , function ( ) {
2019-09-23 10:16:19 -07:00
$ ( this ) . find ( '[autofocus]:first' ) . focus ( ) ;
2019-09-10 19:21:30 +02:00
} ) ;
} ) ;
2020-05-25 21:47:58 +02:00
var clipboard = new Clipboard ( '.clipboard' ) ;
clipboard . on ( 'success' , function ( ) {
$scope . $apply ( function ( ) { $scope . copyBackupIdDone = true ; } ) ;
$timeout ( function ( ) { $scope . copyBackupIdDone = false ; } , 5000 ) ;
} ) ;
2019-09-10 19:21:30 +02:00
$ ( '.modal-backdrop' ) . remove ( ) ;
} ] ) ;