@dudousxd/nestjs-durable-dashboard 0.31.0 → 0.32.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/server/index.cjs +790 -37
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.d.cts +166 -6
- package/dist/server/index.d.ts +166 -6
- package/dist/server/index.js +771 -21
- package/dist/server/index.js.map +1 -1
- package/package.json +1 -1
package/dist/server/index.cjs
CHANGED
|
@@ -27,8 +27,11 @@ __export(index_exports, {
|
|
|
27
27
|
DashboardService: () => DashboardService,
|
|
28
28
|
DurableApiController: () => DurableApiController,
|
|
29
29
|
DurableApiModule: () => DurableApiModule,
|
|
30
|
+
DurableApiSessionGuard: () => DurableApiSessionGuard,
|
|
31
|
+
DurableAuthController: () => DurableAuthController,
|
|
30
32
|
DurableDashboardModule: () => DurableDashboardModule,
|
|
31
|
-
DurableUiController: () => DurableUiController
|
|
33
|
+
DurableUiController: () => DurableUiController,
|
|
34
|
+
DurableUiSessionGuard: () => DurableUiSessionGuard
|
|
32
35
|
});
|
|
33
36
|
module.exports = __toCommonJS(index_exports);
|
|
34
37
|
|
|
@@ -554,15 +557,468 @@ DurableApiController = _ts_decorate2([
|
|
|
554
557
|
])
|
|
555
558
|
], DurableApiController);
|
|
556
559
|
|
|
557
|
-
// src/server/durable-
|
|
558
|
-
var
|
|
559
|
-
|
|
560
|
-
|
|
560
|
+
// src/server/durable-auth.controller.ts
|
|
561
|
+
var import_common5 = require("@nestjs/common");
|
|
562
|
+
|
|
563
|
+
// src/server/auth/dashboard-auth-config.ts
|
|
564
|
+
var DASHBOARD_AUTH = Symbol("DASHBOARD_AUTH");
|
|
565
|
+
var DEFAULT_TTL = "8h";
|
|
566
|
+
var DEFAULT_TTL_MS = 8 * 60 * 60 * 1e3;
|
|
567
|
+
var DURATION_UNITS = {
|
|
568
|
+
s: 1e3,
|
|
569
|
+
m: 60 * 1e3,
|
|
570
|
+
h: 60 * 60 * 1e3,
|
|
571
|
+
d: 24 * 60 * 60 * 1e3
|
|
572
|
+
};
|
|
573
|
+
function durationToMs(ttl) {
|
|
574
|
+
const match = /^(\d+)([smhd])$/.exec(ttl.trim());
|
|
575
|
+
if (!match) return DEFAULT_TTL_MS;
|
|
576
|
+
const unit = DURATION_UNITS[match[2] ?? ""];
|
|
577
|
+
if (unit === void 0) return DEFAULT_TTL_MS;
|
|
578
|
+
return Number(match[1]) * unit;
|
|
579
|
+
}
|
|
580
|
+
__name(durationToMs, "durationToMs");
|
|
581
|
+
function resolveDashboardAuth(options) {
|
|
582
|
+
if (options === void 0) return null;
|
|
583
|
+
if (typeof options.secret !== "string" || options.secret === "") {
|
|
584
|
+
throw new Error("DurableDashboardModule: dashboardAuth.secret is required and must be a non-empty string (HMAC-SHA256 signing key, 32+ bytes recommended). Failing closed.");
|
|
585
|
+
}
|
|
586
|
+
if (typeof options.login !== "function") {
|
|
587
|
+
throw new Error("DurableDashboardModule: dashboardAuth.login is required (a login hook).");
|
|
588
|
+
}
|
|
589
|
+
return {
|
|
590
|
+
secret: options.secret,
|
|
591
|
+
ttlMs: durationToMs(options.ttl ?? DEFAULT_TTL),
|
|
592
|
+
login: options.login
|
|
593
|
+
};
|
|
594
|
+
}
|
|
595
|
+
__name(resolveDashboardAuth, "resolveDashboardAuth");
|
|
596
|
+
|
|
597
|
+
// src/server/auth/login-page.ts
|
|
598
|
+
function renderLoginPage(basePath) {
|
|
599
|
+
const loginAction = `${basePath}/login`;
|
|
600
|
+
const defaultReturnTo = basePath;
|
|
601
|
+
return `<!doctype html>
|
|
602
|
+
<html lang="en">
|
|
603
|
+
<head>
|
|
604
|
+
<meta charset="utf-8" />
|
|
605
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
606
|
+
<meta name="robots" content="noindex, nofollow" />
|
|
607
|
+
<title>Sign in \u2014 Durable</title>
|
|
608
|
+
<style>
|
|
609
|
+
:root { color-scheme: light dark; }
|
|
610
|
+
* { box-sizing: border-box; }
|
|
611
|
+
body {
|
|
612
|
+
margin: 0;
|
|
613
|
+
min-height: 100vh;
|
|
614
|
+
display: flex;
|
|
615
|
+
align-items: center;
|
|
616
|
+
justify-content: center;
|
|
617
|
+
font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
|
|
618
|
+
background: #0b0f14;
|
|
619
|
+
color: #e5e7eb;
|
|
620
|
+
}
|
|
621
|
+
main {
|
|
622
|
+
width: 100%;
|
|
623
|
+
max-width: 360px;
|
|
624
|
+
padding: 32px;
|
|
625
|
+
}
|
|
626
|
+
h1 {
|
|
627
|
+
font-size: 18px;
|
|
628
|
+
font-weight: 600;
|
|
629
|
+
margin: 0 0 4px;
|
|
630
|
+
}
|
|
631
|
+
p.subtitle {
|
|
632
|
+
margin: 0 0 24px;
|
|
633
|
+
color: #9ca3af;
|
|
634
|
+
font-size: 13px;
|
|
635
|
+
}
|
|
636
|
+
label {
|
|
637
|
+
display: block;
|
|
638
|
+
font-size: 12px;
|
|
639
|
+
font-weight: 500;
|
|
640
|
+
margin-bottom: 6px;
|
|
641
|
+
color: #cbd5e1;
|
|
642
|
+
}
|
|
643
|
+
input {
|
|
644
|
+
width: 100%;
|
|
645
|
+
padding: 8px 10px;
|
|
646
|
+
margin-bottom: 16px;
|
|
647
|
+
border-radius: 6px;
|
|
648
|
+
border: 1px solid #374151;
|
|
649
|
+
background: #111827;
|
|
650
|
+
color: #e5e7eb;
|
|
651
|
+
font-size: 14px;
|
|
652
|
+
}
|
|
653
|
+
input:focus { outline: 2px solid #3b82f6; outline-offset: 1px; }
|
|
654
|
+
button {
|
|
655
|
+
width: 100%;
|
|
656
|
+
padding: 9px 10px;
|
|
657
|
+
border-radius: 6px;
|
|
658
|
+
border: none;
|
|
659
|
+
background: #3b82f6;
|
|
660
|
+
color: white;
|
|
661
|
+
font-size: 14px;
|
|
662
|
+
font-weight: 600;
|
|
663
|
+
cursor: pointer;
|
|
664
|
+
}
|
|
665
|
+
button:disabled { opacity: 0.6; cursor: default; }
|
|
666
|
+
button:hover:not(:disabled) { background: #2563eb; }
|
|
667
|
+
#error {
|
|
668
|
+
display: none;
|
|
669
|
+
margin-bottom: 16px;
|
|
670
|
+
padding: 8px 10px;
|
|
671
|
+
border-radius: 6px;
|
|
672
|
+
background: #7f1d1d;
|
|
673
|
+
color: #fecaca;
|
|
674
|
+
font-size: 13px;
|
|
675
|
+
}
|
|
676
|
+
</style>
|
|
677
|
+
</head>
|
|
678
|
+
<body>
|
|
679
|
+
<main>
|
|
680
|
+
<h1>Durable control plane</h1>
|
|
681
|
+
<p class="subtitle">Sign in to continue.</p>
|
|
682
|
+
<div id="error" role="alert">Invalid username or password.</div>
|
|
683
|
+
<form id="login-form" autocomplete="on">
|
|
684
|
+
<label for="username">Username</label>
|
|
685
|
+
<input id="username" name="username" type="text" autocomplete="username" required autofocus />
|
|
686
|
+
<label for="password">Password</label>
|
|
687
|
+
<input id="password" name="password" type="password" autocomplete="current-password" required />
|
|
688
|
+
<button id="submit" type="submit">Sign in</button>
|
|
689
|
+
</form>
|
|
690
|
+
</main>
|
|
691
|
+
<script>
|
|
692
|
+
(function () {
|
|
693
|
+
var LOGIN_ACTION = ${JSON.stringify(loginAction)};
|
|
694
|
+
var DEFAULT_RETURN_TO = ${JSON.stringify(defaultReturnTo)};
|
|
695
|
+
var params = new URLSearchParams(window.location.search);
|
|
696
|
+
var errorBox = document.getElementById('error');
|
|
697
|
+
if (params.get('error')) errorBox.style.display = 'block';
|
|
698
|
+
|
|
699
|
+
function sameOriginReturnTo(value) {
|
|
700
|
+
if (typeof value !== 'string' || value === '') return DEFAULT_RETURN_TO;
|
|
701
|
+
if (value.charAt(0) !== '/' || value.charAt(1) === '/' || value.indexOf('://') !== -1) {
|
|
702
|
+
return DEFAULT_RETURN_TO;
|
|
703
|
+
}
|
|
704
|
+
return value;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
var form = document.getElementById('login-form');
|
|
708
|
+
var submitButton = document.getElementById('submit');
|
|
709
|
+
form.addEventListener('submit', function (event) {
|
|
710
|
+
event.preventDefault();
|
|
711
|
+
submitButton.disabled = true;
|
|
712
|
+
errorBox.style.display = 'none';
|
|
713
|
+
var returnTo = sameOriginReturnTo(params.get('returnTo'));
|
|
714
|
+
fetch(LOGIN_ACTION, {
|
|
715
|
+
method: 'POST',
|
|
716
|
+
headers: { 'content-type': 'application/json' },
|
|
717
|
+
credentials: 'same-origin',
|
|
718
|
+
body: JSON.stringify({
|
|
719
|
+
username: document.getElementById('username').value,
|
|
720
|
+
password: document.getElementById('password').value,
|
|
721
|
+
returnTo: returnTo,
|
|
722
|
+
}),
|
|
723
|
+
})
|
|
724
|
+
.then(function (response) {
|
|
725
|
+
if (!response.ok) throw new Error('unauthorized');
|
|
726
|
+
return response.json();
|
|
727
|
+
})
|
|
728
|
+
.then(function (data) {
|
|
729
|
+
window.location.href = data.redirectTo || DEFAULT_RETURN_TO;
|
|
730
|
+
})
|
|
731
|
+
.catch(function () {
|
|
732
|
+
errorBox.style.display = 'block';
|
|
733
|
+
submitButton.disabled = false;
|
|
734
|
+
});
|
|
735
|
+
});
|
|
736
|
+
})();
|
|
737
|
+
</script>
|
|
738
|
+
</body>
|
|
739
|
+
</html>`;
|
|
740
|
+
}
|
|
741
|
+
__name(renderLoginPage, "renderLoginPage");
|
|
742
|
+
|
|
743
|
+
// src/server/auth/request.ts
|
|
744
|
+
function isRecord(value) {
|
|
745
|
+
return typeof value === "object" && value !== null;
|
|
746
|
+
}
|
|
747
|
+
__name(isRecord, "isRecord");
|
|
748
|
+
function readHeaders(request) {
|
|
749
|
+
if (!isRecord(request)) return {};
|
|
750
|
+
const headers = request.headers;
|
|
751
|
+
return isRecord(headers) ? headers : {};
|
|
752
|
+
}
|
|
753
|
+
__name(readHeaders, "readHeaders");
|
|
754
|
+
function headerToString(value) {
|
|
755
|
+
if (typeof value === "string") return value;
|
|
756
|
+
if (Array.isArray(value) && value.every((part) => typeof part === "string")) {
|
|
757
|
+
return value.join(",");
|
|
758
|
+
}
|
|
759
|
+
return void 0;
|
|
760
|
+
}
|
|
761
|
+
__name(headerToString, "headerToString");
|
|
762
|
+
function readCookieHeader(request) {
|
|
763
|
+
return headerToString(readHeaders(request).cookie);
|
|
764
|
+
}
|
|
765
|
+
__name(readCookieHeader, "readCookieHeader");
|
|
766
|
+
function isHttpsRequest(request) {
|
|
767
|
+
if (isRecord(request) && request.protocol === "https") return true;
|
|
768
|
+
const forwarded = headerToString(readHeaders(request)["x-forwarded-proto"]);
|
|
769
|
+
if (forwarded === void 0) return false;
|
|
770
|
+
return forwarded.split(",").map((proto) => proto.trim().toLowerCase()).includes("https");
|
|
771
|
+
}
|
|
772
|
+
__name(isHttpsRequest, "isHttpsRequest");
|
|
773
|
+
function originalRequestUrl(request) {
|
|
774
|
+
if (!isRecord(request)) return "/";
|
|
775
|
+
if (typeof request.originalUrl === "string" && request.originalUrl !== "") {
|
|
776
|
+
return request.originalUrl;
|
|
777
|
+
}
|
|
778
|
+
if (typeof request.url === "string" && request.url !== "") return request.url;
|
|
779
|
+
return "/";
|
|
780
|
+
}
|
|
781
|
+
__name(originalRequestUrl, "originalRequestUrl");
|
|
782
|
+
function sanitizeReturnTo(candidate, fallback) {
|
|
783
|
+
if (typeof candidate !== "string") return fallback;
|
|
784
|
+
if (!candidate.startsWith("/")) return fallback;
|
|
785
|
+
if (candidate.startsWith("//")) return fallback;
|
|
786
|
+
if (candidate.includes("://")) return fallback;
|
|
787
|
+
return candidate;
|
|
788
|
+
}
|
|
789
|
+
__name(sanitizeReturnTo, "sanitizeReturnTo");
|
|
790
|
+
|
|
791
|
+
// src/server/auth/response.ts
|
|
792
|
+
function isRecord2(value) {
|
|
793
|
+
return typeof value === "object" && value !== null;
|
|
794
|
+
}
|
|
795
|
+
__name(isRecord2, "isRecord");
|
|
796
|
+
function isRawCookieResponse(value) {
|
|
797
|
+
return isRecord2(value) && typeof value.getHeader === "function" && typeof value.setHeader === "function";
|
|
798
|
+
}
|
|
799
|
+
__name(isRawCookieResponse, "isRawCookieResponse");
|
|
800
|
+
function resolveCookieResponse(response) {
|
|
801
|
+
if (isRawCookieResponse(response)) return response;
|
|
802
|
+
if (isRecord2(response) && isRawCookieResponse(response.raw)) return response.raw;
|
|
803
|
+
return null;
|
|
804
|
+
}
|
|
805
|
+
__name(resolveCookieResponse, "resolveCookieResponse");
|
|
806
|
+
function existingSetCookies(response) {
|
|
807
|
+
const current = response.getHeader("set-cookie");
|
|
808
|
+
if (Array.isArray(current)) return current;
|
|
809
|
+
if (typeof current === "string") return [
|
|
810
|
+
current
|
|
811
|
+
];
|
|
812
|
+
return [];
|
|
813
|
+
}
|
|
814
|
+
__name(existingSetCookies, "existingSetCookies");
|
|
815
|
+
function appendSetCookie(response, cookie) {
|
|
816
|
+
const raw = resolveCookieResponse(response);
|
|
817
|
+
if (!raw) return;
|
|
818
|
+
raw.setHeader("set-cookie", [
|
|
819
|
+
...existingSetCookies(raw),
|
|
820
|
+
cookie
|
|
821
|
+
]);
|
|
822
|
+
}
|
|
823
|
+
__name(appendSetCookie, "appendSetCookie");
|
|
824
|
+
function isRawRedirectResponse(value) {
|
|
825
|
+
return isRecord2(value) && typeof value.getHeader === "function" && typeof value.setHeader === "function" && typeof value.end === "function";
|
|
826
|
+
}
|
|
827
|
+
__name(isRawRedirectResponse, "isRawRedirectResponse");
|
|
828
|
+
function resolveRedirectResponse(response) {
|
|
829
|
+
if (isRawRedirectResponse(response)) return response;
|
|
830
|
+
if (isRecord2(response) && isRawRedirectResponse(response.raw)) return response.raw;
|
|
831
|
+
return null;
|
|
832
|
+
}
|
|
833
|
+
__name(resolveRedirectResponse, "resolveRedirectResponse");
|
|
834
|
+
function redirectRaw(response, location, status = 302) {
|
|
835
|
+
const raw = resolveRedirectResponse(response);
|
|
836
|
+
if (!raw) return;
|
|
837
|
+
raw.statusCode = status;
|
|
838
|
+
raw.setHeader("location", location);
|
|
839
|
+
raw.end();
|
|
840
|
+
}
|
|
841
|
+
__name(redirectRaw, "redirectRaw");
|
|
842
|
+
|
|
843
|
+
// src/server/auth/cookie-header.ts
|
|
844
|
+
function parseCookieHeader(header) {
|
|
845
|
+
const cookies = {};
|
|
846
|
+
if (typeof header !== "string" || header === "") return cookies;
|
|
847
|
+
for (const segment of header.split(";")) {
|
|
848
|
+
const eq = segment.indexOf("=");
|
|
849
|
+
if (eq <= 0) continue;
|
|
850
|
+
const name = segment.slice(0, eq).trim();
|
|
851
|
+
if (name === "") continue;
|
|
852
|
+
let rawValue = segment.slice(eq + 1).trim();
|
|
853
|
+
if (rawValue.length >= 2 && rawValue.startsWith('"') && rawValue.endsWith('"')) {
|
|
854
|
+
rawValue = rawValue.slice(1, -1);
|
|
855
|
+
}
|
|
856
|
+
if (name in cookies) continue;
|
|
857
|
+
try {
|
|
858
|
+
cookies[name] = decodeURIComponent(rawValue);
|
|
859
|
+
} catch {
|
|
860
|
+
cookies[name] = rawValue;
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
return cookies;
|
|
864
|
+
}
|
|
865
|
+
__name(parseCookieHeader, "parseCookieHeader");
|
|
866
|
+
function serializeSetCookie(name, value, options) {
|
|
867
|
+
const parts = [
|
|
868
|
+
`${name}=${options.clear ? "" : encodeURIComponent(value)}`
|
|
869
|
+
];
|
|
870
|
+
parts.push(`Path=${options.path}`);
|
|
871
|
+
parts.push("HttpOnly");
|
|
872
|
+
parts.push("SameSite=Lax");
|
|
873
|
+
if (options.secure) parts.push("Secure");
|
|
874
|
+
if (options.clear) {
|
|
875
|
+
parts.push("Max-Age=0");
|
|
876
|
+
parts.push("Expires=Thu, 01 Jan 1970 00:00:00 GMT");
|
|
877
|
+
} else {
|
|
878
|
+
parts.push(`Max-Age=${options.maxAgeSeconds}`);
|
|
879
|
+
}
|
|
880
|
+
return parts.join("; ");
|
|
881
|
+
}
|
|
882
|
+
__name(serializeSetCookie, "serializeSetCookie");
|
|
883
|
+
|
|
884
|
+
// src/server/auth/session-cookie.ts
|
|
885
|
+
var import_node_crypto = require("crypto");
|
|
886
|
+
var EXP_GRACE_MS = 3e4;
|
|
887
|
+
function base64urlEncode(value) {
|
|
888
|
+
return Buffer.from(value, "utf8").toString("base64url");
|
|
889
|
+
}
|
|
890
|
+
__name(base64urlEncode, "base64urlEncode");
|
|
891
|
+
function base64urlDecode(value) {
|
|
892
|
+
return Buffer.from(value, "base64url").toString("utf8");
|
|
893
|
+
}
|
|
894
|
+
__name(base64urlDecode, "base64urlDecode");
|
|
895
|
+
function hmac(secret, payload) {
|
|
896
|
+
return (0, import_node_crypto.createHmac)("sha256", secret).update(payload).digest("base64url");
|
|
897
|
+
}
|
|
898
|
+
__name(hmac, "hmac");
|
|
899
|
+
function signSessionCookie(user, options) {
|
|
900
|
+
const issuedAt = options.now ?? Date.now();
|
|
901
|
+
const session = {
|
|
902
|
+
sub: user.id,
|
|
903
|
+
...user.name !== void 0 ? {
|
|
904
|
+
name: user.name
|
|
905
|
+
} : {},
|
|
906
|
+
roles: user.roles ?? [],
|
|
907
|
+
iat: issuedAt,
|
|
908
|
+
exp: issuedAt + options.ttlMs
|
|
909
|
+
};
|
|
910
|
+
const encodedPayload = base64urlEncode(JSON.stringify(session));
|
|
911
|
+
const signature = hmac(options.secret, encodedPayload);
|
|
912
|
+
return `${encodedPayload}.${signature}`;
|
|
913
|
+
}
|
|
914
|
+
__name(signSessionCookie, "signSessionCookie");
|
|
915
|
+
function isSessionPayload(value) {
|
|
916
|
+
if (typeof value !== "object" || value === null) return false;
|
|
917
|
+
const record = {
|
|
918
|
+
...value
|
|
919
|
+
};
|
|
920
|
+
if (typeof record.sub !== "string") return false;
|
|
921
|
+
if (record.name !== void 0 && typeof record.name !== "string") return false;
|
|
922
|
+
if (!Array.isArray(record.roles)) return false;
|
|
923
|
+
if (!record.roles.every((role) => typeof role === "string")) return false;
|
|
924
|
+
if (typeof record.iat !== "number" || !Number.isFinite(record.iat)) return false;
|
|
925
|
+
if (typeof record.exp !== "number" || !Number.isFinite(record.exp)) return false;
|
|
926
|
+
return true;
|
|
927
|
+
}
|
|
928
|
+
__name(isSessionPayload, "isSessionPayload");
|
|
929
|
+
function verifySessionCookie(value, options) {
|
|
930
|
+
try {
|
|
931
|
+
const dot = value.indexOf(".");
|
|
932
|
+
if (dot <= 0 || dot === value.length - 1) return null;
|
|
933
|
+
const encodedPayload = value.slice(0, dot);
|
|
934
|
+
const providedSignature = value.slice(dot + 1);
|
|
935
|
+
const expectedSignature = hmac(options.secret, encodedPayload);
|
|
936
|
+
const provided = Buffer.from(providedSignature, "base64url");
|
|
937
|
+
const expected = Buffer.from(expectedSignature, "base64url");
|
|
938
|
+
if (provided.length !== expected.length) return null;
|
|
939
|
+
if (!(0, import_node_crypto.timingSafeEqual)(provided, expected)) return null;
|
|
940
|
+
const decoded = JSON.parse(base64urlDecode(encodedPayload));
|
|
941
|
+
if (!isSessionPayload(decoded)) return null;
|
|
942
|
+
const now = options.now ?? Date.now();
|
|
943
|
+
if (now > decoded.exp + EXP_GRACE_MS) return null;
|
|
944
|
+
const session = {
|
|
945
|
+
sub: decoded.sub,
|
|
946
|
+
...decoded.name !== void 0 ? {
|
|
947
|
+
name: decoded.name
|
|
948
|
+
} : {},
|
|
949
|
+
roles: decoded.roles,
|
|
950
|
+
iat: decoded.iat,
|
|
951
|
+
exp: decoded.exp
|
|
952
|
+
};
|
|
953
|
+
return session;
|
|
954
|
+
} catch {
|
|
955
|
+
return null;
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
__name(verifySessionCookie, "verifySessionCookie");
|
|
959
|
+
|
|
960
|
+
// src/server/auth/session-cookie-io.ts
|
|
961
|
+
var SESSION_COOKIE_NAME = "durable_dashboard_session";
|
|
962
|
+
var COOKIE_PATH = "/";
|
|
963
|
+
function issueSessionCookie(user, context) {
|
|
964
|
+
const value = signSessionCookie(user, {
|
|
965
|
+
secret: context.auth.secret,
|
|
966
|
+
ttlMs: context.auth.ttlMs,
|
|
967
|
+
...context.now !== void 0 ? {
|
|
968
|
+
now: context.now
|
|
969
|
+
} : {}
|
|
970
|
+
});
|
|
971
|
+
const cookie = serializeSetCookie(SESSION_COOKIE_NAME, value, {
|
|
972
|
+
path: COOKIE_PATH,
|
|
973
|
+
maxAgeSeconds: Math.floor(context.auth.ttlMs / 1e3),
|
|
974
|
+
secure: isHttpsRequest(context.request)
|
|
975
|
+
});
|
|
976
|
+
appendSetCookie(context.response, cookie);
|
|
977
|
+
}
|
|
978
|
+
__name(issueSessionCookie, "issueSessionCookie");
|
|
979
|
+
function clearSessionCookie(context) {
|
|
980
|
+
const cookie = serializeSetCookie(SESSION_COOKIE_NAME, "", {
|
|
981
|
+
path: COOKIE_PATH,
|
|
982
|
+
maxAgeSeconds: 0,
|
|
983
|
+
secure: isHttpsRequest(context.request),
|
|
984
|
+
clear: true
|
|
985
|
+
});
|
|
986
|
+
appendSetCookie(context.response, cookie);
|
|
987
|
+
}
|
|
988
|
+
__name(clearSessionCookie, "clearSessionCookie");
|
|
989
|
+
function readSessionFromRequest(auth, request) {
|
|
990
|
+
const cookieValue = parseCookieHeader(readCookieHeader(request))[SESSION_COOKIE_NAME];
|
|
991
|
+
if (cookieValue === void 0) return null;
|
|
992
|
+
return verifySessionCookie(cookieValue, {
|
|
993
|
+
secret: auth.secret
|
|
994
|
+
});
|
|
995
|
+
}
|
|
996
|
+
__name(readSessionFromRequest, "readSessionFromRequest");
|
|
997
|
+
function maybeRenewSession(auth, session, request, response) {
|
|
998
|
+
const now = Date.now();
|
|
999
|
+
if (now - session.iat <= auth.ttlMs / 2) return;
|
|
1000
|
+
issueSessionCookie({
|
|
1001
|
+
id: session.sub,
|
|
1002
|
+
...session.name !== void 0 ? {
|
|
1003
|
+
name: session.name
|
|
1004
|
+
} : {},
|
|
1005
|
+
roles: session.roles
|
|
1006
|
+
}, {
|
|
1007
|
+
auth,
|
|
1008
|
+
request,
|
|
1009
|
+
response,
|
|
1010
|
+
now
|
|
1011
|
+
});
|
|
1012
|
+
}
|
|
1013
|
+
__name(maybeRenewSession, "maybeRenewSession");
|
|
561
1014
|
|
|
562
1015
|
// src/server/durable-ui.controller.ts
|
|
563
1016
|
var import_node_fs = require("fs");
|
|
564
1017
|
var import_node_path = require("path");
|
|
565
1018
|
var import_node_url = require("url");
|
|
1019
|
+
var import_common4 = require("@nestjs/common");
|
|
1020
|
+
|
|
1021
|
+
// src/server/auth/login-redirect.exception.ts
|
|
566
1022
|
var import_common3 = require("@nestjs/common");
|
|
567
1023
|
function _ts_decorate3(decorators, target, key, desc) {
|
|
568
1024
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
@@ -571,6 +1027,38 @@ function _ts_decorate3(decorators, target, key, desc) {
|
|
|
571
1027
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
572
1028
|
}
|
|
573
1029
|
__name(_ts_decorate3, "_ts_decorate");
|
|
1030
|
+
var DashboardLoginRedirectException = class extends import_common3.HttpException {
|
|
1031
|
+
static {
|
|
1032
|
+
__name(this, "DashboardLoginRedirectException");
|
|
1033
|
+
}
|
|
1034
|
+
location;
|
|
1035
|
+
constructor(location) {
|
|
1036
|
+
super({
|
|
1037
|
+
location
|
|
1038
|
+
}, import_common3.HttpStatus.FOUND), this.location = location;
|
|
1039
|
+
}
|
|
1040
|
+
};
|
|
1041
|
+
var DashboardLoginRedirectFilter = class {
|
|
1042
|
+
static {
|
|
1043
|
+
__name(this, "DashboardLoginRedirectFilter");
|
|
1044
|
+
}
|
|
1045
|
+
catch(exception, host) {
|
|
1046
|
+
const response = host.switchToHttp().getResponse();
|
|
1047
|
+
redirectRaw(response, exception.location);
|
|
1048
|
+
}
|
|
1049
|
+
};
|
|
1050
|
+
DashboardLoginRedirectFilter = _ts_decorate3([
|
|
1051
|
+
(0, import_common3.Catch)(DashboardLoginRedirectException)
|
|
1052
|
+
], DashboardLoginRedirectFilter);
|
|
1053
|
+
|
|
1054
|
+
// src/server/durable-ui.controller.ts
|
|
1055
|
+
function _ts_decorate4(decorators, target, key, desc) {
|
|
1056
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1057
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
1058
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
1059
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1060
|
+
}
|
|
1061
|
+
__name(_ts_decorate4, "_ts_decorate");
|
|
574
1062
|
function _ts_metadata3(k, v) {
|
|
575
1063
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
576
1064
|
}
|
|
@@ -613,7 +1101,7 @@ var DurableUiController = class {
|
|
|
613
1101
|
index() {
|
|
614
1102
|
const indexPath = (0, import_node_path.join)(this.dir, "index.html");
|
|
615
1103
|
if (!(0, import_node_fs.existsSync)(indexPath)) {
|
|
616
|
-
throw new
|
|
1104
|
+
throw new import_common4.NotFoundException("Dashboard is not built. Run the package build.");
|
|
617
1105
|
}
|
|
618
1106
|
const html = (0, import_node_fs.readFileSync)(indexPath, "utf8").replaceAll(`="${BUILD_BASE}/`, `="${this.basePath}/`);
|
|
619
1107
|
const inject = `<script>window.__DURABLE_BASE__='${this.basePath}';window.__DURABLE_API__='${this.apiBasePath}';</script>`;
|
|
@@ -621,40 +1109,41 @@ var DurableUiController = class {
|
|
|
621
1109
|
}
|
|
622
1110
|
asset(file) {
|
|
623
1111
|
const safe = (0, import_node_path.basename)(file);
|
|
624
|
-
if (safe !== file) throw new
|
|
1112
|
+
if (safe !== file) throw new import_common4.NotFoundException();
|
|
625
1113
|
const root = (0, import_node_path.resolve)(this.dir, "assets");
|
|
626
1114
|
const assetPath = (0, import_node_path.resolve)(root, safe);
|
|
627
1115
|
if (!assetPath.startsWith(root + import_node_path.sep) || !(0, import_node_fs.existsSync)(assetPath)) {
|
|
628
|
-
throw new
|
|
1116
|
+
throw new import_common4.NotFoundException();
|
|
629
1117
|
}
|
|
630
1118
|
const type = CONTENT_TYPES[(0, import_node_path.extname)(safe)] ?? "application/octet-stream";
|
|
631
|
-
return new
|
|
1119
|
+
return new import_common4.StreamableFile((0, import_node_fs.readFileSync)(assetPath), {
|
|
632
1120
|
type
|
|
633
1121
|
});
|
|
634
1122
|
}
|
|
635
1123
|
};
|
|
636
|
-
|
|
637
|
-
(0,
|
|
638
|
-
(0,
|
|
639
|
-
(0,
|
|
1124
|
+
_ts_decorate4([
|
|
1125
|
+
(0, import_common4.Get)(),
|
|
1126
|
+
(0, import_common4.Header)("Content-Type", "text/html; charset=utf-8"),
|
|
1127
|
+
(0, import_common4.Header)("Cache-Control", "no-store, must-revalidate"),
|
|
640
1128
|
_ts_metadata3("design:type", Function),
|
|
641
1129
|
_ts_metadata3("design:paramtypes", []),
|
|
642
1130
|
_ts_metadata3("design:returntype", String)
|
|
643
1131
|
], DurableUiController.prototype, "index", null);
|
|
644
|
-
|
|
645
|
-
(0,
|
|
646
|
-
(0,
|
|
647
|
-
_ts_param3(0, (0,
|
|
1132
|
+
_ts_decorate4([
|
|
1133
|
+
(0, import_common4.Get)("assets/:file"),
|
|
1134
|
+
(0, import_common4.Header)("Cache-Control", "public, max-age=31536000, immutable"),
|
|
1135
|
+
_ts_param3(0, (0, import_common4.Param)("file")),
|
|
648
1136
|
_ts_metadata3("design:type", Function),
|
|
649
1137
|
_ts_metadata3("design:paramtypes", [
|
|
650
1138
|
String
|
|
651
1139
|
]),
|
|
652
|
-
_ts_metadata3("design:returntype", typeof
|
|
1140
|
+
_ts_metadata3("design:returntype", typeof import_common4.StreamableFile === "undefined" ? Object : import_common4.StreamableFile)
|
|
653
1141
|
], DurableUiController.prototype, "asset", null);
|
|
654
|
-
DurableUiController =
|
|
655
|
-
(0,
|
|
656
|
-
|
|
657
|
-
_ts_param3(
|
|
1142
|
+
DurableUiController = _ts_decorate4([
|
|
1143
|
+
(0, import_common4.UseFilters)(DashboardLoginRedirectFilter),
|
|
1144
|
+
(0, import_common4.Controller)(),
|
|
1145
|
+
_ts_param3(0, (0, import_common4.Inject)(DASHBOARD_BASE_PATH)),
|
|
1146
|
+
_ts_param3(1, (0, import_common4.Inject)(DASHBOARD_API_PATH)),
|
|
658
1147
|
_ts_metadata3("design:type", Function),
|
|
659
1148
|
_ts_metadata3("design:paramtypes", [
|
|
660
1149
|
String,
|
|
@@ -662,23 +1151,254 @@ DurableUiController = _ts_decorate3([
|
|
|
662
1151
|
])
|
|
663
1152
|
], DurableUiController);
|
|
664
1153
|
|
|
1154
|
+
// src/server/durable-auth.controller.ts
|
|
1155
|
+
function _ts_decorate5(decorators, target, key, desc) {
|
|
1156
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1157
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
1158
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
1159
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1160
|
+
}
|
|
1161
|
+
__name(_ts_decorate5, "_ts_decorate");
|
|
1162
|
+
function _ts_metadata4(k, v) {
|
|
1163
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
1164
|
+
}
|
|
1165
|
+
__name(_ts_metadata4, "_ts_metadata");
|
|
1166
|
+
function _ts_param4(paramIndex, decorator) {
|
|
1167
|
+
return function(target, key) {
|
|
1168
|
+
decorator(target, key, paramIndex);
|
|
1169
|
+
};
|
|
1170
|
+
}
|
|
1171
|
+
__name(_ts_param4, "_ts_param");
|
|
1172
|
+
function isString(value) {
|
|
1173
|
+
return typeof value === "string";
|
|
1174
|
+
}
|
|
1175
|
+
__name(isString, "isString");
|
|
1176
|
+
var DurableAuthController = class _DurableAuthController {
|
|
1177
|
+
static {
|
|
1178
|
+
__name(this, "DurableAuthController");
|
|
1179
|
+
}
|
|
1180
|
+
auth;
|
|
1181
|
+
basePath;
|
|
1182
|
+
logger = new import_common5.Logger(_DurableAuthController.name);
|
|
1183
|
+
/** Warn once so a throwing `login` hook doesn't spam logs on every failed attempt. */
|
|
1184
|
+
warnedOnHookThrow = false;
|
|
1185
|
+
constructor(auth, basePath) {
|
|
1186
|
+
this.auth = auth;
|
|
1187
|
+
this.basePath = basePath;
|
|
1188
|
+
}
|
|
1189
|
+
// The login page reads `returnTo`/`error` from `location.search` client-side (see
|
|
1190
|
+
// `login-page.ts`), so this response never varies per request and needs no templating beyond
|
|
1191
|
+
// the developer-controlled `basePath`.
|
|
1192
|
+
loginPage() {
|
|
1193
|
+
if (!this.auth) throw new import_common5.NotFoundException();
|
|
1194
|
+
return renderLoginPage(this.basePath);
|
|
1195
|
+
}
|
|
1196
|
+
// Called by the login page's own fetch (JSON, not a classic form POST — see `login-page.ts` for
|
|
1197
|
+
// why: it keeps the login page working with no host-configured body parser beyond the
|
|
1198
|
+
// JSON one Nest wires in by default on both Express and Fastify). Uniform `401` on any failure
|
|
1199
|
+
// (unknown user, wrong password, throwing hook) — no user-enumeration.
|
|
1200
|
+
async login(body, request, response) {
|
|
1201
|
+
const auth = this.requireAuth();
|
|
1202
|
+
if (!isString(body?.username) || !isString(body?.password)) {
|
|
1203
|
+
throw new import_common5.BadRequestException("Body must include string `username` and `password`.");
|
|
1204
|
+
}
|
|
1205
|
+
const user = await this.runLoginHook(auth, body.username, body.password);
|
|
1206
|
+
if (!user) throw new import_common5.UnauthorizedException({
|
|
1207
|
+
message: "Invalid username or password."
|
|
1208
|
+
});
|
|
1209
|
+
issueSessionCookie(user, {
|
|
1210
|
+
auth,
|
|
1211
|
+
request,
|
|
1212
|
+
response
|
|
1213
|
+
});
|
|
1214
|
+
return {
|
|
1215
|
+
redirectTo: sanitizeReturnTo(body.returnTo, this.basePath)
|
|
1216
|
+
};
|
|
1217
|
+
}
|
|
1218
|
+
// Plain `GET` (not `POST`): logging out only ever destroys the CALLER's own session, so it's
|
|
1219
|
+
// safe/idempotent from the caller's perspective — a simple `<a href>` (no JS, no CSRF token)
|
|
1220
|
+
// works. Non-passthrough `@Res()`: this handler owns the response outright (clears the cookie,
|
|
1221
|
+
// then redirects), so Nest must not also try to send a return value afterwards.
|
|
1222
|
+
logout(request, response) {
|
|
1223
|
+
clearSessionCookie({
|
|
1224
|
+
request,
|
|
1225
|
+
response
|
|
1226
|
+
});
|
|
1227
|
+
redirectRaw(response, `${this.basePath}/login`);
|
|
1228
|
+
}
|
|
1229
|
+
requireAuth() {
|
|
1230
|
+
if (!this.auth) throw new import_common5.NotFoundException();
|
|
1231
|
+
return this.auth;
|
|
1232
|
+
}
|
|
1233
|
+
/**
|
|
1234
|
+
* Run the host's `login` hook defensively: a throw is treated as a denial (uniform failure) and
|
|
1235
|
+
* warn-logged once, so a buggy hook never 500s the endpoint into a stack-trace leak nor floods
|
|
1236
|
+
* the logs on repeated bad attempts.
|
|
1237
|
+
*/
|
|
1238
|
+
async runLoginHook(auth, username, password) {
|
|
1239
|
+
try {
|
|
1240
|
+
return await auth.login(username, password) ?? null;
|
|
1241
|
+
} catch (error) {
|
|
1242
|
+
if (!this.warnedOnHookThrow) {
|
|
1243
|
+
this.warnedOnHookThrow = true;
|
|
1244
|
+
this.logger.warn(`dashboardAuth login hook threw; treating as denial. ${error instanceof Error ? error.message : String(error)}`);
|
|
1245
|
+
}
|
|
1246
|
+
return null;
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
};
|
|
1250
|
+
_ts_decorate5([
|
|
1251
|
+
(0, import_common5.Get)("login"),
|
|
1252
|
+
(0, import_common5.Header)("Content-Type", "text/html; charset=utf-8"),
|
|
1253
|
+
(0, import_common5.Header)("Cache-Control", "no-store, must-revalidate"),
|
|
1254
|
+
_ts_metadata4("design:type", Function),
|
|
1255
|
+
_ts_metadata4("design:paramtypes", []),
|
|
1256
|
+
_ts_metadata4("design:returntype", String)
|
|
1257
|
+
], DurableAuthController.prototype, "loginPage", null);
|
|
1258
|
+
_ts_decorate5([
|
|
1259
|
+
(0, import_common5.Post)("login"),
|
|
1260
|
+
(0, import_common5.HttpCode)(200),
|
|
1261
|
+
_ts_param4(0, (0, import_common5.Body)()),
|
|
1262
|
+
_ts_param4(1, (0, import_common5.Req)()),
|
|
1263
|
+
_ts_param4(2, (0, import_common5.Res)({
|
|
1264
|
+
passthrough: true
|
|
1265
|
+
})),
|
|
1266
|
+
_ts_metadata4("design:type", Function),
|
|
1267
|
+
_ts_metadata4("design:paramtypes", [
|
|
1268
|
+
typeof LoginBody === "undefined" ? Object : LoginBody,
|
|
1269
|
+
Object,
|
|
1270
|
+
Object
|
|
1271
|
+
]),
|
|
1272
|
+
_ts_metadata4("design:returntype", Promise)
|
|
1273
|
+
], DurableAuthController.prototype, "login", null);
|
|
1274
|
+
_ts_decorate5([
|
|
1275
|
+
(0, import_common5.Get)("logout"),
|
|
1276
|
+
_ts_param4(0, (0, import_common5.Req)()),
|
|
1277
|
+
_ts_param4(1, (0, import_common5.Res)()),
|
|
1278
|
+
_ts_metadata4("design:type", Function),
|
|
1279
|
+
_ts_metadata4("design:paramtypes", [
|
|
1280
|
+
Object,
|
|
1281
|
+
Object
|
|
1282
|
+
]),
|
|
1283
|
+
_ts_metadata4("design:returntype", void 0)
|
|
1284
|
+
], DurableAuthController.prototype, "logout", null);
|
|
1285
|
+
DurableAuthController = _ts_decorate5([
|
|
1286
|
+
(0, import_common5.Controller)(),
|
|
1287
|
+
_ts_param4(0, (0, import_common5.Optional)()),
|
|
1288
|
+
_ts_param4(0, (0, import_common5.Inject)(DASHBOARD_AUTH)),
|
|
1289
|
+
_ts_param4(1, (0, import_common5.Inject)(DASHBOARD_BASE_PATH)),
|
|
1290
|
+
_ts_metadata4("design:type", Function),
|
|
1291
|
+
_ts_metadata4("design:paramtypes", [
|
|
1292
|
+
Object,
|
|
1293
|
+
String
|
|
1294
|
+
])
|
|
1295
|
+
], DurableAuthController);
|
|
1296
|
+
|
|
665
1297
|
// src/server/durable-dashboard.module.ts
|
|
666
|
-
|
|
1298
|
+
var import_reflect_metadata = require("reflect-metadata");
|
|
1299
|
+
var import_common7 = require("@nestjs/common");
|
|
1300
|
+
var import_core = require("@nestjs/core");
|
|
1301
|
+
|
|
1302
|
+
// src/server/durable-session.guard.ts
|
|
1303
|
+
var import_common6 = require("@nestjs/common");
|
|
1304
|
+
function _ts_decorate6(decorators, target, key, desc) {
|
|
667
1305
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
668
1306
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
669
1307
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
670
1308
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
671
1309
|
}
|
|
672
|
-
__name(
|
|
1310
|
+
__name(_ts_decorate6, "_ts_decorate");
|
|
1311
|
+
function _ts_metadata5(k, v) {
|
|
1312
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
1313
|
+
}
|
|
1314
|
+
__name(_ts_metadata5, "_ts_metadata");
|
|
1315
|
+
function _ts_param5(paramIndex, decorator) {
|
|
1316
|
+
return function(target, key) {
|
|
1317
|
+
decorator(target, key, paramIndex);
|
|
1318
|
+
};
|
|
1319
|
+
}
|
|
1320
|
+
__name(_ts_param5, "_ts_param");
|
|
1321
|
+
var DurableUiSessionGuard = class {
|
|
1322
|
+
static {
|
|
1323
|
+
__name(this, "DurableUiSessionGuard");
|
|
1324
|
+
}
|
|
1325
|
+
auth;
|
|
1326
|
+
basePath;
|
|
1327
|
+
constructor(auth, basePath) {
|
|
1328
|
+
this.auth = auth;
|
|
1329
|
+
this.basePath = basePath;
|
|
1330
|
+
}
|
|
1331
|
+
canActivate(context) {
|
|
1332
|
+
if (!this.auth) return true;
|
|
1333
|
+
const http = context.switchToHttp();
|
|
1334
|
+
const request = http.getRequest();
|
|
1335
|
+
const session = readSessionFromRequest(this.auth, request);
|
|
1336
|
+
if (!session) {
|
|
1337
|
+
const returnTo = encodeURIComponent(originalRequestUrl(request));
|
|
1338
|
+
throw new DashboardLoginRedirectException(`${this.basePath}/login?returnTo=${returnTo}`);
|
|
1339
|
+
}
|
|
1340
|
+
maybeRenewSession(this.auth, session, request, http.getResponse());
|
|
1341
|
+
return true;
|
|
1342
|
+
}
|
|
1343
|
+
};
|
|
1344
|
+
DurableUiSessionGuard = _ts_decorate6([
|
|
1345
|
+
(0, import_common6.Injectable)(),
|
|
1346
|
+
_ts_param5(0, (0, import_common6.Inject)(DASHBOARD_AUTH)),
|
|
1347
|
+
_ts_param5(1, (0, import_common6.Inject)(DASHBOARD_BASE_PATH)),
|
|
1348
|
+
_ts_metadata5("design:type", Function),
|
|
1349
|
+
_ts_metadata5("design:paramtypes", [
|
|
1350
|
+
Object,
|
|
1351
|
+
String
|
|
1352
|
+
])
|
|
1353
|
+
], DurableUiSessionGuard);
|
|
1354
|
+
var DurableApiSessionGuard = class {
|
|
1355
|
+
static {
|
|
1356
|
+
__name(this, "DurableApiSessionGuard");
|
|
1357
|
+
}
|
|
1358
|
+
auth;
|
|
1359
|
+
constructor(auth) {
|
|
1360
|
+
this.auth = auth;
|
|
1361
|
+
}
|
|
1362
|
+
canActivate(context) {
|
|
1363
|
+
if (!this.auth) return true;
|
|
1364
|
+
const http = context.switchToHttp();
|
|
1365
|
+
const request = http.getRequest();
|
|
1366
|
+
const session = readSessionFromRequest(this.auth, request);
|
|
1367
|
+
if (!session) throw new import_common6.UnauthorizedException();
|
|
1368
|
+
maybeRenewSession(this.auth, session, request, http.getResponse());
|
|
1369
|
+
return true;
|
|
1370
|
+
}
|
|
1371
|
+
};
|
|
1372
|
+
DurableApiSessionGuard = _ts_decorate6([
|
|
1373
|
+
(0, import_common6.Injectable)(),
|
|
1374
|
+
_ts_param5(0, (0, import_common6.Inject)(DASHBOARD_AUTH)),
|
|
1375
|
+
_ts_metadata5("design:type", Function),
|
|
1376
|
+
_ts_metadata5("design:paramtypes", [
|
|
1377
|
+
Object
|
|
1378
|
+
])
|
|
1379
|
+
], DurableApiSessionGuard);
|
|
1380
|
+
|
|
1381
|
+
// src/server/durable-dashboard.module.ts
|
|
1382
|
+
function _ts_decorate7(decorators, target, key, desc) {
|
|
1383
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1384
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
1385
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
1386
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1387
|
+
}
|
|
1388
|
+
__name(_ts_decorate7, "_ts_decorate");
|
|
673
1389
|
var GUARDS_METADATA = "__guards__";
|
|
674
1390
|
function normalize(path) {
|
|
675
1391
|
return `/${path.replace(/^\/+|\/+$/g, "")}`;
|
|
676
1392
|
}
|
|
677
1393
|
__name(normalize, "normalize");
|
|
678
|
-
function stampGuards(
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
1394
|
+
function stampGuards(hostGuards, builtIn, controller) {
|
|
1395
|
+
const merged = [
|
|
1396
|
+
...builtIn ? [
|
|
1397
|
+
builtIn
|
|
1398
|
+
] : [],
|
|
1399
|
+
...hostGuards ?? []
|
|
1400
|
+
];
|
|
1401
|
+
Reflect.defineMetadata(GUARDS_METADATA, merged, controller);
|
|
682
1402
|
}
|
|
683
1403
|
__name(stampGuards, "stampGuards");
|
|
684
1404
|
var DurableApiModule = class _DurableApiModule {
|
|
@@ -686,6 +1406,7 @@ var DurableApiModule = class _DurableApiModule {
|
|
|
686
1406
|
__name(this, "DurableApiModule");
|
|
687
1407
|
}
|
|
688
1408
|
static register(options) {
|
|
1409
|
+
stampGuards(options.guards, options.authMayBeConfigured ? DurableApiSessionGuard : void 0, DurableApiController);
|
|
689
1410
|
return {
|
|
690
1411
|
module: _DurableApiModule,
|
|
691
1412
|
imports: [
|
|
@@ -696,6 +1417,8 @@ var DurableApiModule = class _DurableApiModule {
|
|
|
696
1417
|
],
|
|
697
1418
|
providers: [
|
|
698
1419
|
DashboardService,
|
|
1420
|
+
options.authProvider,
|
|
1421
|
+
DurableApiSessionGuard,
|
|
699
1422
|
...options.guards ?? []
|
|
700
1423
|
],
|
|
701
1424
|
exports: [
|
|
@@ -704,17 +1427,33 @@ var DurableApiModule = class _DurableApiModule {
|
|
|
704
1427
|
};
|
|
705
1428
|
}
|
|
706
1429
|
};
|
|
707
|
-
DurableApiModule =
|
|
708
|
-
(0,
|
|
1430
|
+
DurableApiModule = _ts_decorate7([
|
|
1431
|
+
(0, import_common7.Module)({})
|
|
709
1432
|
], DurableApiModule);
|
|
710
1433
|
var DurableDashboardModule = class _DurableDashboardModule {
|
|
711
1434
|
static {
|
|
712
1435
|
__name(this, "DurableDashboardModule");
|
|
713
1436
|
}
|
|
714
1437
|
static forRoot(options = {}) {
|
|
1438
|
+
const authProvider = {
|
|
1439
|
+
provide: DASHBOARD_AUTH,
|
|
1440
|
+
useValue: resolveDashboardAuth(options.dashboardAuth)
|
|
1441
|
+
};
|
|
1442
|
+
return _DurableDashboardModule.build(options, authProvider, options.dashboardAuth !== void 0);
|
|
1443
|
+
}
|
|
1444
|
+
static forRootAsync(options) {
|
|
1445
|
+
const authProvider = {
|
|
1446
|
+
provide: DASHBOARD_AUTH,
|
|
1447
|
+
inject: options.inject ?? [],
|
|
1448
|
+
useFactory: /* @__PURE__ */ __name(async (...deps) => resolveDashboardAuth(await options.useDashboardAuth(...deps)), "useFactory")
|
|
1449
|
+
};
|
|
1450
|
+
return _DurableDashboardModule.build(options, authProvider, true);
|
|
1451
|
+
}
|
|
1452
|
+
/** Shared wiring: static routing + the API module, with `dashboardAuth` supplied by `authProvider`. */
|
|
1453
|
+
static build(options, authProvider, authMayBeConfigured) {
|
|
715
1454
|
const basePath = normalize(options.basePath ?? "/durable");
|
|
716
1455
|
const apiBasePath = normalize(options.apiBasePath ?? `${basePath}/api`);
|
|
717
|
-
stampGuards(options.guards,
|
|
1456
|
+
stampGuards(options.guards, authMayBeConfigured ? DurableUiSessionGuard : void 0, DurableUiController);
|
|
718
1457
|
return {
|
|
719
1458
|
module: _DurableDashboardModule,
|
|
720
1459
|
imports: [
|
|
@@ -728,7 +1467,9 @@ var DurableDashboardModule = class _DurableDashboardModule {
|
|
|
728
1467
|
} : {},
|
|
729
1468
|
...options.guards ? {
|
|
730
1469
|
guards: options.guards
|
|
731
|
-
} : {}
|
|
1470
|
+
} : {},
|
|
1471
|
+
authProvider,
|
|
1472
|
+
authMayBeConfigured
|
|
732
1473
|
}),
|
|
733
1474
|
import_core.RouterModule.register([
|
|
734
1475
|
{
|
|
@@ -742,7 +1483,13 @@ var DurableDashboardModule = class _DurableDashboardModule {
|
|
|
742
1483
|
])
|
|
743
1484
|
],
|
|
744
1485
|
controllers: [
|
|
745
|
-
DurableUiController
|
|
1486
|
+
DurableUiController,
|
|
1487
|
+
// The auth controller (login page + logout) only exists when dashboardAuth might be
|
|
1488
|
+
// configured — mounting it unconditionally would add a new `/login` route even for hosts
|
|
1489
|
+
// that never set `dashboardAuth`, breaking "absent -> today's behavior byte-for-byte".
|
|
1490
|
+
...authMayBeConfigured ? [
|
|
1491
|
+
DurableAuthController
|
|
1492
|
+
] : []
|
|
746
1493
|
],
|
|
747
1494
|
providers: [
|
|
748
1495
|
{
|
|
@@ -753,7 +1500,10 @@ var DurableDashboardModule = class _DurableDashboardModule {
|
|
|
753
1500
|
provide: DASHBOARD_API_PATH,
|
|
754
1501
|
useValue: apiBasePath
|
|
755
1502
|
},
|
|
756
|
-
|
|
1503
|
+
authProvider,
|
|
1504
|
+
DurableUiSessionGuard,
|
|
1505
|
+
// DurableUiController (and DurableAuthController, when present) are hosted HERE, so their
|
|
1506
|
+
// guards DI-instantiate from this module.
|
|
757
1507
|
...options.guards ?? []
|
|
758
1508
|
],
|
|
759
1509
|
// Re-export the API module so its DashboardService reaches importers (e.g. flip's own controllers).
|
|
@@ -763,8 +1513,8 @@ var DurableDashboardModule = class _DurableDashboardModule {
|
|
|
763
1513
|
};
|
|
764
1514
|
}
|
|
765
1515
|
};
|
|
766
|
-
DurableDashboardModule =
|
|
767
|
-
(0,
|
|
1516
|
+
DurableDashboardModule = _ts_decorate7([
|
|
1517
|
+
(0, import_common7.Module)({})
|
|
768
1518
|
], DurableDashboardModule);
|
|
769
1519
|
// Annotate the CommonJS export names for ESM import in node:
|
|
770
1520
|
0 && (module.exports = {
|
|
@@ -773,7 +1523,10 @@ DurableDashboardModule = _ts_decorate4([
|
|
|
773
1523
|
DashboardService,
|
|
774
1524
|
DurableApiController,
|
|
775
1525
|
DurableApiModule,
|
|
1526
|
+
DurableApiSessionGuard,
|
|
1527
|
+
DurableAuthController,
|
|
776
1528
|
DurableDashboardModule,
|
|
777
|
-
DurableUiController
|
|
1529
|
+
DurableUiController,
|
|
1530
|
+
DurableUiSessionGuard
|
|
778
1531
|
});
|
|
779
1532
|
//# sourceMappingURL=index.cjs.map
|