mirror of
https://github.com/portainer/portainer.git
synced 2025-08-07 23:05:26 +02:00
feat(endpoints): add the ability to browse offline endpoints (#2253)
* feat(back): saved data in snapshot * feat(endpoints): adding interceptors to retrieve saved data on offline endpoints * feat(endpoints): offline dashboard working - need tests on offline views * refactor(endpoints): interceptors cleaning and saving/loading offline endpoints data in/from localstorage * feat(endpoints): browsing offline endpoints * feat(endpoints): removing all the link in offline mode - sidebar not working when switching between off and on modes w/ stateManager logic * feat(endpoints): endpoint status detection in real time * fix(endpoints): offline swarm endpoint are not accessible anymore * fix(endpoints): refactor message + disable offline browsing for an endpoint when no snapshot is available for it * fix(endpoints): adding timeout and enabling loading bar for offline requests * fix(endpoints): trying to access a down endpoint wont remove sidebar items if it fails * feat(endpoints): disable checkboxes on offline views for offline mode * feat(endpoints): updating endpoint status when detecting a change * refactor(host): moved offline status panel from engine view to new host view * fix(endpoints): missing endpoint update on ping from home view * fix(api): rework EndpointUpdate operation * refactor(offline): moved endpoint status to EndpointProvider and refactor the status-changed detection * fix(offline): moved status detection to callback on views -> prevent displaying the offline message when endpoint is back online on view change * fix(offline): offline message is now displayed online when browsing an offline endpoint * fix(offline): sidebar updates correctly on endpoint status change * fix(offline): offline panel not displayed and hidden on online mode * refactor(offline): rework of OfflineMode management * refactor(offline): extract information-panel for offlineMode into a component * refactor(offline): remove redundant binding of informationPanel + endpointStatusInterceptor patter as service * refactor(interceptors): moved interceptors pattern to service pattern * feat(stacks): prevent inspection of a stack in offline mode * feat(host): hide devices/disk panels in offline mode * feat(host): disable browse action in offline mode * refactor(home): remove comments
This commit is contained in:
parent
354fda31f1
commit
a61654a35d
59 changed files with 637 additions and 212 deletions
|
@ -6,7 +6,7 @@
|
|||
<i class="fa" ng-class="$ctrl.titleIcon" aria-hidden="true" style="margin-right: 2px;"></i> {{ $ctrl.titleText }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="actionBar">
|
||||
<div class="actionBar" ng-if="!$ctrl.offlineMode">
|
||||
<button type="button" class="btn btn-sm btn-danger"
|
||||
ng-disabled="$ctrl.state.selectedItemCount === 0" ng-click="$ctrl.removeAction($ctrl.state.selectedItems)">
|
||||
<i class="fa fa-trash-alt space-right" aria-hidden="true"></i>Remove
|
||||
|
@ -24,7 +24,7 @@
|
|||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<span class="md-checkbox">
|
||||
<span class="md-checkbox" ng-if="!$ctrl.offlineMode">
|
||||
<input id="select_all" type="checkbox" ng-model="$ctrl.state.selectAll" ng-change="$ctrl.selectAll()" />
|
||||
<label for="select_all"></label>
|
||||
</span>
|
||||
|
@ -54,11 +54,12 @@
|
|||
<tbody>
|
||||
<tr dir-paginate="item in ($ctrl.state.filteredDataSet = ($ctrl.dataset | filter:$ctrl.state.textFilter | orderBy:$ctrl.state.orderBy:$ctrl.state.reverseOrder | itemsPerPage: $ctrl.state.paginatedItemLimit))" ng-class="{active: item.Checked}">
|
||||
<td>
|
||||
<span class="md-checkbox">
|
||||
<span class="md-checkbox" ng-if="!$ctrl.offlineMode">
|
||||
<input id="select_{{ $index }}" type="checkbox" ng-model="item.Checked" ng-change="$ctrl.selectItem(item)" ng-disabled="item.External && item.Type === 2"/>
|
||||
<label for="select_{{ $index }}"></label>
|
||||
</span>
|
||||
<a ui-sref="portainer.stacks.stack({ name: item.Name, id: item.Id, type: item.Type, external: item.External })">{{ item.Name }}</a>
|
||||
<a ng-if="!$ctrl.offlineMode" ui-sref="portainer.stacks.stack({ name: item.Name, id: item.Id, type: item.Type, external: item.External })">{{ item.Name }}</a>
|
||||
<span ng-if="$ctrl.offlineMode">{{ item.Name }}</span>
|
||||
</td>
|
||||
<td>{{ item.Type === 1 ? 'Swarm' : 'Compose' }}</td>
|
||||
<td>
|
||||
|
|
|
@ -9,6 +9,7 @@ angular.module('portainer.app').component('stacksDatatable', {
|
|||
orderBy: '@',
|
||||
reverseOrder: '<',
|
||||
showOwnershipColumn: '<',
|
||||
removeAction: '<'
|
||||
removeAction: '<',
|
||||
offlineMode: '<'
|
||||
}
|
||||
});
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
<information-panel title-text="Offline mode" cant-dismiss="true">
|
||||
<span class="small">
|
||||
<p class="text-muted">
|
||||
<i class="fa fa-exclamation-circle orange-icon" aria-hidden="true" style="margin-right: 2px;"></i>
|
||||
This endpoint is currently offline (read-only). Data shown is based on the latest available snapshot.
|
||||
</p>
|
||||
</span>
|
||||
</information-panel>
|
|
@ -0,0 +1,4 @@
|
|||
angular.module('portainer.app').component('informationPanelOffline', {
|
||||
templateUrl: 'app/portainer/components/information-panel-offline/informationPanelOffline.html',
|
||||
transclude: true
|
||||
});
|
|
@ -2,7 +2,7 @@ angular.module('portainer.app').component('informationPanel', {
|
|||
templateUrl: 'app/portainer/components/information-panel/informationPanel.html',
|
||||
bindings: {
|
||||
titleText: '@',
|
||||
dismissAction: '&'
|
||||
dismissAction: '&?'
|
||||
},
|
||||
transclude: true
|
||||
});
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<span style="float: left;">
|
||||
{{ $ctrl.titleText }}
|
||||
</span>
|
||||
<span class="small" style="float: right;">
|
||||
<span class="small" style="float: right;" ng-if="$ctrl.dismissAction">
|
||||
<a ng-click="$ctrl.dismissAction()"><i class="fa fa-times"></i> dismiss</a>
|
||||
</span>
|
||||
</div>
|
||||
|
|
41
app/portainer/interceptors/endpointStatusInterceptor.js
Normal file
41
app/portainer/interceptors/endpointStatusInterceptor.js
Normal file
|
@ -0,0 +1,41 @@
|
|||
angular.module('portainer.app')
|
||||
.factory('EndpointStatusInterceptor', ['$q', '$injector', 'EndpointProvider', function ($q, $injector, EndpointProvider) {
|
||||
'use strict';
|
||||
var interceptor = {};
|
||||
|
||||
interceptor.response = responseInterceptor;
|
||||
interceptor.responseError = responseErrorInterceptor;
|
||||
|
||||
function canBeOffline(url) {
|
||||
return (_.startsWith(url, 'api/') && (
|
||||
_.includes(url, '/containers') ||
|
||||
_.includes(url, '/images') ||
|
||||
_.includes(url, '/volumes') ||
|
||||
_.includes(url, '/networks') ||
|
||||
_.includes(url, '/info') ||
|
||||
_.includes(url, '/version')
|
||||
));
|
||||
}
|
||||
|
||||
function responseInterceptor(response) {
|
||||
var EndpointService = $injector.get('EndpointService');
|
||||
var url = response.config.url;
|
||||
if (response.status === 200 && canBeOffline(url) && EndpointProvider.offlineMode()) {
|
||||
EndpointProvider.setOfflineMode(false);
|
||||
EndpointService.updateEndpoint(EndpointProvider.endpointID(), {Status: EndpointProvider.endpointStatusFromOfflineMode(false)});
|
||||
}
|
||||
return response || $q.when(response);
|
||||
}
|
||||
|
||||
function responseErrorInterceptor(rejection) {
|
||||
var EndpointService = $injector.get('EndpointService');
|
||||
var url = rejection.config.url;
|
||||
if ((rejection.status === 502 || rejection.status === -1) && canBeOffline(url) && !EndpointProvider.offlineMode()) {
|
||||
EndpointProvider.setOfflineMode(true);
|
||||
EndpointService.updateEndpoint(EndpointProvider.endpointID(), {Status: EndpointProvider.endpointStatusFromOfflineMode(true)});
|
||||
}
|
||||
return $q.reject(rejection);
|
||||
}
|
||||
|
||||
return interceptor;
|
||||
}]);
|
|
@ -7,19 +7,31 @@ angular.module('portainer.app')
|
|||
service.initialize = function() {
|
||||
var endpointID = LocalStorage.getEndpointID();
|
||||
var endpointPublicURL = LocalStorage.getEndpointPublicURL();
|
||||
var offlineMode = LocalStorage.getOfflineMode();
|
||||
|
||||
if (endpointID) {
|
||||
endpoint.ID = endpointID;
|
||||
}
|
||||
if (endpointPublicURL) {
|
||||
endpoint.PublicURL = endpointPublicURL;
|
||||
}
|
||||
if (offlineMode) {
|
||||
endpoint.OfflineMode = offlineMode;
|
||||
}
|
||||
};
|
||||
|
||||
service.clean = function() {
|
||||
endpoint = {};
|
||||
};
|
||||
|
||||
service.endpoint = function() {
|
||||
return endpoint;
|
||||
};
|
||||
|
||||
service.endpointID = function() {
|
||||
if (endpoint.ID === undefined) {
|
||||
endpoint.ID = LocalStorage.getEndpointID();
|
||||
}
|
||||
return endpoint.ID;
|
||||
};
|
||||
|
||||
|
@ -29,6 +41,9 @@ angular.module('portainer.app')
|
|||
};
|
||||
|
||||
service.endpointPublicURL = function() {
|
||||
if (endpoint.PublicURL === undefined) {
|
||||
endpoint.PublicURL = LocalStorage.getEndpointPublicURL();
|
||||
}
|
||||
return endpoint.PublicURL;
|
||||
};
|
||||
|
||||
|
@ -37,5 +52,40 @@ angular.module('portainer.app')
|
|||
LocalStorage.storeEndpointPublicURL(publicURL);
|
||||
};
|
||||
|
||||
service.endpoints = function() {
|
||||
return LocalStorage.getEndpoints();
|
||||
};
|
||||
|
||||
service.setEndpoints = function(data) {
|
||||
LocalStorage.storeEndpoints(data);
|
||||
};
|
||||
|
||||
service.offlineMode = function() {
|
||||
return endpoint.OfflineMode;
|
||||
};
|
||||
|
||||
service.endpointStatusFromOfflineMode = function(isOffline) {
|
||||
return isOffline ? 2 : 1;
|
||||
};
|
||||
|
||||
service.setOfflineMode = function(isOffline) {
|
||||
endpoint.OfflineMode = isOffline;
|
||||
LocalStorage.storeOfflineMode(isOffline);
|
||||
};
|
||||
|
||||
service.setOfflineModeFromStatus = function(status) {
|
||||
var isOffline = status !== 1;
|
||||
endpoint.OfflineMode = isOffline;
|
||||
LocalStorage.storeOfflineMode(isOffline);
|
||||
};
|
||||
|
||||
service.currentEndpoint = function() {
|
||||
var endpointId = endpoint.ID;
|
||||
var endpoints = LocalStorage.getEndpoints();
|
||||
return _.find(endpoints, function (item) {
|
||||
return item.Id === endpointId;
|
||||
});
|
||||
};
|
||||
|
||||
return service;
|
||||
}]);
|
||||
|
|
|
@ -4,9 +4,14 @@ function ExtensionManagerFactory($q, PluginService, SystemService, ExtensionServ
|
|||
'use strict';
|
||||
var service = {};
|
||||
|
||||
service.initEndpointExtensions = function() {
|
||||
service.initEndpointExtensions = function(endpoint) {
|
||||
var deferred = $q.defer();
|
||||
|
||||
if (endpoint.Status !== 1) {
|
||||
deferred.resolve([]);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
SystemService.version()
|
||||
.then(function success(data) {
|
||||
var endpointAPIVersion = parseFloat(data.ApiVersion);
|
||||
|
|
|
@ -14,6 +14,18 @@ angular.module('portainer.app')
|
|||
getEndpointPublicURL: function() {
|
||||
return localStorageService.get('ENDPOINT_PUBLIC_URL');
|
||||
},
|
||||
storeOfflineMode: function(isOffline) {
|
||||
localStorageService.set('ENDPOINT_OFFLINE_MODE', isOffline);
|
||||
},
|
||||
getOfflineMode: function() {
|
||||
return localStorageService.get('ENDPOINT_OFFLINE_MODE');
|
||||
},
|
||||
storeEndpoints: function(data) {
|
||||
localStorageService.set('ENDPOINTS_DATA', data);
|
||||
},
|
||||
getEndpoints: function() {
|
||||
return localStorageService.get('ENDPOINTS_DATA');
|
||||
},
|
||||
storeEndpointState: function(state) {
|
||||
localStorageService.set('ENDPOINT_STATE', state);
|
||||
},
|
||||
|
|
|
@ -135,11 +135,11 @@ function StateManagerFactory($q, SystemService, InfoHelper, LocalStorage, Settin
|
|||
return extensions;
|
||||
}
|
||||
|
||||
manager.updateEndpointState = function(name, type, extensions) {
|
||||
manager.updateEndpointState = function(endpoint, extensions) {
|
||||
var deferred = $q.defer();
|
||||
|
||||
if (type === 3) {
|
||||
state.endpoint.name = name;
|
||||
if (endpoint.Type === 3) {
|
||||
state.endpoint.name = endpoint.Name;
|
||||
state.endpoint.mode = { provider: 'AZURE' };
|
||||
LocalStorage.storeEndpointState(state.endpoint);
|
||||
deferred.resolve();
|
||||
|
@ -147,23 +147,23 @@ function StateManagerFactory($q, SystemService, InfoHelper, LocalStorage, Settin
|
|||
}
|
||||
|
||||
$q.all({
|
||||
version: SystemService.version(),
|
||||
info: SystemService.info()
|
||||
version: endpoint.Status === 1 ? SystemService.version() : $q.when(endpoint.Snapshots[0].SnapshotRaw.Version),
|
||||
info: endpoint.Status === 1 ? SystemService.info() : $q.when(endpoint.Snapshots[0].SnapshotRaw.Info)
|
||||
})
|
||||
.then(function success(data) {
|
||||
var endpointMode = InfoHelper.determineEndpointMode(data.info, type);
|
||||
var endpointMode = InfoHelper.determineEndpointMode(data.info, endpoint.Type);
|
||||
var endpointAPIVersion = parseFloat(data.version.ApiVersion);
|
||||
state.endpoint.mode = endpointMode;
|
||||
state.endpoint.name = name;
|
||||
state.endpoint.name = endpoint.Name;
|
||||
state.endpoint.apiVersion = endpointAPIVersion;
|
||||
state.endpoint.extensions = assignExtensions(extensions);
|
||||
|
||||
if (endpointMode.agentProxy) {
|
||||
if (endpointMode.agentProxy && endpoint.Status === 1) {
|
||||
return AgentPingService.ping().then(function onPingSuccess(data) {
|
||||
state.endpoint.agentApiVersion = data.version;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}).then(function () {
|
||||
LocalStorage.storeEndpointState(state.endpoint);
|
||||
deferred.resolve();
|
||||
|
|
|
@ -1,44 +1,71 @@
|
|||
angular.module('portainer.app')
|
||||
.controller('HomeController', ['$q', '$scope', '$state', 'Authentication', 'EndpointService', 'EndpointHelper', 'GroupService', 'Notifications', 'EndpointProvider', 'StateManager', 'ExtensionManager', 'ModalService', 'MotdService',
|
||||
function ($q, $scope, $state, Authentication, EndpointService, EndpointHelper, GroupService, Notifications, EndpointProvider, StateManager, ExtensionManager, ModalService, MotdService) {
|
||||
.controller('HomeController', ['$q', '$scope', '$state', 'Authentication', 'EndpointService', 'EndpointHelper', 'GroupService', 'Notifications', 'EndpointProvider', 'StateManager', 'ExtensionManager', 'ModalService', 'MotdService', 'SystemService',
|
||||
function ($q, $scope, $state, Authentication, EndpointService, EndpointHelper, GroupService, Notifications, EndpointProvider, StateManager, ExtensionManager, ModalService, MotdService, SystemService) {
|
||||
|
||||
$scope.goToDashboard = function(endpoint) {
|
||||
EndpointProvider.setEndpointID(endpoint.Id);
|
||||
EndpointProvider.setEndpointPublicURL(endpoint.PublicURL);
|
||||
if (endpoint.Type === 3) {
|
||||
switchToAzureEndpoint(endpoint);
|
||||
} else {
|
||||
switchToDockerEndpoint(endpoint);
|
||||
}
|
||||
$scope.goToEdit = function(id) {
|
||||
$state.go('portainer.endpoints.endpoint', { id: id });
|
||||
};
|
||||
|
||||
$scope.dismissImportantInformation = function(hash) {
|
||||
$scope.goToDashboard = function (endpoint) {
|
||||
if (endpoint.Type === 3) {
|
||||
return switchToAzureEndpoint(endpoint);
|
||||
}
|
||||
|
||||
checkEndpointStatus(endpoint)
|
||||
.then(function sucess() {
|
||||
return switchToDockerEndpoint(endpoint);
|
||||
}).catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'Unable to verify endpoint status');
|
||||
});
|
||||
};
|
||||
|
||||
$scope.dismissImportantInformation = function (hash) {
|
||||
StateManager.dismissImportantInformation(hash);
|
||||
};
|
||||
|
||||
$scope.dismissInformationPanel = function(id) {
|
||||
$scope.dismissInformationPanel = function (id) {
|
||||
StateManager.dismissInformationPanel(id);
|
||||
};
|
||||
|
||||
function triggerSnapshot() {
|
||||
EndpointService.snapshot()
|
||||
.then(function success() {
|
||||
Notifications.success('Success', 'Endpoints updated');
|
||||
$state.reload();
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'An error occured during endpoint snapshot');
|
||||
});
|
||||
}
|
||||
|
||||
$scope.triggerSnapshot = function() {
|
||||
$scope.triggerSnapshot = function () {
|
||||
ModalService.confirmEndpointSnapshot(function (result) {
|
||||
if(!result) { return; }
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
triggerSnapshot();
|
||||
});
|
||||
};
|
||||
|
||||
function checkEndpointStatus(endpoint) {
|
||||
var deferred = $q.defer();
|
||||
|
||||
var status = 1;
|
||||
SystemService.ping(endpoint.Id)
|
||||
.then(function sucess() {
|
||||
status = 1;
|
||||
}).catch(function error() {
|
||||
status = 2;
|
||||
}).finally(function () {
|
||||
if (endpoint.Status === status) {
|
||||
deferred.resolve(endpoint);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
EndpointService.updateEndpoint(endpoint.Id, { Status: status })
|
||||
.then(function sucess() {
|
||||
deferred.resolve(endpoint);
|
||||
}).catch(function error(err) {
|
||||
deferred.reject({msg: 'Unable to update endpoint status', err: err});
|
||||
});
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function switchToAzureEndpoint(endpoint) {
|
||||
EndpointProvider.setEndpointID(endpoint.Id);
|
||||
EndpointProvider.setEndpointPublicURL(endpoint.PublicURL);
|
||||
EndpointProvider.setOfflineModeFromStatus(endpoint.Status);
|
||||
StateManager.updateEndpointState(endpoint.Name, endpoint.Type, [])
|
||||
.then(function success() {
|
||||
$state.go('azure.dashboard');
|
||||
|
@ -49,10 +76,21 @@ function ($q, $scope, $state, Authentication, EndpointService, EndpointHelper, G
|
|||
}
|
||||
|
||||
function switchToDockerEndpoint(endpoint) {
|
||||
ExtensionManager.initEndpointExtensions(endpoint.Id)
|
||||
if (endpoint.Status === 2 && endpoint.Snapshots[0] && endpoint.Snapshots[0].Swarm === true) {
|
||||
Notifications.error('Failure', '', 'Endpoint is unreachable. Connect to another swarm manager.');
|
||||
return;
|
||||
} else if (endpoint.Status === 2 && !endpoint.Snapshots[0]) {
|
||||
Notifications.error('Failure', '', 'Endpoint is unreachable and there is no snapshot available for offline browsing.');
|
||||
return;
|
||||
}
|
||||
|
||||
EndpointProvider.setEndpointID(endpoint.Id);
|
||||
EndpointProvider.setEndpointPublicURL(endpoint.PublicURL);
|
||||
EndpointProvider.setOfflineModeFromStatus(endpoint.Status);
|
||||
ExtensionManager.initEndpointExtensions(endpoint)
|
||||
.then(function success(data) {
|
||||
var extensions = data;
|
||||
return StateManager.updateEndpointState(endpoint.Name, endpoint.Type, extensions);
|
||||
return StateManager.updateEndpointState(endpoint, extensions);
|
||||
})
|
||||
.then(function success() {
|
||||
$state.go('docker.dashboard');
|
||||
|
@ -62,10 +100,15 @@ function ($q, $scope, $state, Authentication, EndpointService, EndpointHelper, G
|
|||
});
|
||||
}
|
||||
|
||||
$scope.goToEdit = goToEdit;
|
||||
|
||||
function goToEdit(id) {
|
||||
$state.go('portainer.endpoints.endpoint', { id: id });
|
||||
function triggerSnapshot() {
|
||||
EndpointService.snapshot()
|
||||
.then(function success() {
|
||||
Notifications.success('Success', 'Endpoints updated');
|
||||
$state.reload();
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'An error occured during endpoint snapshot');
|
||||
});
|
||||
}
|
||||
|
||||
function initView() {
|
||||
|
@ -85,6 +128,7 @@ function ($q, $scope, $state, Authentication, EndpointService, EndpointHelper, G
|
|||
var groups = data.groups;
|
||||
EndpointHelper.mapGroupNameToEndpoint(endpoints, groups);
|
||||
$scope.endpoints = endpoints;
|
||||
EndpointProvider.setEndpoints(endpoints);
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'Unable to retrieve endpoint information');
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
angular.module('portainer.app')
|
||||
.controller('MainController', ['$scope', '$cookieStore', 'StateManager',
|
||||
function ($scope, $cookieStore, StateManager) {
|
||||
.controller('MainController', ['$scope', '$cookieStore', 'StateManager', 'EndpointProvider',
|
||||
function ($scope, $cookieStore, StateManager, EndpointProvider) {
|
||||
|
||||
/**
|
||||
* Sidebar Toggle & Cookie Control
|
||||
|
@ -11,6 +11,7 @@ function ($scope, $cookieStore, StateManager) {
|
|||
};
|
||||
|
||||
$scope.applicationState = StateManager.getState();
|
||||
$scope.endpointState = EndpointProvider.endpoint();
|
||||
|
||||
$scope.$watch($scope.getWidth, function(newValue) {
|
||||
if (newValue >= mobileView) {
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
swarm-management="applicationState.endpoint.mode.provider === 'DOCKER_SWARM_MODE' && applicationState.endpoint.mode.role === 'MANAGER'"
|
||||
standalone-management="applicationState.endpoint.mode.provider === 'DOCKER_STANDALONE' || applicationState.endpoint.mode.provider === 'VMWARE_VIC'"
|
||||
admin-access="!applicationState.application.authentication || isAdmin"
|
||||
offline-mode="endpointState.OfflineMode"
|
||||
></docker-sidebar-content>
|
||||
<li class="sidebar-title" ng-if="applicationState.endpoint.mode && applicationState.endpoint.extensions.length > 0">
|
||||
<span>Extensions</span>
|
||||
|
|
|
@ -1,36 +1,37 @@
|
|||
angular.module('portainer.app')
|
||||
.controller('SidebarController', ['$q', '$scope', 'StateManager', 'Notifications', 'Authentication', 'UserService',
|
||||
function ($q, $scope, StateManager, Notifications, Authentication, UserService) {
|
||||
.controller('SidebarController', ['$q', '$scope', 'StateManager', 'Notifications', 'Authentication', 'UserService',
|
||||
function ($q, $scope, StateManager, Notifications, Authentication, UserService) {
|
||||
|
||||
function checkPermissions(memberships) {
|
||||
var isLeader = false;
|
||||
angular.forEach(memberships, function(membership) {
|
||||
if (membership.Role === 1) {
|
||||
isLeader = true;
|
||||
function checkPermissions(memberships) {
|
||||
var isLeader = false;
|
||||
angular.forEach(memberships, function (membership) {
|
||||
if (membership.Role === 1) {
|
||||
isLeader = true;
|
||||
}
|
||||
});
|
||||
$scope.isTeamLeader = isLeader;
|
||||
}
|
||||
});
|
||||
$scope.isTeamLeader = isLeader;
|
||||
}
|
||||
|
||||
function initView() {
|
||||
$scope.uiVersion = StateManager.getState().application.version;
|
||||
$scope.logo = StateManager.getState().application.logo;
|
||||
function initView() {
|
||||
$scope.uiVersion = StateManager.getState().application.version;
|
||||
$scope.logo = StateManager.getState().application.logo;
|
||||
|
||||
var authenticationEnabled = $scope.applicationState.application.authentication;
|
||||
if (authenticationEnabled) {
|
||||
var userDetails = Authentication.getUserDetails();
|
||||
var isAdmin = userDetails.role === 1;
|
||||
$scope.isAdmin = isAdmin;
|
||||
var authenticationEnabled = $scope.applicationState.application.authentication;
|
||||
if (authenticationEnabled) {
|
||||
var userDetails = Authentication.getUserDetails();
|
||||
var isAdmin = userDetails.role === 1;
|
||||
$scope.isAdmin = isAdmin;
|
||||
|
||||
$q.when(!isAdmin ? UserService.userMemberships(userDetails.ID) : [])
|
||||
.then(function success(data) {
|
||||
checkPermissions(data);
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'Unable to retrieve user memberships');
|
||||
});
|
||||
$q.when(!isAdmin ? UserService.userMemberships(userDetails.ID) : [])
|
||||
.then(function success(data) {
|
||||
checkPermissions(data);
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'Unable to retrieve user memberships');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
initView();
|
||||
}
|
||||
}
|
||||
|
||||
initView();
|
||||
}]);
|
||||
]);
|
|
@ -6,7 +6,7 @@
|
|||
</rd-header-title>
|
||||
<rd-header-content>Stacks</rd-header-content>
|
||||
</rd-header>
|
||||
|
||||
<information-panel-offline ng-if="offlineMode"></information-panel-offline>
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<stacks-datatable
|
||||
|
@ -15,6 +15,7 @@
|
|||
order-by="Name"
|
||||
remove-action="removeAction"
|
||||
show-ownership-column="applicationState.application.authentication"
|
||||
offline-mode="offlineMode"
|
||||
></stacks-datatable>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -33,6 +33,8 @@ function ($scope, $state, Notifications, StackService, ModalService, EndpointPro
|
|||
});
|
||||
}
|
||||
|
||||
$scope.offlineMode = false;
|
||||
|
||||
function initView() {
|
||||
var endpointMode = $scope.applicationState.endpoint.mode;
|
||||
var endpointId = EndpointProvider.endpointID();
|
||||
|
@ -45,6 +47,7 @@ function ($scope, $state, Notifications, StackService, ModalService, EndpointPro
|
|||
.then(function success(data) {
|
||||
var stacks = data;
|
||||
$scope.stacks = stacks;
|
||||
$scope.offlineMode = EndpointProvider.offlineMode();
|
||||
})
|
||||
.catch(function error(err) {
|
||||
$scope.stacks = [];
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue