@mintplayer/ng-spark 22.30.0 → 22.31.0
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 +2 -2
- package/auth/README.md +216 -0
- package/fesm2022/mintplayer-ng-spark-auth-account.mjs +2023 -0
- package/fesm2022/mintplayer-ng-spark-auth-account.mjs.map +1 -0
- package/fesm2022/mintplayer-ng-spark-auth-auth-bar.mjs +96 -0
- package/fesm2022/mintplayer-ng-spark-auth-auth-bar.mjs.map +1 -0
- package/fesm2022/mintplayer-ng-spark-auth-confirm-email.mjs +200 -0
- package/fesm2022/mintplayer-ng-spark-auth-confirm-email.mjs.map +1 -0
- package/fesm2022/mintplayer-ng-spark-auth-core.mjs +452 -0
- package/fesm2022/mintplayer-ng-spark-auth-core.mjs.map +1 -0
- package/fesm2022/mintplayer-ng-spark-auth-external-login.mjs +201 -0
- package/fesm2022/mintplayer-ng-spark-auth-external-login.mjs.map +1 -0
- package/fesm2022/mintplayer-ng-spark-auth-forgot-password.mjs +196 -0
- package/fesm2022/mintplayer-ng-spark-auth-forgot-password.mjs.map +1 -0
- package/fesm2022/mintplayer-ng-spark-auth-guards.mjs +16 -0
- package/fesm2022/mintplayer-ng-spark-auth-guards.mjs.map +1 -0
- package/fesm2022/mintplayer-ng-spark-auth-interceptors.mjs +18 -0
- package/fesm2022/mintplayer-ng-spark-auth-interceptors.mjs.map +1 -0
- package/fesm2022/mintplayer-ng-spark-auth-login.mjs +282 -0
- package/fesm2022/mintplayer-ng-spark-auth-login.mjs.map +1 -0
- package/fesm2022/mintplayer-ng-spark-auth-models.mjs +114 -0
- package/fesm2022/mintplayer-ng-spark-auth-models.mjs.map +1 -0
- package/fesm2022/mintplayer-ng-spark-auth-pipes.mjs +43 -0
- package/fesm2022/mintplayer-ng-spark-auth-pipes.mjs.map +1 -0
- package/fesm2022/mintplayer-ng-spark-auth-register.mjs +216 -0
- package/fesm2022/mintplayer-ng-spark-auth-register.mjs.map +1 -0
- package/fesm2022/mintplayer-ng-spark-auth-reset-password.mjs +223 -0
- package/fesm2022/mintplayer-ng-spark-auth-reset-password.mjs.map +1 -0
- package/fesm2022/mintplayer-ng-spark-auth-routes.mjs +194 -0
- package/fesm2022/mintplayer-ng-spark-auth-routes.mjs.map +1 -0
- package/fesm2022/mintplayer-ng-spark-auth-sign-in.mjs +210 -0
- package/fesm2022/mintplayer-ng-spark-auth-sign-in.mjs.map +1 -0
- package/fesm2022/mintplayer-ng-spark-auth-two-factor.mjs +218 -0
- package/fesm2022/mintplayer-ng-spark-auth-two-factor.mjs.map +1 -0
- package/fesm2022/mintplayer-ng-spark-auth.mjs +48 -0
- package/fesm2022/mintplayer-ng-spark-auth.mjs.map +1 -0
- package/fesm2022/mintplayer-ng-spark-client-operations.mjs +294 -2
- package/fesm2022/mintplayer-ng-spark-client-operations.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-identity-provider-connected-applications.mjs +389 -0
- package/fesm2022/mintplayer-ng-spark-identity-provider-connected-applications.mjs.map +1 -0
- package/fesm2022/mintplayer-ng-spark-identity-provider-core.mjs +204 -0
- package/fesm2022/mintplayer-ng-spark-identity-provider-core.mjs.map +1 -0
- package/fesm2022/mintplayer-ng-spark-identity-provider-developers.mjs +486 -0
- package/fesm2022/mintplayer-ng-spark-identity-provider-developers.mjs.map +1 -0
- package/fesm2022/mintplayer-ng-spark-identity-provider-management.mjs +350 -0
- package/fesm2022/mintplayer-ng-spark-identity-provider-management.mjs.map +1 -0
- package/fesm2022/mintplayer-ng-spark-identity-provider.mjs +66 -0
- package/fesm2022/mintplayer-ng-spark-identity-provider.mjs.map +1 -0
- package/fesm2022/mintplayer-ng-spark-pwa.mjs +162 -0
- package/fesm2022/mintplayer-ng-spark-pwa.mjs.map +1 -0
- package/fesm2022/mintplayer-ng-spark-services.mjs +11 -1
- package/fesm2022/mintplayer-ng-spark-services.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-shell.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark.mjs.map +1 -1
- package/package.json +99 -1
- package/types/mintplayer-ng-spark-auth-account.d.ts +255 -0
- package/types/mintplayer-ng-spark-auth-auth-bar.d.ts +22 -0
- package/types/mintplayer-ng-spark-auth-confirm-email.d.ts +21 -0
- package/types/mintplayer-ng-spark-auth-core.d.ts +197 -0
- package/types/mintplayer-ng-spark-auth-external-login.d.ts +132 -0
- package/types/mintplayer-ng-spark-auth-forgot-password.d.ts +18 -0
- package/types/mintplayer-ng-spark-auth-guards.d.ts +15 -0
- package/types/mintplayer-ng-spark-auth-interceptors.d.ts +2 -0
- package/types/mintplayer-ng-spark-auth-login.d.ts +46 -0
- package/types/mintplayer-ng-spark-auth-models.d.ts +489 -0
- package/types/mintplayer-ng-spark-auth-pipes.d.ts +8 -0
- package/types/mintplayer-ng-spark-auth-register.d.ts +21 -0
- package/types/mintplayer-ng-spark-auth-reset-password.d.ts +25 -0
- package/types/mintplayer-ng-spark-auth-routes.d.ts +154 -0
- package/types/mintplayer-ng-spark-auth-sign-in.d.ts +84 -0
- package/types/mintplayer-ng-spark-auth-two-factor.d.ts +23 -0
- package/types/mintplayer-ng-spark-auth.d.ts +24 -0
- package/types/mintplayer-ng-spark-client-operations.d.ts +78 -3
- package/types/mintplayer-ng-spark-identity-provider-connected-applications.d.ts +35 -0
- package/types/mintplayer-ng-spark-identity-provider-core.d.ts +144 -0
- package/types/mintplayer-ng-spark-identity-provider-developers.d.ts +55 -0
- package/types/mintplayer-ng-spark-identity-provider-management.d.ts +33 -0
- package/types/mintplayer-ng-spark-identity-provider.d.ts +64 -0
- package/types/mintplayer-ng-spark-pwa.d.ts +89 -0
- package/types/mintplayer-ng-spark-services.d.ts +10 -0
- package/types/mintplayer-ng-spark-shell.d.ts +2 -2
- package/types/mintplayer-ng-spark.d.ts +4 -4
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
import * as i0 from "@angular/core";
|
|
2
|
+
import { ChangeDetectionStrategy, Component, inject, signal } from "@angular/core";
|
|
3
|
+
import { RouterLink } from "@angular/router";
|
|
4
|
+
import { Color } from "@mintplayer/ng-bootstrap";
|
|
5
|
+
import { BsAlertComponent } from "@mintplayer/ng-bootstrap/alert";
|
|
6
|
+
import { BsBadgeComponent } from "@mintplayer/ng-bootstrap/badge";
|
|
7
|
+
import { BsCardComponent, BsCardHeaderComponent } from "@mintplayer/ng-bootstrap/card";
|
|
8
|
+
import { BsSpinnerComponent } from "@mintplayer/ng-bootstrap/spinner";
|
|
9
|
+
import { BsTableComponent } from "@mintplayer/ng-bootstrap/table";
|
|
10
|
+
import { SPARK_IDENTITY_PROVIDER_QUERIES, SparkIdentityProviderDatePipe, SparkIdentityProviderPortalService, SparkIdentityProviderTextPipe, identityProviderQueryLink, injectIdentityProviderText } from "@mintplayer/ng-spark/identity-provider/core";
|
|
11
|
+
const QUEUES = [
|
|
12
|
+
{
|
|
13
|
+
label: "query.DeveloperRequests",
|
|
14
|
+
alias: SPARK_IDENTITY_PROVIDER_QUERIES.developerRequests
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
label: "query.ScopeApprovals",
|
|
18
|
+
alias: SPARK_IDENTITY_PROVIDER_QUERIES.scopeApprovals
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
label: "query.GoLiveReviews",
|
|
22
|
+
alias: SPARK_IDENTITY_PROVIDER_QUERIES.goLiveReviews
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
label: "query.Applications",
|
|
26
|
+
alias: SPARK_IDENTITY_PROVIDER_QUERIES.applications
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
label: "query.Resources",
|
|
30
|
+
alias: SPARK_IDENTITY_PROVIDER_QUERIES.resources
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
label: "query.Grants",
|
|
34
|
+
alias: SPARK_IDENTITY_PROVIDER_QUERIES.grants
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
label: "query.AuditEvents",
|
|
38
|
+
alias: SPARK_IDENTITY_PROVIDER_QUERIES.auditEvents
|
|
39
|
+
}
|
|
40
|
+
];
|
|
41
|
+
var SparkIdentityProviderManagementComponent = class SparkIdentityProviderManagementComponent {
|
|
42
|
+
text = injectIdentityProviderText();
|
|
43
|
+
portal = inject(SparkIdentityProviderPortalService);
|
|
44
|
+
colors = Color;
|
|
45
|
+
queues = QUEUES;
|
|
46
|
+
queryLink = identityProviderQueryLink;
|
|
47
|
+
loading = signal(true, ...ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []);
|
|
48
|
+
busy = signal(false, ...ngDevMode ? [{ debugName: "busy" }] : /* istanbul ignore next */ []);
|
|
49
|
+
forbidden = signal(false, ...ngDevMode ? [{ debugName: "forbidden" }] : /* istanbul ignore next */ []);
|
|
50
|
+
keys = signal([], ...ngDevMode ? [{ debugName: "keys" }] : /* istanbul ignore next */ []);
|
|
51
|
+
changes = signal([], ...ngDevMode ? [{ debugName: "changes" }] : /* istanbul ignore next */ []);
|
|
52
|
+
error = signal("", ...ngDevMode ? [{ debugName: "error" }] : /* istanbul ignore next */ []);
|
|
53
|
+
notice = signal("", ...ngDevMode ? [{ debugName: "notice" }] : /* istanbul ignore next */ []);
|
|
54
|
+
constructor() {
|
|
55
|
+
this.load();
|
|
56
|
+
}
|
|
57
|
+
async load() {
|
|
58
|
+
const result = await this.portal.signingKeys();
|
|
59
|
+
if (result.ok) this.keys.set(result.value);
|
|
60
|
+
else if (result.status === 403) this.forbidden.set(true);
|
|
61
|
+
else this.error.set(this.text("loadFailed"));
|
|
62
|
+
this.loading.set(false);
|
|
63
|
+
}
|
|
64
|
+
async rotate() {
|
|
65
|
+
if (!confirm(this.text("admin.confirmRotate"))) return;
|
|
66
|
+
this.busy.set(true);
|
|
67
|
+
this.error.set("");
|
|
68
|
+
this.notice.set("");
|
|
69
|
+
const result = await this.portal.rotateSigningKeys();
|
|
70
|
+
if (result.ok) {
|
|
71
|
+
this.changes.set(result.value.changes ?? []);
|
|
72
|
+
this.notice.set(this.text("admin.rotated"));
|
|
73
|
+
await this.load();
|
|
74
|
+
} else if (result.status === 403) this.forbidden.set(true);
|
|
75
|
+
else this.error.set(this.text("admin.rotateFailed"));
|
|
76
|
+
this.busy.set(false);
|
|
77
|
+
}
|
|
78
|
+
stateColor(state) {
|
|
79
|
+
switch (state.toLowerCase()) {
|
|
80
|
+
case "active": return Color.success;
|
|
81
|
+
case "retired": return Color.secondary;
|
|
82
|
+
default: return Color.info;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
86
|
+
minVersion: "12.0.0",
|
|
87
|
+
version: "22.2.0",
|
|
88
|
+
ngImport: i0,
|
|
89
|
+
type: SparkIdentityProviderManagementComponent,
|
|
90
|
+
deps: [],
|
|
91
|
+
target: i0.ɵɵFactoryTarget.Component
|
|
92
|
+
});
|
|
93
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({
|
|
94
|
+
minVersion: "17.0.0",
|
|
95
|
+
version: "22.2.0",
|
|
96
|
+
type: SparkIdentityProviderManagementComponent,
|
|
97
|
+
isStandalone: true,
|
|
98
|
+
selector: "spark-identity-provider-management",
|
|
99
|
+
ngImport: i0,
|
|
100
|
+
template: `
|
|
101
|
+
<div class="d-flex justify-content-center">
|
|
102
|
+
<bs-card style="width: 100%; max-width: 960px;">
|
|
103
|
+
<bs-card-header><h3 class="mb-0">{{ 'admin.title' | idp }}</h3></bs-card-header>
|
|
104
|
+
<div class="p-4">
|
|
105
|
+
@if (loading()) {
|
|
106
|
+
<div class="text-center"><bs-spinner /></div>
|
|
107
|
+
} @else if (forbidden()) {
|
|
108
|
+
<bs-alert [type]="colors.warning" class="d-block spark-idp-not-allowed">{{ 'admin.notAllowed' | idp }}</bs-alert>
|
|
109
|
+
} @else {
|
|
110
|
+
@if (notice()) {
|
|
111
|
+
<bs-alert [type]="colors.success" class="mb-3 d-block spark-idp-notice">{{ notice() }}</bs-alert>
|
|
112
|
+
}
|
|
113
|
+
@if (error()) {
|
|
114
|
+
<bs-alert [type]="colors.danger" class="mb-3 d-block spark-idp-error">{{ error() }}</bs-alert>
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
<h5>{{ 'admin.queues' | idp }}</h5>
|
|
118
|
+
<ul class="list-unstyled mb-4">
|
|
119
|
+
@for (queue of queues; track queue.alias) {
|
|
120
|
+
<li class="py-1"><a class="spark-idp-queue" [routerLink]="queryLink(queue.alias)">{{ queue.label | idp }}</a></li>
|
|
121
|
+
}
|
|
122
|
+
</ul>
|
|
123
|
+
|
|
124
|
+
<h5>{{ 'admin.keys' | idp }}</h5>
|
|
125
|
+
<p class="text-muted">{{ 'admin.keysHint' | idp }}</p>
|
|
126
|
+
@if (keys().length) {
|
|
127
|
+
<bs-table [isResponsive]="true">
|
|
128
|
+
<thead>
|
|
129
|
+
<tr>
|
|
130
|
+
<th>{{ 'admin.kid' | idp }}</th>
|
|
131
|
+
<th>{{ 'admin.algorithm' | idp }}</th>
|
|
132
|
+
<th>{{ 'admin.state' | idp }}</th>
|
|
133
|
+
<th>{{ 'admin.created' | idp }}</th>
|
|
134
|
+
<th>{{ 'admin.activated' | idp }}</th>
|
|
135
|
+
<th>{{ 'admin.retired' | idp }}</th>
|
|
136
|
+
</tr>
|
|
137
|
+
</thead>
|
|
138
|
+
<tbody>
|
|
139
|
+
@for (key of keys(); track key.kid) {
|
|
140
|
+
<tr class="spark-idp-key" [attr.data-state]="key.state">
|
|
141
|
+
<td class="font-monospace text-break">{{ key.kid }}</td>
|
|
142
|
+
<td>{{ key.algorithm }}</td>
|
|
143
|
+
<td><bs-badge [type]="stateColor(key.state)">{{ key.state }}</bs-badge></td>
|
|
144
|
+
<td>{{ key.createdAt | idpDate }}</td>
|
|
145
|
+
<td>{{ key.activatedAt | idpDate }}</td>
|
|
146
|
+
<td>{{ key.retiredAt | idpDate }}</td>
|
|
147
|
+
</tr>
|
|
148
|
+
}
|
|
149
|
+
</tbody>
|
|
150
|
+
</bs-table>
|
|
151
|
+
} @else {
|
|
152
|
+
<p class="text-muted spark-idp-no-keys">{{ 'admin.noKeys' | idp }}</p>
|
|
153
|
+
}
|
|
154
|
+
<button type="button" class="btn btn-outline-danger spark-idp-rotate" [disabled]="busy()" (click)="rotate()">
|
|
155
|
+
@if (busy()) { <bs-spinner class="me-1" /> }
|
|
156
|
+
{{ 'admin.rotate' | idp }}
|
|
157
|
+
</button>
|
|
158
|
+
@if (changes().length) {
|
|
159
|
+
<ul class="small text-muted mt-3 mb-0 spark-idp-rotation-changes">
|
|
160
|
+
@for (change of changes(); track $index) { <li>{{ change }}</li> }
|
|
161
|
+
</ul>
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
</div>
|
|
165
|
+
</bs-card>
|
|
166
|
+
</div>
|
|
167
|
+
`,
|
|
168
|
+
isInline: true,
|
|
169
|
+
dependencies: [
|
|
170
|
+
{
|
|
171
|
+
kind: "directive",
|
|
172
|
+
type: RouterLink,
|
|
173
|
+
selector: "[routerLink]",
|
|
174
|
+
inputs: [
|
|
175
|
+
"target",
|
|
176
|
+
"queryParams",
|
|
177
|
+
"fragment",
|
|
178
|
+
"queryParamsHandling",
|
|
179
|
+
"state",
|
|
180
|
+
"info",
|
|
181
|
+
"relativeTo",
|
|
182
|
+
"preserveFragment",
|
|
183
|
+
"skipLocationChange",
|
|
184
|
+
"replaceUrl",
|
|
185
|
+
"browserUrl",
|
|
186
|
+
"routerLink"
|
|
187
|
+
]
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
kind: "component",
|
|
191
|
+
type: BsAlertComponent,
|
|
192
|
+
selector: "bs-alert",
|
|
193
|
+
inputs: [
|
|
194
|
+
"type",
|
|
195
|
+
"announce",
|
|
196
|
+
"isVisible"
|
|
197
|
+
],
|
|
198
|
+
outputs: ["isVisibleChange", "afterOpenedOrClosed"]
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
kind: "component",
|
|
202
|
+
type: BsBadgeComponent,
|
|
203
|
+
selector: "bs-badge",
|
|
204
|
+
inputs: [
|
|
205
|
+
"type",
|
|
206
|
+
"unit",
|
|
207
|
+
"decorative"
|
|
208
|
+
]
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
kind: "component",
|
|
212
|
+
type: BsCardComponent,
|
|
213
|
+
selector: "bs-card",
|
|
214
|
+
inputs: ["color", "outline"]
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
kind: "component",
|
|
218
|
+
type: BsCardHeaderComponent,
|
|
219
|
+
selector: "bs-card-header",
|
|
220
|
+
inputs: ["color", "navStyle"]
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
kind: "component",
|
|
224
|
+
type: BsSpinnerComponent,
|
|
225
|
+
selector: "bs-spinner",
|
|
226
|
+
inputs: ["type", "color"]
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
kind: "component",
|
|
230
|
+
type: BsTableComponent,
|
|
231
|
+
selector: "bs-table",
|
|
232
|
+
inputs: [
|
|
233
|
+
"isResponsive",
|
|
234
|
+
"striped",
|
|
235
|
+
"hover",
|
|
236
|
+
"border",
|
|
237
|
+
"ariaRowCount"
|
|
238
|
+
]
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
kind: "pipe",
|
|
242
|
+
type: SparkIdentityProviderTextPipe,
|
|
243
|
+
name: "idp"
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
kind: "pipe",
|
|
247
|
+
type: SparkIdentityProviderDatePipe,
|
|
248
|
+
name: "idpDate"
|
|
249
|
+
}
|
|
250
|
+
],
|
|
251
|
+
changeDetection: i0.ChangeDetectionStrategy.OnPush
|
|
252
|
+
});
|
|
253
|
+
};
|
|
254
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
255
|
+
minVersion: "12.0.0",
|
|
256
|
+
version: "22.2.0",
|
|
257
|
+
ngImport: i0,
|
|
258
|
+
type: SparkIdentityProviderManagementComponent,
|
|
259
|
+
decorators: [{
|
|
260
|
+
type: Component,
|
|
261
|
+
args: [{
|
|
262
|
+
selector: "spark-identity-provider-management",
|
|
263
|
+
standalone: true,
|
|
264
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
265
|
+
imports: [
|
|
266
|
+
RouterLink,
|
|
267
|
+
BsAlertComponent,
|
|
268
|
+
BsBadgeComponent,
|
|
269
|
+
BsCardComponent,
|
|
270
|
+
BsCardHeaderComponent,
|
|
271
|
+
BsSpinnerComponent,
|
|
272
|
+
BsTableComponent,
|
|
273
|
+
SparkIdentityProviderTextPipe,
|
|
274
|
+
SparkIdentityProviderDatePipe
|
|
275
|
+
],
|
|
276
|
+
template: `
|
|
277
|
+
<div class="d-flex justify-content-center">
|
|
278
|
+
<bs-card style="width: 100%; max-width: 960px;">
|
|
279
|
+
<bs-card-header><h3 class="mb-0">{{ 'admin.title' | idp }}</h3></bs-card-header>
|
|
280
|
+
<div class="p-4">
|
|
281
|
+
@if (loading()) {
|
|
282
|
+
<div class="text-center"><bs-spinner /></div>
|
|
283
|
+
} @else if (forbidden()) {
|
|
284
|
+
<bs-alert [type]="colors.warning" class="d-block spark-idp-not-allowed">{{ 'admin.notAllowed' | idp }}</bs-alert>
|
|
285
|
+
} @else {
|
|
286
|
+
@if (notice()) {
|
|
287
|
+
<bs-alert [type]="colors.success" class="mb-3 d-block spark-idp-notice">{{ notice() }}</bs-alert>
|
|
288
|
+
}
|
|
289
|
+
@if (error()) {
|
|
290
|
+
<bs-alert [type]="colors.danger" class="mb-3 d-block spark-idp-error">{{ error() }}</bs-alert>
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
<h5>{{ 'admin.queues' | idp }}</h5>
|
|
294
|
+
<ul class="list-unstyled mb-4">
|
|
295
|
+
@for (queue of queues; track queue.alias) {
|
|
296
|
+
<li class="py-1"><a class="spark-idp-queue" [routerLink]="queryLink(queue.alias)">{{ queue.label | idp }}</a></li>
|
|
297
|
+
}
|
|
298
|
+
</ul>
|
|
299
|
+
|
|
300
|
+
<h5>{{ 'admin.keys' | idp }}</h5>
|
|
301
|
+
<p class="text-muted">{{ 'admin.keysHint' | idp }}</p>
|
|
302
|
+
@if (keys().length) {
|
|
303
|
+
<bs-table [isResponsive]="true">
|
|
304
|
+
<thead>
|
|
305
|
+
<tr>
|
|
306
|
+
<th>{{ 'admin.kid' | idp }}</th>
|
|
307
|
+
<th>{{ 'admin.algorithm' | idp }}</th>
|
|
308
|
+
<th>{{ 'admin.state' | idp }}</th>
|
|
309
|
+
<th>{{ 'admin.created' | idp }}</th>
|
|
310
|
+
<th>{{ 'admin.activated' | idp }}</th>
|
|
311
|
+
<th>{{ 'admin.retired' | idp }}</th>
|
|
312
|
+
</tr>
|
|
313
|
+
</thead>
|
|
314
|
+
<tbody>
|
|
315
|
+
@for (key of keys(); track key.kid) {
|
|
316
|
+
<tr class="spark-idp-key" [attr.data-state]="key.state">
|
|
317
|
+
<td class="font-monospace text-break">{{ key.kid }}</td>
|
|
318
|
+
<td>{{ key.algorithm }}</td>
|
|
319
|
+
<td><bs-badge [type]="stateColor(key.state)">{{ key.state }}</bs-badge></td>
|
|
320
|
+
<td>{{ key.createdAt | idpDate }}</td>
|
|
321
|
+
<td>{{ key.activatedAt | idpDate }}</td>
|
|
322
|
+
<td>{{ key.retiredAt | idpDate }}</td>
|
|
323
|
+
</tr>
|
|
324
|
+
}
|
|
325
|
+
</tbody>
|
|
326
|
+
</bs-table>
|
|
327
|
+
} @else {
|
|
328
|
+
<p class="text-muted spark-idp-no-keys">{{ 'admin.noKeys' | idp }}</p>
|
|
329
|
+
}
|
|
330
|
+
<button type="button" class="btn btn-outline-danger spark-idp-rotate" [disabled]="busy()" (click)="rotate()">
|
|
331
|
+
@if (busy()) { <bs-spinner class="me-1" /> }
|
|
332
|
+
{{ 'admin.rotate' | idp }}
|
|
333
|
+
</button>
|
|
334
|
+
@if (changes().length) {
|
|
335
|
+
<ul class="small text-muted mt-3 mb-0 spark-idp-rotation-changes">
|
|
336
|
+
@for (change of changes(); track $index) { <li>{{ change }}</li> }
|
|
337
|
+
</ul>
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
</div>
|
|
341
|
+
</bs-card>
|
|
342
|
+
</div>
|
|
343
|
+
`
|
|
344
|
+
}]
|
|
345
|
+
}],
|
|
346
|
+
ctorParameters: () => []
|
|
347
|
+
});
|
|
348
|
+
export { SparkIdentityProviderManagementComponent };
|
|
349
|
+
|
|
350
|
+
//# sourceMappingURL=mintplayer-ng-spark-identity-provider-management.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mintplayer-ng-spark-identity-provider-management.mjs","names":[],"sources":["../../identity-provider/management/src/spark-identity-provider-management.component.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, inject, signal } from '@angular/core';\nimport { RouterLink } from '@angular/router';\nimport { Color } from '@mintplayer/ng-bootstrap';\nimport { BsAlertComponent } from '@mintplayer/ng-bootstrap/alert';\nimport { BsBadgeComponent } from '@mintplayer/ng-bootstrap/badge';\nimport { BsCardComponent, BsCardHeaderComponent } from '@mintplayer/ng-bootstrap/card';\nimport { BsSpinnerComponent } from '@mintplayer/ng-bootstrap/spinner';\nimport { BsTableComponent } from '@mintplayer/ng-bootstrap/table';\nimport {\n SPARK_IDENTITY_PROVIDER_QUERIES,\n SparkIdentityProviderDatePipe,\n SparkIdentityProviderPortalService,\n SparkIdentityProviderTextPipe,\n SparkSigningKey,\n identityProviderQueryLink,\n injectIdentityProviderText,\n} from '@mintplayer/ng-spark/identity-provider/core';\n\n/** The library's queues and records, in the order an administrator works through them. */\nconst QUEUES: { label: string; alias: string }[] = [\n { label: 'query.DeveloperRequests', alias: SPARK_IDENTITY_PROVIDER_QUERIES.developerRequests },\n { label: 'query.ScopeApprovals', alias: SPARK_IDENTITY_PROVIDER_QUERIES.scopeApprovals },\n { label: 'query.GoLiveReviews', alias: SPARK_IDENTITY_PROVIDER_QUERIES.goLiveReviews },\n { label: 'query.Applications', alias: SPARK_IDENTITY_PROVIDER_QUERIES.applications },\n { label: 'query.Resources', alias: SPARK_IDENTITY_PROVIDER_QUERIES.resources },\n { label: 'query.Grants', alias: SPARK_IDENTITY_PROVIDER_QUERIES.grants },\n { label: 'query.AuditEvents', alias: SPARK_IDENTITY_PROVIDER_QUERIES.auditEvents },\n];\n\n/**\n * The identity provider's management page (`identity-provider/admin`, PRD D9), for its\n * administrators (`ManageAll/IdentityProvider`): the signing keys with a rotate-now action, and links\n * to the library's queues (developer requests, scope approvals, go-live reviews) and records (grants,\n * the audit trail) in the generic query page. The server answers 403 to anyone else, and the page\n * then says so instead of rendering an empty administration screen.\n */\n@Component({\n selector: 'spark-identity-provider-management',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [\n RouterLink, BsAlertComponent, BsBadgeComponent, BsCardComponent, BsCardHeaderComponent, BsSpinnerComponent,\n BsTableComponent, SparkIdentityProviderTextPipe, SparkIdentityProviderDatePipe,\n ],\n template: `\n <div class=\"d-flex justify-content-center\">\n <bs-card style=\"width: 100%; max-width: 960px;\">\n <bs-card-header><h3 class=\"mb-0\">{{ 'admin.title' | idp }}</h3></bs-card-header>\n <div class=\"p-4\">\n @if (loading()) {\n <div class=\"text-center\"><bs-spinner /></div>\n } @else if (forbidden()) {\n <bs-alert [type]=\"colors.warning\" class=\"d-block spark-idp-not-allowed\">{{ 'admin.notAllowed' | idp }}</bs-alert>\n } @else {\n @if (notice()) {\n <bs-alert [type]=\"colors.success\" class=\"mb-3 d-block spark-idp-notice\">{{ notice() }}</bs-alert>\n }\n @if (error()) {\n <bs-alert [type]=\"colors.danger\" class=\"mb-3 d-block spark-idp-error\">{{ error() }}</bs-alert>\n }\n\n <h5>{{ 'admin.queues' | idp }}</h5>\n <ul class=\"list-unstyled mb-4\">\n @for (queue of queues; track queue.alias) {\n <li class=\"py-1\"><a class=\"spark-idp-queue\" [routerLink]=\"queryLink(queue.alias)\">{{ queue.label | idp }}</a></li>\n }\n </ul>\n\n <h5>{{ 'admin.keys' | idp }}</h5>\n <p class=\"text-muted\">{{ 'admin.keysHint' | idp }}</p>\n @if (keys().length) {\n <bs-table [isResponsive]=\"true\">\n <thead>\n <tr>\n <th>{{ 'admin.kid' | idp }}</th>\n <th>{{ 'admin.algorithm' | idp }}</th>\n <th>{{ 'admin.state' | idp }}</th>\n <th>{{ 'admin.created' | idp }}</th>\n <th>{{ 'admin.activated' | idp }}</th>\n <th>{{ 'admin.retired' | idp }}</th>\n </tr>\n </thead>\n <tbody>\n @for (key of keys(); track key.kid) {\n <tr class=\"spark-idp-key\" [attr.data-state]=\"key.state\">\n <td class=\"font-monospace text-break\">{{ key.kid }}</td>\n <td>{{ key.algorithm }}</td>\n <td><bs-badge [type]=\"stateColor(key.state)\">{{ key.state }}</bs-badge></td>\n <td>{{ key.createdAt | idpDate }}</td>\n <td>{{ key.activatedAt | idpDate }}</td>\n <td>{{ key.retiredAt | idpDate }}</td>\n </tr>\n }\n </tbody>\n </bs-table>\n } @else {\n <p class=\"text-muted spark-idp-no-keys\">{{ 'admin.noKeys' | idp }}</p>\n }\n <button type=\"button\" class=\"btn btn-outline-danger spark-idp-rotate\" [disabled]=\"busy()\" (click)=\"rotate()\">\n @if (busy()) { <bs-spinner class=\"me-1\" /> }\n {{ 'admin.rotate' | idp }}\n </button>\n @if (changes().length) {\n <ul class=\"small text-muted mt-3 mb-0 spark-idp-rotation-changes\">\n @for (change of changes(); track $index) { <li>{{ change }}</li> }\n </ul>\n }\n }\n </div>\n </bs-card>\n </div>\n `,\n})\nexport class SparkIdentityProviderManagementComponent {\n private readonly text = injectIdentityProviderText();\n private readonly portal = inject(SparkIdentityProviderPortalService);\n protected readonly colors = Color;\n protected readonly queues = QUEUES;\n protected readonly queryLink = identityProviderQueryLink;\n\n readonly loading = signal(true);\n readonly busy = signal(false);\n readonly forbidden = signal(false);\n readonly keys = signal<SparkSigningKey[]>([]);\n readonly changes = signal<string[]>([]);\n readonly error = signal('');\n readonly notice = signal('');\n\n constructor() {\n void this.load();\n }\n\n async load(): Promise<void> {\n const result = await this.portal.signingKeys();\n if (result.ok) {\n this.keys.set(result.value);\n } else if (result.status === 403) {\n this.forbidden.set(true);\n } else {\n this.error.set(this.text('loadFailed'));\n }\n this.loading.set(false);\n }\n\n async rotate(): Promise<void> {\n if (!confirm(this.text('admin.confirmRotate'))) return;\n this.busy.set(true);\n this.error.set('');\n this.notice.set('');\n const result = await this.portal.rotateSigningKeys();\n if (result.ok) {\n this.changes.set(result.value.changes ?? []);\n this.notice.set(this.text('admin.rotated'));\n await this.load();\n } else if (result.status === 403) {\n this.forbidden.set(true);\n } else {\n this.error.set(this.text('admin.rotateFailed'));\n }\n this.busy.set(false);\n }\n\n protected stateColor(state: string): Color {\n switch (state.toLowerCase()) {\n case 'active': return Color.success;\n case 'retired': return Color.secondary;\n default: return Color.info;\n }\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { sparkAuthGuard } from "@mintplayer/ng-spark/auth/guards";
|
|
2
|
+
export * from "@mintplayer/ng-spark/identity-provider/core";
|
|
3
|
+
function entryPath(entry, defaultPath) {
|
|
4
|
+
if (entry === void 0) return defaultPath;
|
|
5
|
+
return typeof entry === "string" ? entry : entry.path;
|
|
6
|
+
}
|
|
7
|
+
function child(entry, path, loader, guard) {
|
|
8
|
+
const component = typeof entry === "object" && entry.component ? entry.component : void 0;
|
|
9
|
+
return {
|
|
10
|
+
path,
|
|
11
|
+
loadComponent: component ? () => Promise.resolve(component) : loader,
|
|
12
|
+
...guard.length ? { canActivate: guard } : {}
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
function withIdentityProvider(...features) {
|
|
16
|
+
const children = [];
|
|
17
|
+
const paths = {};
|
|
18
|
+
const accountLinks = [];
|
|
19
|
+
for (const feature of features) {
|
|
20
|
+
children.push(...feature.children);
|
|
21
|
+
Object.assign(paths, feature.paths);
|
|
22
|
+
if (feature.accountLinks) accountLinks.push(...feature.accountLinks);
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
children,
|
|
26
|
+
paths,
|
|
27
|
+
accountLinks
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function withConnectedApplications(options) {
|
|
31
|
+
const guard = options?.canActivate ?? [sparkAuthGuard];
|
|
32
|
+
const path = entryPath(options?.connectedApplications, "account/applications");
|
|
33
|
+
return {
|
|
34
|
+
paths: { connectedApplications: "/" + path },
|
|
35
|
+
accountLinks: [{
|
|
36
|
+
key: "connectedApplications",
|
|
37
|
+
path: "/" + path,
|
|
38
|
+
label: {
|
|
39
|
+
en: "Connected applications",
|
|
40
|
+
fr: "Applications connectées",
|
|
41
|
+
nl: "Verbonden applicaties"
|
|
42
|
+
}
|
|
43
|
+
}],
|
|
44
|
+
children: [child(options?.connectedApplications, path, () => import("@mintplayer/ng-spark/identity-provider/connected-applications").then((m) => m.SparkConnectedApplicationsComponent), guard)]
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
function withDeveloperRoutes(options) {
|
|
48
|
+
const guard = options?.canActivate ?? [sparkAuthGuard];
|
|
49
|
+
const developers = entryPath(options?.developers, "developers");
|
|
50
|
+
const invitation = entryPath(options?.invitation, "developers/invitations/:token");
|
|
51
|
+
return {
|
|
52
|
+
paths: { developers: "/" + developers },
|
|
53
|
+
children: [child(options?.developers, developers, () => import("@mintplayer/ng-spark/identity-provider/developers").then((m) => m.SparkDevelopersComponent), guard), child(options?.invitation, invitation, () => import("@mintplayer/ng-spark/identity-provider/developers").then((m) => m.SparkAcceptInvitationComponent), guard)]
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function withManagementRoutes(options) {
|
|
57
|
+
const guard = options?.canActivate ?? [sparkAuthGuard];
|
|
58
|
+
const path = entryPath(options?.identityProviderManagement, "identity-provider/admin");
|
|
59
|
+
return {
|
|
60
|
+
paths: { identityProviderManagement: "/" + path },
|
|
61
|
+
children: [child(options?.identityProviderManagement, path, () => import("@mintplayer/ng-spark/identity-provider/management").then((m) => m.SparkIdentityProviderManagementComponent), guard)]
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
export { withConnectedApplications, withDeveloperRoutes, withIdentityProvider, withManagementRoutes };
|
|
65
|
+
|
|
66
|
+
//# sourceMappingURL=mintplayer-ng-spark-identity-provider.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mintplayer-ng-spark-identity-provider.mjs","names":[],"sources":["../../identity-provider/src/identity-provider-routes.ts"],"sourcesContent":["import type { CanActivateFn } from '@angular/router';\nimport { sparkAuthGuard } from '@mintplayer/ng-spark/auth/guards';\nimport type { SparkAuthRouteEntry, SparkAuthRoutePaths, SparkAccountOverviewLink } from '@mintplayer/ng-spark/auth/models';\nimport type { SparkAuthRoutesFeature } from '@mintplayer/ng-spark/auth/routes';\n\ntype Child = SparkAuthRoutesFeature['children'][number];\ntype Loader = () => Promise<any>;\n\n/**\n * One group of identity-provider pages, passed to {@link withIdentityProvider}. Produced only by\n * `withConnectedApplications()`, `withDeveloperRoutes()` and `withManagementRoutes()`.\n */\nexport interface SparkIdentityProviderRoutesFeature {\n readonly children: Child[];\n readonly paths: SparkAuthRoutePaths;\n readonly accountLinks?: SparkAccountOverviewLink[];\n}\n\n/** Every feature's guard option: defaults to `[sparkAuthGuard]`; `[]` under an already guarded parent. */\nexport interface SparkIdentityProviderGuardOptions {\n canActivate?: CanActivateFn[];\n}\n\nfunction entryPath(entry: SparkAuthRouteEntry | undefined, defaultPath: string): string {\n if (entry === undefined) return defaultPath;\n return typeof entry === 'string' ? entry : entry.path;\n}\n\n/** An explicitly supplied component wins over the library's lazy import, as in `sparkAuthRoutes()`. */\nfunction child(entry: SparkAuthRouteEntry | undefined, path: string, loader: Loader, guard: CanActivateFn[]): Child {\n const component = typeof entry === 'object' && entry.component ? entry.component : undefined;\n return {\n path,\n loadComponent: component ? () => Promise.resolve(component) : loader,\n ...(guard.length ? { canActivate: guard } : {}),\n };\n}\n\n/**\n * The identity provider's SPA pages (`docs/identity_provider_platform_PRD.md` D7), as one\n * `sparkAuthRoutes()` feature:\n *\n * ```ts\n * sparkAuthRoutes(\n * withLocalLogin(), withAccount(),\n * withIdentityProvider(withConnectedApplications(), withDeveloperRoutes(), withManagementRoutes()),\n * )\n * ```\n *\n * The sub-features merge into one feature, and each page keeps its own `import()` (its own secondary\n * entry point), so a page nobody opts into is never bundled. The pages popups show (`/connect/*`:\n * sign-in, consent, device, logout, error) are server-rendered and not part of this.\n *\n * Every path has a literal first segment, so the group neither shadows `sparkRoutes()` nor is shadowed\n * by it. Apps and resources are edited in the generic `po/:type` screens (PRD Q1 = A).\n */\nexport function withIdentityProvider(...features: SparkIdentityProviderRoutesFeature[]): SparkAuthRoutesFeature {\n const children: Child[] = [];\n const paths: SparkAuthRoutePaths = {};\n const accountLinks: SparkAccountOverviewLink[] = [];\n for (const feature of features) {\n children.push(...feature.children);\n Object.assign(paths, feature.paths);\n if (feature.accountLinks) accountLinks.push(...feature.accountLinks);\n }\n return { children, paths, accountLinks };\n}\n\n/**\n * Mounts the connected-applications page (default `account/applications`, PRD D6): the applications\n * the user granted access to, with whole-grant and per-scope withdrawal. Sets\n * `SPARK_AUTH_ROUTE_PATHS.connectedApplications` and adds a link to the account overview.\n */\nexport function withConnectedApplications(\n options?: SparkIdentityProviderGuardOptions & { connectedApplications?: SparkAuthRouteEntry },\n): SparkIdentityProviderRoutesFeature {\n const guard = options?.canActivate ?? [sparkAuthGuard];\n const path = entryPath(options?.connectedApplications, 'account/applications');\n return {\n paths: { connectedApplications: '/' + path },\n accountLinks: [{ key: 'connectedApplications', path: '/' + path, label: { en: 'Connected applications', fr: 'Applications connectées', nl: 'Verbonden applicaties' } }],\n children: [\n child(options?.connectedApplications, path,\n () => import('@mintplayer/ng-spark/identity-provider/connected-applications').then(m => m.SparkConnectedApplicationsComponent),\n guard),\n ],\n };\n}\n\n/**\n * Mounts the developer portal (PRD D2/D3): `developers` (status, terms, request, registration token)\n * and `developers/invitations/:token?app=<applicationId>` (accept a team invitation; the mailed link's\n * target, which the server builds from this default path). Both need a signed-in user; the guard sends\n * anyone else to sign in and back.\n */\nexport function withDeveloperRoutes(\n options?: SparkIdentityProviderGuardOptions & { developers?: SparkAuthRouteEntry; invitation?: SparkAuthRouteEntry },\n): SparkIdentityProviderRoutesFeature {\n const guard = options?.canActivate ?? [sparkAuthGuard];\n const developers = entryPath(options?.developers, 'developers');\n const invitation = entryPath(options?.invitation, 'developers/invitations/:token');\n return {\n paths: { developers: '/' + developers },\n children: [\n child(options?.developers, developers,\n () => import('@mintplayer/ng-spark/identity-provider/developers').then(m => m.SparkDevelopersComponent),\n guard),\n child(options?.invitation, invitation,\n () => import('@mintplayer/ng-spark/identity-provider/developers').then(m => m.SparkAcceptInvitationComponent),\n guard),\n ],\n };\n}\n\n/**\n * Mounts the management page (default `identity-provider/admin`, PRD D9): signing keys with\n * rotate-now, and links to the queues (developer requests, scope approvals, go-live reviews) and\n * records (applications, resources, grants, audit trail). Mount it for everyone signed in: the server\n * decides who is an administrator, and the page tells anyone else they may not see it.\n */\nexport function withManagementRoutes(\n options?: SparkIdentityProviderGuardOptions & { identityProviderManagement?: SparkAuthRouteEntry },\n): SparkIdentityProviderRoutesFeature {\n const guard = options?.canActivate ?? [sparkAuthGuard];\n const path = entryPath(options?.identityProviderManagement, 'identity-provider/admin');\n return {\n paths: { identityProviderManagement: '/' + path },\n children: [\n child(options?.identityProviderManagement, path,\n () => import('@mintplayer/ng-spark/identity-provider/management').then(m => m.SparkIdentityProviderManagementComponent),\n guard),\n ],\n };\n}\n"],"mappings":";;AAuBA,SAAS,UAAU,OAAwC,aAAmB;CAC5E,IAAI,UAAU,QAAW,OAAO;CAChC,OAAO,OAAO,UAAU,WAAW,QAAQ,MAAM;AACnD;AAGA,SAAS,MAAM,OAAwC,MAAc,QAAgB,OAAsB;CACzG,MAAM,YAAY,OAAO,UAAU,YAAY,MAAM,YAAY,MAAM,YAAY;CACnF,OAAO;EACL;EACA,eAAe,kBAAkB,QAAQ,QAAQ,SAAS,IAAI;EAC9D,GAAI,MAAM,SAAS,EAAE,aAAa,MAAK,IAAK,CAAA;CAC7C;AACH;AAoBA,SAAgB,qBAAqB,GAAG,UAA8C;CACpF,MAAM,WAAoB,CAAA;CAC1B,MAAM,QAA6B,CAAA;CACnC,MAAM,eAA2C,CAAA;CACjD,KAAK,MAAM,WAAW,UAAU;EAC9B,SAAS,KAAK,GAAG,QAAQ,QAAQ;EACjC,OAAO,OAAO,OAAO,QAAQ,KAAK;EAClC,IAAI,QAAQ,cAAc,aAAa,KAAK,GAAG,QAAQ,YAAY;CACrE;CACA,OAAO;EAAE;EAAU;EAAO;CAAY;AACxC;AAOA,SAAgB,0BACd,SAA6F;CAE7F,MAAM,QAAQ,SAAS,eAAe,CAAC,cAAc;CACrD,MAAM,OAAO,UAAU,SAAS,uBAAuB,sBAAsB;CAC7E,OAAO;EACL,OAAO,EAAE,uBAAuB,MAAM,KAAI;EAC1C,cAAc,CAAC;GAAE,KAAK;GAAyB,MAAM,MAAM;GAAM,OAAO;IAAE,IAAI;IAA0B,IAAI;IAA2B,IAAI;GAAuB;EAAE,CAAE;EACtK,UAAU,CACR,MAAM,SAAS,uBAAuB,YAC9B,OAAO,gEAAgE,CAAC,MAAK,MAAK,EAAE,mCAAmC,GAC7H,KAAK,CACR;CACF;AACH;AAQA,SAAgB,oBACd,SAAoH;CAEpH,MAAM,QAAQ,SAAS,eAAe,CAAC,cAAc;CACrD,MAAM,aAAa,UAAU,SAAS,YAAY,YAAY;CAC9D,MAAM,aAAa,UAAU,SAAS,YAAY,+BAA+B;CACjF,OAAO;EACL,OAAO,EAAE,YAAY,MAAM,WAAU;EACrC,UAAU,CACR,MAAM,SAAS,YAAY,kBACnB,OAAO,oDAAoD,CAAC,MAAK,MAAK,EAAE,wBAAwB,GACtG,KAAK,GACP,MAAM,SAAS,YAAY,kBACnB,OAAO,oDAAoD,CAAC,MAAK,MAAK,EAAE,8BAA8B,GAC5G,KAAK,CACR;CACF;AACH;AAQA,SAAgB,qBACd,SAAkG;CAElG,MAAM,QAAQ,SAAS,eAAe,CAAC,cAAc;CACrD,MAAM,OAAO,UAAU,SAAS,4BAA4B,yBAAyB;CACrF,OAAO;EACL,OAAO,EAAE,4BAA4B,MAAM,KAAI;EAC/C,UAAU,CACR,MAAM,SAAS,4BAA4B,YACnC,OAAO,oDAAoD,CAAC,MAAK,MAAK,EAAE,wCAAwC,GACtH,KAAK,CACR;CACF;AACH"}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import * as i0 from "@angular/core";
|
|
2
|
+
import { ApplicationRef, DestroyRef, Injectable, InjectionToken, PLATFORM_ID, inject, isDevMode, makeEnvironmentProviders, provideAppInitializer, signal } from "@angular/core";
|
|
3
|
+
import { SwUpdate, provideServiceWorker } from "@angular/service-worker";
|
|
4
|
+
import { DOCUMENT, Location, isPlatformBrowser } from "@angular/common";
|
|
5
|
+
import { NavigationStart, Router } from "@angular/router";
|
|
6
|
+
import { Subscription, filter, first } from "rxjs";
|
|
7
|
+
const SPARK_SW_PAGE_LOADER = new InjectionToken("SPARK_SW_PAGE_LOADER", {
|
|
8
|
+
providedIn: "root",
|
|
9
|
+
factory: () => {
|
|
10
|
+
const location = inject(DOCUMENT).defaultView?.location;
|
|
11
|
+
return {
|
|
12
|
+
assign: (url) => location?.assign(url),
|
|
13
|
+
reload: () => location?.reload()
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
const SPARK_SW_SCHEDULE = new InjectionToken("SPARK_SW_SCHEDULE", {
|
|
18
|
+
providedIn: "root",
|
|
19
|
+
factory: () => ({
|
|
20
|
+
checkIntervalMs: 216e5,
|
|
21
|
+
visibleCheckThrottleMs: 6e4
|
|
22
|
+
})
|
|
23
|
+
});
|
|
24
|
+
const RELOADED_FOR_KEY = "spark-sw-reloaded-for";
|
|
25
|
+
var SparkServiceWorkerUpdates = class SparkServiceWorkerUpdates {
|
|
26
|
+
swUpdate = inject(SwUpdate);
|
|
27
|
+
router = inject(Router, { optional: true });
|
|
28
|
+
location = inject(Location, { optional: true });
|
|
29
|
+
appRef = inject(ApplicationRef);
|
|
30
|
+
document = inject(DOCUMENT);
|
|
31
|
+
platformId = inject(PLATFORM_ID);
|
|
32
|
+
destroyRef = inject(DestroyRef);
|
|
33
|
+
pageLoader = inject(SPARK_SW_PAGE_LOADER);
|
|
34
|
+
schedule = inject(SPARK_SW_SCHEDULE);
|
|
35
|
+
_updateReady = signal(false, ...ngDevMode ? [{ debugName: "_updateReady" }] : /* istanbul ignore next */ []);
|
|
36
|
+
_currentVersion = signal(null, ...ngDevMode ? [{ debugName: "_currentVersion" }] : /* istanbul ignore next */ []);
|
|
37
|
+
_latestVersion = signal(null, ...ngDevMode ? [{ debugName: "_latestVersion" }] : /* istanbul ignore next */ []);
|
|
38
|
+
updateReady = this._updateReady.asReadonly();
|
|
39
|
+
currentVersion = this._currentVersion.asReadonly();
|
|
40
|
+
latestVersion = this._latestVersion.asReadonly();
|
|
41
|
+
started = false;
|
|
42
|
+
leaving = false;
|
|
43
|
+
reloadWhenVisible = false;
|
|
44
|
+
lastCheck = 0;
|
|
45
|
+
start() {
|
|
46
|
+
if (this.started) return;
|
|
47
|
+
this.started = true;
|
|
48
|
+
if (!isPlatformBrowser(this.platformId) || !this.swUpdate.isEnabled) return;
|
|
49
|
+
const subscriptions = new Subscription();
|
|
50
|
+
this.destroyRef.onDestroy(() => subscriptions.unsubscribe());
|
|
51
|
+
subscriptions.add(this.swUpdate.versionUpdates.pipe(filter((event) => event.type === "VERSION_READY")).subscribe((event) => this.onVersionReady(event)));
|
|
52
|
+
subscriptions.add(this.swUpdate.unrecoverable.subscribe(() => this.onUnrecoverable()));
|
|
53
|
+
if (this.router) subscriptions.add(this.router.events.pipe(filter((event) => event instanceof NavigationStart)).subscribe((event) => this.onNavigationStart(event)));
|
|
54
|
+
subscriptions.add(this.appRef.isStable.pipe(first((stable) => stable)).subscribe(() => {
|
|
55
|
+
this.check();
|
|
56
|
+
const interval = setInterval(() => this.check(), this.schedule.checkIntervalMs);
|
|
57
|
+
subscriptions.add(() => clearInterval(interval));
|
|
58
|
+
}));
|
|
59
|
+
const onVisibilityChange = () => {
|
|
60
|
+
if (this.isHidden()) return;
|
|
61
|
+
if (this.reloadWhenVisible) {
|
|
62
|
+
this.reloadWhenVisible = false;
|
|
63
|
+
this.reload();
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (Date.now() - this.lastCheck >= this.schedule.visibleCheckThrottleMs) this.check();
|
|
67
|
+
};
|
|
68
|
+
this.document.addEventListener("visibilitychange", onVisibilityChange);
|
|
69
|
+
subscriptions.add(() => this.document.removeEventListener("visibilitychange", onVisibilityChange));
|
|
70
|
+
}
|
|
71
|
+
onVersionReady(event) {
|
|
72
|
+
const latest = toVersion(event.latestVersion);
|
|
73
|
+
this._currentVersion.set(toVersion(event.currentVersion));
|
|
74
|
+
this._latestVersion.set(latest);
|
|
75
|
+
if (this.readReloadedFor() === latest.hash) return;
|
|
76
|
+
this._updateReady.set(true);
|
|
77
|
+
}
|
|
78
|
+
onUnrecoverable() {
|
|
79
|
+
if (this.isHidden()) {
|
|
80
|
+
this.reloadWhenVisible = true;
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
this.reload();
|
|
84
|
+
}
|
|
85
|
+
onNavigationStart(event) {
|
|
86
|
+
if (!this._updateReady() || this.leaving || this.isHidden()) return;
|
|
87
|
+
this.leaving = true;
|
|
88
|
+
this.writeReloadedFor(this._latestVersion()?.hash);
|
|
89
|
+
this.router?.currentNavigation()?.abort();
|
|
90
|
+
if (event.navigationTrigger === "popstate") {
|
|
91
|
+
this.pageLoader.reload();
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
const target = this.location ? this.location.prepareExternalUrl(event.url) : event.url;
|
|
95
|
+
this.pageLoader.assign(target);
|
|
96
|
+
}
|
|
97
|
+
check() {
|
|
98
|
+
this.lastCheck = Date.now();
|
|
99
|
+
this.swUpdate.checkForUpdate().catch(() => {});
|
|
100
|
+
}
|
|
101
|
+
reload() {
|
|
102
|
+
if (this.leaving) return;
|
|
103
|
+
this.leaving = true;
|
|
104
|
+
this.pageLoader.reload();
|
|
105
|
+
}
|
|
106
|
+
isHidden() {
|
|
107
|
+
return this.document.visibilityState === "hidden";
|
|
108
|
+
}
|
|
109
|
+
readReloadedFor() {
|
|
110
|
+
try {
|
|
111
|
+
return this.document.defaultView?.sessionStorage.getItem(RELOADED_FOR_KEY) ?? null;
|
|
112
|
+
} catch {
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
writeReloadedFor(hash) {
|
|
117
|
+
if (!hash) return;
|
|
118
|
+
try {
|
|
119
|
+
this.document.defaultView?.sessionStorage.setItem(RELOADED_FOR_KEY, hash);
|
|
120
|
+
} catch {}
|
|
121
|
+
}
|
|
122
|
+
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
123
|
+
minVersion: "12.0.0",
|
|
124
|
+
version: "22.2.0",
|
|
125
|
+
ngImport: i0,
|
|
126
|
+
type: SparkServiceWorkerUpdates,
|
|
127
|
+
deps: [],
|
|
128
|
+
target: i0.ɵɵFactoryTarget.Injectable
|
|
129
|
+
});
|
|
130
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({
|
|
131
|
+
minVersion: "12.0.0",
|
|
132
|
+
version: "22.2.0",
|
|
133
|
+
ngImport: i0,
|
|
134
|
+
type: SparkServiceWorkerUpdates,
|
|
135
|
+
providedIn: "root"
|
|
136
|
+
});
|
|
137
|
+
};
|
|
138
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
139
|
+
minVersion: "12.0.0",
|
|
140
|
+
version: "22.2.0",
|
|
141
|
+
ngImport: i0,
|
|
142
|
+
type: SparkServiceWorkerUpdates,
|
|
143
|
+
decorators: [{
|
|
144
|
+
type: Injectable,
|
|
145
|
+
args: [{ providedIn: "root" }]
|
|
146
|
+
}]
|
|
147
|
+
});
|
|
148
|
+
function toVersion(version) {
|
|
149
|
+
return {
|
|
150
|
+
hash: version.hash,
|
|
151
|
+
appData: version.appData
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
function provideSparkServiceWorker(options = {}) {
|
|
155
|
+
return makeEnvironmentProviders([provideServiceWorker(options.script ?? "ngsw-worker.js", {
|
|
156
|
+
enabled: options.enabled ?? !isDevMode(),
|
|
157
|
+
registrationStrategy: options.registrationStrategy ?? "registerWhenStable:30000"
|
|
158
|
+
}), provideAppInitializer(() => inject(SparkServiceWorkerUpdates).start())]);
|
|
159
|
+
}
|
|
160
|
+
export { SPARK_SW_PAGE_LOADER, SPARK_SW_SCHEDULE, SparkServiceWorkerUpdates, provideSparkServiceWorker };
|
|
161
|
+
|
|
162
|
+
//# sourceMappingURL=mintplayer-ng-spark-pwa.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mintplayer-ng-spark-pwa.mjs","names":[],"sources":["../../pwa/src/spark-service-worker-updates.ts","../../pwa/src/provide-spark-service-worker.ts"],"sourcesContent":["import { DOCUMENT, Location, isPlatformBrowser } from '@angular/common';\nimport {\n ApplicationRef,\n DestroyRef,\n Injectable,\n InjectionToken,\n PLATFORM_ID,\n inject,\n signal,\n} from '@angular/core';\nimport { NavigationStart, Router } from '@angular/router';\nimport { SwUpdate, VersionReadyEvent } from '@angular/service-worker';\nimport { Subscription, filter, first } from 'rxjs';\n\n/** A service-worker version as `ngsw.json` describes it. `appData` is the config's `appData`. */\nexport interface SparkAppVersion {\n hash: string;\n appData?: Record<string, unknown>;\n}\n\n/** The two page loads the update policy performs. Replaceable for tests. */\nexport interface SparkPageLoader {\n assign(url: string): void;\n reload(): void;\n}\n\n/** How the update policy loads a page: `window.location` by default. */\nexport const SPARK_SW_PAGE_LOADER = new InjectionToken<SparkPageLoader>('SPARK_SW_PAGE_LOADER', {\n providedIn: 'root',\n factory: () => {\n const location = inject(DOCUMENT).defaultView?.location;\n return {\n assign: (url: string) => location?.assign(url),\n reload: () => location?.reload(),\n };\n },\n});\n\n/** Update-check timing. Defaults: every 6 h, and on becoming visible at most once a minute. */\nexport interface SparkServiceWorkerSchedule {\n checkIntervalMs: number;\n visibleCheckThrottleMs: number;\n}\n\nexport const SPARK_SW_SCHEDULE = new InjectionToken<SparkServiceWorkerSchedule>('SPARK_SW_SCHEDULE', {\n providedIn: 'root',\n factory: () => ({ checkIntervalMs: 6 * 60 * 60 * 1000, visibleCheckThrottleMs: 60 * 1000 }),\n});\n\n/**\n * Remembers, per tab, the version a full load was made for. If the page that load produced still\n * reports the same version as ready (another tab keeps the old version alive, or the worker did not\n * hand this client the new one), the policy does not reload again: one attempt per version.\n */\nconst RELOADED_FOR_KEY = 'spark-sw-reloaded-for';\n\n/**\n * The update policy of `provideSparkServiceWorker()` (#464, Q1b).\n *\n * - `VERSION_READY`: the **next** router navigation becomes a full page load of its target, so the\n * user lands on the page they asked for, in the new version, without losing anything on the page\n * they are leaving (the navigation was leaving it anyway).\n * - `unrecoverable`: the running version can no longer be served, so the page reloads at once.\n * - Checks: once the application is stable, every 6 h, and when the tab becomes visible again\n * (at most once a minute).\n * - **A hidden tab never reloads.** Spark has no unsaved-changes tracking to consult, so a reload\n * nobody is looking at could throw away an open edit. An `unrecoverable` in a hidden tab waits\n * until the tab is visible; a navigation in a hidden tab stays a normal in-app navigation.\n *\n * Nothing happens when the service worker is disabled (development builds, unsupported browsers)\n * or on the server.\n */\n@Injectable({ providedIn: 'root' })\nexport class SparkServiceWorkerUpdates {\n private readonly swUpdate = inject(SwUpdate);\n private readonly router = inject(Router, { optional: true });\n private readonly location = inject(Location, { optional: true });\n private readonly appRef = inject(ApplicationRef);\n private readonly document = inject(DOCUMENT);\n private readonly platformId = inject(PLATFORM_ID);\n private readonly destroyRef = inject(DestroyRef);\n private readonly pageLoader = inject(SPARK_SW_PAGE_LOADER);\n private readonly schedule = inject(SPARK_SW_SCHEDULE);\n\n private readonly _updateReady = signal(false);\n private readonly _currentVersion = signal<SparkAppVersion | null>(null);\n private readonly _latestVersion = signal<SparkAppVersion | null>(null);\n\n /** True once a new version is downloaded and the next navigation will load it. */\n readonly updateReady = this._updateReady.asReadonly();\n /** The running version, known once an update was reported. */\n readonly currentVersion = this._currentVersion.asReadonly();\n /** The version the next navigation loads (`appData.build` is the ng-spark version). */\n readonly latestVersion = this._latestVersion.asReadonly();\n\n private started = false;\n private leaving = false;\n private reloadWhenVisible = false;\n private lastCheck = 0;\n\n /** Starts the policy. Idempotent; called by the app initializer `provideSparkServiceWorker()` adds. */\n start(): void {\n if (this.started) return;\n this.started = true;\n\n if (!isPlatformBrowser(this.platformId) || !this.swUpdate.isEnabled) return;\n\n const subscriptions = new Subscription();\n this.destroyRef.onDestroy(() => subscriptions.unsubscribe());\n\n subscriptions.add(\n this.swUpdate.versionUpdates\n .pipe(filter((event): event is VersionReadyEvent => event.type === 'VERSION_READY'))\n .subscribe((event) => this.onVersionReady(event)),\n );\n\n subscriptions.add(this.swUpdate.unrecoverable.subscribe(() => this.onUnrecoverable()));\n\n if (this.router) {\n subscriptions.add(\n this.router.events\n .pipe(filter((event): event is NavigationStart => event instanceof NavigationStart))\n .subscribe((event) => this.onNavigationStart(event)),\n );\n }\n\n // The first check waits for stability, so it never competes with the initial render.\n subscriptions.add(\n this.appRef.isStable.pipe(first((stable) => stable)).subscribe(() => {\n this.check();\n const interval = setInterval(() => this.check(), this.schedule.checkIntervalMs);\n subscriptions.add(() => clearInterval(interval));\n }),\n );\n\n const onVisibilityChange = () => {\n if (this.isHidden()) return;\n if (this.reloadWhenVisible) {\n this.reloadWhenVisible = false;\n this.reload();\n return;\n }\n if (Date.now() - this.lastCheck >= this.schedule.visibleCheckThrottleMs) this.check();\n };\n this.document.addEventListener('visibilitychange', onVisibilityChange);\n subscriptions.add(() => this.document.removeEventListener('visibilitychange', onVisibilityChange));\n }\n\n private onVersionReady(event: VersionReadyEvent): void {\n const latest = toVersion(event.latestVersion);\n this._currentVersion.set(toVersion(event.currentVersion));\n this._latestVersion.set(latest);\n\n if (this.readReloadedFor() === latest.hash) {\n // This page is the full load made for that version and it is still not the one served.\n // Reloading again would loop; the next deploy, or the next fresh tab, picks it up.\n return;\n }\n this._updateReady.set(true);\n }\n\n private onUnrecoverable(): void {\n if (this.isHidden()) {\n this.reloadWhenVisible = true;\n return;\n }\n this.reload();\n }\n\n private onNavigationStart(event: NavigationStart): void {\n if (!this._updateReady() || this.leaving || this.isHidden()) return;\n\n this.leaving = true;\n this.writeReloadedFor(this._latestVersion()?.hash);\n this.router?.currentNavigation()?.abort();\n\n if (event.navigationTrigger === 'popstate') {\n // Back/forward: the address bar already shows the target, and assign() would add a\n // duplicate history entry.\n this.pageLoader.reload();\n return;\n }\n\n // event.url is relative to the router's base; prepareExternalUrl adds the <base href>.\n const target = this.location ? this.location.prepareExternalUrl(event.url) : event.url;\n this.pageLoader.assign(target);\n }\n\n private check(): void {\n this.lastCheck = Date.now();\n this.swUpdate.checkForUpdate().catch(() => {\n // Offline, or the worker is gone. The next scheduled check tries again.\n });\n }\n\n private reload(): void {\n if (this.leaving) return;\n this.leaving = true;\n this.pageLoader.reload();\n }\n\n private isHidden(): boolean {\n return this.document.visibilityState === 'hidden';\n }\n\n private readReloadedFor(): string | null {\n try {\n return this.document.defaultView?.sessionStorage.getItem(RELOADED_FOR_KEY) ?? null;\n } catch {\n return null;\n }\n }\n\n private writeReloadedFor(hash: string | undefined): void {\n if (!hash) return;\n try {\n this.document.defaultView?.sessionStorage.setItem(RELOADED_FOR_KEY, hash);\n } catch {\n // Storage blocked: the guard is best effort; one reload per navigation is still bounded.\n }\n }\n}\n\nfunction toVersion(version: { hash: string; appData?: object }): SparkAppVersion {\n return { hash: version.hash, appData: version.appData as Record<string, unknown> | undefined };\n}\n","import { EnvironmentProviders, inject, isDevMode, makeEnvironmentProviders, provideAppInitializer } from '@angular/core';\nimport { SwRegistrationOptions, provideServiceWorker } from '@angular/service-worker';\n\nimport { SparkServiceWorkerUpdates } from './spark-service-worker-updates';\n\nexport interface SparkServiceWorkerOptions {\n /** Whether the worker registers. Default: `!isDevMode()`, so only production builds register it. */\n enabled?: boolean;\n /** The worker script, relative to the base href. Default: `ngsw-worker.js`. */\n script?: string;\n /** When the worker registers. Default: `registerWhenStable:30000`. */\n registrationStrategy?: SwRegistrationOptions['registrationStrategy'];\n}\n\n/**\n * Registers Angular's service worker with Spark's update policy (#464, Q1b); see\n * {@link SparkServiceWorkerUpdates} for what happens when a new version is deployed.\n *\n * The application also needs an `ngsw-config.json` with Spark's navigation exclusions and\n * `serviceWorker` in its production build configuration; see `docs/guide-pwa.md`.\n */\nexport function provideSparkServiceWorker(options: SparkServiceWorkerOptions = {}): EnvironmentProviders {\n return makeEnvironmentProviders([\n provideServiceWorker(options.script ?? 'ngsw-worker.js', {\n enabled: options.enabled ?? !isDevMode(),\n registrationStrategy: options.registrationStrategy ?? 'registerWhenStable:30000',\n }),\n provideAppInitializer(() => inject(SparkServiceWorkerUpdates).start()),\n ]);\n}\n"],"mappings":"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"}
|