Do not redirect to error.html if the angular main application fails to init
We now only show the offline banner and retry the application init until box comes back up
This commit is contained in:
@@ -1,91 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html ng-app="Application" ng-controller="Controller">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height" />
|
|
||||||
|
|
||||||
<title> Cloudron Error </title>
|
|
||||||
|
|
||||||
<link id="favicon" type="image/png" rel="icon" href="/api/v1/cloudron/avatar">
|
|
||||||
|
|
||||||
<!-- Theme CSS -->
|
|
||||||
<link type="text/css" rel="stylesheet" href="/theme.css">
|
|
||||||
|
|
||||||
<!-- external fonts and CSS -->
|
|
||||||
<link type="text/css" rel="stylesheet" href="/3rdparty/fontawesome/css/all.min.css">
|
|
||||||
|
|
||||||
<!-- jQuery-->
|
|
||||||
<script type="text/javascript" src="/3rdparty/js/jquery.min.js"></script>
|
|
||||||
|
|
||||||
<!-- Bootstrap Core JavaScript -->
|
|
||||||
<script type="text/javascript" src="/3rdparty/js/bootstrap.min.js"></script>
|
|
||||||
|
|
||||||
<!-- Angularjs scripts -->
|
|
||||||
<script type="text/javascript" src="/3rdparty/js/angular.min.js"></script>
|
|
||||||
<script type="text/javascript" src="/3rdparty/js/angular-loader.min.js"></script>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
// create main application module
|
|
||||||
var app = angular.module('Application', []);
|
|
||||||
|
|
||||||
app.controller('Controller', ['$scope', '$http', function ($scope, $http) {
|
|
||||||
$scope.errorMessage = '';
|
|
||||||
$scope.statusOk = false;
|
|
||||||
$scope.avatarUrl = '/api/v1/cloudron/avatar?' + Math.random();
|
|
||||||
|
|
||||||
var favicon = $('#favicon');
|
|
||||||
if (favicon) favicon.attr('href', $scope.avatarUrl);
|
|
||||||
|
|
||||||
// try to fetch the cloudron status
|
|
||||||
$http.get('/api/v1/cloudron/status').success(function(data, status) {
|
|
||||||
if (status !== 200 || typeof data !== 'object') return console.error(status, data);
|
|
||||||
$scope.statusOk = true;
|
|
||||||
}).error(function (data, status) {
|
|
||||||
console.error(status, data);
|
|
||||||
$scope.statusOk = false;
|
|
||||||
});
|
|
||||||
|
|
||||||
var search = window.location.search.slice(1).split('&').map(function (item) { return item.split('='); }).reduce(function (o, k) { o[k[0]] = k[1]; return o; }, {});
|
|
||||||
|
|
||||||
$scope.errorContext = search.errorContext || '';
|
|
||||||
}]);
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
padding-bottom: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
|
||||||
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body class="status-page">
|
|
||||||
|
|
||||||
<div class="wrapper">
|
|
||||||
<div class="content">
|
|
||||||
<img ng-src="{{avatarUrl}}" width="128" height="128" onerror="this.src = '/img/logo.png'"/>
|
|
||||||
<h1> Cloudron </h1>
|
|
||||||
<div>
|
|
||||||
<h3> <i class="far fa-frown fa-fw text-danger"></i> Something has gone wrong </h3>
|
|
||||||
<a class="btn btn-primary" href="/" ng-show="statusOk">Back to the dashboard</a>
|
|
||||||
<br/>
|
|
||||||
<br/>
|
|
||||||
<p>If you are the server administrator, follow the <a href="https://cloudron.io/documentation/troubleshooting/" target="_blank">troubleshooting guide</a>.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<footer class="text-center">
|
|
||||||
<span class="text-muted">©2019 <a href="https://cloudron.io" target="_blank">Cloudron</a></span>
|
|
||||||
<span class="text-muted"><a href="https://forum.cloudron.io" target="_blank">Forum <i class="fa fa-comments"></i></a></span>
|
|
||||||
</footer>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
/* global EventSource:false */
|
/* global EventSource:false */
|
||||||
/* global asyncForEach: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;
|
var client = null;
|
||||||
|
|
||||||
// variable available only here to avoid this._property pattern
|
// 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 });
|
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 () {
|
Client.prototype.clearNotifications = function () {
|
||||||
Notification.clearAll();
|
Notification.clearAll();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
/* global angular */
|
||||||
/* global moment */
|
/* global moment */
|
||||||
|
/* global $ */
|
||||||
|
|
||||||
// create main application module
|
// create main application module
|
||||||
var app = angular.module('Application', ['angular-md5', 'ui-notification']);
|
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; }, {});
|
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;
|
$scope.initialized = false;
|
||||||
@@ -16,11 +18,6 @@ app.controller('LogsController', ['$scope', '$timeout', '$location', 'Client', f
|
|||||||
$scope.selectedAppInfo = null;
|
$scope.selectedAppInfo = null;
|
||||||
$scope.selectedTaskInfo = null;
|
$scope.selectedTaskInfo = null;
|
||||||
|
|
||||||
$scope.error = function (error) {
|
|
||||||
console.error(error);
|
|
||||||
window.location.href = '/error.html';
|
|
||||||
};
|
|
||||||
|
|
||||||
function ab2str(buf) {
|
function ab2str(buf) {
|
||||||
return String.fromCharCode.apply(null, new Uint16Array(buf));
|
return String.fromCharCode.apply(null, new Uint16Array(buf));
|
||||||
}
|
}
|
||||||
@@ -143,8 +140,10 @@ app.controller('LogsController', ['$scope', '$timeout', '$location', 'Client', f
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
|
||||||
Client.getStatus(function (error, status) {
|
Client.getStatus(function (error, status) {
|
||||||
if (error) return $scope.error(error);
|
if (error) return Client.initError(error, init);
|
||||||
|
|
||||||
if (!status.activated) {
|
if (!status.activated) {
|
||||||
console.log('Not activated yet, redirecting', status);
|
console.log('Not activated yet, redirecting', status);
|
||||||
@@ -164,13 +163,13 @@ app.controller('LogsController', ['$scope', '$timeout', '$location', 'Client', f
|
|||||||
|
|
||||||
// get user profile as the first thing. this populates the "scope" and affects subsequent API calls
|
// get user profile as the first thing. this populates the "scope" and affects subsequent API calls
|
||||||
Client.refreshUserInfo(function (error) {
|
Client.refreshUserInfo(function (error) {
|
||||||
if (error) return $scope.error(error);
|
if (error) return Client.initError(error, init);
|
||||||
|
|
||||||
Client.refreshConfig(function (error) {
|
Client.refreshConfig(function (error) {
|
||||||
if (error) return $scope.error(error);
|
if (error) return Client.initError(error, init);
|
||||||
|
|
||||||
select({ id: search.id, taskId: search.taskId, appId: search.appId, crashId: search.crashId }, function (error) {
|
select({ id: search.id, taskId: search.taskId, appId: search.appId, crashId: search.crashId }, function (error) {
|
||||||
if (error) return $scope.error(error);
|
if (error) return Client.initError(error, init);
|
||||||
|
|
||||||
// now mark the Client to be ready
|
// now mark the Client to be ready
|
||||||
Client.setReady();
|
Client.setReady();
|
||||||
@@ -182,4 +181,7 @@ app.controller('LogsController', ['$scope', '$timeout', '$location', 'Client', f
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
init();
|
||||||
}]);
|
}]);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
/* global angular:false */
|
/* global angular */
|
||||||
/* global $:false */
|
/* global $ */
|
||||||
|
|
||||||
angular.module('Application').controller('MainController', ['$scope', '$route', '$timeout', '$location', 'Client', function ($scope, $route, $timeout, $location, Client) {
|
angular.module('Application').controller('MainController', ['$scope', '$route', '$timeout', '$location', 'Client', function ($scope, $route, $timeout, $location, Client) {
|
||||||
$scope.initialized = false; // used to animate the UI
|
$scope.initialized = false; // used to animate the UI
|
||||||
@@ -26,11 +26,6 @@ angular.module('Application').controller('MainController', ['$scope', '$route',
|
|||||||
Client.logout();
|
Client.logout();
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.error = function (error) {
|
|
||||||
console.error(error);
|
|
||||||
window.location.href = '/error.html';
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.waitingForPlanSelection = false;
|
$scope.waitingForPlanSelection = false;
|
||||||
$('#setupSubscriptionModal').on('hide.bs.modal', function () {
|
$('#setupSubscriptionModal').on('hide.bs.modal', function () {
|
||||||
$scope.waitingForPlanSelection = false;
|
$scope.waitingForPlanSelection = false;
|
||||||
@@ -95,8 +90,9 @@ angular.module('Application').controller('MainController', ['$scope', '$route',
|
|||||||
$scope.notifications = $scope.notifications.filter(function (n) { return n.id !== notificationId; });
|
$scope.notifications = $scope.notifications.filter(function (n) { return n.id !== notificationId; });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function init() {
|
||||||
Client.getStatus(function (error, status) {
|
Client.getStatus(function (error, status) {
|
||||||
if (error) return $scope.error(error);
|
if (error) return Client.initError(error, init);
|
||||||
|
|
||||||
// WARNING if anything about the routing is changed here test these use-cases:
|
// WARNING if anything about the routing is changed here test these use-cases:
|
||||||
//
|
//
|
||||||
@@ -135,13 +131,13 @@ angular.module('Application').controller('MainController', ['$scope', '$route',
|
|||||||
|
|
||||||
// get user profile as the first thing. this populates the "scope" and affects subsequent API calls
|
// get user profile as the first thing. this populates the "scope" and affects subsequent API calls
|
||||||
Client.refreshUserInfo(function (error) {
|
Client.refreshUserInfo(function (error) {
|
||||||
if (error) return $scope.error(error);
|
if (error) return Client.initError(error, init);
|
||||||
|
|
||||||
Client.refreshConfig(function (error) {
|
Client.refreshConfig(function (error) {
|
||||||
if (error) return $scope.error(error);
|
if (error) return Client.initError(error, init);
|
||||||
|
|
||||||
Client.refreshInstalledApps(function (error) {
|
Client.refreshInstalledApps(function (error) {
|
||||||
if (error) return $scope.error(error);
|
if (error) return Client.initError(error, init);
|
||||||
|
|
||||||
// now mark the Client to be ready
|
// now mark the Client to be ready
|
||||||
Client.setReady();
|
Client.setReady();
|
||||||
@@ -157,6 +153,7 @@ angular.module('Application').controller('MainController', ['$scope', '$route',
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
Client.onConfig(function (config) {
|
Client.onConfig(function (config) {
|
||||||
if (config.cloudronName) {
|
if (config.cloudronName) {
|
||||||
@@ -164,6 +161,8 @@ angular.module('Application').controller('MainController', ['$scope', '$route',
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
init();
|
||||||
|
|
||||||
// setup all the dialog focus handling
|
// setup all the dialog focus handling
|
||||||
['updateModal'].forEach(function (id) {
|
['updateModal'].forEach(function (id) {
|
||||||
$('#' + id).on('shown.bs.modal', function () {
|
$('#' + id).on('shown.bs.modal', function () {
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
/* global angular */
|
||||||
/* global tld */
|
/* global tld */
|
||||||
|
/* global $ */
|
||||||
|
|
||||||
// create main application module
|
// create main application module
|
||||||
var app = angular.module('Application', ['angular-md5', 'ui-notification']);
|
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; }, {});
|
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.busy = false;
|
||||||
$scope.error = {};
|
$scope.error = {};
|
||||||
$scope.message = ''; // progress
|
$scope.message = ''; // progress
|
||||||
@@ -261,11 +264,9 @@ app.controller('RestoreController', ['$scope', '$http', 'Client', function ($sco
|
|||||||
|
|
||||||
document.getElementById('gcsKeyFileInput').onchange = readFileLocally($scope.gcsKey, 'content', 'keyFileName');
|
document.getElementById('gcsKeyFileInput').onchange = readFileLocally($scope.gcsKey, 'content', 'keyFileName');
|
||||||
|
|
||||||
|
function init() {
|
||||||
Client.getStatus(function (error, status) {
|
Client.getStatus(function (error, status) {
|
||||||
if (error) {
|
if (error) return Client.initError(error, init);
|
||||||
window.location.href = '/error.html';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status.restore.active) return waitForRestore();
|
if (status.restore.active) return waitForRestore();
|
||||||
|
|
||||||
@@ -279,4 +280,7 @@ app.controller('RestoreController', ['$scope', '$http', 'Client', function ($sco
|
|||||||
$scope.instanceId = search.instanceId;
|
$scope.instanceId = search.instanceId;
|
||||||
$scope.initialized = true;
|
$scope.initialized = true;
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
init();
|
||||||
}]);
|
}]);
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
/* global angular */
|
||||||
|
/* global $ */
|
||||||
|
|
||||||
// create main application module
|
// create main application module
|
||||||
var app = angular.module('Application', ['angular-md5', 'ui-notification', 'ui.bootstrap']);
|
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
|
// 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; }, {});
|
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.initialized = false;
|
||||||
$scope.busy = false;
|
$scope.busy = false;
|
||||||
$scope.account = {
|
$scope.account = {
|
||||||
@@ -43,11 +47,9 @@ app.controller('SetupController', ['$scope', '$http', 'Client', function ($scope
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function init() {
|
||||||
Client.getStatus(function (error, status) {
|
Client.getStatus(function (error, status) {
|
||||||
if (error) {
|
if (error) return Client.initError(error, init);
|
||||||
window.location.href = '/error.html';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we are here from the ip first go to the real domain if already setup
|
// if we are here from the ip first go to the real domain if already setup
|
||||||
if (status.adminFqdn && status.adminFqdn !== window.location.hostname) {
|
if (status.adminFqdn && status.adminFqdn !== window.location.hostname) {
|
||||||
@@ -79,4 +81,7 @@ app.controller('SetupController', ['$scope', '$http', 'Client', function ($scope
|
|||||||
$(document).find("[autofocus]:first").focus();
|
$(document).find("[autofocus]:first").focus();
|
||||||
}, 250);
|
}, 250);
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
init();
|
||||||
}]);
|
}]);
|
||||||
|
|||||||
@@ -49,6 +49,8 @@
|
|||||||
|
|
||||||
<body class="logs">
|
<body class="logs">
|
||||||
|
|
||||||
|
<div class="offline-banner animateMe" ng-show="client.offline" ng-cloak><i class="fa fa-circle-notch fa-spin"></i> Cloudron is offline. Reconnecting...</div>
|
||||||
|
|
||||||
<div class="animateMe ng-hide layout-root" ng-show="initialized">
|
<div class="animateMe ng-hide layout-root" ng-show="initialized">
|
||||||
|
|
||||||
<div class="logs-controls">
|
<div class="logs-controls">
|
||||||
|
|||||||
@@ -37,6 +37,8 @@
|
|||||||
|
|
||||||
<body class="setup" ng-app="Application" ng-controller="RestoreController">
|
<body class="setup" ng-app="Application" ng-controller="RestoreController">
|
||||||
|
|
||||||
|
<div class="offline-banner animateMe" ng-show="client.offline" ng-cloak><i class="fa fa-circle-notch fa-spin"></i> Cloudron is offline. Reconnecting...</div>
|
||||||
|
|
||||||
<div class="main-container ng-cloak text-center" ng-show="busy">
|
<div class="main-container ng-cloak text-center" ng-show="busy">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6 col-md-offset-3">
|
<div class="col-md-6 col-md-offset-3">
|
||||||
|
|||||||
@@ -37,6 +37,8 @@
|
|||||||
|
|
||||||
<body class="setup" ng-app="Application" ng-controller="SetupController">
|
<body class="setup" ng-app="Application" ng-controller="SetupController">
|
||||||
|
|
||||||
|
<div class="offline-banner animateMe" ng-show="client.offline" ng-cloak><i class="fa fa-circle-notch fa-spin"></i> Cloudron is offline. Reconnecting...</div>
|
||||||
|
|
||||||
<div class="main-container ng-cloak text-center" ng-show="busy">
|
<div class="main-container ng-cloak text-center" ng-show="busy">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6 col-md-offset-3">
|
<div class="col-md-6 col-md-offset-3">
|
||||||
|
|||||||
Reference in New Issue
Block a user