Files
cloudron-box/webadmin/src/js/index.js

308 lines
11 KiB
JavaScript
Raw Normal View History

'use strict';
/* global angular:false */
2016-01-13 16:00:37 +01:00
/* global showdown:false */
// deal with accessToken in the query, this is passed for example on password reset
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;
// create main application module
var app = angular.module('Application', ['ngRoute', 'ngAnimate', 'ngSanitize', 'angular-md5', 'slick', 'ui-notification', 'ui.bootstrap-slider']);
app.config(['NotificationProvider', function (NotificationProvider) {
NotificationProvider.setOptions({
delay: 10000,
startTop: 60,
2016-01-14 15:53:47 +01:00
positionX: 'left',
templateUrl: 'templates/notification.html'
});
}]);
// setup all major application routes
app.config(['$routeProvider', function ($routeProvider) {
$routeProvider.when('/', {
redirectTo: '/apps'
}).when('/users', {
controller: 'UsersController',
templateUrl: 'views/users.html'
}).when('/appstore', {
controller: 'AppStoreController',
templateUrl: 'views/appstore.html'
}).when('/appstore/:appId', {
controller: 'AppStoreController',
templateUrl: 'views/appstore.html'
}).when('/apps', {
controller: 'AppsController',
templateUrl: 'views/apps.html'
}).when('/account', {
controller: 'AccountController',
templateUrl: 'views/account.html'
}).when('/graphs', {
controller: 'GraphsController',
templateUrl: 'views/graphs.html'
2015-11-04 17:04:55 -08:00
}).when('/certs', {
controller: 'CertsController',
templateUrl: 'views/certs.html'
}).when('/settings', {
controller: 'SettingsController',
templateUrl: 'views/settings.html'
2016-04-30 18:57:55 -07:00
}).when('/activity', {
controller: 'ActivityController',
templateUrl: 'views/activity.html'
2015-08-04 11:33:17 +02:00
}).when('/support', {
controller: 'SupportController',
templateUrl: 'views/support.html'
}).otherwise({ redirectTo: '/'});
}]);
// keep in sync with appdb.js
var ISTATES = {
PENDING_INSTALL: 'pending_install',
PENDING_CONFIGURE: 'pending_configure',
PENDING_UNINSTALL: 'pending_uninstall',
PENDING_RESTORE: 'pending_restore',
PENDING_UPDATE: 'pending_update',
2015-07-20 10:43:19 -07:00
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;
};
});
app.filter('installationActive', function () {
return function(app) {
if (app.installationState === ISTATES.ERROR) return false;
if (app.installationState === ISTATES.INSTALLED) return false;
return true;
};
});
app.filter('installationStateLabel', function() {
return function(app) {
var waiting = app.progress === 0 ? ' (Waiting)' : '';
switch (app.installationState) {
case ISTATES.PENDING_INSTALL: 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 'Error';
case ISTATES.INSTALLED: {
if (app.runState === 'running') {
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;
break;
}
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('ignoreAdminGroup', function () {
return function (groups) {
return groups.filter(function (group) {
if (group.id) return group.id !== 'admin';
return group !== 'admin';
});
};
});
app.filter('applicationLink', function() {
return function(app) {
if (app.installationState === ISTATES.INSTALLED && app.health === HSTATES.HEALTHY) {
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 || day_diff >= 31)
return;
2016-01-13 16:00:37 +01:00
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') ||
2016-01-13 16:00:37 +01:00
day_diff === 1 && 'Yesterday' ||
day_diff < 7 && day_diff + ' days ago' ||
day_diff < 31 && Math.ceil( day_diff / 7 ) + ' weeks ago';
};
});
app.filter('markdown2html', function () {
var converter = new showdown.Converter({
extensions: ['targetblank'],
simplifiedAutoLink: true,
strikethrough: true,
tables: true
});
return function (text) {
return converter.makeHtml(text);
};
});
2016-04-30 19:49:50 -07:00
// keep this in sync with eventlog.js
var ACTION_ACTIVATE = 'cloudron.activate';
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';
2016-05-01 11:42:12 -07:00
var ACTION_APP_UPDATE = 'app.update';
var ACTION_BACKUP_FINISH = 'backup.finish';
var ACTION_BACKUP_START = 'backup.start';
2016-04-30 22:27:33 -07:00
var ACTION_CERTIFICATE_RENEWAL = 'certificate.renew';
2016-04-30 19:49:50 -07:00
var ACTION_CLI_MODE = 'settings.climode';
var ACTION_UPDATE = 'cloudron.update';
var ACTION_USER_ADD = 'user.add';
2016-04-30 23:16:37 -07:00
var ACTION_USER_LOGIN = 'user.login';
2016-04-30 19:49:50 -07:00
var ACTION_USER_REMOVE = 'user.remove';
var ACTION_USER_UPDATE = 'user.update';
app.filter('eventLogDetails', function() {
return function(eventLog) {
2016-05-01 11:42:12 -07:00
var source = eventLog.source;
2016-04-30 19:49:50 -07:00
var data = eventLog.data;
2016-05-02 09:32:39 -07:00
var errorMessage = data.errorMessage;
2016-04-30 19:49:50 -07:00
switch (eventLog.action) {
2016-05-02 09:32:39 -07:00
case ACTION_ACTIVATE: return 'Cloudron activated';
case ACTION_APP_CONFIGURE: return 'App ' + data.appId + ' was configured';
case ACTION_APP_INSTALL: return 'App ' + data.manifest.appStoreId + '@' + data.manifest.version + ' installed at ' + data.location + ' with id ' + data.appId;
2016-04-30 19:49:50 -07:00
case ACTION_APP_RESTORE: return 'App ' + data.id + ' restored';
case ACTION_APP_UNINSTALL: return 'App ' + data.id + ' uninstalled';
2016-05-02 09:32:39 -07:00
case ACTION_APP_UPDATE: return 'App ' + data.id + ' updated to version ' + data.toManifest.appStoreId + '@' + data.toManifest.version;
case ACTION_BACKUP_START: return 'Backup started';
case ACTION_BACKUP_FINISH: return 'Backup finished. ' + (('error:' + errorMessage) || ('id:' + data.filename));
case ACTION_CERTIFICATE_RENEWAL: return 'Certificate renewal for ' + data.domain + (errorMessage ? ' failed' : 'succeeded');
2016-04-30 19:49:50 -07:00
case ACTION_CLI_MODE: return 'CLI mode was ' + (data.enabled ? 'enabled' : 'disabled');
2016-05-02 09:32:39 -07:00
case ACTION_UPDATE: return 'Updating to version ' + data.boxUpdateInfo.version;
2016-04-30 19:49:50 -07:00
case ACTION_USER_ADD: return 'User ' + data.email + ' added';
2016-05-02 09:32:39 -07:00
case ACTION_USER_LOGIN: return 'User ' + data.username + ' logged in';
case ACTION_USER_REMOVE: return 'User ' + data.userId + ' removed';
case ACTION_USER_UPDATE: return 'User ' + data.userId + ' updated';
2016-04-30 19:49:50 -07:00
default: return eventLog.action;
}
};
});
// 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]);
};
2016-04-04 18:35:38 +02:00
}]);
app.directive('ngClickSelect', function () {
return {
restrict: 'AC',
link: function (scope, element, attrs) {
element.bind('click', function () {
var selection = window.getSelection();
var range = document.createRange();
range.selectNodeContents(this);
selection.removeAllRanges();
selection.addRange(range);
});
}
};
});