2018-01-22 13:01:38 -08:00
'use strict' ;
/* global angular:false */
/* global showdown:false */
2018-05-25 13:56:15 +02:00
/* global moment:false */
2019-01-22 10:54:03 +01:00
/* global $:false */
2018-01-22 13:01:38 -08:00
// deal with accessToken in the query, this is passed for example on password reset and account setup upon invite
var search = decodeURIComponent ( window . location . search ) . slice ( 1 ) . split ( '&' ) . map ( function ( item ) { return item . split ( '=' ) ; } ) . reduce ( function ( o , k ) { o [ k [ 0 ] ] = k [ 1 ] ; return o ; } , { } ) ;
if ( search . accessToken ) {
localStorage . token = search . accessToken ;
// strip the accessToken and expiresAt, then preserve the rest
delete search . accessToken ;
delete search . expiresAt ;
// this will reload the page as this is not a hash change
window . location . search = encodeURIComponent ( Object . keys ( search ) . map ( function ( key ) { return key + '=' + search [ key ] ; } ) . join ( '&' ) ) ;
}
2018-04-02 11:35:02 +02:00
// poor man's async in the global namespace
2019-01-22 10:54:03 +01:00
function asyncForEachParallel ( items , handler , callback ) {
2019-01-16 14:02:08 +01:00
var alreadyDone = 0 ;
var errored = false ;
if ( items . length === 0 ) return callback ( ) ;
function done ( error ) {
// do nothing if already called back due to error
if ( errored ) return ;
if ( error ) {
errored = true ;
return callback ( error ) ;
}
++ alreadyDone ;
// we are done
if ( alreadyDone === items . length ) callback ( ) ;
}
for ( var i = 0 ; i < items . length ; ++ i ) {
handler ( items [ i ] , done ) ;
}
}
2018-04-02 11:35:02 +02:00
function asyncForEach ( items , handler , callback ) {
var cur = 0 ;
if ( items . length === 0 ) return callback ( ) ;
( function iterator ( ) {
handler ( items [ cur ] , function ( error ) {
if ( error ) return callback ( error ) ;
if ( cur >= items . length - 1 ) return callback ( ) ;
++ cur ;
iterator ( ) ;
} ) ;
} ) ( ) ;
}
2018-01-22 13:01:38 -08:00
2018-08-17 16:01:40 -07:00
function asyncSeries ( funcs , callback ) {
var cur = 0 ;
if ( funcs . length === 0 ) return callback ( ) ;
( function iterator ( ) {
funcs [ cur ] ( function ( error ) {
if ( error ) return callback ( error ) ;
if ( cur >= funcs . length - 1 ) return callback ( ) ;
++ cur ;
iterator ( ) ;
} ) ;
} ) ( ) ;
}
2018-01-22 13:01:38 -08:00
// create main application module
var app = angular . module ( 'Application' , [ 'ngFitText' , 'ngRoute' , 'ngAnimate' , 'ngSanitize' , 'angular-md5' , 'base64' , 'slick' , 'ui-notification' , 'ui.bootstrap' , 'ui.bootstrap-slider' , 'ngTld' , 'ui.multiselect' ] ) ;
app . config ( [ 'NotificationProvider' , function ( NotificationProvider ) {
NotificationProvider . setOptions ( {
delay : 5000 ,
startTop : 60 ,
positionX : 'left' ,
templateUrl : 'notification.html'
} ) ;
} ] ) ;
2019-07-31 08:08:46 +02:00
// configure resourceUrlWhitelist https://code.angularjs.org/1.5.8/docs/api/ng/provider/$sceDelegateProvider#resourceUrlWhitelist
app . config ( function ( $sceDelegateProvider ) {
$sceDelegateProvider . resourceUrlWhitelist ( [
// Allow same origin resource loads.
'self' ,
// Allow loading from our assets domain. Notice the difference between * and **.
'https://*cloudron.io/**'
] ) ;
} ) ;
2018-01-22 13:01:38 -08:00
// setup all major application routes
app . config ( [ '$routeProvider' , function ( $routeProvider ) {
$routeProvider . when ( '/' , {
redirectTo : '/apps'
} ) . when ( '/users' , {
controller : 'UsersController' ,
2018-04-06 00:11:08 +02:00
templateUrl : 'views/users.html?<%= revision %>'
2018-01-22 13:01:38 -08:00
} ) . when ( '/appstore' , {
controller : 'AppStoreController' ,
2018-04-06 00:11:08 +02:00
templateUrl : 'views/appstore.html?<%= revision %>'
2018-01-22 13:01:38 -08:00
} ) . when ( '/appstore/:appId' , {
controller : 'AppStoreController' ,
2018-04-06 00:11:08 +02:00
templateUrl : 'views/appstore.html?<%= revision %>'
2018-01-22 13:01:38 -08:00
} ) . when ( '/apps' , {
controller : 'AppsController' ,
2018-04-06 00:11:08 +02:00
templateUrl : 'views/apps.html?<%= revision %>'
2018-01-22 13:01:38 -08:00
} ) . when ( '/account' , {
controller : 'AccountController' ,
2018-04-06 00:11:08 +02:00
templateUrl : 'views/account.html?<%= revision %>'
2018-06-07 14:22:48 +02:00
} ) . when ( '/backups' , {
controller : 'BackupsController' ,
templateUrl : 'views/backups.html?<%= revision %>'
2018-01-22 13:01:38 -08:00
} ) . when ( '/graphs' , {
controller : 'GraphsController' ,
2018-04-06 00:11:08 +02:00
templateUrl : 'views/graphs.html?<%= revision %>'
2018-01-22 13:01:38 -08:00
} ) . when ( '/domains' , {
controller : 'DomainsController' ,
2018-04-06 00:11:08 +02:00
templateUrl : 'views/domains.html?<%= revision %>'
2018-01-22 13:01:38 -08:00
} ) . when ( '/email' , {
controller : 'EmailController' ,
2018-04-06 00:11:08 +02:00
templateUrl : 'views/email.html?<%= revision %>'
2018-04-09 18:00:08 +02:00
} ) . when ( '/email/:domain' , {
controller : 'EmailController' ,
templateUrl : 'views/email.html?<%= revision %>'
2019-01-09 15:18:10 +01:00
} ) . when ( '/notifications' , {
controller : 'NotificationsController' ,
templateUrl : 'views/notifications.html?<%= revision %>'
2018-01-22 13:01:38 -08:00
} ) . when ( '/settings' , {
controller : 'SettingsController' ,
2018-04-06 00:11:08 +02:00
templateUrl : 'views/settings.html?<%= revision %>'
2018-01-22 13:01:38 -08:00
} ) . when ( '/activity' , {
controller : 'ActivityController' ,
2018-04-06 00:11:08 +02:00
templateUrl : 'views/activity.html?<%= revision %>'
2018-01-22 13:01:38 -08:00
} ) . when ( '/support' , {
controller : 'SupportController' ,
2018-04-06 00:11:08 +02:00
templateUrl : 'views/support.html?<%= revision %>'
2018-11-26 08:59:04 +01:00
} ) . when ( '/system' , {
controller : 'SystemController' ,
templateUrl : 'views/system.html?<%= revision %>'
2018-01-22 13:01:38 -08:00
} ) . when ( '/tokens' , {
controller : 'TokensController' ,
2018-04-06 00:11:08 +02:00
templateUrl : 'views/tokens.html?<%= revision %>'
2018-01-22 13:01:38 -08:00
} ) . otherwise ( { redirectTo : '/' } ) ;
} ] ) ;
// keep in sync with appdb.js
var ISTATES = {
PENDING _INSTALL : 'pending_install' ,
PENDING _CLONE : 'pending_clone' ,
PENDING _CONFIGURE : 'pending_configure' ,
PENDING _UNINSTALL : 'pending_uninstall' ,
PENDING _RESTORE : 'pending_restore' ,
PENDING _UPDATE : 'pending_update' ,
PENDING _FORCE _UPDATE : 'pending_force_update' ,
PENDING _BACKUP : 'pending_backup' ,
ERROR : 'error' ,
INSTALLED : 'installed'
} ;
var HSTATES = {
HEALTHY : 'healthy' ,
UNHEALTHY : 'unhealthy' ,
ERROR : 'error' ,
DEAD : 'dead'
} ;
app . filter ( 'installError' , function ( ) {
return function ( app ) {
if ( app . installationState === ISTATES . ERROR ) return true ;
if ( app . installationState === ISTATES . INSTALLED ) {
// app.health can also be null to indicate insufficient data
if ( app . health === HSTATES . UNHEALTHY || app . health === HSTATES . ERROR || app . health === HSTATES . DEAD ) return true ;
}
return false ;
} ;
} ) ;
app . filter ( 'installSuccess' , function ( ) {
return function ( app ) {
return app . installationState === ISTATES . INSTALLED ;
} ;
} ) ;
2018-06-15 13:35:46 +02:00
app . filter ( 'appIsInstalledAndHealthy' , function ( ) {
return function ( app ) {
return ( app . installationState === ISTATES . INSTALLED && app . health === HSTATES . HEALTHY ) ;
}
} ) ;
2018-01-22 13:01:38 -08:00
app . filter ( 'activeOAuthClients' , function ( ) {
return function ( clients , user ) {
2018-08-03 10:09:04 -07:00
return clients . filter ( function ( c ) { return user . admin || ( c . activeTokens && c . activeTokens . length > 0 ) ; } ) ;
2018-01-22 13:01:38 -08:00
} ;
} ) ;
2019-07-31 11:38:28 -07:00
// this appears when an item in app grid is clicked
2018-01-22 13:01:38 -08:00
app . filter ( 'prettyAppMessage' , function ( ) {
return function ( message ) {
if ( message === 'ETRYAGAIN' ) return 'The DNS record for this location is not setup correctly. Please verify your DNS settings and repair this app.' ;
2019-02-08 14:54:24 -08:00
if ( message === 'DNS Record already exists' ) return 'The DNS record for this location already exists. Cloudron does not remove existing DNS records. Manually remove the DNS record and then click on repair.' ;
2018-01-22 13:01:38 -08:00
return message ;
} ;
} ) ;
2019-07-31 11:38:28 -07:00
// this appears as tool tip in app grid
2018-01-22 13:01:38 -08:00
app . filter ( 'shortAppMessage' , function ( ) {
return function ( message ) {
if ( message === 'ETRYAGAIN' ) return 'DNS record not setup correctly' ;
return message ;
} ;
} ) ;
2019-04-15 14:31:12 +02:00
app . filter ( 'selectedTagFilter' , function ( ) {
return function selectedTagFilter ( apps , selectedTags ) {
return apps . filter ( function ( app ) {
if ( selectedTags . length === 0 ) return true ;
2019-05-17 13:01:23 -07:00
if ( ! app . tags ) return false ;
2019-04-15 14:31:12 +02:00
2019-05-17 13:01:23 -07:00
for ( var i = 0 ; i < selectedTags . length ; i ++ ) {
if ( app . tags . indexOf ( selectedTags [ i ] ) === - 1 ) return false ;
}
return true ;
2019-04-15 14:31:12 +02:00
} ) ;
} ;
} ) ;
app . filter ( 'selectedDomainFilter' , function ( ) {
2019-05-20 23:40:02 +02:00
return function selectedDomainFilter ( apps , selectedDomain ) {
2019-04-15 14:31:12 +02:00
return apps . filter ( function ( app ) {
2019-05-20 23:40:02 +02:00
if ( ! selectedDomain ) return true ;
if ( selectedDomain . _alldomains ) return true ; // magic domain for single select, see apps.js ALL_DOMAINS_DOMAIN
2019-04-15 14:31:12 +02:00
2019-05-20 23:40:02 +02:00
if ( selectedDomain . domain === app . domain ) return true ;
return ! ! app . alternateDomains . find ( function ( ad ) { return ad . domain === selectedDomain . domain ; } ) ;
2019-04-15 14:31:12 +02:00
} ) ;
} ;
} ) ;
app . filter ( 'prettyDomains' , function ( ) {
return function prettyDomains ( domains ) {
return domains . map ( function ( d ) { return d . domain ; } ) . join ( ', ' ) ;
} ;
} ) ;
2018-01-22 13:01:38 -08:00
app . filter ( 'prettyMemory' , function ( ) {
return function ( memory ) {
// Adjust the default memory limit if it changes
return memory ? Math . floor ( memory / 1024 / 1024 ) : 256 ;
} ;
} ) ;
app . filter ( 'installationActive' , function ( ) {
return function ( app ) {
if ( app . installationState === ISTATES . ERROR ) return false ;
if ( app . installationState === ISTATES . INSTALLED ) return false ;
return true ;
} ;
} ) ;
2019-07-31 11:38:28 -07:00
// this appears in the app grid
2018-01-22 13:01:38 -08:00
app . filter ( 'installationStateLabel' , function ( ) {
// for better DNS errors
function detailedError ( app ) {
if ( app . installationProgress === 'ETRYAGAIN' ) return 'DNS Error' ;
return 'Error' ;
}
return function ( app ) {
var waiting = app . progress === 0 ? ' (Pending)' : '' ;
switch ( app . installationState ) {
case ISTATES . PENDING _INSTALL :
case ISTATES . PENDING _CLONE :
return 'Installing' + waiting ;
case ISTATES . PENDING _CONFIGURE : return 'Configuring' + waiting ;
case ISTATES . PENDING _UNINSTALL : return 'Uninstalling' + waiting ;
case ISTATES . PENDING _RESTORE : return 'Restoring' + waiting ;
case ISTATES . PENDING _UPDATE : return 'Updating' + waiting ;
case ISTATES . PENDING _FORCE _UPDATE : return 'Updating' + waiting ;
case ISTATES . PENDING _BACKUP : return 'Backing up' + waiting ;
case ISTATES . ERROR : return detailedError ( app ) ;
case ISTATES . INSTALLED : {
2018-05-15 12:07:57 -07:00
if ( app . debugMode ) {
return app . debugMode . readonlyRootfs ? 'Paused (Repair)' : 'Paused (Debug)' ;
} else if ( app . runState === 'running' ) {
2018-01-22 13:01:38 -08:00
if ( ! app . health ) return 'Starting...' ; // no data yet
if ( app . health === HSTATES . HEALTHY ) return 'Running' ;
return 'Not responding' ; // dead/exit/unhealthy
} else if ( app . runState === 'pending_start' ) return 'Starting...' ;
else if ( app . runState === 'pending_stop' ) return 'Stopping...' ;
else if ( app . runState === 'stopped' ) return 'Stopped' ;
else return app . runState ;
}
default : return app . installationState ;
}
} ;
} ) ;
app . filter ( 'readyToUpdate' , function ( ) {
return function ( apps ) {
return apps . every ( function ( app ) {
return ( app . installationState === ISTATES . ERROR ) || ( app . installationState === ISTATES . INSTALLED ) ;
} ) ;
} ;
} ) ;
app . filter ( 'inProgressApps' , function ( ) {
return function ( apps ) {
return apps . filter ( function ( app ) {
return app . installationState !== ISTATES . ERROR && app . installationState !== ISTATES . INSTALLED ;
} ) ;
} ;
} ) ;
app . filter ( 'applicationLink' , function ( ) {
return function ( app ) {
2018-06-14 15:46:55 +02:00
if ( app . installationState === ISTATES . INSTALLED && app . health === HSTATES . HEALTHY && ! app . pendingPostInstallConfirmation ) {
2018-01-22 13:01:38 -08:00
return 'https://' + app . fqdn ;
} else {
return '' ;
}
} ;
} ) ;
app . filter ( 'prettyHref' , function ( ) {
return function ( input ) {
if ( ! input ) return input ;
if ( input . indexOf ( 'http://' ) === 0 ) return input . slice ( 'http://' . length ) ;
if ( input . indexOf ( 'https://' ) === 0 ) return input . slice ( 'https://' . length ) ;
return input ;
} ;
} ) ;
app . filter ( 'prettyDate' , function ( ) {
// http://ejohn.org/files/pretty.js
return function prettyDate ( time ) {
var date = new Date ( time ) ,
diff = ( ( ( new Date ( ) ) . getTime ( ) - date . getTime ( ) ) / 1000 ) + 30 , // add 30seconds for clock skew
day _diff = Math . floor ( diff / 86400 ) ;
if ( isNaN ( day _diff ) || day _diff < 0 )
return 'just now' ;
return day _diff === 0 && (
diff < 60 && 'just now' ||
diff < 120 && '1 minute ago' ||
diff < 3600 && Math . floor ( diff / 60 ) + ' minutes ago' ||
diff < 7200 && '1 hour ago' ||
diff < 86400 && Math . floor ( diff / 3600 ) + ' hours ago' ) ||
day _diff === 1 && 'Yesterday' ||
day _diff < 7 && day _diff + ' days ago' ||
day _diff < 31 && Math . ceil ( day _diff / 7 ) + ' weeks ago' ||
day _diff < 365 && Math . round ( day _diff / 30 ) + ' months ago' ||
Math . round ( day _diff / 365 ) + ' years ago' ;
} ;
} ) ;
2018-03-02 18:58:49 +01:00
app . filter ( 'prettyLongDate' , function ( ) {
return function prettyLongDate ( time ) {
return moment ( time ) . format ( 'MMMM Do YYYY, h:mm:ss a' ) ;
} ;
} ) ;
2019-03-06 15:54:21 +01:00
app . filter ( 'prettyShortDate' , function ( ) {
return function prettyShortDate ( time ) {
return moment ( time ) . format ( 'MMMM Do YYYY' ) ;
} ;
} ) ;
2018-01-22 13:01:38 -08:00
app . filter ( 'markdown2html' , function ( ) {
var converter = new showdown . Converter ( {
2019-06-12 16:15:53 +02:00
extensions : [ ] ,
2018-01-22 13:01:38 -08:00
simplifiedAutoLink : true ,
strikethrough : true ,
tables : true
} ) ;
return function ( text ) {
return converter . makeHtml ( text ) ;
} ;
} ) ;
app . filter ( 'postInstallMessage' , function ( ) {
var SSO _MARKER = '=== sso ===' ;
return function ( text , app ) {
if ( ! text ) return '' ;
if ( ! app ) return text ;
var parts = text . split ( SSO _MARKER ) ;
if ( parts . length === 1 ) {
// [^] matches even newlines. '?' makes it non-greedy
if ( app . sso ) return text . replace ( /\<nosso\>[^]*?\<\/nosso\>/g , '' ) ;
else return text . replace ( /\<sso\>[^]*?\<\/sso\>/g , '' ) ;
}
if ( app . sso ) return parts [ 1 ] ;
else return parts [ 0 ] ;
} ;
} ) ;
// keep this in sync with eventlog.js and CLI tool
var ACTION _ACTIVATE = 'cloudron.activate' ;
2018-11-10 01:34:50 -08:00
var ACTION _PROVISION = 'cloudron.provision' ;
var ACTION _RESTORE = 'cloudron.restore' ;
2019-01-11 12:27:27 -08:00
var ACTION _APP _CLONE = 'app.clone' ;
2018-01-22 13:01:38 -08:00
var ACTION _APP _CONFIGURE = 'app.configure' ;
var ACTION _APP _INSTALL = 'app.install' ;
var ACTION _APP _RESTORE = 'app.restore' ;
var ACTION _APP _UNINSTALL = 'app.uninstall' ;
var ACTION _APP _UPDATE = 'app.update' ;
var ACTION _APP _LOGIN = 'app.login' ;
2019-01-17 17:23:34 +01:00
var ACTION _APP _OOM = 'app.oom' ;
2019-02-11 12:34:06 -08:00
var ACTION _APP _UP = 'app.up' ;
2019-01-17 17:23:34 +01:00
var ACTION _APP _DOWN = 'app.down' ;
2019-01-19 15:53:49 +01:00
var ACTION _APP _TASK _CRASH = 'app.task.crash' ;
2018-01-22 13:01:38 -08:00
var ACTION _BACKUP _FINISH = 'backup.finish' ;
var ACTION _BACKUP _START = 'backup.start' ;
2019-01-11 12:27:27 -08:00
var ACTION _BACKUP _CLEANUP _START = 'backup.cleanup.start' ;
var ACTION _BACKUP _CLEANUP _FINISH = 'backup.cleanup.finish' ;
2018-11-14 20:37:58 -08:00
var ACTION _CERTIFICATE _NEW = 'certificate.new' ;
2018-01-22 13:01:38 -08:00
var ACTION _CERTIFICATE _RENEWAL = 'certificate.renew' ;
2019-02-04 20:24:23 -08:00
var ACTION _DASHBOARD _DOMAIN _UPDATE = 'dashboard.domain.update' ;
2018-11-10 01:09:06 -08:00
var ACTION _DOMAIN _ADD = 'domain.add' ;
var ACTION _DOMAIN _UPDATE = 'domain.update' ;
var ACTION _DOMAIN _REMOVE = 'domain.remove' ;
2018-01-22 13:01:38 -08:00
var ACTION _START = 'cloudron.start' ;
var ACTION _UPDATE = 'cloudron.update' ;
var ACTION _USER _ADD = 'user.add' ;
var ACTION _USER _LOGIN = 'user.login' ;
var ACTION _USER _REMOVE = 'user.remove' ;
var ACTION _USER _UPDATE = 'user.update' ;
2018-07-05 13:54:05 -07:00
var ACTION _USER _TRANSFER = 'user.transfer' ;
2018-01-22 13:01:38 -08:00
2018-11-10 00:32:37 -08:00
var ACTION _MAIL _ENABLED = 'mail.enabled' ;
var ACTION _MAIL _DISABLED = 'mail.disabled' ;
var ACTION _MAIL _MAILBOX _ADD = 'mail.box.add' ;
var ACTION _MAIL _MAILBOX _REMOVE = 'mail.box.remove' ;
var ACTION _MAIL _LIST _ADD = 'mail.list.add' ;
var ACTION _MAIL _LIST _REMOVE = 'mail.list.remove' ;
2019-01-12 09:58:30 -08:00
var ACTION _DYNDNS _UPDATE = 'dyndns.update' ;
2019-01-19 15:53:49 +01:00
var ACTION _SYSTEM _CRASH = 'system.crash' ;
2019-02-05 16:41:17 +01:00
app . filter ( 'eventLogSource' , [ 'Client' , function ( Client ) {
2018-03-02 19:21:24 +01:00
return function ( eventLog ) {
var source = eventLog . source ;
2019-01-17 17:23:34 +01:00
var line = '' ;
2018-03-02 19:21:24 +01:00
2019-02-05 16:40:46 +01:00
line = source . username || source . userId || source . authType || 'system' ;
if ( source . appId ) {
var app = Client . getCachedAppSync ( source . appId ) ;
line += ' - ' + ( app ? app . fqdn : source . appId ) ;
} else if ( source . ip ) {
line += ' - ' + source . ip ;
2019-01-17 17:23:34 +01:00
}
2018-03-02 19:21:24 +01:00
return line ;
} ;
2019-02-05 16:40:46 +01:00
} ] ) ;
2018-03-02 19:21:24 +01:00
2018-01-22 13:01:38 -08:00
app . filter ( 'eventLogDetails' , function ( ) {
// NOTE: if you change this, the CLI tool (cloudron machine eventlog) probably needs fixing as well
return function ( eventLog ) {
var data = eventLog . data ;
var errorMessage = data . errorMessage ;
2019-01-11 12:27:27 -08:00
var details ;
2018-01-22 13:01:38 -08:00
2018-03-02 10:49:46 +01:00
switch ( eventLog . action ) {
2018-03-13 21:56:47 -07:00
case ACTION _ACTIVATE :
return 'Cloudron was activated' ;
2018-11-10 01:34:50 -08:00
case ACTION _PROVISION :
return 'Cloudron was setup' ;
case ACTION _RESTORE :
return 'Cloudron was restored from backup ' + data . backupId ;
2018-03-13 21:56:47 -07:00
case ACTION _APP _CONFIGURE :
2019-01-11 12:27:27 -08:00
if ( ! data . app ) return '' ;
return data . app . manifest . title + ' was re-configured at ' + ( data . app . fqdn || data . app . location ) ;
2018-03-13 21:56:47 -07:00
case ACTION _APP _INSTALL :
2019-01-11 12:27:27 -08:00
if ( ! data . app ) return '' ;
2019-02-11 14:53:23 -08:00
return data . app . manifest . title + ' (package v' + data . app . manifest . version + ') was installed at ' + ( data . app . fqdn || data . app . location ) ;
2018-03-13 21:56:47 -07:00
case ACTION _APP _RESTORE :
2019-01-11 12:27:27 -08:00
if ( ! data . app ) return '' ;
details = data . app . manifest . title + ' was restored at ' + ( data . app . fqdn || data . app . location ) ;
// older versions (<3.5) did not have these fields
2019-01-13 14:47:12 -08:00
if ( data . fromManifest ) details += ' from version ' + data . fromManifest . version ;
if ( data . toManifest ) details += ' to version ' + data . toManifest . version ;
if ( data . backupId ) details += ' using backup ' + data . backupId ;
2019-01-11 12:27:27 -08:00
return details ;
2018-03-13 21:56:47 -07:00
case ACTION _APP _UNINSTALL :
2019-01-11 12:27:27 -08:00
if ( ! data . app ) return '' ;
2019-02-11 14:53:23 -08:00
return data . app . manifest . title + ' (package v' + data . app . manifest . version + ') was uninstalled at ' + ( data . app . fqdn || data . app . location ) ;
2018-03-13 21:56:47 -07:00
case ACTION _APP _UPDATE :
2019-01-11 12:27:27 -08:00
if ( ! data . app ) return '' ;
2019-02-11 14:53:23 -08:00
return data . app . manifest . title + ' at ' + ( data . app . fqdn || data . app . location ) + ' was updated from v' + data . fromManifest . version + ' to v' + data . toManifest . version ;
2019-01-11 12:27:27 -08:00
case ACTION _APP _CLONE :
2019-02-11 14:53:23 -08:00
return data . newApp . manifest . title + ' at ' + ( data . newApp . fqdn || data . newApp . location ) + ' was cloned from ' + ( data . oldApp . fqdn || data . oldApp . location ) + ' from backup ' + data . backupId + ' with v' + data . oldApp . manifest . version ;
2018-03-13 21:56:47 -07:00
case ACTION _APP _LOGIN :
return 'App ' + data . appId + ' logged in' ;
2019-01-17 17:23:34 +01:00
case ACTION _APP _OOM :
2019-02-11 14:38:05 -08:00
return data . app . manifest . title + ' ran out of memory' ;
2019-01-17 17:23:34 +01:00
case ACTION _APP _DOWN :
2019-02-11 14:38:05 -08:00
return data . app . manifest . title + ' is down' ;
2019-02-11 12:34:06 -08:00
case ACTION _APP _UP :
2019-02-11 14:38:05 -08:00
return data . app . manifest . title + ' is back online' ;
2019-01-17 17:23:34 +01:00
2019-01-19 15:53:49 +01:00
case ACTION _APP _TASK _CRASH :
2019-02-11 14:38:05 -08:00
return 'Apptask for app with id ' + data . appId + ' crashed' ;
2019-01-19 15:53:49 +01:00
2018-03-13 21:56:47 -07:00
case ACTION _BACKUP _START :
return 'Backup started' ;
case ACTION _BACKUP _FINISH :
return 'Backup finished' + ( errorMessage ? ( ' error: ' + errorMessage ) : '' ) ;
2019-01-11 12:27:27 -08:00
case ACTION _BACKUP _CLEANUP _START :
return 'Backup cleaner started' ;
case ACTION _BACKUP _CLEANUP _FINISH :
2019-01-12 10:08:32 -08:00
return data . errorMessage ? 'Backup cleaner errored: ' + data . errorMessage : 'Backup cleaner removed ' + data . removedBoxBackups . length + ' backups' ;
2018-03-13 21:56:47 -07:00
2018-11-14 20:37:58 -08:00
case ACTION _CERTIFICATE _NEW :
return 'Certificate install for ' + data . domain + ( errorMessage ? ' failed' : ' succeeded' ) ;
2018-03-13 21:56:47 -07:00
case ACTION _CERTIFICATE _RENEWAL :
return 'Certificate renewal for ' + data . domain + ( errorMessage ? ' failed' : ' succeeded' ) ;
2019-02-04 20:24:23 -08:00
case ACTION _DASHBOARD _DOMAIN _UPDATE :
2019-02-09 16:49:06 -08:00
return 'Dashboard domain set to ' + data . fqdn ;
2019-02-04 20:24:23 -08:00
2018-11-10 01:09:06 -08:00
case ACTION _DOMAIN _ADD :
return 'Domain ' + data . domain + ' with ' + data . provider + ' provider was added' ;
2018-03-13 21:56:47 -07:00
2018-11-10 01:09:06 -08:00
case ACTION _DOMAIN _UPDATE :
return 'Domain ' + data . domain + ' with ' + data . provider + ' provider was updated' ;
2018-07-05 13:54:05 -07:00
2018-11-10 01:09:06 -08:00
case ACTION _DOMAIN _REMOVE :
return 'Domain ' + data . domain + ' was removed' ;
2018-03-13 21:56:47 -07:00
2018-11-10 00:32:37 -08:00
case ACTION _MAIL _ENABLED :
return 'Cloudron Mail was enabled for domain ' + data . domain ;
case ACTION _MAIL _DISABLED :
return 'Cloudron Mail was disabled for domain ' + data . domain ;
case ACTION _MAIL _MAILBOX _ADD :
return 'Mailbox with name ' + data . name + ' was added in domain ' + data . domain ;
case ACTION _MAIL _MAILBOX _REMOVE :
return 'Mailbox with name ' + data . name + ' was removed in domain ' + data . domain ;
case ACTION _MAIL _LIST _ADD :
return 'Mail list with name ' + data . name + ' was added in domain ' + data . domain ;
case ACTION _MAIL _LIST _REMOVE :
return 'Mail list with name ' + data . name + ' was added in domain ' + data . domain ;
2018-11-10 01:09:06 -08:00
case ACTION _START :
return 'Cloudron started with version ' + data . version ;
case ACTION _UPDATE :
return 'Cloudron was updated to version ' + data . boxUpdateInfo . version ;
case ACTION _USER _ADD :
return data . email + ( data . user . username ? ' (' + data . user . username + ')' : '' ) + ' was added' ;
case ACTION _USER _UPDATE :
return ( data . user ? ( data . user . email + ( data . user . username ? ' (' + data . user . username + ')' : '' ) ) : data . userId ) + ' was updated' ;
case ACTION _USER _REMOVE :
return ( data . user ? ( data . user . email + ( data . user . username ? ' (' + data . user . username + ')' : '' ) ) : data . userId ) + ' was removed' ;
case ACTION _USER _TRANSFER :
return 'Apps of ' + data . oldOwnerId + ' was transferred to ' + data . newOwnerId ;
case ACTION _USER _LOGIN :
return ( data . user ? ( data . user . email + ( data . user . username ? ' (' + data . user . username + ')' : '' ) ) : data . userId ) + ' logged in' ;
2019-01-12 09:58:30 -08:00
case ACTION _DYNDNS _UPDATE :
return 'DNS was updated from ' + data . fromIp + ' to ' + data . toIp ;
2019-01-19 15:53:49 +01:00
case ACTION _SYSTEM _CRASH :
return 'A system process crashed' ;
2018-03-13 21:56:47 -07:00
default : return eventLog . action ;
2018-02-19 01:56:09 -08:00
}
2018-03-02 10:49:46 +01:00
} ;
} ) ;
app . filter ( 'eventLogAction' , function ( ) {
return function ( eventLog ) {
2018-01-22 13:01:38 -08:00
switch ( eventLog . action ) {
2018-03-13 21:56:47 -07:00
case ACTION _ACTIVATE : return 'Cloudron activated' ;
2018-11-10 01:34:50 -08:00
case ACTION _RESTORE : return 'Cloudron restored' ;
case ACTION _PROVISION : return 'Cloudron provisioned' ;
2018-03-13 21:56:47 -07:00
case ACTION _APP _CONFIGURE : return 'App configured' ;
case ACTION _APP _INSTALL : return 'App installed' ;
case ACTION _APP _RESTORE : return 'App restored' ;
case ACTION _APP _UNINSTALL : return 'App uninstalled' ;
case ACTION _APP _UPDATE : return 'App updated' ;
2019-01-11 12:27:27 -08:00
case ACTION _APP _CLONE : return 'App cloned' ;
2018-03-13 21:56:47 -07:00
case ACTION _APP _LOGIN : return 'App login' ;
case ACTION _BACKUP _START : return 'Backup started' ;
case ACTION _BACKUP _FINISH : return 'Backup finished' ;
2019-01-11 12:27:27 -08:00
case ACTION _BACKUP _CLEANUP _START : return 'Backup cleaner started' ;
case ACTION _BACKUP _CLEANUP _FINISH : return 'Backup cleaner finished' ;
case ACTION _CERTIFICATE _NEW : return 'Certificated installed' ;
2018-03-13 21:56:47 -07:00
case ACTION _CERTIFICATE _RENEWAL : return 'Certificate renewal' ;
2019-02-04 20:24:23 -08:00
case ACTION _DASHBOARD _DOMAIN _UPDATE : return 'Dashboard domain updated' ;
2018-11-10 01:09:06 -08:00
case ACTION _DOMAIN _ADD : return 'Domain added' ;
case ACTION _DOMAIN _UPDATE : return 'Domain updated' ;
case ACTION _DOMAIN _REMOVE : return 'Domain removed' ;
2018-11-10 00:32:37 -08:00
case ACTION _MAIL _ENABLED : return 'Mail enabled' ;
case ACTION _MAIL _DISABLED : return 'Mail disabled' ;
case ACTION _MAIL _MAILBOX _ADD : return 'Mailbox added' ;
case ACTION _MAIL _MAILBOX _REMOVE : return 'Mailbox removed' ;
case ACTION _MAIL _LIST _ADD : return 'Mail list added' ;
case ACTION _MAIL _LIST _REMOVE : return 'Mail list removed' ;
2018-11-10 01:09:06 -08:00
case ACTION _START : return 'Cloudron started' ;
2019-01-11 12:27:27 -08:00
case ACTION _UPDATE : return 'Cloudron updated' ;
2018-11-10 01:09:06 -08:00
case ACTION _USER _ADD : return 'User added' ;
case ACTION _USER _LOGIN : return 'User login' ;
case ACTION _USER _REMOVE : return 'User removed' ;
case ACTION _USER _UPDATE : return 'User updated' ;
2019-01-12 09:58:30 -08:00
case ACTION _DYNDNS _UPDATE : return 'DNS Updated' ;
2018-03-13 21:56:47 -07:00
default : return eventLog . action ;
2018-01-22 13:01:38 -08:00
}
} ;
} ) ;
// custom directive for dynamic names in forms
// See http://stackoverflow.com/questions/23616578/issue-registering-form-control-with-interpolated-name#answer-23617401
app . directive ( 'laterName' , function ( ) { // (2)
return {
restrict : 'A' ,
require : [ '?ngModel' , '^?form' ] , // (3)
link : function postLink ( scope , elem , attrs , ctrls ) {
attrs . $set ( 'name' , attrs . laterName ) ;
var modelCtrl = ctrls [ 0 ] ; // (3)
var formCtrl = ctrls [ 1 ] ; // (3)
if ( modelCtrl && formCtrl ) {
modelCtrl . $name = attrs . name ; // (4)
formCtrl . $addControl ( modelCtrl ) ; // (2)
scope . $on ( '$destroy' , function ( ) {
formCtrl . $removeControl ( modelCtrl ) ; // (5)
} ) ;
}
}
} ;
} ) ;
app . run ( [ '$route' , '$rootScope' , '$location' , function ( $route , $rootScope , $location ) {
var original = $location . path ;
$location . path = function ( path , reload ) {
if ( reload === false ) {
var lastRoute = $route . current ;
var un = $rootScope . $on ( '$locationChangeSuccess' , function ( ) {
$route . current = lastRoute ;
un ( ) ;
} ) ;
}
return original . apply ( $location , [ path ] ) ;
} ;
} ] ) ;
app . directive ( 'ngClickSelect' , function ( ) {
return {
restrict : 'AC' ,
2019-01-22 10:54:03 +01:00
link : function ( scope , element /*, attrs */ ) {
2018-01-22 13:01:38 -08:00
element . bind ( 'click' , function ( ) {
var selection = window . getSelection ( ) ;
var range = document . createRange ( ) ;
range . selectNodeContents ( this ) ;
selection . removeAllRanges ( ) ;
selection . addRange ( range ) ;
} ) ;
}
} ;
} ) ;
app . directive ( 'ngClickReveal' , function ( ) {
return {
restrict : 'A' ,
link : function ( scope , element , attrs ) {
element . addClass ( 'hand' ) ;
var value = '' ;
scope . $watch ( attrs . ngClickReveal , function ( newValue , oldValue ) {
if ( newValue !== oldValue ) {
element . html ( '<i>hidden</i>' ) ;
value = newValue ;
}
} ) ;
element . bind ( 'click' , function ( ) {
element . text ( value ) ;
} ) ;
}
} ;
} ) ;
// https://codepen.io/webmatze/pen/isuHh
app . directive ( 'tagInput' , function ( ) {
return {
restrict : 'E' ,
scope : {
inputTags : '=taglist'
} ,
link : function ( $scope , element , attrs ) {
$scope . defaultWidth = 200 ;
$scope . tagText = '' ; // current tag being edited
$scope . placeholder = attrs . placeholder ;
$scope . tagArray = function ( ) {
if ( $scope . inputTags === undefined ) {
return [ ] ;
}
return $scope . inputTags . split ( ',' ) . filter ( function ( tag ) {
return tag !== '' ;
} ) ;
} ;
$scope . addTag = function ( ) {
var tagArray ;
if ( $scope . tagText . length === 0 ) {
return ;
}
tagArray = $scope . tagArray ( ) ;
tagArray . push ( $scope . tagText ) ;
$scope . inputTags = tagArray . join ( ',' ) ;
return $scope . tagText = '' ;
} ;
$scope . deleteTag = function ( key ) {
var tagArray ;
tagArray = $scope . tagArray ( ) ;
if ( tagArray . length > 0 && $scope . tagText . length === 0 && key === undefined ) {
tagArray . pop ( ) ;
} else {
if ( key !== undefined ) {
tagArray . splice ( key , 1 ) ;
}
}
return $scope . inputTags = tagArray . join ( ',' ) ;
} ;
$scope . $watch ( 'tagText' , function ( newVal , oldVal ) {
var tempEl ;
if ( ! ( newVal === oldVal && newVal === undefined ) ) {
tempEl = $ ( '<span>' + newVal + '</span>' ) . appendTo ( 'body' ) ;
$scope . inputWidth = tempEl . width ( ) + 5 ;
if ( $scope . inputWidth < $scope . defaultWidth ) {
$scope . inputWidth = $scope . defaultWidth ;
}
return tempEl . remove ( ) ;
}
} ) ;
element . bind ( 'keydown' , function ( e ) {
var key = e . which ;
if ( key === 9 || key === 13 ) {
e . preventDefault ( ) ;
}
if ( key === 8 ) {
return $scope . $apply ( 'deleteTag()' ) ;
}
} ) ;
element . bind ( 'keyup' , function ( e ) {
var key = e . which ;
if ( key === 9 || key === 13 || key === 32 || key === 188 ) {
e . preventDefault ( ) ;
return $scope . $apply ( 'addTag()' ) ;
}
} ) ;
} ,
template :
'<div class="tag-input-container">' +
'<div class="input-tag" data-ng-repeat="tag in tagArray()">' +
'{{tag}}' +
'<div class="delete-tag" data-ng-click="deleteTag($index)">×</div>' +
'</div>' +
'<input type="text" data-ng-model="tagText" ng-blur="addTag()" placeholder="{{placeholder}}"/>' +
'</div>'
} ;
} ) ;
app . config ( [ 'fitTextConfigProvider' , function ( fitTextConfigProvider ) {
fitTextConfigProvider . config = {
loadDelay : 250 ,
compressor : 0.9 ,
min : 8 ,
max : 24
} ;
} ] ) ;