@rancher/shell 3.0.7 → 3.0.8-rc.10
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/banner.svg +1 -0
- package/assets/brand/suse/dark/banner.svg +1 -0
- package/assets/brand/suse/dark/login-landscape.svg +1 -0
- package/assets/brand/suse/dark/rancher-logo.svg +1 -1
- package/assets/brand/suse/favicon.png +0 -0
- package/assets/brand/suse/login-landscape.svg +1 -0
- package/assets/brand/suse/metadata.json +11 -1
- package/assets/brand/suse/rancher-logo.svg +1 -1
- package/assets/fonts/suse/suse-v2-latin-300.woff +0 -0
- package/assets/fonts/suse/suse-v2-latin-300.woff2 +0 -0
- package/assets/fonts/suse/suse-v2-latin-600.woff +0 -0
- package/assets/fonts/suse/suse-v2-latin-600.woff2 +0 -0
- package/assets/fonts/suse/suse-v2-latin-700.woff +0 -0
- package/assets/fonts/suse/suse-v2-latin-700.woff2 +0 -0
- package/assets/fonts/suse/suse-v2-latin-800.woff +0 -0
- package/assets/fonts/suse/suse-v2-latin-800.woff2 +0 -0
- package/assets/fonts/suse/suse-v2-latin-regular.woff +0 -0
- package/assets/fonts/suse/suse-v2-latin-regular.woff2 +0 -0
- package/assets/images/content/README.md +5 -0
- package/assets/images/content/cloud-native.svg +84 -0
- package/assets/images/content/dark/cloud-native.svg +21 -0
- package/assets/images/content/dark/shield.svg +59 -0
- package/assets/images/content/dark/suse.svg +10 -0
- package/assets/images/content/shield.svg +59 -0
- package/assets/images/content/suse.svg +10 -0
- package/assets/images/vendor/githubapp.svg +13 -0
- package/assets/styles/base/_typography.scss +2 -1
- package/assets/styles/fonts/_fontstack.scss +53 -1
- package/assets/styles/global/_cards.scss +0 -3
- package/assets/styles/global/_layout.scss +21 -35
- package/assets/styles/themes/_dark.scss +1 -1
- package/assets/styles/themes/_light.scss +1 -1
- package/assets/styles/themes/_modern.scss +16 -8
- package/assets/styles/themes/_suse.scss +116 -24
- package/assets/translations/en-us.yaml +185 -21
- package/assets/translations/zh-hans.yaml +0 -4
- package/components/AutoscalerCard.vue +113 -0
- package/components/AutoscalerTab.vue +94 -0
- package/components/BackLink.vue +8 -0
- package/components/BannerGraphic.vue +36 -21
- package/components/BrandImage.vue +17 -6
- package/components/ClusterIconMenu.vue +1 -1
- package/components/ClusterProviderIcon.vue +1 -1
- package/components/Cron/CronExpressionEditor.vue +1 -1
- package/components/Cron/CronExpressionEditorModal.vue +1 -1
- package/components/Drawer/Chrome.vue +2 -6
- package/components/Drawer/ResourceDetailDrawer/ConfigTab.vue +4 -9
- package/components/Drawer/ResourceDetailDrawer/YamlTab.vue +3 -8
- package/components/Drawer/ResourceDetailDrawer/composables.ts +3 -4
- package/components/Drawer/ResourceDetailDrawer/index.vue +4 -9
- package/components/Drawer/ResourceDetailDrawer/types.ts +17 -0
- package/components/Drawer/types.ts +3 -0
- package/components/DynamicContent/DynamicContentBanner.vue +102 -0
- package/components/DynamicContent/DynamicContentCloseButton.vue +42 -0
- package/components/DynamicContent/DynamicContentIcon.vue +132 -0
- package/components/DynamicContent/DynamicContentPanel.vue +112 -0
- package/components/DynamicContent/content.ts +78 -0
- package/components/EmberPage.vue +1 -1
- package/components/IconOrSvg.vue +2 -2
- package/components/Inactivity.vue +222 -106
- package/components/InstallHelmCharts.vue +2 -2
- package/components/PaginatedResourceTable.vue +2 -6
- package/components/PopoverCard.vue +192 -0
- package/components/Questions/__tests__/index.test.ts +159 -0
- package/components/Resource/Detail/CopyToClipboard.vue +4 -1
- package/components/Resource/Detail/FetchLoader/composables.ts +18 -4
- package/components/Resource/Detail/Metadata/Annotations/index.vue +2 -2
- package/components/Resource/Detail/Metadata/IdentifyingInformation/__tests__/identifying-fields.test.ts +1 -1
- package/components/Resource/Detail/Metadata/IdentifyingInformation/identifying-fields.ts +4 -0
- package/components/Resource/Detail/Metadata/KeyValueRow.vue +1 -1
- package/components/Resource/Detail/Metadata/Labels/index.vue +2 -2
- package/components/Resource/Detail/Metadata/composables.ts +9 -9
- package/components/Resource/Detail/Metadata/index.vue +3 -3
- package/components/Resource/Detail/ResourcePopover/ResourcePopoverCard.vue +2 -19
- package/components/Resource/Detail/ResourcePopover/__tests__/ResourcePopoverCard.test.ts +0 -29
- package/components/Resource/Detail/ResourcePopover/__tests__/index.test.ts +132 -150
- package/components/Resource/Detail/ResourcePopover/index.vue +54 -159
- package/components/Resource/Detail/TitleBar/__tests__/index.test.ts +0 -2
- package/components/Resource/Detail/TitleBar/composables.ts +2 -1
- package/components/Resource/Detail/TitleBar/index.vue +10 -6
- package/components/Resource/Detail/composables.ts +12 -0
- package/components/ResourceDetail/Masthead/latest.vue +29 -0
- package/components/ResourceDetail/index.vue +5 -2
- package/components/ResourceList/Masthead.vue +1 -1
- package/components/SortableTable/index.vue +18 -2
- package/components/Tabbed/__tests__/index.test.ts +86 -0
- package/components/{nav/WindowManager → Window}/ContainerLogs.vue +1 -1
- package/components/{nav/WindowManager → Window}/ContainerLogsActions.vue +1 -0
- package/components/{nav/WindowManager → Window}/__tests__/ContainerLogs.test.ts +1 -1
- package/components/{nav/WindowManager → Window}/__tests__/ContainerShell.test.ts +2 -2
- package/components/__tests__/AutoscalerCard.test.ts +154 -0
- package/components/__tests__/AutoscalerTab.test.ts +125 -0
- package/components/__tests__/PopoverCard.test.ts +204 -0
- package/components/auth/SelectPrincipal.vue +24 -6
- package/components/auth/__tests__/SelectPrincipal.test.ts +119 -0
- package/components/fleet/FleetConfigMapSelector.vue +117 -0
- package/components/fleet/FleetSecretSelector.vue +127 -0
- package/components/fleet/__tests__/FleetConfigMapSelector.test.ts +125 -0
- package/components/fleet/__tests__/FleetSecretSelector.test.ts +82 -0
- package/components/form/FileImageSelector.vue +13 -4
- package/components/form/FileSelector.vue +11 -2
- package/components/form/ResourceLabeledSelect.vue +1 -0
- package/components/form/__tests__/ResourceLabeledSelect.test.ts +90 -0
- package/components/formatter/Autoscaler.vue +97 -0
- package/components/formatter/InternalExternalIP.vue +198 -24
- package/components/formatter/__tests__/Autoscaler.test.ts +156 -0
- package/components/formatter/__tests__/InternalExternalIP.test.ts +133 -0
- package/components/google/util/__tests__/formatter.test.ts +47 -0
- package/components/google/util/formatter.ts +5 -2
- package/components/nav/Group.vue +12 -3
- package/components/nav/Header.vue +37 -16
- package/components/nav/NamespaceFilter.vue +13 -1
- package/components/nav/NotificationCenter/index.vue +2 -1
- package/components/nav/TopLevelMenu.helper.ts +16 -6
- package/components/nav/TopLevelMenu.vue +4 -2
- package/components/{DraggableZone.vue → nav/WindowManager/PinArea.vue} +47 -80
- package/components/nav/WindowManager/composables/useComponentsMount.ts +70 -0
- package/components/nav/WindowManager/composables/useDimensionsHandler.ts +105 -0
- package/components/nav/WindowManager/composables/useDragHandler.ts +99 -0
- package/components/nav/WindowManager/composables/usePanelHandler.ts +72 -0
- package/components/nav/WindowManager/composables/usePanelsHandler.ts +14 -0
- package/components/nav/WindowManager/composables/useResizeHandler.ts +167 -0
- package/components/nav/WindowManager/composables/useTabsHandler.ts +51 -0
- package/components/nav/WindowManager/constants.ts +23 -0
- package/components/nav/WindowManager/index.vue +61 -575
- package/components/nav/WindowManager/panels/HorizontalPanel.vue +265 -0
- package/components/nav/WindowManager/panels/TabBodyContainer.vue +39 -0
- package/components/nav/WindowManager/panels/VerticalPanel.vue +308 -0
- package/components/templates/default.vue +4 -40
- package/components/templates/home.vue +31 -5
- package/components/templates/plain.vue +30 -4
- package/components/templates/standalone.vue +1 -1
- package/composables/useI18n.ts +10 -1
- package/composables/useInterval.ts +15 -0
- package/config/__test__/uiplugins.test.ts +309 -0
- package/config/labels-annotations.js +9 -1
- package/config/product/auth.js +1 -0
- package/config/product/explorer.js +3 -1
- package/config/product/manager.js +20 -9
- package/config/query-params.js +1 -0
- package/config/router/routes.js +10 -2
- package/config/settings.ts +10 -2
- package/config/store.js +4 -2
- package/config/table-headers.js +8 -0
- package/config/types.js +11 -0
- package/config/uiplugins.js +46 -2
- package/config/version.js +1 -1
- package/core/__test__/extension-manager-impl.test.js +236 -0
- package/core/extension-manager-impl.js +23 -6
- package/core/plugin-helpers.ts +2 -0
- package/core/types-provisioning.ts +4 -1
- package/detail/pod.vue +1 -0
- package/detail/provisioning.cattle.io.cluster.vue +13 -1
- package/dialog/AddonConfigConfirmationDialog.vue +45 -1
- package/dialog/DeveloperLoadExtensionDialog.vue +12 -3
- package/dialog/RollbackWorkloadDialog.vue +2 -5
- package/directives/ui-context.ts +103 -0
- package/edit/__tests__/fleet.cattle.io.helmop.test.ts +52 -11
- package/edit/auth/AuthProviderWarningBanners.vue +14 -1
- package/edit/auth/__tests__/oidc.test.ts +26 -0
- package/edit/auth/github-app-steps.vue +97 -0
- package/edit/auth/github-steps.vue +75 -0
- package/edit/auth/github.vue +99 -65
- package/edit/auth/oidc.vue +5 -1
- package/edit/autoscaling.horizontalpodautoscaler/index.vue +1 -0
- package/edit/cloudcredential.vue +1 -1
- package/edit/configmap.vue +1 -0
- package/edit/constraints.gatekeeper.sh.constraint/index.vue +1 -0
- package/edit/fleet.cattle.io.gitrepo.vue +0 -10
- package/edit/fleet.cattle.io.helmop.vue +51 -2
- 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 -1
- package/edit/management.cattle.io.project.vue +1 -0
- package/edit/monitoring.coreos.com.alertmanagerconfig/index.vue +4 -1
- package/edit/monitoring.coreos.com.alertmanagerconfig/receiverConfig.vue +2 -1
- package/edit/monitoring.coreos.com.prometheusrule/index.vue +1 -0
- package/edit/monitoring.coreos.com.receiver/index.vue +2 -1
- package/edit/monitoring.coreos.com.route.vue +1 -1
- package/edit/namespace.vue +1 -0
- package/edit/networking.istio.io.destinationrule/index.vue +1 -0
- package/edit/networking.k8s.io.ingress/index.vue +1 -0
- package/edit/networking.k8s.io.networkpolicy/PolicyRuleTarget.vue +15 -5
- package/edit/networking.k8s.io.networkpolicy/PolicyRules.vue +1 -0
- package/edit/networking.k8s.io.networkpolicy/index.vue +1 -0
- package/edit/node.vue +1 -0
- package/edit/persistentvolume/index.vue +27 -22
- package/edit/persistentvolume/plugins/awsElasticBlockStore.vue +13 -14
- package/edit/persistentvolume/plugins/azureDisk.vue +49 -48
- package/edit/persistentvolume/plugins/azureFile.vue +15 -14
- package/edit/persistentvolume/plugins/cephfs.vue +15 -14
- package/edit/persistentvolume/plugins/cinder.vue +15 -14
- package/edit/persistentvolume/plugins/csi.vue +18 -16
- package/edit/persistentvolume/plugins/fc.vue +13 -14
- package/edit/persistentvolume/plugins/flexVolume.vue +15 -14
- package/edit/persistentvolume/plugins/flocker.vue +1 -3
- package/edit/persistentvolume/plugins/gcePersistentDisk.vue +13 -14
- package/edit/persistentvolume/plugins/glusterfs.vue +15 -14
- package/edit/persistentvolume/plugins/hostPath.vue +40 -39
- package/edit/persistentvolume/plugins/iscsi.vue +13 -14
- package/edit/persistentvolume/plugins/local.vue +1 -3
- package/edit/persistentvolume/plugins/longhorn.vue +23 -22
- package/edit/persistentvolume/plugins/nfs.vue +15 -14
- package/edit/persistentvolume/plugins/photonPersistentDisk.vue +1 -14
- package/edit/persistentvolume/plugins/portworxVolume.vue +15 -14
- package/edit/persistentvolume/plugins/quobyte.vue +15 -14
- package/edit/persistentvolume/plugins/rbd.vue +15 -14
- package/edit/persistentvolume/plugins/scaleIO.vue +15 -14
- package/edit/persistentvolume/plugins/storageos.vue +15 -14
- package/edit/persistentvolume/plugins/vsphereVolume.vue +1 -3
- package/edit/provisioning.cattle.io.cluster/CustomCommand.vue +32 -5
- package/edit/provisioning.cattle.io.cluster/__tests__/CustomCommand.test.ts +35 -0
- package/edit/provisioning.cattle.io.cluster/__tests__/Networking.test.ts +155 -0
- package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +11 -9
- package/edit/provisioning.cattle.io.cluster/index.vue +25 -15
- package/edit/provisioning.cattle.io.cluster/rke2.vue +98 -17
- package/edit/provisioning.cattle.io.cluster/tabs/AddOnConfig.vue +28 -2
- package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +107 -5
- package/edit/provisioning.cattle.io.cluster/tabs/networking/index.vue +92 -4
- package/edit/secret/index.vue +1 -1
- package/edit/service.vue +9 -4
- package/edit/serviceaccount.vue +1 -0
- package/edit/storage.k8s.io.storageclass/index.vue +1 -0
- package/edit/workload/index.vue +2 -1
- package/edit/workload/mixins/workload.js +1 -1
- package/initialize/App.vue +4 -4
- package/initialize/install-directives.js +2 -0
- package/initialize/install-plugins.js +19 -2
- package/list/projectsecret.vue +1 -1
- package/list/provisioning.cattle.io.cluster.vue +15 -2
- package/machine-config/amazonec2.vue +42 -135
- package/machine-config/azure.vue +1 -1
- package/machine-config/components/EC2Networking.vue +490 -0
- package/machine-config/components/__tests__/EC2Networking.test.ts +148 -0
- package/machine-config/components/__tests__/utils/vpcSubnetMockData.js +294 -0
- package/machine-config/digitalocean.vue +11 -0
- package/machine-config/google.vue +1 -1
- package/mixins/__tests__/brand.spec.ts +2 -2
- package/mixins/__tests__/chart.test.ts +21 -0
- package/mixins/brand.js +1 -7
- package/mixins/chart.js +8 -2
- package/mixins/create-edit-view/index.js +5 -0
- package/models/__tests__/chart.test.ts +49 -12
- package/models/__tests__/compliance.cattle.io.clusterscanprofile.spec.js +30 -0
- package/models/__tests__/provisioning.cattle.io.cluster.test.ts +112 -5
- package/models/catalog.cattle.io.app.js +1 -1
- package/models/chart.js +28 -14
- package/models/cluster/node.js +13 -6
- package/models/cluster.x-k8s.io.machine.js +10 -20
- package/models/cluster.x-k8s.io.machinedeployment.js +5 -1
- package/models/compliance.cattle.io.clusterscanprofile.js +1 -1
- package/models/management.cattle.io.authconfig.js +1 -0
- package/models/management.cattle.io.cluster.js +21 -3
- package/models/management.cattle.io.kontainerdriver.js +1 -0
- package/models/provisioning.cattle.io.cluster.js +249 -33
- package/package.json +6 -5
- package/pages/auth/login.vue +43 -4
- package/pages/auth/verify.vue +1 -1
- package/pages/c/_cluster/apps/charts/AppChartCardSubHeader.vue +3 -2
- package/pages/c/_cluster/apps/charts/__tests__/chart.test.ts +135 -0
- package/pages/c/_cluster/apps/charts/chart.vue +35 -17
- package/pages/c/_cluster/apps/charts/index.vue +11 -13
- package/pages/c/_cluster/apps/charts/install.vue +1 -1
- package/pages/c/_cluster/explorer/EventsTable.vue +89 -3
- package/pages/c/_cluster/explorer/index.vue +8 -6
- package/pages/c/_cluster/explorer/tools/index.vue +3 -3
- package/pages/c/_cluster/manager/hostedprovider/index.vue +220 -0
- package/pages/c/_cluster/settings/brand.vue +1 -1
- package/pages/c/_cluster/settings/performance.vue +12 -25
- package/pages/c/_cluster/uiplugins/__tests__/index.test.ts +7 -0
- package/pages/c/_cluster/uiplugins/catalogs.vue +147 -0
- package/pages/c/_cluster/uiplugins/index.vue +126 -184
- package/pages/home.vue +327 -16
- package/pkg/dynamic-importer.lib.js +4 -0
- package/plugins/axios.js +2 -1
- package/plugins/dashboard-client-init.js +3 -0
- package/plugins/dashboard-store/actions.js +1 -1
- package/plugins/dashboard-store/getters.js +18 -1
- package/plugins/dashboard-store/resource-class.js +21 -6
- package/plugins/dynamic-content.js +13 -0
- package/plugins/i18n.js +8 -0
- package/plugins/steve/__tests__/steve-pagination-utils.test.ts +333 -0
- package/plugins/steve/steve-pagination-utils.ts +41 -22
- package/plugins/steve/subscribe.js +17 -9
- package/plugins/subscribe-events.ts +4 -2
- package/rancher-components/Form/Checkbox/Checkbox.vue +1 -1
- package/rancher-components/Pill/RcStatusBadge/RcStatusBadge.vue +6 -34
- package/rancher-components/Pill/RcStatusBadge/index.ts +0 -1
- package/rancher-components/Pill/RcStatusBadge/types.ts +1 -1
- package/rancher-components/Pill/RcStatusIndicator/RcStatusIndicator.vue +5 -28
- package/rancher-components/Pill/RcStatusIndicator/types.ts +2 -1
- package/rancher-components/Pill/types.ts +0 -1
- package/rancher-components/RcDropdown/RcDropdownItem.vue +1 -0
- package/rancher-components/RcDropdown/RcDropdownItemSelect.vue +5 -1
- package/rancher-components/RcIcon/RcIcon.test.ts +51 -0
- package/rancher-components/RcIcon/RcIcon.vue +46 -0
- package/rancher-components/RcIcon/index.ts +1 -0
- package/rancher-components/RcIcon/types.ts +160 -0
- package/rancher-components/utils/status.test.ts +67 -0
- package/rancher-components/utils/status.ts +77 -0
- package/scripts/extension/publish +1 -1
- package/scripts/typegen.sh +1 -0
- package/store/action-menu.js +8 -0
- package/store/auth.js +11 -6
- package/store/aws.js +8 -6
- package/store/catalog.js +6 -0
- package/store/features.js +2 -0
- package/store/index.js +45 -20
- package/store/notifications.ts +51 -4
- package/store/plugins.js +7 -3
- package/store/prefs.js +12 -6
- package/store/type-map.js +3 -3
- package/store/ui-context.ts +86 -0
- package/store/wm.ts +244 -0
- package/types/kube/kube-api.ts +2 -1
- package/types/notifications/index.ts +27 -3
- package/types/rancher/index.d.ts +1 -0
- package/types/resources/settings.d.ts +29 -7
- package/types/shell/index.d.ts +138 -4
- package/types/store/__tests__/pagination.types.spec.ts +137 -0
- package/types/store/pagination.types.ts +157 -9
- package/types/store/subscribe-events.types.ts +8 -1
- package/types/store/subscribe.types.ts +1 -0
- package/types/window-manager.ts +24 -0
- package/utils/__tests__/cluster.test.ts +379 -1
- package/utils/__tests__/object.test.ts +19 -0
- package/utils/__tests__/provider.test.ts +98 -0
- package/utils/__tests__/selector-typed.test.ts +263 -0
- package/utils/__tests__/version.test.ts +19 -1
- package/utils/autoscaler-utils.ts +7 -0
- package/utils/back-off.ts +3 -3
- package/utils/brand.ts +29 -0
- package/utils/chart.js +18 -0
- package/utils/cluster.js +157 -3
- package/utils/color.js +1 -1
- package/utils/dynamic-content/__tests__/announcement.test.ts +498 -0
- package/utils/dynamic-content/__tests__/config.test.ts +187 -0
- package/utils/dynamic-content/__tests__/index.test.ts +390 -0
- package/utils/dynamic-content/__tests__/info.test.ts +275 -0
- package/utils/dynamic-content/__tests__/new-release.test.ts +216 -0
- package/utils/dynamic-content/__tests__/support-notice.test.ts +262 -0
- package/utils/dynamic-content/__tests__/util.test.ts +235 -0
- package/utils/dynamic-content/announcement.ts +142 -0
- package/utils/dynamic-content/config.ts +55 -0
- package/utils/dynamic-content/example.json +40 -0
- package/utils/dynamic-content/index.ts +277 -0
- package/utils/dynamic-content/info.ts +261 -0
- package/utils/dynamic-content/new-release.ts +126 -0
- package/utils/dynamic-content/notification-handler.ts +48 -0
- package/utils/dynamic-content/support-notice.ts +169 -0
- package/utils/dynamic-content/types.d.ts +153 -0
- package/utils/dynamic-content/util.ts +122 -0
- package/utils/dynamic-importer.js +2 -2
- package/utils/favicon.js +4 -4
- package/utils/inactivity.ts +104 -0
- package/utils/object.js +20 -2
- package/utils/pagination-utils.ts +19 -4
- package/utils/pagination-wrapper.ts +12 -8
- package/utils/provider.ts +14 -0
- package/utils/release-notes.ts +1 -1
- package/utils/scroll.js +7 -0
- package/utils/selector-typed.ts +6 -2
- package/utils/settings.ts +15 -0
- package/utils/validators/machine-pool.ts +13 -3
- package/utils/version.js +15 -0
- package/assets/images/icons/document.svg +0 -3
- package/plugins/nuxt-client-init.js +0 -3
- package/store/wm.js +0 -95
- /package/components/{nav/WindowManager → Window}/ChartReadme.vue +0 -0
- /package/components/{nav/WindowManager → Window}/ContainerShell.vue +0 -0
- /package/components/{nav/WindowManager → Window}/KubectlShell.vue +0 -0
- /package/components/{nav/WindowManager → Window}/MachineSsh.vue +0 -0
- /package/components/{nav/WindowManager → Window}/Window.vue +0 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Notification handler for dynamic content announcements
|
|
3
|
+
*
|
|
4
|
+
* This provides custom handling for read/unread state using a single user preference
|
|
5
|
+
*/
|
|
6
|
+
import { Notification, NotificationHandler } from '@shell/types/notifications';
|
|
7
|
+
import { READ_ANNOUNCEMENTS } from '@shell/store/prefs';
|
|
8
|
+
import { ANNOUNCEMENT_PREFIX } from './announcement';
|
|
9
|
+
|
|
10
|
+
// Global name for this handler that can be used when creating notifications to associate them with this handler
|
|
11
|
+
export const DynamicContentAnnouncementHandlerName = 'dc-announcements';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Create the dynamic content notification handler
|
|
15
|
+
*
|
|
16
|
+
* This is for announcements, where we need to manage an array of IDs of announcements that have been read, which
|
|
17
|
+
* is taken care of by this custom handler.
|
|
18
|
+
*
|
|
19
|
+
* When a notification is read/unread that specifies this handler, we will add or remove its ID from the list of
|
|
20
|
+
* read IDs that we maintain in the user preference value.
|
|
21
|
+
*
|
|
22
|
+
* This allows us to use a single user preference to track read announcements
|
|
23
|
+
*/
|
|
24
|
+
export function createHandler(store: any): NotificationHandler {
|
|
25
|
+
return {
|
|
26
|
+
async onReadUpdated(notification: Notification, read: boolean) {
|
|
27
|
+
if (!notification.id.startsWith(ANNOUNCEMENT_PREFIX)) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const id = notification.id.substring(ANNOUNCEMENT_PREFIX.length);
|
|
32
|
+
const announcements = store.getters['notifications/all'].filter((n: any) => n.id.startsWith(ANNOUNCEMENT_PREFIX));
|
|
33
|
+
const pref = store.getters['prefs/get'](READ_ANNOUNCEMENTS) || '';
|
|
34
|
+
const values = !pref.length ? [] : pref.split(',').filter((v: string) => !announcements.includes(v));
|
|
35
|
+
const valuesUnique = new Set(values);
|
|
36
|
+
|
|
37
|
+
if (read) {
|
|
38
|
+
valuesUnique.add(id);
|
|
39
|
+
} else {
|
|
40
|
+
valuesUnique.delete(id);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const newValues = Array.from(valuesUnique).sort();
|
|
44
|
+
|
|
45
|
+
await store.dispatch('prefs/set', { key: READ_ANNOUNCEMENTS, value: newValues.join(',') });
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* The code in this file is responsible for adding Support notifications driven off of the dynamic content metadata
|
|
4
|
+
*
|
|
5
|
+
* This covers these cases:
|
|
6
|
+
*
|
|
7
|
+
* 1. Current release has reached End of Maintenance (EOL)
|
|
8
|
+
* 2. Current release has reached End of Maintenance (EOM)
|
|
9
|
+
* 3. Current release is approaching End of Maintenance (EOL)
|
|
10
|
+
* 4. Current release is approaching End of Maintenance (EOM)
|
|
11
|
+
*
|
|
12
|
+
* Note that we process in the order to that shown above, and stop at the first one that is active - so reaching EOL will show a notification
|
|
13
|
+
* and we won't look at the others.
|
|
14
|
+
*
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import semver from 'semver';
|
|
18
|
+
import day from 'dayjs';
|
|
19
|
+
import { NotificationLevel } from '@shell/types/notifications';
|
|
20
|
+
import { READ_SUPPORT_NOTICE, READ_UPCOMING_SUPPORT_NOTICE } from '@shell/store/prefs';
|
|
21
|
+
import { removeMatchingNotifications } from './util';
|
|
22
|
+
import { Context, VersionInfo, UpcomingSupportInfo, SupportInfo } from './types';
|
|
23
|
+
import { UPDATE_DATE_FORMAT } from './index';
|
|
24
|
+
|
|
25
|
+
// Number of days ahead of upcoming EOM or EOL that we will notify the user
|
|
26
|
+
const DEFAULT_UPCOMING_WINDOW = 30;
|
|
27
|
+
|
|
28
|
+
// Prefixes used in the notifications IDs created here
|
|
29
|
+
const SUPPORT_NOTICE_PREFIX = 'support-notice-';
|
|
30
|
+
const UPCOMING_SUPPORT_NOTICE_PREFIX = 'upcoming-support-notice-';
|
|
31
|
+
|
|
32
|
+
// Prefixes used in the value of the user preference to track which notifications the user has read
|
|
33
|
+
const PREFIX = {
|
|
34
|
+
EOM: 'eom',
|
|
35
|
+
EOL: 'eol',
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// Internal type used with convenience functions
|
|
39
|
+
type Config = {
|
|
40
|
+
prefValuePrefix?: string;
|
|
41
|
+
pref: any;
|
|
42
|
+
notificationPrefix: string;
|
|
43
|
+
titleKey: string;
|
|
44
|
+
messageKey: string;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Main exported function that will process the support (stateInfo)
|
|
49
|
+
*
|
|
50
|
+
* @param context Context helper providing access to config, logger, store
|
|
51
|
+
* @param statusInfo Support information
|
|
52
|
+
* @param versionInfo Version information
|
|
53
|
+
*/
|
|
54
|
+
|
|
55
|
+
export async function processSupportNotices(context: Context, statusInfo: SupportInfo | undefined, versionInfo: VersionInfo): Promise<void> {
|
|
56
|
+
if (!statusInfo || !versionInfo?.version) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const { version } = versionInfo;
|
|
61
|
+
const { logger } = context;
|
|
62
|
+
|
|
63
|
+
// TODO: ****************************************************************************************
|
|
64
|
+
// TODO: Check if the user is an admin if we are Prime - we only notify admins of EOM and EOL
|
|
65
|
+
// TODO: ****************************************************************************************
|
|
66
|
+
|
|
67
|
+
const status = statusInfo.status || {};
|
|
68
|
+
const majorMinor = `${ semver.major(version) }.${ semver.minor(version) }`;
|
|
69
|
+
|
|
70
|
+
// Check if this version is EOL - we warn of EOL
|
|
71
|
+
// If a version is EOL, then is has passed EOM, so we don't need to check that
|
|
72
|
+
if (status.eol && semver.satisfies(version, status.eol)) {
|
|
73
|
+
logger.info(`This version (${ version }) is End of Life`);
|
|
74
|
+
|
|
75
|
+
return await checkAndAddNotification(context, {
|
|
76
|
+
prefValuePrefix: PREFIX.EOL,
|
|
77
|
+
pref: READ_SUPPORT_NOTICE,
|
|
78
|
+
notificationPrefix: SUPPORT_NOTICE_PREFIX,
|
|
79
|
+
titleKey: 'dynamicContent.eol.title',
|
|
80
|
+
messageKey: 'dynamicContent.eol.message',
|
|
81
|
+
}, majorMinor);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (status.eom && semver.satisfies(version, status.eom)) {
|
|
85
|
+
logger.info(`This version (${ version }) is End of Maintenance`);
|
|
86
|
+
|
|
87
|
+
return await checkAndAddNotification(context, {
|
|
88
|
+
prefValuePrefix: PREFIX.EOM,
|
|
89
|
+
pref: READ_SUPPORT_NOTICE,
|
|
90
|
+
notificationPrefix: SUPPORT_NOTICE_PREFIX,
|
|
91
|
+
titleKey: 'dynamicContent.eom.title',
|
|
92
|
+
messageKey: 'dynamicContent.eom.message',
|
|
93
|
+
}, majorMinor);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Now check for upcoming EOL or EOM
|
|
97
|
+
|
|
98
|
+
// Upcoming EOL
|
|
99
|
+
if (statusInfo.upcoming?.eol && semver.satisfies(version, statusInfo.upcoming.eol.version)) {
|
|
100
|
+
if (await checkAndAddUpcomingNotification(context, statusInfo.upcoming.eol, {
|
|
101
|
+
prefValuePrefix: PREFIX.EOL,
|
|
102
|
+
pref: READ_UPCOMING_SUPPORT_NOTICE,
|
|
103
|
+
notificationPrefix: UPCOMING_SUPPORT_NOTICE_PREFIX,
|
|
104
|
+
titleKey: 'dynamicContent.upcomingEol.title',
|
|
105
|
+
messageKey: 'dynamicContent.upcomingEol.message',
|
|
106
|
+
}, majorMinor)) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Upcoming EOM
|
|
112
|
+
if (statusInfo.upcoming?.eom && semver.satisfies(version, statusInfo.upcoming.eom.version)) {
|
|
113
|
+
await checkAndAddUpcomingNotification(context, statusInfo.upcoming.eom, {
|
|
114
|
+
prefValuePrefix: PREFIX.EOM,
|
|
115
|
+
pref: READ_UPCOMING_SUPPORT_NOTICE,
|
|
116
|
+
notificationPrefix: UPCOMING_SUPPORT_NOTICE_PREFIX,
|
|
117
|
+
titleKey: 'dynamicContent.upcomingEom.title',
|
|
118
|
+
messageKey: 'dynamicContent.upcomingEom.message',
|
|
119
|
+
}, majorMinor);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async function checkAndAddUpcomingNotification(context: Context, info: UpcomingSupportInfo, config: Config, majorMinor: string): Promise<boolean> {
|
|
124
|
+
const now = day(day().format(UPDATE_DATE_FORMAT));
|
|
125
|
+
const upcomingDate = day(day(info.date).format(UPDATE_DATE_FORMAT));
|
|
126
|
+
const distance = upcomingDate.diff(now, 'day');
|
|
127
|
+
const noticeWindow = info.noticeDays || DEFAULT_UPCOMING_WINDOW;
|
|
128
|
+
|
|
129
|
+
// If we've passed the upcoming date, then ignore, as this should have been covered by the eom status
|
|
130
|
+
if (distance > 0 && distance < noticeWindow) {
|
|
131
|
+
await checkAndAddNotification(context, config, majorMinor, distance);
|
|
132
|
+
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Check if a notification already exists or has already been read and if not, add it
|
|
141
|
+
*
|
|
142
|
+
* @param context Context helper providing access to config, logger, store
|
|
143
|
+
* @param config Configuration for the notification
|
|
144
|
+
* @param majorMinor Major.Minor version number
|
|
145
|
+
* @param distance Number of days until the event occurs (for upcoming support events)
|
|
146
|
+
*/
|
|
147
|
+
async function checkAndAddNotification(context: Context, config: Config, majorMinor: string, distance?: number) {
|
|
148
|
+
const { dispatch, getters, logger } = context;
|
|
149
|
+
const t = getters['i18n/t'];
|
|
150
|
+
const lastReadNotice = getters['prefs/get'](config.pref) || '';
|
|
151
|
+
const prefValue = config.prefValuePrefix ? `${ config.prefValuePrefix }-${ majorMinor }` : majorMinor;
|
|
152
|
+
|
|
153
|
+
if (!await removeMatchingNotifications(context, config.notificationPrefix, prefValue) && lastReadNotice !== prefValue) {
|
|
154
|
+
const notification = {
|
|
155
|
+
id: `${ config.notificationPrefix }${ prefValue }`,
|
|
156
|
+
level: NotificationLevel.Warning,
|
|
157
|
+
title: t(config.titleKey, { version: majorMinor, days: distance }),
|
|
158
|
+
message: t(config.messageKey, { version: majorMinor, days: distance }),
|
|
159
|
+
preference: {
|
|
160
|
+
key: config.pref,
|
|
161
|
+
value: prefValue
|
|
162
|
+
},
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
logger.info(`Adding support notification for ${ majorMinor } (${ config.notificationPrefix }${ config.prefValuePrefix })`);
|
|
166
|
+
|
|
167
|
+
await dispatch('notifications/add', notification);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types for Dynamic Content
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { SemVer } from 'semver';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Logger interface for dynamic content
|
|
9
|
+
*/
|
|
10
|
+
export interface Logger {
|
|
11
|
+
error: Function,
|
|
12
|
+
info: Function,
|
|
13
|
+
debug: Function,
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Distribution type
|
|
18
|
+
*/
|
|
19
|
+
export type Distribution = 'community' | 'prime';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Dynamic Content configuration
|
|
23
|
+
*/
|
|
24
|
+
export type Configuration = {
|
|
25
|
+
enabled: boolean;
|
|
26
|
+
debug: boolean;
|
|
27
|
+
log: boolean;
|
|
28
|
+
endpoint: string;
|
|
29
|
+
prime: boolean;
|
|
30
|
+
distribution: Distribution;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Settings configuration that can be supplied in the dynamic content package
|
|
35
|
+
*/
|
|
36
|
+
export type SettingsInfo = {
|
|
37
|
+
releaseNotesUrl: string; // URL format to use when generating release note links for new releases
|
|
38
|
+
suseExtensions: string[]; // Names of extra SUSE UI extensions on top of the list built-in
|
|
39
|
+
debugVersion?: string;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Common context passed through various functions
|
|
44
|
+
*/
|
|
45
|
+
export type Context = {
|
|
46
|
+
dispatch: Function,
|
|
47
|
+
getters: any,
|
|
48
|
+
axios: any,
|
|
49
|
+
logger: Logger,
|
|
50
|
+
isAdmin: boolean,
|
|
51
|
+
config: Configuration,
|
|
52
|
+
settings: SettingsInfo,
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Version information
|
|
57
|
+
*/
|
|
58
|
+
export type VersionInfo = {
|
|
59
|
+
version: SemVer;
|
|
60
|
+
isPrime: boolean;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Information for a new release
|
|
65
|
+
*/
|
|
66
|
+
export type ReleaseInfo = {
|
|
67
|
+
name: string;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Information for an upcoming release
|
|
72
|
+
*/
|
|
73
|
+
export type UpcomingSupportInfo = {
|
|
74
|
+
version: string, // Version number: semver (e.g. '<= 2.12')
|
|
75
|
+
date: Date, // Date when the eom/eol takes place (note, only the day part is used, time is ignored)
|
|
76
|
+
noticeDays?: number, // Number of days in advance to notify the user (if not specified, the default is used)
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Support information covering current EOM/EOL and upcoming EOM/EOL information
|
|
81
|
+
*/
|
|
82
|
+
export type SupportInfo = {
|
|
83
|
+
status: {
|
|
84
|
+
eom: string,
|
|
85
|
+
eol: string,
|
|
86
|
+
},
|
|
87
|
+
upcoming: {
|
|
88
|
+
eom: UpcomingSupportInfo,
|
|
89
|
+
eol: UpcomingSupportInfo,
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Call to action for an announcement
|
|
95
|
+
*/
|
|
96
|
+
export type CallToAction = {
|
|
97
|
+
action: string;
|
|
98
|
+
link: string;
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Announcements to be shown in the notification center or on the home page
|
|
103
|
+
*
|
|
104
|
+
* Target determines where the notification will be shown, supported values:
|
|
105
|
+
*
|
|
106
|
+
* - `notification/announcement` - Shown with `Announcement` level in the Notification Center
|
|
107
|
+
* - `notification/info` - Shown with `Info` level in the Notification Center
|
|
108
|
+
* - `notification/warning` - Shown with `Warning` level in the Notification Center
|
|
109
|
+
* - `homepage/banner` - Shown on the home page as a banner beneath the main banner
|
|
110
|
+
* - `homepage/rhs` - Shown on the home page as a panel beneath the right-hand side links panel
|
|
111
|
+
*
|
|
112
|
+
*/
|
|
113
|
+
export type Announcement = {
|
|
114
|
+
id: string; // Unique id for this announcement
|
|
115
|
+
title: string; // Title to be shown
|
|
116
|
+
message: string; // Message/Body for the announcement
|
|
117
|
+
target: string; // Where the announcement should be shown
|
|
118
|
+
version?: string; // Version or semver expression for when to show this announcement
|
|
119
|
+
audience?: 'admin' | 'all'; // Audience - show for just Admins or for all users
|
|
120
|
+
icon?: string;
|
|
121
|
+
cta?: {
|
|
122
|
+
primary: CallToAction, // Must have a primary call to action, if we have a cta field
|
|
123
|
+
secondary?: CallToAction,
|
|
124
|
+
},
|
|
125
|
+
style?: string; // Styling information that will be interpreted by the rendering component
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Icon information
|
|
130
|
+
*/
|
|
131
|
+
export type AnnouncementNotificationIconData = {
|
|
132
|
+
light: string; // Light mode icon/image
|
|
133
|
+
dark?: string; // Light mode icon/image
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Custom data for announcements stored with the notification
|
|
138
|
+
*/
|
|
139
|
+
export type AnnouncementNotificationData = {
|
|
140
|
+
icon?: AnnouncementNotificationIconData; // Icon/Image to show
|
|
141
|
+
location: string; // Location of the announcement in the UI
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Main type for the metadata that is retrieved from the dynamic content endpoint
|
|
146
|
+
*/
|
|
147
|
+
export type DynamicContent = {
|
|
148
|
+
version: string;
|
|
149
|
+
releases: ReleaseInfo[],
|
|
150
|
+
support: SupportInfo,
|
|
151
|
+
announcements: Announcement[],
|
|
152
|
+
settings?: Partial<SettingsInfo>,
|
|
153
|
+
};
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* The code in this file provides utility functions for dynamic content
|
|
4
|
+
*
|
|
5
|
+
* First up is a helper to remove notifications that match a given prefix
|
|
6
|
+
* Second up is a basic logging helper than will log to the console but can also log to local storage
|
|
7
|
+
* so that we have a persistent log of what the dynamic content code has been doing to help with debugging
|
|
8
|
+
*
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { randomStr } from '@shell/utils/string';
|
|
12
|
+
import { Configuration, Context } from './types';
|
|
13
|
+
import { Notification } from '@shell/types/notifications';
|
|
14
|
+
|
|
15
|
+
const MAX_LOG_MESSAGES = 50;
|
|
16
|
+
|
|
17
|
+
export const LOCAL_STORAGE_CONTENT_DEBUG_LOG = 'rancher-updates-debug-log';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Remove all notifications that have the given prefix, except the one that has the specified current id
|
|
21
|
+
*
|
|
22
|
+
* Returns whether a notification with the current id was found
|
|
23
|
+
*
|
|
24
|
+
* @param dispatch Store dispatcher
|
|
25
|
+
* @param getters Store getters
|
|
26
|
+
* @param prefix Prefix to look for and remove notifications whose IDs start with this prefix
|
|
27
|
+
* @param currentId Current ID of notification to keep, if present
|
|
28
|
+
* @returns If a notification with the current ID was found
|
|
29
|
+
*/
|
|
30
|
+
export async function removeMatchingNotifications(context: Context, prefix: string, currentId: string): Promise<boolean> {
|
|
31
|
+
const { dispatch, getters, logger } = context;
|
|
32
|
+
const id = `${ prefix }${ currentId }`;
|
|
33
|
+
let found = false;
|
|
34
|
+
let removed = 0;
|
|
35
|
+
const all = getters['notifications/all'] || [];
|
|
36
|
+
const ids = all.map((n: Notification) => n.id);
|
|
37
|
+
|
|
38
|
+
for (let i = 0; i < ids.length; i++) {
|
|
39
|
+
const notificationId = ids[i];
|
|
40
|
+
|
|
41
|
+
if (notificationId.startsWith(prefix)) {
|
|
42
|
+
if (notificationId === id) {
|
|
43
|
+
found = true;
|
|
44
|
+
} else {
|
|
45
|
+
await dispatch('notifications/remove', notificationId);
|
|
46
|
+
removed++;
|
|
47
|
+
|
|
48
|
+
logger.debug(`Remove matching notifications ${ prefix }, ${ currentId } - removed notification ${ notificationId }`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (found) {
|
|
54
|
+
logger.debug(`Remove matching notifications ${ prefix }, ${ currentId } - found an existing notification (removed ${ removed })`);
|
|
55
|
+
} else {
|
|
56
|
+
logger.debug(`Remove matching notifications ${ prefix }, ${ currentId } - did not find an existing notification (removed ${ removed })`);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return found;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Create a logger interface that can be used to log messages and errors appropriately
|
|
64
|
+
* @param config Configuration to help us determine where and when to log
|
|
65
|
+
* @returns Logger interface with methods to log for error, info and debug
|
|
66
|
+
*/
|
|
67
|
+
export function createLogger(config: Configuration) {
|
|
68
|
+
return {
|
|
69
|
+
error: (message: string, ...args: any[]) => log(config, 'error', message, ...args),
|
|
70
|
+
info: (message: string, ...args: any[]) => log(config, 'info', message, ...args),
|
|
71
|
+
debug: (message: string, ...args: any[]) => log(config, 'debug', message, ...args),
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Actual logging function that logs appropriately
|
|
77
|
+
* @param config Configuration to help us determine where and when to log
|
|
78
|
+
* @param level Log level (error, info, debug)
|
|
79
|
+
* @param message Log message
|
|
80
|
+
* @param arg Optional argument to be logged
|
|
81
|
+
*/
|
|
82
|
+
function log(config: Configuration, level: string, message: string, ...args: any[]) {
|
|
83
|
+
// Log to the console when appropriate
|
|
84
|
+
if (level === 'error') {
|
|
85
|
+
console.error(message, ...args); // eslint-disable-line no-console
|
|
86
|
+
} else if (level === 'info' && config.log) {
|
|
87
|
+
console.info(message, ...args); // eslint-disable-line no-console
|
|
88
|
+
} else if (level === 'debug' && config.debug) {
|
|
89
|
+
console.debug(message, ...args); // eslint-disable-line no-console
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Only log to local storage if the configuration says we should
|
|
93
|
+
if (config.log) {
|
|
94
|
+
// Add the log message to the log we keep in local storage
|
|
95
|
+
try {
|
|
96
|
+
let data = [];
|
|
97
|
+
|
|
98
|
+
// If we can't parse the data in local storage, then we will reset to an emptry array
|
|
99
|
+
try {
|
|
100
|
+
data = JSON.parse(window.localStorage.getItem(LOCAL_STORAGE_CONTENT_DEBUG_LOG) || '[]');
|
|
101
|
+
} catch {}
|
|
102
|
+
|
|
103
|
+
const item = {
|
|
104
|
+
level,
|
|
105
|
+
message,
|
|
106
|
+
timestamp: new Date().toISOString(),
|
|
107
|
+
args,
|
|
108
|
+
uuid: randomStr(),
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
data.unshift(item);
|
|
112
|
+
|
|
113
|
+
// Limit the number of log messages
|
|
114
|
+
window.localStorage.setItem(LOCAL_STORAGE_CONTENT_DEBUG_LOG, JSON.stringify(data.slice(0, MAX_LOG_MESSAGES)));
|
|
115
|
+
|
|
116
|
+
// Send an event so the UI can update if necessary
|
|
117
|
+
const event = new CustomEvent('dynamicContentLog', { detail: item });
|
|
118
|
+
|
|
119
|
+
window.dispatchEvent(event);
|
|
120
|
+
} catch {}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
@@ -81,7 +81,7 @@ export function importWindowComponent(name) {
|
|
|
81
81
|
throw new Error('Name required');
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
return defineAsyncComponent(() => import(/* webpackChunkName: "components
|
|
84
|
+
return defineAsyncComponent(() => import(/* webpackChunkName: "components" */ `@shell/components/Window/${name}`));
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
export function loadProduct(name) {
|
|
@@ -130,7 +130,7 @@ export function resolveDetail(key) {
|
|
|
130
130
|
}
|
|
131
131
|
|
|
132
132
|
export function resolveWindowComponent(key) {
|
|
133
|
-
return require.resolve(`@shell/components/
|
|
133
|
+
return require.resolve(`@shell/components/Window/${ key }`);
|
|
134
134
|
}
|
|
135
135
|
|
|
136
136
|
export function resolveMachineConfigComponent(key) {
|
package/utils/favicon.js
CHANGED
|
@@ -9,17 +9,17 @@ export function haveSetFavIcon() {
|
|
|
9
9
|
|
|
10
10
|
export function setFavIcon(store) {
|
|
11
11
|
const res = store.getters['management/byId'](MANAGEMENT.SETTING, SETTING.FAVICON);
|
|
12
|
-
const brandSetting = store.getters['management/
|
|
12
|
+
const brandSetting = store.getters['management/brand'];
|
|
13
13
|
const link = findIconLink(document.head.getElementsByTagName('link'));
|
|
14
14
|
|
|
15
15
|
if (link) {
|
|
16
16
|
let brandImage;
|
|
17
17
|
|
|
18
|
-
if (brandSetting
|
|
18
|
+
if (brandSetting === 'suse') {
|
|
19
19
|
brandImage = require('~shell/assets/brand/suse/favicon.png');
|
|
20
|
-
} else if (brandSetting
|
|
20
|
+
} else if (brandSetting === 'csp') {
|
|
21
21
|
brandImage = require('~shell/assets/brand/csp/favicon.png');
|
|
22
|
-
} else if (brandSetting
|
|
22
|
+
} else if (brandSetting === 'harvester') {
|
|
23
23
|
brandImage = require('~shell/assets/brand/harvester/favicon.png');
|
|
24
24
|
}
|
|
25
25
|
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { EXT } from '@shell/config/types';
|
|
2
|
+
import { RancherKubeMetadata } from '@shell/types/kube/kube-api';
|
|
3
|
+
|
|
4
|
+
interface UserActivityResponse {
|
|
5
|
+
metadata: RancherKubeMetadata,
|
|
6
|
+
status: {
|
|
7
|
+
expiresAt: string
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface ParsedInactivitySetting {
|
|
12
|
+
expiresAt: string | undefined,
|
|
13
|
+
sessionTokenName: string | undefined,
|
|
14
|
+
courtesyTimer: number | undefined,
|
|
15
|
+
courtesyCountdown: number | undefined,
|
|
16
|
+
showModalAfter: number | undefined
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface SpecData {
|
|
20
|
+
tokenId: string;
|
|
21
|
+
seenAt?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export class Inactivity {
|
|
25
|
+
private sessionTokenName: string | undefined = undefined;
|
|
26
|
+
|
|
27
|
+
public getSessionTokenName(): string | undefined {
|
|
28
|
+
return this.sessionTokenName;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
public setSessionTokenName(tokenName: string): void {
|
|
32
|
+
this.sessionTokenName = tokenName;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
public async getUserActivity(store: any, sessionTokenName: string, force = true): Promise<UserActivityResponse> {
|
|
36
|
+
try {
|
|
37
|
+
const updatedData = await store.dispatch('management/find', {
|
|
38
|
+
type: EXT.USER_ACTIVITY,
|
|
39
|
+
id: sessionTokenName,
|
|
40
|
+
opt: {
|
|
41
|
+
force, watch: false, logoutOnError: false
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
return updatedData;
|
|
46
|
+
} catch (e: any) {
|
|
47
|
+
if (e._status === 401) {
|
|
48
|
+
return store.dispatch('auth/logout', { sessionIdle: true });
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
console.error(`Could not GET UserActivity for session token ${ sessionTokenName }`, e); // eslint-disable-line no-console
|
|
52
|
+
throw new Error(e);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
public async updateUserActivity(userActivityResource: any, sessionTokenName: string, seenAt: string): Promise<UserActivityResponse> {
|
|
57
|
+
const spec: SpecData = { tokenId: sessionTokenName };
|
|
58
|
+
|
|
59
|
+
if (seenAt) {
|
|
60
|
+
spec.seenAt = seenAt;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
userActivityResource.spec = spec;
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
const savedData = await userActivityResource.save({ force: true });
|
|
67
|
+
|
|
68
|
+
return savedData;
|
|
69
|
+
} catch (e: any) {
|
|
70
|
+
console.error(`Could not update (POST) UserActivity for session token ${ sessionTokenName }`, e); // eslint-disable-line no-console
|
|
71
|
+
throw new Error(e);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
public parseTTLData(userActivityData: UserActivityResponse): ParsedInactivitySetting {
|
|
76
|
+
const currDate = Date.now();
|
|
77
|
+
const endDate = new Date(userActivityData.status?.expiresAt).getTime();
|
|
78
|
+
|
|
79
|
+
// let's give this a 3 second buffer so that we can make sure the logout happens by the frontend
|
|
80
|
+
const thresholdSeconds = Math.floor((endDate - currDate) / 1000) - 3;
|
|
81
|
+
|
|
82
|
+
// Amount of time the user sees the inactivity warning
|
|
83
|
+
const courtesyTimerVal = Math.floor(thresholdSeconds * 0.2); // the modal is shown for 10% of the total time to display
|
|
84
|
+
const courtesyTimer = Math.min(courtesyTimerVal, 60 * 5); // Never show the modal more than 5 minutes
|
|
85
|
+
|
|
86
|
+
const courtesyCountdown = courtesyTimer;
|
|
87
|
+
|
|
88
|
+
// Amount of time before the user sees the inactivity warning
|
|
89
|
+
// Note - time before warning is shown + time warning is shown = settings threshold (total amount of time)
|
|
90
|
+
const showModalAfter = thresholdSeconds - courtesyTimer;
|
|
91
|
+
|
|
92
|
+
return {
|
|
93
|
+
expiresAt: userActivityData.status?.expiresAt,
|
|
94
|
+
sessionTokenName: userActivityData.metadata?.name,
|
|
95
|
+
courtesyTimer,
|
|
96
|
+
courtesyCountdown,
|
|
97
|
+
showModalAfter
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const instance = new Inactivity();
|
|
103
|
+
|
|
104
|
+
export default instance;
|
package/utils/object.js
CHANGED
|
@@ -205,7 +205,7 @@ export function definedKeys(obj) {
|
|
|
205
205
|
return compact(flattenDeep(keys));
|
|
206
206
|
}
|
|
207
207
|
|
|
208
|
-
export function diff(from, to) {
|
|
208
|
+
export function diff(from, to, preventNull = false) {
|
|
209
209
|
from = from || {};
|
|
210
210
|
to = to || {};
|
|
211
211
|
|
|
@@ -234,7 +234,25 @@ export function diff(from, to) {
|
|
|
234
234
|
const missing = difference(fromKeys, toKeys);
|
|
235
235
|
|
|
236
236
|
for ( const k of missing ) {
|
|
237
|
-
|
|
237
|
+
// we need to gate this in order to prevent unforseen problems with addonConfig
|
|
238
|
+
if (preventNull) {
|
|
239
|
+
// keys that come from "definedKeys" method are strings with "" chars inside... We need to clean them up
|
|
240
|
+
// so that we can access the value of the obj property
|
|
241
|
+
let key = k;
|
|
242
|
+
|
|
243
|
+
if (!k.includes('.')) {
|
|
244
|
+
key = k.replaceAll('"', '');
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// // if value exists in "from" but is missing in "to", let's add it, otherwise we just set it null as we did before
|
|
248
|
+
if (from[key] !== undefined && from[key] !== null) {
|
|
249
|
+
set(out, key, from[key]);
|
|
250
|
+
} else {
|
|
251
|
+
set(out, key, null);
|
|
252
|
+
}
|
|
253
|
+
} else {
|
|
254
|
+
set(out, k, null);
|
|
255
|
+
}
|
|
238
256
|
}
|
|
239
257
|
|
|
240
258
|
return out;
|