@rancher/shell 3.0.7 → 3.0.8-rc.1

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.
Files changed (83) hide show
  1. package/assets/images/vendor/githubapp.svg +13 -0
  2. package/assets/styles/base/_typography.scss +1 -1
  3. package/assets/styles/themes/_modern.scss +5 -5
  4. package/assets/translations/en-us.yaml +91 -11
  5. package/assets/translations/zh-hans.yaml +0 -4
  6. package/components/Inactivity.vue +222 -106
  7. package/components/InstallHelmCharts.vue +2 -2
  8. package/components/ResourceDetail/index.vue +1 -1
  9. package/components/SortableTable/index.vue +17 -2
  10. package/components/fleet/FleetConfigMapSelector.vue +117 -0
  11. package/components/fleet/FleetSecretSelector.vue +127 -0
  12. package/components/fleet/__tests__/FleetConfigMapSelector.test.ts +125 -0
  13. package/components/fleet/__tests__/FleetSecretSelector.test.ts +82 -0
  14. package/components/form/FileImageSelector.vue +13 -4
  15. package/components/form/FileSelector.vue +11 -2
  16. package/components/form/ResourceLabeledSelect.vue +1 -0
  17. package/components/form/__tests__/ResourceLabeledSelect.test.ts +90 -0
  18. package/components/nav/Header.vue +1 -0
  19. package/config/product/auth.js +1 -0
  20. package/config/query-params.js +1 -0
  21. package/config/settings.ts +8 -1
  22. package/config/types.js +2 -0
  23. package/dialog/AddonConfigConfirmationDialog.vue +45 -1
  24. package/edit/__tests__/fleet.cattle.io.helmop.test.ts +52 -11
  25. package/edit/auth/AuthProviderWarningBanners.vue +14 -1
  26. package/edit/auth/github-app-steps.vue +97 -0
  27. package/edit/auth/github-steps.vue +75 -0
  28. package/edit/auth/github.vue +94 -65
  29. package/edit/fleet.cattle.io.helmop.vue +51 -2
  30. package/edit/networking.k8s.io.networkpolicy/PolicyRuleTarget.vue +15 -5
  31. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +11 -9
  32. package/edit/provisioning.cattle.io.cluster/rke2.vue +56 -9
  33. package/edit/provisioning.cattle.io.cluster/tabs/AddOnConfig.vue +28 -2
  34. package/list/projectsecret.vue +1 -1
  35. package/machine-config/azure.vue +1 -1
  36. package/mixins/chart.js +1 -1
  37. package/models/__tests__/chart.test.ts +17 -9
  38. package/models/__tests__/compliance.cattle.io.clusterscanprofile.spec.js +30 -0
  39. package/models/catalog.cattle.io.app.js +1 -1
  40. package/models/chart.js +3 -1
  41. package/models/compliance.cattle.io.clusterscanprofile.js +1 -1
  42. package/models/management.cattle.io.authconfig.js +1 -0
  43. package/package.json +2 -2
  44. package/pages/auth/login.vue +5 -2
  45. package/pages/auth/verify.vue +1 -1
  46. package/pages/c/_cluster/apps/charts/AppChartCardSubHeader.vue +3 -2
  47. package/pages/c/_cluster/apps/charts/chart.vue +2 -2
  48. package/pages/c/_cluster/explorer/EventsTable.vue +89 -3
  49. package/pages/c/_cluster/explorer/tools/index.vue +3 -3
  50. package/pages/c/_cluster/settings/performance.vue +12 -25
  51. package/pages/home.vue +313 -12
  52. package/plugins/axios.js +2 -1
  53. package/plugins/dashboard-store/actions.js +1 -1
  54. package/plugins/dashboard-store/resource-class.js +17 -2
  55. package/plugins/steve/steve-pagination-utils.ts +2 -2
  56. package/scripts/extension/publish +1 -1
  57. package/store/auth.js +8 -3
  58. package/store/aws.js +8 -6
  59. package/store/features.js +1 -0
  60. package/store/index.js +9 -3
  61. package/store/prefs.js +6 -0
  62. package/types/kube/kube-api.ts +2 -1
  63. package/types/rancher/index.d.ts +1 -0
  64. package/types/resources/settings.d.ts +29 -7
  65. package/types/shell/index.d.ts +59 -0
  66. package/utils/__tests__/cluster.test.ts +379 -1
  67. package/utils/cluster.js +157 -3
  68. package/utils/dynamic-content/__tests__/config.test.ts +187 -0
  69. package/utils/dynamic-content/__tests__/index.test.ts +390 -0
  70. package/utils/dynamic-content/__tests__/info.test.ts +263 -0
  71. package/utils/dynamic-content/__tests__/new-release.test.ts +216 -0
  72. package/utils/dynamic-content/__tests__/support-notice.test.ts +262 -0
  73. package/utils/dynamic-content/__tests__/util.test.ts +235 -0
  74. package/utils/dynamic-content/config.ts +55 -0
  75. package/utils/dynamic-content/index.ts +273 -0
  76. package/utils/dynamic-content/info.ts +219 -0
  77. package/utils/dynamic-content/new-release.ts +126 -0
  78. package/utils/dynamic-content/support-notice.ts +169 -0
  79. package/utils/dynamic-content/types.d.ts +101 -0
  80. package/utils/dynamic-content/util.ts +122 -0
  81. package/utils/inactivity.ts +104 -0
  82. package/utils/pagination-utils.ts +19 -4
  83. package/utils/release-notes.ts +1 -1
