web-mojo 2.1.1043 → 2.1.1044
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/README.md +19 -16
- package/dist/admin.cjs.js +1 -1
- package/dist/admin.cjs.js.map +1 -1
- package/dist/admin.es.js +12 -10
- package/dist/admin.es.js.map +1 -1
- package/dist/auth.cjs.js +1 -1
- package/dist/auth.cjs.js.map +1 -1
- package/dist/auth.css +305 -266
- package/dist/auth.es.js +537 -2175
- package/dist/auth.es.js.map +1 -1
- package/dist/charts.cjs.js +1 -1
- package/dist/charts.es.js +3 -2
- package/dist/charts.es.js.map +1 -1
- package/dist/chunks/ChatView-Bvkdj-lq.js +2 -0
- package/dist/chunks/{ChatView-CGBaudUc.js.map → ChatView-Bvkdj-lq.js.map} +1 -1
- package/dist/chunks/{ChatView-DguKw-gR.js → ChatView-DBgQzOyI.js} +5 -6
- package/dist/chunks/{ChatView-DguKw-gR.js.map → ChatView-DBgQzOyI.js.map} +1 -1
- package/dist/chunks/{Collection-YRfGoT73.js → Collection-DaTm-2LH.js} +2 -2
- package/dist/chunks/{Collection-YRfGoT73.js.map → Collection-DaTm-2LH.js.map} +1 -1
- package/dist/chunks/{ContextMenu-B4_YS0G8.js → ContextMenu-hQH_6Pyi.js} +349 -2
- package/dist/chunks/ContextMenu-hQH_6Pyi.js.map +1 -0
- package/dist/chunks/ContextMenu-snx9Dd1s.js +3 -0
- package/dist/chunks/ContextMenu-snx9Dd1s.js.map +1 -0
- package/dist/chunks/{DataView-OUqaLmGB.js → DataView-CWejLV3B.js} +2 -1
- package/dist/chunks/DataView-CWejLV3B.js.map +1 -0
- package/dist/chunks/DataView-D7j4IWyS.js +2 -0
- package/dist/chunks/DataView-D7j4IWyS.js.map +1 -0
- package/dist/chunks/Dialog-7T8ENHYD.js +2 -0
- package/dist/chunks/Dialog-7T8ENHYD.js.map +1 -0
- package/dist/chunks/{Dialog-BiVgKzSK.js → Dialog-BcJG5Vta.js} +1358 -4
- package/dist/chunks/Dialog-BcJG5Vta.js.map +1 -0
- package/dist/chunks/{ListView-BMNhd5-B.js → ListView-BrsQ26R6.js} +2 -2
- package/dist/chunks/{ListView-BMNhd5-B.js.map → ListView-BrsQ26R6.js.map} +1 -1
- package/dist/chunks/MetricsMiniChartWidget-CN1HPnWf.js +2 -0
- package/dist/chunks/{MetricsMiniChartWidget-Esvv-lFp.js.map → MetricsMiniChartWidget-CN1HPnWf.js.map} +1 -1
- package/dist/chunks/{MetricsMiniChartWidget-CCroU6BZ.js → MetricsMiniChartWidget-DALWxrzu.js} +2 -2
- package/dist/chunks/{MetricsMiniChartWidget-CCroU6BZ.js.map → MetricsMiniChartWidget-DALWxrzu.js.map} +1 -1
- package/dist/chunks/{PDFViewer-NeL91Gon.js → PDFViewer-CgdSGU1n.js} +2 -2
- package/dist/chunks/{PDFViewer-NeL91Gon.js.map → PDFViewer-CgdSGU1n.js.map} +1 -1
- package/dist/chunks/{PDFViewer-D4uo3oiA.js → PDFViewer-DtJIlPXi.js} +2 -2
- package/dist/chunks/{PDFViewer-D4uo3oiA.js.map → PDFViewer-DtJIlPXi.js.map} +1 -1
- package/dist/chunks/{TopNav-DC8oGpHp.js → TokenManager-BanwFrq7.js} +368 -5
- package/dist/chunks/TokenManager-BanwFrq7.js.map +1 -0
- package/dist/chunks/TokenManager-DIEFCQ3B.js +2 -0
- package/dist/chunks/TokenManager-DIEFCQ3B.js.map +1 -0
- package/dist/chunks/version-BaFu2yii.js +38 -0
- package/dist/chunks/version-BaFu2yii.js.map +1 -0
- package/dist/chunks/version-WMgX72-y.js +2 -0
- package/dist/chunks/version-WMgX72-y.js.map +1 -0
- package/dist/css/web-mojo.css +1 -17
- package/dist/docit.cjs.js +1 -1
- package/dist/docit.cjs.js.map +1 -1
- package/dist/docit.es.js +5 -7
- package/dist/docit.es.js.map +1 -1
- package/dist/index.cjs.js +1 -1
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +30 -32
- package/dist/index.es.js.map +1 -1
- package/dist/lightbox.cjs.js +1 -1
- package/dist/lightbox.cjs.js.map +1 -1
- package/dist/lightbox.es.js +4 -3
- package/dist/lightbox.es.js.map +1 -1
- package/dist/map.es.js +1 -1
- package/dist/timeline.es.js +2 -2
- package/package.json +1 -1
- package/dist/chunks/ChatView-CGBaudUc.js +0 -2
- package/dist/chunks/ContextMenu-B4_YS0G8.js.map +0 -1
- package/dist/chunks/ContextMenu-DcLhcYMp.js +0 -3
- package/dist/chunks/ContextMenu-DcLhcYMp.js.map +0 -1
- package/dist/chunks/DataView-CdDY9ijM.js +0 -2
- package/dist/chunks/DataView-CdDY9ijM.js.map +0 -1
- package/dist/chunks/DataView-OUqaLmGB.js.map +0 -1
- package/dist/chunks/Dialog-BiVgKzSK.js.map +0 -1
- package/dist/chunks/Dialog-DmIPK_Bi.js +0 -2
- package/dist/chunks/Dialog-DmIPK_Bi.js.map +0 -1
- package/dist/chunks/MetricsMiniChartWidget-Esvv-lFp.js +0 -2
- package/dist/chunks/Page-CvbwEoLv.js +0 -2
- package/dist/chunks/Page-CvbwEoLv.js.map +0 -1
- package/dist/chunks/Page-Deq4y2Kq.js +0 -351
- package/dist/chunks/Page-Deq4y2Kq.js.map +0 -1
- package/dist/chunks/TokenManager-CAZNcCMs.js +0 -366
- package/dist/chunks/TokenManager-CAZNcCMs.js.map +0 -1
- package/dist/chunks/TokenManager-CJBYcVqs.js +0 -2
- package/dist/chunks/TokenManager-CJBYcVqs.js.map +0 -1
- package/dist/chunks/TopNav-23B5R-dl.js +0 -2
- package/dist/chunks/TopNav-23B5R-dl.js.map +0 -1
- package/dist/chunks/TopNav-DC8oGpHp.js.map +0 -1
- package/dist/chunks/WebApp-C1vcdSuu.js +0 -1388
- package/dist/chunks/WebApp-C1vcdSuu.js.map +0 -1
- package/dist/chunks/WebApp-CpxtmTk0.js +0 -2
- package/dist/chunks/WebApp-CpxtmTk0.js.map +0 -1
|
@@ -1,366 +0,0 @@
|
|
|
1
|
-
class Token {
|
|
2
|
-
constructor(token) {
|
|
3
|
-
this.token = token;
|
|
4
|
-
this.payload = null;
|
|
5
|
-
this.uid = null;
|
|
6
|
-
this.email = null;
|
|
7
|
-
this.name = null;
|
|
8
|
-
this.exp = null;
|
|
9
|
-
this.iat = null;
|
|
10
|
-
this.isValidToken = false;
|
|
11
|
-
this._decode();
|
|
12
|
-
}
|
|
13
|
-
/**
|
|
14
|
-
* Decode JWT token payload (client-side only, no verification)
|
|
15
|
-
* @private
|
|
16
|
-
*/
|
|
17
|
-
_decode() {
|
|
18
|
-
if (!this.token || typeof this.token !== "string") {
|
|
19
|
-
return;
|
|
20
|
-
}
|
|
21
|
-
try {
|
|
22
|
-
const parts = this.token.split(".");
|
|
23
|
-
if (parts.length !== 3) {
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
const payload = parts[1];
|
|
27
|
-
let base64 = payload.replace(/-/g, "+").replace(/_/g, "/");
|
|
28
|
-
const padding = 4 - base64.length % 4;
|
|
29
|
-
if (padding !== 4) {
|
|
30
|
-
base64 += "=".repeat(padding);
|
|
31
|
-
}
|
|
32
|
-
const decoded = atob(base64);
|
|
33
|
-
this.payload = JSON.parse(decoded);
|
|
34
|
-
this.uid = this.payload.uid || this.payload.sub || this.payload.user_id || null;
|
|
35
|
-
this.email = this.payload.email || null;
|
|
36
|
-
this.name = this.payload.name || this.payload.username || null;
|
|
37
|
-
this.exp = this.payload.exp ? new Date(this.payload.exp * 1e3) : null;
|
|
38
|
-
this.iat = this.payload.iat ? new Date(this.payload.iat * 1e3) : null;
|
|
39
|
-
this.isValidToken = this._checkValidity();
|
|
40
|
-
} catch (error) {
|
|
41
|
-
this.payload = null;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* Check token validity
|
|
46
|
-
* @private
|
|
47
|
-
* @returns {boolean} True if token is valid
|
|
48
|
-
*/
|
|
49
|
-
_checkValidity() {
|
|
50
|
-
if (!this.token || !this.payload) {
|
|
51
|
-
return false;
|
|
52
|
-
}
|
|
53
|
-
if (this.payload.exp) {
|
|
54
|
-
const now = Math.floor(Date.now() / 1e3);
|
|
55
|
-
return now < this.payload.exp;
|
|
56
|
-
}
|
|
57
|
-
return true;
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* Decode JWT token payload (client-side only, no verification)
|
|
61
|
-
* @returns {object|null} Decoded payload or null if invalid
|
|
62
|
-
*/
|
|
63
|
-
decode() {
|
|
64
|
-
return this.payload;
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* Get user ID from token
|
|
68
|
-
* @returns {string|null} User ID or null if not found
|
|
69
|
-
*/
|
|
70
|
-
getUserId() {
|
|
71
|
-
return this.uid;
|
|
72
|
-
}
|
|
73
|
-
/**
|
|
74
|
-
* Check if token is valid (exists and not expired)
|
|
75
|
-
* @returns {boolean} True if token is valid
|
|
76
|
-
*/
|
|
77
|
-
isValid() {
|
|
78
|
-
return this.isValidToken;
|
|
79
|
-
}
|
|
80
|
-
/**
|
|
81
|
-
* Check if token will expire soon
|
|
82
|
-
* @param {number} thresholdMinutes - Minutes before expiry to consider "soon"
|
|
83
|
-
* @returns {boolean} True if expiring soon
|
|
84
|
-
*/
|
|
85
|
-
isExpiringSoon(thresholdMinutes = 5) {
|
|
86
|
-
if (!this.payload?.exp) {
|
|
87
|
-
return false;
|
|
88
|
-
}
|
|
89
|
-
const now = Math.floor(Date.now() / 1e3);
|
|
90
|
-
const threshold = thresholdMinutes * 60;
|
|
91
|
-
return this.payload.exp - now <= threshold;
|
|
92
|
-
}
|
|
93
|
-
/**
|
|
94
|
-
* Check if token is expired
|
|
95
|
-
* @returns {boolean} True if expired
|
|
96
|
-
*/
|
|
97
|
-
isExpired() {
|
|
98
|
-
if (!this.payload?.exp) {
|
|
99
|
-
return false;
|
|
100
|
-
}
|
|
101
|
-
const now = Math.floor(Date.now() / 1e3);
|
|
102
|
-
return now >= this.payload.exp;
|
|
103
|
-
}
|
|
104
|
-
/**
|
|
105
|
-
* Get token age in minutes
|
|
106
|
-
* @returns {number|null} Age in minutes since token was issued, or null if no iat
|
|
107
|
-
*/
|
|
108
|
-
getAgeMinutes() {
|
|
109
|
-
if (!this.payload?.iat) {
|
|
110
|
-
return null;
|
|
111
|
-
}
|
|
112
|
-
const now = Math.floor(Date.now() / 1e3);
|
|
113
|
-
const ageSeconds = now - this.payload.iat;
|
|
114
|
-
return Math.floor(ageSeconds / 60);
|
|
115
|
-
}
|
|
116
|
-
/**
|
|
117
|
-
* Get authorization header value
|
|
118
|
-
* @returns {string|null} Bearer token string or null if no token
|
|
119
|
-
*/
|
|
120
|
-
getAuthHeader() {
|
|
121
|
-
return this.token ? `Bearer ${this.token}` : null;
|
|
122
|
-
}
|
|
123
|
-
/**
|
|
124
|
-
* Get basic user info from token
|
|
125
|
-
* @returns {object|null} User info or null
|
|
126
|
-
*/
|
|
127
|
-
getUserInfo() {
|
|
128
|
-
if (!this.payload) {
|
|
129
|
-
return null;
|
|
130
|
-
}
|
|
131
|
-
return {
|
|
132
|
-
uid: this.uid,
|
|
133
|
-
email: this.email,
|
|
134
|
-
name: this.name,
|
|
135
|
-
exp: this.exp,
|
|
136
|
-
iat: this.iat
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
class TokenManager {
|
|
141
|
-
constructor() {
|
|
142
|
-
this.tokenKey = "access_token";
|
|
143
|
-
this.refreshTokenKey = "refresh_token";
|
|
144
|
-
this.tokenInstance = null;
|
|
145
|
-
}
|
|
146
|
-
/**
|
|
147
|
-
* Store authentication tokens
|
|
148
|
-
* @param {string} token - Access token
|
|
149
|
-
* @param {string} refreshToken - Refresh token (optional)
|
|
150
|
-
* @param {boolean} persistent - Use localStorage if true, sessionStorage if false
|
|
151
|
-
*/
|
|
152
|
-
setTokens(token, refreshToken = null, persistent = true) {
|
|
153
|
-
const storage = persistent ? localStorage : sessionStorage;
|
|
154
|
-
this.tokenInstance = new Token(token);
|
|
155
|
-
if (token) {
|
|
156
|
-
storage.setItem(this.tokenKey, token);
|
|
157
|
-
}
|
|
158
|
-
if (refreshToken) {
|
|
159
|
-
storage.setItem(this.refreshTokenKey, refreshToken);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
/**
|
|
163
|
-
* Get stored access token
|
|
164
|
-
* @returns {string|null} Access token or null if not found
|
|
165
|
-
*/
|
|
166
|
-
getToken() {
|
|
167
|
-
return localStorage.getItem(this.tokenKey) || sessionStorage.getItem(this.tokenKey);
|
|
168
|
-
}
|
|
169
|
-
/**
|
|
170
|
-
* Get stored refresh token
|
|
171
|
-
* @returns {string|null} Refresh token or null if not found
|
|
172
|
-
*/
|
|
173
|
-
getRefreshToken() {
|
|
174
|
-
return localStorage.getItem(this.refreshTokenKey) || sessionStorage.getItem(this.refreshTokenKey);
|
|
175
|
-
}
|
|
176
|
-
/**
|
|
177
|
-
* Clear all stored tokens
|
|
178
|
-
*/
|
|
179
|
-
clearTokens() {
|
|
180
|
-
localStorage.removeItem(this.tokenKey);
|
|
181
|
-
localStorage.removeItem(this.refreshTokenKey);
|
|
182
|
-
sessionStorage.removeItem(this.tokenKey);
|
|
183
|
-
sessionStorage.removeItem(this.refreshTokenKey);
|
|
184
|
-
}
|
|
185
|
-
/**
|
|
186
|
-
* Get Token instance for current stored token
|
|
187
|
-
* @returns {Token|null} Token instance or null if no token
|
|
188
|
-
*/
|
|
189
|
-
getTokenInstance() {
|
|
190
|
-
const currentToken = this.getToken();
|
|
191
|
-
if (!currentToken) {
|
|
192
|
-
this.tokenInstance = null;
|
|
193
|
-
return null;
|
|
194
|
-
}
|
|
195
|
-
if (!this.tokenInstance || this.tokenInstance.token !== currentToken) {
|
|
196
|
-
this.tokenInstance = new Token(currentToken);
|
|
197
|
-
}
|
|
198
|
-
return this.tokenInstance;
|
|
199
|
-
}
|
|
200
|
-
/**
|
|
201
|
-
* Get Token instance for refresh token
|
|
202
|
-
* @returns {Token|null} Token instance or null if no refresh token
|
|
203
|
-
*/
|
|
204
|
-
getRefreshTokenInstance() {
|
|
205
|
-
const currentRefreshToken = this.getRefreshToken();
|
|
206
|
-
if (!currentRefreshToken) {
|
|
207
|
-
this._refreshTokenInstance = null;
|
|
208
|
-
return null;
|
|
209
|
-
}
|
|
210
|
-
if (!this._refreshTokenInstance || this._refreshTokenInstance.token !== currentRefreshToken) {
|
|
211
|
-
this._refreshTokenInstance = new Token(currentRefreshToken);
|
|
212
|
-
}
|
|
213
|
-
return this._refreshTokenInstance;
|
|
214
|
-
}
|
|
215
|
-
/**
|
|
216
|
-
* Decode JWT token payload (client-side only, no verification)
|
|
217
|
-
* @param {string} token - JWT token
|
|
218
|
-
* @returns {object|null} Decoded payload or null if invalid
|
|
219
|
-
*/
|
|
220
|
-
decode(token = null) {
|
|
221
|
-
const jwt = token || this.getToken();
|
|
222
|
-
return new Token(jwt).decode();
|
|
223
|
-
}
|
|
224
|
-
/**
|
|
225
|
-
* Get user ID from token
|
|
226
|
-
* @returns {string|null} User ID or null if not found
|
|
227
|
-
*/
|
|
228
|
-
getUserId() {
|
|
229
|
-
const currentToken = this.getTokenInstance();
|
|
230
|
-
return currentToken ? currentToken.getUserId() : null;
|
|
231
|
-
}
|
|
232
|
-
/**
|
|
233
|
-
* Check if current token is valid (exists and not expired)
|
|
234
|
-
* @returns {boolean} True if token is valid
|
|
235
|
-
*/
|
|
236
|
-
isValid() {
|
|
237
|
-
const currentToken = this.getTokenInstance();
|
|
238
|
-
return currentToken ? currentToken.isValid() : false;
|
|
239
|
-
}
|
|
240
|
-
/**
|
|
241
|
-
* Check if token will expire soon
|
|
242
|
-
* @param {number} thresholdMinutes - Minutes before expiry to consider "soon"
|
|
243
|
-
* @returns {boolean} True if expiring soon
|
|
244
|
-
*/
|
|
245
|
-
isExpiringSoon(thresholdMinutes = 5) {
|
|
246
|
-
const currentToken = this.getTokenInstance();
|
|
247
|
-
return currentToken ? currentToken.isExpiringSoon(thresholdMinutes) : false;
|
|
248
|
-
}
|
|
249
|
-
/**
|
|
250
|
-
* Get authorization header value
|
|
251
|
-
* @returns {string|null} Bearer token string or null if no token
|
|
252
|
-
*/
|
|
253
|
-
getAuthHeader() {
|
|
254
|
-
const currentToken = this.getTokenInstance();
|
|
255
|
-
return currentToken ? currentToken.getAuthHeader() : null;
|
|
256
|
-
}
|
|
257
|
-
/**
|
|
258
|
-
* Get basic user info from token
|
|
259
|
-
* @returns {object|null} User info or null
|
|
260
|
-
*/
|
|
261
|
-
getUserInfo() {
|
|
262
|
-
const currentToken = this.getTokenInstance();
|
|
263
|
-
return currentToken ? currentToken.getUserInfo() : null;
|
|
264
|
-
}
|
|
265
|
-
/**
|
|
266
|
-
* Check current token status and determine what action is needed
|
|
267
|
-
* @returns {object} Status object with action and details
|
|
268
|
-
*/
|
|
269
|
-
checkTokenStatus() {
|
|
270
|
-
const token = this.getTokenInstance();
|
|
271
|
-
const refreshToken = this.getRefreshTokenInstance();
|
|
272
|
-
if (!token || !token.isValid() || token.isExpired()) {
|
|
273
|
-
if (!refreshToken || !refreshToken.isValid() || refreshToken.isExpired()) {
|
|
274
|
-
return {
|
|
275
|
-
action: "logout",
|
|
276
|
-
reason: "Both access and refresh tokens are invalid/expired"
|
|
277
|
-
};
|
|
278
|
-
}
|
|
279
|
-
return {
|
|
280
|
-
action: "refresh",
|
|
281
|
-
reason: "Access token invalid/expired but refresh token valid"
|
|
282
|
-
};
|
|
283
|
-
}
|
|
284
|
-
if (token.isExpiringSoon(10) || token.getAgeMinutes() && token.getAgeMinutes() > 60) {
|
|
285
|
-
if (!refreshToken || !refreshToken.isValid() || refreshToken.isExpired()) {
|
|
286
|
-
return {
|
|
287
|
-
action: "none",
|
|
288
|
-
reason: "Access token expiring but refresh token invalid"
|
|
289
|
-
};
|
|
290
|
-
}
|
|
291
|
-
return {
|
|
292
|
-
action: "refresh",
|
|
293
|
-
reason: "Access token expiring soon or aged"
|
|
294
|
-
};
|
|
295
|
-
}
|
|
296
|
-
return {
|
|
297
|
-
action: "none",
|
|
298
|
-
reason: "All tokens valid and not expiring soon"
|
|
299
|
-
};
|
|
300
|
-
}
|
|
301
|
-
/**
|
|
302
|
-
* Check tokens and take appropriate action
|
|
303
|
-
* @param {object} app - App instance for events and API calls
|
|
304
|
-
* @returns {Promise<boolean>} True if action was taken
|
|
305
|
-
*/
|
|
306
|
-
async checkAndRefreshTokens(app) {
|
|
307
|
-
const status = this.checkTokenStatus();
|
|
308
|
-
switch (status.action) {
|
|
309
|
-
case "logout":
|
|
310
|
-
app.events.emit("auth:unauthorized");
|
|
311
|
-
this.stopAutoRefresh();
|
|
312
|
-
return true;
|
|
313
|
-
case "refresh":
|
|
314
|
-
await this.refreshToken(app);
|
|
315
|
-
return true;
|
|
316
|
-
default:
|
|
317
|
-
return false;
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
startAutoRefresh(app) {
|
|
321
|
-
this.stopAutoRefresh();
|
|
322
|
-
this._tokenWatcher = setInterval(() => {
|
|
323
|
-
this.checkAndRefreshTokens(app);
|
|
324
|
-
}, 6e4);
|
|
325
|
-
}
|
|
326
|
-
stopAutoRefresh() {
|
|
327
|
-
if (this._tokenWatcher) {
|
|
328
|
-
clearInterval(this._tokenWatcher);
|
|
329
|
-
this._tokenWatcher = null;
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
async refreshToken(app) {
|
|
333
|
-
const refreshTokenInstance = this.getRefreshTokenInstance();
|
|
334
|
-
if (!refreshTokenInstance || !refreshTokenInstance.isValid() || refreshTokenInstance.isExpired()) {
|
|
335
|
-
app.events.emit("auth:unauthorized");
|
|
336
|
-
this.stopAutoRefresh();
|
|
337
|
-
return;
|
|
338
|
-
}
|
|
339
|
-
try {
|
|
340
|
-
const response = await app.rest.POST("/api/token/refresh", {
|
|
341
|
-
refresh_token: refreshTokenInstance.token
|
|
342
|
-
});
|
|
343
|
-
const { access_token, refresh_token } = response.data.data;
|
|
344
|
-
this.tokenInstance = null;
|
|
345
|
-
this._refreshTokenInstance = null;
|
|
346
|
-
this.setTokens(access_token, refresh_token);
|
|
347
|
-
app.rest.setAuthToken(access_token);
|
|
348
|
-
app.events.emit("auth:token:refreshed", {
|
|
349
|
-
newToken: access_token,
|
|
350
|
-
newRefreshToken: refresh_token
|
|
351
|
-
});
|
|
352
|
-
console.log("Token refreshed successfully");
|
|
353
|
-
} catch (error) {
|
|
354
|
-
if (error.status === 401 || error.status === 403) {
|
|
355
|
-
app.events.emit("auth:unauthorized");
|
|
356
|
-
this.stopAutoRefresh();
|
|
357
|
-
} else {
|
|
358
|
-
app.events.emit("auth:token:refresh:failed", { error });
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
export {
|
|
364
|
-
TokenManager as T
|
|
365
|
-
};
|
|
366
|
-
//# sourceMappingURL=TokenManager-CAZNcCMs.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"TokenManager-CAZNcCMs.js","sources":["../../src/core/services/TokenManager.js"],"sourcesContent":["/**\n * Token - Individual JWT token handling\n * Handles decoding, validation, and data extraction for a single token\n */\nclass Token {\n constructor(token) {\n this.token = token;\n this.payload = null;\n this.uid = null;\n this.email = null;\n this.name = null;\n this.exp = null;\n this.iat = null;\n this.isValidToken = false;\n\n this._decode();\n }\n\n /**\n * Decode JWT token payload (client-side only, no verification)\n * @private\n */\n _decode() {\n if (!this.token || typeof this.token !== 'string') {\n return;\n }\n\n try {\n const parts = this.token.split('.');\n if (parts.length !== 3) {\n return;\n }\n\n // Decode the payload (second part)\n const payload = parts[1];\n\n // Handle URL-safe base64\n let base64 = payload.replace(/-/g, '+').replace(/_/g, '/');\n const padding = 4 - (base64.length % 4);\n if (padding !== 4) {\n base64 += '='.repeat(padding);\n }\n\n const decoded = atob(base64);\n this.payload = JSON.parse(decoded);\n\n // Extract common properties\n this.uid = this.payload.uid || this.payload.sub || this.payload.user_id || null;\n this.email = this.payload.email || null;\n this.name = this.payload.name || this.payload.username || null;\n this.exp = this.payload.exp ? new Date(this.payload.exp * 1000) : null;\n this.iat = this.payload.iat ? new Date(this.payload.iat * 1000) : null;\n\n // Determine validity\n this.isValidToken = this._checkValidity();\n } catch (error) {\n this.payload = null;\n }\n }\n\n /**\n * Check token validity\n * @private\n * @returns {boolean} True if token is valid\n */\n _checkValidity() {\n if (!this.token || !this.payload) {\n return false;\n }\n\n // Check expiry if present\n if (this.payload.exp) {\n const now = Math.floor(Date.now() / 1000);\n return now < this.payload.exp;\n }\n\n // If no expiry, consider valid\n return true;\n }\n\n /**\n * Decode JWT token payload (client-side only, no verification)\n * @returns {object|null} Decoded payload or null if invalid\n */\n decode() {\n return this.payload;\n }\n\n /**\n * Get user ID from token\n * @returns {string|null} User ID or null if not found\n */\n getUserId() {\n return this.uid;\n }\n\n /**\n * Check if token is valid (exists and not expired)\n * @returns {boolean} True if token is valid\n */\n isValid() {\n return this.isValidToken;\n }\n\n /**\n * Check if token will expire soon\n * @param {number} thresholdMinutes - Minutes before expiry to consider \"soon\"\n * @returns {boolean} True if expiring soon\n */\n isExpiringSoon(thresholdMinutes = 5) {\n if (!this.payload?.exp) {\n return false;\n }\n\n const now = Math.floor(Date.now() / 1000);\n const threshold = thresholdMinutes * 60;\n return (this.payload.exp - now) <= threshold;\n }\n\n /**\n * Check if token is expired\n * @returns {boolean} True if expired\n */\n isExpired() {\n if (!this.payload?.exp) {\n return false;\n }\n\n const now = Math.floor(Date.now() / 1000);\n return now >= this.payload.exp;\n }\n\n /**\n * Get token age in minutes\n * @returns {number|null} Age in minutes since token was issued, or null if no iat\n */\n getAgeMinutes() {\n if (!this.payload?.iat) {\n return null;\n }\n const now = Math.floor(Date.now() / 1000);\n const ageSeconds = now - this.payload.iat;\n return Math.floor(ageSeconds / 60);\n }\n\n /**\n * Get authorization header value\n * @returns {string|null} Bearer token string or null if no token\n */\n getAuthHeader() {\n return this.token ? `Bearer ${this.token}` : null;\n }\n\n /**\n * Get basic user info from token\n * @returns {object|null} User info or null\n */\n getUserInfo() {\n if (!this.payload) {\n return null;\n }\n\n return {\n uid: this.uid,\n email: this.email,\n name: this.name,\n exp: this.exp,\n iat: this.iat\n };\n }\n}\n\n/**\n * TokenManager - Simplified JWT token handling for MOJO Auth\n * Focuses on core token operations: storage, validation, and user ID extraction\n */\n\nexport default class TokenManager {\n constructor() {\n this.tokenKey = 'access_token';\n this.refreshTokenKey = 'refresh_token';\n this.tokenInstance = null;\n }\n\n /**\n * Store authentication tokens\n * @param {string} token - Access token\n * @param {string} refreshToken - Refresh token (optional)\n * @param {boolean} persistent - Use localStorage if true, sessionStorage if false\n */\n setTokens(token, refreshToken = null, persistent = true) {\n const storage = persistent ? localStorage : sessionStorage;\n this.tokenInstance = new Token(token);\n if (token) {\n storage.setItem(this.tokenKey, token);\n }\n\n if (refreshToken) {\n storage.setItem(this.refreshTokenKey, refreshToken);\n }\n }\n\n /**\n * Get stored access token\n * @returns {string|null} Access token or null if not found\n */\n getToken() {\n return localStorage.getItem(this.tokenKey) ||\n sessionStorage.getItem(this.tokenKey);\n }\n\n /**\n * Get stored refresh token\n * @returns {string|null} Refresh token or null if not found\n */\n getRefreshToken() {\n return localStorage.getItem(this.refreshTokenKey) ||\n sessionStorage.getItem(this.refreshTokenKey);\n }\n\n /**\n * Clear all stored tokens\n */\n clearTokens() {\n localStorage.removeItem(this.tokenKey);\n localStorage.removeItem(this.refreshTokenKey);\n sessionStorage.removeItem(this.tokenKey);\n sessionStorage.removeItem(this.refreshTokenKey);\n }\n\n /**\n * Get Token instance for current stored token\n * @returns {Token|null} Token instance or null if no token\n */\n getTokenInstance() {\n const currentToken = this.getToken();\n\n // If no token stored, clear instance and return null\n if (!currentToken) {\n this.tokenInstance = null;\n return null;\n }\n\n // If instance doesn't exist or token changed, create new instance\n if (!this.tokenInstance || this.tokenInstance.token !== currentToken) {\n this.tokenInstance = new Token(currentToken);\n }\n\n return this.tokenInstance;\n }\n\n /**\n * Get Token instance for refresh token\n * @returns {Token|null} Token instance or null if no refresh token\n */\n getRefreshTokenInstance() {\n const currentRefreshToken = this.getRefreshToken();\n\n // If no refresh token stored, clear instance and return null\n if (!currentRefreshToken) {\n this._refreshTokenInstance = null;\n return null;\n }\n\n // If instance doesn't exist or token changed, create new instance\n if (!this._refreshTokenInstance || this._refreshTokenInstance.token !== currentRefreshToken) {\n this._refreshTokenInstance = new Token(currentRefreshToken);\n }\n\n return this._refreshTokenInstance;\n }\n\n /**\n * Decode JWT token payload (client-side only, no verification)\n * @param {string} token - JWT token\n * @returns {object|null} Decoded payload or null if invalid\n */\n decode(token = null) {\n const jwt = token || this.getToken();\n return new Token(jwt).decode();\n }\n\n /**\n * Get user ID from token\n * @returns {string|null} User ID or null if not found\n */\n getUserId() {\n const currentToken = this.getTokenInstance();\n return currentToken ? currentToken.getUserId() : null;\n }\n\n /**\n * Check if current token is valid (exists and not expired)\n * @returns {boolean} True if token is valid\n */\n isValid() {\n const currentToken = this.getTokenInstance();\n return currentToken ? currentToken.isValid() : false;\n }\n\n /**\n * Check if token will expire soon\n * @param {number} thresholdMinutes - Minutes before expiry to consider \"soon\"\n * @returns {boolean} True if expiring soon\n */\n isExpiringSoon(thresholdMinutes = 5) {\n const currentToken = this.getTokenInstance();\n return currentToken ? currentToken.isExpiringSoon(thresholdMinutes) : false;\n }\n\n /**\n * Get authorization header value\n * @returns {string|null} Bearer token string or null if no token\n */\n getAuthHeader() {\n const currentToken = this.getTokenInstance();\n return currentToken ? currentToken.getAuthHeader() : null;\n }\n\n /**\n * Get basic user info from token\n * @returns {object|null} User info or null\n */\n getUserInfo() {\n const currentToken = this.getTokenInstance();\n return currentToken ? currentToken.getUserInfo() : null;\n }\n\n /**\n * Check current token status and determine what action is needed\n * @returns {object} Status object with action and details\n */\n checkTokenStatus() {\n const token = this.getTokenInstance();\n const refreshToken = this.getRefreshTokenInstance();\n\n // If no access token or it's invalid/expired\n if (!token || !token.isValid() || token.isExpired()) {\n // Check if refresh is possible\n if (!refreshToken || !refreshToken.isValid() || refreshToken.isExpired()) {\n return {\n action: 'logout',\n reason: 'Both access and refresh tokens are invalid/expired'\n };\n }\n\n return {\n action: 'refresh',\n reason: 'Access token invalid/expired but refresh token valid'\n };\n }\n\n // Access token is valid - check if it needs refreshing soon\n if (token.isExpiringSoon(10) || (token.getAgeMinutes() && token.getAgeMinutes() > 60)) {\n // Only suggest refresh if refresh token is still valid\n if (!refreshToken || !refreshToken.isValid() || refreshToken.isExpired()) {\n return {\n action: 'none',\n reason: 'Access token expiring but refresh token invalid'\n };\n }\n\n return {\n action: 'refresh',\n reason: 'Access token expiring soon or aged'\n };\n }\n\n return {\n action: 'none',\n reason: 'All tokens valid and not expiring soon'\n };\n }\n\n /**\n * Check tokens and take appropriate action\n * @param {object} app - App instance for events and API calls\n * @returns {Promise<boolean>} True if action was taken\n */\n async checkAndRefreshTokens(app) {\n const status = this.checkTokenStatus();\n\n switch (status.action) {\n case 'logout':\n app.events.emit(\"auth:unauthorized\");\n this.stopAutoRefresh();\n return true;\n\n case 'refresh':\n await this.refreshToken(app);\n return true;\n\n default:\n return false;\n }\n }\n\n startAutoRefresh(app) {\n this.stopAutoRefresh();\n this._tokenWatcher = setInterval(() => {\n this.checkAndRefreshTokens(app);\n }, 60000);\n }\n\n stopAutoRefresh() {\n if (this._tokenWatcher) {\n clearInterval(this._tokenWatcher);\n this._tokenWatcher = null;\n }\n }\n\n async refreshToken(app) {\n const refreshTokenInstance = this.getRefreshTokenInstance();\n\n // Double-check refresh token validity before attempting refresh\n if (!refreshTokenInstance || !refreshTokenInstance.isValid() || refreshTokenInstance.isExpired()) {\n\n app.events.emit(\"auth:unauthorized\");\n this.stopAutoRefresh();\n return;\n }\n\n try {\n\n const response = await app.rest.POST('/api/token/refresh', {\n refresh_token: refreshTokenInstance.token\n });\n\n const { access_token, refresh_token } = response.data.data;\n\n // Clear old cached instances so new tokens are loaded\n this.tokenInstance = null;\n this._refreshTokenInstance = null;\n\n // Store new tokens\n this.setTokens(access_token, refresh_token);\n app.rest.setAuthToken(access_token);\n\n // Emit success event\n app.events.emit('auth:token:refreshed', {\n newToken: access_token,\n newRefreshToken: refresh_token\n });\n\n console.log('Token refreshed successfully');\n\n } catch (error) {\n\n\n // Check if it's an authentication error (refresh token invalid)\n if (error.status === 401 || error.status === 403) {\n\n app.events.emit(\"auth:unauthorized\");\n this.stopAutoRefresh();\n } else {\n // For other errors, emit specific event but don't logout\n app.events.emit('auth:token:refresh:failed', { error });\n }\n }\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
"use strict";class Token{constructor(e){this.token=e,this.payload=null,this.uid=null,this.email=null,this.name=null,this.exp=null,this.iat=null,this.isValidToken=!1,this._decode()}_decode(){if(this.token&&"string"==typeof this.token)try{const e=this.token.split(".");if(3!==e.length)return;let t=e[1].replace(/-/g,"+").replace(/_/g,"/");const s=4-t.length%4;4!==s&&(t+="=".repeat(s));const n=atob(t);this.payload=JSON.parse(n),this.uid=this.payload.uid||this.payload.sub||this.payload.user_id||null,this.email=this.payload.email||null,this.name=this.payload.name||this.payload.username||null,this.exp=this.payload.exp?new Date(1e3*this.payload.exp):null,this.iat=this.payload.iat?new Date(1e3*this.payload.iat):null,this.isValidToken=this._checkValidity()}catch(e){this.payload=null}}_checkValidity(){return!(!this.token||!this.payload)&&(!this.payload.exp||Math.floor(Date.now()/1e3)<this.payload.exp)}decode(){return this.payload}getUserId(){return this.uid}isValid(){return this.isValidToken}isExpiringSoon(e=5){if(!this.payload?.exp)return!1;const t=Math.floor(Date.now()/1e3),s=60*e;return this.payload.exp-t<=s}isExpired(){return!!this.payload?.exp&&Math.floor(Date.now()/1e3)>=this.payload.exp}getAgeMinutes(){if(!this.payload?.iat)return null;const e=Math.floor(Date.now()/1e3)-this.payload.iat;return Math.floor(e/60)}getAuthHeader(){return this.token?`Bearer ${this.token}`:null}getUserInfo(){return this.payload?{uid:this.uid,email:this.email,name:this.name,exp:this.exp,iat:this.iat}:null}}exports.TokenManager=class{constructor(){this.tokenKey="access_token",this.refreshTokenKey="refresh_token",this.tokenInstance=null}setTokens(e,t=null,s=!0){const n=s?localStorage:sessionStorage;this.tokenInstance=new Token(e),e&&n.setItem(this.tokenKey,e),t&&n.setItem(this.refreshTokenKey,t)}getToken(){return localStorage.getItem(this.tokenKey)||sessionStorage.getItem(this.tokenKey)}getRefreshToken(){return localStorage.getItem(this.refreshTokenKey)||sessionStorage.getItem(this.refreshTokenKey)}clearTokens(){localStorage.removeItem(this.tokenKey),localStorage.removeItem(this.refreshTokenKey),sessionStorage.removeItem(this.tokenKey),sessionStorage.removeItem(this.refreshTokenKey)}getTokenInstance(){const e=this.getToken();return e?(this.tokenInstance&&this.tokenInstance.token===e||(this.tokenInstance=new Token(e)),this.tokenInstance):(this.tokenInstance=null,null)}getRefreshTokenInstance(){const e=this.getRefreshToken();return e?(this._refreshTokenInstance&&this._refreshTokenInstance.token===e||(this._refreshTokenInstance=new Token(e)),this._refreshTokenInstance):(this._refreshTokenInstance=null,null)}decode(e=null){const t=e||this.getToken();return new Token(t).decode()}getUserId(){const e=this.getTokenInstance();return e?e.getUserId():null}isValid(){const e=this.getTokenInstance();return!!e&&e.isValid()}isExpiringSoon(e=5){const t=this.getTokenInstance();return!!t&&t.isExpiringSoon(e)}getAuthHeader(){const e=this.getTokenInstance();return e?e.getAuthHeader():null}getUserInfo(){const e=this.getTokenInstance();return e?e.getUserInfo():null}checkTokenStatus(){const e=this.getTokenInstance(),t=this.getRefreshTokenInstance();return e&&e.isValid()&&!e.isExpired()?e.isExpiringSoon(10)||e.getAgeMinutes()&&e.getAgeMinutes()>60?t&&t.isValid()&&!t.isExpired()?{action:"refresh",reason:"Access token expiring soon or aged"}:{action:"none",reason:"Access token expiring but refresh token invalid"}:{action:"none",reason:"All tokens valid and not expiring soon"}:t&&t.isValid()&&!t.isExpired()?{action:"refresh",reason:"Access token invalid/expired but refresh token valid"}:{action:"logout",reason:"Both access and refresh tokens are invalid/expired"}}async checkAndRefreshTokens(e){switch(this.checkTokenStatus().action){case"logout":return e.events.emit("auth:unauthorized"),this.stopAutoRefresh(),!0;case"refresh":return await this.refreshToken(e),!0;default:return!1}}startAutoRefresh(e){this.stopAutoRefresh(),this._tokenWatcher=setInterval(()=>{this.checkAndRefreshTokens(e)},6e4)}stopAutoRefresh(){this._tokenWatcher&&(clearInterval(this._tokenWatcher),this._tokenWatcher=null)}async refreshToken(e){const t=this.getRefreshTokenInstance();if(!t||!t.isValid()||t.isExpired())return e.events.emit("auth:unauthorized"),void this.stopAutoRefresh();try{const s=await e.rest.POST("/api/token/refresh",{refresh_token:t.token}),{access_token:n,refresh_token:o}=s.data.data;this.tokenInstance=null,this._refreshTokenInstance=null,this.setTokens(n,o),e.rest.setAuthToken(n),e.events.emit("auth:token:refreshed",{newToken:n,newRefreshToken:o})}catch(s){401===s.status||403===s.status?(e.events.emit("auth:unauthorized"),this.stopAutoRefresh()):e.events.emit("auth:token:refresh:failed",{error:s})}}};
|
|
2
|
-
//# sourceMappingURL=TokenManager-CJBYcVqs.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"TokenManager-CJBYcVqs.js","sources":["../../src/core/services/TokenManager.js"],"sourcesContent":["/**\n * Token - Individual JWT token handling\n * Handles decoding, validation, and data extraction for a single token\n */\nclass Token {\n constructor(token) {\n this.token = token;\n this.payload = null;\n this.uid = null;\n this.email = null;\n this.name = null;\n this.exp = null;\n this.iat = null;\n this.isValidToken = false;\n\n this._decode();\n }\n\n /**\n * Decode JWT token payload (client-side only, no verification)\n * @private\n */\n _decode() {\n if (!this.token || typeof this.token !== 'string') {\n return;\n }\n\n try {\n const parts = this.token.split('.');\n if (parts.length !== 3) {\n return;\n }\n\n // Decode the payload (second part)\n const payload = parts[1];\n\n // Handle URL-safe base64\n let base64 = payload.replace(/-/g, '+').replace(/_/g, '/');\n const padding = 4 - (base64.length % 4);\n if (padding !== 4) {\n base64 += '='.repeat(padding);\n }\n\n const decoded = atob(base64);\n this.payload = JSON.parse(decoded);\n\n // Extract common properties\n this.uid = this.payload.uid || this.payload.sub || this.payload.user_id || null;\n this.email = this.payload.email || null;\n this.name = this.payload.name || this.payload.username || null;\n this.exp = this.payload.exp ? new Date(this.payload.exp * 1000) : null;\n this.iat = this.payload.iat ? new Date(this.payload.iat * 1000) : null;\n\n // Determine validity\n this.isValidToken = this._checkValidity();\n } catch (error) {\n this.payload = null;\n }\n }\n\n /**\n * Check token validity\n * @private\n * @returns {boolean} True if token is valid\n */\n _checkValidity() {\n if (!this.token || !this.payload) {\n return false;\n }\n\n // Check expiry if present\n if (this.payload.exp) {\n const now = Math.floor(Date.now() / 1000);\n return now < this.payload.exp;\n }\n\n // If no expiry, consider valid\n return true;\n }\n\n /**\n * Decode JWT token payload (client-side only, no verification)\n * @returns {object|null} Decoded payload or null if invalid\n */\n decode() {\n return this.payload;\n }\n\n /**\n * Get user ID from token\n * @returns {string|null} User ID or null if not found\n */\n getUserId() {\n return this.uid;\n }\n\n /**\n * Check if token is valid (exists and not expired)\n * @returns {boolean} True if token is valid\n */\n isValid() {\n return this.isValidToken;\n }\n\n /**\n * Check if token will expire soon\n * @param {number} thresholdMinutes - Minutes before expiry to consider \"soon\"\n * @returns {boolean} True if expiring soon\n */\n isExpiringSoon(thresholdMinutes = 5) {\n if (!this.payload?.exp) {\n return false;\n }\n\n const now = Math.floor(Date.now() / 1000);\n const threshold = thresholdMinutes * 60;\n return (this.payload.exp - now) <= threshold;\n }\n\n /**\n * Check if token is expired\n * @returns {boolean} True if expired\n */\n isExpired() {\n if (!this.payload?.exp) {\n return false;\n }\n\n const now = Math.floor(Date.now() / 1000);\n return now >= this.payload.exp;\n }\n\n /**\n * Get token age in minutes\n * @returns {number|null} Age in minutes since token was issued, or null if no iat\n */\n getAgeMinutes() {\n if (!this.payload?.iat) {\n return null;\n }\n const now = Math.floor(Date.now() / 1000);\n const ageSeconds = now - this.payload.iat;\n return Math.floor(ageSeconds / 60);\n }\n\n /**\n * Get authorization header value\n * @returns {string|null} Bearer token string or null if no token\n */\n getAuthHeader() {\n return this.token ? `Bearer ${this.token}` : null;\n }\n\n /**\n * Get basic user info from token\n * @returns {object|null} User info or null\n */\n getUserInfo() {\n if (!this.payload) {\n return null;\n }\n\n return {\n uid: this.uid,\n email: this.email,\n name: this.name,\n exp: this.exp,\n iat: this.iat\n };\n }\n}\n\n/**\n * TokenManager - Simplified JWT token handling for MOJO Auth\n * Focuses on core token operations: storage, validation, and user ID extraction\n */\n\nexport default class TokenManager {\n constructor() {\n this.tokenKey = 'access_token';\n this.refreshTokenKey = 'refresh_token';\n this.tokenInstance = null;\n }\n\n /**\n * Store authentication tokens\n * @param {string} token - Access token\n * @param {string} refreshToken - Refresh token (optional)\n * @param {boolean} persistent - Use localStorage if true, sessionStorage if false\n */\n setTokens(token, refreshToken = null, persistent = true) {\n const storage = persistent ? localStorage : sessionStorage;\n this.tokenInstance = new Token(token);\n if (token) {\n storage.setItem(this.tokenKey, token);\n }\n\n if (refreshToken) {\n storage.setItem(this.refreshTokenKey, refreshToken);\n }\n }\n\n /**\n * Get stored access token\n * @returns {string|null} Access token or null if not found\n */\n getToken() {\n return localStorage.getItem(this.tokenKey) ||\n sessionStorage.getItem(this.tokenKey);\n }\n\n /**\n * Get stored refresh token\n * @returns {string|null} Refresh token or null if not found\n */\n getRefreshToken() {\n return localStorage.getItem(this.refreshTokenKey) ||\n sessionStorage.getItem(this.refreshTokenKey);\n }\n\n /**\n * Clear all stored tokens\n */\n clearTokens() {\n localStorage.removeItem(this.tokenKey);\n localStorage.removeItem(this.refreshTokenKey);\n sessionStorage.removeItem(this.tokenKey);\n sessionStorage.removeItem(this.refreshTokenKey);\n }\n\n /**\n * Get Token instance for current stored token\n * @returns {Token|null} Token instance or null if no token\n */\n getTokenInstance() {\n const currentToken = this.getToken();\n\n // If no token stored, clear instance and return null\n if (!currentToken) {\n this.tokenInstance = null;\n return null;\n }\n\n // If instance doesn't exist or token changed, create new instance\n if (!this.tokenInstance || this.tokenInstance.token !== currentToken) {\n this.tokenInstance = new Token(currentToken);\n }\n\n return this.tokenInstance;\n }\n\n /**\n * Get Token instance for refresh token\n * @returns {Token|null} Token instance or null if no refresh token\n */\n getRefreshTokenInstance() {\n const currentRefreshToken = this.getRefreshToken();\n\n // If no refresh token stored, clear instance and return null\n if (!currentRefreshToken) {\n this._refreshTokenInstance = null;\n return null;\n }\n\n // If instance doesn't exist or token changed, create new instance\n if (!this._refreshTokenInstance || this._refreshTokenInstance.token !== currentRefreshToken) {\n this._refreshTokenInstance = new Token(currentRefreshToken);\n }\n\n return this._refreshTokenInstance;\n }\n\n /**\n * Decode JWT token payload (client-side only, no verification)\n * @param {string} token - JWT token\n * @returns {object|null} Decoded payload or null if invalid\n */\n decode(token = null) {\n const jwt = token || this.getToken();\n return new Token(jwt).decode();\n }\n\n /**\n * Get user ID from token\n * @returns {string|null} User ID or null if not found\n */\n getUserId() {\n const currentToken = this.getTokenInstance();\n return currentToken ? currentToken.getUserId() : null;\n }\n\n /**\n * Check if current token is valid (exists and not expired)\n * @returns {boolean} True if token is valid\n */\n isValid() {\n const currentToken = this.getTokenInstance();\n return currentToken ? currentToken.isValid() : false;\n }\n\n /**\n * Check if token will expire soon\n * @param {number} thresholdMinutes - Minutes before expiry to consider \"soon\"\n * @returns {boolean} True if expiring soon\n */\n isExpiringSoon(thresholdMinutes = 5) {\n const currentToken = this.getTokenInstance();\n return currentToken ? currentToken.isExpiringSoon(thresholdMinutes) : false;\n }\n\n /**\n * Get authorization header value\n * @returns {string|null} Bearer token string or null if no token\n */\n getAuthHeader() {\n const currentToken = this.getTokenInstance();\n return currentToken ? currentToken.getAuthHeader() : null;\n }\n\n /**\n * Get basic user info from token\n * @returns {object|null} User info or null\n */\n getUserInfo() {\n const currentToken = this.getTokenInstance();\n return currentToken ? currentToken.getUserInfo() : null;\n }\n\n /**\n * Check current token status and determine what action is needed\n * @returns {object} Status object with action and details\n */\n checkTokenStatus() {\n const token = this.getTokenInstance();\n const refreshToken = this.getRefreshTokenInstance();\n\n // If no access token or it's invalid/expired\n if (!token || !token.isValid() || token.isExpired()) {\n // Check if refresh is possible\n if (!refreshToken || !refreshToken.isValid() || refreshToken.isExpired()) {\n return {\n action: 'logout',\n reason: 'Both access and refresh tokens are invalid/expired'\n };\n }\n\n return {\n action: 'refresh',\n reason: 'Access token invalid/expired but refresh token valid'\n };\n }\n\n // Access token is valid - check if it needs refreshing soon\n if (token.isExpiringSoon(10) || (token.getAgeMinutes() && token.getAgeMinutes() > 60)) {\n // Only suggest refresh if refresh token is still valid\n if (!refreshToken || !refreshToken.isValid() || refreshToken.isExpired()) {\n return {\n action: 'none',\n reason: 'Access token expiring but refresh token invalid'\n };\n }\n\n return {\n action: 'refresh',\n reason: 'Access token expiring soon or aged'\n };\n }\n\n return {\n action: 'none',\n reason: 'All tokens valid and not expiring soon'\n };\n }\n\n /**\n * Check tokens and take appropriate action\n * @param {object} app - App instance for events and API calls\n * @returns {Promise<boolean>} True if action was taken\n */\n async checkAndRefreshTokens(app) {\n const status = this.checkTokenStatus();\n\n switch (status.action) {\n case 'logout':\n app.events.emit(\"auth:unauthorized\");\n this.stopAutoRefresh();\n return true;\n\n case 'refresh':\n await this.refreshToken(app);\n return true;\n\n default:\n return false;\n }\n }\n\n startAutoRefresh(app) {\n this.stopAutoRefresh();\n this._tokenWatcher = setInterval(() => {\n this.checkAndRefreshTokens(app);\n }, 60000);\n }\n\n stopAutoRefresh() {\n if (this._tokenWatcher) {\n clearInterval(this._tokenWatcher);\n this._tokenWatcher = null;\n }\n }\n\n async refreshToken(app) {\n const refreshTokenInstance = this.getRefreshTokenInstance();\n\n // Double-check refresh token validity before attempting refresh\n if (!refreshTokenInstance || !refreshTokenInstance.isValid() || refreshTokenInstance.isExpired()) {\n\n app.events.emit(\"auth:unauthorized\");\n this.stopAutoRefresh();\n return;\n }\n\n try {\n\n const response = await app.rest.POST('/api/token/refresh', {\n refresh_token: refreshTokenInstance.token\n });\n\n const { access_token, refresh_token } = response.data.data;\n\n // Clear old cached instances so new tokens are loaded\n this.tokenInstance = null;\n this._refreshTokenInstance = null;\n\n // Store new tokens\n this.setTokens(access_token, refresh_token);\n app.rest.setAuthToken(access_token);\n\n // Emit success event\n app.events.emit('auth:token:refreshed', {\n newToken: access_token,\n newRefreshToken: refresh_token\n });\n\n console.log('Token refreshed successfully');\n\n } catch (error) {\n\n\n // Check if it's an authentication error (refresh token invalid)\n if (error.status === 401 || error.status === 403) {\n\n app.events.emit(\"auth:unauthorized\");\n this.stopAutoRefresh();\n } else {\n // For other errors, emit specific event but don't logout\n app.events.emit('auth:token:refresh:failed', { error });\n }\n }\n }\n}\n"],"names":["Token","constructor","token","this","payload","uid","email","name","exp","iat","isValidToken","_decode","parts","split","length","base64","replace","padding","repeat","decoded","atob","JSON","parse","sub","user_id","username","Date","_checkValidity","error","Math","floor","now","decode","getUserId","isValid","isExpiringSoon","thresholdMinutes","threshold","isExpired","getAgeMinutes","ageSeconds","getAuthHeader","getUserInfo","tokenKey","refreshTokenKey","tokenInstance","setTokens","refreshToken","persistent","storage","localStorage","sessionStorage","setItem","getToken","getItem","getRefreshToken","clearTokens","removeItem","getTokenInstance","currentToken","getRefreshTokenInstance","currentRefreshToken","_refreshTokenInstance","jwt","checkTokenStatus","action","reason","checkAndRefreshTokens","app","events","emit","stopAutoRefresh","startAutoRefresh","_tokenWatcher","setInterval","clearInterval","refreshTokenInstance","response","rest","POST","refresh_token","access_token","data","setAuthToken","newToken","newRefreshToken","status"],"mappings":"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"}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
"use strict";const t=require("./Rest-BNYqGlnP.js"),e=require("./Dialog-DmIPK_Bi.js"),i=require("./ContextMenu-DcLhcYMp.js");class ResultsView extends t.View{constructor(t={}){super({className:"search-results-view flex-grow-1 overflow-auto d-flex flex-column",template:'\n <div class="flex-grow-1 overflow-auto">\n {{#data.loading}}\n <div class="text-center p-4">\n <div class="spinner-border spinner-border-sm text-muted" role="status">\n <span class="visually-hidden">Loading...</span>\n </div>\n <div class="mt-2 small text-muted">{{data.loadingText}}</div>\n </div>\n {{/data.loading}}\n\n {{^data.loading}}\n {{#data.items}}\n <div class="simple-search-item position-relative"\n data-action="select-item"\n data-item-index="{{index}}">\n {{{itemContent}}}\n <i class="bi bi-chevron-right position-absolute end-0 top-50 translate-middle-y me-3 text-muted"></i>\n </div>\n {{/data.items}}\n\n {{#data.showNoResults}}\n <div class="text-center p-4">\n <i class="bi bi-search text-muted mb-2" style="font-size: 1.5rem;"></i>\n <div class="text-muted small">{{data.noResultsText}}</div>\n <button type="button"\n class="btn btn-link btn-sm mt-2 p-0"\n data-action="clear-search">\n Clear search\n </button>\n </div>\n {{/data.showNoResults}}\n\n {{#data.showEmpty}}\n <div class="text-center p-4">\n <i class="{{data.emptyIcon}} text-muted mb-2" style="font-size: 2rem;"></i>\n <div class="text-muted small mb-2">{{data.emptyText}}</div>\n {{#data.emptySubtext}}\n <div class="text-muted" style="font-size: 0.75rem;">\n {{data.emptySubtext}}\n </div>\n {{/data.emptySubtext}}\n </div>\n {{/data.showEmpty}}\n {{/data.loading}}\n </div>\n\n {{#data.showResultsCount}}\n <div class="border-top bg-light p-2 text-center">\n <small class="text-muted">\n {{data.filteredCount}} of {{data.totalCount}}\n </small>\n </div>\n {{/data.showResultsCount}}\n ',...t}),this.parentView=t.parentView}async handleActionSelectItem(t,e){t.preventDefault();const i=parseInt(e.getAttribute("data-item-index"));this.parentView&&this.parentView.handleItemSelection(i)}async handleActionClearSearch(t,e){t.preventDefault(),this.parentView&&this.parentView.clearSearch()}}class SimpleSearchView extends t.View{constructor(t={}){super({className:"simple-search-view h-100 d-flex flex-column",template:'\n <div class="p-3 border-bottom bg-light">\n <div class="d-flex justify-content-between align-items-start mb-3">\n <h6 class="text-muted fw-semibold mb-0">\n {{#data.headerIcon}}<i class="{{data.headerIcon}} me-2"></i>{{/data.headerIcon}}\n {{{data.headerText}}}\n </h6>\n {{#data.showExitButton}}\n <button class="btn btn-link p-0 text-muted simple-search-exit-btn"\n type="button"\n data-action="exit-view"\n title="Exit"\n aria-label="Exit view">\n <i class="bi bi-x-lg" aria-hidden="true"></i>\n </button>\n {{/data.showExitButton}}\n </div>\n <div class="position-relative">\n <input type="text"\n class="form-control form-control-sm pe-5"\n placeholder="{{data.searchPlaceholder}}"\n value="{{data.searchValue}}"\n data-filter="live-search"\n data-filter-debounce="{{data.debounceMs}}"\n data-change-action="search-items">\n <button class="btn btn-link p-0 position-absolute top-50 end-0 translate-middle-y me-2 text-muted simple-search-clear-btn"\n type="button"\n data-action="clear-search"\n title="Clear search"\n aria-label="Clear search">\n <i class="bi bi-x-circle-fill" aria-hidden="true"></i>\n </button>\n </div>\n </div>\n\n <div data-container="results"></div>\n\n {{#data.showFooter}}\n <div class="p-3 border-top bg-light">\n <small class="text-muted">\n <i class="{{data.footerIcon}} me-1"></i>\n {{{data.footerContent}}}\n </small>\n </div>\n {{/data.showFooter}}\n ',...t}),this.Collection=t.Collection,this.collection=t.collection,this.itemTemplate=t.itemTemplate||this.getDefaultItemTemplate(),this.searchFields=t.searchFields||["name"],this.collectionParams={size:25,...t.collectionParams},this.headerText=t.headerText||"Select Item",this.headerIcon=t.headerIcon||"bi bi-list",this.searchPlaceholder=t.searchPlaceholder||"Search...",this.loadingText=t.loadingText||"Loading items...",this.noResultsText=t.noResultsText||"No items match your search",this.emptyText=t.emptyText||"No items available",this.emptySubtext=t.emptySubtext||null,this.emptyIcon=t.emptyIcon||"bi bi-inbox",this.footerContent=t.footerContent||null,this.footerIcon=t.footerIcon||"bi bi-info-circle",this.showExitButton=t.showExitButton||!1,this.searchValue="",this.filteredItems=[],this.loading=!1,this.hasSearched=!1,this.searchTimer=null,this.debounceMs=t.debounceMs||300,this.resultsView=new ResultsView({parentView:this}),!this.collection&&this.Collection&&(this.collection=new this.Collection),this.addChild(this.resultsView)}onInit(){this.collection&&this.setupCollection(),this.collection&&!1!==this.options.autoLoad&&this.loadItems()}setupCollection(){Object.assign(this.collection.params,this.collectionParams),this.collection.on("fetch:success",()=>{this.loading=!1,this.updateFilteredItems()}),this.collection.on("fetch:error",()=>{this.loading=!1})}async loadItems(){if(this.collection){this.loading=!0,this.updateResultsView();try{await this.collection.fetch(),this.updateFilteredItems()}catch(t){console.error("Error loading items:",t);const e=this.getApp();e?.showError?.("Failed to load items. Please try again.")}finally{this.loading=!1,this.updateFilteredItems()}}else console.warn("SimpleSearchView: No collection provided")}updateFilteredItems(){if(!this.collection)return void(this.filteredItems=[]);const t=this.collection.toJSON();if(this.searchValue&&this.searchValue.trim()){const e=this.searchValue.toLowerCase().trim();this.filteredItems=t.filter(t=>this.searchFields.some(i=>{const s=this.getNestedValue(t,i);return s&&s.toString().toLowerCase().includes(e)}))}else this.filteredItems=t;this.updateResultsView()}getNestedValue(t,e){return e.split(".").reduce((t,e)=>t?.[e],t)}async getViewData(){return{searchValue:this.searchValue,showFooter:!!this.footerContent,showExitButton:this.showExitButton,debounceMs:this.debounceMs,headerText:this.headerText,headerIcon:this.headerIcon,searchPlaceholder:this.searchPlaceholder,footerContent:this.footerContent,footerIcon:this.footerIcon}}updateResultsView(){if(!this.resultsView)return;const t=this.collection&&this.collection.length()>0,e=this.filteredItems.length>0,i=this.searchValue.length>0,s=this.filteredItems.map((t,e)=>({...t,index:e,itemContent:this.processItemTemplate(t)}));this.resultsView.data={loading:this.loading,items:s,showEmpty:!this.loading&&!t,showNoResults:!this.loading&&t&&!e&&i,showResultsCount:!this.loading&&t,filteredCount:this.filteredItems.length,totalCount:this.collection?.restEnabled?this.collection?.meta?.count||0:this.collection?.length()||0,loadingText:this.loadingText,noResultsText:this.noResultsText,emptyText:this.emptyText,emptySubtext:this.emptySubtext,emptyIcon:this.emptyIcon},this.resultsView.render()}processItemTemplate(t){let e=this.itemTemplate;return e=e.replace(/\{\{(\w+)\}\}/g,(e,i)=>this.getNestedValue(t,i)||""),e}getDefaultItemTemplate(){return'\n <div class="p-3 border-bottom">\n <div class="fw-semibold text-dark">{{name}}</div>\n <small class="text-muted">{{id}}</small>\n </div>\n '}async onPassThruActionSearchItems(t,e){const i=e.value||"";this.searchValue=i,this.hasSearched=!0,this.searchTimer&&clearTimeout(this.searchTimer),this.performSearch()}async performSearch(){const t={...this.collectionParams};this.searchValue&&this.searchValue.length>1&&(t.search=this.searchValue.trim()),this.collection.setParams(t,!0)}handleItemSelection(t){if(isNaN(t)||t<0||t>=this.filteredItems.length)return void console.error("Invalid item index:",t);const e=this.filteredItems[t],i=this.collection?this.collection.get(e.id):null;this.emit("item:selected",{item:e,model:i,index:t})}setCollection(t){return this.collection=t,this.setupCollection(),this}setItemTemplate(t){return this.itemTemplate=t,this.updateResultsView(),this}setSearchFields(t){return this.searchFields=Array.isArray(t)?t:[t],this}async refresh(){await this.loadItems()}focusSearch(){const t=this.element?.querySelector('input[data-action="search-items"]');t&&t.focus()}async handleActionExitView(t,e){this.emit("exit",{view:this})}async handleActionClearSearch(t,e){this.clearSearch()}clearSearch(){this.searchValue="",this.hasSearched=!1;const t=this.element?.querySelector('input[data-change-action="search-items"]');t&&(t.value="",t.focus()),this.performSearch()}getItemCount(){return this.collection?this.collection.length():0}getFilteredItemCount(){return this.filteredItems.length}hasItems(){return this.getItemCount()>0}getSearchValue(){return this.searchValue}setSearchValue(t){this.searchValue=t||"",this.hasSearched=!!this.searchValue;const e=this.element?.querySelector('input[data-action="search-items"]');return e&&(e.value=this.searchValue),this.performSearch(),this}async onAfterRender(){if(await super.onAfterRender(),this.resultsView&&!this.resultsView.isMounted()){const t=this.element?.querySelector('[data-container="results"]');t&&await this.resultsView.render(!0,t)}this.updateResultsView()}async onBeforeDestroy(){this.searchTimer&&clearTimeout(this.searchTimer),this.collection&&this.collection.off("update"),await super.onBeforeDestroy()}}class GroupSelectorButton extends t.View{constructor(t={}){super({tagName:"div",className:"nav-item",...t});const e=this.getApp();this.Collection=t.Collection||e?.GroupCollection||i.GroupList,this.collection=t.collection||new this.Collection,this.currentGroup=void 0!==t.currentGroup?t.currentGroup:e?.activeGroup,this.buttonClass=t.buttonClass||"btn btn-link nav-link",this.buttonIcon=t.buttonIcon||"bi-building",this.defaultText=t.defaultText||"Select Group",this.itemTemplate=t.itemTemplate,this.searchFields=t.searchFields||["name"],this.headerText=t.headerText||"Select Group",this.searchPlaceholder=t.searchPlaceholder||"Search groups...",this.autoSetActiveGroup=!1!==t.autoSetActiveGroup,this.onGroupSelected=t.onGroupSelected,this.dialog=null,e?.events&&e.events.on("group:changed",t=>{t.group!==this.currentGroup&&this.setCurrentGroup(t.group)})}async getTemplate(){return'\n <button class="{{buttonClass}}" \n data-action="show-selector"\n type="button"\n style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">\n <i class="{{buttonIcon}} me-1"></i>\n <span class="group-name">{{displayName}}</span>\n </button>\n '}async onBeforeRender(){await super.onBeforeRender(),this.currentGroup?.get?.("name")||this.currentGroup,this.buttonClass=this.buttonClass,this.buttonIcon=this.buttonIcon,this.displayName=this.currentGroup?.get?.("name")||this.currentGroup?.name||this.defaultText}async onActionShowSelector(t){const i=new SimpleSearchView({Collection:this.Collection,collection:this.collection,itemTemplate:this.itemTemplate||this.getDefaultItemTemplate(),searchFields:this.searchFields,headerText:this.headerText,searchPlaceholder:this.searchPlaceholder,headerIcon:this.buttonIcon,showExitButton:!1});return this.dialog=new e.default({title:this.headerText,body:i,size:"md",scrollable:!0,noBodyPadding:!0,buttons:[],closeButton:!0}),i.on("item:selected",t=>{this.handleGroupSelection(t.model||t.item),this.dialog&&this.dialog.hide()}),this.dialog.on("hidden",()=>{this.dialog.destroy(),this.dialog=null}),await this.dialog.render(!0,document.body),this.dialog.show(),!0}handleGroupSelection(t){this.currentGroup=t,this.displayName=t?.get?.("name")||t?.name||this.defaultText,this.render();const e=this.getApp();this.autoSetActiveGroup&&e?.setActiveGroup&&e.setActiveGroup(t),this.onGroupSelected&&this.onGroupSelected({group:t}),this.emit("group-selected",{group:t}),e?.events&&(e.events.emit("group:selected",{group:t}),e.events.emit("group:changed",{group:t}))}getDefaultItemTemplate(){return'\n <div class="d-flex align-items-center p-3 border-bottom">\n <div class="flex-grow-1">\n <div class="fw-semibold text-dark">{{name}}</div>\n <small class="text-muted">#{{id}} {{kind}}</small>\n </div>\n </div>\n '}setCurrentGroup(t){this.currentGroup=t,this.displayName=t?.get?.("name")||t?.name||this.defaultText,this.mounted&&this.render()}getCurrentGroup(){return this.currentGroup}}class TopNav extends t.View{constructor(t={}){const e={light:"navbar navbar-expand-lg navbar-light topnav-light",dark:"navbar navbar-expand-lg navbar-dark topnav-dark",clean:"navbar navbar-expand-lg navbar-light topnav-clean",gradient:"navbar navbar-expand-lg navbar-dark topnav-gradient"};let i=e[t.theme||"light"]||e.light;t.shadow&&(i+=` topnav-shadow-${t.shadow}`),super({tagName:"nav",className:i,enableTooltips:!0,style:"position: relative; z-index: 1030;",...t}),this.displayMode=t.displayMode||"both",this.showPageIcon=!1!==t.showPageIcon,this.showPageDescription=t.showPageDescription||!1,this.showBreadcrumbs=t.showBreadcrumbs||!1,this.groupIcon=t.groupIcon||"bi-building",this.currentPage=null,this.previousPage=null,this.config={brand:t.brand||"MOJO App",brandIcon:t.brandIcon||"bi bi-play-circle",brandRoute:t.brandRoute||"/",navItems:t.navItems||[],rightItems:t.rightItems||[],showSidebarToggle:t.showSidebarToggle||!1,sidebarToggleAction:t.sidebarToggleAction||"toggle-sidebar",...t},this.userMenu=t.userMenu||this.findMenuItem("user"),this.userMenu&&(this.userMenu.id="user"),this.loginMenu=t.loginMenu||this.findMenuItem("login"),this.setupPageListeners(),this.setupGroupListeners(),this.groupSelectorButton=null,this.currentGroup=null}findMenuItem(t){let e=this.config.navItems.find(e=>e.id===t);return e||(e=this.config.rightItems.find(e=>e.id===t)),e||null}replaceMenuItem(t,e){const i=this.config.navItems.findIndex(e=>e.id===t);if(-1!==i)return this.config.navItems[i]=e,!0;const s=this.config.rightItems.findIndex(e=>e.id===t);return-1!==s&&(this.config.rightItems[s]=e,!0)}setBrand(t,e=null){this.config.brand=t,this.config.brandIcon=e||this.config.brandIcon,this.render()}setUser(t){t?(this.userMenu.label=t.get("display_name"),this.replaceMenuItem("login",this.userMenu)):this.replaceMenuItem("user",this.loginMenu),this.setModel(t)}_onModelChange(){this.model&&(this.userMenu.label=this.model.get("display_name")),this.isMounted()&&this.render()}async getTemplate(){return'\n <div class="container-fluid">\n {{#data.showSidebarToggle}}\n <button class="topnav-sidebar-toggle me-2" data-action="{{data.sidebarToggleAction}}" aria-label="Toggle Sidebar">\n <i class="bi bi-chevron-right toggle-chevron"></i>\n </button>\n {{/data.showSidebarToggle}}\n\n {{#data.showGroupInfo}}\n <div class="navbar-brand d-flex align-items-center">\n {{#data.groupIcon}}<i class="{{data.groupIcon}} me-2"></i>{{/data.groupIcon}}\n <div>\n <span class="topnav-group-name"\n role="button"\n tabindex="0"\n data-action="open-group-selector"\n style="cursor: pointer;">\n {{data.currentGroupName}}\n </span>\n {{#data.showPageTitle}}\n <span class="text-muted mx-2">|</span>\n <span>{{data.currentPageName}}</span>\n {{/data.showPageTitle}}\n </div>\n </div>\n {{/data.showGroupInfo}}\n\n {{#data.showPageInfo}}\n <div class="navbar-brand d-flex align-items-center">\n {{#data.currentPageIcon}}<i class="{{data.currentPageIcon}} me-2"></i>{{/data.currentPageIcon}}\n <div>\n <span>{{data.currentPageName}}</span>\n {{#data.currentPageDescription}}\n <small class="d-block" style="font-size: 0.75rem; line-height: 1;">{{data.currentPageDescription}}</small>\n {{/data.currentPageDescription}}\n </div>\n </div>\n {{/data.showPageInfo}}\n\n {{#data.showBrand}}\n <a class="navbar-brand" href="{{data.brandRoute}}">\n {{#data.brandIcon}}<i class="{{data.brandIcon}} me-2"></i>{{/data.brandIcon}}\n {{data.brand}}\n </a>\n {{/data.showBrand}}\n\n <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#{{data.navbarId}}">\n <span class="navbar-toggler-icon"></span>\n </button>\n\n <div class="collapse navbar-collapse" id="{{data.navbarId}}">\n {{#data.showNavItems}}\n <ul class="navbar-nav me-auto mb-2 mb-lg-0">\n {{#data.navItems}}\n <li class="nav-item">\n <a class="nav-link {{#active}}active{{/active}}" href="{{route}}" {{#tooltip}}data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-title="{{tooltip}}"{{/tooltip}}>\n {{#icon}}<i class="{{icon}} me-1"></i>{{/icon}}\n {{text}}\n </a>\n </li>\n {{/data.navItems}}\n </ul>\n {{/data.showNavItems}}\n\n {{#data.hasRightItems}}\n <div class="navbar-nav ms-auto">\n {{#data.rightItems}}\n {{#isGroupSelector}}\n <div data-container="group-selector-{{id}}"></div>\n {{/isGroupSelector}}\n {{^isGroupSelector}}\n {{#isDropdown}}\n <div class="nav-item dropdown">\n <a class="nav-link dropdown-toggle" role="button" data-bs-toggle="dropdown" aria-expanded="false">\n {{#icon}}<i class="{{icon}} me-1"></i>{{/icon}}\n {{label}}\n </a>\n <ul class="dropdown-menu dropdown-menu-end">\n {{#items}}\n {{#divider}}\n <li><hr class="dropdown-divider"></li>\n {{/divider}}\n {{^divider}}\n <li>\n <a class="dropdown-item" role="button" {{#action}}data-action="{{action}}"{{/action}}>\n {{#icon}}<i class="{{icon}} me-1"></i>{{/icon}}\n {{label}}\n </a>\n </li>\n {{/divider}}\n {{/items}}\n </ul>\n </div>\n {{/isDropdown}}\n {{^isDropdown}}\n {{#isButton}}\n <button class="{{buttonClass}}" data-action="{{action}}" data-id="{{id}}" {{#tooltip}}data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-title="{{tooltip}}"{{/tooltip}}>\n {{#icon}}<i class="{{icon}} me-1"></i>{{/icon}}\n {{label}}\n </button>\n {{/isButton}}\n {{^isButton}}\n <a class="nav-link" href="{{href}}" {{#action}}data-action="{{action}}"{{/action}} {{#tooltip}}data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-title="{{tooltip}}"{{/tooltip}}>\n {{#icon}}<i class="{{icon}} me-1"></i>{{/icon}}\n {{label}}\n </a>\n {{/isButton}}\n {{/isDropdown}}\n {{/isGroupSelector}}\n {{/data.rightItems}}\n </div>\n {{/data.hasRightItems}}\n </div>\n </div>\n '}async onBeforeRender(){await super.onBeforeRender();const t=this.getApp(),e=this.currentGroup||t?.activeGroup,i="group"===this.displayMode||"group_page_titles"===this.displayMode,s="group_page_titles"===this.displayMode,n="page"===this.displayMode||"both"===this.displayMode,a=!i&&!n,o="menu"===this.displayMode||"both"===this.displayMode,r=this.filterItemsByPermissions(this.config.navItems||[]),l=this.processRightItems(this.config.rightItems||[]);this.data={brand:this.config.brand,brandIcon:this.config.brandIcon,brandRoute:this.config.brandRoute,showBrand:a,navbarId:`navbar-${this.id}`,navItems:r,showNavItems:o,rightItems:l,hasRightItems:l.length>0,showGroupInfo:i,showPageTitle:s,currentGroupName:e?.get?.("name")||e?.name||"Select Group",groupIcon:this.groupIcon,showPageInfo:n,currentPageName:this.currentPage?.title||this.currentPage?.name||"",currentPageIcon:this.currentPage?.icon||this.currentPage?.pageIcon||"",currentPageDescription:this.showPageDescription?this.currentPage?.description:"",showSidebarToggle:this.config.showSidebarToggle,sidebarToggleAction:this.config.sidebarToggleAction,displayMode:this.displayMode}}processRightItems(t){return this.filterItemsByPermissions(t).map(t=>{const e={...t};if(t.items&&(e.items=this.filterItemsByPermissions(t.items)),"group-selector"===t.type){e.isGroupSelector=!0,e.isDropdown=!1,e.isButton=!1;const i={containerId:`group-selector-${t.id||"default"}`};void 0!==t.Collection&&(i.Collection=t.Collection),void 0!==t.collection&&(i.collection=t.collection),void 0!==t.currentGroup&&(i.currentGroup=t.currentGroup),void 0!==t.buttonClass&&(i.buttonClass=t.buttonClass),void 0!==t.buttonIcon&&(i.buttonIcon=t.buttonIcon),void 0!==t.defaultText&&(i.defaultText=t.defaultText),void 0!==t.itemTemplate&&(i.itemTemplate=t.itemTemplate),void 0!==t.searchFields&&(i.searchFields=t.searchFields),void 0!==t.headerText&&(i.headerText=t.headerText),void 0!==t.searchPlaceholder&&(i.searchPlaceholder=t.searchPlaceholder),void 0!==t.autoSetActiveGroup&&(i.autoSetActiveGroup=t.autoSetActiveGroup),void 0!==t.onGroupSelected&&(i.onGroupSelected=t.onGroupSelected);const s=new GroupSelectorButton(i);this.groupSelectorButton=s,this.addChild(s)}else e.items&&e.items.length>0?(e.isDropdown=!0,e.isButton=!1):t.buttonClass?(e.isButton=!0,e.isDropdown=!1):(e.isButton=!1,e.isDropdown=!1);return t.handler&&(this.rightItemHandlers=this.rightItemHandlers||/* @__PURE__ */new Map,this.rightItemHandlers.set(t.id,t.handler)),e})}setupPageListeners(){this.getApp().events.on("page:show",t=>{this.onPageChanged(t)})}setupGroupListeners(){const t=this.getApp();t?.events&&t.events.on(["group:changed","group:loaded"],t=>{t?.group&&(this.currentGroup=t.group),"group"!==this.displayMode&&"group_page_titles"!==this.displayMode||this.mounted&&this.render()})}onPageBeforeChange(t){"page"===this.displayMode||this.displayMode}onPageChanged(t){this.previousPage=this.currentPage,this.currentPage=t.page,"page"!==this.displayMode&&"both"!==this.displayMode||this.updatePageDisplay(),"menu"!==this.displayMode&&"both"!==this.displayMode||this.currentPage&&this.currentPage.route&&this.updateActiveItem(this.currentPage.route)}updatePageDisplay(){this.currentPage&&this.mounted&&this.render()}updateActiveItem(t){const e=t=>t?t.startsWith("/")?t:`/${t}`:"/",i=e(t),s=this.data.navItems.map(t=>{const s=e(t.route);let n=!1;return"/"===s&&"/"===i?n=!0:"/"!==s&&"/"!==i&&(n=i.startsWith(s)||i===s),{...t,active:n}});this.updateData({navItems:s},!0)}onPassThruActionProfile(){this.getApp().events.emit("portal:action",{action:"profile"})}onActionSettings(){this.getApp().events.emit("portal:action",{action:"settings"})}onActionLogout(){this.getApp().events.emit("auth:logout",{action:"logout"})}async onActionOpenGroupSelector(t){if(this.groupSelectorButton)return await this.groupSelectorButton.onActionShowSelector(t),!0;const{GroupList:e}=await Promise.resolve().then(()=>require("./ContextMenu-DcLhcYMp.js")).then(t=>t.Group$1),i=new GroupSelectorButton({Collection:e,currentGroup:this.getApp()?.activeGroup});return await i.onActionShowSelector(t),!0}async handleAction(t,e,i){const s=i.getAttribute("data-id");if(s&&this.rightItemHandlers&&this.rightItemHandlers.has(s)){const n=this.rightItemHandlers.get(s);if("function"==typeof n)return await n.call(this,t,e,i)}const n=`onAction${t.charAt(0).toUpperCase()+t.slice(1).replace(/-([a-z])/g,t=>t[1].toUpperCase())}`;if("function"==typeof this[n])return await this[n](e,i);this.emit("action",{action:t,event:e,element:i,topnav:this})}async onActionDefault(t,e,i){if(this.config.navItems)for(const s of this.config.navItems)if(s.action===t&&s.handler)return await s.handler.call(this,t,e,i),!0;if(this.config.rightItems)for(const s of this.config.rightItems){if(s.action===t&&s.handler)return await s.handler.call(this,t,e,i),!0;if(s.items)for(const n of s.items)if(n.action===t&&n.handler)return await n.handler.call(this,t,e,i),!0}return this.getApp().events.emit("portal:action",{action:t,event:e,el:i}),!1}filterItemsByPermissions(t){if(!t)return[];const e=this.getApp(),i=e?.activeUser;return t.filter(t=>!t.permissions||!i||i.hasPermission(t.permissions))}}exports.SimpleSearchView=SimpleSearchView,exports.TopNav=TopNav;
|
|
2
|
-
//# sourceMappingURL=TopNav-23B5R-dl.js.map
|