diff --git a/static/css/quay.css b/static/css/quay.css index 4140f8170..e46fb2aa6 100644 --- a/static/css/quay.css +++ b/static/css/quay.css @@ -4955,3 +4955,24 @@ i.slack-icon { margin-bottom: 20px; padding-left: 22px; } + +.restart-required { + position: relative; + padding-left: 54px; +} + +.restart-required button { + float: right; + margin-top: 4px; +} + +.restart-required button i.fa { + margin-right: 6px; +} + +.restart-required i.fa-warning { + position: absolute; + top: 24px; + left: 16px; + font-size: 28px; +} diff --git a/static/js/app.js b/static/js/app.js index b5c346f1d..380324f8d 100644 --- a/static/js/app.js +++ b/static/js/app.js @@ -1047,12 +1047,35 @@ quayApp = angular.module('quay', quayDependencies, function($provide, cfpLoading } }; + var freshLoginInProgress = []; + var reject = function(msg) { + for (var i = 0; i < freshLoginInProgress.length; ++i) { + freshLoginInProgress[i].deferred.reject({'data': {'message': msg}}); + } + freshLoginInProgress = []; + }; + + var retry = function() { + for (var i = 0; i < freshLoginInProgress.length; ++i) { + freshLoginInProgress[i].retry(); + } + freshLoginInProgress = []; + }; + var freshLoginFailCheck = function(opName, opArgs) { return function(resp) { var deferred = $q.defer(); // If the error is a fresh login required, show the dialog. if (resp.status == 401 && resp.data['error_type'] == 'fresh_login_required') { + var retryOperation = function() { + apiService[opName].apply(apiService, opArgs).then(function(resp) { + deferred.resolve(resp); + }, function(resp) { + deferred.reject(resp); + }); + }; + var verifyNow = function() { var info = { 'password': $('#freshPassword').val() @@ -1062,19 +1085,27 @@ quayApp = angular.module('quay', quayDependencies, function($provide, cfpLoading // Conduct the sign in of the user. apiService.verifyUser(info).then(function() { - // On success, retry the operation. if it succeeds, then resolve the + // On success, retry the operations. if it succeeds, then resolve the // deferred promise with the result. Otherwise, reject the same. - apiService[opName].apply(apiService, opArgs).then(function(resp) { - deferred.resolve(resp); - }, function(resp) { - deferred.reject(resp); - }); + retry(); }, function(resp) { // Reject with the sign in error. - deferred.reject({'data': {'message': 'Invalid verification credentials'}}); + reject('Invalid verification credentials'); }); }; + // Add the retry call to the in progress list. If there is more than a single + // in progress call, we skip showing the dialog (since it has already been + // shown). + freshLoginInProgress.push({ + 'deferred': deferred, + 'retry': retryOperation + }) + + if (freshLoginInProgress.length > 1) { + return deferred.promise; + } + var box = bootbox.dialog({ "message": 'It has been more than a few minutes since you last logged in, ' + 'so please verify your password to perform this sensitive operation:' + @@ -1092,7 +1123,7 @@ quayApp = angular.module('quay', quayDependencies, function($provide, cfpLoading "label": "Cancel", "className": "btn-default", "callback": function() { - deferred.reject({'data': {'message': 'Verification canceled'}}); + reject('Verification canceled') } } } @@ -1244,6 +1275,40 @@ quayApp = angular.module('quay', quayDependencies, function($provide, cfpLoading return cookieService; }]); + $provide.factory('ContainerService', ['ApiService', '$timeout', + function(ApiService, $timeout) { + var containerService = {}; + containerService.restartContainer = function() { + ApiService.scShutdownContainer(null, null).then(function(resp) { + $timeout(function() { + containerService.checkStatus(); + }, 2000); + }, ApiService.errorDisplay('Cannot restart container. Please report this to support.')) + }; + + containerService.scheduleStatusCheck = function(callback) { + $timeout(function() { + containerService.checkStatus(callback); + }, 2000); + }; + + containerService.checkStatus = function(callback) { + var errorHandler = function(resp) { + if (resp.status == 404 || resp.status == 502) { + // Container has not yet come back up, so we schedule another check. + containerService.scheduleStatusCheck(callback); + return; + } + + return ApiService.errorDisplay('Cannot load status. Please report this to support')(resp); + }; + + ApiService.scRegistryStatus(null, null).then(callback, errorHandler, /* background */true); + }; + + return containerService; + }]); + $provide.factory('UserService', ['ApiService', 'CookieService', '$rootScope', 'Config', function(ApiService, CookieService, $rootScope, Config) { var userResponse = { diff --git a/static/js/controllers/setup.js b/static/js/controllers/setup.js index f5d1b941a..16a136629 100644 --- a/static/js/controllers/setup.js +++ b/static/js/controllers/setup.js @@ -1,4 +1,4 @@ -function SetupCtrl($scope, $timeout, ApiService, Features, UserService, AngularPollChannel, CoreDialog) { +function SetupCtrl($scope, $timeout, ApiService, Features, UserService, ContainerService, CoreDialog) { if (!Features.SUPER_USERS) { return; } @@ -102,6 +102,11 @@ function SetupCtrl($scope, $timeout, ApiService, Features, UserService, AngularP } }); + $scope.restartContainer = function(state) { + $scope.currentStep = state; + ContainerService.restartContainer(); + }; + $scope.showSuperuserPanel = function() { $('#setupModal').modal('hide'); window.location = '/superuser'; @@ -160,37 +165,6 @@ function SetupCtrl($scope, $timeout, ApiService, Features, UserService, AngularP CoreDialog.fatal(title, message); }; - $scope.restartContainer = function(restartState) { - $scope.currentStep = restartState; - ApiService.scShutdownContainer(null, null).then(function(resp) { - $timeout(function() { - $scope.checkStatus(); - }, 2000); - }, ApiService.errorDisplay('Cannot restart container. Please report this to support.')) - }; - - $scope.scheduleStatusCheck = function() { - $timeout(function() { - $scope.checkStatus(); - }, 3000); - }; - - $scope.checkStatus = function() { - var errorHandler = function(resp) { - if (resp.status == 404 || resp.status == 502) { - // Container has not yet come back up, so we schedule another check. - $scope.scheduleStatusCheck(); - return; - } - - return ApiService.errorDisplay('Cannot load status. Please report this to support')(resp); - }; - - ApiService.scRegistryStatus(null, null).then(function(resp) { - $scope.currentStep = resp['status']; - }, errorHandler, /* background */true); - }; - $scope.parseDbUri = function(value) { if (!value) { return null; } @@ -276,6 +250,12 @@ function SetupCtrl($scope, $timeout, ApiService, Features, UserService, AngularP }, ApiService.errorDisplay('Cannot validate database. Please report this to support')); }; + $scope.checkStatus = function() { + ContainerService.checkStatus(function(resp) { + $scope.currentStep = resp['status']; + }); + }; + // Load the initial status. $scope.checkStatus(); } \ No newline at end of file diff --git a/static/js/controllers/superuser.js b/static/js/controllers/superuser.js index 3efbd02d3..d3bbbaab2 100644 --- a/static/js/controllers/superuser.js +++ b/static/js/controllers/superuser.js @@ -1,4 +1,4 @@ -function SuperUserAdminCtrl($scope, $timeout, ApiService, Features, UserService, AngularPollChannel, CoreDialog) { +function SuperUserAdminCtrl($scope, $timeout, ApiService, Features, UserService, ContainerService, AngularPollChannel, CoreDialog) { if (!Features.SUPER_USERS) { return; } @@ -7,6 +7,7 @@ function SuperUserAdminCtrl($scope, $timeout, ApiService, Features, UserService, UserService.updateUserIn($scope); $scope.configStatus = null; + $scope.requiresRestart = null; $scope.logsCounter = 0; $scope.newUser = {}; $scope.createdUser = null; @@ -17,6 +18,10 @@ function SuperUserAdminCtrl($scope, $timeout, ApiService, Features, UserService, $scope.logsScrolled = false; $scope.csrf_token = window.__token; + $scope.configurationSaved = function() { + $scope.requiresRestart = true; + }; + $scope.showCreateUser = function() { $scope.createdUser = null; $('#createUserModal').modal('show'); @@ -183,9 +188,24 @@ function SuperUserAdminCtrl($scope, $timeout, ApiService, Features, UserService, }, ApiService.errorDisplay('Cannot send recovery email')) }; + $scope.restartContainer = function() { + $('#restartingContainerModal').modal({ + keyboard: false, + backdrop: 'static' + }); + + ContainerService.restartContainer(); + $timeout(function() { + $scope.checkStatus(); + }, 2000); + }; + $scope.checkStatus = function() { - ApiService.scRegistryStatus(null, null).then(function(resp) { + ContainerService.checkStatus(function(resp) { + $('#restartingContainerModal').modal('hide'); $scope.configStatus = resp['status']; + $scope.requiresRestart = resp['requires_restart']; + if ($scope.configStatus == 'ready') { $scope.loadUsers(); } else { @@ -197,7 +217,7 @@ function SuperUserAdminCtrl($scope, $timeout, ApiService, Features, UserService, var title = "Installation Incomplete"; CoreDialog.fatal(title, message); } - }, ApiService.errorDisplay('Cannot load status. Please report this to support'), /* background */true); + }); }; // Load the initial status. diff --git a/static/partials/super-user.html b/static/partials/super-user.html index eab840971..a48052d9b 100644 --- a/static/partials/super-user.html +++ b/static/partials/super-user.html @@ -1,6 +1,14 @@