@rancher/shell 3.0.9 → 3.0.11
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/styles/base/_color.scss +4 -0
- package/assets/styles/themes/_light.scss +6 -6
- package/assets/styles/themes/_modern.scss +14 -6
- package/assets/translations/en-us.yaml +9 -10
- package/chart/__tests__/rancher-backup-index.test.ts +248 -0
- package/chart/rancher-backup/index.vue +41 -2
- package/components/BrandImage.vue +6 -5
- package/components/ConsumptionGauge.vue +12 -4
- package/components/CopyToClipboard.vue +28 -0
- package/components/CopyToClipboardText.vue +4 -0
- package/components/CruResource.vue +1 -0
- package/components/DynamicContent/DynamicContentIcon.vue +3 -2
- package/components/ExplorerProjectsNamespaces.vue +1 -4
- package/components/GlobalRoleBindings.vue +1 -5
- package/components/LazyImage.vue +2 -1
- package/components/Resource/Detail/Card/Scaler.vue +4 -4
- package/components/ResourceDetail/index.vue +0 -21
- package/components/Tabbed/index.vue +6 -0
- package/components/__tests__/ConsumptionGauge.test.ts +31 -0
- package/components/__tests__/CruResource.test.ts +35 -1
- package/components/form/ProjectMemberEditor.vue +0 -10
- package/components/nav/TopLevelMenu.helper.ts +7 -79
- package/components/nav/__tests__/TopLevelMenu.helper.test.ts +2 -53
- package/composables/useIsNewDetailPageEnabled.test.ts +98 -0
- package/composables/useIsNewDetailPageEnabled.ts +12 -0
- package/config/private-label.js +2 -1
- package/config/product/apps.js +1 -0
- package/config/product/explorer.js +11 -1
- package/config/table-headers.js +0 -9
- package/config/types.js +0 -1
- package/core/__tests__/extension-manager-impl.test.js +187 -2
- package/core/extension-manager-impl.js +4 -2
- package/core/plugin-helpers.ts +31 -0
- package/detail/__tests__/node.test.ts +83 -0
- package/detail/management.cattle.io.oidcclient.vue +2 -1
- package/detail/node.vue +1 -0
- package/edit/auth/github-app-steps.vue +2 -0
- package/edit/auth/github-steps.vue +2 -0
- package/edit/catalog.cattle.io.clusterrepo.vue +17 -3
- package/edit/cloudcredential.vue +2 -1
- package/edit/management.cattle.io.user.vue +60 -35
- package/edit/monitoring.coreos.com.alertmanagerconfig/receiverConfig.vue +11 -6
- package/edit/provisioning.cattle.io.cluster/index.vue +5 -4
- package/edit/provisioning.cattle.io.cluster/shared.ts +4 -2
- package/edit/secret/generic.vue +1 -0
- package/edit/secret/index.vue +2 -1
- package/edit/service.vue +2 -14
- package/edit/token.vue +29 -68
- package/list/management.cattle.io.feature.vue +7 -1
- package/list/provisioning.cattle.io.cluster.vue +0 -49
- package/mixins/brand.js +2 -1
- package/models/catalog.cattle.io.clusterrepo.js +9 -0
- package/models/cluster.x-k8s.io.machinedeployment.js +8 -3
- package/models/management.cattle.io.authconfig.js +2 -1
- package/models/management.cattle.io.cluster.js +4 -3
- package/models/monitoring.coreos.com.receiver.js +11 -6
- package/models/provisioning.cattle.io.cluster.js +2 -2
- package/models/token.js +0 -4
- package/package.json +12 -12
- package/pages/account/index.vue +67 -96
- package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +66 -9
- package/pages/c/_cluster/apps/charts/index.vue +3 -8
- package/pages/c/_cluster/apps/charts/install.vue +8 -9
- package/pages/c/_cluster/explorer/index.vue +2 -19
- package/pages/c/_cluster/istio/index.vue +4 -2
- package/pages/c/_cluster/longhorn/index.vue +2 -1
- package/pages/c/_cluster/monitoring/index.vue +2 -2
- package/pages/c/_cluster/neuvector/index.vue +2 -1
- package/pages/c/_cluster/settings/performance.vue +0 -5
- package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +2 -1
- package/pages/c/_cluster/uiplugins/index.vue +2 -1
- package/pkg/auto-import.js +41 -0
- package/plugins/dashboard-store/resource-class.js +2 -2
- package/plugins/steve/__tests__/steve-class.test.ts +1 -1
- package/plugins/steve/steve-class.js +3 -3
- package/plugins/steve/steve-pagination-utils.ts +2 -5
- package/plugins/steve/subscribe.js +29 -4
- package/rancher-components/Pill/RcCounterBadge/RcCounterBadge.vue +7 -7
- package/rancher-components/Pill/RcStatusBadge/RcStatusBadge.vue +5 -2
- package/rancher-components/RcButton/RcButton.vue +3 -3
- package/rancher-components/RcButtonSplit/RcButtonSplit.test.ts +253 -0
- package/rancher-components/RcButtonSplit/RcButtonSplit.vue +158 -0
- package/rancher-components/RcButtonSplit/index.ts +1 -0
- package/rancher-components/RcIcon/types.ts +2 -2
- package/rancher-components/RcSection/RcSection.test.ts +323 -0
- package/rancher-components/RcSection/RcSection.vue +252 -0
- package/rancher-components/RcSection/RcSectionActions.test.ts +212 -0
- package/rancher-components/RcSection/RcSectionActions.vue +85 -0
- package/rancher-components/RcSection/RcSectionBadges.test.ts +149 -0
- package/rancher-components/RcSection/RcSectionBadges.vue +29 -0
- package/rancher-components/RcSection/index.ts +12 -0
- package/rancher-components/RcSection/types.ts +86 -0
- package/scripts/test-plugins-build.sh +9 -8
- package/types/shell/index.d.ts +93 -108
- package/utils/__tests__/require-asset.test.ts +98 -0
- package/utils/async.ts +1 -5
- package/utils/brand.ts +3 -1
- package/utils/favicon.js +4 -3
- package/utils/require-asset.ts +95 -0
- package/utils/style.ts +17 -0
- package/utils/units.js +14 -5
- package/vue.config.js +4 -3
- package/components/HarvesterServiceAddOnConfig.vue +0 -207
- package/models/ext.cattle.io.token.js +0 -48
|
@@ -3,6 +3,11 @@ import { isEmpty } from '@shell/utils/object';
|
|
|
3
3
|
import { MONITORING } from '@shell/config/types';
|
|
4
4
|
import jsyaml from 'js-yaml';
|
|
5
5
|
import SteveModel from '@shell/plugins/steve/steve-class';
|
|
6
|
+
import slackLogo from '~shell/assets/images/vendor/slack.svg';
|
|
7
|
+
import emailLogo from '~shell/assets/images/vendor/email.svg';
|
|
8
|
+
import pagerdutyLogo from '~shell/assets/images/vendor/pagerduty.svg';
|
|
9
|
+
import webhookLogo from '~shell/assets/images/vendor/webhook.svg';
|
|
10
|
+
import customLogo from '~shell/assets/images/vendor/custom.svg';
|
|
6
11
|
|
|
7
12
|
// i18n-uses monitoringReceiver.slack.*, monitoringReceiver.email.*, monitoringReceiver.pagerduty.*
|
|
8
13
|
// i18n-uses monitoringReceiver.opsgenie.*, monitoringReceiver.webhook.*, monitoringReceiver.custom.*
|
|
@@ -13,14 +18,14 @@ export const RECEIVERS_TYPES = [
|
|
|
13
18
|
title: 'monitoringReceiver.slack.title',
|
|
14
19
|
info: 'monitoringReceiver.slack.info',
|
|
15
20
|
key: 'slack_configs',
|
|
16
|
-
logo:
|
|
21
|
+
logo: slackLogo
|
|
17
22
|
},
|
|
18
23
|
{
|
|
19
24
|
name: 'email',
|
|
20
25
|
label: 'monitoringReceiver.email.label',
|
|
21
26
|
title: 'monitoringReceiver.email.title',
|
|
22
27
|
key: 'email_configs',
|
|
23
|
-
logo:
|
|
28
|
+
logo: emailLogo
|
|
24
29
|
},
|
|
25
30
|
{
|
|
26
31
|
name: 'pagerduty',
|
|
@@ -28,21 +33,21 @@ export const RECEIVERS_TYPES = [
|
|
|
28
33
|
title: 'monitoringReceiver.pagerduty.title',
|
|
29
34
|
info: 'monitoringReceiver.pagerduty.info',
|
|
30
35
|
key: 'pagerduty_configs',
|
|
31
|
-
logo:
|
|
36
|
+
logo: pagerdutyLogo
|
|
32
37
|
},
|
|
33
38
|
{
|
|
34
39
|
name: 'opsgenie',
|
|
35
40
|
label: 'monitoringReceiver.opsgenie.label',
|
|
36
41
|
title: 'monitoringReceiver.opsgenie.title',
|
|
37
42
|
key: 'opsgenie_configs',
|
|
38
|
-
logo:
|
|
43
|
+
logo: emailLogo
|
|
39
44
|
},
|
|
40
45
|
{
|
|
41
46
|
name: 'webhook',
|
|
42
47
|
label: 'monitoringReceiver.webhook.label',
|
|
43
48
|
title: 'monitoringReceiver.webhook.title',
|
|
44
49
|
key: 'webhook_configs',
|
|
45
|
-
logo:
|
|
50
|
+
logo: webhookLogo,
|
|
46
51
|
banner: 'webhook.banner',
|
|
47
52
|
addButton: 'webhook.add'
|
|
48
53
|
},
|
|
@@ -52,7 +57,7 @@ export const RECEIVERS_TYPES = [
|
|
|
52
57
|
title: 'monitoringReceiver.custom.title',
|
|
53
58
|
info: 'monitoringReceiver.custom.info',
|
|
54
59
|
key: 'webhook_configs',
|
|
55
|
-
logo:
|
|
60
|
+
logo: customLogo
|
|
56
61
|
},
|
|
57
62
|
];
|
|
58
63
|
|
|
@@ -127,9 +127,9 @@ export default class ProvCluster extends SteveModel {
|
|
|
127
127
|
}
|
|
128
128
|
const ready = this.mgmt?.isReady;
|
|
129
129
|
|
|
130
|
-
const canEditRKE2cluster = this.isRke2 && ready && this.canUpdate
|
|
130
|
+
const canEditRKE2cluster = this.isRke2 && ready && this.canUpdate;
|
|
131
131
|
|
|
132
|
-
const canSnapshot = ready && this.isRke2 && this.canUpdate
|
|
132
|
+
const canSnapshot = ready && this.isRke2 && this.canUpdate;
|
|
133
133
|
|
|
134
134
|
const actions = [
|
|
135
135
|
// Note: Actions are not supported in the Steve API, so we check
|
package/models/token.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rancher/shell",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.11",
|
|
4
4
|
"description": "Rancher Dashboard Shell",
|
|
5
5
|
"repository": "https://github.com/rancher/dashboard",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"private": false,
|
|
9
9
|
"types": "types/shell/index.d.ts",
|
|
10
10
|
"engines": {
|
|
11
|
-
"node": ">=
|
|
11
|
+
"node": ">=24.0.0"
|
|
12
12
|
},
|
|
13
13
|
"files": [
|
|
14
14
|
"**/*"
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"@popperjs/core": "2.11.8",
|
|
42
42
|
"@rancher/icons": "2.0.55",
|
|
43
43
|
"@types/is-url": "1.2.30",
|
|
44
|
-
"@types/node": "
|
|
44
|
+
"@types/node": "25.3.3",
|
|
45
45
|
"@types/semver": "^7.5.8",
|
|
46
46
|
"@typescript-eslint/eslint-plugin": "5.62.0",
|
|
47
47
|
"@typescript-eslint/parser": "5.62.0",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"add": "2.0.6",
|
|
54
54
|
"ansi_up": "5.0.0",
|
|
55
55
|
"axios-retry": "3.1.9",
|
|
56
|
-
"axios": "1.13.
|
|
56
|
+
"axios": "1.13.5",
|
|
57
57
|
"babel-eslint": "10.1.0",
|
|
58
58
|
"babel-plugin-module-resolver": "5.0.2",
|
|
59
59
|
"babel-preset-vue": "2.0.2",
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"color": "5.0.3",
|
|
66
66
|
"cookie-universal": "2.2.2",
|
|
67
67
|
"cookie": "0.7.0",
|
|
68
|
-
"core-js": "3.
|
|
68
|
+
"core-js": "3.48.0",
|
|
69
69
|
"cron-validator": "1.4.0",
|
|
70
70
|
"cronstrue": "3.9.0",
|
|
71
71
|
"cross-env": "7.0.3",
|
|
@@ -109,9 +109,9 @@
|
|
|
109
109
|
"js-yaml": "4.1.1",
|
|
110
110
|
"jsdiff": "1.1.1",
|
|
111
111
|
"jsonpath-plus": "10.3.0",
|
|
112
|
-
"jsrsasign": "11.
|
|
112
|
+
"jsrsasign": "11.1.1",
|
|
113
113
|
"jszip": "3.10.1",
|
|
114
|
-
"lodash": "4.
|
|
114
|
+
"lodash": "4.18.1",
|
|
115
115
|
"marked": "4.0.17",
|
|
116
116
|
"node-polyfill-webpack-plugin": "3.0.0",
|
|
117
117
|
"nodemon": "2.0.22",
|
|
@@ -119,7 +119,7 @@
|
|
|
119
119
|
"papaparse": "5.3.0",
|
|
120
120
|
"portal-vue": "~3.0.0",
|
|
121
121
|
"sass-loader": "12.6.0",
|
|
122
|
-
"sass": "1.
|
|
122
|
+
"sass": "1.97.3",
|
|
123
123
|
"serve-static": "1.14.1",
|
|
124
124
|
"set-cookie-parser": "2.4.6",
|
|
125
125
|
"shell-quote": "1.7.3",
|
|
@@ -131,10 +131,10 @@
|
|
|
131
131
|
"ufo": "0.7.11",
|
|
132
132
|
"unfetch": "4.2.0",
|
|
133
133
|
"url-parse": "1.5.10",
|
|
134
|
-
"vue-router": "4.
|
|
134
|
+
"vue-router": "4.6.4",
|
|
135
135
|
"vue-select": "4.0.0-beta.6",
|
|
136
136
|
"vue-server-renderer": "2.7.16",
|
|
137
|
-
"vue": "3.5.
|
|
137
|
+
"vue": "3.5.29",
|
|
138
138
|
"vue3-resize": "0.2.0",
|
|
139
139
|
"vue3-virtual-scroll-list": "0.2.1",
|
|
140
140
|
"vuedraggable": "4.1.0",
|
|
@@ -148,7 +148,7 @@
|
|
|
148
148
|
"xterm-addon-web-links": "0.9.0",
|
|
149
149
|
"xterm-addon-webgl": "0.16.0",
|
|
150
150
|
"xterm": "5.2.1",
|
|
151
|
-
"yarn": "1.22.
|
|
151
|
+
"yarn": "1.22.22"
|
|
152
152
|
},
|
|
153
153
|
"resolutions": {
|
|
154
154
|
"d3-color": "3.1.0",
|
|
@@ -164,7 +164,7 @@
|
|
|
164
164
|
"roarr": "7.0.4",
|
|
165
165
|
"semver": "7.5.4",
|
|
166
166
|
"@types/lodash": "4.17.5",
|
|
167
|
-
"@types/node": "
|
|
167
|
+
"@types/node": "25.3.3",
|
|
168
168
|
"@vue/cli-service/html-webpack-plugin": "^5.0.0"
|
|
169
169
|
},
|
|
170
170
|
"nyc": {
|
package/pages/account/index.vue
CHANGED
|
@@ -9,107 +9,94 @@ import { mapGetters } from 'vuex';
|
|
|
9
9
|
|
|
10
10
|
import { Banner } from '@components/Banner';
|
|
11
11
|
import ResourceTable from '@shell/components/ResourceTable';
|
|
12
|
+
import CopyToClipboardText from '@shell/components/CopyToClipboardText';
|
|
12
13
|
import TabTitle from '@shell/components/TabTitle';
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
import {
|
|
17
|
-
ACCESS_KEY, DESCRIPTION, EXPIRES, EXPIRY_STATE,
|
|
18
|
-
LAST_USED, AGE_NORMAN, SCOPE_NORMAN, NORMAN_KEY_DEPRECATION
|
|
19
|
-
} from '@shell/config/table-headers';
|
|
20
|
-
import { FilterArgs, PaginationParamFilter } from '@shell/types/store/pagination.types';
|
|
15
|
+
const API_ENDPOINT = '/v3';
|
|
21
16
|
|
|
22
17
|
export default {
|
|
23
18
|
components: {
|
|
24
|
-
BackLink, Banner, Loading, ResourceTable, Principal, TabTitle
|
|
19
|
+
CopyToClipboardText, BackLink, Banner, Loading, ResourceTable, Principal, TabTitle
|
|
25
20
|
},
|
|
26
21
|
mixins: [BackRoute],
|
|
27
22
|
async fetch() {
|
|
28
|
-
const hashedRequests = {};
|
|
29
|
-
|
|
30
23
|
this.canChangePassword = await this.calcCanChangePassword();
|
|
31
24
|
|
|
32
|
-
|
|
33
|
-
|
|
25
|
+
if (this.apiKeySchema) {
|
|
26
|
+
this.rows = await this.$store.dispatch('rancher/findAll', { type: NORMAN.TOKEN });
|
|
27
|
+
}
|
|
34
28
|
|
|
35
|
-
|
|
29
|
+
// Get all settings - the API host setting may not be set, so this avoids a 404 request if we look for the specific setting
|
|
30
|
+
const allSettings = await this.$store.dispatch('management/findAll', { type: MANAGEMENT.SETTING });
|
|
31
|
+
const apiHostSetting = allSettings.find((i) => i.id === SETTING.API_HOST);
|
|
32
|
+
const serverUrlSetting = allSettings.find((i) => i.id === SETTING.SERVER_URL);
|
|
36
33
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
34
|
+
this.apiHostSetting = apiHostSetting?.value;
|
|
35
|
+
this.serverUrlSetting = serverUrlSetting?.value;
|
|
40
36
|
|
|
41
|
-
|
|
42
|
-
this.filterByUserTokens = this.$store.getters[`management/paginationEnabled`](EXT.TOKEN);
|
|
43
|
-
|
|
44
|
-
if (this.filterByUserTokens && selfUser?.status?.userID) {
|
|
45
|
-
// Only get associated with the current user
|
|
46
|
-
const opt = { // Of type ActionFindPageArgs
|
|
47
|
-
pagination: new FilterArgs({
|
|
48
|
-
filters: PaginationParamFilter.createSingleField({
|
|
49
|
-
field: 'metadata.fields.1',
|
|
50
|
-
value: selfUser.status?.userID,
|
|
51
|
-
})
|
|
52
|
-
})
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
hashedRequests.steveTokens = this.$store.dispatch(`management/findPage`, { type: EXT.TOKEN, opt });
|
|
56
|
-
} else {
|
|
57
|
-
hashedRequests.steveTokens = this.$store.dispatch('management/findAll', { type: EXT.TOKEN });
|
|
58
|
-
}
|
|
59
|
-
}
|
|
37
|
+
const selfUser = await this.$store.dispatch('auth/getSelfUser');
|
|
60
38
|
|
|
61
39
|
if (selfUser?.canGetUser && selfUser.status?.userID) {
|
|
62
40
|
// Fetch the user info for ChangePassword (ChangePasswordDialog needs the user info for the user whose password is being changed)
|
|
63
|
-
|
|
41
|
+
this.user = await this.$store.dispatch('management/find', {
|
|
64
42
|
type: MANAGEMENT.USER,
|
|
65
43
|
id: selfUser.status?.userID
|
|
66
44
|
});
|
|
45
|
+
} else {
|
|
46
|
+
throw new Error(this.t('changePassword.errors.cannotFetchSelf'));
|
|
67
47
|
}
|
|
68
|
-
|
|
69
|
-
// Get all settings - the API host setting may not be set, so this avoids a 404 request if we look for the specific setting
|
|
70
|
-
hashedRequests.allSettings = this.$store.dispatch('management/findAll', { type: MANAGEMENT.SETTING });
|
|
71
|
-
|
|
72
|
-
const {
|
|
73
|
-
normanTokens, steveTokens, allSettings, user
|
|
74
|
-
} = await allHash(hashedRequests);
|
|
75
|
-
|
|
76
|
-
this.normanTokens = normanTokens;
|
|
77
|
-
this.steveTokens = steveTokens;
|
|
78
|
-
this.user = user;
|
|
79
|
-
|
|
80
|
-
const apiHostSetting = allSettings.find((i) => i.id === SETTING.API_HOST);
|
|
81
|
-
const serverUrlSetting = allSettings.find((i) => i.id === SETTING.SERVER_URL);
|
|
82
|
-
|
|
83
|
-
this.apiHostSetting = apiHostSetting?.value;
|
|
84
|
-
this.serverUrlSetting = serverUrlSetting?.value;
|
|
85
48
|
},
|
|
86
49
|
data() {
|
|
87
50
|
return {
|
|
88
|
-
normanTokenSchema: undefined,
|
|
89
|
-
steveTokenSchema: undefined,
|
|
90
51
|
apiHostSetting: null,
|
|
91
52
|
serverUrlSetting: null,
|
|
92
53
|
rows: null,
|
|
93
54
|
canChangePassword: false,
|
|
94
|
-
user: null
|
|
95
|
-
normanTokens: null,
|
|
96
|
-
steveTokens: null,
|
|
55
|
+
user: null
|
|
97
56
|
};
|
|
98
57
|
},
|
|
99
58
|
computed: {
|
|
100
59
|
...mapGetters({ t: 'i18n/t' }),
|
|
101
60
|
|
|
102
61
|
apiKeyheaders() {
|
|
103
|
-
return [
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
62
|
+
return this.apiKeySchema ? this.$store.getters['type-map/headersFor'](this.apiKeySchema) : [];
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
// Port of Ember code for API Url - see: https://github.com/rancher/ui/blob/8e07c492673171731f3b26af14c978bc103d1828/lib/shared/addon/endpoint/service.js#L58
|
|
66
|
+
apiUrlBase() {
|
|
67
|
+
let setting = this.apiHostSetting;
|
|
68
|
+
|
|
69
|
+
if (setting && setting.indexOf('http') !== 0) {
|
|
70
|
+
setting = `http://${ setting }`;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Use Server Setting URL if the api host setting is not set
|
|
74
|
+
let url = setting || this.serverUrlSetting;
|
|
75
|
+
|
|
76
|
+
// If the URL is relative, add on the current base URL from the browser
|
|
77
|
+
if ( url.indexOf('http') !== 0 ) {
|
|
78
|
+
url = `${ window.location.origin }/${ url.replace(/^\/+/, '') }`;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// URL must end in a single slash
|
|
82
|
+
url = `${ url.replace(/\/+$/, '') }/`;
|
|
83
|
+
|
|
84
|
+
return url;
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
apiUrl() {
|
|
88
|
+
const base = this.apiUrlBase;
|
|
89
|
+
const path = API_ENDPOINT.replace(/^\/+/, '');
|
|
90
|
+
|
|
91
|
+
return `${ base }${ path }`;
|
|
92
|
+
},
|
|
93
|
+
|
|
94
|
+
apiKeySchema() {
|
|
95
|
+
try {
|
|
96
|
+
return this.$store.getters[`rancher/schemaFor`](NORMAN.TOKEN);
|
|
97
|
+
} catch (e) {}
|
|
98
|
+
|
|
99
|
+
return null;
|
|
113
100
|
},
|
|
114
101
|
|
|
115
102
|
principal() {
|
|
@@ -123,7 +110,7 @@ export default {
|
|
|
123
110
|
return principal || {};
|
|
124
111
|
},
|
|
125
112
|
|
|
126
|
-
|
|
113
|
+
apiKeys() {
|
|
127
114
|
// Filter out tokens that are not API Keys and are not expired UI Sessions
|
|
128
115
|
const isApiKey = (key) => {
|
|
129
116
|
const labels = key.labels;
|
|
@@ -133,24 +120,7 @@ export default {
|
|
|
133
120
|
return ( !expired || !labels || !labels['ui-session'] ) && !current;
|
|
134
121
|
};
|
|
135
122
|
|
|
136
|
-
return !this.
|
|
137
|
-
},
|
|
138
|
-
|
|
139
|
-
filteredNewTokens() {
|
|
140
|
-
// Filter out tokens that are not API Keys and are not expired UI Sessions
|
|
141
|
-
const isApiKey = (key) => {
|
|
142
|
-
const labels = key.metadata?.labels;
|
|
143
|
-
const expired = key.status?.expired;
|
|
144
|
-
const current = key.status?.current;
|
|
145
|
-
|
|
146
|
-
return ( !expired || !labels || !labels['ui-session'] ) && !current;
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
return !this.steveTokens ? [] : this.steveTokens.filter(isApiKey);
|
|
150
|
-
},
|
|
151
|
-
|
|
152
|
-
apiKeys() {
|
|
153
|
-
return (this.filteredNormanTokens || []).concat(this.filteredNewTokens || []);
|
|
123
|
+
return !this.rows ? [] : this.rows.filter(isApiKey);
|
|
154
124
|
}
|
|
155
125
|
},
|
|
156
126
|
|
|
@@ -221,9 +191,16 @@ export default {
|
|
|
221
191
|
<div class="keys-header">
|
|
222
192
|
<div>
|
|
223
193
|
<h2 v-t="'accountAndKeys.apiKeys.title'" />
|
|
194
|
+
<div class="api-url">
|
|
195
|
+
<span>{{ t("accountAndKeys.apiKeys.apiEndpoint") }}</span>
|
|
196
|
+
<CopyToClipboardText
|
|
197
|
+
:aria-label="t('accountAndKeys.apiKeys.copyApiEnpoint')"
|
|
198
|
+
:text="apiUrl"
|
|
199
|
+
/>
|
|
200
|
+
</div>
|
|
224
201
|
</div>
|
|
225
202
|
<button
|
|
226
|
-
v-if="
|
|
203
|
+
v-if="apiKeySchema"
|
|
227
204
|
role="button"
|
|
228
205
|
:aria-label="t('accountAndKeys.apiKeys.add.label')"
|
|
229
206
|
class="btn role-primary add mb-20"
|
|
@@ -234,17 +211,11 @@ export default {
|
|
|
234
211
|
</button>
|
|
235
212
|
</div>
|
|
236
213
|
<div
|
|
237
|
-
v-if="
|
|
214
|
+
v-if="apiKeySchema"
|
|
238
215
|
class="keys"
|
|
239
216
|
>
|
|
240
|
-
<Banner
|
|
241
|
-
v-if="filteredNormanTokens.length"
|
|
242
|
-
color="warning"
|
|
243
|
-
class="mb-20"
|
|
244
|
-
:label="t('accountAndKeys.apiKeys.normanTokenDeprecation')"
|
|
245
|
-
/>
|
|
246
217
|
<ResourceTable
|
|
247
|
-
:schema="
|
|
218
|
+
:schema="apiKeySchema"
|
|
248
219
|
:rows="apiKeys"
|
|
249
220
|
:headers="apiKeyheaders"
|
|
250
221
|
key-field="id"
|
|
@@ -1,27 +1,78 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
import { reactive } from 'vue';
|
|
2
3
|
import { RcItemCardAction } from '@components/RcItemCard';
|
|
3
4
|
import { RcButton } from '@components/RcButton';
|
|
4
|
-
import {
|
|
5
|
+
import { isTruncated } from '@shell/utils/style';
|
|
6
|
+
import RcIcon from '@components/RcIcon/RcIcon.vue';
|
|
7
|
+
import type { RcIconType } from '@components/RcIcon/types';
|
|
5
8
|
|
|
6
9
|
interface FooterItem {
|
|
7
|
-
icon?:
|
|
8
|
-
iconTooltip?:
|
|
10
|
+
icon?: RcIconType;
|
|
11
|
+
iconTooltip?: { key?: string; text?: string };
|
|
9
12
|
labels: string[];
|
|
10
13
|
labelTooltip?: string;
|
|
11
14
|
type?: string;
|
|
12
15
|
}
|
|
13
16
|
|
|
14
|
-
const emit = defineEmits<{(e: 'click:item', type: string, label: string): void
|
|
17
|
+
const emit = defineEmits<{(e: 'click:item', type: string, label: string): void }>();
|
|
15
18
|
|
|
16
19
|
defineProps<{
|
|
17
20
|
items: FooterItem[];
|
|
18
21
|
clickable?: boolean;
|
|
19
22
|
}>();
|
|
20
23
|
|
|
21
|
-
|
|
24
|
+
const tooltipOverrides = reactive<Record<string, string | undefined>>({});
|
|
25
|
+
const labelElements: Record<string, HTMLElement | null> = {};
|
|
26
|
+
|
|
27
|
+
function onClickItem(type: string, label: string): void {
|
|
22
28
|
emit('click:item', type, label);
|
|
23
29
|
}
|
|
24
30
|
|
|
31
|
+
/**
|
|
32
|
+
* Creates a unique key for identifying a label element.
|
|
33
|
+
* @param itemIndex - Index of the footer item
|
|
34
|
+
* @param labelIndex - Index of the label within the footer item
|
|
35
|
+
*/
|
|
36
|
+
function createLabelKey(itemIndex: number, labelIndex: number): string {
|
|
37
|
+
return `${ itemIndex }-${ labelIndex }`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Registers a label element reference for truncation detection.
|
|
42
|
+
* @param key - Unique identifier for the label
|
|
43
|
+
* @param el - The HTML element or null
|
|
44
|
+
*/
|
|
45
|
+
function registerLabelRef(key: string, el: HTMLElement | null): void {
|
|
46
|
+
labelElements[key] = el;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Updates the tooltip content based on whether the label text is truncated.
|
|
51
|
+
* If truncated, prepends the full label text to the base tooltip.
|
|
52
|
+
* @param key - Unique identifier for the label
|
|
53
|
+
* @param label - The label text content
|
|
54
|
+
* @param baseTooltip - The original tooltip content
|
|
55
|
+
*/
|
|
56
|
+
function updateTooltipOnHover(key: string, label: string, baseTooltip?: string): void {
|
|
57
|
+
if (!baseTooltip) {
|
|
58
|
+
tooltipOverrides[key] = undefined;
|
|
59
|
+
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const element = labelElements[key];
|
|
64
|
+
|
|
65
|
+
tooltipOverrides[key] = isTruncated(element) ? `${ label }. ${ baseTooltip }` : baseTooltip;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Returns the tooltip to display for a given label.
|
|
70
|
+
* @param key - Unique identifier for the label
|
|
71
|
+
* @param fallback - Fallback tooltip if no override exists
|
|
72
|
+
*/
|
|
73
|
+
function getTooltip(key: string, fallback?: string): string | undefined {
|
|
74
|
+
return tooltipOverrides[key] ?? fallback;
|
|
75
|
+
}
|
|
25
76
|
</script>
|
|
26
77
|
|
|
27
78
|
<template>
|
|
@@ -34,7 +85,7 @@ function onClickItem(type: string, label: string) {
|
|
|
34
85
|
>
|
|
35
86
|
<RcIcon
|
|
36
87
|
v-if="footerItem.icon"
|
|
37
|
-
v-clean-tooltip="footerItem.iconTooltip?.key ? t(footerItem.iconTooltip
|
|
88
|
+
v-clean-tooltip="footerItem.iconTooltip?.key ? t(footerItem.iconTooltip.key) : undefined"
|
|
38
89
|
class="app-chart-card-footer-item-icon"
|
|
39
90
|
:type="footerItem.icon"
|
|
40
91
|
/>
|
|
@@ -47,14 +98,18 @@ function onClickItem(type: string, label: string) {
|
|
|
47
98
|
class="app-chart-card-footer-item-action"
|
|
48
99
|
>
|
|
49
100
|
<rc-button
|
|
50
|
-
v-clean-tooltip="footerItem.labelTooltip"
|
|
101
|
+
v-clean-tooltip="getTooltip(createLabelKey(i, j), footerItem.labelTooltip)"
|
|
51
102
|
variant="ghost"
|
|
52
103
|
class="app-chart-card-footer-button secondary-text-link"
|
|
53
104
|
data-testid="app-chart-card-footer-item-text"
|
|
54
105
|
:aria-label="t('catalog.charts.appChartCard.footerItem.ariaLabel', { filter: label })"
|
|
55
106
|
@click="onClickItem(footerItem.type, label)"
|
|
107
|
+
@mouseenter="updateTooltipOnHover(createLabelKey(i, j), label, footerItem.labelTooltip)"
|
|
56
108
|
>
|
|
57
|
-
<span
|
|
109
|
+
<span
|
|
110
|
+
:ref="(el) => registerLabelRef(createLabelKey(i, j), el as HTMLElement)"
|
|
111
|
+
class="app-chart-card-footer-button-label"
|
|
112
|
+
>{{ label }}</span>
|
|
58
113
|
</rc-button>
|
|
59
114
|
<span
|
|
60
115
|
v-if="footerItem.labels.length > 1 && j !== footerItem.labels.length - 1"
|
|
@@ -63,9 +118,11 @@ function onClickItem(type: string, label: string) {
|
|
|
63
118
|
</rc-item-card-action>
|
|
64
119
|
<span
|
|
65
120
|
v-else
|
|
66
|
-
|
|
121
|
+
:ref="(el) => registerLabelRef(createLabelKey(i, j), el as HTMLElement)"
|
|
122
|
+
v-clean-tooltip="getTooltip(createLabelKey(i, j), footerItem.labelTooltip)"
|
|
67
123
|
class="app-chart-card-footer-item-text"
|
|
68
124
|
data-testid="app-chart-card-footer-item-text"
|
|
125
|
+
@mouseenter="updateTooltipOnHover(createLabelKey(i, j), label, footerItem.labelTooltip)"
|
|
69
126
|
>
|
|
70
127
|
{{ label }}
|
|
71
128
|
<span
|
|
@@ -624,7 +624,6 @@ export default {
|
|
|
624
624
|
<RichTranslation
|
|
625
625
|
k="catalog.charts.appCollectionRepoMissing"
|
|
626
626
|
tag="div"
|
|
627
|
-
:raw="true"
|
|
628
627
|
>
|
|
629
628
|
<template #repoCreate="{ content }">
|
|
630
629
|
<router-link
|
|
@@ -661,10 +660,7 @@ export default {
|
|
|
661
660
|
{{ t('catalog.charts.noCharts.title') }}
|
|
662
661
|
</h1>
|
|
663
662
|
<div class="empty-state-tips">
|
|
664
|
-
<RichTranslation
|
|
665
|
-
k="catalog.charts.noCharts.message"
|
|
666
|
-
:raw="true"
|
|
667
|
-
>
|
|
663
|
+
<RichTranslation k="catalog.charts.noCharts.message">
|
|
668
664
|
<template #resetAllFilters="{ content }">
|
|
669
665
|
<a
|
|
670
666
|
tabindex="0"
|
|
@@ -684,8 +680,7 @@ export default {
|
|
|
684
680
|
</RichTranslation>
|
|
685
681
|
<RichTranslation
|
|
686
682
|
k="catalog.charts.noCharts.docsMessage"
|
|
687
|
-
tag="
|
|
688
|
-
:raw="true"
|
|
683
|
+
tag="span"
|
|
689
684
|
>
|
|
690
685
|
<template #docsUrl="{ content }">
|
|
691
686
|
<a
|
|
@@ -891,7 +886,7 @@ export default {
|
|
|
891
886
|
|
|
892
887
|
.charts-empty-state {
|
|
893
888
|
width: 100%;
|
|
894
|
-
padding: 72px
|
|
889
|
+
padding: 72px 72px;
|
|
895
890
|
text-align: center;
|
|
896
891
|
|
|
897
892
|
.empty-state-title {
|
|
@@ -379,7 +379,7 @@ export default {
|
|
|
379
379
|
/* Look for annotation to say this app is a legacy migrated app (we look in either place for now) */
|
|
380
380
|
this.migratedApp = (this.existing?.spec?.chart?.metadata?.annotations?.[CATALOG_ANNOTATIONS.MIGRATED] === 'true');
|
|
381
381
|
|
|
382
|
-
if (this.repo
|
|
382
|
+
if (this.repo?.isSuseAppCollection) {
|
|
383
383
|
let defaultSelectedSecret = await this.$store.getters['cluster/byId'](SECRET, `cattle-system/${ this.repo.spec.clientSecret.name }`);
|
|
384
384
|
|
|
385
385
|
if (!defaultSelectedSecret) {
|
|
@@ -823,7 +823,7 @@ export default {
|
|
|
823
823
|
}
|
|
824
824
|
}
|
|
825
825
|
|
|
826
|
-
if (this.repo
|
|
826
|
+
if (this.repo?.isSuseAppCollection) {
|
|
827
827
|
await this.initializeDataForNamespaceChanges();
|
|
828
828
|
}
|
|
829
829
|
},
|
|
@@ -938,7 +938,7 @@ export default {
|
|
|
938
938
|
|
|
939
939
|
async initializeDataForNamespaceChanges() {
|
|
940
940
|
// Skip the flow if the data still not fetched, it will trigger after fetching manually
|
|
941
|
-
if (
|
|
941
|
+
if (this.appCoDataFetched) {
|
|
942
942
|
try {
|
|
943
943
|
this.defaultImagePullSecret = await this.$store.dispatch('cluster/find', { type: SECRET, id: `${ this.targetNamespace }/${ this.repo.spec.clientSecret.name }-image-pull-secret` });
|
|
944
944
|
} catch (e) {
|
|
@@ -1028,7 +1028,7 @@ export default {
|
|
|
1028
1028
|
},
|
|
1029
1029
|
|
|
1030
1030
|
async setImagePullSecretData() {
|
|
1031
|
-
if (this.selectedSecret && this.repo
|
|
1031
|
+
if (this.selectedSecret && this.repo?.isSuseAppCollection && this.dontUseDefaultOption !== null) {
|
|
1032
1032
|
if (!this.dontUseDefaultOption && this.defaultImagePullSecret) {
|
|
1033
1033
|
// If the default option is used and the default secret already exists, use it
|
|
1034
1034
|
this.selectedImagePullSecret = this.defaultImagePullSecret.name;
|
|
@@ -1159,7 +1159,7 @@ export default {
|
|
|
1159
1159
|
|
|
1160
1160
|
// Create namespace if it doesn't exist (before hooks run)
|
|
1161
1161
|
// And only if it is SUSE APP Collection, overall should just do the same flow
|
|
1162
|
-
if (!isUpgrade && this.isNamespaceNew && this.repo
|
|
1162
|
+
if (!isUpgrade && this.isNamespaceNew && this.repo?.isSuseAppCollection) {
|
|
1163
1163
|
await this.createNamespaceIfNeeded();
|
|
1164
1164
|
}
|
|
1165
1165
|
|
|
@@ -1355,7 +1355,7 @@ export default {
|
|
|
1355
1355
|
...migratedAnnotations,
|
|
1356
1356
|
[CATALOG_ANNOTATIONS.SOURCE_REPO_TYPE]: this.chart.repoType,
|
|
1357
1357
|
[CATALOG_ANNOTATIONS.SOURCE_REPO_NAME]: this.chart.repoName,
|
|
1358
|
-
...(this.repo
|
|
1358
|
+
...(this.repo?.isSuseAppCollection ? { [CATALOG_ANNOTATIONS.SUSE_APP_COLLECTION]: 'true' } : {}),
|
|
1359
1359
|
},
|
|
1360
1360
|
values,
|
|
1361
1361
|
};
|
|
@@ -1497,7 +1497,7 @@ export default {
|
|
|
1497
1497
|
},
|
|
1498
1498
|
|
|
1499
1499
|
async createImagePullSecret() {
|
|
1500
|
-
if (!this.repo
|
|
1500
|
+
if (!this.repo?.isSuseAppCollection) {
|
|
1501
1501
|
return;
|
|
1502
1502
|
}
|
|
1503
1503
|
|
|
@@ -1655,7 +1655,6 @@ export default {
|
|
|
1655
1655
|
</div>
|
|
1656
1656
|
</div>
|
|
1657
1657
|
<NameNsDescription
|
|
1658
|
-
v-if="showNameEditor"
|
|
1659
1658
|
v-model:value="value"
|
|
1660
1659
|
:description-hidden="true"
|
|
1661
1660
|
:mode="mode"
|
|
@@ -1686,7 +1685,7 @@ export default {
|
|
|
1686
1685
|
</template>
|
|
1687
1686
|
</NameNsDescription>
|
|
1688
1687
|
<div
|
|
1689
|
-
v-if="repo
|
|
1688
|
+
v-if="repo?.isSuseAppCollection"
|
|
1690
1689
|
class="mb-20"
|
|
1691
1690
|
>
|
|
1692
1691
|
<Banner
|