@rancher/shell 3.0.5-rc.5 → 3.0.5-rc.7
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/data/aws-regions.json +1 -0
- package/assets/images/key.svg +17 -0
- package/assets/styles/base/_spacing.scss +2 -2
- package/assets/styles/base/_variables.scss +17 -11
- package/assets/styles/global/_form.scss +1 -1
- package/assets/styles/global/_labeled-input.scss +1 -1
- package/assets/styles/themes/_dark.scss +5 -0
- package/assets/styles/themes/_light.scss +11 -2
- package/assets/styles/vendor/vue-select.scss +1 -1
- package/assets/translations/en-us.yaml +426 -64
- package/assets/translations/zh-hans.yaml +3 -4
- package/cloud-credential/gcp.vue +9 -1
- package/components/AppModal.vue +2 -0
- package/components/CodeMirror.vue +2 -2
- package/components/ConfigMapSettings/Settings.vue +377 -0
- package/components/ConfigMapSettings/index.vue +354 -0
- package/components/CruResource.vue +1 -2
- package/components/DetailText.vue +61 -11
- package/components/Drawer/Chrome.vue +115 -0
- package/components/Drawer/ResourceDetailDrawer/ConfigTab.vue +61 -0
- package/components/Drawer/ResourceDetailDrawer/YamlTab.vue +48 -0
- package/components/Drawer/ResourceDetailDrawer/__tests__/ConfigTab.test.ts +54 -0
- package/components/Drawer/ResourceDetailDrawer/__tests__/YamlTab.test.ts +80 -0
- package/components/Drawer/ResourceDetailDrawer/__tests__/composables.test.ts +106 -0
- package/components/Drawer/ResourceDetailDrawer/__tests__/helpers.test.ts +42 -0
- package/components/Drawer/ResourceDetailDrawer/composables.ts +53 -0
- package/components/Drawer/ResourceDetailDrawer/helpers.ts +10 -0
- package/components/Drawer/ResourceDetailDrawer/index.vue +111 -0
- package/components/GrowlManager.vue +16 -15
- package/components/IconOrSvg.vue +5 -0
- package/components/KeyValueView.vue +1 -1
- package/components/Loading.vue +1 -1
- package/components/LocaleSelector.vue +9 -1
- package/components/PaginatedResourceTable.vue +46 -1
- package/components/ProgressBarMulti.vue +1 -0
- package/components/PromptModal.vue +6 -1
- package/components/PromptRestore.vue +22 -44
- package/components/RelatedResources.vue +4 -12
- package/components/Resource/Detail/Additional.vue +46 -0
- package/components/Resource/Detail/Metadata/Annotations/__tests__/index.test.ts +1 -1
- package/components/Resource/Detail/Metadata/Annotations/index.vue +5 -0
- package/components/Resource/Detail/Metadata/IdentifyingInformation/__tests__/identifying-fields.test.ts +223 -0
- package/components/Resource/Detail/Metadata/IdentifyingInformation/composable.ts +47 -256
- package/components/Resource/Detail/Metadata/IdentifyingInformation/identifying-fields.ts +317 -0
- package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +34 -5
- package/components/Resource/Detail/Metadata/KeyValue.vue +32 -22
- package/components/Resource/Detail/Metadata/Labels/__tests__/index.test.ts +1 -1
- package/components/Resource/Detail/Metadata/Labels/index.vue +4 -0
- package/components/Resource/Detail/Metadata/Rectangle.vue +3 -1
- package/components/Resource/Detail/Metadata/__tests__/KeyValue.test.ts +1 -1
- package/components/Resource/Detail/Metadata/__tests__/Rectangle.test.ts +1 -1
- package/components/Resource/Detail/Metadata/__tests__/composables.test.ts +75 -0
- package/components/Resource/Detail/Metadata/composables.ts +60 -11
- package/components/Resource/Detail/Metadata/index.vue +12 -5
- package/components/Resource/Detail/Page.vue +15 -0
- package/components/Resource/Detail/ResourceRow.vue +37 -18
- package/components/Resource/Detail/ResourceTabs/ConfigMapDataTab/__tests__/composables.test.ts +29 -0
- package/components/Resource/Detail/ResourceTabs/ConfigMapDataTab/__tests__/index.test.ts +48 -0
- package/components/Resource/Detail/ResourceTabs/ConfigMapDataTab/composables.ts +31 -0
- package/components/Resource/Detail/ResourceTabs/ConfigMapDataTab/index.vue +50 -0
- package/components/Resource/Detail/ResourceTabs/KnownHostsTab/__tests__/composables.test.ts +66 -0
- package/components/Resource/Detail/ResourceTabs/KnownHostsTab/composables.ts +21 -0
- package/components/Resource/Detail/ResourceTabs/KnownHostsTab/index.vue +31 -0
- package/components/Resource/Detail/ResourceTabs/SecretDataTab/Basic.vue +45 -0
- package/components/Resource/Detail/ResourceTabs/SecretDataTab/BasicAuth.vue +31 -0
- package/components/Resource/Detail/ResourceTabs/SecretDataTab/Certificate.vue +31 -0
- package/components/Resource/Detail/ResourceTabs/SecretDataTab/Registry.vue +22 -0
- package/components/Resource/Detail/ResourceTabs/SecretDataTab/ServiceAccountToken.vue +31 -0
- package/components/Resource/Detail/ResourceTabs/SecretDataTab/Ssh.vue +32 -0
- package/components/Resource/Detail/ResourceTabs/SecretDataTab/__tests__/Basic.test.ts +40 -0
- package/components/Resource/Detail/ResourceTabs/SecretDataTab/__tests__/BasicAuth.test.ts +33 -0
- package/components/Resource/Detail/ResourceTabs/SecretDataTab/__tests__/Certificate.test.ts +33 -0
- package/components/Resource/Detail/ResourceTabs/SecretDataTab/__tests__/Registry.test.ts +27 -0
- package/components/Resource/Detail/ResourceTabs/SecretDataTab/__tests__/ServiceAccountToken.test.ts +33 -0
- package/components/Resource/Detail/ResourceTabs/SecretDataTab/__tests__/Ssh.test.ts +33 -0
- package/components/Resource/Detail/ResourceTabs/SecretDataTab/__tests__/auth-types.test.ts +186 -0
- package/components/Resource/Detail/ResourceTabs/SecretDataTab/__tests__/composables.test.ts +102 -0
- package/components/Resource/Detail/ResourceTabs/SecretDataTab/auth-types.ts +109 -0
- package/components/Resource/Detail/ResourceTabs/SecretDataTab/composeables.ts +52 -0
- package/components/Resource/Detail/ResourceTabs/SecretDataTab/index.vue +71 -0
- package/components/Resource/Detail/SpacedRow.vue +1 -1
- package/components/Resource/Detail/TitleBar/Title.vue +2 -1
- package/components/Resource/Detail/TitleBar/__tests__/Title.test.ts +1 -1
- package/components/Resource/Detail/TitleBar/__tests__/Top.test.ts +1 -1
- package/components/Resource/Detail/TitleBar/__tests__/composables.test.ts +63 -0
- package/components/Resource/Detail/TitleBar/__tests__/index.test.ts +1 -1
- package/components/Resource/Detail/TitleBar/composables.ts +45 -0
- package/components/Resource/Detail/TitleBar/index.vue +85 -13
- package/components/Resource/Detail/composables.ts +45 -0
- package/components/ResourceDetail/Masthead/__tests__/index.test.ts +70 -0
- package/components/ResourceDetail/{__tests__/Masthead.test.ts → Masthead/__tests__/legacy.test.ts} +3 -3
- package/components/ResourceDetail/Masthead/index.vue +65 -0
- package/components/ResourceDetail/Masthead/latest.vue +44 -0
- package/components/ResourceDetail/{Masthead.vue → Masthead/legacy.vue} +1 -1
- package/components/ResourceDetail/__tests__/index.test.ts +26 -5
- package/components/ResourceDetail/index.vue +33 -17
- package/components/ResourceDetail/legacy.vue +18 -1
- package/components/ResourceList/Masthead.vue +6 -0
- package/components/ResourceList/index.vue +1 -0
- package/components/ResourceTable.vue +6 -1
- package/components/ResourceYaml.vue +15 -2
- package/components/RichTranslation.vue +106 -0
- package/components/SlideInPanelManager.vue +46 -11
- package/components/SortableTable/index.vue +1 -1
- package/components/SortableTable/selection.js +0 -1
- package/components/StateDot/index.vue +28 -0
- package/components/Tabbed/index.vue +17 -16
- package/components/Wizard.vue +4 -2
- package/components/__tests__/ConfigMapSettings.test.ts +376 -0
- package/components/__tests__/GrowlManager.test.ts +0 -25
- package/components/__tests__/PromptRestore.test.ts +1 -65
- package/components/__tests__/RichTranslation.test.ts +115 -0
- package/components/auth/login/ldap.vue +1 -1
- package/components/fleet/FleetApplications.vue +0 -7
- package/components/fleet/FleetClusterTargets/TargetsList.vue +66 -0
- package/components/fleet/FleetClusterTargets/index.vue +455 -0
- package/components/fleet/FleetClusters.vue +25 -6
- package/components/fleet/FleetGitRepoPaths.vue +476 -0
- package/components/fleet/FleetHelmOps.vue +8 -0
- package/components/fleet/FleetRepos.vue +1 -6
- package/components/fleet/FleetResources.vue +4 -5
- package/components/fleet/FleetValuesFrom.vue +295 -0
- package/components/fleet/__tests__/FleetClusterTargets.test.ts +1224 -0
- package/components/fleet/__tests__/FleetGitRepoPaths.test.ts +265 -0
- package/components/fleet/__tests__/FleetOCIStorageSecret.test.ts +13 -13
- package/components/fleet/__tests__/FleetValuesFrom.test.ts +300 -0
- package/components/fleet/dashboard/ResourceCard.vue +1 -0
- package/components/fleet/dashboard/ResourceCardSummary.vue +1 -5
- package/components/fleet/dashboard/ResourceDetails.vue +8 -10
- package/components/fleet/dashboard/ResourcePanel.vue +17 -9
- package/components/form/ArrayList.vue +13 -2
- package/components/form/ChangePassword.vue +3 -1
- package/components/form/FileImageSelector.vue +1 -1
- package/components/form/Footer.vue +10 -4
- package/components/form/KeyValue.vue +81 -43
- package/components/form/LabeledSelect.vue +56 -16
- package/components/form/Labels.vue +90 -17
- package/components/form/MatchExpressions.vue +46 -5
- package/components/form/NameNsDescription.vue +2 -1
- package/components/form/Networking.vue +24 -19
- package/components/form/ResourceLabeledSelect.vue +4 -3
- package/components/form/ResourceSelector.vue +1 -0
- package/components/form/ResourceTabs/index.vue +5 -0
- package/components/form/SecretSelector.vue +9 -2
- package/components/form/Select.vue +57 -19
- package/components/form/SelectOrCreateAuthSecret.vue +6 -3
- package/components/form/SimpleSecretSelector.vue +9 -2
- package/components/form/Taints.vue +21 -2
- package/components/form/UnitInput.vue +8 -0
- package/components/form/ValueFromResource.vue +1 -1
- package/components/form/__tests__/LabeledSelect.test.ts +8 -4
- package/components/form/__tests__/Labels.test.ts +360 -0
- package/components/form/__tests__/MatchExpressions.test.ts +16 -13
- package/components/form/__tests__/Networking.test.ts +116 -0
- package/components/form/__tests__/Select.test.ts +5 -2
- package/components/formatter/FleetApplicationSource.vue +1 -1
- package/components/formatter/PodImages.vue +1 -1
- package/components/formatter/WorkloadHealthScale.vue +1 -1
- package/components/formatter/__tests__/LiveDate.test.ts +10 -2
- package/components/google/AccountAccess.vue +209 -0
- package/components/google/types/gcp.d.ts +136 -0
- package/components/google/types/index.d.ts +101 -0
- package/components/google/util/__mocks__/gcp.ts +465 -0
- package/components/google/util/formatter.ts +82 -0
- package/components/google/util/gcp.ts +134 -0
- package/components/google/util/index.d.ts +11 -0
- package/components/nav/Favorite.vue +1 -1
- package/components/nav/Group.vue +71 -45
- package/components/nav/Header.vue +5 -1
- package/components/nav/NamespaceFilter.vue +13 -1
- package/components/nav/NotificationCenter/Notification.vue +510 -0
- package/components/nav/NotificationCenter/NotificationHeader.vue +112 -0
- package/components/nav/NotificationCenter/index.vue +148 -0
- package/composables/drawer.ts +26 -0
- package/composables/resources.test.ts +63 -0
- package/composables/resources.ts +38 -0
- package/composables/useIsNewDetailPageEnabled.ts +17 -0
- package/config/labels-annotations.js +8 -0
- package/config/pagination-table-headers.js +8 -1
- package/config/product/auth.js +16 -1
- package/config/product/{cis.js → compliance.js} +23 -26
- package/config/product/explorer.js +32 -3
- package/config/product/fleet.js +7 -0
- package/config/product/manager.js +0 -1
- package/config/product/settings.js +22 -11
- package/config/query-params.js +13 -0
- package/config/roles.ts +1 -1
- package/config/router/navigation-guards/authentication.js +51 -2
- package/config/router/routes.js +47 -31
- package/config/settings.ts +21 -3
- package/config/store.js +2 -0
- package/config/system-namespaces.js +1 -1
- package/config/table-headers.js +32 -3
- package/config/types.js +16 -7
- package/config/version.js +1 -1
- package/core/plugin.ts +32 -7
- package/core/types.ts +18 -1
- package/detail/{cis.cattle.io.clusterscan.vue → compliance.cattle.io.clusterscan.vue} +22 -18
- package/detail/management.cattle.io.fleetworkspace.vue +18 -27
- package/detail/management.cattle.io.oidcclient.vue +369 -0
- package/detail/node.vue +2 -2
- package/detail/pod.vue +2 -2
- package/detail/provisioning.cattle.io.cluster.vue +3 -47
- package/detail/service.vue +10 -1
- package/detail/workload/index.vue +8 -2
- package/dialog/ExtensionCatalogUninstallDialog.vue +7 -4
- package/dialog/GenericPrompt.vue +1 -1
- package/dialog/ImportDialog.vue +8 -8
- package/dialog/OidcClientSecretDialog.vue +117 -0
- package/dialog/RotateEncryptionKeyDialog.vue +10 -30
- package/edit/__tests__/cis.cattle.io.clusterscan.test.ts +3 -3
- package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +5 -2
- package/edit/auth/ldap/__tests__/config.test.ts +14 -0
- package/edit/auth/ldap/config.vue +24 -0
- package/edit/autoscaling.horizontalpodautoscaler/index.vue +4 -1
- package/edit/{cis.cattle.io.clusterscan.vue → compliance.cattle.io.clusterscan.vue} +30 -31
- package/edit/{cis.cattle.io.clusterscanbenchmark.vue → compliance.cattle.io.clusterscanbenchmark.vue} +4 -4
- package/edit/{cis.cattle.io.clusterscanprofile.vue → compliance.cattle.io.clusterscanprofile.vue} +5 -5
- package/edit/configmap.vue +8 -2
- package/edit/constraints.gatekeeper.sh.constraint/index.vue +1 -0
- package/edit/fleet.cattle.io.gitrepo.vue +44 -222
- package/edit/fleet.cattle.io.helmop.vue +44 -269
- package/edit/helm.cattle.io.projecthelmchart.vue +1 -0
- package/edit/k8s.cni.cncf.io.networkattachmentdefinition.vue +1 -0
- package/edit/logging-flow/index.vue +1 -0
- package/edit/logging.banzaicloud.io.output/index.vue +1 -0
- package/edit/management.cattle.io.fleetworkspace.vue +1 -0
- package/edit/management.cattle.io.oidcclient.vue +162 -0
- package/edit/management.cattle.io.project.vue +4 -1
- package/edit/monitoring.coreos.com.alertmanagerconfig/index.vue +1 -1
- package/edit/monitoring.coreos.com.alertmanagerconfig/receiverConfig.vue +5 -0
- package/edit/monitoring.coreos.com.prometheusrule/index.vue +1 -0
- package/edit/monitoring.coreos.com.receiver/auth.vue +30 -30
- package/edit/monitoring.coreos.com.receiver/index.vue +1 -0
- package/edit/monitoring.coreos.com.receiver/types/email.vue +1 -1
- package/edit/monitoring.coreos.com.route.vue +1 -0
- package/edit/namespace.vue +1 -0
- package/edit/networking.istio.io.destinationrule/index.vue +4 -1
- package/edit/networking.k8s.io.ingress/Certificate.vue +12 -12
- package/edit/networking.k8s.io.ingress/__tests__/Certificate.test.ts +165 -0
- package/edit/networking.k8s.io.ingress/index.vue +4 -1
- package/edit/networking.k8s.io.networkpolicy/PolicyRules.vue +7 -2
- package/edit/networking.k8s.io.networkpolicy/index.vue +6 -2
- package/edit/node.vue +1 -0
- package/edit/persistentvolume/index.vue +4 -1
- package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +3 -2
- package/edit/provisioning.cattle.io.cluster/rke2.vue +516 -426
- package/edit/provisioning.cattle.io.cluster/tabs/Basics.vue +48 -39
- package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +5 -0
- package/edit/provisioning.cattle.io.cluster/tabs/registries/RegistryConfigs.vue +2 -2
- package/edit/resources.cattle.io.restore.vue +1 -1
- package/edit/secret/basic.vue +1 -0
- package/edit/secret/index.vue +127 -15
- package/edit/service.vue +4 -1
- package/edit/serviceaccount.vue +4 -1
- package/edit/storage.k8s.io.storageclass/index.vue +4 -1
- package/edit/workload/index.vue +5 -0
- package/list/{cis.cattle.io.clusterscan.vue → compliance.cattle.io.clusterscan.vue} +2 -2
- package/list/management.cattle.io.oidcclient.vue +108 -0
- package/list/node.vue +2 -0
- package/list/projectsecret.vue +345 -0
- package/list/secret.vue +109 -0
- package/machine-config/amazonec2.vue +3 -24
- package/machine-config/components/GCEImage.vue +374 -0
- package/machine-config/google.vue +617 -0
- package/mixins/__tests__/brand.spec.ts +170 -0
- package/mixins/brand.js +16 -17
- package/mixins/create-edit-view/impl.js +10 -1
- package/mixins/create-edit-view/index.js +5 -0
- package/mixins/resource-fetch-api-pagination.js +24 -8
- package/mixins/resource-fetch.js +3 -1
- package/mixins/vue-select-overrides.js +1 -0
- package/models/cluster.x-k8s.io.machinedeployment.js +11 -2
- package/models/{cis.cattle.io.clusterscan.js → compliance.cattle.io.clusterscan.js} +8 -8
- package/models/{cis.cattle.io.clusterscanbenchmark.js → compliance.cattle.io.clusterscanbenchmark.js} +1 -1
- package/models/{cis.cattle.io.clusterscanprofile.js → compliance.cattle.io.clusterscanprofile.js} +5 -5
- package/models/{cis.cattle.io.clusterscanreport.js → compliance.cattle.io.clusterscanreport.js} +1 -1
- package/models/fleet-application.js +8 -79
- package/models/fleet.cattle.io.cluster.js +13 -2
- package/models/fleet.cattle.io.gitrepo.js +2 -2
- package/models/fleet.cattle.io.helmop.js +9 -39
- package/models/management.cattle.io.fleetworkspace.js +2 -1
- package/models/management.cattle.io.oidcclient.js +18 -0
- package/models/management.cattle.io.registration.js +3 -0
- package/models/provisioning.cattle.io.cluster.js +29 -33
- package/models/secret.js +157 -2
- package/models/service.js +4 -0
- package/models/workload.js +5 -0
- package/package.json +2 -2
- package/pages/about.vue +4 -58
- package/pages/auth/login.vue +1 -1
- package/pages/c/_cluster/apps/charts/AddRepoLink.vue +0 -1
- package/pages/c/_cluster/apps/charts/index.vue +296 -81
- package/pages/c/_cluster/auth/user.retention/index.vue +87 -78
- package/pages/c/_cluster/explorer/index.vue +3 -3
- package/pages/c/_cluster/explorer/projectsecret.vue +34 -0
- package/pages/c/_cluster/explorer/tools/pages/_page.vue +0 -1
- package/pages/c/_cluster/fleet/application/create.vue +3 -2
- package/pages/c/_cluster/fleet/index.vue +94 -57
- package/pages/c/_cluster/fleet/settings/index.vue +229 -0
- package/pages/c/_cluster/longhorn/index.vue +5 -2
- package/pages/c/_cluster/uiplugins/CatalogList/index.vue +16 -1
- package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +2 -2
- package/pages/explorer/resource/detail/configmap.vue +30 -7
- package/pages/explorer/resource/detail/projectsecret.vue +9 -0
- package/pages/explorer/resource/detail/secret.vue +63 -0
- package/pages/home.vue +9 -55
- package/pages/support/index.vue +4 -6
- package/plugins/dashboard-store/__tests__/normalize.test.ts +223 -0
- package/plugins/dashboard-store/__tests__/resource-class.test.ts +191 -0
- package/plugins/dashboard-store/__tests__/utils/normalize-usecases.ts +1526 -0
- package/plugins/dashboard-store/actions.js +19 -5
- package/plugins/dashboard-store/getters.js +4 -0
- package/plugins/dashboard-store/normalize.js +29 -17
- package/plugins/dashboard-store/resource-class.js +68 -19
- package/plugins/steve/steve-pagination-utils.ts +38 -19
- package/plugins/steve/subscribe.js +6 -1
- package/rancher-components/Banner/Banner.vue +13 -0
- package/rancher-components/Form/Checkbox/Checkbox.vue +9 -4
- package/rancher-components/Form/LabeledInput/LabeledInput.vue +1 -1
- package/rancher-components/LabeledTooltip/LabeledTooltip.vue +1 -0
- package/rancher-components/RcItemCard/RcItemCard.vue +8 -3
- package/store/auth.js +2 -0
- package/store/catalog.js +23 -1
- package/store/growl.js +97 -8
- package/store/index.js +6 -0
- package/store/notifications.ts +426 -0
- package/store/prefs.js +0 -1
- package/store/type-map.js +19 -16
- package/store/uiplugins.ts +15 -1
- package/types/fleet.d.ts +24 -0
- package/types/kube/kube-api.ts +12 -0
- package/types/notifications/index.ts +74 -0
- package/types/shell/index.d.ts +661 -589
- package/types/store/dashboard-store.types.ts +16 -0
- package/types/store/pagination.types.ts +16 -6
- package/utils/__tests__/create-yaml.test.ts +235 -0
- package/utils/__tests__/fleet.test.ts +148 -0
- package/utils/__tests__/object.test.ts +54 -1
- package/utils/__tests__/string.test.ts +273 -1
- package/utils/__tests__/time.test.ts +31 -0
- package/utils/auth.js +9 -2
- package/utils/create-yaml.js +103 -9
- package/utils/crypto/encryption.ts +103 -0
- package/utils/cspAdaptor.ts +51 -0
- package/utils/fleet.ts +54 -65
- package/utils/object.js +36 -0
- package/utils/pagination-utils.ts +19 -1
- package/utils/release-notes.ts +48 -0
- package/utils/selector-typed.ts +7 -2
- package/utils/string.js +24 -0
- package/utils/{time.js → time.ts} +25 -6
- package/utils/uiplugins.ts +22 -0
- package/utils/validators/formRules/index.ts +3 -0
- package/components/Resource/Detail/TitleBar/composable.ts +0 -31
- package/config/product/legacy.js +0 -62
- package/models/etcdbackup.js +0 -45
- package/pages/c/_cluster/legacy/pages/_page.vue +0 -29
- package/pages/c/_cluster/legacy/project/_page.vue +0 -57
- package/pages/c/_cluster/legacy/project/index.vue +0 -32
- package/pages/c/_cluster/legacy/project/pipelines.vue +0 -96
package/store/growl.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { clear, findBy, removeObject } from '@shell/utils/array';
|
|
2
2
|
import { stringify } from '@shell/utils/error';
|
|
3
|
+
import { NotificationLevel } from '@shell/types/notifications';
|
|
3
4
|
|
|
4
5
|
const DEFAULT_TIMEOUT = 5000;
|
|
5
6
|
|
|
7
|
+
const MAX_GROWLS = 5;
|
|
8
|
+
|
|
6
9
|
export const state = function() {
|
|
7
10
|
return {
|
|
8
11
|
nextId: 1,
|
|
@@ -13,18 +16,28 @@ export const state = function() {
|
|
|
13
16
|
export const getters = {
|
|
14
17
|
find: (state) => ({ key, val }) => {
|
|
15
18
|
return findBy(state.stack, key, val);
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
// findBy is slow, so more efficient getter for id
|
|
22
|
+
byId: (state) => (id) => {
|
|
23
|
+
return state.stack.find((item) => item.id === id);
|
|
16
24
|
}
|
|
17
25
|
};
|
|
18
26
|
|
|
19
27
|
export const mutations = {
|
|
20
28
|
add(state, data) {
|
|
29
|
+
if (state.stack.length === MAX_GROWLS) {
|
|
30
|
+
// Remove the last one
|
|
31
|
+
state.stack.pop();
|
|
32
|
+
}
|
|
33
|
+
|
|
21
34
|
state.stack = [
|
|
22
|
-
...state.stack,
|
|
23
35
|
{
|
|
24
36
|
id: state.nextId++,
|
|
25
37
|
started: (new Date().getTime()),
|
|
26
38
|
...data
|
|
27
|
-
}
|
|
39
|
+
},
|
|
40
|
+
...state.stack
|
|
28
41
|
];
|
|
29
42
|
},
|
|
30
43
|
|
|
@@ -50,11 +63,29 @@ export const actions = {
|
|
|
50
63
|
commit('remove', id);
|
|
51
64
|
},
|
|
52
65
|
|
|
53
|
-
|
|
66
|
+
async close({ commit, dispatch, getters }, id ) {
|
|
67
|
+
const growl = getters.byId(id);
|
|
68
|
+
|
|
69
|
+
commit('remove', id);
|
|
70
|
+
|
|
71
|
+
// If the growl has a notification, mark it as read if the user dismisses the growl
|
|
72
|
+
if (growl.notification) {
|
|
73
|
+
await dispatch('notifications/markRead', growl.notification, { root: true });
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
async success({ commit, dispatch }, data) {
|
|
78
|
+
// Send a notification for the growl
|
|
79
|
+
const notification = await dispatch('notifications/fromGrowl', {
|
|
80
|
+
...data,
|
|
81
|
+
level: NotificationLevel.Success
|
|
82
|
+
}, { root: true });
|
|
83
|
+
|
|
54
84
|
commit('add', {
|
|
55
85
|
color: 'success',
|
|
56
86
|
icon: 'checkmark',
|
|
57
87
|
timeout: DEFAULT_TIMEOUT,
|
|
88
|
+
notification,
|
|
58
89
|
...data
|
|
59
90
|
});
|
|
60
91
|
},
|
|
@@ -68,31 +99,89 @@ export const actions = {
|
|
|
68
99
|
});
|
|
69
100
|
},
|
|
70
101
|
|
|
71
|
-
warning({ commit }, data) {
|
|
102
|
+
async warning({ commit, dispatch }, data) {
|
|
103
|
+
// Send a notification for the growl
|
|
104
|
+
const notification = await dispatch('notifications/fromGrowl', {
|
|
105
|
+
...data,
|
|
106
|
+
level: NotificationLevel.Warning
|
|
107
|
+
}, { root: true });
|
|
108
|
+
|
|
72
109
|
commit('add', {
|
|
73
110
|
color: 'warning',
|
|
74
111
|
icon: 'warning',
|
|
75
112
|
timeout: DEFAULT_TIMEOUT,
|
|
113
|
+
notification,
|
|
76
114
|
...data
|
|
77
115
|
});
|
|
78
116
|
},
|
|
79
117
|
|
|
80
|
-
error({ commit }, data) {
|
|
118
|
+
async error({ commit, dispatch }, data) {
|
|
119
|
+
// Send a notification for the growl
|
|
120
|
+
const notification = await dispatch('notifications/fromGrowl', {
|
|
121
|
+
...data,
|
|
122
|
+
level: NotificationLevel.Error
|
|
123
|
+
}, { root: true });
|
|
124
|
+
|
|
81
125
|
commit('add', {
|
|
82
126
|
color: 'error',
|
|
83
127
|
icon: 'error',
|
|
84
|
-
timeout:
|
|
128
|
+
timeout: DEFAULT_TIMEOUT, // Errors timeout since they will be available as a notification
|
|
129
|
+
notification,
|
|
85
130
|
...data
|
|
86
131
|
});
|
|
87
132
|
},
|
|
88
133
|
|
|
89
|
-
fromError({ commit }, { title, err }) {
|
|
134
|
+
async fromError({ commit, dispatch }, { title, err }) {
|
|
135
|
+
// Send a notification for the growl
|
|
136
|
+
const notification = await dispatch('notifications/fromGrowl', {
|
|
137
|
+
title,
|
|
138
|
+
message: stringify(err),
|
|
139
|
+
level: NotificationLevel.Error
|
|
140
|
+
}, { root: true });
|
|
141
|
+
|
|
90
142
|
commit('add', {
|
|
91
143
|
color: 'error',
|
|
92
144
|
icon: 'error',
|
|
93
|
-
timeout:
|
|
145
|
+
timeout: DEFAULT_TIMEOUT, // Errors timeout since they will be available as a notification
|
|
146
|
+
notification,
|
|
94
147
|
title,
|
|
95
148
|
message: stringify(err),
|
|
96
149
|
});
|
|
150
|
+
},
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Used to create a growl when a notification is sent
|
|
154
|
+
*
|
|
155
|
+
* Growls are only shown for Success, Warning and Error notifications
|
|
156
|
+
*/
|
|
157
|
+
notification({ commit }, notification) {
|
|
158
|
+
const growl = {
|
|
159
|
+
title: notification.title,
|
|
160
|
+
message: notification.message,
|
|
161
|
+
notification: notification.id,
|
|
162
|
+
timeout: DEFAULT_TIMEOUT,
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
switch (notification.level) {
|
|
166
|
+
case NotificationLevel.Success:
|
|
167
|
+
growl.color = 'success';
|
|
168
|
+
growl.icon = 'checkmark';
|
|
169
|
+
break;
|
|
170
|
+
case NotificationLevel.Warning:
|
|
171
|
+
growl.color = 'warning';
|
|
172
|
+
growl.icon = 'warning';
|
|
173
|
+
break;
|
|
174
|
+
case NotificationLevel.Error:
|
|
175
|
+
growl.color = 'error';
|
|
176
|
+
growl.icon = 'error';
|
|
177
|
+
break;
|
|
178
|
+
default:
|
|
179
|
+
growl.skip = true;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// We don't show growls for info, announcement, task
|
|
183
|
+
if (!growl.skip) {
|
|
184
|
+
commit('add', growl);
|
|
185
|
+
}
|
|
97
186
|
}
|
|
98
187
|
};
|
package/store/index.js
CHANGED
|
@@ -39,6 +39,7 @@ import { STORE, BLANK_CLUSTER } from '@shell/store/store-types';
|
|
|
39
39
|
import { isDevBuild } from '@shell/utils/version';
|
|
40
40
|
import { markRaw } from 'vue';
|
|
41
41
|
import paginationUtils from '@shell/utils/pagination-utils';
|
|
42
|
+
import { addReleaseNotesNotification } from '@shell/utils/release-notes';
|
|
42
43
|
|
|
43
44
|
// Disables strict mode for all store instances to prevent warning about changing state outside of mutations
|
|
44
45
|
// because it's more efficient to do that sometimes.
|
|
@@ -896,6 +897,11 @@ export const actions = {
|
|
|
896
897
|
setBrand(brand);
|
|
897
898
|
}
|
|
898
899
|
|
|
900
|
+
// Add the notification for the release notes
|
|
901
|
+
if (isRancher) {
|
|
902
|
+
await addReleaseNotesNotification(dispatch, getters);
|
|
903
|
+
}
|
|
904
|
+
|
|
899
905
|
if (systemNamespaces) {
|
|
900
906
|
const namespace = (systemNamespaces.value || systemNamespaces.default)?.split(',');
|
|
901
907
|
|
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
import { md5 } from '@shell/utils/crypto';
|
|
2
|
+
import { randomStr } from '@shell/utils/string';
|
|
3
|
+
import { EncryptedNotification, Notification, StoredNotification } from '@shell/types/notifications';
|
|
4
|
+
import { encrypt, decrypt, deriveKey } from '@shell/utils/crypto/encryption';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Key used to store notifications in the browser's local storage
|
|
8
|
+
*/
|
|
9
|
+
const LOCAL_STORAGE_KEY_PREFIX = 'rancher-notifications-';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Expire notifications in seconds (14 days)
|
|
13
|
+
*/
|
|
14
|
+
const EXPIRY = 14 * 24 * 60 * 60;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Maximum number of notifications that will be kept
|
|
18
|
+
*/
|
|
19
|
+
const MAX_NOTIFICATIONS = 50;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Broadcast channel name to send changes across tabs
|
|
23
|
+
*/
|
|
24
|
+
const NOTIFICATION_CHANNEL_NAME = 'rancher-notification-sync';
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Store for the UI Notification Centre
|
|
28
|
+
*/
|
|
29
|
+
interface NotificationsStore {
|
|
30
|
+
localStorageKey: string,
|
|
31
|
+
userId: string;
|
|
32
|
+
notifications: StoredNotification[],
|
|
33
|
+
encryptionKey: CryptoKey | undefined,
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const state = function(): NotificationsStore {
|
|
37
|
+
const notifications: StoredNotification[] = [];
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
localStorageKey: '',
|
|
41
|
+
userId: '',
|
|
42
|
+
encryptionKey: undefined,
|
|
43
|
+
notifications,
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
let bc: BroadcastChannel;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Sync notifications to other tabs using the broadcast channel. Send the user id, to cover corner case
|
|
51
|
+
* where a stale login exists for a different user in another tab.
|
|
52
|
+
*/
|
|
53
|
+
function sync(userId: string, operation: string, param?: any) {
|
|
54
|
+
bc?.postMessage({
|
|
55
|
+
userId,
|
|
56
|
+
operation,
|
|
57
|
+
param
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async function saveEncryptedNotification(getters: any, notification: Notification) {
|
|
62
|
+
const toEncrypt: EncryptedNotification = {
|
|
63
|
+
title: notification.title,
|
|
64
|
+
message: notification.message,
|
|
65
|
+
level: notification.level,
|
|
66
|
+
primaryAction: notification.primaryAction,
|
|
67
|
+
secondaryAction: notification.secondaryAction,
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const localStorageKey = getters['localStorageKey'];
|
|
71
|
+
const encryptionKey = getters['encryptionKey'];
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
const data = JSON.stringify(toEncrypt);
|
|
75
|
+
const enc = await encrypt(data, encryptionKey);
|
|
76
|
+
|
|
77
|
+
window.localStorage.setItem(`${ localStorageKey }-${ notification.id }`, JSON.stringify(enc));
|
|
78
|
+
} catch (e) {
|
|
79
|
+
console.error('Unable to save notification to local storage', e); // eslint-disable-line no-console
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Sync the notifications index to local storage.
|
|
85
|
+
*
|
|
86
|
+
* We only store the non-sensitive data about the notifications in the index - the other data is stored in individual entries which are encrypted.
|
|
87
|
+
*/
|
|
88
|
+
function syncIndex(state: NotificationsStore) {
|
|
89
|
+
const localStorageKey = state.localStorageKey;
|
|
90
|
+
|
|
91
|
+
// We just want the id, created, read and progress properties for the index
|
|
92
|
+
const index = state.notifications.map((n) => ({
|
|
93
|
+
id: n.id,
|
|
94
|
+
created: n.created,
|
|
95
|
+
read: n.read,
|
|
96
|
+
progress: n.progress,
|
|
97
|
+
}));
|
|
98
|
+
|
|
99
|
+
try {
|
|
100
|
+
window.localStorage.setItem(localStorageKey, JSON.stringify(index));
|
|
101
|
+
} catch (e) {
|
|
102
|
+
console.error('Unable to save notifications index to local storage', e); // eslint-disable-line no-console
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export const getters = {
|
|
107
|
+
all: (state: NotificationsStore) => {
|
|
108
|
+
return state.notifications;
|
|
109
|
+
},
|
|
110
|
+
|
|
111
|
+
item: (state: NotificationsStore) => {
|
|
112
|
+
return (id: string) => {
|
|
113
|
+
return state.notifications.find((i) => i.id === id);
|
|
114
|
+
};
|
|
115
|
+
},
|
|
116
|
+
|
|
117
|
+
// Count of unread notifications
|
|
118
|
+
unreadCount: (state: NotificationsStore) => {
|
|
119
|
+
return state.notifications.filter((n) => !n.read).length;
|
|
120
|
+
},
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Local storage key includes the user key
|
|
124
|
+
*/
|
|
125
|
+
localStorageKey: (state: NotificationsStore) => {
|
|
126
|
+
return state.localStorageKey;
|
|
127
|
+
},
|
|
128
|
+
|
|
129
|
+
userId: (state: NotificationsStore) => {
|
|
130
|
+
return state.userId;
|
|
131
|
+
},
|
|
132
|
+
|
|
133
|
+
encryptionKey: (state: NotificationsStore) => {
|
|
134
|
+
return state.encryptionKey;
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
export const mutations = {
|
|
139
|
+
add(state: NotificationsStore, notification: Notification) {
|
|
140
|
+
if (!notification.id) {
|
|
141
|
+
notification.id = randomStr();
|
|
142
|
+
} else {
|
|
143
|
+
// Check that there is not already a notification with this id
|
|
144
|
+
const index = state.notifications.findIndex((n) => n.id === notification.id);
|
|
145
|
+
|
|
146
|
+
if (index !== -1) {
|
|
147
|
+
console.error(`Can not add a notification with the same id as an existing notification (${ notification.id })`); // eslint-disable-line no-console
|
|
148
|
+
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const stored = {
|
|
154
|
+
...notification,
|
|
155
|
+
read: false,
|
|
156
|
+
created: new Date()
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
// Add to top of list
|
|
160
|
+
state.notifications.unshift(stored);
|
|
161
|
+
|
|
162
|
+
// Check that we have not exceeded the maximum number of notifications
|
|
163
|
+
if (state.notifications.length > MAX_NOTIFICATIONS) {
|
|
164
|
+
const removed = state.notifications.pop();
|
|
165
|
+
|
|
166
|
+
if (removed) {
|
|
167
|
+
// Remove the encrypted data for the notification that we just removed
|
|
168
|
+
window.localStorage.removeItem(`${ state.localStorageKey }-${ removed.id }`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
syncIndex(state);
|
|
173
|
+
},
|
|
174
|
+
|
|
175
|
+
markRead(state: NotificationsStore, id: string) {
|
|
176
|
+
const notification = state.notifications.find((n) => n.id === id);
|
|
177
|
+
|
|
178
|
+
if (notification && !notification.read) {
|
|
179
|
+
notification.read = true;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
syncIndex(state);
|
|
183
|
+
},
|
|
184
|
+
|
|
185
|
+
markUnread(state: NotificationsStore, id: string) {
|
|
186
|
+
const notification = state.notifications.find((n) => n.id === id);
|
|
187
|
+
|
|
188
|
+
if (notification && notification.read) {
|
|
189
|
+
notification.read = false;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
syncIndex(state);
|
|
193
|
+
},
|
|
194
|
+
|
|
195
|
+
markAllRead(state: NotificationsStore) {
|
|
196
|
+
state.notifications.forEach((notification) => {
|
|
197
|
+
if (!notification.read) {
|
|
198
|
+
notification.read = true;
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
syncIndex(state);
|
|
203
|
+
},
|
|
204
|
+
|
|
205
|
+
update(state: NotificationsStore, notification: Partial<Notification>) {
|
|
206
|
+
if (notification?.id) {
|
|
207
|
+
const index = state.notifications.findIndex((n) => n.id === notification.id);
|
|
208
|
+
|
|
209
|
+
if (index >= 0) {
|
|
210
|
+
state.notifications[index] = {
|
|
211
|
+
...state.notifications[index],
|
|
212
|
+
...notification
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
syncIndex(state);
|
|
218
|
+
},
|
|
219
|
+
|
|
220
|
+
clearAll(state: NotificationsStore) {
|
|
221
|
+
// Remove the encrypted data for each notification
|
|
222
|
+
state.notifications.forEach((n) => {
|
|
223
|
+
window.localStorage.removeItem(`${ state.localStorageKey }-${ n.id }`);
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
state.notifications = [];
|
|
227
|
+
syncIndex(state);
|
|
228
|
+
},
|
|
229
|
+
|
|
230
|
+
remove(state: NotificationsStore, id: string) {
|
|
231
|
+
// Remove the encrypted data for the notification
|
|
232
|
+
window.localStorage.removeItem(`${ state.localStorageKey }-${ id }`);
|
|
233
|
+
|
|
234
|
+
state.notifications = state.notifications.filter((n) => n.id !== id);
|
|
235
|
+
syncIndex(state);
|
|
236
|
+
},
|
|
237
|
+
|
|
238
|
+
load(state: NotificationsStore, notifications: StoredNotification[]) {
|
|
239
|
+
state.notifications = notifications;
|
|
240
|
+
},
|
|
241
|
+
|
|
242
|
+
localStorageKey(state: NotificationsStore, userKey: string) {
|
|
243
|
+
state.localStorageKey = `${ LOCAL_STORAGE_KEY_PREFIX }${ userKey }`;
|
|
244
|
+
},
|
|
245
|
+
|
|
246
|
+
userId(state: NotificationsStore, userId: string) {
|
|
247
|
+
state.userId = userId;
|
|
248
|
+
},
|
|
249
|
+
|
|
250
|
+
encryptionKey(state: NotificationsStore, encryptionKey: CryptoKey) {
|
|
251
|
+
state.encryptionKey = encryptionKey;
|
|
252
|
+
},
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
export const actions = {
|
|
256
|
+
async add( { commit, dispatch, getters }: any, notification: Notification) {
|
|
257
|
+
// We encrypt the notification on add - this is the only time we will encrypt it
|
|
258
|
+
if (!notification.id) {
|
|
259
|
+
notification.id = randomStr();
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Need to save the encrypted notification to local storage
|
|
263
|
+
await saveEncryptedNotification(getters, notification);
|
|
264
|
+
|
|
265
|
+
commit('add', notification);
|
|
266
|
+
sync(getters['userId'], 'add', notification);
|
|
267
|
+
|
|
268
|
+
// Show a growl for the notification if necessary
|
|
269
|
+
dispatch('growl/notification', notification, { root: true });
|
|
270
|
+
},
|
|
271
|
+
|
|
272
|
+
async fromGrowl( { commit, getters }: any, notification: Notification) {
|
|
273
|
+
notification.id = randomStr();
|
|
274
|
+
|
|
275
|
+
// Need to save the encrypted notification to local storage
|
|
276
|
+
await saveEncryptedNotification(getters, notification);
|
|
277
|
+
|
|
278
|
+
commit('add', notification);
|
|
279
|
+
sync(getters['userId'], 'add', notification);
|
|
280
|
+
|
|
281
|
+
return notification.id;
|
|
282
|
+
},
|
|
283
|
+
|
|
284
|
+
update({ commit, getters }: any, notification: Notification) {
|
|
285
|
+
commit('update', notification);
|
|
286
|
+
sync(getters['userId'], 'update', notification);
|
|
287
|
+
},
|
|
288
|
+
|
|
289
|
+
async markRead({ commit, dispatch, getters }: any, id: string) {
|
|
290
|
+
commit('markRead', id);
|
|
291
|
+
sync(getters['userId'], 'markRead', id);
|
|
292
|
+
|
|
293
|
+
const notification = getters.item(id);
|
|
294
|
+
|
|
295
|
+
if (notification?.preference) {
|
|
296
|
+
await dispatch('prefs/set', notification.preference, { root: true });
|
|
297
|
+
}
|
|
298
|
+
},
|
|
299
|
+
|
|
300
|
+
async markUnread({ commit, dispatch, getters }: any, id: string) {
|
|
301
|
+
commit('markUnread', id);
|
|
302
|
+
sync(getters['userId'], 'markUnread', id);
|
|
303
|
+
|
|
304
|
+
const notification = getters.item(id) as Notification;
|
|
305
|
+
|
|
306
|
+
if (notification?.preference) {
|
|
307
|
+
await dispatch('prefs/set', {
|
|
308
|
+
key: notification.preference.key,
|
|
309
|
+
value: notification.preference.unsetValue || '',
|
|
310
|
+
}, { root: true });
|
|
311
|
+
}
|
|
312
|
+
},
|
|
313
|
+
|
|
314
|
+
async markAllRead({ commit, dispatch, getters }: any) {
|
|
315
|
+
commit('markAllRead');
|
|
316
|
+
sync(getters['userId'], 'markAllRead');
|
|
317
|
+
|
|
318
|
+
// For all notifications that have a preference, set the preference, since they are now read
|
|
319
|
+
const withPreference = getters.all.filter((n: Notification) => !!n.preference);
|
|
320
|
+
|
|
321
|
+
for (let i = 0; i < withPreference.length; i++) {
|
|
322
|
+
await dispatch('prefs/set', withPreference[i].preference, { root: true });
|
|
323
|
+
}
|
|
324
|
+
},
|
|
325
|
+
|
|
326
|
+
remove({ commit, getters }: any, id: string) {
|
|
327
|
+
commit('remove', id);
|
|
328
|
+
sync(getters['userId'], 'remove', id);
|
|
329
|
+
},
|
|
330
|
+
|
|
331
|
+
clearAll({ commit, getters }: any) {
|
|
332
|
+
commit('clearAll');
|
|
333
|
+
sync(getters['userId'], 'clearAll');
|
|
334
|
+
},
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Initialize the notifications store and load the notifications from local storage
|
|
338
|
+
*/
|
|
339
|
+
async init({ commit, getters } : any, userData: any) {
|
|
340
|
+
const userKey = userData.id;
|
|
341
|
+
const userId = userData.v3User?.uuid;
|
|
342
|
+
|
|
343
|
+
if (!userKey || !userId) {
|
|
344
|
+
console.error('Unable to initialize notifications - required user info not available'); // eslint-disable-line no-console
|
|
345
|
+
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// Notifications are stored under a key for the current user, so set the local storage key based on the user id
|
|
350
|
+
commit('localStorageKey', md5(userKey, 'hex'));
|
|
351
|
+
commit('userId', userId);
|
|
352
|
+
|
|
353
|
+
let index: StoredNotification[] = [];
|
|
354
|
+
let notifications: StoredNotification[] = [];
|
|
355
|
+
const localStorageKey = getters['localStorageKey'];
|
|
356
|
+
|
|
357
|
+
let encryptionKey;
|
|
358
|
+
|
|
359
|
+
try {
|
|
360
|
+
encryptionKey = await deriveKey(userId);
|
|
361
|
+
} catch (e) {
|
|
362
|
+
console.error('Unable to generate encryption key for notifications', e); // eslint-disable-line no-console
|
|
363
|
+
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// Store the encryption key
|
|
368
|
+
commit('encryptionKey', encryptionKey);
|
|
369
|
+
|
|
370
|
+
// Load the notifications from local storage
|
|
371
|
+
// We store the index of notifications in local storage, and the actual notification data is stored in individual entries which are encrypted
|
|
372
|
+
try {
|
|
373
|
+
const data = window.localStorage.getItem(localStorageKey) || '[]';
|
|
374
|
+
|
|
375
|
+
index = JSON.parse(data) as StoredNotification[];
|
|
376
|
+
} catch (e) {
|
|
377
|
+
console.error('Unable to read notifications from local storage', e); // eslint-disable-line no-console
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
for (let i = 0; i < index.length; i++) {
|
|
381
|
+
const n = index[i];
|
|
382
|
+
|
|
383
|
+
try {
|
|
384
|
+
const data = window.localStorage.getItem(`${ localStorageKey }-${ n.id }`);
|
|
385
|
+
const parsedData = data ? JSON.parse(data) : '{}';
|
|
386
|
+
const decryptedString = await decrypt(parsedData, encryptionKey);
|
|
387
|
+
const decrypted = JSON.parse(decryptedString) as EncryptedNotification;
|
|
388
|
+
|
|
389
|
+
// Overlay the decrypted data onto the notification
|
|
390
|
+
notifications.push({
|
|
391
|
+
...n,
|
|
392
|
+
...decrypted
|
|
393
|
+
});
|
|
394
|
+
} catch (e) {
|
|
395
|
+
console.error('Unable to decrypt notification data', e); // eslint-disable-line no-console
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// Expire old notifications
|
|
400
|
+
const now = new Date();
|
|
401
|
+
|
|
402
|
+
notifications = notifications.filter((n: StoredNotification) => {
|
|
403
|
+
// Try ... catch in case the date parsing fails
|
|
404
|
+
try {
|
|
405
|
+
const created = new Date(n.created);
|
|
406
|
+
const diff = (now.getTime() - created.getTime()) / 1000; // Diff in seconds
|
|
407
|
+
|
|
408
|
+
return diff < EXPIRY;
|
|
409
|
+
} catch (e) {}
|
|
410
|
+
|
|
411
|
+
return true;
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
commit('load', notifications);
|
|
415
|
+
|
|
416
|
+
// Set up broadcast listener to listen for updates from other tabs
|
|
417
|
+
bc = new BroadcastChannel(NOTIFICATION_CHANNEL_NAME);
|
|
418
|
+
|
|
419
|
+
bc.onmessage = (msgEvent: any) => {
|
|
420
|
+
// Ignore events where the user id does not match (corner case of stale login in another tab)
|
|
421
|
+
if (msgEvent?.data?.operation && msgEvent?.data?.userId === userId) {
|
|
422
|
+
commit(msgEvent.data.operation, msgEvent.data.param);
|
|
423
|
+
}
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
};
|
package/store/prefs.js
CHANGED
|
@@ -74,7 +74,6 @@ export const HIDE_REPOS = create('hide-repos', [], { parseJSON });
|
|
|
74
74
|
export const HIDE_DESC = create('hide-desc', [], { parseJSON });
|
|
75
75
|
export const HIDE_SENSITIVE = create('hide-sensitive', true, { options: [true, false], parseJSON });
|
|
76
76
|
export const SHOW_PRE_RELEASE = create('show-pre-release', false, { options: [false, true], parseJSON });
|
|
77
|
-
export const SHOW_CHART_MODE = create('chart-mode', 'featured', { parseJSON });
|
|
78
77
|
|
|
79
78
|
export const DATE_FORMAT = create('date-format', 'ddd, MMM D YYYY', {
|
|
80
79
|
options: [
|
package/store/type-map.js
CHANGED
|
@@ -512,21 +512,22 @@ export const getters = {
|
|
|
512
512
|
|
|
513
513
|
optionsFor(state, getters, rootState, rootGetters) {
|
|
514
514
|
const def = {
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
515
|
+
listCreateButtonLabelKey: undefined,
|
|
516
|
+
isCreatable: true,
|
|
517
|
+
isEditable: true,
|
|
518
|
+
isRemovable: true,
|
|
519
|
+
showState: true,
|
|
520
|
+
showAge: true,
|
|
521
|
+
canYaml: true,
|
|
522
|
+
namespaced: null,
|
|
523
|
+
listGroups: [],
|
|
524
|
+
listGroupsWillOverride: false,
|
|
525
|
+
listMandatorySort: null,
|
|
526
|
+
depaginate: false,
|
|
527
|
+
customRoute: undefined,
|
|
528
|
+
resourceEditMasthead: true,
|
|
529
|
+
custom: {},
|
|
530
|
+
subTypes: [],
|
|
530
531
|
};
|
|
531
532
|
|
|
532
533
|
return (schemaOrType, pagination) => {
|
|
@@ -754,11 +755,13 @@ export const getters = {
|
|
|
754
755
|
let group = findBy(tree.children, 'name', name);
|
|
755
756
|
|
|
756
757
|
if ( !group ) {
|
|
758
|
+
const groupDefaultTypeFor = getters.groupDefaultTypeFor(name);
|
|
759
|
+
|
|
757
760
|
group = {
|
|
758
761
|
name,
|
|
759
762
|
label,
|
|
760
763
|
weight: getters.groupWeightFor(name, forBasic),
|
|
761
|
-
defaultType:
|
|
764
|
+
defaultType: typeof groupDefaultTypeFor === 'function' ? groupDefaultTypeFor() : groupDefaultTypeFor,
|
|
762
765
|
};
|
|
763
766
|
|
|
764
767
|
tree.children.push(group);
|