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