@rancher/shell 0.3.7 → 0.3.8
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/translations/en-us.yaml +27 -12
- package/components/Inactivity.vue +229 -0
- package/config/settings.ts +4 -0
- package/creators/app/files/.gitignore +73 -0
- package/creators/app/init +1 -0
- package/edit/provisioning.cattle.io.cluster/rke2.vue +3 -15
- package/edit/workload/mixins/workload.js +12 -4
- package/layouts/blank.vue +4 -0
- package/layouts/default.vue +3 -0
- package/layouts/home.vue +4 -1
- package/layouts/plain.vue +4 -1
- package/models/provisioning.cattle.io.cluster.js +24 -0
- package/package.json +1 -1
- package/pages/c/_cluster/explorer/index.vue +1 -4
- package/pages/c/_cluster/settings/performance.vue +61 -7
- package/store/index.js +36 -21
|
@@ -1249,7 +1249,7 @@ cluster:
|
|
|
1249
1249
|
memory: Memory
|
|
1250
1250
|
disk: Disk
|
|
1251
1251
|
image: Image
|
|
1252
|
-
network:
|
|
1252
|
+
network:
|
|
1253
1253
|
title: Networks
|
|
1254
1254
|
network: Network
|
|
1255
1255
|
addNetwork: Add Network
|
|
@@ -1827,7 +1827,7 @@ cluster:
|
|
|
1827
1827
|
option: Default - RKE2 Embedded
|
|
1828
1828
|
defaultPodSecurityAdmissionConfigurationTemplateName:
|
|
1829
1829
|
label: Pod Security Admission Configuration Template
|
|
1830
|
-
option:
|
|
1830
|
+
option:
|
|
1831
1831
|
none: (None)
|
|
1832
1832
|
default: Default - RKE2 Embedded
|
|
1833
1833
|
cisProfile:
|
|
@@ -2202,16 +2202,16 @@ fleet:
|
|
|
2202
2202
|
error: Error
|
|
2203
2203
|
ready: Ready
|
|
2204
2204
|
errors: Errors
|
|
2205
|
-
workspaces:
|
|
2205
|
+
workspaces:
|
|
2206
2206
|
tabs:
|
|
2207
2207
|
restrictions: Allowed Target Namespaces
|
|
2208
2208
|
timeout: Workspace creation timeout. It's possible the workspace was created. We suggest checking the workspace page before trying to create another.
|
|
2209
|
-
restrictions:
|
|
2209
|
+
restrictions:
|
|
2210
2210
|
addTitle: 'allowedTargetNamespaces'
|
|
2211
2211
|
addLabel: Add
|
|
2212
2212
|
banner: "{count, plural,
|
|
2213
2213
|
=0 { Adding namespaces here will create a GitRepoRestriction. }
|
|
2214
|
-
other { Only the Git Repo Restriction's <code>allowedTargetNamespaces</code> is managed here. You can make additional changes to the Git Repo Restriction}
|
|
2214
|
+
other { Only the Git Repo Restriction's <code>allowedTargetNamespaces</code> is managed here. You can make additional changes to the Git Repo Restriction}
|
|
2215
2215
|
}"
|
|
2216
2216
|
footer:
|
|
2217
2217
|
docs: Docs
|
|
@@ -2260,7 +2260,7 @@ gatekeeperIndex:
|
|
|
2260
2260
|
unavailable: OPA + Gatekeeper is not available in the system-charts catalog.
|
|
2261
2261
|
violations: Violations
|
|
2262
2262
|
|
|
2263
|
-
gatekeeperInstall:
|
|
2263
|
+
gatekeeperInstall:
|
|
2264
2264
|
auditInterval: Auto Interval
|
|
2265
2265
|
constraintViolationsLimit: Constraint Violations Limit
|
|
2266
2266
|
runtimeDefaultSeccompProfile: Enable Runtime Default Seccomp Profile
|
|
@@ -3952,6 +3952,15 @@ podDisruptionBudget:
|
|
|
3952
3952
|
maxUnavailable:
|
|
3953
3953
|
label: Max. unavailable Pods
|
|
3954
3954
|
|
|
3955
|
+
inactivity:
|
|
3956
|
+
title: Session expiring
|
|
3957
|
+
titleExpired: Session expired
|
|
3958
|
+
banner: Your session is about to expire due to inactivity. Any unsaved changes will be lost.
|
|
3959
|
+
bannerExpired: Your session has expired in this tab due to inactivity.
|
|
3960
|
+
content: Click “Resume Session” to keep the session in this tab active or refresh the browser after the session has expired.
|
|
3961
|
+
contentExpired: To return to this page click “Refresh” below or refresh the browser.
|
|
3962
|
+
cta: Resume Session
|
|
3963
|
+
ctaExpired: Refresh
|
|
3955
3964
|
|
|
3956
3965
|
# Rancher Extensions
|
|
3957
3966
|
plugins:
|
|
@@ -4051,7 +4060,7 @@ plugins:
|
|
|
4051
4060
|
podSecurityAdmission:
|
|
4052
4061
|
name: Pod Security Admission
|
|
4053
4062
|
description: Define the admission control mode you want to use for the pod security
|
|
4054
|
-
banner:
|
|
4063
|
+
banner:
|
|
4055
4064
|
modifications: 'Changing any template that is currently in use will cause an update to those live clusters the next time the cluster is updated'
|
|
4056
4065
|
labels:
|
|
4057
4066
|
enforce: Enforce
|
|
@@ -4065,7 +4074,7 @@ podSecurityAdmission:
|
|
|
4065
4074
|
restricted: restricted
|
|
4066
4075
|
version:
|
|
4067
4076
|
placeholder: 'Version (default: latest)'
|
|
4068
|
-
exemptions:
|
|
4077
|
+
exemptions:
|
|
4069
4078
|
title: Exemptions
|
|
4070
4079
|
description: Allow the creation of pods for specific Usernames, RuntimeClassNames, and Namespaces that would otherwise be prohibited due to the policies set above.
|
|
4071
4080
|
placeholder: Enter a comma separated list of {psaExemptionsControl}
|
|
@@ -5186,7 +5195,7 @@ storageClass:
|
|
|
5186
5195
|
warning: 'The {provisioner} in-tree plugin is deprecated: Find a CSI driver <a target="_blank" rel="noopener noreferrer nofollow" href="https://kubernetes-csi.github.io/docs/drivers.html">here.</a>'
|
|
5187
5196
|
harvesterhci:
|
|
5188
5197
|
title: Harvester (CSI)
|
|
5189
|
-
warning:
|
|
5198
|
+
warning:
|
|
5190
5199
|
unSatisfiesVersion: Please upgrade your Harvester CSI driver version to use this feature. (csi-driver >= v0.1.15)
|
|
5191
5200
|
hostStorageClass:
|
|
5192
5201
|
label: Host Storage Class
|
|
@@ -6131,8 +6140,6 @@ workload:
|
|
|
6131
6140
|
pod: Pod
|
|
6132
6141
|
containers: Containers
|
|
6133
6142
|
|
|
6134
|
-
|
|
6135
|
-
|
|
6136
6143
|
##############################
|
|
6137
6144
|
# Model Properties
|
|
6138
6145
|
##############################
|
|
@@ -6778,7 +6785,7 @@ typeLabel:
|
|
|
6778
6785
|
{count, plural,
|
|
6779
6786
|
one { Cluster Registration Token }
|
|
6780
6787
|
other { Cluster Registration Tokens }
|
|
6781
|
-
}
|
|
6788
|
+
}
|
|
6782
6789
|
|
|
6783
6790
|
action:
|
|
6784
6791
|
clone: Clone
|
|
@@ -7002,6 +7009,14 @@ performance:
|
|
|
7002
7009
|
label: Websocket Web Worker
|
|
7003
7010
|
description: Updates to resources pushed to the UI come via WebSocket and are handled in the UI thread. Enable this option to handle cluster WebSocket updates in a Web Worker in a separate thread. This should help the responsiveness of the UI in systems where resources change often.
|
|
7004
7011
|
checkboxLabel: Enable Advanced Websocket Web Worker
|
|
7012
|
+
inactivity:
|
|
7013
|
+
title: Inactivity
|
|
7014
|
+
checkboxLabel: Enable inactivity session expiration
|
|
7015
|
+
inputLabel: Inactivity timeout (minutes)
|
|
7016
|
+
information: To change the automatic logout behaviour, edit the authorisation and/or session token timeout values (<code>auth-user-session-ttl-minutes</code> and <code>auth-token-max-ttl-minutes</code>) in the Settings page.
|
|
7017
|
+
description: When enabled and the user is inactive past the specified timeout, the UI will no longer fresh page content and the user must reload the page to continue.
|
|
7018
|
+
authUserTTL: This timeout cannot be higher than the user session timeout auth-user-session-ttl-minutes, which is currently {current} minutes.
|
|
7019
|
+
|
|
7005
7020
|
|
|
7006
7021
|
banner:
|
|
7007
7022
|
label: Fixed Banners
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import ModalWithCard from '@shell/components/ModalWithCard';
|
|
3
|
+
import { Banner } from '@components/Banner';
|
|
4
|
+
import PercentageBar from '@shell/components/PercentageBar.vue';
|
|
5
|
+
import throttle from 'lodash/throttle';
|
|
6
|
+
import { MANAGEMENT } from '@shell/config/types';
|
|
7
|
+
import { DEFAULT_PERF_SETTING, SETTING } from '@shell/config/settings';
|
|
8
|
+
|
|
9
|
+
export default {
|
|
10
|
+
name: 'Inactivity',
|
|
11
|
+
components: {
|
|
12
|
+
ModalWithCard, Banner, PercentageBar
|
|
13
|
+
},
|
|
14
|
+
data() {
|
|
15
|
+
return {
|
|
16
|
+
enabled: null,
|
|
17
|
+
isOpen: false,
|
|
18
|
+
isInactive: false,
|
|
19
|
+
showModalAfter: null,
|
|
20
|
+
inactivityTimeoutId: null,
|
|
21
|
+
courtesyTimer: null,
|
|
22
|
+
courtesyTimerId: null,
|
|
23
|
+
courtesyCountdown: null,
|
|
24
|
+
trackInactivity: throttle(this._trackInactivity, 1000),
|
|
25
|
+
};
|
|
26
|
+
},
|
|
27
|
+
async mounted() {
|
|
28
|
+
// Info: normally, this is done in the fetch hook but for some reasons while awaiting for things that will take a while, it won't be ready by the time mounted() is called, pending for investigation.
|
|
29
|
+
let settings;
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
const settingsString = await this.$store.dispatch('management/find', { type: MANAGEMENT.SETTING, id: SETTING.UI_PERFORMANCE });
|
|
33
|
+
|
|
34
|
+
settings = settingsString?.value ? JSON.parse(settingsString.value) : DEFAULT_PERF_SETTING;
|
|
35
|
+
} catch { }
|
|
36
|
+
|
|
37
|
+
if (!settings || !settings?.inactivity || !settings?.inactivity.enabled) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
this.enabled = settings?.inactivity?.enabled || false;
|
|
42
|
+
|
|
43
|
+
// Total amount of time before the user's session is lost
|
|
44
|
+
const thresholdToSeconds = settings?.inactivity?.threshold * 60;
|
|
45
|
+
|
|
46
|
+
// Amount of time the user sees the inactivity warning
|
|
47
|
+
this.courtesyTimer = Math.floor(thresholdToSeconds * 0.1);
|
|
48
|
+
this.courtesyTimer = Math.min(this.courtesyTimer, 60 * 5); // Never show the modal more than 5 minutes
|
|
49
|
+
// Amount of time before the user sees the inactivity warning
|
|
50
|
+
// Note - time before warning is shown + time warning is shown = settings threshold (total amount of time)
|
|
51
|
+
this.showModalAfter = thresholdToSeconds - this.courtesyTimer;
|
|
52
|
+
|
|
53
|
+
console.debug(`Inactivity modal will show after ${ this.showModalAfter / 60 }(m) and be shown for ${ this.courtesyTimer / 60 }(m)`); // eslint-disable-line no-console
|
|
54
|
+
|
|
55
|
+
this.courtesyCountdown = this.courtesyTimer;
|
|
56
|
+
|
|
57
|
+
if (settings?.inactivity.enabled) {
|
|
58
|
+
this.trackInactivity();
|
|
59
|
+
this.addIdleListeners();
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
beforeDestroy() {
|
|
63
|
+
this.removeEventListener();
|
|
64
|
+
this.clearAllTimeouts();
|
|
65
|
+
},
|
|
66
|
+
methods: {
|
|
67
|
+
_trackInactivity() {
|
|
68
|
+
if (this.isInactive || this.isOpen || !this.showModalAfter) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
this.clearAllTimeouts();
|
|
73
|
+
const endTime = Date.now() + this.showModalAfter * 1000;
|
|
74
|
+
|
|
75
|
+
const checkInactivityTimer = () => {
|
|
76
|
+
const now = Date.now();
|
|
77
|
+
|
|
78
|
+
if (now >= endTime) {
|
|
79
|
+
this.isOpen = true;
|
|
80
|
+
this.startCountdown();
|
|
81
|
+
|
|
82
|
+
this.$modal.show('inactivityModal');
|
|
83
|
+
} else {
|
|
84
|
+
this.inactivityTimeoutId = setTimeout(checkInactivityTimer, 1000);
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
checkInactivityTimer();
|
|
89
|
+
},
|
|
90
|
+
startCountdown() {
|
|
91
|
+
const endTime = Date.now() + (this.courtesyCountdown * 1000);
|
|
92
|
+
|
|
93
|
+
const checkCountdown = () => {
|
|
94
|
+
const now = Date.now();
|
|
95
|
+
|
|
96
|
+
if (now >= endTime) {
|
|
97
|
+
this.isInactive = true;
|
|
98
|
+
this.unsubscribe();
|
|
99
|
+
this.clearAllTimeouts();
|
|
100
|
+
} else {
|
|
101
|
+
this.courtesyCountdown = Math.floor((endTime - now) / 1000);
|
|
102
|
+
this.courtesyTimerId = setTimeout(checkCountdown, 1000);
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
checkCountdown();
|
|
107
|
+
},
|
|
108
|
+
addIdleListeners() {
|
|
109
|
+
document.addEventListener('mousemove', this.trackInactivity);
|
|
110
|
+
document.addEventListener('mousedown', this.trackInactivity);
|
|
111
|
+
document.addEventListener('keypress', this.trackInactivity);
|
|
112
|
+
document.addEventListener('touchmove', this.trackInactivity);
|
|
113
|
+
document.addEventListener('visibilitychange', this.trackInactivity);
|
|
114
|
+
},
|
|
115
|
+
removeEventListener() {
|
|
116
|
+
document.removeEventListener('mousemove', this.trackInactivity);
|
|
117
|
+
document.removeEventListener('mousedown', this.trackInactivity);
|
|
118
|
+
document.removeEventListener('keypress', this.trackInactivity);
|
|
119
|
+
document.removeEventListener('touchmove', this.trackInactivity);
|
|
120
|
+
document.removeEventListener('visibilitychange', this.trackInactivity);
|
|
121
|
+
},
|
|
122
|
+
|
|
123
|
+
resume() {
|
|
124
|
+
this.isInactive = false;
|
|
125
|
+
this.isOpen = false;
|
|
126
|
+
this.courtesyCountdown = this.courtesyTimer;
|
|
127
|
+
this.clearAllTimeouts();
|
|
128
|
+
|
|
129
|
+
this.$modal.hide('inactivityModal');
|
|
130
|
+
},
|
|
131
|
+
|
|
132
|
+
refresh() {
|
|
133
|
+
window.location.reload();
|
|
134
|
+
},
|
|
135
|
+
|
|
136
|
+
unsubscribe() {
|
|
137
|
+
this.$store.dispatch('unsubscribe');
|
|
138
|
+
},
|
|
139
|
+
clearAllTimeouts() {
|
|
140
|
+
clearTimeout(this.inactivityTimeoutId);
|
|
141
|
+
clearTimeout(this.courtesyTimerId);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
},
|
|
145
|
+
computed: {
|
|
146
|
+
isInactiveTexts() {
|
|
147
|
+
return this.isInactive ? {
|
|
148
|
+
title: this.t('inactivity.titleExpired'),
|
|
149
|
+
banner: this.t('inactivity.bannerExpired'),
|
|
150
|
+
content: this.t('inactivity.contentExpired'),
|
|
151
|
+
} : {
|
|
152
|
+
title: this.t('inactivity.title'),
|
|
153
|
+
banner: this.t('inactivity.banner'),
|
|
154
|
+
content: this.t('inactivity.content'),
|
|
155
|
+
};
|
|
156
|
+
},
|
|
157
|
+
timerPercentageLeft() {
|
|
158
|
+
return Math.floor((this.courtesyCountdown / this.courtesyTimer ) * 100);
|
|
159
|
+
},
|
|
160
|
+
colorStops() {
|
|
161
|
+
return {
|
|
162
|
+
0: '--info', 30: '--info', 70: '--info'
|
|
163
|
+
};
|
|
164
|
+
},
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
</script>
|
|
168
|
+
|
|
169
|
+
<template>
|
|
170
|
+
<ModalWithCard
|
|
171
|
+
ref="inactivityModal"
|
|
172
|
+
name="inactivityModal"
|
|
173
|
+
save-text="Continue"
|
|
174
|
+
:v-if="isOpen"
|
|
175
|
+
@finish="resume"
|
|
176
|
+
>
|
|
177
|
+
<template #title>
|
|
178
|
+
{{ isInactiveTexts.title }}
|
|
179
|
+
</template>
|
|
180
|
+
<span>{{ courtesyCountdown }}</span>
|
|
181
|
+
|
|
182
|
+
<template #content>
|
|
183
|
+
<Banner color="info">
|
|
184
|
+
{{ isInactiveTexts.banner }}
|
|
185
|
+
</Banner>
|
|
186
|
+
|
|
187
|
+
<p>
|
|
188
|
+
{{ isInactiveTexts.content }}
|
|
189
|
+
</p>
|
|
190
|
+
|
|
191
|
+
<PercentageBar
|
|
192
|
+
v-if="!isInactive"
|
|
193
|
+
class="mt-20"
|
|
194
|
+
:value="timerPercentageLeft"
|
|
195
|
+
:color-stops="colorStops"
|
|
196
|
+
/>
|
|
197
|
+
</template>
|
|
198
|
+
|
|
199
|
+
<template
|
|
200
|
+
#footer
|
|
201
|
+
>
|
|
202
|
+
<div class="card-actions">
|
|
203
|
+
<button
|
|
204
|
+
v-if="!isInactive"
|
|
205
|
+
class="btn role-tertiary bg-primary"
|
|
206
|
+
@click.prevent="resume"
|
|
207
|
+
>
|
|
208
|
+
<t k="inactivity.cta" />
|
|
209
|
+
</button>
|
|
210
|
+
|
|
211
|
+
<button
|
|
212
|
+
v-if="isInactive"
|
|
213
|
+
class="btn role-tertiary bg-primary"
|
|
214
|
+
@click.prevent="refresh"
|
|
215
|
+
>
|
|
216
|
+
<t k="inactivity.ctaExpired" />
|
|
217
|
+
</button>
|
|
218
|
+
</div>
|
|
219
|
+
</template>
|
|
220
|
+
</ModalWithCard>
|
|
221
|
+
</template>
|
|
222
|
+
|
|
223
|
+
<style lang="scss" scoped>
|
|
224
|
+
.card-actions {
|
|
225
|
+
display: flex;
|
|
226
|
+
width: 100%;
|
|
227
|
+
justify-content: flex-end;
|
|
228
|
+
}
|
|
229
|
+
</style>
|
package/config/settings.ts
CHANGED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# compiled output
|
|
2
|
+
/dist
|
|
3
|
+
/tmp
|
|
4
|
+
/out-tsc
|
|
5
|
+
|
|
6
|
+
# Runtime data
|
|
7
|
+
pids
|
|
8
|
+
*.pid
|
|
9
|
+
*.seed
|
|
10
|
+
*.pid.lock
|
|
11
|
+
|
|
12
|
+
# Directory for instrumented libs generated by jscoverage/JSCover
|
|
13
|
+
lib-cov
|
|
14
|
+
|
|
15
|
+
# Coverage directory used by tools like istanbul
|
|
16
|
+
coverage
|
|
17
|
+
|
|
18
|
+
# nyc test coverage
|
|
19
|
+
.nyc_output
|
|
20
|
+
|
|
21
|
+
# IDEs and editors
|
|
22
|
+
.idea
|
|
23
|
+
.project
|
|
24
|
+
.classpath
|
|
25
|
+
.c9/
|
|
26
|
+
*.launch
|
|
27
|
+
.settings/
|
|
28
|
+
*.sublime-workspace
|
|
29
|
+
|
|
30
|
+
# IDE - VSCode
|
|
31
|
+
.vscode/*
|
|
32
|
+
!.vscode/settings.json
|
|
33
|
+
!.vscode/tasks.json
|
|
34
|
+
!.vscode/launch.json
|
|
35
|
+
!.vscode/extensions.json
|
|
36
|
+
|
|
37
|
+
# misc
|
|
38
|
+
.sass-cache
|
|
39
|
+
connect.lock
|
|
40
|
+
typings
|
|
41
|
+
|
|
42
|
+
# Logs
|
|
43
|
+
logs
|
|
44
|
+
*.log
|
|
45
|
+
npm-debug.log*
|
|
46
|
+
yarn-debug.log*
|
|
47
|
+
yarn-error.log*
|
|
48
|
+
|
|
49
|
+
# Dependency directories
|
|
50
|
+
node_modules/
|
|
51
|
+
jspm_packages/
|
|
52
|
+
|
|
53
|
+
# Optional npm cache directory
|
|
54
|
+
.npm
|
|
55
|
+
|
|
56
|
+
# Optional eslint cache
|
|
57
|
+
.eslintcache
|
|
58
|
+
|
|
59
|
+
# Optional REPL history
|
|
60
|
+
.node_repl_history
|
|
61
|
+
|
|
62
|
+
# Output of 'npm pack'
|
|
63
|
+
*.tgz
|
|
64
|
+
|
|
65
|
+
# Yarn Integrity file
|
|
66
|
+
.yarn-integrity
|
|
67
|
+
|
|
68
|
+
# dotenv environment variables file
|
|
69
|
+
.env
|
|
70
|
+
|
|
71
|
+
# System Files
|
|
72
|
+
.DS_Store
|
|
73
|
+
Thumbs.db
|
package/creators/app/init
CHANGED
|
@@ -306,12 +306,7 @@ export default {
|
|
|
306
306
|
const lastDefaultPodSecurityPolicyTemplateName = this.value.spec.defaultPodSecurityPolicyTemplateName;
|
|
307
307
|
const previousKubernetesVersion = this.value.spec.kubernetesVersion;
|
|
308
308
|
|
|
309
|
-
const truncateLimit = this.value.
|
|
310
|
-
|
|
311
|
-
// Is hostname truncation supported by the backend?
|
|
312
|
-
const provSchema = this.$store.getters['management/schemaFor'](CAPI.RANCHER_CLUSTER);
|
|
313
|
-
const specSchemaName = provSchema?.resourceFields?.spec?.type;
|
|
314
|
-
const specSchema = specSchemaName ? this.$store.getters['management/schemaFor'](specSchemaName) : {};
|
|
309
|
+
const truncateLimit = this.value.defaultHostnameLengthLimit;
|
|
315
310
|
|
|
316
311
|
return {
|
|
317
312
|
loadedOnce: false,
|
|
@@ -350,7 +345,6 @@ export default {
|
|
|
350
345
|
psps: null, // List of policies if any
|
|
351
346
|
truncateHostnames: truncateLimit === NETBIOS_TRUNCATION_LENGTH,
|
|
352
347
|
truncateLimit,
|
|
353
|
-
supportsTruncation: !!specSchema?.resourceFields?.machinePoolDefaults,
|
|
354
348
|
};
|
|
355
349
|
},
|
|
356
350
|
|
|
@@ -1100,14 +1094,9 @@ export default {
|
|
|
1100
1094
|
*/
|
|
1101
1095
|
truncateName() {
|
|
1102
1096
|
if (this.truncateHostnames) {
|
|
1103
|
-
this.value.
|
|
1104
|
-
this.value.machinePoolDefaults.hostnameLengthLimit = 15;
|
|
1097
|
+
this.value.defaultHostnameLengthLimit = NETBIOS_TRUNCATION_LENGTH;
|
|
1105
1098
|
} else {
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
if (Object.keys(this.value.machinePoolDefaults).length === 0) {
|
|
1109
|
-
delete this.value.machinePoolDefaults;
|
|
1110
|
-
}
|
|
1099
|
+
this.value.removeDefaultHostnameLengthLimit();
|
|
1111
1100
|
}
|
|
1112
1101
|
},
|
|
1113
1102
|
/**
|
|
@@ -2598,7 +2587,6 @@ export default {
|
|
|
2598
2587
|
/>
|
|
2599
2588
|
</div>
|
|
2600
2589
|
<div
|
|
2601
|
-
v-if="supportsTruncation"
|
|
2602
2590
|
class="col span-6"
|
|
2603
2591
|
>
|
|
2604
2592
|
<Checkbox
|
|
@@ -26,7 +26,7 @@ import Loading from '@shell/components/Loading';
|
|
|
26
26
|
import Networking from '@shell/components/form/Networking';
|
|
27
27
|
import VolumeClaimTemplate from '@shell/edit/workload/VolumeClaimTemplate';
|
|
28
28
|
import Job from '@shell/edit/workload/Job';
|
|
29
|
-
import { _EDIT, _CREATE, _VIEW } from '@shell/config/query-params';
|
|
29
|
+
import { _EDIT, _CREATE, _VIEW, _CLONE } from '@shell/config/query-params';
|
|
30
30
|
import WorkloadPorts from '@shell/components/form/WorkloadPorts';
|
|
31
31
|
import ContainerResourceLimit from '@shell/components/ContainerResourceLimit';
|
|
32
32
|
import KeyValue from '@shell/components/form/KeyValue';
|
|
@@ -178,7 +178,7 @@ export default {
|
|
|
178
178
|
|
|
179
179
|
// EDIT view for POD
|
|
180
180
|
// Transform it from POD world to workload
|
|
181
|
-
if ((this.mode === _EDIT || this.mode === _VIEW ) && this.value.type === 'pod'
|
|
181
|
+
if ((this.mode === _EDIT || this.mode === _VIEW || this.realMode === _CLONE ) && this.value.type === 'pod') {
|
|
182
182
|
const podSpec = { ...this.value.spec };
|
|
183
183
|
const metadata = { ...this.value.metadata };
|
|
184
184
|
|
|
@@ -198,6 +198,7 @@ export default {
|
|
|
198
198
|
if (
|
|
199
199
|
this.mode === _CREATE ||
|
|
200
200
|
this.mode === _VIEW ||
|
|
201
|
+
this.realMode === _CLONE ||
|
|
201
202
|
(!createSidecar && !this.value.hasSidecars) // hasSideCars = containers.length > 1 || initContainers.length;
|
|
202
203
|
) {
|
|
203
204
|
container = containers[0];
|
|
@@ -703,7 +704,7 @@ export default {
|
|
|
703
704
|
if (
|
|
704
705
|
this.type !== WORKLOAD_TYPES.JOB &&
|
|
705
706
|
this.type !== WORKLOAD_TYPES.CRON_JOB &&
|
|
706
|
-
this.mode === _CREATE
|
|
707
|
+
(this.mode === _CREATE || this.realMode === _CLONE)
|
|
707
708
|
) {
|
|
708
709
|
this.spec.selector = { matchLabels: this.value.workloadSelector };
|
|
709
710
|
Object.assign(this.value.metadata.labels, this.value.workloadSelector);
|
|
@@ -721,7 +722,7 @@ export default {
|
|
|
721
722
|
if (
|
|
722
723
|
this.type !== WORKLOAD_TYPES.JOB &&
|
|
723
724
|
this.type !== WORKLOAD_TYPES.CRON_JOB &&
|
|
724
|
-
this.mode === _CREATE
|
|
725
|
+
(this.mode === _CREATE || this.realMode === _CLONE)
|
|
725
726
|
) {
|
|
726
727
|
if (!template.metadata) {
|
|
727
728
|
template.metadata = { labels: this.value.workloadSelector };
|
|
@@ -784,6 +785,13 @@ export default {
|
|
|
784
785
|
|
|
785
786
|
template.metadata.namespace = this.value.metadata.namespace;
|
|
786
787
|
|
|
788
|
+
// Handle the case where the user has changed the name of the workload
|
|
789
|
+
// Only do this for clone. Not allowed for edit
|
|
790
|
+
if (this.realMode === _CLONE) {
|
|
791
|
+
template.metadata.name = this.value.metadata.name;
|
|
792
|
+
template.metadata.description = this.value.metadata.description;
|
|
793
|
+
}
|
|
794
|
+
|
|
787
795
|
// delete this.value.kind;
|
|
788
796
|
if (this.container && !this.container.name) {
|
|
789
797
|
this.$set(this.container, 'name', this.value.metadata.name);
|
package/layouts/blank.vue
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
<script>
|
|
2
2
|
import Brand from '@shell/mixins/brand';
|
|
3
|
+
import Inactivity from '@shell/components/Inactivity';
|
|
3
4
|
|
|
4
5
|
export default {
|
|
6
|
+
components: { Inactivity },
|
|
5
7
|
middleware: ['authenticated'],
|
|
6
8
|
mixins: [Brand],
|
|
7
9
|
};
|
|
@@ -10,6 +12,8 @@ export default {
|
|
|
10
12
|
<template>
|
|
11
13
|
<main class="main-layout">
|
|
12
14
|
<nuxt />
|
|
15
|
+
|
|
16
|
+
<Inactivity />
|
|
13
17
|
</main>
|
|
14
18
|
</template>
|
|
15
19
|
|
package/layouts/default.vue
CHANGED
|
@@ -16,6 +16,7 @@ import PromptModal from '@shell/components/PromptModal';
|
|
|
16
16
|
import AssignTo from '@shell/components/AssignTo';
|
|
17
17
|
import Group from '@shell/components/nav/Group';
|
|
18
18
|
import Header from '@shell/components/nav/Header';
|
|
19
|
+
import Inactivity from '@shell/components/Inactivity';
|
|
19
20
|
import Brand from '@shell/mixins/brand';
|
|
20
21
|
import FixedBanner from '@shell/components/FixedBanner';
|
|
21
22
|
import AwsComplianceBanner from '@shell/components/AwsComplianceBanner';
|
|
@@ -57,6 +58,7 @@ export default {
|
|
|
57
58
|
AwsComplianceBanner,
|
|
58
59
|
AzureWarning,
|
|
59
60
|
DraggableZone,
|
|
61
|
+
Inactivity
|
|
60
62
|
},
|
|
61
63
|
|
|
62
64
|
mixins: [PageHeaderActions, Brand, BrowserTabVisibility],
|
|
@@ -765,6 +767,7 @@ export default {
|
|
|
765
767
|
</div>
|
|
766
768
|
<FixedBanner :footer="true" />
|
|
767
769
|
<GrowlManager />
|
|
770
|
+
<Inactivity />
|
|
768
771
|
<DraggableZone ref="draggableZone" />
|
|
769
772
|
</div>
|
|
770
773
|
</template>
|
package/layouts/home.vue
CHANGED
|
@@ -7,6 +7,7 @@ import { mapPref, THEME_SHORTCUT } from '@shell/store/prefs';
|
|
|
7
7
|
import AwsComplianceBanner from '@shell/components/AwsComplianceBanner';
|
|
8
8
|
import AzureWarning from '@shell/components/auth/AzureWarning';
|
|
9
9
|
import BrowserTabVisibility from '@shell/mixins/browser-tab-visibility';
|
|
10
|
+
import Inactivity from '@shell/components/Inactivity';
|
|
10
11
|
import { mapState } from 'vuex';
|
|
11
12
|
|
|
12
13
|
export default {
|
|
@@ -16,7 +17,8 @@ export default {
|
|
|
16
17
|
FixedBanner,
|
|
17
18
|
GrowlManager,
|
|
18
19
|
AzureWarning,
|
|
19
|
-
AwsComplianceBanner
|
|
20
|
+
AwsComplianceBanner,
|
|
21
|
+
Inactivity
|
|
20
22
|
},
|
|
21
23
|
|
|
22
24
|
mixins: [Brand, BrowserTabVisibility],
|
|
@@ -51,6 +53,7 @@ export default {
|
|
|
51
53
|
<template>
|
|
52
54
|
<div class="dashboard-root">
|
|
53
55
|
<FixedBanner :header="true" />
|
|
56
|
+
<Inactivity />
|
|
54
57
|
<AwsComplianceBanner />
|
|
55
58
|
<AzureWarning />
|
|
56
59
|
|
package/layouts/plain.vue
CHANGED
|
@@ -11,6 +11,7 @@ import GrowlManager from '@shell/components/GrowlManager';
|
|
|
11
11
|
import AwsComplianceBanner from '@shell/components/AwsComplianceBanner';
|
|
12
12
|
import AzureWarning from '@shell/components/auth/AzureWarning';
|
|
13
13
|
import BrowserTabVisibility from '@shell/mixins/browser-tab-visibility';
|
|
14
|
+
import Inactivity from '@shell/components/Inactivity';
|
|
14
15
|
|
|
15
16
|
export default {
|
|
16
17
|
|
|
@@ -23,7 +24,8 @@ export default {
|
|
|
23
24
|
FixedBanner,
|
|
24
25
|
GrowlManager,
|
|
25
26
|
AwsComplianceBanner,
|
|
26
|
-
AzureWarning
|
|
27
|
+
AzureWarning,
|
|
28
|
+
Inactivity
|
|
27
29
|
},
|
|
28
30
|
|
|
29
31
|
middleware: ['authenticated'],
|
|
@@ -83,6 +85,7 @@ export default {
|
|
|
83
85
|
|
|
84
86
|
<FixedBanner :footer="true" />
|
|
85
87
|
<GrowlManager />
|
|
88
|
+
<Inactivity />
|
|
86
89
|
</div>
|
|
87
90
|
</template>
|
|
88
91
|
|
|
@@ -393,6 +393,30 @@ export default class ProvCluster extends SteveModel {
|
|
|
393
393
|
}
|
|
394
394
|
}
|
|
395
395
|
|
|
396
|
+
get machinePoolDefaults() {
|
|
397
|
+
return this.spec.rkeConfig?.machinePoolDefaults;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
set defaultHostnameLengthLimit(value) {
|
|
401
|
+
this.spec.rkeConfig = this.spec.rkeConfig || {};
|
|
402
|
+
this.spec.rkeConfig.machinePoolDefaults = this.spec.rkeConfig.machinePoolDefaults || {};
|
|
403
|
+
this.spec.rkeConfig.machinePoolDefaults.hostnameLengthLimit = value;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
get defaultHostnameLengthLimit() {
|
|
407
|
+
return this.spec.rkeConfig?.machinePoolDefaults?.hostnameLengthLimit;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
removeDefaultHostnameLengthLimit() {
|
|
411
|
+
if (this.machinePoolDefaults?.hostnameLengthLimit) {
|
|
412
|
+
delete this.spec.rkeConfig.machinePoolDefaults.hostnameLengthLimit;
|
|
413
|
+
|
|
414
|
+
if (Object.keys(this.spec?.rkeConfig?.machinePoolDefaults).length === 0) {
|
|
415
|
+
delete this.spec.rkeConfig.machinePoolDefaults;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
396
420
|
get nodes() {
|
|
397
421
|
return this.$rootGetters['management/all'](MANAGEMENT.NODE).filter(node => node.id.startsWith(this.mgmtClusterId));
|
|
398
422
|
}
|
package/package.json
CHANGED
|
@@ -23,7 +23,6 @@ import {
|
|
|
23
23
|
WORKLOAD_TYPES,
|
|
24
24
|
COUNT,
|
|
25
25
|
CATALOG,
|
|
26
|
-
POD,
|
|
27
26
|
PSP,
|
|
28
27
|
} from '@shell/config/types';
|
|
29
28
|
import { mapPref, CLUSTER_TOOLS_TIP, PSP_DEPRECATION_BANNER } from '@shell/store/prefs';
|
|
@@ -264,11 +263,9 @@ export default {
|
|
|
264
263
|
},
|
|
265
264
|
|
|
266
265
|
podsUsed() {
|
|
267
|
-
const pods = resourceCounts(this.$store, POD);
|
|
268
|
-
|
|
269
266
|
return {
|
|
270
267
|
total: parseSi(this.currentCluster?.status?.allocatable?.pods || '0'),
|
|
271
|
-
useful: pods
|
|
268
|
+
useful: parseSi(this.currentCluster?.status?.requested?.pods || '0'),
|
|
272
269
|
};
|
|
273
270
|
},
|
|
274
271
|
|
|
@@ -23,13 +23,17 @@ export default {
|
|
|
23
23
|
async fetch() {
|
|
24
24
|
try {
|
|
25
25
|
this.uiPerfSetting = await this.$store.dispatch('management/find', { type: MANAGEMENT.SETTING, id: SETTING.UI_PERFORMANCE });
|
|
26
|
-
} catch
|
|
26
|
+
} catch {
|
|
27
27
|
this.uiPerfSetting = await this.$store.dispatch('management/create', { type: MANAGEMENT.SETTING }, { root: true });
|
|
28
28
|
// Setting does not exist - create a new one
|
|
29
29
|
this.uiPerfSetting.value = JSON.stringify(DEFAULT_PERF_SETTING);
|
|
30
30
|
this.uiPerfSetting.metadata = { name: SETTING.UI_PERFORMANCE };
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
try {
|
|
34
|
+
this.authUserTTL = await this.$store.dispatch(`management/find`, { type: MANAGEMENT.SETTING, id: SETTING.AUTH_USER_SESSION_TTL_MINUTES });
|
|
35
|
+
} catch {}
|
|
36
|
+
|
|
33
37
|
const sValue = this.uiPerfSetting?.value || JSON.stringify(DEFAULT_PERF_SETTING);
|
|
34
38
|
|
|
35
39
|
this.value = {
|
|
@@ -42,11 +46,13 @@ export default {
|
|
|
42
46
|
|
|
43
47
|
data() {
|
|
44
48
|
return {
|
|
45
|
-
uiPerfSetting:
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
49
|
+
uiPerfSetting: DEFAULT_PERF_SETTING,
|
|
50
|
+
authUserTTL: null,
|
|
51
|
+
bannerVal: {},
|
|
52
|
+
value: {},
|
|
53
|
+
errors: [],
|
|
54
|
+
gcStartedEnabled: null,
|
|
55
|
+
isInactivityThresholdValid: false,
|
|
50
56
|
};
|
|
51
57
|
},
|
|
52
58
|
|
|
@@ -56,9 +62,28 @@ export default {
|
|
|
56
62
|
|
|
57
63
|
return schema?.resourceMethods?.includes('PUT') ? _EDIT : _VIEW;
|
|
58
64
|
},
|
|
65
|
+
|
|
66
|
+
canSave() {
|
|
67
|
+
return this.value.inactivity.enabled ? this.isInactivityThresholdValid : true;
|
|
68
|
+
}
|
|
59
69
|
},
|
|
60
70
|
|
|
61
71
|
methods: {
|
|
72
|
+
validateInactivityThreshold(value) {
|
|
73
|
+
if (!this.authUserTTL?.value) {
|
|
74
|
+
this.isInactivityThresholdValid = true;
|
|
75
|
+
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (parseInt(value) > parseInt(this.authUserTTL?.value)) {
|
|
80
|
+
this.isInactivityThresholdValid = false;
|
|
81
|
+
|
|
82
|
+
return this.t('performance.inactivity.authUserTTL', { current: this.authUserTTL.value });
|
|
83
|
+
}
|
|
84
|
+
this.isInactivityThresholdValid = true;
|
|
85
|
+
},
|
|
86
|
+
|
|
62
87
|
async save(btnCB) {
|
|
63
88
|
this.uiPerfSetting.value = JSON.stringify(this.value);
|
|
64
89
|
this.errors = [];
|
|
@@ -89,8 +114,36 @@ export default {
|
|
|
89
114
|
</h1>
|
|
90
115
|
<div>
|
|
91
116
|
<div class="ui-perf-setting">
|
|
92
|
-
<!--
|
|
117
|
+
<!-- Inactivity -->
|
|
93
118
|
<div class="mt-20">
|
|
119
|
+
<h2>{{ t('performance.inactivity.title') }}</h2>
|
|
120
|
+
<p>{{ t('performance.inactivity.description') }}</p>
|
|
121
|
+
<Checkbox
|
|
122
|
+
v-model="value.inactivity.enabled"
|
|
123
|
+
:mode="mode"
|
|
124
|
+
:label="t('performance.inactivity.checkboxLabel')"
|
|
125
|
+
class="mt-10 mb-20"
|
|
126
|
+
:primary="true"
|
|
127
|
+
/>
|
|
128
|
+
<div class="ml-20">
|
|
129
|
+
<LabeledInput
|
|
130
|
+
v-model="value.inactivity.threshold"
|
|
131
|
+
:mode="mode"
|
|
132
|
+
:label="t('performance.inactivity.inputLabel')"
|
|
133
|
+
:disabled="!value.inactivity.enabled"
|
|
134
|
+
class="input mb-10"
|
|
135
|
+
type="number"
|
|
136
|
+
min="0"
|
|
137
|
+
:rules="[validateInactivityThreshold]"
|
|
138
|
+
/>
|
|
139
|
+
<span
|
|
140
|
+
v-clean-html="t('performance.inactivity.information', {}, true)"
|
|
141
|
+
:class="{ 'text-muted': !value.incrementalLoading.enabled }"
|
|
142
|
+
/>
|
|
143
|
+
</div>
|
|
144
|
+
</div>
|
|
145
|
+
<!-- Websocket Notifications -->
|
|
146
|
+
<div class="mt-40">
|
|
94
147
|
<h2>{{ t('performance.websocketNotification.label') }}</h2>
|
|
95
148
|
<p>{{ t('performance.websocketNotification.description') }}</p>
|
|
96
149
|
<Checkbox
|
|
@@ -296,6 +349,7 @@ export default {
|
|
|
296
349
|
<AsyncButton
|
|
297
350
|
class="pull-right mt-20"
|
|
298
351
|
mode="apply"
|
|
352
|
+
:disabled="!canSave"
|
|
299
353
|
@click="save"
|
|
300
354
|
/>
|
|
301
355
|
</div>
|
package/store/index.js
CHANGED
|
@@ -1,32 +1,37 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
COUNT, NAMESPACE, NORMAN, MANAGEMENT, FLEET, UI, VIRTUAL_HARVESTER_PROVIDER, DEFAULT_WORKSPACE
|
|
4
|
-
} from '@shell/config/types';
|
|
5
|
-
import { CLUSTER as CLUSTER_PREF, NAMESPACE_FILTERS, LAST_NAMESPACE, WORKSPACE } from '@shell/store/prefs';
|
|
6
|
-
import { allHash, allHashSettled } from '@shell/utils/promise';
|
|
7
|
-
import { ClusterNotFoundError, ApiError } from '@shell/utils/error';
|
|
8
|
-
import { sortBy } from '@shell/utils/sort';
|
|
9
|
-
import { filterBy, findBy } from '@shell/utils/array';
|
|
10
|
-
import { BOTH, CLUSTER_LEVEL, NAMESPACED } from '@shell/store/type-map';
|
|
11
|
-
import { NAME as EXPLORER } from '@shell/config/product/explorer';
|
|
12
|
-
import { TIMED_OUT, LOGGED_OUT, _FLAGGED, UPGRADED } from '@shell/config/query-params';
|
|
1
|
+
import { BACK_TO } from '@shell/config/local-storage';
|
|
13
2
|
import { setBrand, setVendor } from '@shell/config/private-label';
|
|
14
|
-
import {
|
|
3
|
+
import { NAME as EXPLORER } from '@shell/config/product/explorer';
|
|
4
|
+
import { LOGGED_OUT, TIMED_OUT, UPGRADED, _FLAGGED } from '@shell/config/query-params';
|
|
15
5
|
import { SETTING } from '@shell/config/settings';
|
|
16
|
-
import
|
|
17
|
-
|
|
18
|
-
|
|
6
|
+
import {
|
|
7
|
+
COUNT,
|
|
8
|
+
DEFAULT_WORKSPACE,
|
|
9
|
+
FLEET,
|
|
10
|
+
MANAGEMENT,
|
|
11
|
+
NAMESPACE, NORMAN,
|
|
12
|
+
UI, VIRTUAL_HARVESTER_PROVIDER
|
|
13
|
+
} from '@shell/config/types';
|
|
19
14
|
import { BY_TYPE } from '@shell/plugins/dashboard-store/classify';
|
|
15
|
+
import Steve from '@shell/plugins/steve';
|
|
16
|
+
import { STEVE_MODEL_TYPES } from '@shell/plugins/steve/getters';
|
|
17
|
+
import { CLUSTER as CLUSTER_PREF, LAST_NAMESPACE, NAMESPACE_FILTERS, WORKSPACE } from '@shell/store/prefs';
|
|
18
|
+
import { BOTH, CLUSTER_LEVEL, NAMESPACED } from '@shell/store/type-map';
|
|
19
|
+
import { filterBy, findBy } from '@shell/utils/array';
|
|
20
|
+
import { ApiError, ClusterNotFoundError } from '@shell/utils/error';
|
|
21
|
+
import { gcActions, gcGetters } from '@shell/utils/gc/gc-root-store';
|
|
20
22
|
import {
|
|
21
|
-
NAMESPACE_FILTER_ALL_USER as ALL_USER,
|
|
22
|
-
NAMESPACE_FILTER_ALL_SYSTEM as ALL_SYSTEM,
|
|
23
23
|
NAMESPACE_FILTER_ALL_ORPHANS as ALL_ORPHANS,
|
|
24
|
-
|
|
24
|
+
NAMESPACE_FILTER_ALL_SYSTEM as ALL_SYSTEM,
|
|
25
|
+
NAMESPACE_FILTER_ALL_USER as ALL_USER,
|
|
25
26
|
NAMESPACE_FILTER_NAMESPACED_NO as NAMESPACED_NO,
|
|
26
27
|
NAMESPACE_FILTER_NAMESPACED_PREFIX as NAMESPACED_PREFIX,
|
|
28
|
+
NAMESPACE_FILTER_NAMESPACED_YES as NAMESPACED_YES,
|
|
27
29
|
splitNamespaceFilterKey,
|
|
28
30
|
} from '@shell/utils/namespace-filter';
|
|
29
|
-
import {
|
|
31
|
+
import { allHash, allHashSettled } from '@shell/utils/promise';
|
|
32
|
+
import { sortBy } from '@shell/utils/sort';
|
|
33
|
+
import { addParam } from '@shell/utils/url';
|
|
34
|
+
import semver from 'semver';
|
|
30
35
|
|
|
31
36
|
// Disables strict mode for all store instances to prevent warning about changing state outside of mutations
|
|
32
37
|
// because it's more efficient to do that sometimes.
|
|
@@ -591,7 +596,6 @@ export const mutations = {
|
|
|
591
596
|
state.isMultiCluster = isMultiCluster;
|
|
592
597
|
state.isRancher = isRancher;
|
|
593
598
|
},
|
|
594
|
-
|
|
595
599
|
clusterReady(state, ready) {
|
|
596
600
|
state.clusterReady = ready;
|
|
597
601
|
},
|
|
@@ -1122,5 +1126,16 @@ export const actions = {
|
|
|
1122
1126
|
commit(`setIsSingleProduct`, isSingleProduct);
|
|
1123
1127
|
},
|
|
1124
1128
|
|
|
1129
|
+
unsubscribe( { state, dispatch }) {
|
|
1130
|
+
// It would be nice to grab all vuex module stores that we've registered, apparently this is only possible via the
|
|
1131
|
+
// internal properties store._modules.root._children.
|
|
1132
|
+
// So instead loop through all state entries to find stores
|
|
1133
|
+
return Object.entries(state).filter(([storeName, storeState]) => {
|
|
1134
|
+
if (storeState?.allowStreaming) {
|
|
1135
|
+
dispatch(`${ storeName }/unsubscribe`);
|
|
1136
|
+
}
|
|
1137
|
+
});
|
|
1138
|
+
},
|
|
1139
|
+
|
|
1125
1140
|
...gcActions
|
|
1126
1141
|
};
|