@coolms/core-angular 2.0.0-alpha.4 → 2.0.0-alpha.6
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/CHANGELOG.md +88 -0
- package/README.md +4 -0
- package/fesm2022/coolms-core-angular.mjs +517 -359
- package/fesm2022/coolms-core-angular.mjs.map +1 -1
- package/package.json +2 -2
- package/types/coolms-core-angular.d.ts +232 -41
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import * as i0 from "@angular/core";
|
|
2
|
-
import { ChangeDetectionStrategy, Component, DestroyRef, Injectable, InjectionToken, computed, effect, inject, input, makeEnvironmentProviders, provideAppInitializer, signal, untracked } from "@angular/core";
|
|
2
|
+
import { ChangeDetectionStrategy, Component, DestroyRef, Injectable, InjectionToken, Injector, computed, effect, inject, input, makeEnvironmentProviders, provideAppInitializer, signal, untracked } from "@angular/core";
|
|
3
3
|
import { HttpClient, HttpContext, HttpContextToken, HttpErrorResponse, HttpParams } from "@angular/common/http";
|
|
4
|
-
import { Action, Selector, State, Store, provideStore } from "@ngxs/store";
|
|
4
|
+
import { Action, Actions, Selector, State, Store, ofActionDispatched, provideStore } from "@ngxs/store";
|
|
5
5
|
import { __decorate } from "tslib";
|
|
6
6
|
import { ReplaySubject, Subject, catchError, debounceTime, defer, finalize, firstValueFrom, from, map, of, shareReplay, switchMap, take, tap, throwError } from "rxjs";
|
|
7
7
|
import { catchError as catchError$1, map as map$1, shareReplay as shareReplay$1, switchMap as switchMap$1, tap as tap$1 } from "rxjs/operators";
|
|
8
8
|
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
|
|
9
|
-
import { Router } from "@angular/router";
|
|
9
|
+
import { ActivatedRoute, Router } from "@angular/router";
|
|
10
10
|
import { Location, NgIf } from "@angular/common";
|
|
11
11
|
import * as i1 from "@angular/forms";
|
|
12
12
|
import { FormsModule } from "@angular/forms";
|
|
@@ -27,6 +27,9 @@ let AppConfigState = class AppConfigState {
|
|
|
27
27
|
loaded: true
|
|
28
28
|
});
|
|
29
29
|
}
|
|
30
|
+
static config(state) {
|
|
31
|
+
return state.config;
|
|
32
|
+
}
|
|
30
33
|
static manifest(state) {
|
|
31
34
|
return state.config?.manifest ?? null;
|
|
32
35
|
}
|
|
@@ -38,7 +41,7 @@ let AppConfigState = class AppConfigState {
|
|
|
38
41
|
}
|
|
39
42
|
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
40
43
|
minVersion: "12.0.0",
|
|
41
|
-
version: "22.2.
|
|
44
|
+
version: "22.2.3",
|
|
42
45
|
ngImport: i0,
|
|
43
46
|
type: AppConfigState,
|
|
44
47
|
deps: [],
|
|
@@ -46,12 +49,13 @@ let AppConfigState = class AppConfigState {
|
|
|
46
49
|
});
|
|
47
50
|
static ɵprov = i0.ɵɵngDeclareInjectable({
|
|
48
51
|
minVersion: "12.0.0",
|
|
49
|
-
version: "22.2.
|
|
52
|
+
version: "22.2.3",
|
|
50
53
|
ngImport: i0,
|
|
51
54
|
type: AppConfigState
|
|
52
55
|
});
|
|
53
56
|
};
|
|
54
57
|
__decorate([Action(SetAppConfig)], AppConfigState.prototype, "set", null);
|
|
58
|
+
__decorate([Selector()], AppConfigState, "config", null);
|
|
55
59
|
__decorate([Selector()], AppConfigState, "manifest", null);
|
|
56
60
|
__decorate([Selector()], AppConfigState, "viewers", null);
|
|
57
61
|
__decorate([Selector()], AppConfigState, "loaded", null);
|
|
@@ -64,7 +68,7 @@ AppConfigState = __decorate([State({
|
|
|
64
68
|
})], AppConfigState);
|
|
65
69
|
i0.ɵɵngDeclareClassMetadata({
|
|
66
70
|
minVersion: "12.0.0",
|
|
67
|
-
version: "22.2.
|
|
71
|
+
version: "22.2.3",
|
|
68
72
|
ngImport: i0,
|
|
69
73
|
type: AppConfigState,
|
|
70
74
|
decorators: [{ type: Injectable }],
|
|
@@ -98,7 +102,7 @@ var IdentityApiClient = class IdentityApiClient {
|
|
|
98
102
|
}
|
|
99
103
|
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
100
104
|
minVersion: "12.0.0",
|
|
101
|
-
version: "22.2.
|
|
105
|
+
version: "22.2.3",
|
|
102
106
|
ngImport: i0,
|
|
103
107
|
type: IdentityApiClient,
|
|
104
108
|
deps: [],
|
|
@@ -106,7 +110,7 @@ var IdentityApiClient = class IdentityApiClient {
|
|
|
106
110
|
});
|
|
107
111
|
static ɵprov = i0.ɵɵngDeclareInjectable({
|
|
108
112
|
minVersion: "12.0.0",
|
|
109
|
-
version: "22.2.
|
|
113
|
+
version: "22.2.3",
|
|
110
114
|
ngImport: i0,
|
|
111
115
|
type: IdentityApiClient,
|
|
112
116
|
providedIn: "root"
|
|
@@ -114,7 +118,7 @@ var IdentityApiClient = class IdentityApiClient {
|
|
|
114
118
|
};
|
|
115
119
|
i0.ɵɵngDeclareClassMetadata({
|
|
116
120
|
minVersion: "12.0.0",
|
|
117
|
-
version: "22.2.
|
|
121
|
+
version: "22.2.3",
|
|
118
122
|
ngImport: i0,
|
|
119
123
|
type: IdentityApiClient,
|
|
120
124
|
decorators: [{
|
|
@@ -122,46 +126,6 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
122
126
|
args: [{ providedIn: "root" }]
|
|
123
127
|
}]
|
|
124
128
|
});
|
|
125
|
-
var RealtimeTokenClient = class RealtimeTokenClient {
|
|
126
|
-
http = inject(HttpClient);
|
|
127
|
-
store = inject(Store);
|
|
128
|
-
get manifest() {
|
|
129
|
-
const m = this.store.selectSnapshot(AppConfigState.manifest);
|
|
130
|
-
if (!m) throw new Error("ApiManifest not loaded — call AppInitService.load() first");
|
|
131
|
-
return m;
|
|
132
|
-
}
|
|
133
|
-
connectionToken() {
|
|
134
|
-
return this.http.post(this.manifest.apiBase + "/centrifugo/connection-token", {});
|
|
135
|
-
}
|
|
136
|
-
subscriptionToken(channel) {
|
|
137
|
-
return this.http.post(this.manifest.apiBase + "/centrifugo/subscription-token", { channel });
|
|
138
|
-
}
|
|
139
|
-
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
140
|
-
minVersion: "12.0.0",
|
|
141
|
-
version: "22.2.0",
|
|
142
|
-
ngImport: i0,
|
|
143
|
-
type: RealtimeTokenClient,
|
|
144
|
-
deps: [],
|
|
145
|
-
target: i0.ɵɵFactoryTarget.Injectable
|
|
146
|
-
});
|
|
147
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({
|
|
148
|
-
minVersion: "12.0.0",
|
|
149
|
-
version: "22.2.0",
|
|
150
|
-
ngImport: i0,
|
|
151
|
-
type: RealtimeTokenClient,
|
|
152
|
-
providedIn: "root"
|
|
153
|
-
});
|
|
154
|
-
};
|
|
155
|
-
i0.ɵɵngDeclareClassMetadata({
|
|
156
|
-
minVersion: "12.0.0",
|
|
157
|
-
version: "22.2.0",
|
|
158
|
-
ngImport: i0,
|
|
159
|
-
type: RealtimeTokenClient,
|
|
160
|
-
decorators: [{
|
|
161
|
-
type: Injectable,
|
|
162
|
-
args: [{ providedIn: "root" }]
|
|
163
|
-
}]
|
|
164
|
-
});
|
|
165
129
|
var Login = class {
|
|
166
130
|
identifier;
|
|
167
131
|
password;
|
|
@@ -191,6 +155,255 @@ var PatchCurrentUser = class {
|
|
|
191
155
|
this.patch = patch;
|
|
192
156
|
}
|
|
193
157
|
};
|
|
158
|
+
const ELEVATION_PROMPT = new InjectionToken("coolms.elevation-prompt");
|
|
159
|
+
const ELEVATION_ACTION = new HttpContextToken(() => null);
|
|
160
|
+
const ELEVATION_NOTICE = new InjectionToken("coolms.elevation-notice");
|
|
161
|
+
const BYPASS_ELEVATION = new HttpContextToken(() => false);
|
|
162
|
+
const CROSS_TAB_KEY = "coolms.elevation.changed";
|
|
163
|
+
const RECONCILE_MIN_INTERVAL_MS = 2e3;
|
|
164
|
+
const ELEVATION_REQUIRED_HEADER = "X-Elevation-Required";
|
|
165
|
+
const BACKGROUND_REQUEST = new HttpContextToken(() => false);
|
|
166
|
+
function isElevationRefusal(err) {
|
|
167
|
+
return err instanceof HttpErrorResponse && err.status === 403 && !!err.headers.get("X-Elevation-Required");
|
|
168
|
+
}
|
|
169
|
+
var ElevationService = class ElevationService {
|
|
170
|
+
http = inject(HttpClient);
|
|
171
|
+
store = inject(Store);
|
|
172
|
+
prompt = inject(ELEVATION_PROMPT, { optional: true });
|
|
173
|
+
destroyRef = inject(DestroyRef);
|
|
174
|
+
served = signal(null, ...ngDevMode ? [{ debugName: "served" }] : /* istanbul ignore next */ []);
|
|
175
|
+
state = () => {
|
|
176
|
+
const served = this.served();
|
|
177
|
+
if (served === null || !isPast(served)) return served;
|
|
178
|
+
const expired = expiredView(served);
|
|
179
|
+
untracked(() => this.served.set(expired));
|
|
180
|
+
this.announceExpiryLater();
|
|
181
|
+
return expired;
|
|
182
|
+
};
|
|
183
|
+
elevated = () => this.state()?.elevated ?? false;
|
|
184
|
+
mayElevate = () => this.state()?.mayElevate ?? false;
|
|
185
|
+
expiresAt = () => {
|
|
186
|
+
const iso = this.state()?.expiresAt;
|
|
187
|
+
return iso ? new Date(iso) : null;
|
|
188
|
+
};
|
|
189
|
+
changes$ = new Subject();
|
|
190
|
+
expiryTimer = null;
|
|
191
|
+
pendingPrompt = null;
|
|
192
|
+
expiryAnnouncePending = false;
|
|
193
|
+
lastReconcileAt = 0;
|
|
194
|
+
constructor() {
|
|
195
|
+
if (typeof window !== "undefined") {
|
|
196
|
+
window.addEventListener("storage", this.onStorage);
|
|
197
|
+
window.addEventListener("focus", this.onFocus);
|
|
198
|
+
if (typeof document !== "undefined") document.addEventListener("visibilitychange", this.onVisibilityChange);
|
|
199
|
+
this.destroyRef.onDestroy(() => {
|
|
200
|
+
window.removeEventListener("storage", this.onStorage);
|
|
201
|
+
window.removeEventListener("focus", this.onFocus);
|
|
202
|
+
if (typeof document !== "undefined") document.removeEventListener("visibilitychange", this.onVisibilityChange);
|
|
203
|
+
this.clearTimer();
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
get canPrompt() {
|
|
208
|
+
return this.available && this.prompt !== null;
|
|
209
|
+
}
|
|
210
|
+
get apiBase() {
|
|
211
|
+
return this.store.selectSnapshot(AppConfigState.manifest)?.apiBase ?? "/api/v1";
|
|
212
|
+
}
|
|
213
|
+
get available() {
|
|
214
|
+
return this.url !== null;
|
|
215
|
+
}
|
|
216
|
+
get url() {
|
|
217
|
+
return this.store.selectSnapshot(AppConfigState.manifest)?.identity?.elevationUrl ?? null;
|
|
218
|
+
}
|
|
219
|
+
isThisApi(url) {
|
|
220
|
+
const apiBase = this.store.selectSnapshot(AppConfigState.manifest)?.apiBase ?? "/api/v1";
|
|
221
|
+
let path = url.split("?")[0];
|
|
222
|
+
if (/^https?:\/\//.test(path)) try {
|
|
223
|
+
const parsed = new URL(path);
|
|
224
|
+
if (typeof window !== "undefined" && parsed.host !== window.location.host) return false;
|
|
225
|
+
path = parsed.pathname;
|
|
226
|
+
} catch {
|
|
227
|
+
return false;
|
|
228
|
+
}
|
|
229
|
+
return path === apiBase || path.startsWith(`${apiBase}/`);
|
|
230
|
+
}
|
|
231
|
+
refresh() {
|
|
232
|
+
const url = this.url;
|
|
233
|
+
if (!url) return of(this.inert());
|
|
234
|
+
return this.http.get(url, this.options()).pipe(tap((s) => this.apply(s)));
|
|
235
|
+
}
|
|
236
|
+
elevate(password, code) {
|
|
237
|
+
const url = this.url;
|
|
238
|
+
if (!url) return of(this.inert());
|
|
239
|
+
const body = { password };
|
|
240
|
+
if (code) body.code = code;
|
|
241
|
+
return this.http.post(url, body, this.options()).pipe(tap((s) => this.apply(s, "granted")));
|
|
242
|
+
}
|
|
243
|
+
drop(reason = "dropped_by_user") {
|
|
244
|
+
const url = this.url;
|
|
245
|
+
if (!url) return of(this.inert());
|
|
246
|
+
return this.http.delete(`${url}?reason=${reason}`, this.options()).pipe(switchMap(() => this.refresh()));
|
|
247
|
+
}
|
|
248
|
+
offerFor(refusal) {
|
|
249
|
+
if (!this.available || !this.prompt) return of(false);
|
|
250
|
+
return this.refresh().pipe(switchMap((state) => state.elevated ? of(false) : this.requestElevation({
|
|
251
|
+
state,
|
|
252
|
+
refusal
|
|
253
|
+
})));
|
|
254
|
+
}
|
|
255
|
+
requestElevation(request) {
|
|
256
|
+
if (!this.prompt) return of(false);
|
|
257
|
+
if (this.pendingPrompt) return this.pendingPrompt;
|
|
258
|
+
const prompt = this.prompt;
|
|
259
|
+
this.pendingPrompt = defer(() => prompt.open(request)).pipe(finalize(() => {
|
|
260
|
+
this.pendingPrompt = null;
|
|
261
|
+
}), shareReplay(1));
|
|
262
|
+
return this.pendingPrompt;
|
|
263
|
+
}
|
|
264
|
+
options() {
|
|
265
|
+
return { context: new HttpContext().set(BYPASS_ELEVATION, true) };
|
|
266
|
+
}
|
|
267
|
+
inert() {
|
|
268
|
+
return {
|
|
269
|
+
elevated: false,
|
|
270
|
+
ended: {
|
|
271
|
+
reason: "never",
|
|
272
|
+
at: null
|
|
273
|
+
},
|
|
274
|
+
lifetimeSeconds: 0,
|
|
275
|
+
lifetimeCeilingSeconds: 0,
|
|
276
|
+
mfaRequired: false,
|
|
277
|
+
warnings: []
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
apply(next, explicit) {
|
|
281
|
+
const prev = untracked(() => this.served());
|
|
282
|
+
this.served.set(next);
|
|
283
|
+
this.schedule(next);
|
|
284
|
+
const kind = explicit ?? this.kindOf(prev, next);
|
|
285
|
+
if (kind === null) return;
|
|
286
|
+
this.expiryAnnouncePending = false;
|
|
287
|
+
this.changes$.next({
|
|
288
|
+
kind,
|
|
289
|
+
state: next
|
|
290
|
+
});
|
|
291
|
+
this.announceToOtherTabs();
|
|
292
|
+
}
|
|
293
|
+
kindOf(prev, next) {
|
|
294
|
+
const was = prev?.elevated ?? false;
|
|
295
|
+
if (was && !next.elevated) return next.ended.reason === "expired" ? "expired" : "dropped";
|
|
296
|
+
if (!was && next.elevated) return "granted";
|
|
297
|
+
if (was && next.elevated && (prev?.expiresAt ?? null) !== (next.expiresAt ?? null)) return "observed";
|
|
298
|
+
return null;
|
|
299
|
+
}
|
|
300
|
+
announceExpiryLater() {
|
|
301
|
+
if (this.expiryAnnouncePending) return;
|
|
302
|
+
this.expiryAnnouncePending = true;
|
|
303
|
+
queueMicrotask(() => {
|
|
304
|
+
if (!this.expiryAnnouncePending) return;
|
|
305
|
+
this.expiryAnnouncePending = false;
|
|
306
|
+
const state = untracked(() => this.served());
|
|
307
|
+
if (state !== null && !state.elevated) this.changes$.next({
|
|
308
|
+
kind: "expired",
|
|
309
|
+
state
|
|
310
|
+
});
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
schedule(state) {
|
|
314
|
+
this.clearTimer();
|
|
315
|
+
if (!state.elevated || !state.expiresAt) return;
|
|
316
|
+
const wanted = Date.parse(state.expiresAt) - Date.now() + 250;
|
|
317
|
+
const ms = Math.min(Math.max(1e3, wanted), 2147483647);
|
|
318
|
+
this.expiryTimer = setTimeout(() => {
|
|
319
|
+
this.expiryTimer = null;
|
|
320
|
+
this.refresh().subscribe({
|
|
321
|
+
next: (s) => {
|
|
322
|
+
if (s.elevated && s.expiresAt && Date.parse(s.expiresAt) <= Date.now()) this.scheduleRetry();
|
|
323
|
+
},
|
|
324
|
+
error: () => this.scheduleRetry()
|
|
325
|
+
});
|
|
326
|
+
}, ms);
|
|
327
|
+
}
|
|
328
|
+
scheduleRetry() {
|
|
329
|
+
this.clearTimer();
|
|
330
|
+
this.expiryTimer = setTimeout(() => {
|
|
331
|
+
this.expiryTimer = null;
|
|
332
|
+
this.refresh().subscribe({ error: () => void 0 });
|
|
333
|
+
}, 5e3);
|
|
334
|
+
}
|
|
335
|
+
clearTimer() {
|
|
336
|
+
if (this.expiryTimer !== null) {
|
|
337
|
+
clearTimeout(this.expiryTimer);
|
|
338
|
+
this.expiryTimer = null;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
announceToOtherTabs() {
|
|
342
|
+
try {
|
|
343
|
+
localStorage.setItem(CROSS_TAB_KEY, String(Date.now()));
|
|
344
|
+
} catch {}
|
|
345
|
+
}
|
|
346
|
+
onStorage = (event) => {
|
|
347
|
+
if (event.key !== CROSS_TAB_KEY || !this.available) return;
|
|
348
|
+
this.refresh().subscribe({ error: () => void 0 });
|
|
349
|
+
};
|
|
350
|
+
onFocus = () => this.reconcile();
|
|
351
|
+
onVisibilityChange = () => {
|
|
352
|
+
if (document.visibilityState === "visible") this.reconcile();
|
|
353
|
+
};
|
|
354
|
+
reconcile() {
|
|
355
|
+
this.state();
|
|
356
|
+
if (!this.available) return;
|
|
357
|
+
const now = Date.now();
|
|
358
|
+
if (now - this.lastReconcileAt < RECONCILE_MIN_INTERVAL_MS) return;
|
|
359
|
+
this.lastReconcileAt = now;
|
|
360
|
+
this.refresh().subscribe({ error: () => void 0 });
|
|
361
|
+
}
|
|
362
|
+
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
363
|
+
minVersion: "12.0.0",
|
|
364
|
+
version: "22.2.3",
|
|
365
|
+
ngImport: i0,
|
|
366
|
+
type: ElevationService,
|
|
367
|
+
deps: [],
|
|
368
|
+
target: i0.ɵɵFactoryTarget.Injectable
|
|
369
|
+
});
|
|
370
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({
|
|
371
|
+
minVersion: "12.0.0",
|
|
372
|
+
version: "22.2.3",
|
|
373
|
+
ngImport: i0,
|
|
374
|
+
type: ElevationService,
|
|
375
|
+
providedIn: "root"
|
|
376
|
+
});
|
|
377
|
+
};
|
|
378
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
379
|
+
minVersion: "12.0.0",
|
|
380
|
+
version: "22.2.3",
|
|
381
|
+
ngImport: i0,
|
|
382
|
+
type: ElevationService,
|
|
383
|
+
decorators: [{
|
|
384
|
+
type: Injectable,
|
|
385
|
+
args: [{ providedIn: "root" }]
|
|
386
|
+
}],
|
|
387
|
+
ctorParameters: () => []
|
|
388
|
+
});
|
|
389
|
+
function isPast(state) {
|
|
390
|
+
if (!state.elevated || !state.expiresAt) return false;
|
|
391
|
+
const at = Date.parse(state.expiresAt);
|
|
392
|
+
return Number.isFinite(at) && at <= Date.now();
|
|
393
|
+
}
|
|
394
|
+
function expiredView(state) {
|
|
395
|
+
return {
|
|
396
|
+
...state,
|
|
397
|
+
elevated: false,
|
|
398
|
+
expiresAt: null,
|
|
399
|
+
grantedAt: null,
|
|
400
|
+
factorConfirmed: null,
|
|
401
|
+
ended: {
|
|
402
|
+
reason: "expired",
|
|
403
|
+
at: state.expiresAt ?? null
|
|
404
|
+
}
|
|
405
|
+
};
|
|
406
|
+
}
|
|
194
407
|
var UserPreferencesService = class UserPreferencesService {
|
|
195
408
|
http = inject(HttpClient);
|
|
196
409
|
store = inject(Store);
|
|
@@ -287,7 +500,10 @@ var UserPreferencesService = class UserPreferencesService {
|
|
|
287
500
|
syncToServer(prefs) {
|
|
288
501
|
const url = this.store.selectSnapshot(AppConfigState.manifest)?.auth?.preferencesUrl;
|
|
289
502
|
if (!url) return;
|
|
290
|
-
this.http.patch(url, { uiPrefs: prefs }, {
|
|
503
|
+
this.http.patch(url, { uiPrefs: prefs }, {
|
|
504
|
+
headers: { "Content-Type": "application/merge-patch+json" },
|
|
505
|
+
context: new HttpContext().set(BACKGROUND_REQUEST, true)
|
|
506
|
+
}).subscribe({ error: (err) => console.warn("Preferences sync failed:", err) });
|
|
291
507
|
}
|
|
292
508
|
getGridPref(gridId) {
|
|
293
509
|
try {
|
|
@@ -397,7 +613,7 @@ var UserPreferencesService = class UserPreferencesService {
|
|
|
397
613
|
}
|
|
398
614
|
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
399
615
|
minVersion: "12.0.0",
|
|
400
|
-
version: "22.2.
|
|
616
|
+
version: "22.2.3",
|
|
401
617
|
ngImport: i0,
|
|
402
618
|
type: UserPreferencesService,
|
|
403
619
|
deps: [],
|
|
@@ -405,7 +621,7 @@ var UserPreferencesService = class UserPreferencesService {
|
|
|
405
621
|
});
|
|
406
622
|
static ɵprov = i0.ɵɵngDeclareInjectable({
|
|
407
623
|
minVersion: "12.0.0",
|
|
408
|
-
version: "22.2.
|
|
624
|
+
version: "22.2.3",
|
|
409
625
|
ngImport: i0,
|
|
410
626
|
type: UserPreferencesService,
|
|
411
627
|
providedIn: "root"
|
|
@@ -413,7 +629,7 @@ var UserPreferencesService = class UserPreferencesService {
|
|
|
413
629
|
};
|
|
414
630
|
i0.ɵɵngDeclareClassMetadata({
|
|
415
631
|
minVersion: "12.0.0",
|
|
416
|
-
version: "22.2.
|
|
632
|
+
version: "22.2.3",
|
|
417
633
|
ngImport: i0,
|
|
418
634
|
type: UserPreferencesService,
|
|
419
635
|
decorators: [{
|
|
@@ -509,7 +725,7 @@ let AuthState = class AuthState {
|
|
|
509
725
|
}
|
|
510
726
|
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
511
727
|
minVersion: "12.0.0",
|
|
512
|
-
version: "22.2.
|
|
728
|
+
version: "22.2.3",
|
|
513
729
|
ngImport: i0,
|
|
514
730
|
type: AuthState,
|
|
515
731
|
deps: [],
|
|
@@ -517,7 +733,7 @@ let AuthState = class AuthState {
|
|
|
517
733
|
});
|
|
518
734
|
static ɵprov = i0.ɵɵngDeclareInjectable({
|
|
519
735
|
minVersion: "12.0.0",
|
|
520
|
-
version: "22.2.
|
|
736
|
+
version: "22.2.3",
|
|
521
737
|
ngImport: i0,
|
|
522
738
|
type: AuthState
|
|
523
739
|
});
|
|
@@ -543,7 +759,7 @@ AuthState = __decorate([State({
|
|
|
543
759
|
})], AuthState);
|
|
544
760
|
i0.ɵɵngDeclareClassMetadata({
|
|
545
761
|
minVersion: "12.0.0",
|
|
546
|
-
version: "22.2.
|
|
762
|
+
version: "22.2.3",
|
|
547
763
|
ngImport: i0,
|
|
548
764
|
type: AuthState,
|
|
549
765
|
decorators: [{ type: Injectable }],
|
|
@@ -555,6 +771,127 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
555
771
|
patchCurrentUser: []
|
|
556
772
|
}
|
|
557
773
|
});
|
|
774
|
+
var ConsoleAccessService = class ConsoleAccessService {
|
|
775
|
+
static URL = "/api/v1/admin/manifest";
|
|
776
|
+
static LEGACY_URL = "/api/v1/console/manifest";
|
|
777
|
+
static REFUSED_REASON = "no-console-access";
|
|
778
|
+
http = inject(HttpClient);
|
|
779
|
+
store = inject(Store);
|
|
780
|
+
answer = "unknown";
|
|
781
|
+
pending = null;
|
|
782
|
+
generation = 0;
|
|
783
|
+
constructor() {
|
|
784
|
+
inject(Actions).pipe(ofActionDispatched(Login, Logout, RestoreSession)).subscribe(() => {
|
|
785
|
+
this.generation++;
|
|
786
|
+
this.answer = "unknown";
|
|
787
|
+
this.pending = null;
|
|
788
|
+
});
|
|
789
|
+
}
|
|
790
|
+
ensure() {
|
|
791
|
+
if (!this.store.selectSnapshot(AuthState.isAuthenticated)) return Promise.resolve("unknown");
|
|
792
|
+
if ("unknown" !== this.answer) return Promise.resolve(this.answer);
|
|
793
|
+
if (null !== this.pending) return this.pending;
|
|
794
|
+
const askedFor = this.generation;
|
|
795
|
+
const pending = firstValueFrom(this.http.get(ConsoleAccessService.URL).pipe(catchError((error) => error instanceof HttpErrorResponse && 404 === error.status ? this.http.get(ConsoleAccessService.LEGACY_URL) : throwError(() => error)), map((response) => ({
|
|
796
|
+
access: "granted",
|
|
797
|
+
response
|
|
798
|
+
})), catchError((error) => of({
|
|
799
|
+
access: error instanceof HttpErrorResponse && 403 === error.status ? "refused" : "unknown",
|
|
800
|
+
response: null
|
|
801
|
+
})))).then(({ access, response }) => {
|
|
802
|
+
if (askedFor !== this.generation) return this.ensure();
|
|
803
|
+
if (null !== response) this.merge(response);
|
|
804
|
+
this.pending = null;
|
|
805
|
+
this.answer = access;
|
|
806
|
+
return access;
|
|
807
|
+
});
|
|
808
|
+
this.pending = pending;
|
|
809
|
+
return pending;
|
|
810
|
+
}
|
|
811
|
+
signOut() {
|
|
812
|
+
const logout = this.store.selectSnapshot(AppConfigState.manifest)?.auth?.logout ?? "/api/v1/auth/logout";
|
|
813
|
+
return this.http.post(logout, null).pipe(catchError(() => of(null)), switchMap(() => this.store.dispatch(new Logout())), map(() => void 0));
|
|
814
|
+
}
|
|
815
|
+
merge(response) {
|
|
816
|
+
const config = this.store.selectSnapshot(AppConfigState.config);
|
|
817
|
+
this.store.dispatch(new SetAppConfig({
|
|
818
|
+
...config ?? {},
|
|
819
|
+
manifest: {
|
|
820
|
+
...config?.manifest ?? {},
|
|
821
|
+
...response.manifest
|
|
822
|
+
}
|
|
823
|
+
}));
|
|
824
|
+
}
|
|
825
|
+
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
826
|
+
minVersion: "12.0.0",
|
|
827
|
+
version: "22.2.3",
|
|
828
|
+
ngImport: i0,
|
|
829
|
+
type: ConsoleAccessService,
|
|
830
|
+
deps: [],
|
|
831
|
+
target: i0.ɵɵFactoryTarget.Injectable
|
|
832
|
+
});
|
|
833
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({
|
|
834
|
+
minVersion: "12.0.0",
|
|
835
|
+
version: "22.2.3",
|
|
836
|
+
ngImport: i0,
|
|
837
|
+
type: ConsoleAccessService,
|
|
838
|
+
providedIn: "root"
|
|
839
|
+
});
|
|
840
|
+
};
|
|
841
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
842
|
+
minVersion: "12.0.0",
|
|
843
|
+
version: "22.2.3",
|
|
844
|
+
ngImport: i0,
|
|
845
|
+
type: ConsoleAccessService,
|
|
846
|
+
decorators: [{
|
|
847
|
+
type: Injectable,
|
|
848
|
+
args: [{ providedIn: "root" }]
|
|
849
|
+
}],
|
|
850
|
+
ctorParameters: () => []
|
|
851
|
+
});
|
|
852
|
+
const BACKGROUND = { context: new HttpContext().set(BACKGROUND_REQUEST, true) };
|
|
853
|
+
var RealtimeTokenClient = class RealtimeTokenClient {
|
|
854
|
+
static REFUSED = "no console access: no realtime connection";
|
|
855
|
+
http = inject(HttpClient);
|
|
856
|
+
store = inject(Store);
|
|
857
|
+
injector = inject(Injector);
|
|
858
|
+
get manifest() {
|
|
859
|
+
const m = this.store.selectSnapshot(AppConfigState.manifest);
|
|
860
|
+
if (!m) throw new Error("ApiManifest not loaded — call AppInitService.load() first");
|
|
861
|
+
return m;
|
|
862
|
+
}
|
|
863
|
+
connectionToken() {
|
|
864
|
+
return defer(() => this.injector.get(ConsoleAccessService).ensure()).pipe(switchMap((access) => "refused" === access ? throwError(() => new Error(RealtimeTokenClient.REFUSED)) : this.http.post(this.manifest.apiBase + "/centrifugo/connection-token", {}, BACKGROUND)));
|
|
865
|
+
}
|
|
866
|
+
subscriptionToken(channel) {
|
|
867
|
+
return this.http.post(this.manifest.apiBase + "/centrifugo/subscription-token", { channel }, BACKGROUND);
|
|
868
|
+
}
|
|
869
|
+
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
870
|
+
minVersion: "12.0.0",
|
|
871
|
+
version: "22.2.3",
|
|
872
|
+
ngImport: i0,
|
|
873
|
+
type: RealtimeTokenClient,
|
|
874
|
+
deps: [],
|
|
875
|
+
target: i0.ɵɵFactoryTarget.Injectable
|
|
876
|
+
});
|
|
877
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({
|
|
878
|
+
minVersion: "12.0.0",
|
|
879
|
+
version: "22.2.3",
|
|
880
|
+
ngImport: i0,
|
|
881
|
+
type: RealtimeTokenClient,
|
|
882
|
+
providedIn: "root"
|
|
883
|
+
});
|
|
884
|
+
};
|
|
885
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
886
|
+
minVersion: "12.0.0",
|
|
887
|
+
version: "22.2.3",
|
|
888
|
+
ngImport: i0,
|
|
889
|
+
type: RealtimeTokenClient,
|
|
890
|
+
decorators: [{
|
|
891
|
+
type: Injectable,
|
|
892
|
+
args: [{ providedIn: "root" }]
|
|
893
|
+
}]
|
|
894
|
+
});
|
|
558
895
|
const LOCK_NAME = "coolms_auth_refresh";
|
|
559
896
|
const FRESH_TOKEN_GRACE_MS = 3e4;
|
|
560
897
|
const STORAGE_KEY$1 = "coolms_token";
|
|
@@ -632,7 +969,7 @@ var AuthRefreshCoordinator = class AuthRefreshCoordinator {
|
|
|
632
969
|
}
|
|
633
970
|
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
634
971
|
minVersion: "12.0.0",
|
|
635
|
-
version: "22.2.
|
|
972
|
+
version: "22.2.3",
|
|
636
973
|
ngImport: i0,
|
|
637
974
|
type: AuthRefreshCoordinator,
|
|
638
975
|
deps: [],
|
|
@@ -640,7 +977,7 @@ var AuthRefreshCoordinator = class AuthRefreshCoordinator {
|
|
|
640
977
|
});
|
|
641
978
|
static ɵprov = i0.ɵɵngDeclareInjectable({
|
|
642
979
|
minVersion: "12.0.0",
|
|
643
|
-
version: "22.2.
|
|
980
|
+
version: "22.2.3",
|
|
644
981
|
ngImport: i0,
|
|
645
982
|
type: AuthRefreshCoordinator,
|
|
646
983
|
providedIn: "root"
|
|
@@ -648,7 +985,7 @@ var AuthRefreshCoordinator = class AuthRefreshCoordinator {
|
|
|
648
985
|
};
|
|
649
986
|
i0.ɵɵngDeclareClassMetadata({
|
|
650
987
|
minVersion: "12.0.0",
|
|
651
|
-
version: "22.2.
|
|
988
|
+
version: "22.2.3",
|
|
652
989
|
ngImport: i0,
|
|
653
990
|
type: AuthRefreshCoordinator,
|
|
654
991
|
decorators: [{
|
|
@@ -711,7 +1048,7 @@ var CrossTabAuthSyncService = class CrossTabAuthSyncService {
|
|
|
711
1048
|
};
|
|
712
1049
|
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
713
1050
|
minVersion: "12.0.0",
|
|
714
|
-
version: "22.2.
|
|
1051
|
+
version: "22.2.3",
|
|
715
1052
|
ngImport: i0,
|
|
716
1053
|
type: CrossTabAuthSyncService,
|
|
717
1054
|
deps: [],
|
|
@@ -719,7 +1056,7 @@ var CrossTabAuthSyncService = class CrossTabAuthSyncService {
|
|
|
719
1056
|
});
|
|
720
1057
|
static ɵprov = i0.ɵɵngDeclareInjectable({
|
|
721
1058
|
minVersion: "12.0.0",
|
|
722
|
-
version: "22.2.
|
|
1059
|
+
version: "22.2.3",
|
|
723
1060
|
ngImport: i0,
|
|
724
1061
|
type: CrossTabAuthSyncService,
|
|
725
1062
|
providedIn: "root"
|
|
@@ -727,7 +1064,7 @@ var CrossTabAuthSyncService = class CrossTabAuthSyncService {
|
|
|
727
1064
|
};
|
|
728
1065
|
i0.ɵɵngDeclareClassMetadata({
|
|
729
1066
|
minVersion: "12.0.0",
|
|
730
|
-
version: "22.2.
|
|
1067
|
+
version: "22.2.3",
|
|
731
1068
|
ngImport: i0,
|
|
732
1069
|
type: CrossTabAuthSyncService,
|
|
733
1070
|
decorators: [{
|
|
@@ -788,7 +1125,7 @@ var AppInitService = class AppInitService {
|
|
|
788
1125
|
}
|
|
789
1126
|
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
790
1127
|
minVersion: "12.0.0",
|
|
791
|
-
version: "22.2.
|
|
1128
|
+
version: "22.2.3",
|
|
792
1129
|
ngImport: i0,
|
|
793
1130
|
type: AppInitService,
|
|
794
1131
|
deps: [],
|
|
@@ -796,7 +1133,7 @@ var AppInitService = class AppInitService {
|
|
|
796
1133
|
});
|
|
797
1134
|
static ɵprov = i0.ɵɵngDeclareInjectable({
|
|
798
1135
|
minVersion: "12.0.0",
|
|
799
|
-
version: "22.2.
|
|
1136
|
+
version: "22.2.3",
|
|
800
1137
|
ngImport: i0,
|
|
801
1138
|
type: AppInitService,
|
|
802
1139
|
providedIn: "root"
|
|
@@ -804,7 +1141,7 @@ var AppInitService = class AppInitService {
|
|
|
804
1141
|
};
|
|
805
1142
|
i0.ɵɵngDeclareClassMetadata({
|
|
806
1143
|
minVersion: "12.0.0",
|
|
807
|
-
version: "22.2.
|
|
1144
|
+
version: "22.2.3",
|
|
808
1145
|
ngImport: i0,
|
|
809
1146
|
type: AppInitService,
|
|
810
1147
|
decorators: [{
|
|
@@ -816,9 +1153,13 @@ const authGuard = () => {
|
|
|
816
1153
|
const init = inject(AppInitService);
|
|
817
1154
|
const store = inject(Store);
|
|
818
1155
|
const router = inject(Router);
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
return router.createUrlTree(["/login"]);
|
|
1156
|
+
const consoleAccess = inject(ConsoleAccessService);
|
|
1157
|
+
return init.ready$.pipe(take(1), switchMap(() => {
|
|
1158
|
+
if (!store.selectSnapshot(AuthState.isAuthenticated)) return of(router.createUrlTree(["/login"]));
|
|
1159
|
+
return from(consoleAccess.ensure()).pipe(switchMap((access) => {
|
|
1160
|
+
if (!store.selectSnapshot(AuthState.isAuthenticated)) return of(router.createUrlTree(["/login"]));
|
|
1161
|
+
return "refused" !== access ? of(true) : consoleAccess.signOut().pipe(map(() => router.createUrlTree(["/login"], { queryParams: { reason: ConsoleAccessService.REFUSED_REASON } })));
|
|
1162
|
+
}));
|
|
822
1163
|
}));
|
|
823
1164
|
};
|
|
824
1165
|
const loginPageGuard = () => {
|
|
@@ -833,262 +1174,28 @@ const loginPageGuard = () => {
|
|
|
833
1174
|
const ELEVATION_WARNING_PASSWORD_NOT_SET = "elevation.password_not_set";
|
|
834
1175
|
const ELEVATION_WARNING_FACTOR_MISSING = "elevation.factor_missing";
|
|
835
1176
|
const ELEVATION_WARNING_MFA_OFF = "elevation.mfa_off";
|
|
836
|
-
const
|
|
837
|
-
const BYPASS_ELEVATION = new HttpContextToken(() => false);
|
|
838
|
-
const CROSS_TAB_KEY = "coolms.elevation.changed";
|
|
839
|
-
const RECONCILE_MIN_INTERVAL_MS = 2e3;
|
|
840
|
-
const ELEVATION_REQUIRED_HEADER = "X-Elevation-Required";
|
|
841
|
-
var ElevationService = class ElevationService {
|
|
842
|
-
http = inject(HttpClient);
|
|
843
|
-
store = inject(Store);
|
|
844
|
-
prompt = inject(ELEVATION_PROMPT, { optional: true });
|
|
845
|
-
destroyRef = inject(DestroyRef);
|
|
846
|
-
served = signal(null, ...ngDevMode ? [{ debugName: "served" }] : /* istanbul ignore next */ []);
|
|
847
|
-
state = () => {
|
|
848
|
-
const served = this.served();
|
|
849
|
-
if (served === null || !isPast(served)) return served;
|
|
850
|
-
const expired = expiredView(served);
|
|
851
|
-
untracked(() => this.served.set(expired));
|
|
852
|
-
this.announceExpiryLater();
|
|
853
|
-
return expired;
|
|
854
|
-
};
|
|
855
|
-
elevated = () => this.state()?.elevated ?? false;
|
|
856
|
-
expiresAt = () => {
|
|
857
|
-
const iso = this.state()?.expiresAt;
|
|
858
|
-
return iso ? new Date(iso) : null;
|
|
859
|
-
};
|
|
860
|
-
changes$ = new Subject();
|
|
861
|
-
expiryTimer = null;
|
|
862
|
-
pendingPrompt = null;
|
|
863
|
-
expiryAnnouncePending = false;
|
|
864
|
-
lastReconcileAt = 0;
|
|
865
|
-
constructor() {
|
|
866
|
-
if (typeof window !== "undefined") {
|
|
867
|
-
window.addEventListener("storage", this.onStorage);
|
|
868
|
-
window.addEventListener("focus", this.onFocus);
|
|
869
|
-
if (typeof document !== "undefined") document.addEventListener("visibilitychange", this.onVisibilityChange);
|
|
870
|
-
this.destroyRef.onDestroy(() => {
|
|
871
|
-
window.removeEventListener("storage", this.onStorage);
|
|
872
|
-
window.removeEventListener("focus", this.onFocus);
|
|
873
|
-
if (typeof document !== "undefined") document.removeEventListener("visibilitychange", this.onVisibilityChange);
|
|
874
|
-
this.clearTimer();
|
|
875
|
-
});
|
|
876
|
-
}
|
|
877
|
-
}
|
|
878
|
-
get available() {
|
|
879
|
-
return this.url !== null;
|
|
880
|
-
}
|
|
881
|
-
get url() {
|
|
882
|
-
return this.store.selectSnapshot(AppConfigState.manifest)?.identity?.elevationUrl ?? null;
|
|
883
|
-
}
|
|
884
|
-
isThisApi(url) {
|
|
885
|
-
const apiBase = this.store.selectSnapshot(AppConfigState.manifest)?.apiBase ?? "/api/v1";
|
|
886
|
-
let path = url.split("?")[0];
|
|
887
|
-
if (/^https?:\/\//.test(path)) try {
|
|
888
|
-
const parsed = new URL(path);
|
|
889
|
-
if (typeof window !== "undefined" && parsed.host !== window.location.host) return false;
|
|
890
|
-
path = parsed.pathname;
|
|
891
|
-
} catch {
|
|
892
|
-
return false;
|
|
893
|
-
}
|
|
894
|
-
return path === apiBase || path.startsWith(`${apiBase}/`);
|
|
895
|
-
}
|
|
896
|
-
refresh() {
|
|
897
|
-
const url = this.url;
|
|
898
|
-
if (!url) return of(this.inert());
|
|
899
|
-
return this.http.get(url, this.options()).pipe(tap((s) => this.apply(s)));
|
|
900
|
-
}
|
|
901
|
-
elevate(password, code) {
|
|
902
|
-
const url = this.url;
|
|
903
|
-
if (!url) return of(this.inert());
|
|
904
|
-
const body = { password };
|
|
905
|
-
if (code) body.code = code;
|
|
906
|
-
return this.http.post(url, body, this.options()).pipe(tap((s) => this.apply(s, "granted")));
|
|
907
|
-
}
|
|
908
|
-
drop(reason = "dropped_by_user") {
|
|
909
|
-
const url = this.url;
|
|
910
|
-
if (!url) return of(this.inert());
|
|
911
|
-
return this.http.delete(`${url}?reason=${reason}`, this.options()).pipe(switchMap(() => this.refresh()));
|
|
912
|
-
}
|
|
913
|
-
offerFor(refusal) {
|
|
914
|
-
if (!this.available || !this.prompt) return of(false);
|
|
915
|
-
return this.refresh().pipe(switchMap((state) => state.elevated ? of(false) : this.requestElevation({
|
|
916
|
-
state,
|
|
917
|
-
refusal
|
|
918
|
-
})));
|
|
919
|
-
}
|
|
920
|
-
requestElevation(request) {
|
|
921
|
-
if (!this.prompt) return of(false);
|
|
922
|
-
if (this.pendingPrompt) return this.pendingPrompt;
|
|
923
|
-
const prompt = this.prompt;
|
|
924
|
-
this.pendingPrompt = defer(() => prompt.open(request)).pipe(finalize(() => {
|
|
925
|
-
this.pendingPrompt = null;
|
|
926
|
-
}), shareReplay(1));
|
|
927
|
-
return this.pendingPrompt;
|
|
928
|
-
}
|
|
929
|
-
options() {
|
|
930
|
-
return { context: new HttpContext().set(BYPASS_ELEVATION, true) };
|
|
931
|
-
}
|
|
932
|
-
inert() {
|
|
933
|
-
return {
|
|
934
|
-
elevated: false,
|
|
935
|
-
ended: {
|
|
936
|
-
reason: "never",
|
|
937
|
-
at: null
|
|
938
|
-
},
|
|
939
|
-
lifetimeSeconds: 0,
|
|
940
|
-
lifetimeCeilingSeconds: 0,
|
|
941
|
-
mfaRequired: false,
|
|
942
|
-
warnings: []
|
|
943
|
-
};
|
|
944
|
-
}
|
|
945
|
-
apply(next, explicit) {
|
|
946
|
-
const prev = untracked(() => this.served());
|
|
947
|
-
this.served.set(next);
|
|
948
|
-
this.schedule(next);
|
|
949
|
-
const kind = explicit ?? this.kindOf(prev, next);
|
|
950
|
-
if (kind === null) return;
|
|
951
|
-
this.expiryAnnouncePending = false;
|
|
952
|
-
this.changes$.next({
|
|
953
|
-
kind,
|
|
954
|
-
state: next
|
|
955
|
-
});
|
|
956
|
-
this.announceToOtherTabs();
|
|
957
|
-
}
|
|
958
|
-
kindOf(prev, next) {
|
|
959
|
-
const was = prev?.elevated ?? false;
|
|
960
|
-
if (was && !next.elevated) return next.ended.reason === "expired" ? "expired" : "dropped";
|
|
961
|
-
if (!was && next.elevated) return "granted";
|
|
962
|
-
if (was && next.elevated && (prev?.expiresAt ?? null) !== (next.expiresAt ?? null)) return "observed";
|
|
963
|
-
return null;
|
|
964
|
-
}
|
|
965
|
-
announceExpiryLater() {
|
|
966
|
-
if (this.expiryAnnouncePending) return;
|
|
967
|
-
this.expiryAnnouncePending = true;
|
|
968
|
-
queueMicrotask(() => {
|
|
969
|
-
if (!this.expiryAnnouncePending) return;
|
|
970
|
-
this.expiryAnnouncePending = false;
|
|
971
|
-
const state = untracked(() => this.served());
|
|
972
|
-
if (state !== null && !state.elevated) this.changes$.next({
|
|
973
|
-
kind: "expired",
|
|
974
|
-
state
|
|
975
|
-
});
|
|
976
|
-
});
|
|
977
|
-
}
|
|
978
|
-
schedule(state) {
|
|
979
|
-
this.clearTimer();
|
|
980
|
-
if (!state.elevated || !state.expiresAt) return;
|
|
981
|
-
const wanted = Date.parse(state.expiresAt) - Date.now() + 250;
|
|
982
|
-
const ms = Math.min(Math.max(1e3, wanted), 2147483647);
|
|
983
|
-
this.expiryTimer = setTimeout(() => {
|
|
984
|
-
this.expiryTimer = null;
|
|
985
|
-
this.refresh().subscribe({
|
|
986
|
-
next: (s) => {
|
|
987
|
-
if (s.elevated && s.expiresAt && Date.parse(s.expiresAt) <= Date.now()) this.scheduleRetry();
|
|
988
|
-
},
|
|
989
|
-
error: () => this.scheduleRetry()
|
|
990
|
-
});
|
|
991
|
-
}, ms);
|
|
992
|
-
}
|
|
993
|
-
scheduleRetry() {
|
|
994
|
-
this.clearTimer();
|
|
995
|
-
this.expiryTimer = setTimeout(() => {
|
|
996
|
-
this.expiryTimer = null;
|
|
997
|
-
this.refresh().subscribe({ error: () => void 0 });
|
|
998
|
-
}, 5e3);
|
|
999
|
-
}
|
|
1000
|
-
clearTimer() {
|
|
1001
|
-
if (this.expiryTimer !== null) {
|
|
1002
|
-
clearTimeout(this.expiryTimer);
|
|
1003
|
-
this.expiryTimer = null;
|
|
1004
|
-
}
|
|
1005
|
-
}
|
|
1006
|
-
announceToOtherTabs() {
|
|
1007
|
-
try {
|
|
1008
|
-
localStorage.setItem(CROSS_TAB_KEY, String(Date.now()));
|
|
1009
|
-
} catch {}
|
|
1010
|
-
}
|
|
1011
|
-
onStorage = (event) => {
|
|
1012
|
-
if (event.key !== CROSS_TAB_KEY || !this.available) return;
|
|
1013
|
-
this.refresh().subscribe({ error: () => void 0 });
|
|
1014
|
-
};
|
|
1015
|
-
onFocus = () => this.reconcile();
|
|
1016
|
-
onVisibilityChange = () => {
|
|
1017
|
-
if (document.visibilityState === "visible") this.reconcile();
|
|
1018
|
-
};
|
|
1019
|
-
reconcile() {
|
|
1020
|
-
this.state();
|
|
1021
|
-
if (!this.available) return;
|
|
1022
|
-
const now = Date.now();
|
|
1023
|
-
if (now - this.lastReconcileAt < RECONCILE_MIN_INTERVAL_MS) return;
|
|
1024
|
-
this.lastReconcileAt = now;
|
|
1025
|
-
this.refresh().subscribe({ error: () => void 0 });
|
|
1026
|
-
}
|
|
1027
|
-
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
1028
|
-
minVersion: "12.0.0",
|
|
1029
|
-
version: "22.2.0",
|
|
1030
|
-
ngImport: i0,
|
|
1031
|
-
type: ElevationService,
|
|
1032
|
-
deps: [],
|
|
1033
|
-
target: i0.ɵɵFactoryTarget.Injectable
|
|
1034
|
-
});
|
|
1035
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({
|
|
1036
|
-
minVersion: "12.0.0",
|
|
1037
|
-
version: "22.2.0",
|
|
1038
|
-
ngImport: i0,
|
|
1039
|
-
type: ElevationService,
|
|
1040
|
-
providedIn: "root"
|
|
1041
|
-
});
|
|
1042
|
-
};
|
|
1043
|
-
i0.ɵɵngDeclareClassMetadata({
|
|
1044
|
-
minVersion: "12.0.0",
|
|
1045
|
-
version: "22.2.0",
|
|
1046
|
-
ngImport: i0,
|
|
1047
|
-
type: ElevationService,
|
|
1048
|
-
decorators: [{
|
|
1049
|
-
type: Injectable,
|
|
1050
|
-
args: [{ providedIn: "root" }]
|
|
1051
|
-
}],
|
|
1052
|
-
ctorParameters: () => []
|
|
1053
|
-
});
|
|
1054
|
-
function isPast(state) {
|
|
1055
|
-
if (!state.elevated || !state.expiresAt) return false;
|
|
1056
|
-
const at = Date.parse(state.expiresAt);
|
|
1057
|
-
return Number.isFinite(at) && at <= Date.now();
|
|
1058
|
-
}
|
|
1059
|
-
function expiredView(state) {
|
|
1060
|
-
return {
|
|
1061
|
-
...state,
|
|
1062
|
-
elevated: false,
|
|
1063
|
-
expiresAt: null,
|
|
1064
|
-
grantedAt: null,
|
|
1065
|
-
factorConfirmed: null,
|
|
1066
|
-
ended: {
|
|
1067
|
-
reason: "expired",
|
|
1068
|
-
at: state.expiresAt ?? null
|
|
1069
|
-
}
|
|
1070
|
-
};
|
|
1071
|
-
}
|
|
1177
|
+
const FRAMEWORK_REFUSAL = /^\s*access denied\.?\s*$/i;
|
|
1072
1178
|
var ErrorHandlerService = class ErrorHandlerService {
|
|
1073
1179
|
humanize(err) {
|
|
1074
1180
|
if (!(err instanceof HttpErrorResponse)) return err instanceof Error ? err.message : "An unexpected error occurred.";
|
|
1181
|
+
const elevationRefusal = isElevationRefusal(err);
|
|
1075
1182
|
const body = err.error;
|
|
1076
1183
|
if (body && typeof body === "object") {
|
|
1077
1184
|
const detail = body["detail"] ?? body["hydra:description"] ?? body["message"];
|
|
1078
|
-
if (typeof detail === "string" && detail.trim()) return detail.trim();
|
|
1185
|
+
if (typeof detail === "string" && detail.trim() && !FRAMEWORK_REFUSAL.test(detail)) return detail.trim();
|
|
1079
1186
|
if (Array.isArray(body["violations"])) {
|
|
1080
1187
|
const msgs = body["violations"].map((v) => v.message).filter(Boolean);
|
|
1081
1188
|
if (msgs.length) return msgs.join(" ");
|
|
1082
1189
|
}
|
|
1083
1190
|
}
|
|
1084
|
-
return this.statusMessage(err.status);
|
|
1191
|
+
return elevationRefusal ? "This needs an elevated session." : this.statusMessage(err.status);
|
|
1085
1192
|
}
|
|
1086
1193
|
statusMessage(status) {
|
|
1087
1194
|
switch (status) {
|
|
1088
1195
|
case 0: return "Network error. Check your connection.";
|
|
1089
1196
|
case 400: return "Invalid request.";
|
|
1090
1197
|
case 401: return "Session expired. Please sign in again.";
|
|
1091
|
-
case 403: return "
|
|
1198
|
+
case 403: return "You don't have access to this.";
|
|
1092
1199
|
case 404: return "Resource not found.";
|
|
1093
1200
|
case 409: return "Conflict — resource already exists.";
|
|
1094
1201
|
case 422: return "Validation failed.";
|
|
@@ -1100,7 +1207,7 @@ var ErrorHandlerService = class ErrorHandlerService {
|
|
|
1100
1207
|
}
|
|
1101
1208
|
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
1102
1209
|
minVersion: "12.0.0",
|
|
1103
|
-
version: "22.2.
|
|
1210
|
+
version: "22.2.3",
|
|
1104
1211
|
ngImport: i0,
|
|
1105
1212
|
type: ErrorHandlerService,
|
|
1106
1213
|
deps: [],
|
|
@@ -1108,7 +1215,7 @@ var ErrorHandlerService = class ErrorHandlerService {
|
|
|
1108
1215
|
});
|
|
1109
1216
|
static ɵprov = i0.ɵɵngDeclareInjectable({
|
|
1110
1217
|
minVersion: "12.0.0",
|
|
1111
|
-
version: "22.2.
|
|
1218
|
+
version: "22.2.3",
|
|
1112
1219
|
ngImport: i0,
|
|
1113
1220
|
type: ErrorHandlerService,
|
|
1114
1221
|
providedIn: "root"
|
|
@@ -1116,7 +1223,7 @@ var ErrorHandlerService = class ErrorHandlerService {
|
|
|
1116
1223
|
};
|
|
1117
1224
|
i0.ɵɵngDeclareClassMetadata({
|
|
1118
1225
|
minVersion: "12.0.0",
|
|
1119
|
-
version: "22.2.
|
|
1226
|
+
version: "22.2.3",
|
|
1120
1227
|
ngImport: i0,
|
|
1121
1228
|
type: ErrorHandlerService,
|
|
1122
1229
|
decorators: [{
|
|
@@ -1124,42 +1231,85 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
1124
1231
|
args: [{ providedIn: "root" }]
|
|
1125
1232
|
}]
|
|
1126
1233
|
});
|
|
1234
|
+
const READS = /* @__PURE__ */ new Set([
|
|
1235
|
+
"GET",
|
|
1236
|
+
"HEAD",
|
|
1237
|
+
"OPTIONS"
|
|
1238
|
+
]);
|
|
1127
1239
|
const elevationInterceptor = (req, next) => {
|
|
1128
1240
|
const elevation = inject(ElevationService);
|
|
1241
|
+
if (req.context.get(BYPASS_ELEVATION) || req.context.get(BACKGROUND_REQUEST) || READS.has(req.method.toUpperCase()) || !elevation.isThisApi(req.url)) return next(req);
|
|
1242
|
+
const notice = inject(ELEVATION_NOTICE, { optional: true });
|
|
1129
1243
|
const errors = inject(ErrorHandlerService);
|
|
1130
|
-
if (req.context.get(BYPASS_ELEVATION) || !elevation.isThisApi(req.url)) return next(req);
|
|
1131
1244
|
return next(req).pipe(catchError((err) => {
|
|
1132
|
-
if (!(err
|
|
1133
|
-
|
|
1245
|
+
if (!isElevationRefusal(err)) return throwError(() => err);
|
|
1246
|
+
const refusal = errors.humanize(err);
|
|
1247
|
+
const action = actionOf(req, elevation.apiBase);
|
|
1248
|
+
const decline = () => {
|
|
1249
|
+
notice?.refused(refusal, action);
|
|
1250
|
+
return throwError(() => err);
|
|
1251
|
+
};
|
|
1252
|
+
if (!elevation.canPrompt) return decline();
|
|
1253
|
+
return elevation.refresh().pipe(switchMap((state) => state.elevated ? of(true) : elevation.requestElevation({
|
|
1254
|
+
state,
|
|
1255
|
+
refusal,
|
|
1256
|
+
action
|
|
1257
|
+
})), catchError(() => of(false)), switchMap((granted) => granted ? next(req) : decline()));
|
|
1134
1258
|
}));
|
|
1135
1259
|
};
|
|
1260
|
+
function actionOf(req, apiBase = "/api/v1") {
|
|
1261
|
+
const named = req.context.get(ELEVATION_ACTION);
|
|
1262
|
+
if (named) return named;
|
|
1263
|
+
const verb = {
|
|
1264
|
+
POST: "Creating",
|
|
1265
|
+
PUT: "Saving",
|
|
1266
|
+
PATCH: "Saving",
|
|
1267
|
+
DELETE: "Deleting"
|
|
1268
|
+
}[req.method.toUpperCase()] ?? "This change";
|
|
1269
|
+
let path = req.url.split("?")[0];
|
|
1270
|
+
try {
|
|
1271
|
+
path = new URL(path, "http://x").pathname;
|
|
1272
|
+
} catch {}
|
|
1273
|
+
if (path.startsWith(apiBase)) path = path.slice(apiBase.length);
|
|
1274
|
+
const words = path.split("/").filter((s) => s !== "" && !/^[0-9a-f-]{8,}$/i.test(s) && !/^\d+$/.test(s)).map((s) => s.replace(/[-_]/g, " "));
|
|
1275
|
+
return words.length === 0 ? verb : `${verb} ${words.join(" ")}`;
|
|
1276
|
+
}
|
|
1136
1277
|
var LoginComponent = class LoginComponent {
|
|
1137
1278
|
identifier = "";
|
|
1138
1279
|
password = "";
|
|
1139
|
-
loading = false;
|
|
1140
|
-
error = null;
|
|
1280
|
+
loading = signal(false, ...ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []);
|
|
1281
|
+
error = signal(null, ...ngDevMode ? [{ debugName: "error" }] : /* istanbul ignore next */ []);
|
|
1141
1282
|
store = inject(Store);
|
|
1142
1283
|
router = inject(Router);
|
|
1284
|
+
static NO_CONSOLE_ACCESS = "This account has no access to the console.";
|
|
1285
|
+
constructor() {
|
|
1286
|
+
inject(ActivatedRoute).queryParamMap.pipe(takeUntilDestroyed()).subscribe((params) => {
|
|
1287
|
+
if (ConsoleAccessService.REFUSED_REASON === params.get("reason")) {
|
|
1288
|
+
this.loading.set(false);
|
|
1289
|
+
this.error.set(LoginComponent.NO_CONSOLE_ACCESS);
|
|
1290
|
+
}
|
|
1291
|
+
});
|
|
1292
|
+
}
|
|
1143
1293
|
submit() {
|
|
1144
1294
|
if (!this.identifier || !this.password) return;
|
|
1145
|
-
this.loading
|
|
1146
|
-
this.error
|
|
1295
|
+
this.loading.set(true);
|
|
1296
|
+
this.error.set(null);
|
|
1147
1297
|
this.store.dispatch(new Login(this.identifier, this.password)).subscribe({
|
|
1148
1298
|
next: () => {
|
|
1149
1299
|
this.router.navigate(["/"]).catch(() => {
|
|
1150
|
-
this.loading
|
|
1151
|
-
this.error
|
|
1300
|
+
this.loading.set(false);
|
|
1301
|
+
this.error.set("Signed in, but the admin could not be opened. Please reload.");
|
|
1152
1302
|
});
|
|
1153
1303
|
},
|
|
1154
1304
|
error: (err) => {
|
|
1155
|
-
this.loading
|
|
1156
|
-
this.error
|
|
1305
|
+
this.loading.set(false);
|
|
1306
|
+
this.error.set(err?.error?.detail ?? err?.error?.message ?? err?.message ?? "Invalid credentials. Please try again.");
|
|
1157
1307
|
}
|
|
1158
1308
|
});
|
|
1159
1309
|
}
|
|
1160
1310
|
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
1161
1311
|
minVersion: "12.0.0",
|
|
1162
|
-
version: "22.2.
|
|
1312
|
+
version: "22.2.3",
|
|
1163
1313
|
ngImport: i0,
|
|
1164
1314
|
type: LoginComponent,
|
|
1165
1315
|
deps: [],
|
|
@@ -1167,12 +1317,12 @@ var LoginComponent = class LoginComponent {
|
|
|
1167
1317
|
});
|
|
1168
1318
|
static ɵcmp = i0.ɵɵngDeclareComponent({
|
|
1169
1319
|
minVersion: "14.0.0",
|
|
1170
|
-
version: "22.2.
|
|
1320
|
+
version: "22.2.3",
|
|
1171
1321
|
type: LoginComponent,
|
|
1172
1322
|
isStandalone: true,
|
|
1173
1323
|
selector: "coolms-admin-login",
|
|
1174
1324
|
ngImport: i0,
|
|
1175
|
-
template: "<div class=\"d-flex align-items-center justify-content-center min-vh-100 bg-light\">\n <div class=\"card shadow-sm\" style=\"width: 360px\">\n <div class=\"card-body p-4\">\n <h1 class=\"h5 mb-1\">CoolMS Admin</h1>\n <p class=\"text-muted small mb-4\">Sign in to continue</p>\n\n <div *ngIf=\"error\" class=\"alert alert-danger py-2 small\">{{ error }}</div>\n\n <form (ngSubmit)=\"submit()\" #loginForm=\"ngForm\">\n <div class=\"mb-3\">\n <label class=\"form-label\">Email or username</label>\n <input\n type=\"text\"\n class=\"form-control\"\n [(ngModel)]=\"identifier\"\n name=\"identifier\"\n required\n autocomplete=\"username\"\n />\n </div>\n <div class=\"mb-3\">\n <label class=\"form-label\">Password</label>\n <input\n type=\"password\"\n class=\"form-control\"\n [(ngModel)]=\"password\"\n name=\"password\"\n required\n autocomplete=\"current-password\"\n />\n </div>\n <button\n type=\"submit\"\n class=\"cms-btn cms-btn-primary w-100 justify-content-center\"\n [disabled]=\"loading || loginForm.invalid\"\n >\n {{ loading ? 'Signing in…' : 'Sign in' }}\n </button>\n </form>\n </div>\n </div>\n</div>\n",
|
|
1325
|
+
template: "<div class=\"d-flex align-items-center justify-content-center min-vh-100 bg-light\">\n <div class=\"card shadow-sm\" style=\"width: 360px\">\n <div class=\"card-body p-4\">\n <h1 class=\"h5 mb-1\">CoolMS Admin</h1>\n <p class=\"text-muted small mb-4\">Sign in to continue</p>\n\n <div *ngIf=\"error()\" class=\"alert alert-danger py-2 small\">{{ error() }}</div>\n\n <form (ngSubmit)=\"submit()\" #loginForm=\"ngForm\">\n <div class=\"mb-3\">\n <label class=\"form-label\">Email or username</label>\n <input\n type=\"text\"\n class=\"form-control\"\n [(ngModel)]=\"identifier\"\n name=\"identifier\"\n required\n autocomplete=\"username\"\n />\n </div>\n <div class=\"mb-3\">\n <label class=\"form-label\">Password</label>\n <input\n type=\"password\"\n class=\"form-control\"\n [(ngModel)]=\"password\"\n name=\"password\"\n required\n autocomplete=\"current-password\"\n />\n </div>\n <button\n type=\"submit\"\n class=\"cms-btn cms-btn-primary w-100 justify-content-center\"\n [disabled]=\"loading() || loginForm.invalid\"\n >\n {{ loading() ? 'Signing in…' : 'Sign in' }}\n </button>\n </form>\n </div>\n </div>\n</div>\n",
|
|
1176
1326
|
dependencies: [
|
|
1177
1327
|
{
|
|
1178
1328
|
kind: "ngmodule",
|
|
@@ -1240,7 +1390,7 @@ var LoginComponent = class LoginComponent {
|
|
|
1240
1390
|
};
|
|
1241
1391
|
i0.ɵɵngDeclareClassMetadata({
|
|
1242
1392
|
minVersion: "12.0.0",
|
|
1243
|
-
version: "22.2.
|
|
1393
|
+
version: "22.2.3",
|
|
1244
1394
|
ngImport: i0,
|
|
1245
1395
|
type: LoginComponent,
|
|
1246
1396
|
decorators: [{
|
|
@@ -1249,9 +1399,10 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
1249
1399
|
selector: "coolms-admin-login",
|
|
1250
1400
|
standalone: true,
|
|
1251
1401
|
imports: [FormsModule, NgIf],
|
|
1252
|
-
template: "<div class=\"d-flex align-items-center justify-content-center min-vh-100 bg-light\">\n <div class=\"card shadow-sm\" style=\"width: 360px\">\n <div class=\"card-body p-4\">\n <h1 class=\"h5 mb-1\">CoolMS Admin</h1>\n <p class=\"text-muted small mb-4\">Sign in to continue</p>\n\n <div *ngIf=\"error\" class=\"alert alert-danger py-2 small\">{{ error }}</div>\n\n <form (ngSubmit)=\"submit()\" #loginForm=\"ngForm\">\n <div class=\"mb-3\">\n <label class=\"form-label\">Email or username</label>\n <input\n type=\"text\"\n class=\"form-control\"\n [(ngModel)]=\"identifier\"\n name=\"identifier\"\n required\n autocomplete=\"username\"\n />\n </div>\n <div class=\"mb-3\">\n <label class=\"form-label\">Password</label>\n <input\n type=\"password\"\n class=\"form-control\"\n [(ngModel)]=\"password\"\n name=\"password\"\n required\n autocomplete=\"current-password\"\n />\n </div>\n <button\n type=\"submit\"\n class=\"cms-btn cms-btn-primary w-100 justify-content-center\"\n [disabled]=\"loading || loginForm.invalid\"\n >\n {{ loading ? 'Signing in…' : 'Sign in' }}\n </button>\n </form>\n </div>\n </div>\n</div>\n"
|
|
1402
|
+
template: "<div class=\"d-flex align-items-center justify-content-center min-vh-100 bg-light\">\n <div class=\"card shadow-sm\" style=\"width: 360px\">\n <div class=\"card-body p-4\">\n <h1 class=\"h5 mb-1\">CoolMS Admin</h1>\n <p class=\"text-muted small mb-4\">Sign in to continue</p>\n\n <div *ngIf=\"error()\" class=\"alert alert-danger py-2 small\">{{ error() }}</div>\n\n <form (ngSubmit)=\"submit()\" #loginForm=\"ngForm\">\n <div class=\"mb-3\">\n <label class=\"form-label\">Email or username</label>\n <input\n type=\"text\"\n class=\"form-control\"\n [(ngModel)]=\"identifier\"\n name=\"identifier\"\n required\n autocomplete=\"username\"\n />\n </div>\n <div class=\"mb-3\">\n <label class=\"form-label\">Password</label>\n <input\n type=\"password\"\n class=\"form-control\"\n [(ngModel)]=\"password\"\n name=\"password\"\n required\n autocomplete=\"current-password\"\n />\n </div>\n <button\n type=\"submit\"\n class=\"cms-btn cms-btn-primary w-100 justify-content-center\"\n [disabled]=\"loading() || loginForm.invalid\"\n >\n {{ loading() ? 'Signing in…' : 'Sign in' }}\n </button>\n </form>\n </div>\n </div>\n</div>\n"
|
|
1253
1403
|
}]
|
|
1254
|
-
}]
|
|
1404
|
+
}],
|
|
1405
|
+
ctorParameters: () => []
|
|
1255
1406
|
});
|
|
1256
1407
|
var ConfigService = class ConfigService {
|
|
1257
1408
|
http = inject(HttpClient);
|
|
@@ -1276,14 +1427,17 @@ var ConfigService = class ConfigService {
|
|
|
1276
1427
|
const key = `${type}/${id}`;
|
|
1277
1428
|
if (!this.cache.has(key)) {
|
|
1278
1429
|
const base = this.store.selectSnapshot(AppConfigState.manifest)?.configBase ?? "/api/v1/config";
|
|
1279
|
-
const obs = this.http.get(`${base}/${type}/${id}`).pipe(map((r) => r.data ?? r),
|
|
1430
|
+
const obs = this.http.get(`${base}/${type}/${id}`).pipe(map((r) => r.data ?? r), catchError((err) => {
|
|
1431
|
+
this.cache.delete(key);
|
|
1432
|
+
return throwError(() => err);
|
|
1433
|
+
}), shareReplay(1));
|
|
1280
1434
|
this.cache.set(key, obs);
|
|
1281
1435
|
}
|
|
1282
1436
|
return this.cache.get(key);
|
|
1283
1437
|
}
|
|
1284
1438
|
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
1285
1439
|
minVersion: "12.0.0",
|
|
1286
|
-
version: "22.2.
|
|
1440
|
+
version: "22.2.3",
|
|
1287
1441
|
ngImport: i0,
|
|
1288
1442
|
type: ConfigService,
|
|
1289
1443
|
deps: [],
|
|
@@ -1291,7 +1445,7 @@ var ConfigService = class ConfigService {
|
|
|
1291
1445
|
});
|
|
1292
1446
|
static ɵprov = i0.ɵɵngDeclareInjectable({
|
|
1293
1447
|
minVersion: "12.0.0",
|
|
1294
|
-
version: "22.2.
|
|
1448
|
+
version: "22.2.3",
|
|
1295
1449
|
ngImport: i0,
|
|
1296
1450
|
type: ConfigService,
|
|
1297
1451
|
providedIn: "root"
|
|
@@ -1299,7 +1453,7 @@ var ConfigService = class ConfigService {
|
|
|
1299
1453
|
};
|
|
1300
1454
|
i0.ɵɵngDeclareClassMetadata({
|
|
1301
1455
|
minVersion: "12.0.0",
|
|
1302
|
-
version: "22.2.
|
|
1456
|
+
version: "22.2.3",
|
|
1303
1457
|
ngImport: i0,
|
|
1304
1458
|
type: ConfigService,
|
|
1305
1459
|
decorators: [{
|
|
@@ -1409,7 +1563,7 @@ var ThemeService = class ThemeService {
|
|
|
1409
1563
|
}
|
|
1410
1564
|
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
1411
1565
|
minVersion: "12.0.0",
|
|
1412
|
-
version: "22.2.
|
|
1566
|
+
version: "22.2.3",
|
|
1413
1567
|
ngImport: i0,
|
|
1414
1568
|
type: ThemeService,
|
|
1415
1569
|
deps: [],
|
|
@@ -1417,7 +1571,7 @@ var ThemeService = class ThemeService {
|
|
|
1417
1571
|
});
|
|
1418
1572
|
static ɵprov = i0.ɵɵngDeclareInjectable({
|
|
1419
1573
|
minVersion: "12.0.0",
|
|
1420
|
-
version: "22.2.
|
|
1574
|
+
version: "22.2.3",
|
|
1421
1575
|
ngImport: i0,
|
|
1422
1576
|
type: ThemeService,
|
|
1423
1577
|
providedIn: "root"
|
|
@@ -1425,7 +1579,7 @@ var ThemeService = class ThemeService {
|
|
|
1425
1579
|
};
|
|
1426
1580
|
i0.ɵɵngDeclareClassMetadata({
|
|
1427
1581
|
minVersion: "12.0.0",
|
|
1428
|
-
version: "22.2.
|
|
1582
|
+
version: "22.2.3",
|
|
1429
1583
|
ngImport: i0,
|
|
1430
1584
|
type: ThemeService,
|
|
1431
1585
|
decorators: [{
|
|
@@ -1441,7 +1595,7 @@ var CmsLoaderComponent = class CmsLoaderComponent {
|
|
|
1441
1595
|
clipId = "cms-loader-" + Math.random().toString(36).slice(2, 9);
|
|
1442
1596
|
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
1443
1597
|
minVersion: "12.0.0",
|
|
1444
|
-
version: "22.2.
|
|
1598
|
+
version: "22.2.3",
|
|
1445
1599
|
ngImport: i0,
|
|
1446
1600
|
type: CmsLoaderComponent,
|
|
1447
1601
|
deps: [],
|
|
@@ -1449,7 +1603,7 @@ var CmsLoaderComponent = class CmsLoaderComponent {
|
|
|
1449
1603
|
});
|
|
1450
1604
|
static ɵcmp = i0.ɵɵngDeclareComponent({
|
|
1451
1605
|
minVersion: "17.0.0",
|
|
1452
|
-
version: "22.2.
|
|
1606
|
+
version: "22.2.3",
|
|
1453
1607
|
type: CmsLoaderComponent,
|
|
1454
1608
|
isStandalone: true,
|
|
1455
1609
|
selector: "cms-loader",
|
|
@@ -1510,7 +1664,7 @@ var CmsLoaderComponent = class CmsLoaderComponent {
|
|
|
1510
1664
|
};
|
|
1511
1665
|
i0.ɵɵngDeclareClassMetadata({
|
|
1512
1666
|
minVersion: "12.0.0",
|
|
1513
|
-
version: "22.2.
|
|
1667
|
+
version: "22.2.3",
|
|
1514
1668
|
ngImport: i0,
|
|
1515
1669
|
type: CmsLoaderComponent,
|
|
1516
1670
|
decorators: [{
|
|
@@ -1660,7 +1814,7 @@ var SidebarStateService = class SidebarStateService {
|
|
|
1660
1814
|
}
|
|
1661
1815
|
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
1662
1816
|
minVersion: "12.0.0",
|
|
1663
|
-
version: "22.2.
|
|
1817
|
+
version: "22.2.3",
|
|
1664
1818
|
ngImport: i0,
|
|
1665
1819
|
type: SidebarStateService,
|
|
1666
1820
|
deps: [],
|
|
@@ -1668,7 +1822,7 @@ var SidebarStateService = class SidebarStateService {
|
|
|
1668
1822
|
});
|
|
1669
1823
|
static ɵprov = i0.ɵɵngDeclareInjectable({
|
|
1670
1824
|
minVersion: "12.0.0",
|
|
1671
|
-
version: "22.2.
|
|
1825
|
+
version: "22.2.3",
|
|
1672
1826
|
ngImport: i0,
|
|
1673
1827
|
type: SidebarStateService,
|
|
1674
1828
|
providedIn: "root"
|
|
@@ -1676,7 +1830,7 @@ var SidebarStateService = class SidebarStateService {
|
|
|
1676
1830
|
};
|
|
1677
1831
|
i0.ɵɵngDeclareClassMetadata({
|
|
1678
1832
|
minVersion: "12.0.0",
|
|
1679
|
-
version: "22.2.
|
|
1833
|
+
version: "22.2.3",
|
|
1680
1834
|
ngImport: i0,
|
|
1681
1835
|
type: SidebarStateService,
|
|
1682
1836
|
decorators: [{
|
|
@@ -1692,7 +1846,7 @@ var EntitySearchService = class EntitySearchService {
|
|
|
1692
1846
|
}
|
|
1693
1847
|
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
1694
1848
|
minVersion: "12.0.0",
|
|
1695
|
-
version: "22.2.
|
|
1849
|
+
version: "22.2.3",
|
|
1696
1850
|
ngImport: i0,
|
|
1697
1851
|
type: EntitySearchService,
|
|
1698
1852
|
deps: [],
|
|
@@ -1700,7 +1854,7 @@ var EntitySearchService = class EntitySearchService {
|
|
|
1700
1854
|
});
|
|
1701
1855
|
static ɵprov = i0.ɵɵngDeclareInjectable({
|
|
1702
1856
|
minVersion: "12.0.0",
|
|
1703
|
-
version: "22.2.
|
|
1857
|
+
version: "22.2.3",
|
|
1704
1858
|
ngImport: i0,
|
|
1705
1859
|
type: EntitySearchService,
|
|
1706
1860
|
providedIn: "root"
|
|
@@ -1708,7 +1862,7 @@ var EntitySearchService = class EntitySearchService {
|
|
|
1708
1862
|
};
|
|
1709
1863
|
i0.ɵɵngDeclareClassMetadata({
|
|
1710
1864
|
minVersion: "12.0.0",
|
|
1711
|
-
version: "22.2.
|
|
1865
|
+
version: "22.2.3",
|
|
1712
1866
|
ngImport: i0,
|
|
1713
1867
|
type: EntitySearchService,
|
|
1714
1868
|
decorators: [{
|
|
@@ -1724,8 +1878,8 @@ var NaviGraphService = class NaviGraphService {
|
|
|
1724
1878
|
adminNav = signal([], ...ngDevMode ? [{ debugName: "adminNav" }] : /* istanbul ignore next */ []);
|
|
1725
1879
|
topbarNav = signal([], ...ngDevMode ? [{ debugName: "topbarNav" }] : /* istanbul ignore next */ []);
|
|
1726
1880
|
cache = /* @__PURE__ */ new Map();
|
|
1727
|
-
loadAdminNav(url) {
|
|
1728
|
-
return this.loadTree(url).pipe(map((tree) => {
|
|
1881
|
+
loadAdminNav(url, options = {}) {
|
|
1882
|
+
return this.loadTree(url, options.fresh ?? false).pipe(map((tree) => {
|
|
1729
1883
|
this.adminNav.set(tree);
|
|
1730
1884
|
return tree;
|
|
1731
1885
|
}));
|
|
@@ -1775,8 +1929,8 @@ var NaviGraphService = class NaviGraphService {
|
|
|
1775
1929
|
default: return false;
|
|
1776
1930
|
}
|
|
1777
1931
|
}
|
|
1778
|
-
loadTree(url) {
|
|
1779
|
-
if (this.cache.has(url)) return of(this.cache.get(url));
|
|
1932
|
+
loadTree(url, fresh = false) {
|
|
1933
|
+
if (!fresh && this.cache.has(url)) return of(this.cache.get(url));
|
|
1780
1934
|
return this.http.get(url, { headers: { Accept: "application/ld+json" } }).pipe(map((r) => {
|
|
1781
1935
|
const raw = r["member"] ?? r["hydra:member"] ?? [];
|
|
1782
1936
|
const flat = Array.isArray(raw) ? raw : [];
|
|
@@ -1785,7 +1939,7 @@ var NaviGraphService = class NaviGraphService {
|
|
|
1785
1939
|
return tree;
|
|
1786
1940
|
}), catchError((err) => {
|
|
1787
1941
|
console.error("[NaviGraph] loadTree failed for", url, this.errors.humanize(err));
|
|
1788
|
-
return of([]);
|
|
1942
|
+
return of(this.cache.get(url) ?? []);
|
|
1789
1943
|
}));
|
|
1790
1944
|
}
|
|
1791
1945
|
buildTree(flat) {
|
|
@@ -1806,7 +1960,7 @@ var NaviGraphService = class NaviGraphService {
|
|
|
1806
1960
|
}
|
|
1807
1961
|
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
1808
1962
|
minVersion: "12.0.0",
|
|
1809
|
-
version: "22.2.
|
|
1963
|
+
version: "22.2.3",
|
|
1810
1964
|
ngImport: i0,
|
|
1811
1965
|
type: NaviGraphService,
|
|
1812
1966
|
deps: [],
|
|
@@ -1814,7 +1968,7 @@ var NaviGraphService = class NaviGraphService {
|
|
|
1814
1968
|
});
|
|
1815
1969
|
static ɵprov = i0.ɵɵngDeclareInjectable({
|
|
1816
1970
|
minVersion: "12.0.0",
|
|
1817
|
-
version: "22.2.
|
|
1971
|
+
version: "22.2.3",
|
|
1818
1972
|
ngImport: i0,
|
|
1819
1973
|
type: NaviGraphService,
|
|
1820
1974
|
providedIn: "root"
|
|
@@ -1822,7 +1976,7 @@ var NaviGraphService = class NaviGraphService {
|
|
|
1822
1976
|
};
|
|
1823
1977
|
i0.ɵɵngDeclareClassMetadata({
|
|
1824
1978
|
minVersion: "12.0.0",
|
|
1825
|
-
version: "22.2.
|
|
1979
|
+
version: "22.2.3",
|
|
1826
1980
|
ngImport: i0,
|
|
1827
1981
|
type: NaviGraphService,
|
|
1828
1982
|
decorators: [{
|
|
@@ -1830,6 +1984,9 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
1830
1984
|
args: [{ providedIn: "root" }]
|
|
1831
1985
|
}]
|
|
1832
1986
|
});
|
|
1987
|
+
function naviAccessOf(node) {
|
|
1988
|
+
return node.access === "elevate" ? "elevate" : "allowed";
|
|
1989
|
+
}
|
|
1833
1990
|
var ComponentRegistry = class ComponentRegistry {
|
|
1834
1991
|
static map = /* @__PURE__ */ new Map();
|
|
1835
1992
|
static register(key, component) {
|
|
@@ -1858,7 +2015,7 @@ var ComponentRegistry = class ComponentRegistry {
|
|
|
1858
2015
|
}
|
|
1859
2016
|
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
1860
2017
|
minVersion: "12.0.0",
|
|
1861
|
-
version: "22.2.
|
|
2018
|
+
version: "22.2.3",
|
|
1862
2019
|
ngImport: i0,
|
|
1863
2020
|
type: ComponentRegistry,
|
|
1864
2021
|
deps: [],
|
|
@@ -1866,7 +2023,7 @@ var ComponentRegistry = class ComponentRegistry {
|
|
|
1866
2023
|
});
|
|
1867
2024
|
static ɵprov = i0.ɵɵngDeclareInjectable({
|
|
1868
2025
|
minVersion: "12.0.0",
|
|
1869
|
-
version: "22.2.
|
|
2026
|
+
version: "22.2.3",
|
|
1870
2027
|
ngImport: i0,
|
|
1871
2028
|
type: ComponentRegistry,
|
|
1872
2029
|
providedIn: "root"
|
|
@@ -1874,7 +2031,7 @@ var ComponentRegistry = class ComponentRegistry {
|
|
|
1874
2031
|
};
|
|
1875
2032
|
i0.ɵɵngDeclareClassMetadata({
|
|
1876
2033
|
minVersion: "12.0.0",
|
|
1877
|
-
version: "22.2.
|
|
2034
|
+
version: "22.2.3",
|
|
1878
2035
|
ngImport: i0,
|
|
1879
2036
|
type: ComponentRegistry,
|
|
1880
2037
|
decorators: [{
|
|
@@ -1957,7 +2114,7 @@ var ConsoleActivation = class ConsoleActivation {
|
|
|
1957
2114
|
}
|
|
1958
2115
|
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
1959
2116
|
minVersion: "12.0.0",
|
|
1960
|
-
version: "22.2.
|
|
2117
|
+
version: "22.2.3",
|
|
1961
2118
|
ngImport: i0,
|
|
1962
2119
|
type: ConsoleActivation,
|
|
1963
2120
|
deps: [],
|
|
@@ -1965,7 +2122,7 @@ var ConsoleActivation = class ConsoleActivation {
|
|
|
1965
2122
|
});
|
|
1966
2123
|
static ɵprov = i0.ɵɵngDeclareInjectable({
|
|
1967
2124
|
minVersion: "12.0.0",
|
|
1968
|
-
version: "22.2.
|
|
2125
|
+
version: "22.2.3",
|
|
1969
2126
|
ngImport: i0,
|
|
1970
2127
|
type: ConsoleActivation,
|
|
1971
2128
|
providedIn: "root"
|
|
@@ -1973,7 +2130,7 @@ var ConsoleActivation = class ConsoleActivation {
|
|
|
1973
2130
|
};
|
|
1974
2131
|
i0.ɵɵngDeclareClassMetadata({
|
|
1975
2132
|
minVersion: "12.0.0",
|
|
1976
|
-
version: "22.2.
|
|
2133
|
+
version: "22.2.3",
|
|
1977
2134
|
ngImport: i0,
|
|
1978
2135
|
type: ConsoleActivation,
|
|
1979
2136
|
decorators: [{
|
|
@@ -1984,7 +2141,8 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
1984
2141
|
function consoleModuleInstalled(module) {
|
|
1985
2142
|
return () => {
|
|
1986
2143
|
const activation = inject(ConsoleActivation);
|
|
1987
|
-
|
|
2144
|
+
const consoleAccess = inject(ConsoleAccessService);
|
|
2145
|
+
return inject(AppInitService).ready$.pipe(take(1), switchMap(() => from(consoleAccess.ensure())), map(() => activation.isInstalled(module)));
|
|
1988
2146
|
};
|
|
1989
2147
|
}
|
|
1990
2148
|
function consoleChildren(entries) {
|
|
@@ -2043,6 +2201,6 @@ function shouldStamp(url) {
|
|
|
2043
2201
|
if (path.startsWith("/api/v1/auth/")) return false;
|
|
2044
2202
|
return true;
|
|
2045
2203
|
}
|
|
2046
|
-
export { AppConfigState, AppInitService, AuthRefreshCoordinator, AuthState, BYPASS_AUTH, BYPASS_ELEVATION, CONSOLE_CONTRACT, CONSOLE_ENTRIES, CURRENT_SECTION, CmsLoaderComponent, ComponentRegistry, ConfigService, ConsoleActivation, ConsoleAssemblyError, ConsolePanelHost, CrossTabAuthSyncService, ELEVATION_PROMPT, ELEVATION_REQUIRED_HEADER, ELEVATION_WARNING_FACTOR_MISSING, ELEVATION_WARNING_MFA_OFF, ELEVATION_WARNING_PASSWORD_NOT_SET, ElevationService, EntitySearchService, ErrorHandlerService, IdentityApiClient, Login, LoginComponent, Logout, NaviGraphService, PatchCurrentUser, RealtimeTokenClient, RestoreSession, SetAppConfig, SetTokens, SidebarStateService, ThemeService, UserPreferencesService, assembleConsole, authGuard, authInterceptor, consoleChildren, consoleEntry, consoleModuleInstalled, elevationInterceptor, installedConsoleModules, isPortProvision, loginPageGuard, provideConsole, resolvePattern, satisfiesRange, sectionInterceptor };
|
|
2204
|
+
export { AppConfigState, AppInitService, AuthRefreshCoordinator, AuthState, BACKGROUND_REQUEST, BYPASS_AUTH, BYPASS_ELEVATION, CONSOLE_CONTRACT, CONSOLE_ENTRIES, CURRENT_SECTION, CmsLoaderComponent, ComponentRegistry, ConfigService, ConsoleAccessService, ConsoleActivation, ConsoleAssemblyError, ConsolePanelHost, CrossTabAuthSyncService, ELEVATION_ACTION, ELEVATION_NOTICE, ELEVATION_PROMPT, ELEVATION_REQUIRED_HEADER, ELEVATION_WARNING_FACTOR_MISSING, ELEVATION_WARNING_MFA_OFF, ELEVATION_WARNING_PASSWORD_NOT_SET, ElevationService, EntitySearchService, ErrorHandlerService, IdentityApiClient, Login, LoginComponent, Logout, NaviGraphService, PatchCurrentUser, RealtimeTokenClient, RestoreSession, SetAppConfig, SetTokens, SidebarStateService, ThemeService, UserPreferencesService, actionOf, assembleConsole, authGuard, authInterceptor, consoleChildren, consoleEntry, consoleModuleInstalled, elevationInterceptor, installedConsoleModules, isElevationRefusal, isPortProvision, loginPageGuard, naviAccessOf, provideConsole, resolvePattern, satisfiesRange, sectionInterceptor };
|
|
2047
2205
|
|
|
2048
2206
|
//# sourceMappingURL=coolms-core-angular.mjs.map
|