@dudousxd/nestjs-durable-dashboard 0.53.0 → 0.55.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/dist/client/durable-client.d.ts +67 -3
- package/dist/client/durable-client.d.ts.map +1 -1
- package/dist/client/durable-client.js +26 -46
- package/dist/client/durable-client.js.map +1 -1
- package/dist/client/event-tail.d.ts +25 -0
- package/dist/client/event-tail.d.ts.map +1 -0
- package/dist/client/event-tail.js +30 -0
- package/dist/client/event-tail.js.map +1 -0
- package/dist/server/index.cjs +322 -1337
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.d.cts +149 -389
- package/dist/server/index.d.ts +149 -389
- package/dist/server/index.js +308 -1317
- package/dist/server/index.js.map +1 -1
- package/dist/spa/assets/{bench-Db-gqOEF.js → bench-DCclNvcZ.js} +1 -1
- package/dist/spa/assets/index-B-wtX4GV.js +66 -0
- package/dist/spa/assets/index-CJdk28vt.css +1 -0
- package/dist/spa/assets/{index-4tEXGRlA.js → index-ChjKRQZx.js} +1 -1
- package/dist/spa/assets/{preview-BvCp1zR5.js → preview-CdsjvOrK.js} +1 -1
- package/dist/spa/bench.html +3 -3
- package/dist/spa/index.html +3 -3
- package/dist/spa/preview.html +3 -3
- package/package.json +5 -18
- package/dist/client/console-session.d.ts +0 -63
- package/dist/client/console-session.d.ts.map +0 -1
- package/dist/client/console-session.js +0 -107
- package/dist/client/console-session.js.map +0 -1
- package/dist/react/index.d.ts +0 -4
- package/dist/react/index.d.ts.map +0 -1
- package/dist/react/index.js +0 -14
- package/dist/react/index.js.map +0 -1
- package/dist/react/open-console-button.d.ts +0 -26
- package/dist/react/open-console-button.d.ts.map +0 -1
- package/dist/react/open-console-button.js +0 -26
- package/dist/react/open-console-button.js.map +0 -1
- package/dist/react/use-open-console.d.ts +0 -40
- package/dist/react/use-open-console.d.ts.map +0 -1
- package/dist/react/use-open-console.js +0 -77
- package/dist/react/use-open-console.js.map +0 -1
- package/dist/spa/assets/index-BbR4CZ1K.css +0 -1
- package/dist/spa/assets/index-CEV7yPmk.js +0 -66
package/dist/server/index.js
CHANGED
|
@@ -174,6 +174,51 @@ var DashboardService = class {
|
|
|
174
174
|
return this.gateway.cancel(runId, opts);
|
|
175
175
|
}
|
|
176
176
|
/**
|
|
177
|
+
* Publish a settled run's lifecycle event again, to its subscribers that have not started (or to
|
|
178
|
+
* `subscriber` alone). Undefined when the gateway cannot republish (a tenant proxy).
|
|
179
|
+
*/
|
|
180
|
+
async republishLifecycle(runId, subscriber) {
|
|
181
|
+
return this.gateway.republishLifecycle?.({
|
|
182
|
+
runId,
|
|
183
|
+
...subscriber !== void 0 ? {
|
|
184
|
+
subscriber
|
|
185
|
+
} : {}
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Who starts whom through lifecycle events, for `workflow`, plus `triggerCounts` when this process
|
|
190
|
+
* counts the engine's events (the control plane) and the workflow has subscribers. Undefined when
|
|
191
|
+
* the gateway cannot answer.
|
|
192
|
+
*/
|
|
193
|
+
async workflowLifecycle(workflow) {
|
|
194
|
+
const links = await this.gateway.workflowLifecycle?.(workflow);
|
|
195
|
+
if (!links) return links;
|
|
196
|
+
const counts = this.triggerCountsOf(links);
|
|
197
|
+
return counts ? {
|
|
198
|
+
...links,
|
|
199
|
+
triggerCounts: counts
|
|
200
|
+
} : links;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* How many runs each subscriber of `links.workflow` was started for and how many times starting it
|
|
204
|
+
* failed, counted by this process's metrics since it started. Undefined without metrics (a tenant)
|
|
205
|
+
* or without subscribers.
|
|
206
|
+
*/
|
|
207
|
+
triggerCountsOf(links) {
|
|
208
|
+
const snapshot = this.metricsCollector?.snapshot();
|
|
209
|
+
if (!snapshot || links.startsOnSettle.length === 0) return void 0;
|
|
210
|
+
const triggered = snapshot.triggeredBySubscriber[links.workflow] ?? {};
|
|
211
|
+
const failed = snapshot.triggerFailedBySubscriber[links.workflow] ?? {};
|
|
212
|
+
const counts = {};
|
|
213
|
+
for (const { workflow } of links.startsOnSettle) {
|
|
214
|
+
counts[workflow] = {
|
|
215
|
+
triggered: triggered[workflow] ?? 0,
|
|
216
|
+
failed: failed[workflow] ?? 0
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
return counts;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
177
222
|
* Apply `retry` or `cancel` to every run matching a filter (status / tag / workflow / namespace /
|
|
178
223
|
* origin) — e.g. "retry every `dead` run tagged `type:mel`". The filter is the same one the run
|
|
179
224
|
* list uses, so a bulk action can never reach wider than the list it was launched from — but note
|
|
@@ -274,7 +319,7 @@ DashboardService = _ts_decorate([
|
|
|
274
319
|
|
|
275
320
|
// src/server/durable-api.controller.ts
|
|
276
321
|
import { ApplyFilter, ApplyFilterInterceptor, FilterRunner } from "@dudousxd/nestjs-filter";
|
|
277
|
-
import { Body, Controller, Get, Header, Headers, NotFoundException, Param, Post, Query, Res, Sse, UseFilters, UseInterceptors } from "@nestjs/common";
|
|
322
|
+
import { BadRequestException as BadRequestException2, Body, Controller, Get, Header, Headers, NotFoundException, NotImplementedException, Param, Post, Query, Res, Sse, UseFilters, UseInterceptors } from "@nestjs/common";
|
|
278
323
|
|
|
279
324
|
// src/server/durable-run.ts
|
|
280
325
|
var DurableRun = class {
|
|
@@ -351,87 +396,6 @@ __name(runValueAxisFor, "runValueAxisFor");
|
|
|
351
396
|
|
|
352
397
|
// src/server/not-modified.ts
|
|
353
398
|
import { Catch, HttpException } from "@nestjs/common";
|
|
354
|
-
|
|
355
|
-
// src/server/auth/response.ts
|
|
356
|
-
function isRecord(value) {
|
|
357
|
-
return typeof value === "object" && value !== null;
|
|
358
|
-
}
|
|
359
|
-
__name(isRecord, "isRecord");
|
|
360
|
-
function isRawCookieResponse(value) {
|
|
361
|
-
return isRecord(value) && typeof value.getHeader === "function" && typeof value.setHeader === "function";
|
|
362
|
-
}
|
|
363
|
-
__name(isRawCookieResponse, "isRawCookieResponse");
|
|
364
|
-
function resolveCookieResponse(response) {
|
|
365
|
-
if (isRawCookieResponse(response)) return response;
|
|
366
|
-
if (isRecord(response) && isRawCookieResponse(response.raw)) return response.raw;
|
|
367
|
-
return null;
|
|
368
|
-
}
|
|
369
|
-
__name(resolveCookieResponse, "resolveCookieResponse");
|
|
370
|
-
function existingSetCookies(response) {
|
|
371
|
-
const current = response.getHeader("set-cookie");
|
|
372
|
-
if (Array.isArray(current)) return current;
|
|
373
|
-
if (typeof current === "string") return [
|
|
374
|
-
current
|
|
375
|
-
];
|
|
376
|
-
return [];
|
|
377
|
-
}
|
|
378
|
-
__name(existingSetCookies, "existingSetCookies");
|
|
379
|
-
function appendSetCookie(response, cookie) {
|
|
380
|
-
const raw = resolveCookieResponse(response);
|
|
381
|
-
if (!raw) return;
|
|
382
|
-
raw.setHeader("set-cookie", [
|
|
383
|
-
...existingSetCookies(raw),
|
|
384
|
-
cookie
|
|
385
|
-
]);
|
|
386
|
-
}
|
|
387
|
-
__name(appendSetCookie, "appendSetCookie");
|
|
388
|
-
function isRawRedirectResponse(value) {
|
|
389
|
-
return isRecord(value) && typeof value.getHeader === "function" && typeof value.setHeader === "function" && typeof value.end === "function";
|
|
390
|
-
}
|
|
391
|
-
__name(isRawRedirectResponse, "isRawRedirectResponse");
|
|
392
|
-
function resolveRedirectResponse(response) {
|
|
393
|
-
if (isRawRedirectResponse(response)) return response;
|
|
394
|
-
if (isRecord(response) && isRawRedirectResponse(response.raw)) return response.raw;
|
|
395
|
-
return null;
|
|
396
|
-
}
|
|
397
|
-
__name(resolveRedirectResponse, "resolveRedirectResponse");
|
|
398
|
-
function redirectRaw(response, location, status = 302) {
|
|
399
|
-
const raw = resolveRedirectResponse(response);
|
|
400
|
-
if (!raw) return;
|
|
401
|
-
raw.statusCode = status;
|
|
402
|
-
raw.setHeader("location", location);
|
|
403
|
-
raw.end();
|
|
404
|
-
}
|
|
405
|
-
__name(redirectRaw, "redirectRaw");
|
|
406
|
-
function responseAlreadyWritten(response) {
|
|
407
|
-
const raw = resolveRedirectResponse(response);
|
|
408
|
-
return raw?.headersSent === true;
|
|
409
|
-
}
|
|
410
|
-
__name(responseAlreadyWritten, "responseAlreadyWritten");
|
|
411
|
-
function writeHtmlRaw(response, html, status = 200) {
|
|
412
|
-
const raw = resolveRedirectResponse(response);
|
|
413
|
-
if (!raw) return;
|
|
414
|
-
raw.statusCode = status;
|
|
415
|
-
raw.setHeader("content-type", "text/html; charset=utf-8");
|
|
416
|
-
raw.setHeader("cache-control", "no-store, must-revalidate");
|
|
417
|
-
raw.end(html);
|
|
418
|
-
}
|
|
419
|
-
__name(writeHtmlRaw, "writeHtmlRaw");
|
|
420
|
-
function setRawHeader(response, name, value) {
|
|
421
|
-
resolveCookieResponse(response)?.setHeader(name, value);
|
|
422
|
-
}
|
|
423
|
-
__name(setRawHeader, "setRawHeader");
|
|
424
|
-
function writeNotModifiedRaw(response, etag) {
|
|
425
|
-
const raw = resolveRedirectResponse(response);
|
|
426
|
-
if (!raw) return;
|
|
427
|
-
raw.statusCode = 304;
|
|
428
|
-
raw.setHeader("etag", etag);
|
|
429
|
-
raw.setHeader("cache-control", "private, no-cache");
|
|
430
|
-
raw.end();
|
|
431
|
-
}
|
|
432
|
-
__name(writeNotModifiedRaw, "writeNotModifiedRaw");
|
|
433
|
-
|
|
434
|
-
// src/server/not-modified.ts
|
|
435
399
|
function _ts_decorate2(decorators, target, key, desc) {
|
|
436
400
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
437
401
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
|
|
@@ -446,6 +410,31 @@ function _ts_decorate2(decorators, target, key, desc) {
|
|
|
446
410
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
447
411
|
}
|
|
448
412
|
__name(_ts_decorate2, "_ts_decorate");
|
|
413
|
+
function isRawResponse(value) {
|
|
414
|
+
if (typeof value !== "object" || value === null) return false;
|
|
415
|
+
const candidate = value;
|
|
416
|
+
return typeof candidate.setHeader === "function" && typeof candidate.end === "function";
|
|
417
|
+
}
|
|
418
|
+
__name(isRawResponse, "isRawResponse");
|
|
419
|
+
function rawResponse(response) {
|
|
420
|
+
if (isRawResponse(response)) return response;
|
|
421
|
+
const raw = response?.raw;
|
|
422
|
+
return isRawResponse(raw) ? raw : null;
|
|
423
|
+
}
|
|
424
|
+
__name(rawResponse, "rawResponse");
|
|
425
|
+
function setRawHeader(response, name, value) {
|
|
426
|
+
rawResponse(response)?.setHeader(name, value);
|
|
427
|
+
}
|
|
428
|
+
__name(setRawHeader, "setRawHeader");
|
|
429
|
+
function writeNotModifiedRaw(response, etag) {
|
|
430
|
+
const raw = rawResponse(response);
|
|
431
|
+
if (!raw) return;
|
|
432
|
+
raw.statusCode = 304;
|
|
433
|
+
raw.setHeader("etag", etag);
|
|
434
|
+
raw.setHeader("cache-control", "private, no-cache");
|
|
435
|
+
raw.end();
|
|
436
|
+
}
|
|
437
|
+
__name(writeNotModifiedRaw, "writeNotModifiedRaw");
|
|
449
438
|
var NotModifiedException = class extends HttpException {
|
|
450
439
|
static {
|
|
451
440
|
__name(this, "NotModifiedException");
|
|
@@ -761,10 +750,10 @@ var RunQueryAdapter = class {
|
|
|
761
750
|
}
|
|
762
751
|
unsupported(clause, "equals, notEquals, gt, gte, lt, lte, in");
|
|
763
752
|
}
|
|
764
|
-
applyOffsetPagination(qb,
|
|
753
|
+
applyOffsetPagination(qb, page, size) {
|
|
765
754
|
this.draft(qb).narrow({
|
|
766
755
|
limit: size,
|
|
767
|
-
offset: (
|
|
756
|
+
offset: (page - 1) * size
|
|
768
757
|
});
|
|
769
758
|
}
|
|
770
759
|
/**
|
|
@@ -1108,9 +1097,9 @@ function _ts_param2(paramIndex, decorator) {
|
|
|
1108
1097
|
}
|
|
1109
1098
|
__name(_ts_param2, "_ts_param");
|
|
1110
1099
|
var FAN_INLINE_MAX = 50;
|
|
1111
|
-
var
|
|
1100
|
+
var DurableApiControllerBase = class {
|
|
1112
1101
|
static {
|
|
1113
|
-
__name(this, "
|
|
1102
|
+
__name(this, "DurableApiControllerBase");
|
|
1114
1103
|
}
|
|
1115
1104
|
dashboard;
|
|
1116
1105
|
runner;
|
|
@@ -1202,6 +1191,15 @@ var DurableApiController = class {
|
|
|
1202
1191
|
triggerSchedule(id) {
|
|
1203
1192
|
return this.dashboard.triggerSchedule(id).catch(notFound);
|
|
1204
1193
|
}
|
|
1194
|
+
/**
|
|
1195
|
+
* Who starts whom through lifecycle events, for one workflow: the workflows that start when it
|
|
1196
|
+
* settles, and the ones whose settling starts it. What the console's workflow page lists.
|
|
1197
|
+
*/
|
|
1198
|
+
async workflowLifecycle(name) {
|
|
1199
|
+
const links = await this.dashboard.workflowLifecycle(name).catch(notSupportedByServer);
|
|
1200
|
+
if (!links) throw new NotImplementedException("workflow lifecycle links need the control plane");
|
|
1201
|
+
return links;
|
|
1202
|
+
}
|
|
1205
1203
|
/** This deployment's durable role (control plane vs tenant) + tenant name — for the header badge. */
|
|
1206
1204
|
topology() {
|
|
1207
1205
|
return this.dashboard.topology();
|
|
@@ -1231,7 +1229,7 @@ var DurableApiController = class {
|
|
|
1231
1229
|
}
|
|
1232
1230
|
/** One page of a summarized fan's members, in seq order. Versioned like {@link run}. */
|
|
1233
1231
|
async fanMembers(id, group, offset, limit, failed, ifNoneMatch, response) {
|
|
1234
|
-
const
|
|
1232
|
+
const page = await this.dashboard.getFanMembers(id, group, {
|
|
1235
1233
|
offset: offset === void 0 ? void 0 : Number(offset),
|
|
1236
1234
|
limit: limit === void 0 ? void 0 : Number(limit),
|
|
1237
1235
|
failedOnly: failed === "true",
|
|
@@ -1239,8 +1237,8 @@ var DurableApiController = class {
|
|
|
1239
1237
|
ifNoneMatch
|
|
1240
1238
|
}
|
|
1241
1239
|
});
|
|
1242
|
-
if (!
|
|
1243
|
-
const { etag, notModified, ...body } =
|
|
1240
|
+
if (!page) throw new NotFoundException(`fan ${group} of run ${id} not found`);
|
|
1241
|
+
const { etag, notModified, ...body } = page;
|
|
1244
1242
|
if (etag) {
|
|
1245
1243
|
if (notModified) throw new NotModifiedException(etag);
|
|
1246
1244
|
setEtag(response, etag);
|
|
@@ -1277,6 +1275,28 @@ var DurableApiController = class {
|
|
|
1277
1275
|
if (!result) throw new NotFoundException(`run ${id} not found`);
|
|
1278
1276
|
return result;
|
|
1279
1277
|
}
|
|
1278
|
+
/**
|
|
1279
|
+
* Publish a settled run's lifecycle event again, to the subscribers that have not started from it
|
|
1280
|
+
* (or only `subscriber`). Same gate as retry/cancel.
|
|
1281
|
+
*/
|
|
1282
|
+
async republishLifecycle(id, body) {
|
|
1283
|
+
let result;
|
|
1284
|
+
try {
|
|
1285
|
+
result = await this.dashboard.republishLifecycle(id, body?.subscriber || void 0);
|
|
1286
|
+
} catch (error) {
|
|
1287
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1288
|
+
if (/^run .* not found$/.test(message)) throw new NotFoundException(message);
|
|
1289
|
+
if (/^run .* has not settled/.test(message)) throw new BadRequestException2(message);
|
|
1290
|
+
if (error?.code === "bad-request") {
|
|
1291
|
+
throw new BadRequestException2(message);
|
|
1292
|
+
}
|
|
1293
|
+
return notSupportedByServer(error);
|
|
1294
|
+
}
|
|
1295
|
+
if (!result) {
|
|
1296
|
+
throw new NotImplementedException("republishing lifecycle events needs the control plane");
|
|
1297
|
+
}
|
|
1298
|
+
return result;
|
|
1299
|
+
}
|
|
1280
1300
|
async continue(id) {
|
|
1281
1301
|
const result = await this.dashboard.continue(id);
|
|
1282
1302
|
if (!result) throw new NotFoundException(`run ${id} is not paused at a breakpoint`);
|
|
@@ -1316,7 +1336,7 @@ _ts_decorate5([
|
|
|
1316
1336
|
typeof RunQueryDraft === "undefined" ? Object : RunQueryDraft
|
|
1317
1337
|
]),
|
|
1318
1338
|
_ts_metadata4("design:returntype", Promise)
|
|
1319
|
-
],
|
|
1339
|
+
], DurableApiControllerBase.prototype, "runs", null);
|
|
1320
1340
|
_ts_decorate5([
|
|
1321
1341
|
Get("runs/facets"),
|
|
1322
1342
|
_ts_param2(0, ApplyFilter(RunFilter)),
|
|
@@ -1325,7 +1345,7 @@ _ts_decorate5([
|
|
|
1325
1345
|
typeof RunQueryDraft === "undefined" ? Object : RunQueryDraft
|
|
1326
1346
|
]),
|
|
1327
1347
|
_ts_metadata4("design:returntype", void 0)
|
|
1328
|
-
],
|
|
1348
|
+
], DurableApiControllerBase.prototype, "facets", null);
|
|
1329
1349
|
_ts_decorate5([
|
|
1330
1350
|
Get("runs/values"),
|
|
1331
1351
|
_ts_param2(0, Query()),
|
|
@@ -1334,20 +1354,20 @@ _ts_decorate5([
|
|
|
1334
1354
|
typeof Record === "undefined" ? Object : Record
|
|
1335
1355
|
]),
|
|
1336
1356
|
_ts_metadata4("design:returntype", void 0)
|
|
1337
|
-
],
|
|
1357
|
+
], DurableApiControllerBase.prototype, "values", null);
|
|
1338
1358
|
_ts_decorate5([
|
|
1339
1359
|
Get("metrics"),
|
|
1340
1360
|
Header("Content-Type", "text/plain; version=0.0.4"),
|
|
1341
1361
|
_ts_metadata4("design:type", Function),
|
|
1342
1362
|
_ts_metadata4("design:paramtypes", []),
|
|
1343
1363
|
_ts_metadata4("design:returntype", void 0)
|
|
1344
|
-
],
|
|
1364
|
+
], DurableApiControllerBase.prototype, "metrics", null);
|
|
1345
1365
|
_ts_decorate5([
|
|
1346
1366
|
Get("workers"),
|
|
1347
1367
|
_ts_metadata4("design:type", Function),
|
|
1348
1368
|
_ts_metadata4("design:paramtypes", []),
|
|
1349
1369
|
_ts_metadata4("design:returntype", void 0)
|
|
1350
|
-
],
|
|
1370
|
+
], DurableApiControllerBase.prototype, "workers", null);
|
|
1351
1371
|
_ts_decorate5([
|
|
1352
1372
|
Get("schedules"),
|
|
1353
1373
|
_ts_param2(0, Query("namespace")),
|
|
@@ -1364,7 +1384,7 @@ _ts_decorate5([
|
|
|
1364
1384
|
String
|
|
1365
1385
|
]),
|
|
1366
1386
|
_ts_metadata4("design:returntype", void 0)
|
|
1367
|
-
],
|
|
1387
|
+
], DurableApiControllerBase.prototype, "schedules", null);
|
|
1368
1388
|
_ts_decorate5([
|
|
1369
1389
|
Post("schedules/:id/pause"),
|
|
1370
1390
|
_ts_param2(0, Param("id")),
|
|
@@ -1373,7 +1393,7 @@ _ts_decorate5([
|
|
|
1373
1393
|
String
|
|
1374
1394
|
]),
|
|
1375
1395
|
_ts_metadata4("design:returntype", void 0)
|
|
1376
|
-
],
|
|
1396
|
+
], DurableApiControllerBase.prototype, "pauseSchedule", null);
|
|
1377
1397
|
_ts_decorate5([
|
|
1378
1398
|
Post("schedules/:id/resume"),
|
|
1379
1399
|
_ts_param2(0, Param("id")),
|
|
@@ -1382,7 +1402,7 @@ _ts_decorate5([
|
|
|
1382
1402
|
String
|
|
1383
1403
|
]),
|
|
1384
1404
|
_ts_metadata4("design:returntype", void 0)
|
|
1385
|
-
],
|
|
1405
|
+
], DurableApiControllerBase.prototype, "resumeSchedule", null);
|
|
1386
1406
|
_ts_decorate5([
|
|
1387
1407
|
Post("schedules/:id/trigger"),
|
|
1388
1408
|
_ts_param2(0, Param("id")),
|
|
@@ -1391,13 +1411,22 @@ _ts_decorate5([
|
|
|
1391
1411
|
String
|
|
1392
1412
|
]),
|
|
1393
1413
|
_ts_metadata4("design:returntype", void 0)
|
|
1394
|
-
],
|
|
1414
|
+
], DurableApiControllerBase.prototype, "triggerSchedule", null);
|
|
1415
|
+
_ts_decorate5([
|
|
1416
|
+
Get("workflows/:name/lifecycle"),
|
|
1417
|
+
_ts_param2(0, Param("name")),
|
|
1418
|
+
_ts_metadata4("design:type", Function),
|
|
1419
|
+
_ts_metadata4("design:paramtypes", [
|
|
1420
|
+
String
|
|
1421
|
+
]),
|
|
1422
|
+
_ts_metadata4("design:returntype", Promise)
|
|
1423
|
+
], DurableApiControllerBase.prototype, "workflowLifecycle", null);
|
|
1395
1424
|
_ts_decorate5([
|
|
1396
1425
|
Get("topology"),
|
|
1397
1426
|
_ts_metadata4("design:type", Function),
|
|
1398
1427
|
_ts_metadata4("design:paramtypes", []),
|
|
1399
1428
|
_ts_metadata4("design:returntype", void 0)
|
|
1400
|
-
],
|
|
1429
|
+
], DurableApiControllerBase.prototype, "topology", null);
|
|
1401
1430
|
_ts_decorate5([
|
|
1402
1431
|
Get("runs/:id"),
|
|
1403
1432
|
_ts_param2(0, Param("id")),
|
|
@@ -1414,7 +1443,7 @@ _ts_decorate5([
|
|
|
1414
1443
|
Object
|
|
1415
1444
|
]),
|
|
1416
1445
|
_ts_metadata4("design:returntype", Promise)
|
|
1417
|
-
],
|
|
1446
|
+
], DurableApiControllerBase.prototype, "run", null);
|
|
1418
1447
|
_ts_decorate5([
|
|
1419
1448
|
Get("runs/:id/fans/:group"),
|
|
1420
1449
|
_ts_param2(0, Param("id")),
|
|
@@ -1437,7 +1466,7 @@ _ts_decorate5([
|
|
|
1437
1466
|
Object
|
|
1438
1467
|
]),
|
|
1439
1468
|
_ts_metadata4("design:returntype", Promise)
|
|
1440
|
-
],
|
|
1469
|
+
], DurableApiControllerBase.prototype, "fanMembers", null);
|
|
1441
1470
|
_ts_decorate5([
|
|
1442
1471
|
Sse("runs/:id/stream"),
|
|
1443
1472
|
_ts_param2(0, Param("id")),
|
|
@@ -1446,7 +1475,7 @@ _ts_decorate5([
|
|
|
1446
1475
|
String
|
|
1447
1476
|
]),
|
|
1448
1477
|
_ts_metadata4("design:returntype", void 0)
|
|
1449
|
-
],
|
|
1478
|
+
], DurableApiControllerBase.prototype, "stream", null);
|
|
1450
1479
|
_ts_decorate5([
|
|
1451
1480
|
Post("runs/:id/retry"),
|
|
1452
1481
|
_ts_param2(0, Param("id")),
|
|
@@ -1455,7 +1484,7 @@ _ts_decorate5([
|
|
|
1455
1484
|
String
|
|
1456
1485
|
]),
|
|
1457
1486
|
_ts_metadata4("design:returntype", void 0)
|
|
1458
|
-
],
|
|
1487
|
+
], DurableApiControllerBase.prototype, "retry", null);
|
|
1459
1488
|
_ts_decorate5([
|
|
1460
1489
|
Post("runs/:id/retry-with-input"),
|
|
1461
1490
|
_ts_param2(0, Param("id")),
|
|
@@ -1466,7 +1495,7 @@ _ts_decorate5([
|
|
|
1466
1495
|
Object
|
|
1467
1496
|
]),
|
|
1468
1497
|
_ts_metadata4("design:returntype", void 0)
|
|
1469
|
-
],
|
|
1498
|
+
], DurableApiControllerBase.prototype, "retryWithInput", null);
|
|
1470
1499
|
_ts_decorate5([
|
|
1471
1500
|
Post("bulk/:action"),
|
|
1472
1501
|
_ts_param2(0, Param("action")),
|
|
@@ -1479,7 +1508,7 @@ _ts_decorate5([
|
|
|
1479
1508
|
String
|
|
1480
1509
|
]),
|
|
1481
1510
|
_ts_metadata4("design:returntype", void 0)
|
|
1482
|
-
],
|
|
1511
|
+
], DurableApiControllerBase.prototype, "bulk", null);
|
|
1483
1512
|
_ts_decorate5([
|
|
1484
1513
|
Post("runs/:id/cancel"),
|
|
1485
1514
|
_ts_param2(0, Param("id")),
|
|
@@ -1490,7 +1519,18 @@ _ts_decorate5([
|
|
|
1490
1519
|
String
|
|
1491
1520
|
]),
|
|
1492
1521
|
_ts_metadata4("design:returntype", Promise)
|
|
1493
|
-
],
|
|
1522
|
+
], DurableApiControllerBase.prototype, "cancel", null);
|
|
1523
|
+
_ts_decorate5([
|
|
1524
|
+
Post("runs/:id/republish-lifecycle"),
|
|
1525
|
+
_ts_param2(0, Param("id")),
|
|
1526
|
+
_ts_param2(1, Body()),
|
|
1527
|
+
_ts_metadata4("design:type", Function),
|
|
1528
|
+
_ts_metadata4("design:paramtypes", [
|
|
1529
|
+
String,
|
|
1530
|
+
Object
|
|
1531
|
+
]),
|
|
1532
|
+
_ts_metadata4("design:returntype", Promise)
|
|
1533
|
+
], DurableApiControllerBase.prototype, "republishLifecycle", null);
|
|
1494
1534
|
_ts_decorate5([
|
|
1495
1535
|
Post("runs/:id/continue"),
|
|
1496
1536
|
_ts_param2(0, Param("id")),
|
|
@@ -1499,7 +1539,7 @@ _ts_decorate5([
|
|
|
1499
1539
|
String
|
|
1500
1540
|
]),
|
|
1501
1541
|
_ts_metadata4("design:returntype", Promise)
|
|
1502
|
-
],
|
|
1542
|
+
], DurableApiControllerBase.prototype, "continue", null);
|
|
1503
1543
|
_ts_decorate5([
|
|
1504
1544
|
Post("runs/:id/redispatch"),
|
|
1505
1545
|
_ts_param2(0, Param("id")),
|
|
@@ -1508,7 +1548,7 @@ _ts_decorate5([
|
|
|
1508
1548
|
String
|
|
1509
1549
|
]),
|
|
1510
1550
|
_ts_metadata4("design:returntype", Promise)
|
|
1511
|
-
],
|
|
1551
|
+
], DurableApiControllerBase.prototype, "redispatch", null);
|
|
1512
1552
|
_ts_decorate5([
|
|
1513
1553
|
Post("webhooks/:token"),
|
|
1514
1554
|
_ts_param2(0, Param("token")),
|
|
@@ -1519,7 +1559,7 @@ _ts_decorate5([
|
|
|
1519
1559
|
Object
|
|
1520
1560
|
]),
|
|
1521
1561
|
_ts_metadata4("design:returntype", Promise)
|
|
1522
|
-
],
|
|
1562
|
+
], DurableApiControllerBase.prototype, "webhook", null);
|
|
1523
1563
|
_ts_decorate5([
|
|
1524
1564
|
Get("runs/:id/events/:key"),
|
|
1525
1565
|
_ts_param2(0, Param("id")),
|
|
@@ -1530,7 +1570,7 @@ _ts_decorate5([
|
|
|
1530
1570
|
String
|
|
1531
1571
|
]),
|
|
1532
1572
|
_ts_metadata4("design:returntype", void 0)
|
|
1533
|
-
],
|
|
1573
|
+
], DurableApiControllerBase.prototype, "event", null);
|
|
1534
1574
|
_ts_decorate5([
|
|
1535
1575
|
Post("runs/:id/updates/:name"),
|
|
1536
1576
|
_ts_param2(0, Param("id")),
|
|
@@ -1543,8 +1583,8 @@ _ts_decorate5([
|
|
|
1543
1583
|
Object
|
|
1544
1584
|
]),
|
|
1545
1585
|
_ts_metadata4("design:returntype", void 0)
|
|
1546
|
-
],
|
|
1547
|
-
|
|
1586
|
+
], DurableApiControllerBase.prototype, "update", null);
|
|
1587
|
+
DurableApiControllerBase = _ts_decorate5([
|
|
1548
1588
|
Controller(),
|
|
1549
1589
|
UseInterceptors(ApplyFilterInterceptor),
|
|
1550
1590
|
UseFilters(NotModifiedFilter),
|
|
@@ -1553,7 +1593,7 @@ DurableApiController = _ts_decorate5([
|
|
|
1553
1593
|
typeof DashboardService === "undefined" ? Object : DashboardService,
|
|
1554
1594
|
typeof FilterRunner === "undefined" ? Object : FilterRunner
|
|
1555
1595
|
])
|
|
1556
|
-
],
|
|
1596
|
+
], DurableApiControllerBase);
|
|
1557
1597
|
function notFound(error) {
|
|
1558
1598
|
if (error instanceof Error && error.name === "ScheduleNotFoundError") {
|
|
1559
1599
|
throw new NotFoundException(error.message);
|
|
@@ -1561,498 +1601,59 @@ function notFound(error) {
|
|
|
1561
1601
|
throw error;
|
|
1562
1602
|
}
|
|
1563
1603
|
__name(notFound, "notFound");
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
// src/server/auth/dashboard-auth-config.ts
|
|
1569
|
-
var DASHBOARD_AUTH = Symbol("DASHBOARD_AUTH");
|
|
1570
|
-
var DEFAULT_TTL = "8h";
|
|
1571
|
-
var DEFAULT_TTL_MS = 8 * 60 * 60 * 1e3;
|
|
1572
|
-
var DURATION_UNITS = {
|
|
1573
|
-
s: 1e3,
|
|
1574
|
-
m: 60 * 1e3,
|
|
1575
|
-
h: 60 * 60 * 1e3,
|
|
1576
|
-
d: 24 * 60 * 60 * 1e3
|
|
1577
|
-
};
|
|
1578
|
-
function durationToMs(ttl) {
|
|
1579
|
-
const match = /^(\d+)([smhd])$/.exec(ttl.trim());
|
|
1580
|
-
if (!match) return DEFAULT_TTL_MS;
|
|
1581
|
-
const unit = DURATION_UNITS[match[2] ?? ""];
|
|
1582
|
-
if (unit === void 0) return DEFAULT_TTL_MS;
|
|
1583
|
-
return Number(match[1]) * unit;
|
|
1584
|
-
}
|
|
1585
|
-
__name(durationToMs, "durationToMs");
|
|
1586
|
-
function resolveDashboardAuth(options) {
|
|
1587
|
-
if (options === void 0) return null;
|
|
1588
|
-
if (typeof options.secret !== "string" || options.secret === "") {
|
|
1589
|
-
throw new Error("DurableDashboardModule: dashboardAuth.secret is required and must be a non-empty string (HMAC-SHA256 signing key, 32+ bytes recommended). Failing closed.");
|
|
1590
|
-
}
|
|
1591
|
-
const modes = [];
|
|
1592
|
-
if (options.session !== void 0) {
|
|
1593
|
-
if (typeof options.session !== "function") {
|
|
1594
|
-
throw new Error("DurableDashboardModule: dashboardAuth.session must be a function.");
|
|
1595
|
-
}
|
|
1596
|
-
modes.push("session");
|
|
1597
|
-
}
|
|
1598
|
-
if (options.login !== void 0) {
|
|
1599
|
-
if (typeof options.login !== "function") {
|
|
1600
|
-
throw new Error("DurableDashboardModule: dashboardAuth.login must be a function.");
|
|
1601
|
-
}
|
|
1602
|
-
modes.push("login");
|
|
1603
|
-
}
|
|
1604
|
-
if (options.revalidate !== void 0 && typeof options.revalidate !== "function") {
|
|
1605
|
-
throw new Error("DurableDashboardModule: dashboardAuth.revalidate must be a function.");
|
|
1604
|
+
function notSupportedByServer(error) {
|
|
1605
|
+
if (error?.code === "unsupported-request") {
|
|
1606
|
+
throw new NotImplementedException(error instanceof Error ? error.message : String(error));
|
|
1606
1607
|
}
|
|
1607
|
-
|
|
1608
|
-
throw new Error("DurableDashboardModule: dashboardAuth.unauthenticatedPage must be a function.");
|
|
1609
|
-
}
|
|
1610
|
-
if (modes.length === 0) {
|
|
1611
|
-
throw new Error("DurableDashboardModule: dashboardAuth needs at least one of `session` or `login` (otherwise the cookie can never be minted). Failing closed.");
|
|
1612
|
-
}
|
|
1613
|
-
return {
|
|
1614
|
-
secret: options.secret,
|
|
1615
|
-
ttlMs: durationToMs(options.ttl ?? DEFAULT_TTL),
|
|
1616
|
-
modes,
|
|
1617
|
-
...options.session !== void 0 ? {
|
|
1618
|
-
session: options.session
|
|
1619
|
-
} : {},
|
|
1620
|
-
...options.login !== void 0 ? {
|
|
1621
|
-
login: options.login
|
|
1622
|
-
} : {},
|
|
1623
|
-
...options.revalidate !== void 0 ? {
|
|
1624
|
-
revalidate: options.revalidate
|
|
1625
|
-
} : {},
|
|
1626
|
-
...options.unauthenticatedPage !== void 0 ? {
|
|
1627
|
-
unauthenticatedPage: options.unauthenticatedPage
|
|
1628
|
-
} : {}
|
|
1629
|
-
};
|
|
1630
|
-
}
|
|
1631
|
-
__name(resolveDashboardAuth, "resolveDashboardAuth");
|
|
1632
|
-
|
|
1633
|
-
// src/server/auth/login-page.ts
|
|
1634
|
-
function page(content, title = "Durable") {
|
|
1635
|
-
return `<!doctype html>
|
|
1636
|
-
<html lang="en">
|
|
1637
|
-
<head>
|
|
1638
|
-
<meta charset="utf-8" />
|
|
1639
|
-
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
1640
|
-
<meta name="robots" content="noindex, nofollow" />
|
|
1641
|
-
<title>${title}</title>
|
|
1642
|
-
<style>
|
|
1643
|
-
:root { color-scheme: dark; }
|
|
1644
|
-
* { box-sizing: border-box; }
|
|
1645
|
-
body {
|
|
1646
|
-
margin: 0;
|
|
1647
|
-
min-height: 100vh;
|
|
1648
|
-
display: flex;
|
|
1649
|
-
align-items: center;
|
|
1650
|
-
justify-content: center;
|
|
1651
|
-
background: #09090b;
|
|
1652
|
-
color: #e4e4e7;
|
|
1653
|
-
font: 14px/1.5 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
1654
|
-
padding: 16px;
|
|
1655
|
-
}
|
|
1656
|
-
.card {
|
|
1657
|
-
width: 100%;
|
|
1658
|
-
max-width: 384px;
|
|
1659
|
-
border: 1px solid #27272a;
|
|
1660
|
-
background: #18181b;
|
|
1661
|
-
border-radius: 8px;
|
|
1662
|
-
padding: 32px;
|
|
1663
|
-
box-shadow: 0 25px 50px -12px rgb(0 0 0 / 0.5);
|
|
1664
|
-
}
|
|
1665
|
-
.brand {
|
|
1666
|
-
margin: 0 0 24px;
|
|
1667
|
-
text-align: center;
|
|
1668
|
-
font-size: 18px;
|
|
1669
|
-
font-weight: 600;
|
|
1670
|
-
color: #34d399;
|
|
1671
|
-
}
|
|
1672
|
-
h1 {
|
|
1673
|
-
margin: 0 0 12px;
|
|
1674
|
-
font-size: 16px;
|
|
1675
|
-
font-weight: 600;
|
|
1676
|
-
color: #f4f4f5;
|
|
1677
|
-
}
|
|
1678
|
-
p { margin: 0 0 16px; color: #a1a1aa; }
|
|
1679
|
-
form { display: flex; flex-direction: column; gap: 16px; }
|
|
1680
|
-
label { display: flex; flex-direction: column; gap: 6px; }
|
|
1681
|
-
.field-label {
|
|
1682
|
-
font-size: 11px;
|
|
1683
|
-
text-transform: uppercase;
|
|
1684
|
-
letter-spacing: 0.05em;
|
|
1685
|
-
color: #71717a;
|
|
1686
|
-
}
|
|
1687
|
-
input {
|
|
1688
|
-
border-radius: 4px;
|
|
1689
|
-
border: 1px solid #3f3f46;
|
|
1690
|
-
background: #09090b;
|
|
1691
|
-
color: #f4f4f5;
|
|
1692
|
-
padding: 8px 12px;
|
|
1693
|
-
font: inherit;
|
|
1694
|
-
outline: none;
|
|
1695
|
-
}
|
|
1696
|
-
input:focus { border-color: rgb(52 211 153 / 0.6); }
|
|
1697
|
-
#error {
|
|
1698
|
-
display: none;
|
|
1699
|
-
margin: 0;
|
|
1700
|
-
font-size: 12px;
|
|
1701
|
-
color: #fb7185;
|
|
1702
|
-
}
|
|
1703
|
-
button, .button {
|
|
1704
|
-
margin-top: 8px;
|
|
1705
|
-
border-radius: 4px;
|
|
1706
|
-
border: 1px solid rgb(52 211 153 / 0.4);
|
|
1707
|
-
background: rgb(52 211 153 / 0.1);
|
|
1708
|
-
color: #6ee7b7;
|
|
1709
|
-
padding: 8px 12px;
|
|
1710
|
-
font: inherit;
|
|
1711
|
-
font-size: 12px;
|
|
1712
|
-
font-weight: 500;
|
|
1713
|
-
text-transform: uppercase;
|
|
1714
|
-
letter-spacing: 0.05em;
|
|
1715
|
-
cursor: pointer;
|
|
1716
|
-
display: inline-block;
|
|
1717
|
-
text-align: center;
|
|
1718
|
-
text-decoration: none;
|
|
1719
|
-
box-sizing: border-box;
|
|
1720
|
-
}
|
|
1721
|
-
button:hover:not(:disabled), .button:hover { background: rgb(52 211 153 / 0.2); }
|
|
1722
|
-
button:disabled { opacity: 0.6; cursor: default; }
|
|
1723
|
-
</style>
|
|
1724
|
-
</head>
|
|
1725
|
-
<body>
|
|
1726
|
-
<div class="card">
|
|
1727
|
-
${content}
|
|
1728
|
-
</div>
|
|
1729
|
-
</body>
|
|
1730
|
-
</html>`;
|
|
1608
|
+
throw error;
|
|
1731
1609
|
}
|
|
1732
|
-
__name(
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
return page(`<p class="brand">Durable</p>
|
|
1737
|
-
<form id="login-form" autocomplete="on">
|
|
1738
|
-
<label>
|
|
1739
|
-
<span class="field-label">Username</span>
|
|
1740
|
-
<input id="username" name="username" type="text" autocomplete="username" required autofocus />
|
|
1741
|
-
</label>
|
|
1742
|
-
<label>
|
|
1743
|
-
<span class="field-label">Password</span>
|
|
1744
|
-
<input id="password" name="password" type="password" autocomplete="current-password" />
|
|
1745
|
-
</label>
|
|
1746
|
-
<p id="error" role="alert">Invalid username or password.</p>
|
|
1747
|
-
<button id="submit" type="submit">Sign in</button>
|
|
1748
|
-
</form>
|
|
1749
|
-
<script>
|
|
1750
|
-
(function () {
|
|
1751
|
-
var LOGIN_ACTION = ${JSON.stringify(loginAction)};
|
|
1752
|
-
var DEFAULT_RETURN_TO = ${JSON.stringify(defaultReturnTo)};
|
|
1753
|
-
var params = new URLSearchParams(window.location.search);
|
|
1754
|
-
var errorBox = document.getElementById('error');
|
|
1755
|
-
if (params.get('error')) errorBox.style.display = 'block';
|
|
1756
|
-
|
|
1757
|
-
function sameOriginReturnTo(value) {
|
|
1758
|
-
if (typeof value !== 'string' || value === '') return DEFAULT_RETURN_TO;
|
|
1759
|
-
if (value.charAt(0) !== '/' || value.charAt(1) === '/' || value.indexOf('://') !== -1) {
|
|
1760
|
-
return DEFAULT_RETURN_TO;
|
|
1761
|
-
}
|
|
1762
|
-
return value;
|
|
1610
|
+
__name(notSupportedByServer, "notSupportedByServer");
|
|
1611
|
+
var DurableApiController = class extends DurableApiControllerBase {
|
|
1612
|
+
static {
|
|
1613
|
+
__name(this, "DurableApiController");
|
|
1763
1614
|
}
|
|
1615
|
+
};
|
|
1616
|
+
DurableApiController = _ts_decorate5([
|
|
1617
|
+
Controller()
|
|
1618
|
+
], DurableApiController);
|
|
1764
1619
|
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
errorBox.style.display = 'none';
|
|
1771
|
-
var returnTo = sameOriginReturnTo(params.get('returnTo'));
|
|
1772
|
-
fetch(LOGIN_ACTION, {
|
|
1773
|
-
method: 'POST',
|
|
1774
|
-
headers: { 'content-type': 'application/json' },
|
|
1775
|
-
credentials: 'same-origin',
|
|
1776
|
-
body: JSON.stringify({
|
|
1777
|
-
username: document.getElementById('username').value,
|
|
1778
|
-
password: document.getElementById('password').value,
|
|
1779
|
-
returnTo: returnTo,
|
|
1780
|
-
}),
|
|
1781
|
-
})
|
|
1782
|
-
.then(function (response) {
|
|
1783
|
-
if (!response.ok) throw new Error('unauthorized');
|
|
1784
|
-
return response.json();
|
|
1785
|
-
})
|
|
1786
|
-
.then(function (data) {
|
|
1787
|
-
window.location.href = data.redirectTo || DEFAULT_RETURN_TO;
|
|
1788
|
-
})
|
|
1789
|
-
.catch(function () {
|
|
1790
|
-
errorBox.style.display = 'block';
|
|
1791
|
-
submitButton.disabled = false;
|
|
1792
|
-
});
|
|
1793
|
-
});
|
|
1794
|
-
})();
|
|
1795
|
-
</script>`, "Sign in \u2014 Durable");
|
|
1796
|
-
}
|
|
1797
|
-
__name(renderLoginPage, "renderLoginPage");
|
|
1798
|
-
function renderSessionRequiredPage(basePath) {
|
|
1799
|
-
return page(`<h1>Open this console from your application</h1>
|
|
1800
|
-
<p>Your session is minted by the host app. Use its console launcher to sign in, then reload.</p>
|
|
1801
|
-
<a class="button" href="${basePath}">Retry</a>`);
|
|
1802
|
-
}
|
|
1803
|
-
__name(renderSessionRequiredPage, "renderSessionRequiredPage");
|
|
1804
|
-
|
|
1805
|
-
// src/server/auth/request.ts
|
|
1806
|
-
function isRecord2(value) {
|
|
1807
|
-
return typeof value === "object" && value !== null;
|
|
1808
|
-
}
|
|
1809
|
-
__name(isRecord2, "isRecord");
|
|
1810
|
-
function readHeaders(request) {
|
|
1811
|
-
if (!isRecord2(request)) return {};
|
|
1812
|
-
const headers = request.headers;
|
|
1813
|
-
return isRecord2(headers) ? headers : {};
|
|
1814
|
-
}
|
|
1815
|
-
__name(readHeaders, "readHeaders");
|
|
1816
|
-
function headerToString(value) {
|
|
1817
|
-
if (typeof value === "string") return value;
|
|
1818
|
-
if (Array.isArray(value) && value.every((part) => typeof part === "string")) {
|
|
1819
|
-
return value.join(",");
|
|
1820
|
-
}
|
|
1821
|
-
return void 0;
|
|
1822
|
-
}
|
|
1823
|
-
__name(headerToString, "headerToString");
|
|
1824
|
-
function readCookieHeader(request) {
|
|
1825
|
-
return headerToString(readHeaders(request).cookie);
|
|
1826
|
-
}
|
|
1827
|
-
__name(readCookieHeader, "readCookieHeader");
|
|
1828
|
-
function isHttpsRequest(request) {
|
|
1829
|
-
if (isRecord2(request) && request.protocol === "https") return true;
|
|
1830
|
-
const forwarded = headerToString(readHeaders(request)["x-forwarded-proto"]);
|
|
1831
|
-
if (forwarded === void 0) return false;
|
|
1832
|
-
return forwarded.split(",").map((proto) => proto.trim().toLowerCase()).includes("https");
|
|
1833
|
-
}
|
|
1834
|
-
__name(isHttpsRequest, "isHttpsRequest");
|
|
1835
|
-
function originalRequestUrl(request) {
|
|
1836
|
-
if (!isRecord2(request)) return "/";
|
|
1837
|
-
if (typeof request.originalUrl === "string" && request.originalUrl !== "") {
|
|
1838
|
-
return request.originalUrl;
|
|
1839
|
-
}
|
|
1840
|
-
if (typeof request.url === "string" && request.url !== "") return request.url;
|
|
1841
|
-
return "/";
|
|
1842
|
-
}
|
|
1843
|
-
__name(originalRequestUrl, "originalRequestUrl");
|
|
1844
|
-
function sanitizeReturnTo(candidate, fallback) {
|
|
1845
|
-
if (typeof candidate !== "string") return fallback;
|
|
1846
|
-
if (!candidate.startsWith("/")) return fallback;
|
|
1847
|
-
if (candidate.startsWith("//")) return fallback;
|
|
1848
|
-
if (candidate.includes("://")) return fallback;
|
|
1849
|
-
return candidate;
|
|
1850
|
-
}
|
|
1851
|
-
__name(sanitizeReturnTo, "sanitizeReturnTo");
|
|
1620
|
+
// src/server/durable-dashboard.module.ts
|
|
1621
|
+
import "reflect-metadata";
|
|
1622
|
+
import { ApplyFilterInterceptor as ApplyFilterInterceptor2, FilterRunner as FilterRunner2 } from "@dudousxd/nestjs-filter";
|
|
1623
|
+
import { Logger, Module } from "@nestjs/common";
|
|
1624
|
+
import { ModuleRef, RouterModule } from "@nestjs/core";
|
|
1852
1625
|
|
|
1853
|
-
// src/server/
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
}
|
|
1866
|
-
if (name in cookies) continue;
|
|
1867
|
-
try {
|
|
1868
|
-
cookies[name] = decodeURIComponent(rawValue);
|
|
1869
|
-
} catch {
|
|
1870
|
-
cookies[name] = rawValue;
|
|
1626
|
+
// src/server/controller-decorators.ts
|
|
1627
|
+
import "reflect-metadata";
|
|
1628
|
+
var CAN_ANYONE_METADATA = "authorization:can-anyone";
|
|
1629
|
+
function markOpenToAuthorization(controller) {
|
|
1630
|
+
Reflect.defineMetadata(CAN_ANYONE_METADATA, true, controller);
|
|
1631
|
+
}
|
|
1632
|
+
__name(markOpenToAuthorization, "markOpenToAuthorization");
|
|
1633
|
+
function controllerFor(base, decorators) {
|
|
1634
|
+
if (decorators === void 0 || decorators.length === 0) return base;
|
|
1635
|
+
const derived = class extends base {
|
|
1636
|
+
static {
|
|
1637
|
+
__name(this, "derived");
|
|
1871
1638
|
}
|
|
1872
|
-
}
|
|
1873
|
-
return cookies;
|
|
1874
|
-
}
|
|
1875
|
-
__name(parseCookieHeader, "parseCookieHeader");
|
|
1876
|
-
function serializeSetCookie(name, value, options) {
|
|
1877
|
-
const parts = [
|
|
1878
|
-
`${name}=${options.clear ? "" : encodeURIComponent(value)}`
|
|
1879
|
-
];
|
|
1880
|
-
parts.push(`Path=${options.path}`);
|
|
1881
|
-
parts.push("HttpOnly");
|
|
1882
|
-
parts.push("SameSite=Lax");
|
|
1883
|
-
if (options.secure) parts.push("Secure");
|
|
1884
|
-
if (options.clear) {
|
|
1885
|
-
parts.push("Max-Age=0");
|
|
1886
|
-
parts.push("Expires=Thu, 01 Jan 1970 00:00:00 GMT");
|
|
1887
|
-
} else {
|
|
1888
|
-
parts.push(`Max-Age=${options.maxAgeSeconds}`);
|
|
1889
|
-
}
|
|
1890
|
-
return parts.join("; ");
|
|
1891
|
-
}
|
|
1892
|
-
__name(serializeSetCookie, "serializeSetCookie");
|
|
1893
|
-
|
|
1894
|
-
// src/server/auth/session-cookie.ts
|
|
1895
|
-
import { createHmac, timingSafeEqual } from "node:crypto";
|
|
1896
|
-
var EXP_GRACE_MS = 3e4;
|
|
1897
|
-
function base64urlEncode(value) {
|
|
1898
|
-
return Buffer.from(value, "utf8").toString("base64url");
|
|
1899
|
-
}
|
|
1900
|
-
__name(base64urlEncode, "base64urlEncode");
|
|
1901
|
-
function base64urlDecode(value) {
|
|
1902
|
-
return Buffer.from(value, "base64url").toString("utf8");
|
|
1903
|
-
}
|
|
1904
|
-
__name(base64urlDecode, "base64urlDecode");
|
|
1905
|
-
function hmac(secret, payload) {
|
|
1906
|
-
return createHmac("sha256", secret).update(payload).digest("base64url");
|
|
1907
|
-
}
|
|
1908
|
-
__name(hmac, "hmac");
|
|
1909
|
-
function signSessionCookie(user, options) {
|
|
1910
|
-
const issuedAt = options.now ?? Date.now();
|
|
1911
|
-
const session = {
|
|
1912
|
-
sub: user.id,
|
|
1913
|
-
...user.name !== void 0 ? {
|
|
1914
|
-
name: user.name
|
|
1915
|
-
} : {},
|
|
1916
|
-
roles: user.roles ?? [],
|
|
1917
|
-
iat: issuedAt,
|
|
1918
|
-
exp: issuedAt + options.ttlMs
|
|
1919
|
-
};
|
|
1920
|
-
const encodedPayload = base64urlEncode(JSON.stringify(session));
|
|
1921
|
-
const signature = hmac(options.secret, encodedPayload);
|
|
1922
|
-
return `${encodedPayload}.${signature}`;
|
|
1923
|
-
}
|
|
1924
|
-
__name(signSessionCookie, "signSessionCookie");
|
|
1925
|
-
function isSessionPayload(value) {
|
|
1926
|
-
if (typeof value !== "object" || value === null) return false;
|
|
1927
|
-
const record = {
|
|
1928
|
-
...value
|
|
1929
1639
|
};
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
if (!Array.isArray(record.roles)) return false;
|
|
1933
|
-
if (!record.roles.every((role) => typeof role === "string")) return false;
|
|
1934
|
-
if (typeof record.iat !== "number" || !Number.isFinite(record.iat)) return false;
|
|
1935
|
-
if (typeof record.exp !== "number" || !Number.isFinite(record.exp)) return false;
|
|
1936
|
-
return true;
|
|
1937
|
-
}
|
|
1938
|
-
__name(isSessionPayload, "isSessionPayload");
|
|
1939
|
-
function verifySessionCookie(value, options) {
|
|
1940
|
-
try {
|
|
1941
|
-
const dot = value.indexOf(".");
|
|
1942
|
-
if (dot <= 0 || dot === value.length - 1) return null;
|
|
1943
|
-
const encodedPayload = value.slice(0, dot);
|
|
1944
|
-
const providedSignature = value.slice(dot + 1);
|
|
1945
|
-
const expectedSignature = hmac(options.secret, encodedPayload);
|
|
1946
|
-
const provided = Buffer.from(providedSignature, "base64url");
|
|
1947
|
-
const expected = Buffer.from(expectedSignature, "base64url");
|
|
1948
|
-
if (provided.length !== expected.length) return null;
|
|
1949
|
-
if (!timingSafeEqual(provided, expected)) return null;
|
|
1950
|
-
const decoded = JSON.parse(base64urlDecode(encodedPayload));
|
|
1951
|
-
if (!isSessionPayload(decoded)) return null;
|
|
1952
|
-
const now = options.now ?? Date.now();
|
|
1953
|
-
if (now > decoded.exp + EXP_GRACE_MS) return null;
|
|
1954
|
-
const session = {
|
|
1955
|
-
sub: decoded.sub,
|
|
1956
|
-
...decoded.name !== void 0 ? {
|
|
1957
|
-
name: decoded.name
|
|
1958
|
-
} : {},
|
|
1959
|
-
roles: decoded.roles,
|
|
1960
|
-
iat: decoded.iat,
|
|
1961
|
-
exp: decoded.exp
|
|
1962
|
-
};
|
|
1963
|
-
return session;
|
|
1964
|
-
} catch {
|
|
1965
|
-
return null;
|
|
1966
|
-
}
|
|
1967
|
-
}
|
|
1968
|
-
__name(verifySessionCookie, "verifySessionCookie");
|
|
1969
|
-
|
|
1970
|
-
// src/server/auth/session-cookie-io.ts
|
|
1971
|
-
var SESSION_COOKIE_NAME = "durable_dashboard_session";
|
|
1972
|
-
var COOKIE_PATH = "/";
|
|
1973
|
-
function issueSessionCookie(user, context) {
|
|
1974
|
-
const value = signSessionCookie(user, {
|
|
1975
|
-
secret: context.auth.secret,
|
|
1976
|
-
ttlMs: context.auth.ttlMs,
|
|
1977
|
-
...context.now !== void 0 ? {
|
|
1978
|
-
now: context.now
|
|
1979
|
-
} : {}
|
|
1980
|
-
});
|
|
1981
|
-
const cookie = serializeSetCookie(SESSION_COOKIE_NAME, value, {
|
|
1982
|
-
path: COOKIE_PATH,
|
|
1983
|
-
maxAgeSeconds: Math.floor(context.auth.ttlMs / 1e3),
|
|
1984
|
-
secure: isHttpsRequest(context.request)
|
|
1985
|
-
});
|
|
1986
|
-
appendSetCookie(context.response, cookie);
|
|
1987
|
-
}
|
|
1988
|
-
__name(issueSessionCookie, "issueSessionCookie");
|
|
1989
|
-
function clearSessionCookie(context) {
|
|
1990
|
-
const cookie = serializeSetCookie(SESSION_COOKIE_NAME, "", {
|
|
1991
|
-
path: COOKIE_PATH,
|
|
1992
|
-
maxAgeSeconds: 0,
|
|
1993
|
-
secure: isHttpsRequest(context.request),
|
|
1994
|
-
clear: true
|
|
1995
|
-
});
|
|
1996
|
-
appendSetCookie(context.response, cookie);
|
|
1997
|
-
}
|
|
1998
|
-
__name(clearSessionCookie, "clearSessionCookie");
|
|
1999
|
-
function readSessionFromRequest(auth, request) {
|
|
2000
|
-
const cookieValue = parseCookieHeader(readCookieHeader(request))[SESSION_COOKIE_NAME];
|
|
2001
|
-
if (cookieValue === void 0) return null;
|
|
2002
|
-
return verifySessionCookie(cookieValue, {
|
|
2003
|
-
secret: auth.secret
|
|
1640
|
+
Object.defineProperty(derived, "name", {
|
|
1641
|
+
value: base.name
|
|
2004
1642
|
});
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
const revalidate = auth.revalidate;
|
|
2010
|
-
if (!revalidate) return Promise.resolve(true);
|
|
2011
|
-
const key = `${auth.secret}:${user.id}`;
|
|
2012
|
-
const inFlight = inFlightRevalidations.get(key);
|
|
2013
|
-
if (inFlight) return inFlight;
|
|
2014
|
-
const call = Promise.resolve().then(() => revalidate(user)).catch(() => false).finally(() => {
|
|
2015
|
-
inFlightRevalidations.delete(key);
|
|
2016
|
-
});
|
|
2017
|
-
inFlightRevalidations.set(key, call);
|
|
2018
|
-
return call;
|
|
2019
|
-
}
|
|
2020
|
-
__name(revalidateOnce, "revalidateOnce");
|
|
2021
|
-
async function maybeRenewSession(auth, session, request, response, now = Date.now()) {
|
|
2022
|
-
if (now - session.iat <= auth.ttlMs / 2) return true;
|
|
2023
|
-
const user = {
|
|
2024
|
-
id: session.sub,
|
|
2025
|
-
...session.name !== void 0 ? {
|
|
2026
|
-
name: session.name
|
|
2027
|
-
} : {},
|
|
2028
|
-
roles: session.roles
|
|
2029
|
-
};
|
|
2030
|
-
const allowed = await revalidateOnce(auth, user);
|
|
2031
|
-
if (!allowed) {
|
|
2032
|
-
clearSessionCookie({
|
|
2033
|
-
request,
|
|
2034
|
-
response
|
|
2035
|
-
});
|
|
2036
|
-
return false;
|
|
1643
|
+
markOpenToAuthorization(derived);
|
|
1644
|
+
let decorated = derived;
|
|
1645
|
+
for (const decorator of decorators) {
|
|
1646
|
+
decorated = decorator(decorated) ?? decorated;
|
|
2037
1647
|
}
|
|
2038
|
-
|
|
2039
|
-
auth,
|
|
2040
|
-
request,
|
|
2041
|
-
response,
|
|
2042
|
-
now
|
|
2043
|
-
});
|
|
2044
|
-
return true;
|
|
1648
|
+
return decorated;
|
|
2045
1649
|
}
|
|
2046
|
-
__name(
|
|
1650
|
+
__name(controllerFor, "controllerFor");
|
|
2047
1651
|
|
|
2048
1652
|
// src/server/durable-ui.controller.ts
|
|
2049
1653
|
import { existsSync, readFileSync } from "node:fs";
|
|
2050
1654
|
import { basename, extname, join, resolve, sep } from "node:path";
|
|
2051
1655
|
import { fileURLToPath } from "node:url";
|
|
2052
|
-
import { Controller as Controller2, Get as Get2, Header as Header2, Inject as
|
|
2053
|
-
|
|
2054
|
-
// src/server/auth/login-redirect.exception.ts
|
|
2055
|
-
import { Catch as Catch2, HttpException as HttpException2, HttpStatus } from "@nestjs/common";
|
|
1656
|
+
import { Controller as Controller2, Get as Get2, Header as Header2, Inject as Inject2, NotFoundException as NotFoundException2, Param as Param2, StreamableFile } from "@nestjs/common";
|
|
2056
1657
|
function _ts_decorate6(decorators, target, key, desc) {
|
|
2057
1658
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
2058
1659
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
|
|
@@ -2067,46 +1668,6 @@ function _ts_decorate6(decorators, target, key, desc) {
|
|
|
2067
1668
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
2068
1669
|
}
|
|
2069
1670
|
__name(_ts_decorate6, "_ts_decorate");
|
|
2070
|
-
var DashboardLoginRedirectException = class extends HttpException2 {
|
|
2071
|
-
static {
|
|
2072
|
-
__name(this, "DashboardLoginRedirectException");
|
|
2073
|
-
}
|
|
2074
|
-
location;
|
|
2075
|
-
constructor(location) {
|
|
2076
|
-
super({
|
|
2077
|
-
location
|
|
2078
|
-
}, HttpStatus.FOUND), this.location = location;
|
|
2079
|
-
}
|
|
2080
|
-
};
|
|
2081
|
-
var DashboardLoginRedirectFilter = class {
|
|
2082
|
-
static {
|
|
2083
|
-
__name(this, "DashboardLoginRedirectFilter");
|
|
2084
|
-
}
|
|
2085
|
-
catch(exception, host) {
|
|
2086
|
-
const response = host.switchToHttp().getResponse();
|
|
2087
|
-
redirectRaw(response, exception.location);
|
|
2088
|
-
}
|
|
2089
|
-
};
|
|
2090
|
-
DashboardLoginRedirectFilter = _ts_decorate6([
|
|
2091
|
-
Catch2(DashboardLoginRedirectException)
|
|
2092
|
-
], DashboardLoginRedirectFilter);
|
|
2093
|
-
|
|
2094
|
-
// src/server/auth/session-required.exception.ts
|
|
2095
|
-
import { Catch as Catch3, HttpException as HttpException3, HttpStatus as HttpStatus2, Inject as Inject2, Logger, Optional as Optional2 } from "@nestjs/common";
|
|
2096
|
-
function _ts_decorate7(decorators, target, key, desc) {
|
|
2097
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
2098
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
|
|
2099
|
-
r = Reflect.decorate(decorators, target, key, desc);
|
|
2100
|
-
} else {
|
|
2101
|
-
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
2102
|
-
if (d = decorators[i]) {
|
|
2103
|
-
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
2104
|
-
}
|
|
2105
|
-
}
|
|
2106
|
-
}
|
|
2107
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
2108
|
-
}
|
|
2109
|
-
__name(_ts_decorate7, "_ts_decorate");
|
|
2110
1671
|
function _ts_metadata5(metadataKey, metadataValue) {
|
|
2111
1672
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") {
|
|
2112
1673
|
return Reflect.metadata(metadataKey, metadataValue);
|
|
@@ -2119,91 +1680,6 @@ function _ts_param3(paramIndex, decorator) {
|
|
|
2119
1680
|
};
|
|
2120
1681
|
}
|
|
2121
1682
|
__name(_ts_param3, "_ts_param");
|
|
2122
|
-
var DashboardSessionRequiredException = class extends HttpException3 {
|
|
2123
|
-
static {
|
|
2124
|
-
__name(this, "DashboardSessionRequiredException");
|
|
2125
|
-
}
|
|
2126
|
-
basePath;
|
|
2127
|
-
constructor(basePath) {
|
|
2128
|
-
super({
|
|
2129
|
-
basePath
|
|
2130
|
-
}, HttpStatus2.UNAUTHORIZED), this.basePath = basePath;
|
|
2131
|
-
}
|
|
2132
|
-
};
|
|
2133
|
-
var DashboardSessionRequiredFilter = class _DashboardSessionRequiredFilter {
|
|
2134
|
-
static {
|
|
2135
|
-
__name(this, "DashboardSessionRequiredFilter");
|
|
2136
|
-
}
|
|
2137
|
-
auth;
|
|
2138
|
-
logger = new Logger(_DashboardSessionRequiredFilter.name);
|
|
2139
|
-
warned = false;
|
|
2140
|
-
constructor(auth = null) {
|
|
2141
|
-
this.auth = auth;
|
|
2142
|
-
}
|
|
2143
|
-
async catch(exception, host) {
|
|
2144
|
-
const http = host.switchToHttp();
|
|
2145
|
-
const response = http.getResponse();
|
|
2146
|
-
const hook = this.auth?.unauthenticatedPage;
|
|
2147
|
-
if (hook) {
|
|
2148
|
-
try {
|
|
2149
|
-
await hook({
|
|
2150
|
-
request: http.getRequest(),
|
|
2151
|
-
response,
|
|
2152
|
-
basePath: exception.basePath
|
|
2153
|
-
});
|
|
2154
|
-
if (responseAlreadyWritten(response)) return;
|
|
2155
|
-
this.warnOnce("dashboardAuth.unauthenticatedPage returned without writing a response; falling back to the built-in page. The hook must write AND end the response it is given.");
|
|
2156
|
-
} catch (error) {
|
|
2157
|
-
this.warnOnce(`dashboardAuth.unauthenticatedPage threw; falling back to the built-in page. ${String(error)}`);
|
|
2158
|
-
if (responseAlreadyWritten(response)) return;
|
|
2159
|
-
}
|
|
2160
|
-
}
|
|
2161
|
-
writeHtmlRaw(response, renderSessionRequiredPage(exception.basePath), HttpStatus2.UNAUTHORIZED);
|
|
2162
|
-
}
|
|
2163
|
-
/** Once per process, not once per request: a broken hook fires on every denied navigation. */
|
|
2164
|
-
warnOnce(message) {
|
|
2165
|
-
if (this.warned) return;
|
|
2166
|
-
this.warned = true;
|
|
2167
|
-
this.logger.warn(message);
|
|
2168
|
-
}
|
|
2169
|
-
};
|
|
2170
|
-
DashboardSessionRequiredFilter = _ts_decorate7([
|
|
2171
|
-
Catch3(DashboardSessionRequiredException),
|
|
2172
|
-
_ts_param3(0, Optional2()),
|
|
2173
|
-
_ts_param3(0, Inject2(DASHBOARD_AUTH)),
|
|
2174
|
-
_ts_metadata5("design:type", Function),
|
|
2175
|
-
_ts_metadata5("design:paramtypes", [
|
|
2176
|
-
Object
|
|
2177
|
-
])
|
|
2178
|
-
], DashboardSessionRequiredFilter);
|
|
2179
|
-
|
|
2180
|
-
// src/server/durable-ui.controller.ts
|
|
2181
|
-
function _ts_decorate8(decorators, target, key, desc) {
|
|
2182
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
2183
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
|
|
2184
|
-
r = Reflect.decorate(decorators, target, key, desc);
|
|
2185
|
-
} else {
|
|
2186
|
-
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
2187
|
-
if (d = decorators[i]) {
|
|
2188
|
-
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
2189
|
-
}
|
|
2190
|
-
}
|
|
2191
|
-
}
|
|
2192
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
2193
|
-
}
|
|
2194
|
-
__name(_ts_decorate8, "_ts_decorate");
|
|
2195
|
-
function _ts_metadata6(metadataKey, metadataValue) {
|
|
2196
|
-
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") {
|
|
2197
|
-
return Reflect.metadata(metadataKey, metadataValue);
|
|
2198
|
-
}
|
|
2199
|
-
}
|
|
2200
|
-
__name(_ts_metadata6, "_ts_metadata");
|
|
2201
|
-
function _ts_param4(paramIndex, decorator) {
|
|
2202
|
-
return function(target, key) {
|
|
2203
|
-
decorator(target, key, paramIndex);
|
|
2204
|
-
};
|
|
2205
|
-
}
|
|
2206
|
-
__name(_ts_param4, "_ts_param");
|
|
2207
1683
|
var DASHBOARD_BASE_PATH = Symbol("DASHBOARD_BASE_PATH");
|
|
2208
1684
|
var DASHBOARD_API_PATH = Symbol("DASHBOARD_API_PATH");
|
|
2209
1685
|
var BUILD_BASE = "/durable";
|
|
@@ -2220,9 +1696,9 @@ var CONTENT_TYPES = {
|
|
|
2220
1696
|
".woff2": "font/woff2",
|
|
2221
1697
|
".ico": "image/x-icon"
|
|
2222
1698
|
};
|
|
2223
|
-
var
|
|
1699
|
+
var DurableUiControllerBase = class {
|
|
2224
1700
|
static {
|
|
2225
|
-
__name(this, "
|
|
1701
|
+
__name(this, "DurableUiControllerBase");
|
|
2226
1702
|
}
|
|
2227
1703
|
basePath;
|
|
2228
1704
|
apiBasePath;
|
|
@@ -2256,223 +1732,45 @@ var DurableUiController = class {
|
|
|
2256
1732
|
});
|
|
2257
1733
|
}
|
|
2258
1734
|
};
|
|
2259
|
-
|
|
1735
|
+
_ts_decorate6([
|
|
2260
1736
|
Get2(),
|
|
2261
1737
|
Header2("Content-Type", "text/html; charset=utf-8"),
|
|
2262
1738
|
Header2("Cache-Control", "no-store, must-revalidate"),
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
],
|
|
2267
|
-
|
|
1739
|
+
_ts_metadata5("design:type", Function),
|
|
1740
|
+
_ts_metadata5("design:paramtypes", []),
|
|
1741
|
+
_ts_metadata5("design:returntype", String)
|
|
1742
|
+
], DurableUiControllerBase.prototype, "index", null);
|
|
1743
|
+
_ts_decorate6([
|
|
2268
1744
|
Get2("assets/:file"),
|
|
2269
1745
|
Header2("Cache-Control", "public, max-age=31536000, immutable"),
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
1746
|
+
_ts_param3(0, Param2("file")),
|
|
1747
|
+
_ts_metadata5("design:type", Function),
|
|
1748
|
+
_ts_metadata5("design:paramtypes", [
|
|
2273
1749
|
String
|
|
2274
1750
|
]),
|
|
2275
|
-
|
|
2276
|
-
],
|
|
2277
|
-
|
|
2278
|
-
UseFilters2(DashboardLoginRedirectFilter, DashboardSessionRequiredFilter),
|
|
1751
|
+
_ts_metadata5("design:returntype", typeof StreamableFile === "undefined" ? Object : StreamableFile)
|
|
1752
|
+
], DurableUiControllerBase.prototype, "asset", null);
|
|
1753
|
+
DurableUiControllerBase = _ts_decorate6([
|
|
2279
1754
|
Controller2(),
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
1755
|
+
_ts_param3(0, Inject2(DASHBOARD_BASE_PATH)),
|
|
1756
|
+
_ts_param3(1, Inject2(DASHBOARD_API_PATH)),
|
|
1757
|
+
_ts_metadata5("design:type", Function),
|
|
1758
|
+
_ts_metadata5("design:paramtypes", [
|
|
2284
1759
|
String,
|
|
2285
1760
|
String
|
|
2286
1761
|
])
|
|
2287
|
-
],
|
|
2288
|
-
|
|
2289
|
-
// src/server/durable-auth.controller.ts
|
|
2290
|
-
function _ts_decorate9(decorators, target, key, desc) {
|
|
2291
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
2292
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
|
|
2293
|
-
r = Reflect.decorate(decorators, target, key, desc);
|
|
2294
|
-
} else {
|
|
2295
|
-
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
2296
|
-
if (d = decorators[i]) {
|
|
2297
|
-
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
2298
|
-
}
|
|
2299
|
-
}
|
|
2300
|
-
}
|
|
2301
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
2302
|
-
}
|
|
2303
|
-
__name(_ts_decorate9, "_ts_decorate");
|
|
2304
|
-
function _ts_metadata7(metadataKey, metadataValue) {
|
|
2305
|
-
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") {
|
|
2306
|
-
return Reflect.metadata(metadataKey, metadataValue);
|
|
2307
|
-
}
|
|
2308
|
-
}
|
|
2309
|
-
__name(_ts_metadata7, "_ts_metadata");
|
|
2310
|
-
function _ts_param5(paramIndex, decorator) {
|
|
2311
|
-
return function(target, key) {
|
|
2312
|
-
decorator(target, key, paramIndex);
|
|
2313
|
-
};
|
|
2314
|
-
}
|
|
2315
|
-
__name(_ts_param5, "_ts_param");
|
|
2316
|
-
function isString(value) {
|
|
2317
|
-
return typeof value === "string";
|
|
2318
|
-
}
|
|
2319
|
-
__name(isString, "isString");
|
|
2320
|
-
var DurableAuthController = class _DurableAuthController {
|
|
1762
|
+
], DurableUiControllerBase);
|
|
1763
|
+
var DurableUiController = class extends DurableUiControllerBase {
|
|
2321
1764
|
static {
|
|
2322
|
-
__name(this, "
|
|
2323
|
-
}
|
|
2324
|
-
auth;
|
|
2325
|
-
basePath;
|
|
2326
|
-
logger = new Logger2(_DurableAuthController.name);
|
|
2327
|
-
/** Warn once per hook kind so a throwing hook doesn't spam logs on every failed attempt. */
|
|
2328
|
-
warnedHooks = /* @__PURE__ */ new Set();
|
|
2329
|
-
constructor(auth, basePath) {
|
|
2330
|
-
this.auth = auth;
|
|
2331
|
-
this.basePath = basePath;
|
|
2332
|
-
}
|
|
2333
|
-
// The login page reads `returnTo`/`error` from `location.search` client-side (see
|
|
2334
|
-
// `login-page.ts`), so this response never varies per request and needs no templating beyond
|
|
2335
|
-
// the developer-controlled `basePath`.
|
|
2336
|
-
loginPage() {
|
|
2337
|
-
const auth = this.requireAuth();
|
|
2338
|
-
if (!auth.modes.includes("login")) throw new NotFoundException3();
|
|
2339
|
-
return renderLoginPage(this.basePath);
|
|
2340
|
-
}
|
|
2341
|
-
// Called by the login page's own fetch (JSON, not a classic form POST — see `login-page.ts` for
|
|
2342
|
-
// why: it keeps the login page working with no host-configured body parser beyond the
|
|
2343
|
-
// JSON one Nest wires in by default on both Express and Fastify). Uniform `401` on any failure
|
|
2344
|
-
// (unknown user, wrong password, throwing hook) — no user-enumeration.
|
|
2345
|
-
async login(body, request, response) {
|
|
2346
|
-
const auth = this.requireAuth();
|
|
2347
|
-
if (!auth.modes.includes("login")) throw new NotFoundException3();
|
|
2348
|
-
if (!isString(body?.username) || !isString(body?.password)) {
|
|
2349
|
-
throw new BadRequestException2("Body must include string `username` and `password`.");
|
|
2350
|
-
}
|
|
2351
|
-
const { username, password } = body;
|
|
2352
|
-
const user = await this.runHook("login", () => auth.login?.(username, password) ?? null);
|
|
2353
|
-
if (!user) throw new UnauthorizedException({
|
|
2354
|
-
message: "Invalid username or password."
|
|
2355
|
-
});
|
|
2356
|
-
issueSessionCookie(user, {
|
|
2357
|
-
auth,
|
|
2358
|
-
request,
|
|
2359
|
-
response
|
|
2360
|
-
});
|
|
2361
|
-
return {
|
|
2362
|
-
redirectTo: sanitizeReturnTo(body.returnTo, this.basePath)
|
|
2363
|
-
};
|
|
2364
|
-
}
|
|
2365
|
-
// Mode A: the host frontend (carrying its own auth) POSTs here; the host hook validates the raw
|
|
2366
|
-
// request and returns the session user, or `null` to deny. This is the SSO path — the host's own
|
|
2367
|
-
// guard decides, and no credential ever reaches this library.
|
|
2368
|
-
async session(request, response) {
|
|
2369
|
-
const auth = this.requireAuth();
|
|
2370
|
-
if (!auth.modes.includes("session")) throw new NotFoundException3();
|
|
2371
|
-
const user = await this.runHook("session", () => auth.session?.(request) ?? null);
|
|
2372
|
-
if (!user) throw new UnauthorizedException();
|
|
2373
|
-
issueSessionCookie(user, {
|
|
2374
|
-
auth,
|
|
2375
|
-
request,
|
|
2376
|
-
response
|
|
2377
|
-
});
|
|
2378
|
-
}
|
|
2379
|
-
// Plain `GET` (not `POST`): logging out only ever destroys the CALLER's own session, so it's
|
|
2380
|
-
// safe/idempotent from the caller's perspective — a simple `<a href>` (no JS, no CSRF token)
|
|
2381
|
-
// works. Non-passthrough `@Res()`: this handler owns the response outright (clears the cookie,
|
|
2382
|
-
// then redirects), so Nest must not also try to send a return value afterwards.
|
|
2383
|
-
logout(request, response) {
|
|
2384
|
-
clearSessionCookie({
|
|
2385
|
-
request,
|
|
2386
|
-
response
|
|
2387
|
-
});
|
|
2388
|
-
redirectRaw(response, this.auth?.modes.includes("login") ? `${this.basePath}/login` : this.basePath);
|
|
2389
|
-
}
|
|
2390
|
-
requireAuth() {
|
|
2391
|
-
if (!this.auth) throw new NotFoundException3();
|
|
2392
|
-
return this.auth;
|
|
2393
|
-
}
|
|
2394
|
-
/**
|
|
2395
|
-
* Run a host hook (`login` or `session`) defensively: a throw is treated as a denial (uniform
|
|
2396
|
-
* failure) and warn-logged once PER KIND, so a buggy hook never 500s the endpoint into a
|
|
2397
|
-
* stack-trace leak nor floods the logs on repeated bad attempts.
|
|
2398
|
-
*/
|
|
2399
|
-
async runHook(kind, run) {
|
|
2400
|
-
try {
|
|
2401
|
-
return await run() ?? null;
|
|
2402
|
-
} catch (error) {
|
|
2403
|
-
if (!this.warnedHooks.has(kind)) {
|
|
2404
|
-
this.warnedHooks.add(kind);
|
|
2405
|
-
this.logger.warn(`dashboardAuth ${kind} hook threw; treating as denial. ${error instanceof Error ? error.message : String(error)}`);
|
|
2406
|
-
}
|
|
2407
|
-
return null;
|
|
2408
|
-
}
|
|
1765
|
+
__name(this, "DurableUiController");
|
|
2409
1766
|
}
|
|
2410
1767
|
};
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
Header3("Cache-Control", "no-store, must-revalidate"),
|
|
2415
|
-
_ts_metadata7("design:type", Function),
|
|
2416
|
-
_ts_metadata7("design:paramtypes", []),
|
|
2417
|
-
_ts_metadata7("design:returntype", String)
|
|
2418
|
-
], DurableAuthController.prototype, "loginPage", null);
|
|
2419
|
-
_ts_decorate9([
|
|
2420
|
-
Post2("login"),
|
|
2421
|
-
HttpCode(200),
|
|
2422
|
-
_ts_param5(0, Body2()),
|
|
2423
|
-
_ts_param5(1, Req()),
|
|
2424
|
-
_ts_param5(2, Res2({
|
|
2425
|
-
passthrough: true
|
|
2426
|
-
})),
|
|
2427
|
-
_ts_metadata7("design:type", Function),
|
|
2428
|
-
_ts_metadata7("design:paramtypes", [
|
|
2429
|
-
typeof LoginBody === "undefined" ? Object : LoginBody,
|
|
2430
|
-
Object,
|
|
2431
|
-
Object
|
|
2432
|
-
]),
|
|
2433
|
-
_ts_metadata7("design:returntype", Promise)
|
|
2434
|
-
], DurableAuthController.prototype, "login", null);
|
|
2435
|
-
_ts_decorate9([
|
|
2436
|
-
Post2("session"),
|
|
2437
|
-
HttpCode(204),
|
|
2438
|
-
_ts_param5(0, Req()),
|
|
2439
|
-
_ts_param5(1, Res2({
|
|
2440
|
-
passthrough: true
|
|
2441
|
-
})),
|
|
2442
|
-
_ts_metadata7("design:type", Function),
|
|
2443
|
-
_ts_metadata7("design:paramtypes", [
|
|
2444
|
-
Object,
|
|
2445
|
-
Object
|
|
2446
|
-
]),
|
|
2447
|
-
_ts_metadata7("design:returntype", Promise)
|
|
2448
|
-
], DurableAuthController.prototype, "session", null);
|
|
2449
|
-
_ts_decorate9([
|
|
2450
|
-
Get3("logout"),
|
|
2451
|
-
_ts_param5(0, Req()),
|
|
2452
|
-
_ts_param5(1, Res2()),
|
|
2453
|
-
_ts_metadata7("design:type", Function),
|
|
2454
|
-
_ts_metadata7("design:paramtypes", [
|
|
2455
|
-
Object,
|
|
2456
|
-
Object
|
|
2457
|
-
]),
|
|
2458
|
-
_ts_metadata7("design:returntype", void 0)
|
|
2459
|
-
], DurableAuthController.prototype, "logout", null);
|
|
2460
|
-
DurableAuthController = _ts_decorate9([
|
|
2461
|
-
Controller3(),
|
|
2462
|
-
_ts_param5(0, Optional3()),
|
|
2463
|
-
_ts_param5(0, Inject4(DASHBOARD_AUTH)),
|
|
2464
|
-
_ts_param5(1, Inject4(DASHBOARD_BASE_PATH)),
|
|
2465
|
-
_ts_metadata7("design:type", Function),
|
|
2466
|
-
_ts_metadata7("design:paramtypes", [
|
|
2467
|
-
Object,
|
|
2468
|
-
String
|
|
2469
|
-
])
|
|
2470
|
-
], DurableAuthController);
|
|
1768
|
+
DurableUiController = _ts_decorate6([
|
|
1769
|
+
Controller2()
|
|
1770
|
+
], DurableUiController);
|
|
2471
1771
|
|
|
2472
|
-
// src/server/durable-dashboard
|
|
2473
|
-
|
|
2474
|
-
import { ForbiddenException, Inject as Inject5, Injectable as Injectable4, Logger as Logger3, UnauthorizedException as UnauthorizedException2 } from "@nestjs/common";
|
|
2475
|
-
function _ts_decorate10(decorators, target, key, desc) {
|
|
1772
|
+
// src/server/durable-dashboard.module.ts
|
|
1773
|
+
function _ts_decorate7(decorators, target, key, desc) {
|
|
2476
1774
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
2477
1775
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
|
|
2478
1776
|
r = Reflect.decorate(decorators, target, key, desc);
|
|
@@ -2485,342 +1783,59 @@ function _ts_decorate10(decorators, target, key, desc) {
|
|
|
2485
1783
|
}
|
|
2486
1784
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
2487
1785
|
}
|
|
2488
|
-
__name(
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
decorator(target, key, paramIndex);
|
|
2498
|
-
};
|
|
2499
|
-
}
|
|
2500
|
-
__name(_ts_param6, "_ts_param");
|
|
2501
|
-
var DURABLE_DASHBOARD_TOKEN_ENV = "DURABLE_DASHBOARD_TOKEN";
|
|
2502
|
-
var DASHBOARD_GATE = Symbol("DASHBOARD_GATE");
|
|
2503
|
-
var LOG_CONTEXT = "DurableDashboard";
|
|
2504
|
-
var HOW_TO_CONFIGURE = [
|
|
2505
|
-
"Configure one of: `guards: [YourAuthGuard]`, `dashboardAuth: { secret, session | login }`",
|
|
2506
|
-
"(or `useDashboardAuth` in forRootAsync), a bearer token via the `token` option or the",
|
|
2507
|
-
`${DURABLE_DASHBOARD_TOKEN_ENV} env var, or \`allowUnauthenticated: true\` when the console is`,
|
|
2508
|
-
"already protected outside this module (a global guard, a reverse proxy, a private network)."
|
|
1786
|
+
__name(_ts_decorate7, "_ts_decorate");
|
|
1787
|
+
markOpenToAuthorization(DurableApiController);
|
|
1788
|
+
markOpenToAuthorization(DurableUiController);
|
|
1789
|
+
var DASHBOARD_PROTECTION_CHECK = Symbol("DASHBOARD_PROTECTION_CHECK");
|
|
1790
|
+
var UNPROTECTED_CONSOLE_IN_PRODUCTION = [
|
|
1791
|
+
"DurableDashboardModule: NODE_ENV=production and the durable console is mounted with no",
|
|
1792
|
+
"protection, so the app refuses to boot. Protect it one of two ways:",
|
|
1793
|
+
"(1) decorators on the mounted console: DurableDashboardModule.forRoot({ decorators: [UseGuards(AuthenticationGuard), Can(ConsolePolicy, 'view')] });",
|
|
1794
|
+
"(2) mount it yourself: DurableDashboardModule.forRoot({ routes: false }) plus @Controller('durable') @UseGuards(AuthenticationGuard) @Can(ConsolePolicy, 'view') class ConsoleUi extends DurableUiControllerBase {} (and the same over DurableApiControllerBase at 'durable/api')."
|
|
2509
1795
|
].join(" ");
|
|
2510
1796
|
function isProduction() {
|
|
2511
1797
|
return (process.env.NODE_ENV ?? "").trim().toLowerCase() === "production";
|
|
2512
1798
|
}
|
|
2513
1799
|
__name(isProduction, "isProduction");
|
|
2514
|
-
function
|
|
2515
|
-
if (options.hasGuards || auth !== null) return {
|
|
2516
|
-
mode: "delegated"
|
|
2517
|
-
};
|
|
2518
|
-
const token = options.token ?? process.env[DURABLE_DASHBOARD_TOKEN_ENV];
|
|
2519
|
-
if (typeof token === "string" && token !== "") return {
|
|
2520
|
-
mode: "token",
|
|
2521
|
-
token
|
|
2522
|
-
};
|
|
2523
|
-
if (options.allowUnauthenticated === true) return {
|
|
2524
|
-
mode: "open"
|
|
2525
|
-
};
|
|
1800
|
+
function protectionCheckProvider() {
|
|
2526
1801
|
return {
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
switch (gate.mode) {
|
|
2533
|
-
case "open":
|
|
2534
|
-
logger.warn("allowUnauthenticated: true. The durable console (including retry/cancel and other mutating endpoints) is served with NO authentication from this module. Only do this when a global guard or reverse proxy already protects it.");
|
|
2535
|
-
return;
|
|
2536
|
-
case "token":
|
|
2537
|
-
if (options.allowUnauthenticated === true) {
|
|
2538
|
-
logger.warn("allowUnauthenticated: true is ignored because a dashboard token is configured; requests must carry the token.");
|
|
2539
|
-
}
|
|
2540
|
-
return;
|
|
2541
|
-
case "unconfigured":
|
|
2542
|
-
logger.warn([
|
|
2543
|
-
"The durable console is UNAUTHENTICATED: no guards, dashboardAuth, token or",
|
|
2544
|
-
"allowUnauthenticated were configured. It is served open because NODE_ENV is not",
|
|
2545
|
-
'"production", but it WILL deny every request when NODE_ENV=production.',
|
|
2546
|
-
HOW_TO_CONFIGURE
|
|
2547
|
-
].join(" "));
|
|
2548
|
-
return;
|
|
2549
|
-
case "deny":
|
|
2550
|
-
logger.error([
|
|
2551
|
-
"The durable console has no authentication configured and NODE_ENV=production, so it",
|
|
2552
|
-
"is failing closed: every console request (UI and API) is denied.",
|
|
2553
|
-
HOW_TO_CONFIGURE
|
|
2554
|
-
].join(" "));
|
|
2555
|
-
return;
|
|
2556
|
-
case "delegated":
|
|
2557
|
-
return;
|
|
2558
|
-
}
|
|
2559
|
-
}
|
|
2560
|
-
__name(reportDashboardGate, "reportDashboardGate");
|
|
2561
|
-
function dashboardGateProvider(options) {
|
|
2562
|
-
return {
|
|
2563
|
-
provide: DASHBOARD_GATE,
|
|
2564
|
-
inject: [
|
|
2565
|
-
DASHBOARD_AUTH
|
|
2566
|
-
],
|
|
2567
|
-
useFactory: /* @__PURE__ */ __name((auth) => {
|
|
2568
|
-
const gate = resolveDashboardGate(options, auth);
|
|
2569
|
-
reportDashboardGate(gate, options);
|
|
2570
|
-
return gate;
|
|
1802
|
+
provide: DASHBOARD_PROTECTION_CHECK,
|
|
1803
|
+
useFactory: /* @__PURE__ */ __name(() => {
|
|
1804
|
+
if (isProduction()) throw new Error(UNPROTECTED_CONSOLE_IN_PRODUCTION);
|
|
1805
|
+
new Logger("DurableDashboard").warn("The durable console is mounted with no `decorators`, so it is served OPEN (retry/cancel included). That is refused when NODE_ENV=production: pass `decorators` (e.g. [UseGuards(AuthenticationGuard), Can(ConsolePolicy, 'view')]) or mount it yourself with `routes: false`.");
|
|
1806
|
+
return true;
|
|
2571
1807
|
}, "useFactory")
|
|
2572
1808
|
};
|
|
2573
1809
|
}
|
|
2574
|
-
__name(
|
|
2575
|
-
function
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
return void 0;
|
|
2586
|
-
}
|
|
2587
|
-
__name(headerValue, "headerValue");
|
|
2588
|
-
function readDashboardToken(request) {
|
|
2589
|
-
if (!isRecord3(request)) return void 0;
|
|
2590
|
-
const authorization = headerValue(request, "authorization");
|
|
2591
|
-
if (authorization) {
|
|
2592
|
-
const match = /^Bearer\s+(.+)$/i.exec(authorization);
|
|
2593
|
-
if (match?.[1]) return match[1].trim();
|
|
2594
|
-
}
|
|
2595
|
-
const header = headerValue(request, "x-durable-token");
|
|
2596
|
-
if (header) return header.trim();
|
|
2597
|
-
const method = typeof request.method === "string" ? request.method.toUpperCase() : "";
|
|
2598
|
-
if (method === "GET" || method === "HEAD") {
|
|
2599
|
-
const query = request.query;
|
|
2600
|
-
if (isRecord3(query)) {
|
|
2601
|
-
const value = query.token;
|
|
2602
|
-
if (typeof value === "string" && value !== "") return value;
|
|
2603
|
-
}
|
|
2604
|
-
}
|
|
2605
|
-
return void 0;
|
|
2606
|
-
}
|
|
2607
|
-
__name(readDashboardToken, "readDashboardToken");
|
|
2608
|
-
function secretsMatch(provided, expected) {
|
|
2609
|
-
const a = Buffer.from(provided);
|
|
2610
|
-
const b = Buffer.from(expected);
|
|
2611
|
-
if (a.length !== b.length) return false;
|
|
2612
|
-
return timingSafeEqual2(a, b);
|
|
2613
|
-
}
|
|
2614
|
-
__name(secretsMatch, "secretsMatch");
|
|
2615
|
-
var DurableDashboardGateGuard = class {
|
|
2616
|
-
static {
|
|
2617
|
-
__name(this, "DurableDashboardGateGuard");
|
|
2618
|
-
}
|
|
2619
|
-
gate;
|
|
2620
|
-
constructor(gate) {
|
|
2621
|
-
this.gate = gate;
|
|
2622
|
-
}
|
|
2623
|
-
canActivate(context) {
|
|
2624
|
-
switch (this.gate.mode) {
|
|
2625
|
-
case "delegated":
|
|
2626
|
-
case "open":
|
|
2627
|
-
case "unconfigured":
|
|
2628
|
-
return true;
|
|
2629
|
-
case "deny":
|
|
2630
|
-
throw new ForbiddenException([
|
|
2631
|
-
"Durable dashboard authentication is not configured, and NODE_ENV=production denies",
|
|
2632
|
-
"an unauthenticated console.",
|
|
2633
|
-
HOW_TO_CONFIGURE
|
|
2634
|
-
].join(" "));
|
|
2635
|
-
case "token": {
|
|
2636
|
-
const provided = readDashboardToken(context.switchToHttp().getRequest());
|
|
2637
|
-
if (provided !== void 0 && secretsMatch(provided, this.gate.token ?? "")) return true;
|
|
2638
|
-
throw new UnauthorizedException2("A valid durable dashboard token is required (Authorization: Bearer <token>, x-durable-token, or ?token= on GET).");
|
|
2639
|
-
}
|
|
2640
|
-
}
|
|
2641
|
-
}
|
|
2642
|
-
};
|
|
2643
|
-
DurableDashboardGateGuard = _ts_decorate10([
|
|
2644
|
-
Injectable4(),
|
|
2645
|
-
_ts_param6(0, Inject5(DASHBOARD_GATE)),
|
|
2646
|
-
_ts_metadata8("design:type", Function),
|
|
2647
|
-
_ts_metadata8("design:paramtypes", [
|
|
2648
|
-
typeof DashboardGate === "undefined" ? Object : DashboardGate
|
|
2649
|
-
])
|
|
2650
|
-
], DurableDashboardGateGuard);
|
|
2651
|
-
|
|
2652
|
-
// src/server/durable-dashboard.module.ts
|
|
2653
|
-
import "reflect-metadata";
|
|
2654
|
-
import { ApplyFilterInterceptor as ApplyFilterInterceptor2, FilterRunner as FilterRunner2 } from "@dudousxd/nestjs-filter";
|
|
2655
|
-
import { Module } from "@nestjs/common";
|
|
2656
|
-
import { ModuleRef, RouterModule } from "@nestjs/core";
|
|
2657
|
-
|
|
2658
|
-
// src/server/durable-session.guard.ts
|
|
2659
|
-
import { Inject as Inject6, Injectable as Injectable5, UnauthorizedException as UnauthorizedException3 } from "@nestjs/common";
|
|
2660
|
-
function _ts_decorate11(decorators, target, key, desc) {
|
|
2661
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
2662
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
|
|
2663
|
-
r = Reflect.decorate(decorators, target, key, desc);
|
|
2664
|
-
} else {
|
|
2665
|
-
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
2666
|
-
if (d = decorators[i]) {
|
|
2667
|
-
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
2668
|
-
}
|
|
2669
|
-
}
|
|
2670
|
-
}
|
|
2671
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
2672
|
-
}
|
|
2673
|
-
__name(_ts_decorate11, "_ts_decorate");
|
|
2674
|
-
function _ts_metadata9(metadataKey, metadataValue) {
|
|
2675
|
-
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") {
|
|
2676
|
-
return Reflect.metadata(metadataKey, metadataValue);
|
|
2677
|
-
}
|
|
2678
|
-
}
|
|
2679
|
-
__name(_ts_metadata9, "_ts_metadata");
|
|
2680
|
-
function _ts_param7(paramIndex, decorator) {
|
|
2681
|
-
return function(target, key) {
|
|
2682
|
-
decorator(target, key, paramIndex);
|
|
2683
|
-
};
|
|
2684
|
-
}
|
|
2685
|
-
__name(_ts_param7, "_ts_param");
|
|
2686
|
-
var DurableUiSessionGuard = class {
|
|
2687
|
-
static {
|
|
2688
|
-
__name(this, "DurableUiSessionGuard");
|
|
2689
|
-
}
|
|
2690
|
-
auth;
|
|
2691
|
-
basePath;
|
|
2692
|
-
constructor(auth, basePath) {
|
|
2693
|
-
this.auth = auth;
|
|
2694
|
-
this.basePath = basePath;
|
|
2695
|
-
}
|
|
2696
|
-
async canActivate(context) {
|
|
2697
|
-
if (!this.auth) return true;
|
|
2698
|
-
const auth = this.auth;
|
|
2699
|
-
const http = context.switchToHttp();
|
|
2700
|
-
const request = http.getRequest();
|
|
2701
|
-
const session = readSessionFromRequest(auth, request);
|
|
2702
|
-
if (!session) {
|
|
2703
|
-
this.denyUnauthenticated(auth, request);
|
|
2704
|
-
}
|
|
2705
|
-
if (!await maybeRenewSession(auth, session, request, http.getResponse())) {
|
|
2706
|
-
this.denyUnauthenticated(auth, request);
|
|
2707
|
-
}
|
|
2708
|
-
return true;
|
|
2709
|
-
}
|
|
2710
|
-
/**
|
|
2711
|
-
* A revoked session (the host's `revalidate` hook says no) is denied exactly like an absent
|
|
2712
|
-
* one — by design, there is nothing left to distinguish "never had a session" from "had one,
|
|
2713
|
-
* then lost it" once the cookie is cleared, so both get the same Mode-aware treatment.
|
|
2714
|
-
*
|
|
2715
|
-
* Takes `auth` explicitly rather than reading `this.auth`: `canActivate`'s `!this.auth` early
|
|
2716
|
-
* return narrows the field within that method only — TS doesn't carry it across a method call
|
|
2717
|
-
* — so callers pass the already-narrowed value instead of this method re-deriving (or
|
|
2718
|
-
* asserting away) the nullability.
|
|
2719
|
-
*/
|
|
2720
|
-
denyUnauthenticated(auth, request) {
|
|
2721
|
-
if (!auth.modes.includes("login")) throw new DashboardSessionRequiredException(this.basePath);
|
|
2722
|
-
const returnTo = encodeURIComponent(originalRequestUrl(request));
|
|
2723
|
-
throw new DashboardLoginRedirectException(`${this.basePath}/login?returnTo=${returnTo}`);
|
|
2724
|
-
}
|
|
2725
|
-
};
|
|
2726
|
-
DurableUiSessionGuard = _ts_decorate11([
|
|
2727
|
-
Injectable5(),
|
|
2728
|
-
_ts_param7(0, Inject6(DASHBOARD_AUTH)),
|
|
2729
|
-
_ts_param7(1, Inject6(DASHBOARD_BASE_PATH)),
|
|
2730
|
-
_ts_metadata9("design:type", Function),
|
|
2731
|
-
_ts_metadata9("design:paramtypes", [
|
|
2732
|
-
Object,
|
|
2733
|
-
String
|
|
2734
|
-
])
|
|
2735
|
-
], DurableUiSessionGuard);
|
|
2736
|
-
var DurableApiSessionGuard = class {
|
|
2737
|
-
static {
|
|
2738
|
-
__name(this, "DurableApiSessionGuard");
|
|
2739
|
-
}
|
|
2740
|
-
auth;
|
|
2741
|
-
constructor(auth) {
|
|
2742
|
-
this.auth = auth;
|
|
2743
|
-
}
|
|
2744
|
-
async canActivate(context) {
|
|
2745
|
-
if (!this.auth) return true;
|
|
2746
|
-
const auth = this.auth;
|
|
2747
|
-
const http = context.switchToHttp();
|
|
2748
|
-
const request = http.getRequest();
|
|
2749
|
-
const session = readSessionFromRequest(auth, request);
|
|
2750
|
-
if (!session) throw this.unauthorized(auth);
|
|
2751
|
-
if (!await maybeRenewSession(auth, session, request, http.getResponse())) {
|
|
2752
|
-
throw this.unauthorized(auth);
|
|
2753
|
-
}
|
|
2754
|
-
return true;
|
|
2755
|
-
}
|
|
2756
|
-
/**
|
|
2757
|
-
* A bare 401 body carrying `{ auth: { modes } }` — mirrors `@dudousxd/nestjs-telescope`'s
|
|
2758
|
-
* dashboardAuth 401. The API is fetched (never navigated), so this is the ONLY way the console
|
|
2759
|
-
* SPA (`durable-client.ts`) learns which auth surface to send the operator to on a mid-session
|
|
2760
|
-
* 401; `DurableUiSessionGuard`'s redirect/session-required exceptions above don't reach it.
|
|
2761
|
-
*/
|
|
2762
|
-
unauthorized(auth) {
|
|
2763
|
-
return new UnauthorizedException3({
|
|
2764
|
-
auth: {
|
|
2765
|
-
modes: auth.modes
|
|
2766
|
-
}
|
|
2767
|
-
});
|
|
2768
|
-
}
|
|
2769
|
-
};
|
|
2770
|
-
DurableApiSessionGuard = _ts_decorate11([
|
|
2771
|
-
Injectable5(),
|
|
2772
|
-
_ts_param7(0, Inject6(DASHBOARD_AUTH)),
|
|
2773
|
-
_ts_metadata9("design:type", Function),
|
|
2774
|
-
_ts_metadata9("design:paramtypes", [
|
|
2775
|
-
Object
|
|
2776
|
-
])
|
|
2777
|
-
], DurableApiSessionGuard);
|
|
2778
|
-
|
|
2779
|
-
// src/server/durable-dashboard.module.ts
|
|
2780
|
-
function _ts_decorate12(decorators, target, key, desc) {
|
|
2781
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
2782
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
|
|
2783
|
-
r = Reflect.decorate(decorators, target, key, desc);
|
|
2784
|
-
} else {
|
|
2785
|
-
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
2786
|
-
if (d = decorators[i]) {
|
|
2787
|
-
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
2788
|
-
}
|
|
2789
|
-
}
|
|
2790
|
-
}
|
|
2791
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
2792
|
-
}
|
|
2793
|
-
__name(_ts_decorate12, "_ts_decorate");
|
|
2794
|
-
var GUARDS_METADATA = "__guards__";
|
|
1810
|
+
__name(protectionCheckProvider, "protectionCheckProvider");
|
|
1811
|
+
function assertNothingToDecorate(options) {
|
|
1812
|
+
if (options.decorators === void 0) return;
|
|
1813
|
+
throw new Error([
|
|
1814
|
+
"DurableDashboardModule.forRoot({ routes: false }) mounts no controllers, so `decorators`",
|
|
1815
|
+
"would protect nothing. Put your protection on the controllers you mount (extend",
|
|
1816
|
+
"DurableUiControllerBase / DurableApiControllerBase and decorate them with @UseGuards /",
|
|
1817
|
+
"@Can), and drop `decorators`."
|
|
1818
|
+
].join(" "));
|
|
1819
|
+
}
|
|
1820
|
+
__name(assertNothingToDecorate, "assertNothingToDecorate");
|
|
2795
1821
|
function normalize(path) {
|
|
2796
1822
|
return `/${path.replace(/^\/+|\/+$/g, "")}`;
|
|
2797
1823
|
}
|
|
2798
1824
|
__name(normalize, "normalize");
|
|
2799
|
-
function stampGuards(hostGuards, builtIn, controller) {
|
|
2800
|
-
const merged = [
|
|
2801
|
-
...builtIn.filter((guard) => guard !== void 0),
|
|
2802
|
-
...hostGuards ?? []
|
|
2803
|
-
];
|
|
2804
|
-
Reflect.defineMetadata(GUARDS_METADATA, merged, controller);
|
|
2805
|
-
}
|
|
2806
|
-
__name(stampGuards, "stampGuards");
|
|
2807
1825
|
var DurableApiModule = class _DurableApiModule {
|
|
2808
1826
|
static {
|
|
2809
1827
|
__name(this, "DurableApiModule");
|
|
2810
1828
|
}
|
|
2811
1829
|
static register(options) {
|
|
2812
|
-
|
|
2813
|
-
options.authMayBeConfigured ? DurableApiSessionGuard : void 0,
|
|
2814
|
-
options.stampGate ? DurableDashboardGateGuard : void 0
|
|
2815
|
-
], DurableApiController);
|
|
1830
|
+
const routes = options.routes !== false;
|
|
2816
1831
|
return {
|
|
2817
1832
|
module: _DurableApiModule,
|
|
2818
1833
|
imports: [
|
|
2819
1834
|
...options.imports ?? []
|
|
2820
1835
|
],
|
|
2821
|
-
controllers: [
|
|
2822
|
-
DurableApiController
|
|
2823
|
-
],
|
|
1836
|
+
controllers: routes ? [
|
|
1837
|
+
controllerFor(DurableApiController, options.decorators)
|
|
1838
|
+
] : [],
|
|
2824
1839
|
providers: [
|
|
2825
1840
|
DashboardService,
|
|
2826
1841
|
// `@dudousxd/nestjs-filter` is wired PRIVATELY here rather than through
|
|
@@ -2853,23 +1868,22 @@ var DurableApiModule = class _DurableApiModule {
|
|
|
2853
1868
|
provide: RUN_QUERY_ADAPTER,
|
|
2854
1869
|
useExisting: RunQueryAdapter
|
|
2855
1870
|
},
|
|
2856
|
-
RunFilter
|
|
2857
|
-
options.authProvider,
|
|
2858
|
-
DurableApiSessionGuard,
|
|
2859
|
-
// Resolved ONCE here (this module is imported by the UI module, which reuses it via the
|
|
2860
|
-
// export below), so the boot report is logged once per mount.
|
|
2861
|
-
dashboardGateProvider(options.gate),
|
|
2862
|
-
DurableDashboardGateGuard,
|
|
2863
|
-
...options.guards ?? []
|
|
1871
|
+
RunFilter
|
|
2864
1872
|
],
|
|
2865
|
-
|
|
1873
|
+
// Self-mounted (`routes: false`): the host's own subclass of `DurableApiControllerBase` is
|
|
1874
|
+
// hosted in the HOST's module, so what its constructor injects must be exported. `FilterRunner`
|
|
1875
|
+
// is only exported then: exporting it from a mounted console would shadow a host's own
|
|
1876
|
+
// `FilterModule` runner in whatever module imports the dashboard.
|
|
1877
|
+
exports: routes ? [
|
|
1878
|
+
DashboardService
|
|
1879
|
+
] : [
|
|
2866
1880
|
DashboardService,
|
|
2867
|
-
|
|
1881
|
+
FilterRunner2
|
|
2868
1882
|
]
|
|
2869
1883
|
};
|
|
2870
1884
|
}
|
|
2871
1885
|
};
|
|
2872
|
-
DurableApiModule =
|
|
1886
|
+
DurableApiModule = _ts_decorate7([
|
|
2873
1887
|
Module({})
|
|
2874
1888
|
], DurableApiModule);
|
|
2875
1889
|
var DurableDashboardModule = class _DurableDashboardModule {
|
|
@@ -2877,58 +1891,58 @@ var DurableDashboardModule = class _DurableDashboardModule {
|
|
|
2877
1891
|
__name(this, "DurableDashboardModule");
|
|
2878
1892
|
}
|
|
2879
1893
|
static forRoot(options = {}) {
|
|
2880
|
-
const authProvider = {
|
|
2881
|
-
provide: DASHBOARD_AUTH,
|
|
2882
|
-
useValue: resolveDashboardAuth(options.dashboardAuth)
|
|
2883
|
-
};
|
|
2884
|
-
const authConfigured = options.dashboardAuth !== void 0;
|
|
2885
|
-
return _DurableDashboardModule.build(options, authProvider, authConfigured, authConfigured);
|
|
2886
|
-
}
|
|
2887
|
-
static forRootAsync(options) {
|
|
2888
|
-
const authProvider = {
|
|
2889
|
-
provide: DASHBOARD_AUTH,
|
|
2890
|
-
inject: options.inject ?? [],
|
|
2891
|
-
useFactory: /* @__PURE__ */ __name(async (...deps) => resolveDashboardAuth(await options.useDashboardAuth(...deps)), "useFactory")
|
|
2892
|
-
};
|
|
2893
|
-
return _DurableDashboardModule.build(options, authProvider, true, false);
|
|
2894
|
-
}
|
|
2895
|
-
/** Shared wiring: static routing + the API module, with `dashboardAuth` supplied by `authProvider`. */
|
|
2896
|
-
static build(options, authProvider, authMayBeConfigured, authStaticallyConfigured) {
|
|
2897
1894
|
const basePath = normalize(options.basePath ?? "/durable");
|
|
2898
1895
|
const apiBasePath = normalize(options.apiBasePath ?? `${basePath}/api`);
|
|
2899
|
-
const
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
}
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
1896
|
+
const pathProviders = [
|
|
1897
|
+
{
|
|
1898
|
+
provide: DASHBOARD_BASE_PATH,
|
|
1899
|
+
useValue: basePath
|
|
1900
|
+
},
|
|
1901
|
+
{
|
|
1902
|
+
provide: DASHBOARD_API_PATH,
|
|
1903
|
+
useValue: apiBasePath
|
|
1904
|
+
}
|
|
1905
|
+
];
|
|
1906
|
+
if (options.routes === false) {
|
|
1907
|
+
assertNothingToDecorate(options);
|
|
1908
|
+
const api = DurableApiModule.register({
|
|
1909
|
+
...options.imports ? {
|
|
1910
|
+
imports: options.imports
|
|
1911
|
+
} : {},
|
|
1912
|
+
routes: false
|
|
1913
|
+
});
|
|
1914
|
+
return {
|
|
1915
|
+
module: _DurableDashboardModule,
|
|
1916
|
+
imports: [
|
|
1917
|
+
...options.imports ?? [],
|
|
1918
|
+
api
|
|
1919
|
+
],
|
|
1920
|
+
controllers: [],
|
|
1921
|
+
providers: pathProviders,
|
|
1922
|
+
// What a host's subclasses of `DurableUiControllerBase` / `DurableApiControllerBase` inject:
|
|
1923
|
+
// the SPA's paths, and the read/action service and the filter runner via the API module.
|
|
1924
|
+
exports: [
|
|
1925
|
+
DurableApiModule,
|
|
1926
|
+
DASHBOARD_BASE_PATH,
|
|
1927
|
+
DASHBOARD_API_PATH
|
|
1928
|
+
]
|
|
1929
|
+
};
|
|
1930
|
+
}
|
|
1931
|
+
const decorated = (options.decorators?.length ?? 0) > 0;
|
|
2914
1932
|
return {
|
|
2915
1933
|
module: _DurableDashboardModule,
|
|
2916
1934
|
imports: [
|
|
2917
1935
|
...options.imports ?? [],
|
|
2918
|
-
//
|
|
2919
|
-
//
|
|
1936
|
+
// Host imports must reach the API controller's HOST module: enhancers resolve from their
|
|
1937
|
+
// controller's own module, never from a parent (see DurableApiModule.register).
|
|
2920
1938
|
// Spread-only-when-set: exactOptionalPropertyTypes rejects an explicit `undefined`.
|
|
2921
1939
|
DurableApiModule.register({
|
|
2922
1940
|
...options.imports ? {
|
|
2923
1941
|
imports: options.imports
|
|
2924
1942
|
} : {},
|
|
2925
|
-
...options.
|
|
2926
|
-
|
|
2927
|
-
} : {}
|
|
2928
|
-
authProvider,
|
|
2929
|
-
authMayBeConfigured,
|
|
2930
|
-
gate,
|
|
2931
|
-
stampGate
|
|
1943
|
+
...options.decorators ? {
|
|
1944
|
+
decorators: options.decorators
|
|
1945
|
+
} : {}
|
|
2932
1946
|
}),
|
|
2933
1947
|
RouterModule.register([
|
|
2934
1948
|
{
|
|
@@ -2942,30 +1956,13 @@ var DurableDashboardModule = class _DurableDashboardModule {
|
|
|
2942
1956
|
])
|
|
2943
1957
|
],
|
|
2944
1958
|
controllers: [
|
|
2945
|
-
DurableUiController,
|
|
2946
|
-
// The auth controller (login page + logout) only exists when dashboardAuth might be
|
|
2947
|
-
// configured — mounting it unconditionally would add a new `/login` route even for hosts
|
|
2948
|
-
// that never set `dashboardAuth`, breaking "absent -> today's behavior byte-for-byte".
|
|
2949
|
-
...authMayBeConfigured ? [
|
|
2950
|
-
DurableAuthController
|
|
2951
|
-
] : []
|
|
1959
|
+
controllerFor(DurableUiController, options.decorators)
|
|
2952
1960
|
],
|
|
2953
1961
|
providers: [
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
{
|
|
2959
|
-
provide: DASHBOARD_API_PATH,
|
|
2960
|
-
useValue: apiBasePath
|
|
2961
|
-
},
|
|
2962
|
-
authProvider,
|
|
2963
|
-
DurableUiSessionGuard,
|
|
2964
|
-
// `DASHBOARD_GATE` itself comes from the imported DurableApiModule (resolved once there).
|
|
2965
|
-
DurableDashboardGateGuard,
|
|
2966
|
-
// DurableUiController (and DurableAuthController, when present) are hosted HERE, so their
|
|
2967
|
-
// guards DI-instantiate from this module.
|
|
2968
|
-
...options.guards ?? []
|
|
1962
|
+
...pathProviders,
|
|
1963
|
+
...decorated ? [] : [
|
|
1964
|
+
protectionCheckProvider()
|
|
1965
|
+
]
|
|
2969
1966
|
],
|
|
2970
1967
|
// Re-export the API module so its DashboardService reaches importers (e.g. flip's own controllers).
|
|
2971
1968
|
exports: [
|
|
@@ -2974,26 +1971,20 @@ var DurableDashboardModule = class _DurableDashboardModule {
|
|
|
2974
1971
|
};
|
|
2975
1972
|
}
|
|
2976
1973
|
};
|
|
2977
|
-
DurableDashboardModule =
|
|
1974
|
+
DurableDashboardModule = _ts_decorate7([
|
|
2978
1975
|
Module({})
|
|
2979
1976
|
], DurableDashboardModule);
|
|
2980
1977
|
export {
|
|
2981
1978
|
DASHBOARD_API_PATH,
|
|
2982
1979
|
DASHBOARD_BASE_PATH,
|
|
2983
|
-
|
|
2984
|
-
DURABLE_DASHBOARD_TOKEN_ENV,
|
|
1980
|
+
DASHBOARD_PROTECTION_CHECK,
|
|
2985
1981
|
DashboardService,
|
|
2986
1982
|
DurableApiController,
|
|
1983
|
+
DurableApiControllerBase,
|
|
2987
1984
|
DurableApiModule,
|
|
2988
|
-
DurableApiSessionGuard,
|
|
2989
|
-
DurableAuthController,
|
|
2990
|
-
DurableDashboardGateGuard,
|
|
2991
1985
|
DurableDashboardModule,
|
|
2992
1986
|
DurableUiController,
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
readDashboardToken,
|
|
2996
|
-
reportDashboardGate,
|
|
2997
|
-
resolveDashboardGate
|
|
1987
|
+
DurableUiControllerBase,
|
|
1988
|
+
UNPROTECTED_CONSOLE_IN_PRODUCTION
|
|
2998
1989
|
};
|
|
2999
1990
|
//# sourceMappingURL=index.js.map
|