diff --git a/src/error.html b/src/error.html deleted file mode 100644 index b12dba52e..000000000 --- a/src/error.html +++ /dev/null @@ -1,91 +0,0 @@ - - - - - - - Cloudron Error - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- -

Cloudron

-
-

Something has gone wrong

- Back to the dashboard -
-
-

If you are the server administrator, follow the troubleshooting guide.

-
-
-
- - - - - - diff --git a/src/js/client.js b/src/js/client.js index def155d84..3b183bde1 100644 --- a/src/js/client.js +++ b/src/js/client.js @@ -5,7 +5,7 @@ /* global EventSource:false */ /* global asyncForEach:false */ -angular.module('Application').service('Client', ['$http', '$interval', 'md5', 'Notification', function ($http, $interval, md5, Notification) { +angular.module('Application').service('Client', ['$http', '$interval', '$timeout', 'md5', 'Notification', function ($http, $interval, $timeout, md5, Notification) { var client = null; // variable available only here to avoid this._property pattern @@ -190,6 +190,13 @@ angular.module('Application').service('Client', ['$http', '$interval', 'md5', 'N Notification.error({ title: 'Cloudron Error', message: message }); }; + // handles application startup errors, mostly only when dashboard is loaded and api endpoint is down + Client.prototype.initError = function (error, initFunction) { + console.error('Application startup error', error); + + $timeout(initFunction, 5000); // we will try to re-init the app + }; + Client.prototype.clearNotifications = function () { Notification.clearAll(); }; diff --git a/src/js/logs.js b/src/js/logs.js index 5861ea711..0b87e2e5c 100644 --- a/src/js/logs.js +++ b/src/js/logs.js @@ -1,11 +1,13 @@ 'use strict'; +/* global angular */ /* global moment */ +/* global $ */ // create main application module var app = angular.module('Application', ['angular-md5', 'ui-notification']); -app.controller('LogsController', ['$scope', '$timeout', '$location', 'Client', function ($scope, $timeout, $location, Client) { +app.controller('LogsController', ['$scope', 'Client', function ($scope, Client) { 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; }, {}); $scope.initialized = false; @@ -16,11 +18,6 @@ app.controller('LogsController', ['$scope', '$timeout', '$location', 'Client', f $scope.selectedAppInfo = null; $scope.selectedTaskInfo = null; - $scope.error = function (error) { - console.error(error); - window.location.href = '/error.html'; - }; - function ab2str(buf) { return String.fromCharCode.apply(null, new Uint16Array(buf)); } @@ -143,43 +140,48 @@ app.controller('LogsController', ['$scope', '$timeout', '$location', 'Client', f } } - Client.getStatus(function (error, status) { - if (error) return $scope.error(error); + function init() { - if (!status.activated) { - console.log('Not activated yet, redirecting', status); - window.location.href = '/'; - return; - } + Client.getStatus(function (error, status) { + if (error) return Client.initError(error, init); - // check version and force reload if needed - if (!localStorage.version) { - localStorage.version = status.version; - } else if (localStorage.version !== status.version) { - localStorage.version = status.version; - window.location.reload(true); - } + if (!status.activated) { + console.log('Not activated yet, redirecting', status); + window.location.href = '/'; + return; + } - console.log('Running log version ', localStorage.version); + // check version and force reload if needed + if (!localStorage.version) { + localStorage.version = status.version; + } else if (localStorage.version !== status.version) { + localStorage.version = status.version; + window.location.reload(true); + } - // get user profile as the first thing. this populates the "scope" and affects subsequent API calls - Client.refreshUserInfo(function (error) { - if (error) return $scope.error(error); + console.log('Running log version ', localStorage.version); - Client.refreshConfig(function (error) { - if (error) return $scope.error(error); + // get user profile as the first thing. this populates the "scope" and affects subsequent API calls + Client.refreshUserInfo(function (error) { + if (error) return Client.initError(error, init); - select({ id: search.id, taskId: search.taskId, appId: search.appId, crashId: search.crashId }, function (error) { - if (error) return $scope.error(error); + Client.refreshConfig(function (error) { + if (error) return Client.initError(error, init); - // now mark the Client to be ready - Client.setReady(); + select({ id: search.id, taskId: search.taskId, appId: search.appId, crashId: search.crashId }, function (error) { + if (error) return Client.initError(error, init); - $scope.initialized = true; + // now mark the Client to be ready + Client.setReady(); - showLogs(); + $scope.initialized = true; + + showLogs(); + }); }); }); }); - }); + } + + init(); }]); diff --git a/src/js/main.js b/src/js/main.js index bd71c3345..bf4b37bb4 100644 --- a/src/js/main.js +++ b/src/js/main.js @@ -1,7 +1,7 @@ 'use strict'; -/* global angular:false */ -/* global $:false */ +/* global angular */ +/* global $ */ angular.module('Application').controller('MainController', ['$scope', '$route', '$timeout', '$location', 'Client', function ($scope, $route, $timeout, $location, Client) { $scope.initialized = false; // used to animate the UI @@ -26,11 +26,6 @@ angular.module('Application').controller('MainController', ['$scope', '$route', Client.logout(); }; - $scope.error = function (error) { - console.error(error); - window.location.href = '/error.html'; - }; - $scope.waitingForPlanSelection = false; $('#setupSubscriptionModal').on('hide.bs.modal', function () { $scope.waitingForPlanSelection = false; @@ -95,68 +90,70 @@ angular.module('Application').controller('MainController', ['$scope', '$route', $scope.notifications = $scope.notifications.filter(function (n) { return n.id !== notificationId; }); }; - Client.getStatus(function (error, status) { - if (error) return $scope.error(error); + function init() { + Client.getStatus(function (error, status) { + if (error) return Client.initError(error, init); - // WARNING if anything about the routing is changed here test these use-cases: - // - // 1. Caas - // 3. selfhosted restore - // 4. local development with gulp develop + // WARNING if anything about the routing is changed here test these use-cases: + // + // 1. Caas + // 3. selfhosted restore + // 4. local development with gulp develop - if (!status.activated) { - console.log('Not activated yet, redirecting', status); - if (status.restore.active || status.restore.errorMessage) { // show the error message in restore page - window.location.href = '/restore.html'; - } else { - window.location.href = status.adminFqdn ? '/setup.html' : '/setupdns.html'; + if (!status.activated) { + console.log('Not activated yet, redirecting', status); + if (status.restore.active || status.restore.errorMessage) { // show the error message in restore page + window.location.href = '/restore.html'; + } else { + window.location.href = status.adminFqdn ? '/setup.html' : '/setupdns.html'; + } + return; } - return; - } - // support local development with localhost check - if (window.location.hostname !== status.adminFqdn && window.location.hostname !== 'localhost') { - // user is accessing by IP or by the old admin location (pre-migration) - window.location.href = '/setupdns.html'; - return; - } + // support local development with localhost check + if (window.location.hostname !== status.adminFqdn && window.location.hostname !== 'localhost') { + // user is accessing by IP or by the old admin location (pre-migration) + window.location.href = '/setupdns.html'; + return; + } - $scope.status = status; + $scope.status = status; - // check version and force reload if needed - if (!localStorage.version) { - localStorage.version = status.version; - } else if (localStorage.version !== status.version) { - localStorage.version = status.version; - window.location.reload(true); - } + // check version and force reload if needed + if (!localStorage.version) { + localStorage.version = status.version; + } else if (localStorage.version !== status.version) { + localStorage.version = status.version; + window.location.reload(true); + } - console.log('Running dashboard version ', localStorage.version); + console.log('Running dashboard version ', localStorage.version); - // get user profile as the first thing. this populates the "scope" and affects subsequent API calls - Client.refreshUserInfo(function (error) { - if (error) return $scope.error(error); + // get user profile as the first thing. this populates the "scope" and affects subsequent API calls + Client.refreshUserInfo(function (error) { + if (error) return Client.initError(error, init); - Client.refreshConfig(function (error) { - if (error) return $scope.error(error); + Client.refreshConfig(function (error) { + if (error) return Client.initError(error, init); - Client.refreshInstalledApps(function (error) { - if (error) return $scope.error(error); + Client.refreshInstalledApps(function (error) { + if (error) return Client.initError(error, init); - // now mark the Client to be ready - Client.setReady(); + // now mark the Client to be ready + Client.setReady(); - $scope.config = Client.getConfig(); + $scope.config = Client.getConfig(); - $scope.initialized = true; + $scope.initialized = true; - refreshNotifications(); + refreshNotifications(); - $scope.updateSubscriptionStatus(); + $scope.updateSubscriptionStatus(); + }); }); }); }); - }); + } Client.onConfig(function (config) { if (config.cloudronName) { @@ -164,6 +161,8 @@ angular.module('Application').controller('MainController', ['$scope', '$route', } }); + init(); + // setup all the dialog focus handling ['updateModal'].forEach(function (id) { $('#' + id).on('shown.bs.modal', function () { diff --git a/src/js/restore.js b/src/js/restore.js index b52ae1800..b99260062 100644 --- a/src/js/restore.js +++ b/src/js/restore.js @@ -1,6 +1,8 @@ 'use strict'; +/* global angular */ /* global tld */ +/* global $ */ // create main application module var app = angular.module('Application', ['angular-md5', 'ui-notification']); @@ -11,9 +13,10 @@ app.filter('zoneName', function () { }; }); -app.controller('RestoreController', ['$scope', '$http', 'Client', function ($scope, $http, Client) { +app.controller('RestoreController', ['$scope', 'Client', function ($scope, Client) { 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; }, {}); + $scope.client = Client; $scope.busy = false; $scope.error = {}; $scope.message = ''; // progress @@ -261,22 +264,23 @@ app.controller('RestoreController', ['$scope', '$http', 'Client', function ($sco document.getElementById('gcsKeyFileInput').onchange = readFileLocally($scope.gcsKey, 'content', 'keyFileName'); - Client.getStatus(function (error, status) { - if (error) { - window.location.href = '/error.html'; - return; - } + function init() { + Client.getStatus(function (error, status) { + if (error) return Client.initError(error, init); - if (status.restore.active) return waitForRestore(); + if (status.restore.active) return waitForRestore(); - if (status.restore.errorMessage) $scope.error.generic = status.restore.errorMessage; + if (status.restore.errorMessage) $scope.error.generic = status.restore.errorMessage; - if (status.activated) { - window.location.href = '/'; - return; - } + if (status.activated) { + window.location.href = '/'; + return; + } - $scope.instanceId = search.instanceId; - $scope.initialized = true; - }); + $scope.instanceId = search.instanceId; + $scope.initialized = true; + }); + } + + init(); }]); diff --git a/src/js/setup.js b/src/js/setup.js index 1bc001560..d8b0bff07 100644 --- a/src/js/setup.js +++ b/src/js/setup.js @@ -1,12 +1,16 @@ 'use strict'; +/* global angular */ +/* global $ */ + // create main application module var app = angular.module('Application', ['angular-md5', 'ui-notification', 'ui.bootstrap']); -app.controller('SetupController', ['$scope', '$http', 'Client', function ($scope, $http, Client) { +app.controller('SetupController', ['$scope', 'Client', function ($scope, Client) { // Stupid angular location provider either wants html5 location mode or not, do the query parsing on my own 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; }, {}); + $scope.client = Client; $scope.initialized = false; $scope.busy = false; $scope.account = { @@ -43,40 +47,41 @@ app.controller('SetupController', ['$scope', '$http', 'Client', function ($scope }); }; - Client.getStatus(function (error, status) { - if (error) { - window.location.href = '/error.html'; - return; - } + function init() { + Client.getStatus(function (error, status) { + if (error) return Client.initError(error, init); - // if we are here from the ip first go to the real domain if already setup - if (status.adminFqdn && status.adminFqdn !== window.location.hostname) { - window.location.href = 'https://' + status.adminFqdn + '/setup.html'; - return; - } + // if we are here from the ip first go to the real domain if already setup + if (status.adminFqdn && status.adminFqdn !== window.location.hostname) { + window.location.href = 'https://' + status.adminFqdn + '/setup.html'; + return; + } - // if we don't have a domain yet, first go to domain setup - if (!status.adminFqdn) { - window.location.href = '/setupdns.html'; - return; - } + // if we don't have a domain yet, first go to domain setup + if (!status.adminFqdn) { + window.location.href = '/setupdns.html'; + return; + } - if (status.activated) { - window.location.href = '/'; - return; - } + if (status.activated) { + window.location.href = '/'; + return; + } - $scope.account.email = search.email || $scope.account.email; - $scope.account.displayName = search.displayName || $scope.account.displayName; - $scope.account.requireEmail = !search.email; - $scope.provider = status.provider; - $scope.apiServerOrigin = status.apiServerOrigin; + $scope.account.email = search.email || $scope.account.email; + $scope.account.displayName = search.displayName || $scope.account.displayName; + $scope.account.requireEmail = !search.email; + $scope.provider = status.provider; + $scope.apiServerOrigin = status.apiServerOrigin; - $scope.initialized = true; + $scope.initialized = true; - // Ensure we have a good autofocus - setTimeout(function () { - $(document).find("[autofocus]:first").focus(); - }, 250); - }); + // Ensure we have a good autofocus + setTimeout(function () { + $(document).find("[autofocus]:first").focus(); + }, 250); + }); + } + + init(); }]); diff --git a/src/logs.html b/src/logs.html index a4242810e..e91ce9326 100644 --- a/src/logs.html +++ b/src/logs.html @@ -49,6 +49,8 @@ +
Cloudron is offline. Reconnecting...
+
diff --git a/src/restore.html b/src/restore.html index 389e74338..a57aba263 100644 --- a/src/restore.html +++ b/src/restore.html @@ -37,6 +37,8 @@ +
Cloudron is offline. Reconnecting...
+
diff --git a/src/setup.html b/src/setup.html index 082fd0e8e..4f759d5b6 100644 --- a/src/setup.html +++ b/src/setup.html @@ -37,6 +37,8 @@ +
Cloudron is offline. Reconnecting...
+