@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.
- package/assets/images/vendor/githubapp.svg +13 -0
- package/assets/styles/base/_typography.scss +1 -1
- package/assets/styles/themes/_modern.scss +5 -5
- package/assets/translations/en-us.yaml +91 -11
- package/assets/translations/zh-hans.yaml +0 -4
- package/components/Inactivity.vue +222 -106
- package/components/InstallHelmCharts.vue +2 -2
- package/components/ResourceDetail/index.vue +1 -1
- package/components/SortableTable/index.vue +17 -2
- 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/nav/Header.vue +1 -0
- package/config/product/auth.js +1 -0
- package/config/query-params.js +1 -0
- package/config/settings.ts +8 -1
- package/config/types.js +2 -0
- package/dialog/AddonConfigConfirmationDialog.vue +45 -1
- package/edit/__tests__/fleet.cattle.io.helmop.test.ts +52 -11
- package/edit/auth/AuthProviderWarningBanners.vue +14 -1
- package/edit/auth/github-app-steps.vue +97 -0
- package/edit/auth/github-steps.vue +75 -0
- package/edit/auth/github.vue +94 -65
- package/edit/fleet.cattle.io.helmop.vue +51 -2
- package/edit/networking.k8s.io.networkpolicy/PolicyRuleTarget.vue +15 -5
- package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +11 -9
- package/edit/provisioning.cattle.io.cluster/rke2.vue +56 -9
- package/edit/provisioning.cattle.io.cluster/tabs/AddOnConfig.vue +28 -2
- package/list/projectsecret.vue +1 -1
- package/machine-config/azure.vue +1 -1
- package/mixins/chart.js +1 -1
- package/models/__tests__/chart.test.ts +17 -9
- package/models/__tests__/compliance.cattle.io.clusterscanprofile.spec.js +30 -0
- package/models/catalog.cattle.io.app.js +1 -1
- package/models/chart.js +3 -1
- package/models/compliance.cattle.io.clusterscanprofile.js +1 -1
- package/models/management.cattle.io.authconfig.js +1 -0
- package/package.json +2 -2
- package/pages/auth/login.vue +5 -2
- package/pages/auth/verify.vue +1 -1
- package/pages/c/_cluster/apps/charts/AppChartCardSubHeader.vue +3 -2
- package/pages/c/_cluster/apps/charts/chart.vue +2 -2
- package/pages/c/_cluster/explorer/EventsTable.vue +89 -3
- package/pages/c/_cluster/explorer/tools/index.vue +3 -3
- package/pages/c/_cluster/settings/performance.vue +12 -25
- package/pages/home.vue +313 -12
- package/plugins/axios.js +2 -1
- package/plugins/dashboard-store/actions.js +1 -1
- package/plugins/dashboard-store/resource-class.js +17 -2
- package/plugins/steve/steve-pagination-utils.ts +2 -2
- package/scripts/extension/publish +1 -1
- package/store/auth.js +8 -3
- package/store/aws.js +8 -6
- package/store/features.js +1 -0
- package/store/index.js +9 -3
- package/store/prefs.js +6 -0
- package/types/kube/kube-api.ts +2 -1
- package/types/rancher/index.d.ts +1 -0
- package/types/resources/settings.d.ts +29 -7
- package/types/shell/index.d.ts +59 -0
- package/utils/__tests__/cluster.test.ts +379 -1
- package/utils/cluster.js +157 -3
- 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 +263 -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/config.ts +55 -0
- package/utils/dynamic-content/index.ts +273 -0
- package/utils/dynamic-content/info.ts +219 -0
- package/utils/dynamic-content/new-release.ts +126 -0
- package/utils/dynamic-content/support-notice.ts +169 -0
- package/utils/dynamic-content/types.d.ts +101 -0
- package/utils/dynamic-content/util.ts +122 -0
- package/utils/inactivity.ts +104 -0
- package/utils/pagination-utils.ts +19 -4
- 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 {
|
|
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
|
-
|
|
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
|
|
239
|
+
return undefined;
|
|
229
240
|
}
|
|
230
241
|
|
|
231
242
|
const settings = this.getSettings({ rootGetters });
|
|
232
243
|
|
|
233
|
-
return
|
|
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 {
|
package/utils/release-notes.ts
CHANGED
|
@@ -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/
|
|
23
|
+
await dispatch('notifications/remove', notification.id);
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
26
|
}
|