@rancher/shell 0.1.2 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/brand/suse/dark/rancher-logo.svg +1 -148
- package/assets/brand/suse/rancher-logo.svg +1 -130
- package/assets/images/featured/img1.jpg +0 -0
- package/assets/images/featured.jpg +0 -0
- package/assets/images/generic-plugin.svg +7 -0
- package/assets/styles/themes/_dark.scss +3 -0
- package/assets/styles/themes/_light.scss +3 -0
- package/assets/styles/themes/_suse.scss +1 -1
- package/assets/translations/en-us.yaml +209 -813
- package/assets/translations/zh-hans.yaml +28 -792
- package/components/ActionMenu.vue +3 -3
- package/components/AsyncButton.vue +17 -2
- package/components/ButtonDropdown.vue +4 -0
- package/components/Carousel.vue +291 -0
- package/components/CodeMirror.vue +6 -8
- package/components/CommunityLinks.vue +70 -19
- package/components/ContainerResourceLimit.vue +14 -0
- package/components/CruResource.vue +11 -3
- package/components/Dialog.vue +102 -0
- package/components/ExplorerMembers.vue +121 -0
- package/components/ExplorerProjectsNamespaces.vue +404 -0
- package/components/GrafanaDashboard.vue +17 -2
- package/components/IconMessage.vue +9 -1
- package/components/LocaleSelector.vue +114 -0
- package/components/PromptModal.vue +2 -3
- package/components/ResourceList/index.vue +1 -1
- package/components/ResourceTable.vue +9 -7
- package/components/SimpleBox.vue +6 -4
- package/components/SingleClusterInfo.vue +1 -1
- package/components/SortableTable/index.vue +18 -25
- package/components/SortableTable/selection.js +1 -0
- package/components/Tabbed/Tab.vue +5 -0
- package/components/Tabbed/index.vue +29 -2
- package/components/auth/AzureWarning.vue +5 -1
- package/components/auth/Principal.vue +2 -1
- package/components/auth/RoleDetailEdit.vue +18 -11
- package/components/fleet/FleetBundles.vue +8 -3
- package/components/fleet/FleetRepos.vue +0 -2
- package/components/fleet/FleetSummary.vue +6 -0
- package/components/form/KeyValue.vue +80 -58
- package/components/form/NameNsDescription.vue +12 -8
- package/components/form/NodeScheduling.vue +1 -1
- package/components/form/ResourceTabs/index.vue +5 -1
- package/components/form/WorkloadPorts.vue +1 -1
- package/components/formatter/ClusterLink.vue +3 -7
- package/components/formatter/WorkloadHealthScale.vue +1 -1
- package/components/nav/Header.vue +9 -9
- package/components/nav/NamespaceFilter.vue +10 -7
- package/components/nav/TopLevelMenu.vue +10 -65
- package/components/nav/WindowManager/ContainerLogs.vue +1 -1
- package/config/footer.js +13 -14
- package/config/labels-annotations.js +2 -1
- package/config/product/explorer.js +5 -4
- package/config/product/harvester-manager.js +64 -2
- package/config/product/legacy.js +0 -47
- package/config/product/manager.js +9 -0
- package/config/product/multi-cluster-apps.js +0 -12
- package/config/product/settings.js +12 -1
- package/config/product/uiplugins.js +17 -0
- package/config/settings.js +37 -72
- package/config/table-headers.js +0 -1
- package/config/types.js +9 -25
- package/config/uiplugins.js +60 -0
- package/content/docs/en-us/getting-started.md +1 -26
- package/core/plugin-routes.ts +34 -22
- package/core/plugin.ts +15 -3
- package/core/plugins-loader.js +2 -0
- package/core/plugins.js +91 -36
- package/core/types.ts +7 -1
- package/detail/provisioning.cattle.io.cluster.vue +15 -2
- package/detail/workload/index.vue +11 -5
- package/{components/dialog → dialog}/AddClusterMemberDialog.vue +0 -0
- package/{components/dialog → dialog}/AddCustomBadgeDialog.vue +0 -0
- package/{components/dialog → dialog}/AddProjectMemberDialog.vue +0 -0
- package/{components/dialog → dialog}/AddonConfigConfirmationDialog.vue +0 -0
- package/dialog/DiagnosticTimingsDialog.vue +116 -0
- package/{components/dialog → dialog}/DrainNode.vue +0 -0
- package/{components/dialog → dialog}/ForceMachineRemoveDialog.vue +0 -0
- package/{components/dialog → dialog}/GenericPrompt.vue +0 -0
- package/{components/dialog → dialog}/RollbackWorkloadDialog.vue +0 -0
- package/{components/dialog → dialog}/RotateCertificatesDialog.vue +9 -3
- package/{components/dialog → dialog}/RotateEncryptionKeyDialog.vue +0 -0
- package/{components/dialog → dialog}/SaveAsRKETemplateDialog.vue +0 -0
- package/{components/dialog → dialog}/ScaleMachineDownDialog.vue +0 -0
- package/edit/auth/azuread.vue +44 -6
- package/edit/management.cattle.io.project.vue +2 -2
- package/edit/namespace.vue +17 -10
- package/edit/networking.k8s.io.ingress/index.vue +2 -2
- package/edit/persistentvolume/index.vue +3 -0
- package/edit/persistentvolumeclaim.vue +1 -0
- package/edit/pod.vue +27 -0
- package/edit/provisioning.cattle.io.cluster/CustomCommand.vue +1 -1
- package/edit/provisioning.cattle.io.cluster/MachinePool.vue +33 -5
- package/edit/provisioning.cattle.io.cluster/rke2.vue +76 -5
- package/edit/service.vue +8 -6
- package/edit/workload/__tests__/Upgrading.test.ts +1 -0
- package/edit/workload/index.vue +375 -15
- package/edit/workload/mixins/workload.js +62 -7
- package/edit/workload/storage/ContainerMountPaths.vue +240 -0
- package/edit/workload/storage/Mount.vue +1 -0
- package/edit/workload/storage/awsElasticBlockStore.vue +20 -1
- package/edit/workload/storage/azureDisk.vue +22 -2
- package/edit/workload/storage/azureFile.vue +20 -2
- package/edit/workload/storage/csi/index.vue +23 -1
- package/edit/workload/storage/gcePersistentDisk.vue +20 -2
- package/edit/workload/storage/index.vue +23 -49
- package/edit/workload/storage/persistentVolumeClaim/persistentvolumeclaim.vue +1 -0
- package/edit/workload/storage/vsphereVolume.vue +11 -1
- package/layouts/default.vue +63 -32
- package/layouts/error.vue +5 -1
- package/layouts/home.vue +14 -5
- package/layouts/plain.vue +10 -5
- package/list/harvesterhci.io.management.cluster.vue +74 -33
- package/list/management.cattle.io.setting.vue +3 -3
- package/list/namespace.vue +3 -5
- package/list/provisioning.cattle.io.cluster.vue +1 -1
- package/machine-config/amazonec2.vue +2 -0
- package/machine-config/harvester.vue +100 -51
- package/middleware/authenticated.js +56 -52
- package/mixins/form-validation.js +1 -1
- package/mixins/resource-fetch.js +3 -1
- package/models/catalog.cattle.io.uiplugin.js +34 -0
- package/models/cluster/node.js +25 -2
- package/models/fleet.cattle.io.bundle.js +27 -20
- package/models/harvesterhci.io.management.cluster.js +200 -5
- package/models/management.cattle.io.cluster.js +1 -1
- package/models/management.cattle.io.clusterroletemplatebinding.js +9 -0
- package/models/management.cattle.io.project.js +23 -2
- package/models/namespace.js +19 -3
- package/models/pod.js +19 -2
- package/models/provisioning.cattle.io.cluster.js +16 -6
- package/models/workload.js +9 -246
- package/models/workload.service.js +314 -0
- package/nuxt.config.js +80 -34
- package/package.json +107 -108
- package/pages/auth/login.vue +11 -2
- package/pages/auth/setup.vue +1 -1
- package/pages/c/_cluster/_product/members/index.vue +3 -93
- package/pages/c/_cluster/_product/projectsnamespaces.vue +6 -403
- package/pages/c/_cluster/apps/charts/index.vue +46 -1
- package/pages/c/_cluster/apps/charts/install.vue +10 -9
- package/pages/c/_cluster/explorer/index.vue +72 -9
- package/pages/c/_cluster/explorer/tools/index.vue +12 -5
- package/pages/c/_cluster/mcapps/index.vue +1 -1
- package/pages/c/_cluster/settings/brand.vue +0 -40
- package/pages/c/_cluster/settings/links.vue +200 -0
- package/pages/c/_cluster/settings/performance.vue +19 -16
- package/pages/c/_cluster/uiplugins/DeveloperInstallDialog.vue +232 -0
- package/pages/c/_cluster/uiplugins/InstallDialog.vue +242 -0
- package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +284 -0
- package/pages/c/_cluster/uiplugins/RemoveUIPlugins.vue +130 -0
- package/pages/c/_cluster/uiplugins/SetupUIPlugins.vue +253 -0
- package/pages/c/_cluster/uiplugins/UninstallDialog.vue +115 -0
- package/pages/c/_cluster/uiplugins/index.vue +694 -0
- package/pages/diagnostic.vue +185 -101
- package/pages/docs/_doc.vue +3 -1
- package/pages/fail-whale.vue +1 -10
- package/pages/home.vue +21 -56
- package/pages/index.vue +18 -4
- package/pages/prefs.vue +108 -86
- package/pages/safeMode.vue +17 -0
- package/pages/support/index.vue +23 -15
- package/pkg/auto-import.js +44 -7
- package/pkg/dynamic-importer.lib.js +4 -0
- package/pkg/dynamic-plugin-loader.js +28 -0
- package/pkg/import.js +2 -2
- package/pkg/model-loader-require.lib.js +3 -0
- package/pkg/vue.config.js +9 -6
- package/plugins/dashboard-store/model-loader-require.js +12 -0
- package/plugins/dashboard-store/model-loader.js +4 -1
- package/plugins/dashboard-store/resource-class.js +12 -5
- package/plugins/formatters.js +15 -0
- package/plugins/plugin.js +56 -4
- package/plugins/steve/actions.js +1 -1
- package/plugins/steve/index.js +6 -4
- package/plugins/steve/mutations.js +1 -1
- package/plugins/steve/subscribe.js +89 -56
- package/plugins/steve/web-worker.steve-sub-worker.js +24 -15
- package/promptRemove/management.cattle.io.globalrole.vue +47 -0
- package/promptRemove/management.cattle.io.roletemplate.vue +47 -0
- package/promptRemove/mixin/roleDeletionCheck.js +97 -0
- package/rancher-components/Form/Checkbox/Checkbox.test.ts +77 -0
- package/rancher-components/Form/Checkbox/Checkbox.vue +12 -2
- package/scripts/build-pkg.sh +48 -2
- package/scripts/drone-build-pkg.sh +31 -0
- package/scripts/publish-shell.sh +11 -12
- package/scripts/serve-pkgs +17 -10
- package/scripts/sync-shell-deps +37 -0
- package/store/catalog.js +12 -9
- package/store/i18n.js +26 -12
- package/store/index.js +4 -181
- package/store/prefs.js +46 -2
- package/store/type-map.js +47 -33
- package/store/uiplugins.ts +15 -61
- package/utils/__tests__/object.test.ts +0 -24
- package/utils/__tests__/selector.test.ts +1 -1
- package/utils/cluster.js +1 -1
- package/utils/custom-validators.js +1 -12
- package/utils/dynamic-importer.js +5 -1
- package/utils/grafana.js +2 -6
- package/utils/socket.js +41 -20
- package/utils/string.js +1 -7
- package/utils/validators/formRules/__tests__/index.test.ts +108 -0
- package/utils/validators/formRules/index.ts +9 -1
- package/utils/validators/setting.js +0 -35
- package/yarn-error.log +195 -0
- package/components/FilterLabel.vue +0 -254
- package/components/HarvesterUpgradeProgressBarList.vue +0 -109
- package/components/VMConsoleBar.vue +0 -87
- package/components/dialog/harvester/AddHotplugModal.vue +0 -159
- package/components/dialog/harvester/BackupModal.vue +0 -117
- package/components/dialog/harvester/CloneTemplate.vue +0 -125
- package/components/dialog/harvester/EjectCDROMDialog.vue +0 -157
- package/components/dialog/harvester/ExportImageDialog.vue +0 -152
- package/components/dialog/harvester/MaintenanceDialog.vue +0 -94
- package/components/dialog/harvester/MigrationDialog.vue +0 -154
- package/components/dialog/harvester/RestoreDialog.vue +0 -153
- package/components/dialog/harvester/SupportBundle.vue +0 -217
- package/components/dialog/harvester/UnplugVolume.vue +0 -108
- package/components/form/SerialConsole/index.vue +0 -267
- package/components/formatter/AttachVMWithName.vue +0 -46
- package/components/formatter/CloudInitType.vue +0 -27
- package/components/formatter/HarvesterBackupTargetValidation.vue +0 -43
- package/components/formatter/HarvesterCPUUsed.vue +0 -122
- package/components/formatter/HarvesterDiskState.vue +0 -66
- package/components/formatter/HarvesterHostName.vue +0 -66
- package/components/formatter/HarvesterIpAddress.vue +0 -90
- package/components/formatter/HarvesterMemoryUsed.vue +0 -140
- package/components/formatter/HarvesterMigrationState.vue +0 -85
- package/components/formatter/HarvesterNodeName.vue +0 -49
- package/components/formatter/HarvesterStorageUsed.vue +0 -194
- package/components/formatter/HarvesterVmState.vue +0 -123
- package/components/nav/HarvesterUpgrade.vue +0 -232
- package/components/novnc/NovncConsole.vue +0 -93
- package/components/novnc/NovncConsoleItem.vue +0 -89
- package/components/novnc/NovncConsoleWrapper.vue +0 -243
- package/config/harvester-map.js +0 -44
- package/config/harvester-table-headers.js +0 -27
- package/config/product/harvester.js +0 -305
- package/detail/harvesterhci.io.host/HarvesterHostBasic.vue +0 -364
- package/detail/harvesterhci.io.host/HarvesterHostDisk.vue +0 -200
- package/detail/harvesterhci.io.host/HarvesterHostNetwork.vue +0 -89
- package/detail/harvesterhci.io.host/VirtualMachineInstance.vue +0 -134
- package/detail/harvesterhci.io.host/index.vue +0 -243
- package/detail/harvesterhci.io.virtualmachinebackup/index.vue +0 -221
- package/detail/harvesterhci.io.virtualmachineimage.vue +0 -118
- package/detail/kubevirt.io.virtualmachine/VirtualMachineTabs/VirtualMachineBasics.vue +0 -279
- package/detail/kubevirt.io.virtualmachine/VirtualMachineTabs/VirtualMachineEvents.vue +0 -75
- package/detail/kubevirt.io.virtualmachine/VirtualMachineTabs/VirtualMachineKeypairs.vue +0 -114
- package/detail/kubevirt.io.virtualmachine/VirtualMachineTabs/VirtualMachineMigration.vue +0 -79
- package/detail/kubevirt.io.virtualmachine/index.vue +0 -213
- package/edit/harvesterhci.io.cloudtemplate.vue +0 -123
- package/edit/harvesterhci.io.host/HarvesterDisk.vue +0 -262
- package/edit/harvesterhci.io.host/index.vue +0 -533
- package/edit/harvesterhci.io.keypair.vue +0 -112
- package/edit/harvesterhci.io.managedchart/index.vue +0 -25
- package/edit/harvesterhci.io.managedchart/rancher-monitoring.vue +0 -172
- package/edit/harvesterhci.io.networkattachmentdefinition.vue +0 -210
- package/edit/harvesterhci.io.setting/additional-ca.vue +0 -36
- package/edit/harvesterhci.io.setting/backup-target.vue +0 -182
- package/edit/harvesterhci.io.setting/http-proxy.vue +0 -79
- package/edit/harvesterhci.io.setting/index.vue +0 -201
- package/edit/harvesterhci.io.setting/overcommit-config.vue +0 -94
- package/edit/harvesterhci.io.setting/ssl-certificates.vue +0 -117
- package/edit/harvesterhci.io.setting/ssl-parameters.vue +0 -161
- package/edit/harvesterhci.io.setting/support-bundle-image.vue +0 -134
- package/edit/harvesterhci.io.setting/support-bundle-namespaces.vue +0 -73
- package/edit/harvesterhci.io.setting/vip-pools.vue +0 -244
- package/edit/harvesterhci.io.setting/vm-force-reset-policy.vue +0 -81
- package/edit/harvesterhci.io.virtualmachinebackup.vue +0 -256
- package/edit/harvesterhci.io.virtualmachineimage.vue +0 -364
- package/edit/harvesterhci.io.virtualmachinetemplateversion.vue +0 -340
- package/edit/harvesterhci.io.volume.vue +0 -195
- package/edit/kubevirt.io.virtualmachine/VirtualMachineAccessCredentials/AccessCredentialsUsers.vue +0 -190
- package/edit/kubevirt.io.virtualmachine/VirtualMachineAccessCredentials/index.vue +0 -212
- package/edit/kubevirt.io.virtualmachine/VirtualMachineAccessCredentials/type/basicAuth.vue +0 -94
- package/edit/kubevirt.io.virtualmachine/VirtualMachineAccessCredentials/type/sshkey.vue +0 -85
- package/edit/kubevirt.io.virtualmachine/VirtualMachineCloudConfig/DataTemplate.vue +0 -153
- package/edit/kubevirt.io.virtualmachine/VirtualMachineCloudConfig/index.vue +0 -279
- package/edit/kubevirt.io.virtualmachine/VirtualMachineCpuMemory.vue +0 -113
- package/edit/kubevirt.io.virtualmachine/VirtualMachineNetwork/__tests__/HarvesterEditNetwork.test.ts +0 -41
- package/edit/kubevirt.io.virtualmachine/VirtualMachineNetwork/base.vue +0 -281
- package/edit/kubevirt.io.virtualmachine/VirtualMachineNetwork/index.vue +0 -142
- package/edit/kubevirt.io.virtualmachine/VirtualMachineReserved.vue +0 -54
- package/edit/kubevirt.io.virtualmachine/VirtualMachineSSHKey.vue +0 -256
- package/edit/kubevirt.io.virtualmachine/VirtualMachineVolume/index.vue +0 -391
- package/edit/kubevirt.io.virtualmachine/VirtualMachineVolume/type/__tests__/HarvesterEditContainer.test.ts +0 -40
- package/edit/kubevirt.io.virtualmachine/VirtualMachineVolume/type/__tests__/HarvesterEditExisting.test.ts +0 -102
- package/edit/kubevirt.io.virtualmachine/VirtualMachineVolume/type/__tests__/HarvesterEditVMImage.test.ts +0 -117
- package/edit/kubevirt.io.virtualmachine/VirtualMachineVolume/type/__tests__/HarvesterEditVolume.test.ts +0 -74
- package/edit/kubevirt.io.virtualmachine/VirtualMachineVolume/type/container.vue +0 -132
- package/edit/kubevirt.io.virtualmachine/VirtualMachineVolume/type/existing.vue +0 -303
- package/edit/kubevirt.io.virtualmachine/VirtualMachineVolume/type/vmImage.vue +0 -285
- package/edit/kubevirt.io.virtualmachine/VirtualMachineVolume/type/volume.vue +0 -188
- package/edit/kubevirt.io.virtualmachine/index.vue +0 -642
- package/edit/network.harvesterhci.io.clusternetwork/index.vue +0 -19
- package/edit/network.harvesterhci.io.clusternetwork/vlan.vue +0 -134
- package/edit/workload/types/Deployment.vue +0 -377
- package/edit/workload/types/Generic.vue +0 -295
- package/list/harvesterhci.io.cloudtemplate.vue +0 -78
- package/list/harvesterhci.io.dashboard/HarvesterUpgrade.vue +0 -211
- package/list/harvesterhci.io.dashboard/UpgradeInfo.vue +0 -40
- package/list/harvesterhci.io.dashboard/index.vue +0 -752
- package/list/harvesterhci.io.host/index.vue +0 -186
- package/list/harvesterhci.io.networkattachmentdefinition.vue +0 -167
- package/list/harvesterhci.io.setting.vue +0 -241
- package/list/harvesterhci.io.virtualmachinebackup.vue +0 -172
- package/list/harvesterhci.io.virtualmachineimage.vue +0 -80
- package/list/harvesterhci.io.virtualmachinetemplateversion.vue +0 -173
- package/list/harvesterhci.io.volume.vue +0 -122
- package/list/kubevirt.io.virtualmachine.vue +0 -193
- package/mixins/harvester-vm/impl.js +0 -267
- package/mixins/harvester-vm/index.js +0 -1357
- package/models/harvester/configmap.js +0 -32
- package/models/harvester/harvesterhci.io.blockdevice.js +0 -55
- package/models/harvester/harvesterhci.io.keypair.js +0 -12
- package/models/harvester/harvesterhci.io.setting.js +0 -127
- package/models/harvester/harvesterhci.io.supportbundle.js +0 -35
- package/models/harvester/harvesterhci.io.upgrade.js +0 -226
- package/models/harvester/harvesterhci.io.virtualmachinebackup.js +0 -116
- package/models/harvester/harvesterhci.io.virtualmachineimage.js +0 -255
- package/models/harvester/harvesterhci.io.virtualmachinerestore.js +0 -43
- package/models/harvester/harvesterhci.io.virtualmachinetemplate.js +0 -69
- package/models/harvester/harvesterhci.io.virtualmachinetemplateversion.js +0 -227
- package/models/harvester/k8s.cni.cncf.io.networkattachmentdefinition.js +0 -32
- package/models/harvester/kubevirt.io.virtualmachine.js +0 -850
- package/models/harvester/kubevirt.io.virtualmachineinstance.js +0 -142
- package/models/harvester/management.cattle.io.managedchart.js +0 -191
- package/models/harvester/management.cattle.io.setting.js +0 -40
- package/models/harvester/network.harvesterhci.io.clusternetwork.js +0 -100
- package/models/harvester/network.harvesterhci.io.nodenetwork.js +0 -34
- package/models/harvester/node.js +0 -255
- package/models/harvester/persistentvolumeclaim.js +0 -166
- package/models/harvester/pod.js +0 -185
- package/pages/c/_cluster/harvester/airgapupgrade/index.vue +0 -309
- package/pages/c/_cluster/harvester/console/_uid/serial.vue +0 -51
- package/pages/c/_cluster/harvester/console/_uid/vnc.vue +0 -52
- package/pages/c/_cluster/harvester/index.vue +0 -24
- package/pages/c/_cluster/harvester/support/index.vue +0 -154
- package/pages/plugins.vue +0 -387
- package/pkg/model-loader.lib.js +0 -3
- package/promptRemove/kubevirt.io.virtualmachine.vue +0 -164
- package/server/verdaccio-middleware.js +0 -56
- package/store/harvester-common.js +0 -126
- package/utils/validators/vm-datavolumes.js +0 -38
- package/utils/validators/vm-image.js +0 -32
- package/utils/validators/vm.js +0 -221
package/plugins/plugin.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
// This plugin loads any
|
|
2
|
-
|
|
3
|
-
import {
|
|
1
|
+
// This plugin loads any UI Plugins at app load time
|
|
2
|
+
import { allHashSettled } from '@shell/utils/promise';
|
|
3
|
+
import { shouldLoadPlugin, UI_PLUGIN_BASE_URL } from '@shell/config/uiplugins';
|
|
4
4
|
|
|
5
5
|
const META_NAME_PREFIX = 'app-autoload-';
|
|
6
6
|
|
|
7
7
|
export default async(context) => {
|
|
8
|
+
// UI Plugins declared in the HTML head
|
|
8
9
|
const meta = context.app?.head?.meta || [];
|
|
9
10
|
const hash = {};
|
|
10
11
|
|
|
@@ -18,5 +19,56 @@ export default async(context) => {
|
|
|
18
19
|
}
|
|
19
20
|
});
|
|
20
21
|
|
|
21
|
-
|
|
22
|
+
// Provide a mechanism to load the UI without the plugins loaded - in case there is a problem
|
|
23
|
+
let loadPlugins = true;
|
|
24
|
+
|
|
25
|
+
if (context.route?.path.endsWith('/safeMode')) {
|
|
26
|
+
loadPlugins = false;
|
|
27
|
+
console.warn('Safe Mode - plugins will not be loaded'); // eslint-disable-line no-console
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (loadPlugins) {
|
|
31
|
+
const { store, $plugin } = context;
|
|
32
|
+
|
|
33
|
+
// Fetch list of installed plugins from endpoint
|
|
34
|
+
try {
|
|
35
|
+
const res = await store.dispatch('management/request', {
|
|
36
|
+
url: `${ UI_PLUGIN_BASE_URL }/index.json`,
|
|
37
|
+
headers: { accept: 'application/json' }
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
if (res) {
|
|
41
|
+
const entries = res.entries || res.Entries || {};
|
|
42
|
+
|
|
43
|
+
Object.values(entries).forEach((plugin) => {
|
|
44
|
+
if (shouldLoadPlugin(plugin)) {
|
|
45
|
+
let url;
|
|
46
|
+
|
|
47
|
+
if (plugin?.metadata?.['direct'] === 'true') {
|
|
48
|
+
url = plugin.endpoint;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
hash[plugin.name] = $plugin.loadAsyncByNameAndVersion(plugin.name, plugin.version, url);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
} catch (e) {
|
|
56
|
+
console.error('Could not load UI Plugin list', e); // eslint-disable-line no-console
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Load all of the plugins
|
|
60
|
+
const pluginLoads = await allHashSettled(hash);
|
|
61
|
+
|
|
62
|
+
// Some pluigns may have failed to load - store this
|
|
63
|
+
Object.keys(pluginLoads).forEach((name) => {
|
|
64
|
+
const res = pluginLoads[name];
|
|
65
|
+
|
|
66
|
+
if (res?.status === 'rejected') {
|
|
67
|
+
console.error(`Failed to load plugin: ${ name }`); // eslint-disable-line no-console
|
|
68
|
+
|
|
69
|
+
// Record error in the uiplugins store, so that we can show this to the user
|
|
70
|
+
context.store.dispatch('uiplugins/setError', { name, error: true });
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
}
|
|
22
74
|
};
|
package/plugins/steve/actions.js
CHANGED
|
@@ -178,7 +178,7 @@ export default {
|
|
|
178
178
|
const res = err.response;
|
|
179
179
|
|
|
180
180
|
// Go to the logout page for 401s, unless redirectUnauthorized specifically disables (for the login page)
|
|
181
|
-
if ( opt.redirectUnauthorized !== false &&
|
|
181
|
+
if ( opt.redirectUnauthorized !== false && res.status === 401 ) {
|
|
182
182
|
dispatch('auth/logout', opt.logoutOnError, { root: true });
|
|
183
183
|
}
|
|
184
184
|
|
package/plugins/steve/index.js
CHANGED
|
@@ -10,7 +10,7 @@ import getters, { STEVE_MODEL_TYPES } from './getters';
|
|
|
10
10
|
import mutations from './mutations';
|
|
11
11
|
import actions from './actions';
|
|
12
12
|
|
|
13
|
-
function SteveFactory(namespace, baseUrl) {
|
|
13
|
+
export function SteveFactory(namespace, baseUrl) {
|
|
14
14
|
return {
|
|
15
15
|
...coreStoreModule,
|
|
16
16
|
|
|
@@ -50,6 +50,10 @@ function SteveFactory(namespace, baseUrl) {
|
|
|
50
50
|
};
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
export const steveStoreInit = (store, ctx) => {
|
|
54
|
+
createWorker(store, ctx);
|
|
55
|
+
};
|
|
56
|
+
|
|
53
57
|
export default (config) => {
|
|
54
58
|
config.namespace = config.namespace || '';
|
|
55
59
|
|
|
@@ -67,8 +71,6 @@ export default (config) => {
|
|
|
67
71
|
return coreStore(
|
|
68
72
|
SteveFactory(config.namespace, config.baseUrl),
|
|
69
73
|
config,
|
|
70
|
-
|
|
71
|
-
createWorker(store, ctx);
|
|
72
|
-
}
|
|
74
|
+
steveStoreInit
|
|
73
75
|
);
|
|
74
76
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { addObject, clear, removeObject } from '@shell/utils/array';
|
|
2
2
|
import { get } from '@shell/utils/object';
|
|
3
3
|
import { COUNT, SCHEMA } from '@shell/config/types';
|
|
4
|
+
import { getPerformanceSetting } from '@shell/config/settings';
|
|
4
5
|
import Socket, {
|
|
5
6
|
EVENT_CONNECTED,
|
|
6
7
|
EVENT_DISCONNECTED,
|
|
@@ -15,8 +16,7 @@ import { DATE_FORMAT, TIME_FORMAT } from '@shell/store/prefs';
|
|
|
15
16
|
import { escapeHtml } from '@shell/utils/string';
|
|
16
17
|
|
|
17
18
|
// eslint-disable-next-line
|
|
18
|
-
import
|
|
19
|
-
import * as Comlink from 'comlink';
|
|
19
|
+
import webworker from './web-worker.steve-sub-worker.js';
|
|
20
20
|
|
|
21
21
|
export const NO_WATCH = 'NO_WATCH';
|
|
22
22
|
export const NO_SCHEMA = 'NO_SCHEMA';
|
|
@@ -24,9 +24,6 @@ export const NO_SCHEMA = 'NO_SCHEMA';
|
|
|
24
24
|
// minimum length of time a disconnect notification is shown
|
|
25
25
|
const MINIMUM_TIME_NOTIFIED = 3000;
|
|
26
26
|
|
|
27
|
-
// minimum time a socket must be disconnected for before sending a growl
|
|
28
|
-
const MINIMUM_TIME_DISCONNECTED = 10000;
|
|
29
|
-
|
|
30
27
|
// We only create a worker for the cluster store
|
|
31
28
|
export function createWorker(store, ctx) {
|
|
32
29
|
const { getters } = ctx;
|
|
@@ -38,16 +35,33 @@ export function createWorker(store, ctx) {
|
|
|
38
35
|
return;
|
|
39
36
|
}
|
|
40
37
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
38
|
+
const workerActions = {
|
|
39
|
+
load: (resource) => {
|
|
40
|
+
queueChange(ctx, resource, true, 'Change');
|
|
41
|
+
},
|
|
42
|
+
destroyWorker: () => {
|
|
43
|
+
if (store.$workers) {
|
|
44
|
+
delete store.$workers[storeName];
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
};
|
|
44
48
|
|
|
45
49
|
if (!store.$workers[storeName]) {
|
|
46
|
-
const worker =
|
|
50
|
+
const worker = new webworker();
|
|
47
51
|
|
|
48
52
|
store.$workers[storeName] = worker;
|
|
49
53
|
|
|
50
|
-
worker.initWorker
|
|
54
|
+
worker.postMessage({ initWorker: { storeName } });
|
|
55
|
+
|
|
56
|
+
store.$workers[storeName].onmessage = (e) => {
|
|
57
|
+
/* on the off chance there's more than key in the message, we handle them in the order that they "keys" method provides which is
|
|
58
|
+
// good enough for now considering that we never send more than one message action at a time right now */
|
|
59
|
+
const messageActions = Object.keys(e?.data);
|
|
60
|
+
|
|
61
|
+
messageActions.forEach((action) => {
|
|
62
|
+
workerActions[action](e?.data[action]);
|
|
63
|
+
});
|
|
64
|
+
};
|
|
51
65
|
}
|
|
52
66
|
}
|
|
53
67
|
|
|
@@ -118,6 +132,10 @@ function queueChange({ getters, state }, { data, revision }, load, label) {
|
|
|
118
132
|
}
|
|
119
133
|
}
|
|
120
134
|
|
|
135
|
+
function growlsDisabled(rootGetters) {
|
|
136
|
+
return getPerformanceSetting(rootGetters)?.disableWebsocketNotification;
|
|
137
|
+
}
|
|
138
|
+
|
|
121
139
|
export const actions = {
|
|
122
140
|
subscribe(ctx, opt) {
|
|
123
141
|
const {
|
|
@@ -144,7 +162,9 @@ export const actions = {
|
|
|
144
162
|
socket.setAutoReconnect(true);
|
|
145
163
|
socket.setUrl(url);
|
|
146
164
|
} else {
|
|
147
|
-
|
|
165
|
+
const maxTries = growlsDisabled(rootGetters) ? null : 3;
|
|
166
|
+
|
|
167
|
+
socket = new Socket(`${ state.config.baseUrl }/subscribe`, true, null, null, maxTries);
|
|
148
168
|
|
|
149
169
|
commit('setSocket', socket);
|
|
150
170
|
socket.addEventListener(EVENT_CONNECTED, (e) => {
|
|
@@ -176,24 +196,17 @@ export const actions = {
|
|
|
176
196
|
});
|
|
177
197
|
}
|
|
178
198
|
|
|
179
|
-
socket.connect(get(opt, 'metadata'));
|
|
199
|
+
socket.connect(get(opt, 'metadata') );
|
|
180
200
|
},
|
|
181
201
|
|
|
182
|
-
unsubscribe({
|
|
202
|
+
unsubscribe({ commit, getters, state }) {
|
|
183
203
|
const socket = state.socket;
|
|
184
204
|
const worker = (this.$workers || {})[getters.storeName];
|
|
185
205
|
|
|
186
206
|
commit('setWantSocket', false);
|
|
187
207
|
|
|
188
208
|
if (worker) {
|
|
189
|
-
|
|
190
|
-
worker.destroyWorker();
|
|
191
|
-
worker[Comlink.releaseProxy]();
|
|
192
|
-
} catch (e) {
|
|
193
|
-
console.error(e); // eslint-disable-line no-console
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
delete this.$workers[getters.storeName];
|
|
209
|
+
worker.postMessage({ destroyWorker: true }); // we're only passing the boolean here because the key needs to be something truthy to ensure it's passed on the object.
|
|
197
210
|
}
|
|
198
211
|
|
|
199
212
|
if ( socket ) {
|
|
@@ -395,8 +408,10 @@ export const actions = {
|
|
|
395
408
|
commit, dispatch, state, getters, rootGetters
|
|
396
409
|
}, event) {
|
|
397
410
|
state.debugSocket && console.info(`WebSocket Opened [${ getters.storeName }]`); // eslint-disable-line no-console
|
|
398
|
-
|
|
399
411
|
const socket = event.currentTarget;
|
|
412
|
+
const tries = event?.detail?.tries; // have to pull it off of the event because the socket's tries is already reset to 0
|
|
413
|
+
const t = rootGetters['i18n/t'];
|
|
414
|
+
const disableGrowl = growlsDisabled(rootGetters);
|
|
400
415
|
|
|
401
416
|
this.$socket = socket;
|
|
402
417
|
|
|
@@ -419,19 +434,16 @@ export const actions = {
|
|
|
419
434
|
if ( socket.hasReconnected ) {
|
|
420
435
|
await dispatch('reconnectWatches');
|
|
421
436
|
// Check for disconnect notifications and clear them
|
|
422
|
-
const growlErr = rootGetters['growl/find']({ key: 'url', val:
|
|
437
|
+
const growlErr = rootGetters['growl/find']({ key: 'url', val: socket.url });
|
|
423
438
|
|
|
424
439
|
if (growlErr) {
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
dispatch('growl/remove', growlErr.id, { root: true });
|
|
433
|
-
}, growlErr.earliestClose - now);
|
|
434
|
-
}
|
|
440
|
+
dispatch('growl/remove', growlErr.id, { root: true });
|
|
441
|
+
}
|
|
442
|
+
if (tries > 1 && !disableGrowl) {
|
|
443
|
+
dispatch('growl/success', {
|
|
444
|
+
title: t('growl.reconnected.title'),
|
|
445
|
+
message: t('growl.reconnected.message', { url: this.$socket.url, tries }),
|
|
446
|
+
}, { root: true });
|
|
435
447
|
}
|
|
436
448
|
}
|
|
437
449
|
|
|
@@ -455,32 +467,53 @@ export const actions = {
|
|
|
455
467
|
}, e) {
|
|
456
468
|
clearTimeout(state.queueTimer);
|
|
457
469
|
state.queueTimer = null;
|
|
458
|
-
if (e.type === EVENT_DISCONNECT_ERROR) {
|
|
459
|
-
// do not send a growl notification unless the socket stays disconnected for more than MINIMUM_TIME_DISCONNECTED
|
|
460
|
-
setTimeout(() => {
|
|
461
|
-
if (state.socket.isConnected()) {
|
|
462
|
-
return;
|
|
463
|
-
}
|
|
464
|
-
const dateFormat = escapeHtml( rootGetters['prefs/get'](DATE_FORMAT));
|
|
465
|
-
const timeFormat = escapeHtml( rootGetters['prefs/get'](TIME_FORMAT));
|
|
466
|
-
const time = e?.srcElement?.disconnectedAt || Date.now();
|
|
467
470
|
|
|
468
|
-
|
|
469
|
-
|
|
471
|
+
// determine if websocket notifications are disabled
|
|
472
|
+
const disableGrowl = growlsDisabled(rootGetters);
|
|
473
|
+
|
|
474
|
+
if (!disableGrowl) {
|
|
475
|
+
const dateFormat = escapeHtml( rootGetters['prefs/get'](DATE_FORMAT));
|
|
476
|
+
const timeFormat = escapeHtml( rootGetters['prefs/get'](TIME_FORMAT));
|
|
477
|
+
const time = e?.srcElement?.disconnectedAt || Date.now();
|
|
478
|
+
|
|
479
|
+
const timeFormatted = `${ day(time).format(`${ dateFormat } ${ timeFormat }`) }`;
|
|
480
|
+
const url = e?.srcElement?.url;
|
|
481
|
+
const tries = state?.socket?.tries;
|
|
470
482
|
|
|
471
|
-
|
|
483
|
+
const t = rootGetters['i18n/t'];
|
|
472
484
|
|
|
485
|
+
const growlErr = rootGetters['growl/find']({ key: 'url', val: url });
|
|
486
|
+
|
|
487
|
+
if (e.type === EVENT_CONNECT_ERROR) { // if this occurs, then we're at least retrying to connect
|
|
488
|
+
if (growlErr) {
|
|
489
|
+
dispatch('growl/remove', growlErr.id, { root: true });
|
|
490
|
+
}
|
|
473
491
|
dispatch('growl/error', {
|
|
474
|
-
title: t('growl.
|
|
475
|
-
message: t('growl.
|
|
492
|
+
title: t('growl.connectError.title'),
|
|
493
|
+
message: t('growl.connectError.message', {
|
|
494
|
+
url, time: timeFormatted, tries
|
|
495
|
+
}, { raw: true }),
|
|
476
496
|
icon: 'error',
|
|
477
|
-
earliestClose: time + MINIMUM_TIME_NOTIFIED
|
|
497
|
+
earliestClose: time + MINIMUM_TIME_NOTIFIED,
|
|
478
498
|
url
|
|
479
499
|
}, { root: true });
|
|
480
|
-
},
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
500
|
+
} else if (e.type === EVENT_DISCONNECT_ERROR) { // if this occurs, we've given up on trying to reconnect
|
|
501
|
+
if (growlErr) {
|
|
502
|
+
dispatch('growl/remove', growlErr.id, { root: true });
|
|
503
|
+
}
|
|
504
|
+
dispatch('growl/error', {
|
|
505
|
+
title: t('growl.disconnectError.title'),
|
|
506
|
+
message: t('growl.disconnectError.message', {
|
|
507
|
+
url, time: timeFormatted, tries
|
|
508
|
+
}, { raw: true }),
|
|
509
|
+
icon: 'error',
|
|
510
|
+
earliestClose: time + MINIMUM_TIME_NOTIFIED,
|
|
511
|
+
url
|
|
512
|
+
}, { root: true });
|
|
513
|
+
} else {
|
|
514
|
+
// if the error is not a connect error or disconnect error, the socket never worked: log whether the current browser is safari
|
|
515
|
+
console.error(`WebSocket Connection Error [${ getters.storeName }]`, e.detail); // eslint-disable-line no-console
|
|
516
|
+
}
|
|
484
517
|
}
|
|
485
518
|
},
|
|
486
519
|
|
|
@@ -572,7 +605,7 @@ export const actions = {
|
|
|
572
605
|
const worker = (this.$workers || {})[ctx.getters.storeName];
|
|
573
606
|
|
|
574
607
|
if (worker) {
|
|
575
|
-
worker.countsUpdate
|
|
608
|
+
worker.postMessage({ countsUpdate: msg });
|
|
576
609
|
|
|
577
610
|
// No further processing - let the web worker debounce the counts
|
|
578
611
|
return;
|
|
@@ -585,7 +618,7 @@ export const actions = {
|
|
|
585
618
|
const worker = (this.$workers || {})[ctx.getters.storeName];
|
|
586
619
|
|
|
587
620
|
if (worker) {
|
|
588
|
-
worker.updateSchema
|
|
621
|
+
worker.postMessage({ updateSchema: data });
|
|
589
622
|
|
|
590
623
|
// No further processing - let the web worker check the schema updates
|
|
591
624
|
return;
|
|
@@ -620,7 +653,7 @@ export const actions = {
|
|
|
620
653
|
const worker = (this.$workers || {})[ctx.getters.storeName];
|
|
621
654
|
|
|
622
655
|
if (worker) {
|
|
623
|
-
worker.removeSchema
|
|
656
|
+
worker.postMessage({ removeSchema: data.id });
|
|
624
657
|
}
|
|
625
658
|
}
|
|
626
659
|
|
|
@@ -701,7 +734,7 @@ export const mutations = {
|
|
|
701
734
|
clear(state.started);
|
|
702
735
|
clear(state.pendingFrames);
|
|
703
736
|
clear(state.queue);
|
|
704
|
-
|
|
737
|
+
clearTimeout(state.queueTimer);
|
|
705
738
|
state.deferredRequests = {};
|
|
706
739
|
state.queueTimer = null;
|
|
707
740
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import * as Comlink from 'comlink';
|
|
2
1
|
import { SCHEMA } from '@shell/config/types';
|
|
3
2
|
|
|
4
3
|
const COUNTS_FLUSH_TIMEOUT = 5000;
|
|
@@ -6,7 +5,6 @@ const SCHEMA_FLUSH_TIMEOUT = 2500;
|
|
|
6
5
|
|
|
7
6
|
const state = {
|
|
8
7
|
store: '', // Store name
|
|
9
|
-
load: undefined, // Load callback to load a resource into the store
|
|
10
8
|
counts: [], // Buffer of count resources recieved in a given window
|
|
11
9
|
countTimer: undefined, // Tiemr to flush the count buffer
|
|
12
10
|
flushTimer: undefined, // Timer to flush the schema chaneg queue
|
|
@@ -61,22 +59,33 @@ state.flushTimer = setTimeout(flush, SCHEMA_FLUSH_TIMEOUT);
|
|
|
61
59
|
|
|
62
60
|
// Callback to the store's load function (in the main thread) to process a load
|
|
63
61
|
function load(data) {
|
|
64
|
-
|
|
65
|
-
state.load(data);
|
|
66
|
-
}
|
|
62
|
+
self.postMessage({ load: data });
|
|
67
63
|
}
|
|
68
64
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
65
|
+
const workerActions = {
|
|
66
|
+
onmessage: (e) => {
|
|
67
|
+
/* on the off chance there's more than key in the message, we handle them in the order that they "keys" method provides which is
|
|
68
|
+
// good enough for now considering that we never send more than one message action at a time right now */
|
|
69
|
+
const messageActions = Object.keys(e?.data);
|
|
70
|
+
|
|
71
|
+
messageActions.forEach((action) => {
|
|
72
|
+
if (workerActions[action]) {
|
|
73
|
+
workerActions[action](e?.data[action]);
|
|
74
|
+
} else {
|
|
75
|
+
console.warn('no associated action for:', action); // eslint-disable-line no-console
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
},
|
|
79
|
+
initWorker: ({ storeName }) => {
|
|
72
80
|
state.store = storeName;
|
|
73
|
-
state.load = loadFn;
|
|
74
81
|
},
|
|
75
82
|
|
|
76
|
-
destroyWorker() {
|
|
83
|
+
destroyWorker: () => {
|
|
77
84
|
clearTimeout(state.countTimer);
|
|
78
85
|
clearTimeout(state.flushTimer);
|
|
79
86
|
|
|
87
|
+
self.postMessage({ destroyWorker: true }); // we're only passing the boolean here because the key needs to be something truthy to ensure it's passed on the object.
|
|
88
|
+
|
|
80
89
|
// Web worker global function to terminate the web worker
|
|
81
90
|
close();
|
|
82
91
|
},
|
|
@@ -98,7 +107,7 @@ const fns = {
|
|
|
98
107
|
},
|
|
99
108
|
|
|
100
109
|
// Called to load schema
|
|
101
|
-
|
|
110
|
+
loadSchemas: (schemas) => {
|
|
102
111
|
schemas.forEach((schema) => {
|
|
103
112
|
// These properties are added to the object, but aren't on the raw object, so remove them
|
|
104
113
|
// otherwise our comparison will show changes when there aren't any
|
|
@@ -107,16 +116,17 @@ const fns = {
|
|
|
107
116
|
|
|
108
117
|
state.schemas[schema.id] = hashObj(schema);
|
|
109
118
|
});
|
|
119
|
+
// console.log(JSON.parse(JSON.stringify(state.resources.schemas)));
|
|
110
120
|
},
|
|
111
121
|
|
|
112
122
|
// Called when schema is updated
|
|
113
|
-
updateSchema(schema) {
|
|
123
|
+
updateSchema: (schema) => {
|
|
114
124
|
// Add the schema to the queue to be checked to see if the schema really changed
|
|
115
125
|
state.queue.push(schema);
|
|
116
126
|
},
|
|
117
127
|
|
|
118
128
|
// Remove the cached schema
|
|
119
|
-
removeSchema(id) {
|
|
129
|
+
removeSchema: (id) => {
|
|
120
130
|
// Remove anything in the queue related to the schema - we don't want to send any pending updates later for a schema that has been removed
|
|
121
131
|
state.queue = state.queue.filter(schema => schema.id !== id);
|
|
122
132
|
|
|
@@ -125,5 +135,4 @@ const fns = {
|
|
|
125
135
|
}
|
|
126
136
|
};
|
|
127
137
|
|
|
128
|
-
//
|
|
129
|
-
Comlink.expose(fns);
|
|
138
|
+
onmessage = workerActions.onmessage; // bind everything to the worker's onmessage handler via the workerAction
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import RoleDeletionCheck from '@shell/promptRemove/mixin/roleDeletionCheck';
|
|
3
|
+
export default {
|
|
4
|
+
name: 'GlobalRolePromptRemove',
|
|
5
|
+
mixins: [RoleDeletionCheck],
|
|
6
|
+
props: {
|
|
7
|
+
value: {
|
|
8
|
+
type: Array,
|
|
9
|
+
default: () => {
|
|
10
|
+
return [];
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
|
|
14
|
+
type: {
|
|
15
|
+
type: String,
|
|
16
|
+
required: true
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
</script>
|
|
21
|
+
|
|
22
|
+
<template>
|
|
23
|
+
<div>
|
|
24
|
+
<template>
|
|
25
|
+
{{ t('promptRemove.attemptingToRemove', { type }) }} <span
|
|
26
|
+
v-html="resourceNames(names, plusMore, t)"
|
|
27
|
+
></span>
|
|
28
|
+
</template>
|
|
29
|
+
<div v-if="info" class="text info mb-10 mt-20">
|
|
30
|
+
<span v-html="info" />
|
|
31
|
+
</div>
|
|
32
|
+
<div v-if="warning" class="text-warning mb-10 mt-20">
|
|
33
|
+
{{ warning }}
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
36
|
+
</template>
|
|
37
|
+
|
|
38
|
+
<style lang="scss" scoped>
|
|
39
|
+
.text.info {
|
|
40
|
+
display: flex;
|
|
41
|
+
align-items: center;
|
|
42
|
+
|
|
43
|
+
> span {
|
|
44
|
+
margin-right: 5px;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
</style>
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import RoleDeletionCheck from '@shell/promptRemove/mixin/roleDeletionCheck';
|
|
3
|
+
export default {
|
|
4
|
+
name: 'RoleTemplatePromptRemove',
|
|
5
|
+
mixins: [RoleDeletionCheck],
|
|
6
|
+
props: {
|
|
7
|
+
value: {
|
|
8
|
+
type: Array,
|
|
9
|
+
default: () => {
|
|
10
|
+
return [];
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
|
|
14
|
+
type: {
|
|
15
|
+
type: String,
|
|
16
|
+
required: true
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
</script>
|
|
21
|
+
|
|
22
|
+
<template>
|
|
23
|
+
<div>
|
|
24
|
+
<template>
|
|
25
|
+
{{ t('promptRemove.attemptingToRemove', { type }) }} <span
|
|
26
|
+
v-html="resourceNames(names, plusMore, t)"
|
|
27
|
+
></span>
|
|
28
|
+
</template>
|
|
29
|
+
<div v-if="info" class="text info mb-10 mt-20">
|
|
30
|
+
<span v-html="info" />
|
|
31
|
+
</div>
|
|
32
|
+
<div v-if="warning" class="text-warning mb-10 mt-20">
|
|
33
|
+
{{ warning }}
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
36
|
+
</template>
|
|
37
|
+
|
|
38
|
+
<style lang="scss" scoped>
|
|
39
|
+
.text.info {
|
|
40
|
+
display: flex;
|
|
41
|
+
align-items: center;
|
|
42
|
+
|
|
43
|
+
> span {
|
|
44
|
+
margin-right: 5px;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
</style>
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { mapState, mapGetters } from 'vuex';
|
|
2
|
+
import { resourceNames } from '@shell/utils/string';
|
|
3
|
+
import { MANAGEMENT } from '@shell/config/types';
|
|
4
|
+
import { SUBTYPE_MAPPING } from '@shell/models/management.cattle.io.roletemplate';
|
|
5
|
+
const CLUSTER = SUBTYPE_MAPPING.CLUSTER.key;
|
|
6
|
+
|
|
7
|
+
export default {
|
|
8
|
+
data() {
|
|
9
|
+
return {
|
|
10
|
+
warning: '',
|
|
11
|
+
info: '',
|
|
12
|
+
};
|
|
13
|
+
},
|
|
14
|
+
|
|
15
|
+
computed: {
|
|
16
|
+
...mapState('action-menu', ['toRemove']),
|
|
17
|
+
...mapGetters({ t: 'i18n/t' }),
|
|
18
|
+
|
|
19
|
+
names() {
|
|
20
|
+
return this.toRemove.map(obj => obj.nameDisplay).slice(0, 5);
|
|
21
|
+
},
|
|
22
|
+
|
|
23
|
+
plusMore() {
|
|
24
|
+
const remaining = this.toRemove.length - this.names.length;
|
|
25
|
+
|
|
26
|
+
return this.t('promptRemove.andOthers', { count: remaining });
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
watch: {
|
|
30
|
+
value: {
|
|
31
|
+
handler(neu) {
|
|
32
|
+
this.handleRoleDeletionCheck(neu, neu[0].type, this.$route.hash);
|
|
33
|
+
},
|
|
34
|
+
immediate: true
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
methods: {
|
|
38
|
+
resourceNames,
|
|
39
|
+
async handleRoleDeletionCheck(rolesToRemove, resourceType, queryHash) {
|
|
40
|
+
this.warning = '';
|
|
41
|
+
let resourceToCheck;
|
|
42
|
+
let propToMatch;
|
|
43
|
+
let numberOfRolesWithBinds = 0;
|
|
44
|
+
const uniqueUsersWithBinds = new Set();
|
|
45
|
+
|
|
46
|
+
this.info = this.t('rbac.globalRoles.waiting', { count: rolesToRemove.length });
|
|
47
|
+
|
|
48
|
+
switch (resourceType) {
|
|
49
|
+
case MANAGEMENT.GLOBAL_ROLE:
|
|
50
|
+
resourceToCheck = MANAGEMENT.GLOBAL_ROLE_BINDING;
|
|
51
|
+
propToMatch = 'globalRoleName';
|
|
52
|
+
break;
|
|
53
|
+
default:
|
|
54
|
+
if (queryHash.includes(CLUSTER)) {
|
|
55
|
+
resourceToCheck = MANAGEMENT.CLUSTER_ROLE_TEMPLATE_BINDING;
|
|
56
|
+
} else {
|
|
57
|
+
resourceToCheck = MANAGEMENT.PROJECT_ROLE_TEMPLATE_BINDING;
|
|
58
|
+
}
|
|
59
|
+
propToMatch = 'roleTemplateName';
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
const request = await this.$store.dispatch('management/request', {
|
|
65
|
+
url: `/v1/${ resourceToCheck }`,
|
|
66
|
+
method: 'get',
|
|
67
|
+
}, { root: true });
|
|
68
|
+
|
|
69
|
+
if (request.data && request.data.length) {
|
|
70
|
+
rolesToRemove.forEach((toRemove) => {
|
|
71
|
+
const usedRoles = request.data.filter(item => item[propToMatch] === toRemove.id);
|
|
72
|
+
|
|
73
|
+
if (usedRoles.length) {
|
|
74
|
+
const uniqueUsers = [...new Set(usedRoles.map(item => item.userName))];
|
|
75
|
+
|
|
76
|
+
if (uniqueUsers.length) {
|
|
77
|
+
numberOfRolesWithBinds++;
|
|
78
|
+
uniqueUsers.forEach(user => uniqueUsersWithBinds.add(user));
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
if (numberOfRolesWithBinds && uniqueUsersWithBinds.size) {
|
|
84
|
+
this.info = '';
|
|
85
|
+
this.warning = this.t('rbac.globalRoles.usersBound', { count: uniqueUsersWithBinds.size });
|
|
86
|
+
} else {
|
|
87
|
+
this.info = this.t('rbac.globalRoles.notBound', null, true);
|
|
88
|
+
}
|
|
89
|
+
} else {
|
|
90
|
+
this.info = this.t('rbac.globalRoles.notBound', null, true);
|
|
91
|
+
}
|
|
92
|
+
} catch (e) {
|
|
93
|
+
this.info = this.t('rbac.globalRoles.unableToCheck');
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
};
|