From eb9f6c77f450895eb471a81099a9cd51b8ed03de Mon Sep 17 00:00:00 2001 From: Chaim Lev-Ari Date: Tue, 14 Dec 2021 09:34:54 +0200 Subject: [PATCH] refactor(endpoints): remove endpointProvider from views [EE-1136] (#5359) [EE-1136] --- .../host-browser/hostBrowserController.js | 5 +- app/agent/components/host-browser/index.js | 3 + app/agent/components/volume-browser/index.js | 1 + .../volume-browser/volumeBrowserController.js | 2 +- app/agent/services/hostBrowserService.js | 9 +- app/agent/services/volumeBrowserService.js | 7 +- .../containersDatatable.html | 2 +- .../containersDatatable.js | 1 + .../containersDatatableController.js | 4 +- .../actions/servicesDatatableActions.js | 1 + .../servicesDatatableActionsController.js | 7 +- .../services-datatable/servicesDatatable.html | 3 +- .../services-datatable/servicesDatatable.js | 2 + .../servicesDatatableController.js | 4 +- .../console/containerConsoleController.js | 8 +- app/docker/views/containers/containers.html | 1 + .../views/containers/containersController.js | 47 +-- .../views/dashboard/dashboardController.js | 19 +- .../host-browser-view/host-browser-view.html | 2 +- .../host-browser-view/host-browser-view.js | 3 + app/docker/views/host/host-view-controller.js | 7 +- app/docker/views/images/imagesController.js | 6 +- .../views/networks/networksController.js | 7 +- .../nodes/node-browser/node-browser.html | 2 +- .../views/nodes/node-browser/node-browser.js | 3 + app/docker/views/services/services.html | 2 + .../views/services/servicesController.js | 5 +- .../volumes/browse/browseVolumeController.js | 25 +- .../views/volumes/browse/browsevolume.html | 2 +- app/docker/views/volumes/volumesController.js | 7 +- .../views/applications/console/console.js | 1 + .../applications/console/consoleController.js | 5 +- .../views/applications/edit/application.js | 1 + .../edit/applicationController.js | 5 +- app/kubernetes/views/dashboard/dashboard.js | 3 + .../views/dashboard/dashboardController.js | 7 +- app/kubernetes/views/deploy/deploy.js | 3 + .../views/deploy/deployController.js | 18 +- .../access/resourcePoolAccess.js | 1 + .../access/resourcePoolAccessController.js | 10 +- app/kubernetes/views/volumes/volumes.js | 1 + .../views/volumes/volumesController.js | 18 +- app/portainer/helpers/endpointHelper.js | 4 + app/portainer/rest/stack.js | 51 ++- app/portainer/services/api/templateService.js | 11 +- app/portainer/services/stateManager.js | 398 +++++++++--------- .../customTemplatesViewController.js | 4 +- .../custom-templates-view/index.js | 3 + .../endpoints/edit/endpointController.js | 2 - .../init/endpoint/initEndpointController.js | 3 +- .../create/createRegistryController.js | 18 +- .../stacks/create/createStackController.js | 11 +- app/portainer/views/stacks/edit/stack.html | 5 +- .../views/stacks/edit/stackController.js | 35 +- .../views/stacks/stacksController.js | 17 +- .../views/templates/templatesController.js | 5 +- 56 files changed, 408 insertions(+), 429 deletions(-) diff --git a/app/agent/components/host-browser/hostBrowserController.js b/app/agent/components/host-browser/hostBrowserController.js index 68992fab6..e1fcc998d 100644 --- a/app/agent/components/host-browser/hostBrowserController.js +++ b/app/agent/components/host-browser/hostBrowserController.js @@ -141,8 +141,11 @@ export class HostBrowserController { return this.$async(this.onFileSelectedForUploadAsync, file); } async onFileSelectedForUploadAsync(file) { + if (!this.endpointId) { + throw new Error('missing endpoint id'); + } try { - await this.HostBrowserService.upload(this.state.path, file); + await this.HostBrowserService.upload(this.endpointId, this.state.path, file); this.onFileUploaded(); } catch (err) { this.Notifications.error('Failure', err, 'Unable to upload file'); diff --git a/app/agent/components/host-browser/index.js b/app/agent/components/host-browser/index.js index d38643d6f..936d16bd7 100644 --- a/app/agent/components/host-browser/index.js +++ b/app/agent/components/host-browser/index.js @@ -4,4 +4,7 @@ import { HostBrowserController } from './hostBrowserController'; angular.module('portainer.agent').component('hostBrowser', { controller: HostBrowserController, templateUrl: './hostBrowser.html', + bindings: { + endpointId: '<', + }, }); diff --git a/app/agent/components/volume-browser/index.js b/app/agent/components/volume-browser/index.js index e52633a51..5d5104ca7 100644 --- a/app/agent/components/volume-browser/index.js +++ b/app/agent/components/volume-browser/index.js @@ -9,5 +9,6 @@ angular.module('portainer.agent').component('volumeBrowser', { volumeId: '<', nodeName: '<', isUploadEnabled: '<', + endpointId: '<', }, }); diff --git a/app/agent/components/volume-browser/volumeBrowserController.js b/app/agent/components/volume-browser/volumeBrowserController.js index 7f6a55e97..239ceae6d 100644 --- a/app/agent/components/volume-browser/volumeBrowserController.js +++ b/app/agent/components/volume-browser/volumeBrowserController.js @@ -111,7 +111,7 @@ export class VolumeBrowserController { } async onFileSelectedForUploadAsync(file) { try { - await this.VolumeBrowserService.upload(this.state.path, file, this.volumeId); + await this.VolumeBrowserService.upload(this.endpointId, this.state.path, file, this.volumeId); this.onFileUploaded(); } catch (err) { this.Notifications.error('Failure', err, 'Unable to upload file'); diff --git a/app/agent/services/hostBrowserService.js b/app/agent/services/hostBrowserService.js index fd2d5eed7..f6e6a2808 100644 --- a/app/agent/services/hostBrowserService.js +++ b/app/agent/services/hostBrowserService.js @@ -2,7 +2,8 @@ import angular from 'angular'; angular.module('portainer.agent').factory('HostBrowserService', HostBrowserServiceFactory); -function HostBrowserServiceFactory(Browse, Upload, API_ENDPOINT_ENDPOINTS, EndpointProvider, StateManager) { +/* @ngInject */ +function HostBrowserServiceFactory(Browse, Upload, API_ENDPOINT_ENDPOINTS, StateManager) { return { ls, get, delete: deletePath, rename, upload }; function ls(path) { @@ -25,14 +26,14 @@ function HostBrowserServiceFactory(Browse, Upload, API_ENDPOINT_ENDPOINTS, Endpo return Browse.rename({}, payload).$promise; } - function upload(path, file, onProgress) { + function upload(endpointId, Path, file, onProgress) { const agentVersion = StateManager.getAgentApiVersion(); - const url = `${API_ENDPOINT_ENDPOINTS}/${EndpointProvider.endpointID()}/docker${agentVersion > 1 ? '/v' + agentVersion : ''}/browse/put`; + const url = `${API_ENDPOINT_ENDPOINTS}/${endpointId}/docker${agentVersion > 1 ? '/v' + agentVersion : ''}/browse/put`; return new Promise((resolve, reject) => { Upload.upload({ url: url, - data: { file: file, Path: path }, + data: { file, Path }, }).then(resolve, reject, onProgress); }); } diff --git a/app/agent/services/volumeBrowserService.js b/app/agent/services/volumeBrowserService.js index 233f5df22..29cefcdd3 100644 --- a/app/agent/services/volumeBrowserService.js +++ b/app/agent/services/volumeBrowserService.js @@ -2,7 +2,8 @@ import angular from 'angular'; angular.module('portainer.agent').factory('VolumeBrowserService', VolumeBrowserServiceFactory); -function VolumeBrowserServiceFactory(StateManager, Browse, BrowseVersion1, API_ENDPOINT_ENDPOINTS, EndpointProvider, Upload) { +/* @ngInject */ +function VolumeBrowserServiceFactory(StateManager, Browse, BrowseVersion1, API_ENDPOINT_ENDPOINTS, Upload) { return { ls, get, @@ -41,13 +42,13 @@ function VolumeBrowserServiceFactory(StateManager, Browse, BrowseVersion1, API_E return getBrowseService().rename({ volumeID: volumeId, version: getAgentApiVersion() }, payload).$promise; } - function upload(path, file, volumeId, onProgress) { + function upload(endpointId, path, file, volumeId, onProgress) { const agentVersion = StateManager.getAgentApiVersion(); if (agentVersion < 2) { throw new Error('upload is not supported on this agent version'); } - const url = `${API_ENDPOINT_ENDPOINTS}/${EndpointProvider.endpointID()}/docker/v${agentVersion}/browse/put?volumeID=${volumeId}`; + const url = `${API_ENDPOINT_ENDPOINTS}/${endpointId}/docker/v${agentVersion}/browse/put?volumeID=${volumeId}`; return new Promise((resolve, reject) => { Upload.upload({ diff --git a/app/docker/components/datatables/containers-datatable/containersDatatable.html b/app/docker/components/datatables/containers-datatable/containersDatatable.html index b61e81163..e48177a49 100644 --- a/app/docker/components/datatables/containers-datatable/containersDatatable.html +++ b/app/docker/components/datatables/containers-datatable/containersDatatable.html @@ -264,7 +264,7 @@ ng-if="item.Ports.length > 0" ng-repeat="p in item.Ports | unique: 'public'" class="image-tag" - ng-href="http://{{ $ctrl.state.publicURL || p.host }}:{{ p.public }}" + ng-href="http://{{ $ctrl.endpointPublicUrl || p.host }}:{{ p.public }}" target="_blank" > {{ p.public }}:{{ p.private }} diff --git a/app/docker/components/datatables/containers-datatable/containersDatatable.js b/app/docker/components/datatables/containers-datatable/containersDatatable.js index d748c27aa..e93050f81 100644 --- a/app/docker/components/datatables/containers-datatable/containersDatatable.js +++ b/app/docker/components/datatables/containers-datatable/containersDatatable.js @@ -13,5 +13,6 @@ angular.module('portainer.docker').component('containersDatatable', { offlineMode: '<', refreshCallback: '<', notAutoFocus: '<', + endpointPublicUrl: '<', }, }); diff --git a/app/docker/components/datatables/containers-datatable/containersDatatableController.js b/app/docker/components/datatables/containers-datatable/containersDatatableController.js index baf2292c0..1178078de 100644 --- a/app/docker/components/datatables/containers-datatable/containersDatatableController.js +++ b/app/docker/components/datatables/containers-datatable/containersDatatableController.js @@ -4,8 +4,7 @@ angular.module('portainer.docker').controller('ContainersDatatableController', [ '$scope', '$controller', 'DatatableService', - 'EndpointProvider', - function ($scope, $controller, DatatableService, EndpointProvider) { + function ($scope, $controller, DatatableService) { angular.extend(this, $controller('GenericDatatableController', { $scope: $scope })); var ctrl = this; @@ -14,7 +13,6 @@ angular.module('portainer.docker').controller('ContainersDatatableController', [ noStoppedItemsSelected: true, noRunningItemsSelected: true, noPausedItemsSelected: true, - publicURL: EndpointProvider.endpointPublicURL(), }); this.settings = Object.assign(this.settings, { diff --git a/app/docker/components/datatables/services-datatable/actions/servicesDatatableActions.js b/app/docker/components/datatables/services-datatable/actions/servicesDatatableActions.js index 89ddbc6d1..8d4b918ac 100644 --- a/app/docker/components/datatables/services-datatable/actions/servicesDatatableActions.js +++ b/app/docker/components/datatables/services-datatable/actions/servicesDatatableActions.js @@ -6,5 +6,6 @@ angular.module('portainer.docker').component('servicesDatatableActions', { selectedItemCount: '=', showUpdateAction: '<', showAddAction: '<', + endpointId: '<', }, }); diff --git a/app/docker/components/datatables/services-datatable/actions/servicesDatatableActionsController.js b/app/docker/components/datatables/services-datatable/actions/servicesDatatableActionsController.js index d760390d5..de6849191 100644 --- a/app/docker/components/datatables/services-datatable/actions/servicesDatatableActionsController.js +++ b/app/docker/components/datatables/services-datatable/actions/servicesDatatableActionsController.js @@ -7,8 +7,9 @@ angular.module('portainer.docker').controller('ServicesDatatableActionsControlle 'ModalService', 'ImageHelper', 'WebhookService', - 'EndpointProvider', - function ($q, $state, ServiceService, ServiceHelper, Notifications, ModalService, ImageHelper, WebhookService, EndpointProvider) { + function ($q, $state, ServiceService, ServiceHelper, Notifications, ModalService, ImageHelper, WebhookService) { + const ctrl = this; + this.scaleAction = function scaleService(service) { var config = ServiceHelper.serviceToConfig(service.Model); config.Mode.Replicated.Replicas = service.Replicas; @@ -84,7 +85,7 @@ angular.module('portainer.docker').controller('ServicesDatatableActionsControlle angular.forEach(services, function (service) { ServiceService.remove(service) .then(function success() { - return WebhookService.webhooks(service.Id, EndpointProvider.endpointID()); + return WebhookService.webhooks(service.Id, ctrl.endpointId); }) .then(function success(data) { return $q.when(data.length !== 0 && WebhookService.deleteWebhook(data[0].Id)); diff --git a/app/docker/components/datatables/services-datatable/servicesDatatable.html b/app/docker/components/datatables/services-datatable/servicesDatatable.html index b28081383..15ddbdd7d 100644 --- a/app/docker/components/datatables/services-datatable/servicesDatatable.html +++ b/app/docker/components/datatables/services-datatable/servicesDatatable.html @@ -47,6 +47,7 @@ selected-item-count="$ctrl.state.selectedItemCount" show-add-action="$ctrl.showAddAction" show-update-action="$ctrl.showUpdateAction" + endpoint-id="$ctrl.endpointId" > diff --git a/app/docker/views/containers/containersController.js b/app/docker/views/containers/containersController.js index 1240c99ee..4c60cd1b1 100644 --- a/app/docker/views/containers/containersController.js +++ b/app/docker/views/containers/containersController.js @@ -1,29 +1,26 @@ -angular.module('portainer.docker').controller('ContainersController', [ - '$scope', - 'ContainerService', - 'Notifications', - 'EndpointProvider', - function ($scope, ContainerService, Notifications, EndpointProvider) { - $scope.offlineMode = false; +angular.module('portainer.docker').controller('ContainersController', ContainersController); - $scope.getContainers = getContainers; +/* @ngInject */ +function ContainersController($scope, ContainerService, Notifications, endpoint) { + $scope.offlineMode = endpoint.Status !== 1; + $scope.endpoint = endpoint; - function getContainers() { - ContainerService.containers(1) - .then(function success(data) { - $scope.containers = data; - $scope.offlineMode = EndpointProvider.offlineMode(); - }) - .catch(function error(err) { - Notifications.error('Failure', err, 'Unable to retrieve containers'); - $scope.containers = []; - }); - } + $scope.getContainers = getContainers; - function initView() { - getContainers(); - } + function getContainers() { + ContainerService.containers(1) + .then(function success(data) { + $scope.containers = data; + }) + .catch(function error(err) { + Notifications.error('Failure', err, 'Unable to retrieve containers'); + $scope.containers = []; + }); + } - initView(); - }, -]); + function initView() { + getContainers(); + } + + initView(); +} diff --git a/app/docker/views/dashboard/dashboardController.js b/app/docker/views/dashboard/dashboardController.js index 0e09b30d1..ca78395ed 100644 --- a/app/docker/views/dashboard/dashboardController.js +++ b/app/docker/views/dashboard/dashboardController.js @@ -1,6 +1,8 @@ import angular from 'angular'; import _ from 'lodash'; +import { isOfflineEndpoint } from '@/portainer/helpers/endpointHelper'; + angular.module('portainer.docker').controller('DashboardController', [ '$scope', '$q', @@ -12,9 +14,7 @@ angular.module('portainer.docker').controller('DashboardController', [ 'SystemService', 'ServiceService', 'StackService', - 'EndpointService', 'Notifications', - 'EndpointProvider', 'StateManager', 'TagService', 'endpoint', @@ -29,9 +29,7 @@ angular.module('portainer.docker').controller('DashboardController', [ SystemService, ServiceService, StackService, - EndpointService, Notifications, - EndpointProvider, StateManager, TagService, endpoint @@ -45,8 +43,7 @@ angular.module('portainer.docker').controller('DashboardController', [ async function initView() { const endpointMode = $scope.applicationState.endpoint.mode; - const endpointId = EndpointProvider.endpointID(); - $scope.endpointId = endpointId; + $scope.endpoint = endpoint; $scope.showStacks = await shouldShowStacks(); @@ -56,9 +53,8 @@ angular.module('portainer.docker').controller('DashboardController', [ volumes: VolumeService.volumes(), networks: NetworkService.networks(true, true, true), services: endpointMode.provider === 'DOCKER_SWARM_MODE' && endpointMode.role === 'MANAGER' ? ServiceService.services() : [], - stacks: StackService.stacks(true, endpointMode.provider === 'DOCKER_SWARM_MODE' && endpointMode.role === 'MANAGER', endpointId), + stacks: StackService.stacks(true, endpointMode.provider === 'DOCKER_SWARM_MODE' && endpointMode.role === 'MANAGER', endpoint.Id), info: SystemService.info(), - endpoint: EndpointService.endpoint(endpointId), tags: TagService.tags(), }) .then(function success(data) { @@ -69,11 +65,10 @@ angular.module('portainer.docker').controller('DashboardController', [ $scope.serviceCount = data.services.length; $scope.stackCount = data.stacks.length; $scope.info = data.info; - $scope.endpoint = data.endpoint; - $scope.endpointTags = $scope.endpoint.TagIds.length + $scope.endpointTags = endpoint.TagIds.length ? _.join( _.filter( - _.map($scope.endpoint.TagIds, (id) => { + _.map(endpoint.TagIds, (id) => { const tag = data.tags.find((tag) => tag.Id === id); return tag ? tag.Name : ''; }), @@ -82,7 +77,7 @@ angular.module('portainer.docker').controller('DashboardController', [ ', ' ) : '-'; - $scope.offlineMode = EndpointProvider.offlineMode(); + $scope.offlineMode = isOfflineEndpoint(endpoint); }) .catch(function error(err) { Notifications.error('Failure', err, 'Unable to load dashboard data'); diff --git a/app/docker/views/host/host-browser-view/host-browser-view.html b/app/docker/views/host/host-browser-view/host-browser-view.html index b8836c773..7c8309e73 100644 --- a/app/docker/views/host/host-browser-view/host-browser-view.html +++ b/app/docker/views/host/host-browser-view/host-browser-view.html @@ -7,6 +7,6 @@
- +
diff --git a/app/docker/views/host/host-browser-view/host-browser-view.js b/app/docker/views/host/host-browser-view/host-browser-view.js index 4887dece2..94095eca7 100644 --- a/app/docker/views/host/host-browser-view/host-browser-view.js +++ b/app/docker/views/host/host-browser-view/host-browser-view.js @@ -1,4 +1,7 @@ angular.module('portainer.docker').component('hostBrowserView', { templateUrl: './host-browser-view.html', controller: 'HostBrowserViewController', + bindings: { + endpoint: '<', + }, }); diff --git a/app/docker/views/host/host-view-controller.js b/app/docker/views/host/host-view-controller.js index b7433ed0f..b9b8ec010 100644 --- a/app/docker/views/host/host-view-controller.js +++ b/app/docker/views/host/host-view-controller.js @@ -1,3 +1,5 @@ +import { isOfflineEndpoint } from '@/portainer/helpers/endpointHelper'; + angular.module('portainer.docker').controller('HostViewController', [ '$q', 'SystemService', @@ -6,8 +8,7 @@ angular.module('portainer.docker').controller('HostViewController', [ 'AgentService', 'ContainerService', 'Authentication', - 'EndpointProvider', - function HostViewController($q, SystemService, Notifications, StateManager, AgentService, ContainerService, Authentication, EndpointProvider) { + function HostViewController($q, SystemService, Notifications, StateManager, AgentService, ContainerService, Authentication) { var ctrl = this; this.$onInit = initView; @@ -39,7 +40,7 @@ angular.module('portainer.docker').controller('HostViewController', [ .then(function success(data) { ctrl.engineDetails = buildEngineDetails(data); ctrl.hostDetails = buildHostDetails(data.info); - ctrl.state.offlineMode = EndpointProvider.offlineMode(); + ctrl.state.offlineMode = isOfflineEndpoint(ctrl.endpoint); ctrl.jobs = data.jobs; if (ctrl.state.isAgent && agentApiVersion > 1) { diff --git a/app/docker/views/images/imagesController.js b/app/docker/views/images/imagesController.js index 98dc3eaad..7224fe029 100644 --- a/app/docker/views/images/imagesController.js +++ b/app/docker/views/images/imagesController.js @@ -1,5 +1,6 @@ import _ from 'lodash-es'; import { PorImageRegistryModel } from 'Docker/models/porImageRegistry'; +import { isOfflineEndpoint } from '@/portainer/helpers/endpointHelper'; angular.module('portainer.docker').controller('ImagesController', [ '$scope', @@ -11,9 +12,8 @@ angular.module('portainer.docker').controller('ImagesController', [ 'HttpRequestHelper', 'FileSaver', 'Blob', - 'EndpointProvider', 'endpoint', - function ($scope, $state, Authentication, ImageService, Notifications, ModalService, HttpRequestHelper, FileSaver, Blob, EndpointProvider, endpoint) { + function ($scope, $state, Authentication, ImageService, Notifications, ModalService, HttpRequestHelper, FileSaver, Blob, endpoint) { $scope.endpoint = endpoint; $scope.isAdmin = Authentication.isAdmin(); @@ -142,7 +142,7 @@ angular.module('portainer.docker').controller('ImagesController', [ ImageService.images(true) .then(function success(data) { $scope.images = data; - $scope.offlineMode = EndpointProvider.offlineMode(); + $scope.offlineMode = isOfflineEndpoint(endpoint); }) .catch(function error(err) { Notifications.error('Failure', err, 'Unable to retrieve images'); diff --git a/app/docker/views/networks/networksController.js b/app/docker/views/networks/networksController.js index 2b193e425..6289f8fb3 100644 --- a/app/docker/views/networks/networksController.js +++ b/app/docker/views/networks/networksController.js @@ -1,5 +1,6 @@ import _ from 'lodash-es'; import DockerNetworkHelper from 'Docker/helpers/networkHelper'; +import { isOfflineEndpoint } from '@/portainer/helpers/endpointHelper'; angular.module('portainer.docker').controller('NetworksController', [ '$q', @@ -8,9 +9,9 @@ angular.module('portainer.docker').controller('NetworksController', [ 'NetworkService', 'Notifications', 'HttpRequestHelper', - 'EndpointProvider', + 'endpoint', 'AgentService', - function ($q, $scope, $state, NetworkService, Notifications, HttpRequestHelper, EndpointProvider, AgentService) { + function ($q, $scope, $state, NetworkService, Notifications, HttpRequestHelper, endpoint, AgentService) { $scope.removeAction = function (selectedItems) { var actionCount = selectedItems.length; angular.forEach(selectedItems, function (network) { @@ -65,7 +66,7 @@ angular.module('portainer.docker').controller('NetworksController', [ $q.all(req) .then((data) => { - $scope.offlineMode = EndpointProvider.offlineMode(); + $scope.offlineMode = isOfflineEndpoint(endpoint); const networks = _.forEach(data.networks, (item) => (item.Subs = [])); if ($scope.applicationState.endpoint.mode.agentProxy && $scope.applicationState.endpoint.mode.provider === 'DOCKER_SWARM_MODE') { $scope.networks = groupSwarmNetworksManagerNodesFirst(data.networks, data.agents); diff --git a/app/docker/views/nodes/node-browser/node-browser.html b/app/docker/views/nodes/node-browser/node-browser.html index 7e9dfe3ce..acf2e29f2 100644 --- a/app/docker/views/nodes/node-browser/node-browser.html +++ b/app/docker/views/nodes/node-browser/node-browser.html @@ -7,6 +7,6 @@
- +
diff --git a/app/docker/views/nodes/node-browser/node-browser.js b/app/docker/views/nodes/node-browser/node-browser.js index b073a2a40..dc7d60be7 100644 --- a/app/docker/views/nodes/node-browser/node-browser.js +++ b/app/docker/views/nodes/node-browser/node-browser.js @@ -1,4 +1,7 @@ angular.module('portainer.docker').component('nodeBrowserView', { templateUrl: './node-browser.html', controller: 'NodeBrowserController', + bindings: { + endpoint: '<', + }, }); diff --git a/app/docker/views/services/services.html b/app/docker/views/services/services.html index 2db6429db..b9c777baa 100644 --- a/app/docker/views/services/services.html +++ b/app/docker/views/services/services.html @@ -22,6 +22,8 @@ show-add-action="true" show-stack-column="true" refresh-callback="getServices" + endpoint-public-url="endpoint.PublicURL" + endpoint-id="endpoint.Id" > diff --git a/app/docker/views/services/servicesController.js b/app/docker/views/services/servicesController.js index 6e33208d3..d3b0c58a4 100644 --- a/app/docker/views/services/servicesController.js +++ b/app/docker/views/services/servicesController.js @@ -8,8 +8,11 @@ angular.module('portainer.docker').controller('ServicesController', [ 'TaskHelper', 'NodeService', 'ContainerService', - function ($q, $scope, ServiceService, ServiceHelper, Notifications, TaskService, TaskHelper, NodeService, ContainerService) { + 'endpoint', + function ($q, $scope, ServiceService, ServiceHelper, Notifications, TaskService, TaskHelper, NodeService, ContainerService, endpoint) { $scope.getServices = getServices; + $scope.endpoint = endpoint; + function getServices() { var agentProxy = $scope.applicationState.endpoint.mode.agentProxy; diff --git a/app/docker/views/volumes/browse/browseVolumeController.js b/app/docker/views/volumes/browse/browseVolumeController.js index ef75459d8..6a53ce8f3 100644 --- a/app/docker/views/volumes/browse/browseVolumeController.js +++ b/app/docker/views/volumes/browse/browseVolumeController.js @@ -1,14 +1,13 @@ -angular.module('portainer.docker').controller('BrowseVolumeController', [ - '$scope', - '$transition$', - 'StateManager', - function ($scope, $transition$, StateManager) { - function initView() { - $scope.volumeId = $transition$.params().id; - $scope.nodeName = $transition$.params().nodeName; - $scope.agentApiVersion = StateManager.getAgentApiVersion(); - } +angular.module('portainer.docker').controller('BrowseVolumeController', BrowseVolumeController); - initView(); - }, -]); +/* @ngInject */ +function BrowseVolumeController($scope, $transition$, StateManager, endpoint) { + function initView() { + $scope.volumeId = $transition$.params().id; + $scope.nodeName = $transition$.params().nodeName; + $scope.agentApiVersion = StateManager.getAgentApiVersion(); + $scope.endpointId = endpoint.Id; + } + + initView(); +} diff --git a/app/docker/views/volumes/browse/browsevolume.html b/app/docker/views/volumes/browse/browsevolume.html index d6b57c769..3a569c366 100644 --- a/app/docker/views/volumes/browse/browsevolume.html +++ b/app/docker/views/volumes/browse/browsevolume.html @@ -7,6 +7,6 @@
- +
diff --git a/app/docker/views/volumes/volumesController.js b/app/docker/views/volumes/volumesController.js index 2044adf9d..ccd36db7d 100644 --- a/app/docker/views/volumes/volumesController.js +++ b/app/docker/views/volumes/volumesController.js @@ -1,3 +1,5 @@ +import { isOfflineEndpoint } from '@/portainer/helpers/endpointHelper'; + angular.module('portainer.docker').controller('VolumesController', [ '$q', '$scope', @@ -7,11 +9,10 @@ angular.module('portainer.docker').controller('VolumesController', [ 'VolumeHelper', 'Notifications', 'HttpRequestHelper', - 'EndpointProvider', 'Authentication', 'ModalService', 'endpoint', - function ($q, $scope, $state, VolumeService, ServiceService, VolumeHelper, Notifications, HttpRequestHelper, EndpointProvider, Authentication, ModalService, endpoint) { + function ($q, $scope, $state, VolumeService, ServiceService, VolumeHelper, Notifications, HttpRequestHelper, Authentication, ModalService, endpoint) { $scope.removeAction = function (selectedItems) { ModalService.confirmDeletion('Do you want to remove the selected volume(s)?', (confirmed) => { if (confirmed) { @@ -52,7 +53,7 @@ angular.module('portainer.docker').controller('VolumesController', [ }) .then(function success(data) { var services = data.services; - $scope.offlineMode = EndpointProvider.offlineMode(); + $scope.offlineMode = isOfflineEndpoint(endpoint); $scope.volumes = data.attached .map(function (volume) { volume.dangling = false; diff --git a/app/kubernetes/views/applications/console/console.js b/app/kubernetes/views/applications/console/console.js index 2a44c97b4..780e4f0ac 100644 --- a/app/kubernetes/views/applications/console/console.js +++ b/app/kubernetes/views/applications/console/console.js @@ -4,5 +4,6 @@ angular.module('portainer.kubernetes').component('kubernetesApplicationConsoleVi controllerAs: 'ctrl', bindings: { $transition$: '<', + endpoint: '<', }, }); diff --git a/app/kubernetes/views/applications/console/consoleController.js b/app/kubernetes/views/applications/console/consoleController.js index 29c881417..590bbedcf 100644 --- a/app/kubernetes/views/applications/console/consoleController.js +++ b/app/kubernetes/views/applications/console/consoleController.js @@ -4,12 +4,11 @@ import { baseHref } from '@/portainer/helpers/pathHelper'; class KubernetesApplicationConsoleController { /* @ngInject */ - constructor($async, $state, Notifications, KubernetesApplicationService, EndpointProvider, LocalStorage) { + constructor($async, $state, Notifications, KubernetesApplicationService, LocalStorage) { this.$async = $async; this.$state = $state; this.Notifications = Notifications; this.KubernetesApplicationService = KubernetesApplicationService; - this.EndpointProvider = EndpointProvider; this.LocalStorage = LocalStorage; this.onInit = this.onInit.bind(this); @@ -52,7 +51,7 @@ class KubernetesApplicationConsoleController { connectConsole() { const params = { token: this.LocalStorage.getJWT(), - endpointId: this.EndpointProvider.endpointID(), + endpointId: this.endpoint.Id, namespace: this.application.ResourcePool, podName: this.podName, containerName: this.containerName, diff --git a/app/kubernetes/views/applications/edit/application.js b/app/kubernetes/views/applications/edit/application.js index ae935f0f4..43b19b784 100644 --- a/app/kubernetes/views/applications/edit/application.js +++ b/app/kubernetes/views/applications/edit/application.js @@ -4,5 +4,6 @@ angular.module('portainer.kubernetes').component('kubernetesApplicationView', { controllerAs: 'ctrl', bindings: { $transition$: '<', + endpoint: '<', }, }); diff --git a/app/kubernetes/views/applications/edit/applicationController.js b/app/kubernetes/views/applications/edit/applicationController.js index 6133b9785..06ec3f02d 100644 --- a/app/kubernetes/views/applications/edit/applicationController.js +++ b/app/kubernetes/views/applications/edit/applicationController.js @@ -112,7 +112,7 @@ class KubernetesApplicationController { KubernetesStackService, KubernetesPodService, KubernetesNodeService, - EndpointProvider, + StackService ) { this.$async = $async; @@ -131,7 +131,6 @@ class KubernetesApplicationController { this.KubernetesApplicationDeploymentTypes = KubernetesApplicationDeploymentTypes; this.KubernetesApplicationTypes = KubernetesApplicationTypes; - this.EndpointProvider = EndpointProvider; this.KubernetesDeploymentTypes = KubernetesDeploymentTypes; this.ApplicationDataAccessPolicies = KubernetesApplicationDataAccessPolicies; @@ -365,7 +364,7 @@ class KubernetesApplicationController { placementWarning: false, expandedNote: false, useIngress: false, - useServerMetrics: this.EndpointProvider.currentEndpoint().Kubernetes.Configuration.UseServerMetrics, + useServerMetrics: this.endpoint.Kubernetes.Configuration.UseServerMetrics, }; this.state.activeTab = this.LocalStorage.getActiveTab('application'); diff --git a/app/kubernetes/views/dashboard/dashboard.js b/app/kubernetes/views/dashboard/dashboard.js index 76b30ec2e..c2ada72c2 100644 --- a/app/kubernetes/views/dashboard/dashboard.js +++ b/app/kubernetes/views/dashboard/dashboard.js @@ -2,4 +2,7 @@ angular.module('portainer.kubernetes').component('kubernetesDashboardView', { templateUrl: './dashboard.html', controller: 'KubernetesDashboardController', controllerAs: 'ctrl', + bindings: { + endpoint: '<', + }, }); diff --git a/app/kubernetes/views/dashboard/dashboardController.js b/app/kubernetes/views/dashboard/dashboardController.js index 7293e4067..8ec79a0b4 100644 --- a/app/kubernetes/views/dashboard/dashboardController.js +++ b/app/kubernetes/views/dashboard/dashboardController.js @@ -9,7 +9,6 @@ class KubernetesDashboardController { $async, Notifications, EndpointService, - EndpointProvider, KubernetesResourcePoolService, KubernetesApplicationService, KubernetesConfigurationService, @@ -20,7 +19,6 @@ class KubernetesDashboardController { this.$async = $async; this.Notifications = Notifications; this.EndpointService = EndpointService; - this.EndpointProvider = EndpointProvider; this.KubernetesResourcePoolService = KubernetesResourcePoolService; this.KubernetesApplicationService = KubernetesApplicationService; this.KubernetesConfigurationService = KubernetesConfigurationService; @@ -37,16 +35,13 @@ class KubernetesDashboardController { const isAdmin = this.Authentication.isAdmin(); try { - const endpointId = this.EndpointProvider.endpointID(); - const [endpoint, pools, applications, configurations, volumes, tags] = await Promise.all([ - this.EndpointService.endpoint(endpointId), + const [pools, applications, configurations, volumes, tags] = await Promise.all([ this.KubernetesResourcePoolService.get(), this.KubernetesApplicationService.get(), this.KubernetesConfigurationService.get(), this.KubernetesVolumeService.get(), this.TagService.tags(), ]); - this.endpoint = endpoint; this.applications = applications; this.volumes = volumes; diff --git a/app/kubernetes/views/deploy/deploy.js b/app/kubernetes/views/deploy/deploy.js index f02365586..d7e45f439 100644 --- a/app/kubernetes/views/deploy/deploy.js +++ b/app/kubernetes/views/deploy/deploy.js @@ -2,4 +2,7 @@ angular.module('portainer.kubernetes').component('kubernetesDeployView', { templateUrl: './deploy.html', controller: 'KubernetesDeployController', controllerAs: 'ctrl', + bindings: { + endpoint: '<', + }, }); diff --git a/app/kubernetes/views/deploy/deployController.js b/app/kubernetes/views/deploy/deployController.js index 41bcb92c2..5d6f08e15 100644 --- a/app/kubernetes/views/deploy/deployController.js +++ b/app/kubernetes/views/deploy/deployController.js @@ -8,26 +8,13 @@ import { KubernetesDeployManifestTypes, KubernetesDeployBuildMethods, Kubernetes import { buildOption } from '@/portainer/components/box-selector'; class KubernetesDeployController { /* @ngInject */ - constructor( - $async, - $state, - $window, - Authentication, - ModalService, - Notifications, - EndpointProvider, - KubernetesResourcePoolService, - StackService, - WebhookHelper, - CustomTemplateService - ) { + constructor($async, $state, $window, Authentication, ModalService, Notifications, KubernetesResourcePoolService, StackService, WebhookHelper, CustomTemplateService) { this.$async = $async; this.$state = $state; this.$window = $window; this.Authentication = Authentication; this.ModalService = ModalService; this.Notifications = Notifications; - this.EndpointProvider = EndpointProvider; this.KubernetesResourcePoolService = KubernetesResourcePoolService; this.StackService = StackService; this.WebhookHelper = WebhookHelper; @@ -73,7 +60,6 @@ class KubernetesDeployController { this.ManifestDeployTypes = KubernetesDeployManifestTypes; this.BuildMethods = KubernetesDeployBuildMethods; - this.endpointId = this.EndpointProvider.endpointID(); this.onChangeTemplateId = this.onChangeTemplateId.bind(this); this.deployAsync = this.deployAsync.bind(this); @@ -246,7 +232,7 @@ class KubernetesDeployController { payload.ManifestURL = this.formValues.ManifestURL; } - await this.StackService.kubernetesDeploy(this.endpointId, method, payload); + await this.StackService.kubernetesDeploy(this.endpoint.Id, method, payload); this.Notifications.success('Manifest successfully deployed'); this.state.isEditorDirty = false; diff --git a/app/kubernetes/views/resource-pools/access/resourcePoolAccess.js b/app/kubernetes/views/resource-pools/access/resourcePoolAccess.js index cda4acfbd..4bb584b9f 100644 --- a/app/kubernetes/views/resource-pools/access/resourcePoolAccess.js +++ b/app/kubernetes/views/resource-pools/access/resourcePoolAccess.js @@ -4,5 +4,6 @@ angular.module('portainer.kubernetes').component('kubernetesResourcePoolAccessVi controllerAs: 'ctrl', bindings: { $transition$: '<', + endpoint: '<', }, }); diff --git a/app/kubernetes/views/resource-pools/access/resourcePoolAccessController.js b/app/kubernetes/views/resource-pools/access/resourcePoolAccessController.js index 88748e92f..284e0097b 100644 --- a/app/kubernetes/views/resource-pools/access/resourcePoolAccessController.js +++ b/app/kubernetes/views/resource-pools/access/resourcePoolAccessController.js @@ -6,15 +6,13 @@ import KubernetesConfigMapHelper from 'Kubernetes/helpers/configMapHelper'; class KubernetesResourcePoolAccessController { /* @ngInject */ - constructor($async, $state, Notifications, KubernetesResourcePoolService, KubernetesConfigMapService, EndpointProvider, EndpointService, GroupService, AccessService) { + constructor($async, $state, Notifications, KubernetesResourcePoolService, KubernetesConfigMapService, GroupService, AccessService) { this.$async = $async; this.$state = $state; this.Notifications = Notifications; this.KubernetesResourcePoolService = KubernetesResourcePoolService; this.KubernetesConfigMapService = KubernetesConfigMapService; - this.EndpointProvider = EndpointProvider; - this.EndpointService = EndpointService; this.GroupService = GroupService; this.AccessService = AccessService; @@ -36,6 +34,7 @@ class KubernetesResourcePoolAccessController { * Init */ async onInit() { + const endpoint = this.endpoint; this.state = { actionInProgress: false, viewReady: false, @@ -45,12 +44,9 @@ class KubernetesResourcePoolAccessController { multiselectOutput: [], }; - this.endpointId = this.EndpointProvider.endpointID(); - try { const name = this.$transition$.params().id; - let [endpoint, pool, configMap] = await Promise.all([ - this.EndpointService.endpoint(this.endpointId), + let [pool, configMap] = await Promise.all([ this.KubernetesResourcePoolService.get(name), this.KubernetesConfigMapService.getAccess(KubernetesPortainerConfigMapNamespace, KubernetesPortainerConfigMapConfigName), ]); diff --git a/app/kubernetes/views/volumes/volumes.js b/app/kubernetes/views/volumes/volumes.js index f004c20ff..72060c39a 100644 --- a/app/kubernetes/views/volumes/volumes.js +++ b/app/kubernetes/views/volumes/volumes.js @@ -4,5 +4,6 @@ angular.module('portainer.kubernetes').component('kubernetesVolumesView', { controllerAs: 'ctrl', bindings: { $transition$: '<', + endpoint: '<', }, }); diff --git a/app/kubernetes/views/volumes/volumesController.js b/app/kubernetes/views/volumes/volumesController.js index 4d2b75dbe..dcf10856a 100644 --- a/app/kubernetes/views/volumes/volumesController.js +++ b/app/kubernetes/views/volumes/volumesController.js @@ -21,25 +21,13 @@ function computeSize(volumes) { class KubernetesVolumesController { /* @ngInject */ - constructor( - $async, - $state, - Notifications, - Authentication, - ModalService, - LocalStorage, - EndpointProvider, - KubernetesStorageService, - KubernetesVolumeService, - KubernetesApplicationService - ) { + constructor($async, $state, Notifications, Authentication, ModalService, LocalStorage, KubernetesStorageService, KubernetesVolumeService, KubernetesApplicationService) { this.$async = $async; this.$state = $state; this.Notifications = Notifications; this.Authentication = Authentication; this.ModalService = ModalService; this.LocalStorage = LocalStorage; - this.EndpointProvider = EndpointProvider; this.KubernetesStorageService = KubernetesStorageService; this.KubernetesVolumeService = KubernetesVolumeService; this.KubernetesApplicationService = KubernetesApplicationService; @@ -87,7 +75,7 @@ class KubernetesVolumesController { const [volumes, applications, storages] = await Promise.all([ this.KubernetesVolumeService.get(), this.KubernetesApplicationService.get(), - this.KubernetesStorageService.get(this.state.endpointId), + this.KubernetesStorageService.get(this.endpoint.Id), ]); this.volumes = _.map(volumes, (volume) => { @@ -107,9 +95,7 @@ class KubernetesVolumesController { async onInit() { this.state = { viewReady: false, - // endpointId: this.$transition$.params().endpointId, // TODO: use this when moving to endpointID in URL currentName: this.$state.$current.name, - endpointId: this.EndpointProvider.endpointID(), activeTab: this.LocalStorage.getActiveTab('volumes'), isAdmin: this.Authentication.isAdmin(), }; diff --git a/app/portainer/helpers/endpointHelper.js b/app/portainer/helpers/endpointHelper.js index 0ec117564..10deede58 100644 --- a/app/portainer/helpers/endpointHelper.js +++ b/app/portainer/helpers/endpointHelper.js @@ -35,3 +35,7 @@ export default class EndpointHelper { } } } + +export function isOfflineEndpoint(endpoint) { + return endpoint.Status !== 1; +} diff --git a/app/portainer/rest/stack.js b/app/portainer/rest/stack.js index d41bdf04a..bf8dc29b6 100644 --- a/app/portainer/rest/stack.js +++ b/app/portainer/rest/stack.js @@ -1,26 +1,25 @@ -angular.module('portainer.app').factory('Stack', [ - '$resource', - 'EndpointProvider', - 'API_ENDPOINT_STACKS', - function StackFactory($resource, EndpointProvider, API_ENDPOINT_STACKS) { - 'use strict'; - return $resource( - API_ENDPOINT_STACKS + '/:id/:action/:subaction', - {}, - { - get: { method: 'GET', params: { id: '@id' } }, - query: { method: 'GET', isArray: true }, - create: { method: 'POST', ignoreLoadingBar: true }, - update: { method: 'PUT', params: { id: '@id' }, ignoreLoadingBar: true }, - associate: { method: 'PUT', params: { id: '@id', swarmId: '@swarmId', endpointId: '@endpointId', orphanedRunning: '@orphanedRunning', action: 'associate' } }, - remove: { method: 'DELETE', params: { id: '@id', external: '@external', endpointId: '@endpointId' } }, - getStackFile: { method: 'GET', params: { id: '@id', action: 'file' } }, - migrate: { method: 'POST', params: { id: '@id', action: 'migrate', endpointId: '@endpointId' }, ignoreLoadingBar: true }, - start: { method: 'POST', params: { id: '@id', action: 'start' } }, - stop: { method: 'POST', params: { id: '@id', action: 'stop' } }, - updateGit: { method: 'PUT', params: { id: '@id', action: 'git', subaction: 'redeploy' } }, - updateGitStackSettings: { method: 'POST', params: { id: '@id', action: 'git' }, ignoreLoadingBar: true }, - } - ); - }, -]); +import angular from 'angular'; + +angular.module('portainer.app').factory('Stack', StackFactory); + +/* @ngInject */ +function StackFactory($resource, API_ENDPOINT_STACKS) { + return $resource( + API_ENDPOINT_STACKS + '/:id/:action', + {}, + { + get: { method: 'GET', params: { id: '@id' } }, + query: { method: 'GET', isArray: true }, + create: { method: 'POST', ignoreLoadingBar: true }, + update: { method: 'PUT', params: { id: '@id' }, ignoreLoadingBar: true }, + associate: { method: 'PUT', params: { id: '@id', swarmId: '@swarmId', endpointId: '@endpointId', orphanedRunning: '@orphanedRunning', action: 'associate' } }, + remove: { method: 'DELETE', params: { id: '@id', external: '@external', endpointId: '@endpointId' } }, + getStackFile: { method: 'GET', params: { id: '@id', action: 'file' } }, + migrate: { method: 'POST', params: { id: '@id', action: 'migrate', endpointId: '@endpointId' }, ignoreLoadingBar: true }, + start: { method: 'POST', params: { id: '@id', action: 'start' } }, + stop: { method: 'POST', params: { id: '@id', action: 'stop' } }, + updateGit: { method: 'PUT', params: { id: '@id', action: 'git', subaction: 'redeploy' } }, + updateGitStackSettings: { method: 'POST', params: { id: '@id', action: 'git' }, ignoreLoadingBar: true }, + } + ); +} diff --git a/app/portainer/services/api/templateService.js b/app/portainer/services/api/templateService.js index 04265c85b..e108e92be 100644 --- a/app/portainer/services/api/templateService.js +++ b/app/portainer/services/api/templateService.js @@ -4,12 +4,13 @@ import { TemplateViewModel } from '../../models/template'; angular.module('portainer.app').factory('TemplateService', TemplateServiceFactory); /* @ngInject */ -function TemplateServiceFactory($q, Templates, TemplateHelper, EndpointProvider, ImageHelper, ContainerHelper, EndpointService) { - var service = {}; +function TemplateServiceFactory($q, Templates, TemplateHelper, ImageHelper, ContainerHelper, EndpointService) { + var service = { + templates, + }; - service.templates = function () { + function templates(endpointId) { const deferred = $q.defer(); - const endpointId = EndpointProvider.currentEndpoint().Id; $q.all({ templates: Templates.query().$promise, @@ -36,7 +37,7 @@ function TemplateServiceFactory($q, Templates, TemplateHelper, EndpointProvider, }); return deferred.promise; - }; + } service.templateFile = templateFile; function templateFile(repositoryUrl, composeFilePathInRepository) { diff --git a/app/portainer/services/stateManager.js b/app/portainer/services/stateManager.js index 8cb44ae64..462a43cd0 100644 --- a/app/portainer/services/stateManager.js +++ b/app/portainer/services/stateManager.js @@ -1,236 +1,226 @@ import moment from 'moment'; -angular.module('portainer.app').factory('StateManager', [ - '$q', - '$async', - 'SystemService', - 'InfoHelper', - 'LocalStorage', - 'SettingsService', - 'StatusService', - 'APPLICATION_CACHE_VALIDITY', - 'AgentPingService', - '$analytics', - 'EndpointProvider', - function StateManagerFactory( - $q, - $async, - SystemService, - InfoHelper, - LocalStorage, - SettingsService, - StatusService, - APPLICATION_CACHE_VALIDITY, - AgentPingService, - $analytics, - EndpointProvider - ) { - var manager = {}; +angular.module('portainer.app').factory('StateManager', StateManagerFactory); - var state = { - loading: true, - application: {}, - endpoint: {}, - UI: { - dismissedInfoPanels: {}, - dismissedInfoHash: '', - }, - extensions: [], - }; +/* @ngInject */ +function StateManagerFactory( + $async, + $q, + SystemService, + InfoHelper, + LocalStorage, + SettingsService, + StatusService, + APPLICATION_CACHE_VALIDITY, + AgentPingService, + $analytics, + EndpointProvider +) { + var manager = {}; - manager.setVersionInfo = function (versionInfo) { - state.application.versionStatus = versionInfo; - LocalStorage.storeApplicationState(state.application); - }; + var state = { + loading: true, + application: {}, + endpoint: {}, + UI: { + dismissedInfoPanels: {}, + dismissedInfoHash: '', + }, + extensions: [], + }; - manager.dismissInformationPanel = function (id) { - state.UI.dismissedInfoPanels[id] = true; - LocalStorage.storeUIState(state.UI); - }; + manager.setVersionInfo = function (versionInfo) { + state.application.versionStatus = versionInfo; + LocalStorage.storeApplicationState(state.application); + }; - manager.dismissImportantInformation = function (hash) { - state.UI.dismissedInfoHash = hash; - LocalStorage.storeUIState(state.UI); - }; + manager.dismissInformationPanel = function (id) { + state.UI.dismissedInfoPanels[id] = true; + LocalStorage.storeUIState(state.UI); + }; - manager.getState = function () { - return state; - }; + manager.dismissImportantInformation = function (hash) { + state.UI.dismissedInfoHash = hash; + LocalStorage.storeUIState(state.UI); + }; - manager.clean = function () { - state.endpoint = {}; - state.application = {}; - }; + manager.getState = function () { + return state; + }; - manager.cleanEndpoint = function () { - state.endpoint = {}; - EndpointProvider.clean(); - }; + manager.clean = function () { + state.endpoint = {}; + state.application = {}; + }; - manager.updateLogo = function (logoURL) { - state.application.logo = logoURL; - LocalStorage.storeApplicationState(state.application); - }; + manager.cleanEndpoint = function () { + state.endpoint = {}; + EndpointProvider.clean(); + }; - manager.updateTheme = function (theme) { - state.application.theme = theme; - LocalStorage.storeApplicationState(state.application); - }; + manager.updateLogo = function (logoURL) { + state.application.logo = logoURL; + LocalStorage.storeApplicationState(state.application); + }; - manager.updateSnapshotInterval = function (interval) { - state.application.snapshotInterval = interval; - LocalStorage.storeApplicationState(state.application); - }; + manager.updateTheme = function (theme) { + state.application.theme = theme; + LocalStorage.storeApplicationState(state.application); + }; - manager.updateEnableEdgeComputeFeatures = function updateEnableEdgeComputeFeatures(enableEdgeComputeFeatures) { - state.application.enableEdgeComputeFeatures = enableEdgeComputeFeatures; - LocalStorage.storeApplicationState(state.application); - }; + manager.updateSnapshotInterval = function (interval) { + state.application.snapshotInterval = interval; + LocalStorage.storeApplicationState(state.application); + }; - manager.updateEnableTelemetry = function updateEnableTelemetry(enableTelemetry) { - state.application.enableTelemetry = enableTelemetry; - $analytics.setOptOut(!enableTelemetry); - LocalStorage.storeApplicationState(state.application); - }; + manager.updateEnableEdgeComputeFeatures = function updateEnableEdgeComputeFeatures(enableEdgeComputeFeatures) { + state.application.enableEdgeComputeFeatures = enableEdgeComputeFeatures; + LocalStorage.storeApplicationState(state.application); + }; - function assignStateFromStatusAndSettings(status, settings) { - state.application.version = status.Version; - state.application.edition = status.Edition; - state.application.instanceId = status.InstanceID; + manager.updateEnableTelemetry = function updateEnableTelemetry(enableTelemetry) { + state.application.enableTelemetry = enableTelemetry; + $analytics.setOptOut(!enableTelemetry); + LocalStorage.storeApplicationState(state.application); + }; - state.application.enableTelemetry = settings.EnableTelemetry; - state.application.logo = settings.LogoURL; - state.application.snapshotInterval = settings.SnapshotInterval; - state.application.enableEdgeComputeFeatures = settings.EnableEdgeComputeFeatures; - state.application.validity = moment().unix(); - } + function assignStateFromStatusAndSettings(status, settings) { + state.application.version = status.Version; + state.application.edition = status.Edition; + state.application.instanceId = status.InstanceID; - function loadApplicationState() { - var deferred = $q.defer(); + state.application.enableTelemetry = settings.EnableTelemetry; + state.application.logo = settings.LogoURL; + state.application.snapshotInterval = settings.SnapshotInterval; + state.application.enableEdgeComputeFeatures = settings.EnableEdgeComputeFeatures; + state.application.validity = moment().unix(); + } - $q.all({ - settings: SettingsService.publicSettings(), - status: StatusService.status(), + function loadApplicationState() { + var deferred = $q.defer(); + + $q.all({ + settings: SettingsService.publicSettings(), + status: StatusService.status(), + }) + .then(function success(data) { + var status = data.status; + var settings = data.settings; + assignStateFromStatusAndSettings(status, settings); + LocalStorage.storeApplicationState(state.application); + deferred.resolve(state); }) - .then(function success(data) { - var status = data.status; - var settings = data.settings; - assignStateFromStatusAndSettings(status, settings); - LocalStorage.storeApplicationState(state.application); - deferred.resolve(state); - }) - .catch(function error(err) { - deferred.reject({ msg: 'Unable to retrieve server settings and status', err: err }); - }); - - return deferred.promise; - } - - manager.initialize = initialize; - async function initialize() { - return $async(async () => { - const UIState = LocalStorage.getUIState(); - if (UIState) { - state.UI = UIState; - } - - const endpointState = LocalStorage.getEndpointState(); - if (endpointState) { - state.endpoint = endpointState; - } - - const applicationState = LocalStorage.getApplicationState(); - if (isAppStateValid(applicationState)) { - state.application = applicationState; - } else { - await loadApplicationState(); - } - - state.loading = false; - $analytics.setPortainerStatus(state.application.instanceId, state.application.version); - $analytics.setOptOut(!state.application.enableTelemetry); - return state; + .catch(function error(err) { + deferred.reject({ msg: 'Unable to retrieve server settings and status', err: err }); }); - } - function isAppStateValid(appState) { - if (!appState || !appState.validity) { - return false; - } - const now = moment().unix(); - const cacheValidity = now - appState.validity; - return cacheValidity < APPLICATION_CACHE_VALIDITY; - } + return deferred.promise; + } - function assignExtensions(endpointExtensions) { - var extensions = []; - - for (var i = 0; i < endpointExtensions.length; i++) { - var extension = endpointExtensions[i]; - if (extension.Type === 1) { - extensions.push('storidge'); - } + manager.initialize = initialize; + async function initialize() { + return $async(async () => { + const UIState = LocalStorage.getUIState(); + if (UIState) { + state.UI = UIState; } - return extensions; - } - - manager.updateEndpointState = function (endpoint, extensions) { - var deferred = $q.defer(); - - if (endpoint.Type === 3) { - state.endpoint.name = endpoint.Name; - state.endpoint.mode = { provider: 'AZURE' }; - LocalStorage.storeEndpointState(state.endpoint); - deferred.resolve(); - return deferred.promise; - } else if (endpoint.Type === 5 || endpoint.Type === 6 || endpoint.Type === 7) { - state.endpoint.name = endpoint.Name; - state.endpoint.mode = { provider: 'KUBERNETES' }; - LocalStorage.storeEndpointState(state.endpoint); - deferred.resolve(); - return deferred.promise; + const endpointState = LocalStorage.getEndpointState(); + if (endpointState) { + state.endpoint = endpointState; } - const reload = endpoint.Status === 1 || !endpoint.Snaphosts || !endpoint.Snaphosts.length || !endpoint.Snapshots[0].SnapshotRaw; + const applicationState = LocalStorage.getApplicationState(); + if (isAppStateValid(applicationState)) { + state.application = applicationState; + } else { + await loadApplicationState(); + } - $q.all({ - version: reload ? SystemService.version() : $q.when(endpoint.Snapshots[0].SnapshotRaw.Version), - info: reload ? SystemService.info() : $q.when(endpoint.Snapshots[0].SnapshotRaw.Info), - }) - .then(function success(data) { - var endpointMode = InfoHelper.determineEndpointMode(data.info, endpoint.Type); - var endpointAPIVersion = parseFloat(data.version.ApiVersion); - state.endpoint.mode = endpointMode; - state.endpoint.name = endpoint.Name; - state.endpoint.type = endpoint.Type; - state.endpoint.apiVersion = endpointAPIVersion; - state.endpoint.extensions = assignExtensions(extensions); + state.loading = false; + $analytics.setPortainerStatus(state.application.instanceId, state.application.version); + $analytics.setOptOut(!state.application.enableTelemetry); + return state; + }); + } - 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(); - }) - .catch(function error(err) { - deferred.reject({ msg: 'Unable to connect to the Docker environment', err: err }); - }) - .finally(function final() { - state.loading = false; - }); + function isAppStateValid(appState) { + if (!appState || !appState.validity) { + return false; + } + const now = moment().unix(); + const cacheValidity = now - appState.validity; + return cacheValidity < APPLICATION_CACHE_VALIDITY; + } + function assignExtensions(endpointExtensions) { + var extensions = []; + + for (var i = 0; i < endpointExtensions.length; i++) { + var extension = endpointExtensions[i]; + if (extension.Type === 1) { + extensions.push('storidge'); + } + } + + return extensions; + } + + manager.updateEndpointState = function (endpoint, extensions) { + var deferred = $q.defer(); + + if (endpoint.Type === 3) { + state.endpoint.name = endpoint.Name; + state.endpoint.mode = { provider: 'AZURE' }; + LocalStorage.storeEndpointState(state.endpoint); + deferred.resolve(); return deferred.promise; - }; + } else if (endpoint.Type === 5 || endpoint.Type === 6 || endpoint.Type === 7) { + state.endpoint.name = endpoint.Name; + state.endpoint.mode = { provider: 'KUBERNETES' }; + LocalStorage.storeEndpointState(state.endpoint); + deferred.resolve(); + return deferred.promise; + } - manager.getAgentApiVersion = function getAgentApiVersion() { - return state.endpoint.agentApiVersion; - }; + const reload = endpoint.Status === 1 || !endpoint.Snaphosts || !endpoint.Snaphosts.length || !endpoint.Snapshots[0].SnapshotRaw; - return manager; - }, -]); + $q.all({ + version: reload ? SystemService.version() : $q.when(endpoint.Snapshots[0].SnapshotRaw.Version), + info: reload ? SystemService.info() : $q.when(endpoint.Snapshots[0].SnapshotRaw.Info), + }) + .then(function success(data) { + var endpointMode = InfoHelper.determineEndpointMode(data.info, endpoint.Type); + var endpointAPIVersion = parseFloat(data.version.ApiVersion); + state.endpoint.mode = endpointMode; + state.endpoint.name = endpoint.Name; + state.endpoint.type = endpoint.Type; + state.endpoint.apiVersion = endpointAPIVersion; + state.endpoint.extensions = assignExtensions(extensions); + + 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(); + }) + .catch(function error(err) { + deferred.reject({ msg: 'Unable to connect to the Docker environment', err: err }); + }) + .finally(function final() { + state.loading = false; + }); + + return deferred.promise; + }; + + manager.getAgentApiVersion = function getAgentApiVersion() { + return state.endpoint.agentApiVersion; + }; + + return manager; +} diff --git a/app/portainer/views/custom-templates/custom-templates-view/customTemplatesViewController.js b/app/portainer/views/custom-templates/custom-templates-view/customTemplatesViewController.js index 4ac2a1c01..a4290e9e1 100644 --- a/app/portainer/views/custom-templates/custom-templates-view/customTemplatesViewController.js +++ b/app/portainer/views/custom-templates/custom-templates-view/customTemplatesViewController.js @@ -11,7 +11,6 @@ class CustomTemplatesViewController { $state, Authentication, CustomTemplateService, - EndpointProvider, FormValidator, ModalService, NetworkService, @@ -26,7 +25,6 @@ class CustomTemplatesViewController { this.$state = $state; this.Authentication = Authentication; this.CustomTemplateService = CustomTemplateService; - this.EndpointProvider = EndpointProvider; this.FormValidator = FormValidator; this.ModalService = ModalService; this.NetworkService = NetworkService; @@ -132,7 +130,7 @@ class CustomTemplatesViewController { } const stackName = this.formValues.name; - const endpointId = this.EndpointProvider.endpointID(); + const endpointId = this.endpoint.Id; this.state.actionInProgress = true; diff --git a/app/portainer/views/custom-templates/custom-templates-view/index.js b/app/portainer/views/custom-templates/custom-templates-view/index.js index 16b00aacd..a3f3d18c4 100644 --- a/app/portainer/views/custom-templates/custom-templates-view/index.js +++ b/app/portainer/views/custom-templates/custom-templates-view/index.js @@ -3,4 +3,7 @@ import CustomTemplatesViewController from './customTemplatesViewController.js'; angular.module('portainer.app').component('customTemplatesView', { templateUrl: './customTemplatesView.html', controller: CustomTemplatesViewController, + bindings: { + endpoint: '<', + }, }); diff --git a/app/portainer/views/endpoints/edit/endpointController.js b/app/portainer/views/endpoints/edit/endpointController.js index 6163d5d71..95d585ddd 100644 --- a/app/portainer/views/endpoints/edit/endpointController.js +++ b/app/portainer/views/endpoints/edit/endpointController.js @@ -19,7 +19,6 @@ function EndpointController( EndpointService, GroupService, TagService, - EndpointProvider, Notifications, Authentication, SettingsService, @@ -191,7 +190,6 @@ function EndpointController( EndpointService.updateEndpoint(endpoint.Id, payload).then( function success() { Notifications.success('Environment updated', $scope.endpoint.Name); - EndpointProvider.setEndpointPublicURL(endpoint.PublicURL); $state.go('portainer.endpoints', {}, { reload: true }); }, function error(err) { diff --git a/app/portainer/views/init/endpoint/initEndpointController.js b/app/portainer/views/init/endpoint/initEndpointController.js index 2fbd4ec27..610ef467a 100644 --- a/app/portainer/views/init/endpoint/initEndpointController.js +++ b/app/portainer/views/init/endpoint/initEndpointController.js @@ -9,12 +9,11 @@ require('./includes/agent.html'); class InitEndpointController { /* @ngInject */ - constructor($async, $scope, $state, EndpointService, EndpointProvider, StateManager, Notifications) { + constructor($async, $scope, $state, EndpointService, StateManager, Notifications) { this.$async = $async; this.$scope = $scope; this.$state = $state; this.EndpointService = EndpointService; - this.EndpointProvider = EndpointProvider; this.StateManager = StateManager; this.Notifications = Notifications; diff --git a/app/portainer/views/registries/create/createRegistryController.js b/app/portainer/views/registries/create/createRegistryController.js index da45db1af..b2e878e9a 100644 --- a/app/portainer/views/registries/create/createRegistryController.js +++ b/app/portainer/views/registries/create/createRegistryController.js @@ -3,8 +3,8 @@ import { RegistryCreateFormValues } from 'Portainer/models/registry'; class CreateRegistryController { /* @ngInject */ - constructor($async, $state, EndpointProvider, Notifications, RegistryService, RegistryGitlabService) { - Object.assign(this, { $async, $state, EndpointProvider, Notifications, RegistryService, RegistryGitlabService }); + constructor($async, $state, Notifications, RegistryService, RegistryGitlabService) { + Object.assign(this, { $async, $state, Notifications, RegistryService, RegistryGitlabService }); this.RegistryTypes = RegistryTypes; this.state = { @@ -17,6 +17,7 @@ class CreateRegistryController { selectedItems: [], }, originViewReference: 'portainer.registries', + originalEndpointId: null, }; this.createRegistry = this.createRegistry.bind(this); @@ -105,7 +106,7 @@ class CreateRegistryController { this.state.actionInProgress = true; await this.RegistryService.createGitlabRegistries(this.model, this.state.gitlab.selectedItems); this.Notifications.success('Registries successfully created'); - this.$state.go(this.state.originViewReference, { endpointId: this.EndpointProvider.endpointID() }); + this.$state.go(this.state.originViewReference, { endpointId: this.state.originalEndpointId }); } catch (err) { this.Notifications.error('Failure', err, 'Unable to create registries'); this.state.actionInProgress = false; @@ -119,7 +120,7 @@ class CreateRegistryController { this.state.actionInProgress = true; await this.RegistryService.createRegistry(this.model); this.Notifications.success('Registry successfully created'); - this.$state.go(this.state.originViewReference, { endpointId: this.EndpointProvider.endpointID() }); + this.$state.go(this.state.originViewReference, { endpointId: this.state.originalEndpointId }); } catch (err) { this.Notifications.error('Failure', err, 'Unable to create registry'); this.state.actionInProgress = false; @@ -130,9 +131,12 @@ class CreateRegistryController { $onInit() { this.model = new RegistryCreateFormValues(); - const origin = this.$transition$.originalTransition().from(); - if (origin.name && /^[a-z]+\.registries$/.test(origin.name)) { - this.state.originViewReference = origin; + const from = this.$transition$.from(); + const params = this.$transition$.params('from'); + + if (from.name && /^[a-z]+\.registries$/.test(from.name)) { + this.state.originViewReference = from; + this.state.originalEndpointId = params.endpointId || null; } } } diff --git a/app/portainer/views/stacks/create/createStackController.js b/app/portainer/views/stacks/create/createStackController.js index 0183a6b97..edd0db982 100644 --- a/app/portainer/views/stacks/create/createStackController.js +++ b/app/portainer/views/stacks/create/createStackController.js @@ -19,13 +19,13 @@ angular FormValidator, ResourceControlService, FormHelper, - EndpointProvider, StackHelper, ContainerHelper, CustomTemplateService, ContainerService, WebhookHelper, - clipboard + clipboard, + endpoint ) { $scope.onChangeTemplateId = onChangeTemplateId; $scope.buildAnalyticsProperties = buildAnalyticsProperties; @@ -307,12 +307,7 @@ angular $scope.state.StackType = 1; } - try { - const endpoint = EndpointProvider.currentEndpoint(); - $scope.composeSyntaxMaxVersion = endpoint.ComposeSyntaxMaxVersion; - } catch (err) { - Notifications.error('Failure', err, 'Unable to retrieve the ComposeSyntaxMaxVersion'); - } + $scope.composeSyntaxMaxVersion = endpoint.ComposeSyntaxMaxVersion; try { const containers = await ContainerService.containers(true); diff --git a/app/portainer/views/stacks/edit/stack.html b/app/portainer/views/stacks/edit/stack.html index 02a922bb7..c599d32ae 100644 --- a/app/portainer/views/stacks/edit/stack.html +++ b/app/portainer/views/stacks/edit/stack.html @@ -116,7 +116,7 @@ ng-if="regular && endpoints.length > 0" endpoints="endpoints" groups="groups" - current-endpoint-id="currentEndpointId" + current-endpoint-id="endpoint.Id" on-duplicate="duplicateStack(name, endpointId)" on-migrate="migrateStack(name, endpointId)" yaml-error="state.yamlError" @@ -216,6 +216,7 @@ show-host-column="false" show-add-action="false" not-auto-focus="true" + endpoint-public-url="endpoint.PublicURL" > @@ -236,6 +237,8 @@ show-add-action="false" show-stack-column="false" not-auto-focus="true" + endpoint-public-url="endpoint.PublicURL" + endpoint-id="endpoint.Id" > diff --git a/app/portainer/views/stacks/edit/stackController.js b/app/portainer/views/stacks/edit/stackController.js index f11546a34..d7e2d3c96 100644 --- a/app/portainer/views/stacks/edit/stackController.js +++ b/app/portainer/views/stacks/edit/stackController.js @@ -24,6 +24,7 @@ angular.module('portainer.app').controller('StackController', [ 'ResourceControlService', 'Authentication', 'ContainerHelper', + 'endpoint', function ( $async, $q, @@ -47,8 +48,10 @@ angular.module('portainer.app').controller('StackController', [ StackHelper, ResourceControlService, Authentication, - ContainerHelper + ContainerHelper, + endpoint ) { + $scope.endpoint = endpoint; $scope.state = { actionInProgress: false, migrationInProgress: false, @@ -79,16 +82,18 @@ angular.module('portainer.app').controller('StackController', [ $scope.formValues.Env = value; } - $scope.duplicateStack = function duplicateStack(name, endpointId) { + $scope.duplicateStack = function duplicateStack(name, targetEndpointId) { var stack = $scope.stack; var env = FormHelper.removeInvalidEnvVars($scope.formValues.Env); - EndpointProvider.setEndpointID(endpointId); + // sets the targetEndpointID as global for interceptors + EndpointProvider.setEndpointID(targetEndpointId); - return StackService.duplicateStack(name, $scope.stackFileContent, env, endpointId, stack.Type).then(onDuplicationSuccess).catch(notifyOnError); + return StackService.duplicateStack(name, $scope.stackFileContent, env, targetEndpointId, stack.Type).then(onDuplicationSuccess).catch(notifyOnError); function onDuplicationSuccess() { Notifications.success('Stack successfully duplicated'); $state.go('docker.stacks', {}, { reload: true }); + // sets back the original endpointID as global for interceptors EndpointProvider.setEndpointID(stack.EndpointId); } @@ -132,11 +137,10 @@ angular.module('portainer.app').controller('StackController', [ }); }; - function migrateStack(name, endpointId) { - var stack = $scope.stack; - var targetEndpointId = endpointId; + function migrateStack(name, targetEndpointId) { + const stack = $scope.stack; - var migrateRequest = StackService.migrateSwarmStack; + let migrateRequest = StackService.migrateSwarmStack; if (stack.Type === 2) { migrateRequest = StackService.migrateComposeStack; } @@ -145,9 +149,8 @@ angular.module('portainer.app').controller('StackController', [ // The EndpointID property is not available for these stacks, we can pass // the current endpoint identifier as a part of the migrate request. It will be used if // the EndpointID property is not defined on the stack. - var originalEndpointId = EndpointProvider.endpointID(); if (stack.EndpointId === 0) { - stack.EndpointId = originalEndpointId; + stack.EndpointId = endpoint.Id; } $scope.state.migrationInProgress = true; @@ -213,9 +216,8 @@ angular.module('portainer.app').controller('StackController', [ // The EndpointID property is not available for these stacks, we can pass // the current endpoint identifier as a part of the update request. It will be used if // the EndpointID property is not defined on the stack. - var endpointId = EndpointProvider.endpointID(); if (stack.EndpointId === 0) { - stack.EndpointId = endpointId; + stack.EndpointId = endpoint.Id; } $scope.state.actionInProgress = true; @@ -433,8 +435,6 @@ angular.module('portainer.app').controller('StackController', [ var stackName = $transition$.params().name; $scope.stackName = stackName; - $scope.currentEndpointId = EndpointProvider.endpointID(); - const regular = $transition$.params().regular == 'true'; $scope.regular = regular; @@ -456,12 +456,7 @@ angular.module('portainer.app').controller('StackController', [ loadStack(stackId); } - try { - const endpoint = EndpointProvider.currentEndpoint(); - $scope.composeSyntaxMaxVersion = endpoint.ComposeSyntaxMaxVersion; - } catch (err) { - Notifications.error('Failure', err, 'Unable to retrieve the ComposeSyntaxMaxVersion'); - } + $scope.composeSyntaxMaxVersion = endpoint.ComposeSyntaxMaxVersion; $scope.stackType = $transition$.params().type; } diff --git a/app/portainer/views/stacks/stacksController.js b/app/portainer/views/stacks/stacksController.js index afbf981f1..23b192dd2 100644 --- a/app/portainer/views/stacks/stacksController.js +++ b/app/portainer/views/stacks/stacksController.js @@ -1,7 +1,9 @@ +import { isOfflineEndpoint } from '@/portainer/helpers/endpointHelper'; + angular.module('portainer.app').controller('StacksController', StacksController); /* @ngInject */ -function StacksController($scope, $state, Notifications, StackService, ModalService, EndpointProvider, Authentication, endpoint) { +function StacksController($scope, $state, Notifications, StackService, ModalService, Authentication, endpoint) { $scope.removeAction = function (selectedItems) { ModalService.confirmDeletion('Do you want to remove the selected stack(s)? Associated services will be removed as well.', function onConfirm(confirmed) { if (!confirmed) { @@ -12,8 +14,8 @@ function StacksController($scope, $state, Notifications, StackService, ModalServ }; function deleteSelectedStacks(stacks) { - var endpointId = EndpointProvider.endpointID(); - var actionCount = stacks.length; + const endpointId = endpoint.Id; + let actionCount = stacks.length; angular.forEach(stacks, function (stack) { StackService.remove(stack, stack.External, endpointId) .then(function success() { @@ -39,15 +41,14 @@ function StacksController($scope, $state, Notifications, StackService, ModalServ $scope.getStacks = getStacks; function getStacks() { - var endpointMode = $scope.applicationState.endpoint.mode; - var endpointId = EndpointProvider.endpointID(); + const endpointMode = $scope.applicationState.endpoint.mode; + const endpointId = endpoint.Id; const includeOrphanedStacks = Authentication.isAdmin(); StackService.stacks(true, endpointMode.provider === 'DOCKER_SWARM_MODE' && endpointMode.role === 'MANAGER', endpointId, includeOrphanedStacks) - .then(function success(data) { - var stacks = data; + .then(function success(stacks) { $scope.stacks = stacks; - $scope.offlineMode = EndpointProvider.offlineMode(); + $scope.offlineMode = isOfflineEndpoint(endpoint); }) .catch(function error(err) { $scope.stacks = []; diff --git a/app/portainer/views/templates/templatesController.js b/app/portainer/views/templates/templatesController.js index d6da3a689..a9d9964cf 100644 --- a/app/portainer/views/templates/templatesController.js +++ b/app/portainer/views/templates/templatesController.js @@ -276,9 +276,10 @@ angular.module('portainer.app').controller('TemplatesController', [ var endpointMode = $scope.applicationState.endpoint.mode; var apiVersion = $scope.applicationState.endpoint.apiVersion; + const endpointId = +$state.params.endpointId; $q.all({ - templates: TemplateService.templates(), + templates: TemplateService.templates(endpointId), volumes: VolumeService.getVolumes(), networks: NetworkService.networks( endpointMode.provider === 'DOCKER_STANDALONE' || endpointMode.provider === 'DOCKER_SWARM_MODE', @@ -296,7 +297,7 @@ angular.module('portainer.app').controller('TemplatesController', [ }) .catch(function error(err) { $scope.templates = []; - Notifications.error('Failure', err, 'An error occured during apps initialization.'); + Notifications.error('Failure', err, 'An error occurred during apps initialization.'); }); }