@@ -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
+ }
@@ -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;
@@ -1,4 +1,6 @@
1
- import { PaginationFeature, PaginationSettings, PaginationSettingsStore, PaginationSettingsStores } from '@shell/types/resources/settings';
1
+ import {
2
+ PaginationFeature, PaginationFeatureHomePageClusterConfig, PaginationFeatureName, PaginationSettings, PaginationSettingsFeatures, PaginationSettingsStore, PaginationSettingsStores
3
+ } from '@shell/types/resources/settings';
2
4
  import {
3
5
  NAMESPACE_FILTER_ALL_USER as ALL_USER,
4
6
  NAMESPACE_FILTER_ALL as ALL,
@@ -22,6 +24,15 @@ import { EXT_IDS } from '@shell/core/plugin';
22
24
  import { ExtensionManager } from '@shell/types/extension-manager';
23
25
  import { DEFAULT_PERF_SETTING } from '@shell/config/settings';
24
26
 
27
+ const homePageClusterFeature: PaginationFeature<PaginationFeatureHomePageClusterConfig> = {
28
+ version: 1,
29
+ enabled: true,
30
+ configuration: {
31
+ threshold: 500, results: 250, pagesPerRow: 25
32
+ }
33
+ };
34
+ const PAGINATION_SETTINGS_FEATURE_DEFAULTS: PaginationSettingsFeatures = { homePageCluster: homePageClusterFeature };
35
+
25
36
  /**
26
37
  * Helper functions for server side pagination
27
38
  */
@@ -222,15 +233,19 @@ class PaginationUtils {
222
233
  return this.isFeatureEnabled({ rootGetters }, 'listManualRefresh');
223
234
  }
224
235
 
225
- private isFeatureEnabled({ rootGetters }: any, featureName: PaginationFeature): boolean {
236
+ getFeature<Config = any>({ rootGetters }: any, featureName: PaginationFeatureName): PaginationFeature<Config> | undefined {
226
237
  // Cache must be enabled to support pagination api
227
238
  if (!this.isSteveCacheEnabled({ rootGetters })) {
228
- return false;
239
+ return undefined;
229
240
  }
230
241
 
231
242
  const settings = this.getSettings({ rootGetters });
232
243
 
233
- return !!settings.features?.[featureName]?.enabled;
244
+ return settings.features?.[featureName] || PAGINATION_SETTINGS_FEATURE_DEFAULTS[featureName];
245
+ }
246
+
247
+ private isFeatureEnabled({ rootGetters }: any, featureName: PaginationFeatureName): boolean {
248
+ return !!this.getFeature({ rootGetters }, featureName)?.enabled;
234
249
  }
235
250
 
236
251
  resourceChangesDebounceMs({ rootGetters }: any): number | undefined {
@@ -20,7 +20,7 @@ export async function addReleaseNotesNotification(dispatch: any, getters: any) {
20
20
  if (notification.id === id) {
21
21
  found = true;
22
22
  } else {
23
- await dispatch('notifications/delete', notification.id);
23
+ await dispatch('notifications/remove', notification.id);
24
24
  }
25
25
  }
26
26
  }