Files
cloudron-box/webadmin/src/js/client.js
Johannes Zellner 17d48f3fce Specify the expiration on the client. Currently this is 100 years.
I am not sure if this is the best approach, or if we should introduce a magic value instead.
2016-06-07 15:54:32 +02:00

874 lines
34 KiB
JavaScript

'use strict';
/* global angular */
/* global EventSource */
angular.module('Application').service('Client', ['$http', 'md5', 'Notification', function ($http, md5, Notification) {
var client = null;
// Keep this in sync with docs and constants.js, docker.js
var DEFAULT_MEMORY_LIMIT = 1024 * 1024 * 256;
function ClientError(statusCode, messageOrObject) {
Error.call(this);
this.name = this.constructor.name;
this.statusCode = statusCode;
if (messageOrObject === null || typeof messageOrObject === 'undefined') {
this.message = 'Empty message or object';
} else if (typeof messageOrObject === 'string') {
this.message = messageOrObject;
} else if (messageOrObject.message) {
this.message = messageOrObject.message;
} else {
this.message = JSON.stringify(messageOrObject);
}
}
function defaultErrorHandler(callback) {
return function (data, status) {
if (status === 401) return client.login();
if (status === 503) {
// this could indicate a update/upgrade/restore/migration
client.progress(function (error, result) {
if (error) {
client.error(error);
return callback(new ClientError(status, data));
}
if (result.update && result.update.percent !== -1) window.location.href = '/update.html';
else callback(new ClientError(status, data));
}, function (data, status) {
client.error(data);
return callback(new ClientError(status, data));
});
return;
}
if (status >= 500) {
client.error(data);
return callback(new ClientError(status, data));
}
var obj = data;
try {
obj = JSON.parse(data);
} catch (e) {}
callback(new ClientError(status, obj));
};
}
function Client() {
this._ready = false;
this._configListener = [];
this._readyListener = [];
this._userInfo = {
id: null,
username: null,
email: null,
admin: false
};
this._token = null;
this._config = {
apiServerOrigin: null,
webServerOrigin: null,
fqdn: null,
ip: null,
revision: null,
update: { box: null, apps: null },
isDev: false,
progress: {},
isCustomDomain: false,
developerMode: false,
region: null,
size: null,
memory: 0
};
this._installedApps = [];
this._clientId = '<%= oauth.clientId %>';
this._clientSecret = '<%= oauth.clientSecret %>';
this.apiOrigin = '<%= oauth.apiOrigin %>';
this.avatar = '';
this.resetAvatar();
this.setToken(localStorage.token);
}
Client.prototype.error = function (error) {
var message = '';
if (typeof error === 'object') {
message = error.message || error;
} else {
message = error;
}
Notification.error({ title: 'Cloudron Error', message: message });
};
/*
Example usage with an action:
var actionScope = $scope.$new(true);
actionScope.action = '/#/certs';
Client.notify('title', 'message', true, actionScope);
*/
Client.prototype.notify = function (title, message, persitent, type, actionScope) {
var options = { title: title, message: message};
if (persitent) options.delay = 'never'; // any non Number means never timeout
if (actionScope) {
if (typeof actionScope.action !== 'string') throw('an actionScope has to have an action url');
options.scope = actionScope;
}
if (type === 'error') Notification.error(options);
else if (type === 'success') Notification.success(options);
else if (type === 'info') Notification.info(options);
else throw('Invalid notification type "' + type + '"');
};
Client.prototype.setReady = function () {
if (this._ready) return;
this._ready = true;
this._readyListener.forEach(function (callback) {
callback();
});
};
Client.prototype.onReady = function (callback) {
if (this._ready) callback();
this._readyListener.push(callback);
};
Client.prototype.onConfig = function (callback) {
this._configListener.push(callback);
if (this._config && this._config.apiServerOrigin) callback(this._config);
};
Client.prototype.resetAvatar = function () {
this.avatar = this.apiOrigin + '/api/v1/cloudron/avatar?' + String(Math.random()).slice(2);
var favicon = $('#favicon');
if (favicon) favicon.attr('href', this.avatar);
};
Client.prototype.setUserInfo = function (userInfo) {
// In order to keep the angular bindings alive, set each property individually
this._userInfo.id = userInfo.id;
this._userInfo.username = userInfo.username;
this._userInfo.email = userInfo.email;
this._userInfo.displayName = userInfo.displayName;
this._userInfo.admin = !!userInfo.admin;
this._userInfo.showTutorial = !!userInfo.showTutorial;
this._userInfo.gravatar = 'https://www.gravatar.com/avatar/' + md5.createHash(userInfo.email.toLowerCase()) + '.jpg?s=24&d=mm';
this._userInfo.gravatarHuge = 'https://www.gravatar.com/avatar/' + md5.createHash(userInfo.email.toLowerCase()) + '.jpg?s=128&d=mm';
};
Client.prototype.setConfig = function (config) {
var that = this;
angular.copy(config, this._config);
this._configListener.forEach(function (callback) {
callback(that._config);
});
};
Client.prototype.getInstalledApps = function () {
return this._installedApps;
};
Client.prototype.getUserInfo = function () {
return this._userInfo;
};
Client.prototype.getConfig = function () {
return this._config;
};
Client.prototype.setToken = function (token) {
$http.defaults.headers.common.Authorization = 'Bearer ' + token;
if (!token) localStorage.removeItem('token');
else localStorage.token = token;
this._token = token;
};
/*
* Rest API wrappers
*/
Client.prototype.config = function (callback) {
$http.get(client.apiOrigin + '/api/v1/cloudron/config').success(function(data, status) {
if (status !== 200 || typeof data !== 'object') return callback(new ClientError(status, data));
callback(null, data);
}).error(defaultErrorHandler(callback));
};
Client.prototype.userInfo = function (callback) {
$http.get(client.apiOrigin + '/api/v1/profile').success(function(data, status) {
if (status !== 200 || typeof data !== 'object') return callback(new ClientError(status, data));
callback(null, data);
}).error(defaultErrorHandler(callback));
};
Client.prototype.changeDeveloperMode = function (enabled, password, callback) {
var that = this;
var data = { password: password, enabled: enabled };
$http.post(client.apiOrigin + '/api/v1/developer', data).success(function (data, status) {
if (status !== 200) return callback(new ClientError(status, data));
// will get overriden after polling for config, but ensures quick UI update
that._config.developerMode = enabled;
callback(null);
}).error(defaultErrorHandler(callback));
};
Client.prototype.changeCloudronAvatar = function (avatarFile, callback) {
var fd = new FormData();
fd.append('avatar', avatarFile);
$http.post(client.apiOrigin + '/api/v1/settings/cloudron_avatar', fd, {
headers: { 'Content-Type': undefined },
transformRequest: angular.identity
}).success(function(data, status) {
if (status !== 202) return callback(new ClientError(status, data));
callback(null);
}).error(defaultErrorHandler(callback));
};
Client.prototype.installApp = function (id, manifest, title, config, callback) {
var that = this;
var data = {
appStoreId: id + '@' + manifest.version,
location: config.location,
portBindings: config.portBindings,
accessRestriction: config.accessRestriction,
cert: config.cert,
key: config.key
};
$http.post(client.apiOrigin + '/api/v1/apps/install', data).success(function (data, status) {
if (status !== 202 || typeof data !== 'object') return defaultErrorHandler(callback);
// put new app with amended title in cache
data.manifest = { title: title };
var icons = that.getAppIconUrls(data);
data.iconUrl = icons.cloudron;
data.iconUrlStore = icons.store;
data.progress = 0;
that._installedApps.push(data);
callback(null, data.id);
}).error(defaultErrorHandler(callback));
};
Client.prototype.restoreApp = function (appId, password, callback) {
var data = { password: password };
$http.post(client.apiOrigin + '/api/v1/apps/' + appId + '/restore', data).success(function (data, status) {
if (status !== 202) return callback(new ClientError(status, data));
callback(null);
}).error(defaultErrorHandler(callback));
};
Client.prototype.uninstallApp = function (appId, password, callback) {
var data = { password: password };
$http.post(client.apiOrigin + '/api/v1/apps/' + appId + '/uninstall', data).success(function (data, status) {
if (status !== 202) return callback(new ClientError(status, data));
callback(null);
}).error(defaultErrorHandler(callback));
};
Client.prototype.configureApp = function (id, password, config, callback) {
var data = {
appId: id,
password: password,
location: config.location,
portBindings: config.portBindings,
accessRestriction: config.accessRestriction,
cert: config.cert,
key: config.key,
memoryLimit: config.memoryLimit,
altDomain: config.altDomain || null
};
$http.post(client.apiOrigin + '/api/v1/apps/' + id + '/configure', data).success(function (data, status) {
if (status !== 202) return callback(new ClientError(status, data));
callback(null);
}).error(defaultErrorHandler(callback));
};
Client.prototype.updateApp = function (id, manifest, portBindings, password, callback) {
$http.post(client.apiOrigin + '/api/v1/apps/' + id + '/update', { appStoreId: manifest.id + '@' + manifest.version, password: password, portBindings: portBindings }).success(function (data, status) {
if (status !== 202) return callback(new ClientError(status, data));
callback(null);
}).error(defaultErrorHandler(callback));
};
Client.prototype.startApp = function (id, callback) {
var data = { };
$http.post(client.apiOrigin + '/api/v1/apps/' + id + '/start', data).success(function (data, status) {
if (status !== 202) return callback(new ClientError(status, data));
callback(null);
}).error(defaultErrorHandler(callback));
};
Client.prototype.stopApp = function (id, callback) {
var data = { };
$http.post(client.apiOrigin + '/api/v1/apps/' + id + '/stop', data).success(function (data, status) {
if (status !== 202) return callback(new ClientError(status, data));
callback(null);
}).error(defaultErrorHandler(callback));
};
Client.prototype.progress = function (callback, errorCallback) {
// this is used in the defaultErrorHandler itself, and avoids a loop
if (typeof errorCallback !== 'function') errorCallback = defaultErrorHandler(callback);
$http.get(client.apiOrigin + '/api/v1/cloudron/progress').success(function(data, status) {
if (status !== 200 || typeof data !== 'object') return callback(new ClientError(status, data));
callback(null, data);
}).error(errorCallback);
};
Client.prototype.version = function (callback) {
$http.get(client.apiOrigin + '/api/v1/cloudron/status').success(function(data, status) {
if (status !== 200 || typeof data !== 'object') return callback(new ClientError(status, data));
callback(null, data);
}).error(defaultErrorHandler(callback));
};
Client.prototype.getStatus = function (callback) {
$http.get(client.apiOrigin + '/api/v1/cloudron/status').success(function(data, status) {
if (status !== 200 || typeof data !== 'object') return callback(new ClientError(status, data));
callback(null, data);
}).error(defaultErrorHandler(callback));
};
Client.prototype.setDnsConfig = function (dnsConfig, callback) {
$http.post(client.apiOrigin + '/api/v1/settings/dns_config', dnsConfig).success(function(data, status) {
if (status !== 200) return callback(new ClientError(status, data));
callback(null);
}).error(defaultErrorHandler(callback));
};
Client.prototype.getDnsConfig = function (callback) {
$http.get(client.apiOrigin + '/api/v1/settings/dns_config').success(function(data, status) {
if (status !== 200) return callback(new ClientError(status, data));
callback(null, data);
}).error(defaultErrorHandler(callback));
};
Client.prototype.getBackups = function (callback) {
$http.get(client.apiOrigin + '/api/v1/backups').success(function (data, status) {
if (status !== 200 || typeof data !== 'object') return callback(new ClientError(status, data));
callback(null, data.backups);
}).error(defaultErrorHandler(callback));
};
Client.prototype.backup = function (callback) {
$http.post(client.apiOrigin + '/api/v1/backups').success(function(data, status) {
if (status !== 202 || typeof data !== 'object') return callback(new ClientError(status, data));
callback(null, data);
}).error(defaultErrorHandler(callback));
};
Client.prototype.getEventLogs = function (action, search, page, perPage, callback) {
var config = {
params: {
action: action,
search: search,
page: page,
per_page: perPage
}
};
$http.get(client.apiOrigin + '/api/v1/eventlog', config).success(function (data, status) {
if (status !== 200 || typeof data !== 'object') return callback(new ClientError(status, data));
callback(null, data.eventlogs);
}).error(defaultErrorHandler(callback));
};
Client.prototype.getApps = function (callback) {
$http.get(client.apiOrigin + '/api/v1/apps').success(function (data, status) {
if (status !== 200 || typeof data !== 'object') return callback(new ClientError(status, data));
callback(null, data.apps);
}).error(defaultErrorHandler(callback));
};
Client.prototype.getUsers = function (callback) {
$http.get(client.apiOrigin + '/api/v1/users').success(function (data, status) {
if (status !== 200 || typeof data !== 'object') return callback(new ClientError(status, data));
callback(null, data.users);
}).error(defaultErrorHandler(callback));
};
Client.prototype.getGroups = function (callback) {
$http.get(client.apiOrigin + '/api/v1/groups').success(function (data, status) {
if (status !== 200 || typeof data !== 'object') return callback(new ClientError(status, data));
callback(null, data.groups);
}).error(defaultErrorHandler(callback));
};
Client.prototype.getMailboxes = function (callback) {
$http.get(client.apiOrigin + '/api/v1/mailboxes').success(function (data, status) {
if (status !== 200 || typeof data !== 'object') return callback(new ClientError(status, data));
callback(null, data.mailboxes);
}).error(defaultErrorHandler(callback));
};
Client.prototype.setGroups = function (userId, groupIds, callback) {
$http.put(client.apiOrigin + '/api/v1/users/' + userId + '/groups', { groupIds: groupIds }).success(function (data, status) {
if (status !== 204) return callback(new ClientError(status, data));
callback(null);
}).error(defaultErrorHandler(callback));
};
Client.prototype.getGroup = function (groupId, callback) {
$http.get(client.apiOrigin + '/api/v1/groups/' + groupId).success(function (data, status) {
if (status !== 200 || typeof data !== 'object') return callback(new ClientError(status, data));
callback(null, data);
}).error(defaultErrorHandler(callback));
};
Client.prototype.createGroup = function (name, callback) {
var data = {
name: name
};
$http.post(client.apiOrigin + '/api/v1/groups', data).success(function(data, status) {
if (status !== 201 || typeof data !== 'object') return callback(new ClientError(status, data));
callback(null, data);
}).error(defaultErrorHandler(callback));
};
Client.prototype.removeGroup = function (groupId, password, callback) {
var data = {
password: password
};
$http({ method: 'DELETE', url: client.apiOrigin + '/api/v1/groups/' + groupId, data: data, headers: { 'Content-Type': 'application/json' }}).success(function(data, status) {
if (status !== 204) return callback(new ClientError(status, data));
callback(null, data);
}).error(defaultErrorHandler(callback));
};
Client.prototype.getNonApprovedApps = function (callback) {
if (!this._config.developerMode) return callback(null, []);
$http.get(client.apiOrigin + '/api/v1/developer/apps').success(function (data, status) {
if (status !== 200 || typeof data !== 'object') return callback(new ClientError(status, data));
callback(null, data.apps || []);
}).error(defaultErrorHandler(callback));
};
Client.prototype.getApp = function (appId, callback) {
var appFound = null;
this._installedApps.some(function (app) {
if (app.id === appId) {
appFound = app;
return true;
} else {
return false;
}
});
if (appFound) return callback(null, appFound);
else return callback(new Error('App not found'));
};
Client.prototype.getAppLogStream = function (appId) {
var source = new EventSource(client.apiOrigin + '/api/v1/apps/' + appId + '/logstream');
return source;
};
Client.prototype.getAppIconUrls = function (app) {
return {
cloudron: app.iconUrl ? (this.apiOrigin + app.iconUrl + '?access_token=' + this._token) : null,
store: app.appStoreId ? (this._config.apiServerOrigin + '/api/v1/apps/' + app.appStoreId + '/versions/' + app.manifest.version + '/icon') : null
};
};
Client.prototype.sendInvite = function (user, callback) {
$http.post(client.apiOrigin + '/api/v1/users/' + user.id + '/invite', {}).success(function (data, status) {
if (status !== 200) return callback(new ClientError(status, data));
callback(null, data.resetToken);
}).error(defaultErrorHandler(callback));
};
Client.prototype.createAdmin = function (username, password, email, displayName, setupToken, callback) {
var payload = {
username: username,
password: password,
email: email,
displayName: displayName
};
var that = this;
$http.post(client.apiOrigin + '/api/v1/cloudron/activate?setupToken=' + setupToken, payload).success(function(data, status) {
if (status !== 201 || typeof data !== 'object') return callback(new ClientError(status, data));
that.setToken(data.token);
that.setUserInfo({ username: username, email: email, admin: true });
callback(null, data.activated);
}).error(defaultErrorHandler(callback));
};
Client.prototype.getOAuthClients = function (callback) {
$http.get(client.apiOrigin + '/api/v1/oauth/clients').success(function(data, status) {
if (status !== 200 || typeof data !== 'object') return callback(new ClientError(status, data));
callback(null, data.clients);
}).error(defaultErrorHandler(callback));
};
Client.prototype.createOAuthClient = function (appId, scope, redirectURI, callback) {
var data = {
appId: appId,
scope: scope,
redirectURI: redirectURI
};
$http.post(client.apiOrigin + '/api/v1/oauth/clients', data).success(function(data, status) {
if (status !== 201 || typeof data !== 'object') return callback(new ClientError(status, data));
callback(null, data.clients);
}).error(defaultErrorHandler(callback));
};
Client.prototype.delOAuthClient = function (id, callback) {
$http.delete(client.apiOrigin + '/api/v1/oauth/clients/' + id).success(function(data, status) {
if (status !== 204) return callback(new ClientError(status, data));
callback(null);
}).error(defaultErrorHandler(callback));
};
Client.prototype.createTokenByClientId = function (id, expiresAt, callback) {
$http.post(client.apiOrigin + '/api/v1/oauth/clients/' + id + '/tokens?expiresAt=' + expiresAt).success(function(data, status) {
if (status !== 201) return callback(new ClientError(status, data));
callback(null, data.token);
}).error(defaultErrorHandler(callback));
};
Client.prototype.getTokensByClientId = function (id, callback) {
$http.get(client.apiOrigin + '/api/v1/oauth/clients/' + id + '/tokens').success(function(data, status) {
if (status !== 200) return callback(new ClientError(status, data));
callback(null, data.tokens);
}).error(defaultErrorHandler(callback));
};
Client.prototype.delTokensByClientId = function (id, callback) {
$http.delete(client.apiOrigin + '/api/v1/oauth/clients/' + id + '/tokens').success(function(data, status) {
if (status !== 204) return callback(new ClientError(status, data));
callback(null);
}).error(defaultErrorHandler(callback));
};
Client.prototype.delToken = function (clientId, tokenId, callback) {
$http.delete(client.apiOrigin + '/api/v1/oauth/clients/' + clientId + '/tokens/' + tokenId).success(function(data, status) {
if (status !== 204) return callback(new ClientError(status, data));
callback(null);
}).error(defaultErrorHandler(callback));
};
Client.prototype.update = function (password, callback) {
$http.post(client.apiOrigin + '/api/v1/cloudron/update', { password: password }).success(function(data, status) {
if (status !== 202 || typeof data !== 'object') return callback(new ClientError(status, data));
callback(null, data);
}).error(defaultErrorHandler(callback));
};
Client.prototype.reboot = function (callback) {
$http.post(client.apiOrigin + '/api/v1/cloudron/reboot', { }).success(function(data, status) {
if (status !== 202 || typeof data !== 'object') return callback(new ClientError(status, data));
callback(null, data);
}).error(defaultErrorHandler(callback));
};
Client.prototype.migrate = function (size, region, password, callback) {
$http.post(client.apiOrigin + '/api/v1/cloudron/migrate', { size: size, region: region, password: password }).success(function(data, status) {
if (status !== 202 || typeof data !== 'object') return callback(new ClientError(status, data));
callback(null, data);
}).error(defaultErrorHandler(callback));
};
Client.prototype.setCertificate = function (certificateFile, keyFile, callback) {
$http.post(client.apiOrigin + '/api/v1/settings/certificate', { cert: certificateFile, key: keyFile }).success(function(data, status) {
if (status !== 202) return callback(new ClientError(status, data));
callback(null);
}).error(defaultErrorHandler(callback));
};
Client.prototype.setAdminCertificate = function (certificateFile, keyFile, callback) {
$http.post(client.apiOrigin + '/api/v1/settings/admin_certificate', { cert: certificateFile, key: keyFile }).success(function(data, status) {
if (status !== 202) return callback(new ClientError(status, data));
callback(null);
}).error(defaultErrorHandler(callback));
};
Client.prototype.graphs = function (targets, from, callback) {
var config = {
params: {
target: targets,
format: 'json',
from: from
}
};
$http.get(client.apiOrigin + '/api/v1/cloudron/graphs', config).success(function (data, status) {
if (status !== 200 || typeof data !== 'object') return callback(new ClientError(status, data));
callback(null, data);
}).error(defaultErrorHandler(callback));
};
Client.prototype.feedback = function (type, subject, description, callback) {
var data = {
type: type,
subject: subject,
description: description
};
$http.post(client.apiOrigin + '/api/v1/cloudron/feedback', data).success(function (data, status) {
if (status !== 201) return callback(new ClientError(status, data));
callback(null);
}).error(defaultErrorHandler(callback));
};
Client.prototype.createMailbox = function (name, callback) {
var data = {
name: name
};
$http.post(client.apiOrigin + '/api/v1/mailboxes', data).success(function(data, status) {
if (status !== 201 || typeof data !== 'object') return callback(new ClientError(status, data));
callback(null, data);
}).error(defaultErrorHandler(callback));
};
Client.prototype.removeMailbox = function (name, callback) {
var data = {
name: name
};
$http({ method: 'DELETE', url: client.apiOrigin + '/api/v1/mailboxes/' + name, data: data, headers: { 'Content-Type': 'application/json' }}).success(function(data, status) {
if (status !== 204) return callback(new ClientError(status, data));
callback(null);
}).error(defaultErrorHandler(callback));
};
Client.prototype.setAliases = function (name, aliases, callback) {
var data = {
aliases: aliases
};
$http.put(client.apiOrigin + '/api/v1/mailboxes/' + name + '/aliases', data).success(function(data, status) {
if (status !== 200) return callback(new ClientError(status, data));
callback(null);
}).error(defaultErrorHandler(callback));
};
Client.prototype.createUser = function (username, email, displayName, sendInvite, callback) {
var data = {
username: username,
email: email,
displayName: displayName,
invite: !!sendInvite
};
$http.post(client.apiOrigin + '/api/v1/users', data).success(function(data, status) {
if (status !== 201 || typeof data !== 'object') return callback(new ClientError(status, data));
callback(null, data);
}).error(defaultErrorHandler(callback));
};
Client.prototype.updateUser = function (user, callback) {
var data = {
email: user.email,
displayName: user.displayName
};
$http.post(client.apiOrigin + '/api/v1/users/' + user.id, data).success(function(data, status) {
if (status !== 204) return callback(new ClientError(status, data));
callback(null);
}).error(defaultErrorHandler(callback));
};
Client.prototype.removeUser = function (userId, password, callback) {
var data = {
password: password
};
$http({ method: 'DELETE', url: client.apiOrigin + '/api/v1/users/' + userId, data: data, headers: { 'Content-Type': 'application/json' }}).success(function(data, status) {
if (status !== 204) return callback(new ClientError(status, data));
callback(null);
}).error(defaultErrorHandler(callback));
};
Client.prototype.changePassword = function (currentPassword, newPassword, callback) {
var data = {
password: currentPassword,
newPassword: newPassword
};
$http.post(client.apiOrigin + '/api/v1/profile/password', data).success(function(data, status) {
if (status !== 204) return callback(new ClientError(status, data));
callback(null, data);
}).error(defaultErrorHandler(callback));
};
Client.prototype.refreshUserInfo = function (callback) {
var that = this;
callback = typeof callback === 'function' ? callback : function () {};
this.userInfo(function (error, result) {
if (error) return callback(error);
that.setUserInfo(result);
callback(null);
});
};
Client.prototype.refreshConfig = function (callback) {
var that = this;
callback = typeof callback === 'function' ? callback : function () {};
this.config(function (error, result) {
if (error) return callback(error);
that.setConfig(result);
callback(null);
});
};
Client.prototype.appPostProcess = function (app) {
// calculate the icon paths
var icons = this.getAppIconUrls(app);
app.iconUrl = icons.cloudron;
app.iconUrlStore = icons.store;
// extract progress percentage
var installationProgress = app.installationProgress || '';
var progress = parseInt(installationProgress.split(',')[0]);
if (isNaN(progress)) progress = 0;
app.progress = progress;
return app;
};
Client.prototype.refreshInstalledApps = function (callback) {
var that = this;
callback = typeof callback === 'function' ? callback : function () {};
this.getApps(function (error, apps) {
if (error) return callback(error);
// insert or update new apps
apps.forEach(function (app) {
var found = false;
for (var i = 0; i < that._installedApps.length; ++i) {
if (that._installedApps[i].id === app.id) {
found = i;
break;
}
}
var tmp = {};
angular.copy(app, tmp);
that.appPostProcess(tmp);
// only replace if the app is already known
if (found !== false) {
angular.copy(tmp, that._installedApps[found]);
} else {
that._installedApps.push(tmp);
}
});
// filter out old entries, going backwards to allow splicing
for(var i = that._installedApps.length - 1; i >= 0; --i) {
if (!apps.some(function (elem) { return (elem.id === that._installedApps[i].id); })) {
that._installedApps.splice(i, 1);
}
}
callback(null);
});
};
Client.prototype.login = function () {
this.setToken(null);
this._userInfo = {};
var callbackURL = window.location.protocol + '//' + window.location.host + '/login_callback.html';
var scope = 'root,profile,apps';
// generate a state id to protect agains csrf
var state = Math.floor((1 + Math.random()) * 0x1000000000000).toString(16).substring(1);
window.localStorage.oauth2State = state;
// stash for further use in login_callback
window.localStorage.returnTo = '/' + window.location.hash;
window.location.href = this.apiOrigin + '/api/v1/oauth/dialog/authorize?response_type=token&client_id=' + this._clientId + '&redirect_uri=' + callbackURL + '&scope=' + scope + '&state=' + state;
};
Client.prototype.logout = function () {
this.setToken(null);
this._userInfo = {};
// logout from OAuth session
var origin = window.location.protocol + "//" + window.location.host;
window.location.href = this.apiOrigin + '/api/v1/session/logout?redirect=' + origin;
};
Client.prototype.exchangeCodeForToken = function (code, callback) {
var data = {
grant_type: 'authorization_code',
code: code,
redirect_uri: window.location.protocol + '//' + window.location.host,
client_id: this._clientId,
client_secret: this._clientSecret
};
$http.post(client.apiOrigin + '/api/v1/oauth/token?response_type=token&client_id=' + this._clientId, data).success(function(data, status) {
if (status !== 200 || typeof data !== 'object') return callback(new ClientError(status, data));
callback(null, data.access_token);
}).error(defaultErrorHandler(callback));
};
Client.prototype.enoughResourcesAvailable = function (app) {
var needed = app.manifest.memoryLimit || DEFAULT_MEMORY_LIMIT; // RAM+Swap
var used = this.getInstalledApps().reduce(function (prev, cur) { return prev + (cur.manifest.memoryLimit || DEFAULT_MEMORY_LIMIT); }, 0);
var roundedMemory = Math.round(this.getConfig().memory / (1024 * 1024 * 1024)) * 1024 * 1024 * 1024; // round to nearest GB
var totalMemory = roundedMemory * 1.2; // box-setup.sh creates equal amount of swap. 1.2 factor is arbitrary
var available = (totalMemory || 0) - used;
return (available - needed) >= 0;
};
Client.prototype.setShowTutorial = function (show, callback) {
var data = { showTutorial: show };
$http.post(client.apiOrigin + '/api/v1/profile/tutorial', data).success(function (data, status) {
if (status !== 204) return callback(new ClientError(status, data));
callback(null);
}).error(defaultErrorHandler(callback));
};
client = new Client();
return client;
}]);