@dsh-plus/access-gate 0.1.4 → 0.2.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/lib/client.js +49 -163
- package/lib/index.d.ts +1 -9
- package/lib/index.js +183 -258
- package/package.json +3 -2
- package/src/client/card.tsx +21 -112
- package/src/client/i18n.ts +23 -37
- package/src/config.ts +9 -23
- package/src/decision.ts +42 -50
- package/src/index.ts +26 -7
- package/src/interceptor.ts +33 -11
- package/src/routes.ts +47 -109
- package/src/token-page.ts +88 -0
- package/src/login-page.ts +0 -77
package/lib/client.js
CHANGED
|
@@ -91,41 +91,6 @@ window.__ModuleLoader__.load({
|
|
|
91
91
|
}
|
|
92
92
|
//#endregion
|
|
93
93
|
//#region ../shared/src/client/fields.tsx
|
|
94
|
-
function TextField(props) {
|
|
95
|
-
const p = props.prefix ?? "dshp";
|
|
96
|
-
const invalid = props.invalid === true;
|
|
97
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
98
|
-
className: `${p}-field`,
|
|
99
|
-
children: [
|
|
100
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
101
|
-
className: `${p}-head`,
|
|
102
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
103
|
-
className: `${p}-label`,
|
|
104
|
-
htmlFor: props.id,
|
|
105
|
-
children: props.label
|
|
106
|
-
}), props.badge !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
107
|
-
className: `${p}-badge ${props.badge.set ? `${p}-badgeSet` : `${p}-badgeUnset`}`,
|
|
108
|
-
children: props.badge.text
|
|
109
|
-
}) : null]
|
|
110
|
-
}),
|
|
111
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
112
|
-
id: props.id,
|
|
113
|
-
className: `${p}-input${invalid ? ` ${p}-inputInvalid` : ""}`,
|
|
114
|
-
type: props.password === true ? "password" : "text",
|
|
115
|
-
inputMode: props.numeric === true ? "numeric" : void 0,
|
|
116
|
-
autoComplete: props.password === true ? "off" : void 0,
|
|
117
|
-
"aria-invalid": invalid || void 0,
|
|
118
|
-
value: props.value,
|
|
119
|
-
disabled: props.disabled === true,
|
|
120
|
-
onChange: (event) => props.onEdit(event.target.value)
|
|
121
|
-
}),
|
|
122
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
123
|
-
className: invalid ? `${p}-invalid` : `${p}-hint`,
|
|
124
|
-
children: invalid ? props.invalidLabel ?? "" : props.hint ?? ""
|
|
125
|
-
})
|
|
126
|
-
]
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
94
|
/**
|
|
130
95
|
* 勾选行:自绘圆角扁平开关(switch),对齐官方 WebUI 无原生 checkbox 的
|
|
131
96
|
* 设计语言(官方选择类控件全走胶囊/菜单,见 cardCss 的 -switch 规则)。
|
|
@@ -395,47 +360,39 @@ ${extra}
|
|
|
395
360
|
/**
|
|
396
361
|
* 「访问控制」配置卡片:注册进 settings.plugin.item 插槽(官方插件配置页)。
|
|
397
362
|
* 外壳与基础控件走 @dsh-plus/shared/client 套件(CardChrome/TextField/CheckRow),
|
|
398
|
-
*
|
|
399
|
-
* 配置读写走官方 settingsScope 传输:value 为 schema
|
|
400
|
-
*
|
|
401
|
-
*
|
|
402
|
-
*
|
|
363
|
+
* 本文件保留业务字段(白名单 textarea/诊断面板)与保存逻辑。
|
|
364
|
+
* 配置读写走官方 settingsScope 传输:value 为 schema 解析后的视图,
|
|
365
|
+
* 保存经 settings.update 深合并。
|
|
366
|
+
* 卡片顶部「当前页面诊断」读 /dsh-plus/gate/status(本页放行原因/客户端 IP/
|
|
367
|
+
* 官方 cookie 状态)。
|
|
368
|
+
*
|
|
369
|
+
* 与官方认证合并后:访问凭据由官方 browser-auth 持有(30 天 cookie),
|
|
370
|
+
* 卡片不再管理令牌;allowedIps 仅为可选附加 IP 围栏。
|
|
403
371
|
* @module access-gate/client/card
|
|
404
372
|
*/
|
|
405
373
|
function draftFromValue(value) {
|
|
406
374
|
return {
|
|
407
375
|
enabled: value.enabled,
|
|
408
|
-
token: "",
|
|
409
376
|
allowedIpsText: value.allowedIps.join("\n"),
|
|
410
|
-
trustForwardedFor: value.trustForwardedFor
|
|
411
|
-
cookieMaxAgeHours: String(value.cookieMaxAgeHours),
|
|
412
|
-
loginFailLimit: String(value.loginFailLimit),
|
|
413
|
-
loginCooldownMs: String(value.loginCooldownMs)
|
|
377
|
+
trustForwardedFor: value.trustForwardedFor
|
|
414
378
|
};
|
|
415
379
|
}
|
|
416
|
-
function isPositiveInt(text) {
|
|
417
|
-
return /^[0-9]+$/.test(text) && Number(text) > 0;
|
|
418
|
-
}
|
|
419
380
|
function toPatch(draft) {
|
|
420
381
|
return {
|
|
421
382
|
enabled: draft.enabled,
|
|
422
383
|
allowedIps: draft.allowedIpsText.split("\n").map((s) => s.trim()).filter((s) => s.length > 0),
|
|
423
|
-
trustForwardedFor: draft.trustForwardedFor
|
|
424
|
-
cookieMaxAgeHours: Number(draft.cookieMaxAgeHours),
|
|
425
|
-
loginFailLimit: Number(draft.loginFailLimit),
|
|
426
|
-
loginCooldownMs: Number(draft.loginCooldownMs)
|
|
384
|
+
trustForwardedFor: draft.trustForwardedFor
|
|
427
385
|
};
|
|
428
386
|
}
|
|
429
387
|
/** reason 枚举 → 文案 key。 */
|
|
430
388
|
const REASON_KEYS = {
|
|
431
389
|
local: "diagLocal",
|
|
432
|
-
|
|
433
|
-
token: "diagToken"
|
|
390
|
+
cookie: "diagCookie"
|
|
434
391
|
};
|
|
435
392
|
function verdictLabel(t, diag) {
|
|
436
393
|
if (!diag.enabled) return t("diagOff");
|
|
437
394
|
if (diag.verdict === "pass") return t("diagPass");
|
|
438
|
-
if (diag.verdict === "
|
|
395
|
+
if (diag.verdict === "token-page") return t("diagTokenPage");
|
|
439
396
|
return t("diagBlock");
|
|
440
397
|
}
|
|
441
398
|
function AccessGateCard(props) {
|
|
@@ -444,7 +401,6 @@ ${extra}
|
|
|
444
401
|
const value = snapshot.value;
|
|
445
402
|
const [open, setOpen] = (0, react.useState)(false);
|
|
446
403
|
const [draft, setDraft] = (0, react.useState)(null);
|
|
447
|
-
const [tokenConfigured, setTokenConfigured] = (0, react.useState)(false);
|
|
448
404
|
const [diag, setDiag] = (0, react.useState)(null);
|
|
449
405
|
const [saving, setSaving] = (0, react.useState)(false);
|
|
450
406
|
const [status, setStatus] = (0, react.useState)(IDLE_STATUS);
|
|
@@ -452,17 +408,6 @@ ${extra}
|
|
|
452
408
|
if (value === void 0 || draft !== null) return;
|
|
453
409
|
setDraft(draftFromValue(value));
|
|
454
410
|
}, [value, draft]);
|
|
455
|
-
(0, react.useEffect)(() => {
|
|
456
|
-
let alive = true;
|
|
457
|
-
api.describe().then((view) => {
|
|
458
|
-
if (!alive) return;
|
|
459
|
-
const ns = view.namespaces.find((candidate) => candidate.ns === SETTINGS_NS);
|
|
460
|
-
setTokenConfigured(ns?.secrets.some((secret) => secret.path.join(".") === "token" && secret.set) ?? false);
|
|
461
|
-
}).catch(() => {});
|
|
462
|
-
return () => {
|
|
463
|
-
alive = false;
|
|
464
|
-
};
|
|
465
|
-
}, [api, snapshot]);
|
|
466
411
|
(0, react.useEffect)(() => {
|
|
467
412
|
let alive = true;
|
|
468
413
|
fetch("/dsh-plus/gate/status").then((res) => res.ok ? res.json() : null).then((body) => {
|
|
@@ -472,9 +417,7 @@ ${extra}
|
|
|
472
417
|
alive = false;
|
|
473
418
|
};
|
|
474
419
|
}, [snapshot]);
|
|
475
|
-
const failClosed = (0, react.useMemo)(() => value?.enabled === true && value.allowedIps.length === 0 && !tokenConfigured, [value, tokenConfigured]);
|
|
476
420
|
const dirty = (0, react.useMemo)(() => value !== void 0 && draft !== null && JSON.stringify(toPatch(draft)) !== JSON.stringify(toPatch(draftFromValue(value))), [value, draft]);
|
|
477
|
-
const invalid = (0, react.useMemo)(() => draft !== null && (!isPositiveInt(draft.cookieMaxAgeHours) || !isPositiveInt(draft.loginFailLimit) || !isPositiveInt(draft.loginCooldownMs)), [draft]);
|
|
478
421
|
if (value === void 0 || draft === null) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", {
|
|
479
422
|
className: "dag-card",
|
|
480
423
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
@@ -492,7 +435,6 @@ ${extra}
|
|
|
492
435
|
const onSave = () => {
|
|
493
436
|
setSaving(true);
|
|
494
437
|
const patch = toPatch(draft);
|
|
495
|
-
if (draft.token !== "") patch.token = draft.token;
|
|
496
438
|
const revision = scope.getSnapshot().revision;
|
|
497
439
|
api.update(patch, revision).then(async () => {
|
|
498
440
|
await scope.load();
|
|
@@ -534,14 +476,14 @@ ${extra}
|
|
|
534
476
|
key: "save",
|
|
535
477
|
label: t(saving ? "saving" : "save"),
|
|
536
478
|
variant: "primary",
|
|
537
|
-
disabled: !dirty ||
|
|
479
|
+
disabled: !dirty || saving || disabled,
|
|
538
480
|
onClick: onSave
|
|
539
481
|
}],
|
|
540
482
|
children: [
|
|
541
|
-
|
|
542
|
-
className: "dag-
|
|
543
|
-
children: t("
|
|
544
|
-
})
|
|
483
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
484
|
+
className: "dag-hint",
|
|
485
|
+
children: t("mergedHint")
|
|
486
|
+
}),
|
|
545
487
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(CheckRow, {
|
|
546
488
|
prefix: "dag",
|
|
547
489
|
id: "dag-enabled",
|
|
@@ -550,20 +492,6 @@ ${extra}
|
|
|
550
492
|
disabled,
|
|
551
493
|
onEdit: (v) => edit("enabled", v)
|
|
552
494
|
}),
|
|
553
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(TextField, {
|
|
554
|
-
prefix: "dag",
|
|
555
|
-
id: "dag-token",
|
|
556
|
-
label: t("token"),
|
|
557
|
-
hint: t("tokenHint"),
|
|
558
|
-
value: draft.token,
|
|
559
|
-
password: true,
|
|
560
|
-
disabled,
|
|
561
|
-
badge: {
|
|
562
|
-
text: t(tokenConfigured ? "tokenSet" : "tokenUnset"),
|
|
563
|
-
set: tokenConfigured
|
|
564
|
-
},
|
|
565
|
-
onEdit: (v) => edit("token", v)
|
|
566
|
-
}),
|
|
567
495
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
568
496
|
className: "dag-field",
|
|
569
497
|
children: [
|
|
@@ -597,42 +525,6 @@ ${extra}
|
|
|
597
525
|
disabled,
|
|
598
526
|
onEdit: (v) => edit("trustForwardedFor", v)
|
|
599
527
|
}),
|
|
600
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(TextField, {
|
|
601
|
-
prefix: "dag",
|
|
602
|
-
id: "dag-cookie",
|
|
603
|
-
label: t("cookieMaxAgeHours"),
|
|
604
|
-
hint: t("cookieMaxAgeHoursHint"),
|
|
605
|
-
value: draft.cookieMaxAgeHours,
|
|
606
|
-
numeric: true,
|
|
607
|
-
disabled,
|
|
608
|
-
invalid: !isPositiveInt(draft.cookieMaxAgeHours),
|
|
609
|
-
invalidLabel: t("invalidNumber"),
|
|
610
|
-
onEdit: (v) => edit("cookieMaxAgeHours", v)
|
|
611
|
-
}),
|
|
612
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(TextField, {
|
|
613
|
-
prefix: "dag",
|
|
614
|
-
id: "dag-faillimit",
|
|
615
|
-
label: t("loginFailLimit"),
|
|
616
|
-
hint: t("loginFailLimitHint"),
|
|
617
|
-
value: draft.loginFailLimit,
|
|
618
|
-
numeric: true,
|
|
619
|
-
disabled,
|
|
620
|
-
invalid: !isPositiveInt(draft.loginFailLimit),
|
|
621
|
-
invalidLabel: t("invalidNumber"),
|
|
622
|
-
onEdit: (v) => edit("loginFailLimit", v)
|
|
623
|
-
}),
|
|
624
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(TextField, {
|
|
625
|
-
prefix: "dag",
|
|
626
|
-
id: "dag-cooldown",
|
|
627
|
-
label: t("loginCooldownMs"),
|
|
628
|
-
hint: t("loginCooldownMsHint"),
|
|
629
|
-
value: draft.loginCooldownMs,
|
|
630
|
-
numeric: true,
|
|
631
|
-
disabled,
|
|
632
|
-
invalid: !isPositiveInt(draft.loginCooldownMs),
|
|
633
|
-
invalidLabel: t("invalidNumber"),
|
|
634
|
-
onEdit: (v) => edit("loginCooldownMs", v)
|
|
635
|
-
}),
|
|
636
528
|
diag !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
637
529
|
className: `dag-diag${diag.invalidEntries.length > 0 ? " dag-diagWarn" : ""}`,
|
|
638
530
|
children: [
|
|
@@ -670,6 +562,16 @@ ${extra}
|
|
|
670
562
|
children: diag.reason !== null ? t(REASON_KEYS[diag.reason] ?? diag.reason) : "—"
|
|
671
563
|
})]
|
|
672
564
|
}),
|
|
565
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
566
|
+
className: "dag-diagRow",
|
|
567
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
568
|
+
className: "dag-diagKey",
|
|
569
|
+
children: t("diagOfficial")
|
|
570
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
571
|
+
className: "dag-diagVal",
|
|
572
|
+
children: diag.officialAuthed ? t("diagOfficialYes") : t("diagOfficialNo")
|
|
573
|
+
})]
|
|
574
|
+
}),
|
|
673
575
|
diag.invalidEntries.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
674
576
|
className: "dag-diagRow",
|
|
675
577
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
@@ -695,69 +597,53 @@ ${extra}
|
|
|
695
597
|
const NS = "dsh-plus-access-gate";
|
|
696
598
|
const ownZh = {
|
|
697
599
|
title: "访问控制",
|
|
698
|
-
description: "Web
|
|
600
|
+
description: "Web 访问围栏:与官方登录合并——官方 cookie 为唯一凭据;未认证导航显示启动令牌输入页(PWA 内可用);可选 IP 附加围栏。",
|
|
699
601
|
enabled: "启用访问围栏",
|
|
700
|
-
enabledHint: "
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
tokenUnset: "未配置",
|
|
705
|
-
allowedIps: "放行 IP 白名单",
|
|
706
|
-
allowedIpsHint: "每行一个:精确 IP 或 CIDR(IPv4/IPv6 皆可),如 100.86.108.55 或 fd7a:115c:a1e0::/48。",
|
|
602
|
+
enabledHint: "关闭即完全旁路(等价插件缺席);启用后未认证流量被拦截到令牌输入页或拒绝。",
|
|
603
|
+
mergedHint: "登录凭据由官方认证持有(启动令牌换 30 天 cookie)。获取当前令牌:服务器执行 dshctl url,或本机访问 /dsh-plus/gate/launch-url。",
|
|
604
|
+
allowedIps: "附加 IP 围栏白名单",
|
|
605
|
+
allowedIpsHint: "每行一个:精确 IP 或 CIDR(IPv4/IPv6 皆可)。非空时仅白名单来源可通行与登录;留空 = 不限制来源 IP(仅官方登录保护)。",
|
|
707
606
|
trustForwardedFor: "信任 x-forwarded-for 还原客户端 IP",
|
|
708
607
|
trustForwardedForHint: "仅当入口代理强制覆盖该头时开启(tailscale serve 满足);直连暴露场景必须关闭。",
|
|
709
|
-
cookieMaxAgeHours: "登录有效期(小时)",
|
|
710
|
-
cookieMaxAgeHoursHint: "令牌登录后 cookie 的有效期。",
|
|
711
|
-
loginFailLimit: "登录失败阈值",
|
|
712
|
-
loginFailLimitHint: "每个 IP 在冷却窗口内允许的失败次数,超出进入冷却。",
|
|
713
|
-
loginCooldownMs: "登录冷却(毫秒)",
|
|
714
|
-
loginCooldownMsHint: "失败达到阈值后的冷却时长。",
|
|
715
608
|
diagTitle: "当前页面诊断",
|
|
716
609
|
diagVerdict: "本页判定",
|
|
717
610
|
diagClientIp: "客户端 IP",
|
|
718
611
|
diagReason: "放行原因",
|
|
719
612
|
diagLocal: "本机直连",
|
|
720
|
-
|
|
721
|
-
|
|
613
|
+
diagCookie: "官方 cookie",
|
|
614
|
+
diagOfficial: "官方登录状态",
|
|
615
|
+
diagOfficialYes: "已登录",
|
|
616
|
+
diagOfficialNo: "未登录",
|
|
722
617
|
diagPass: "放行",
|
|
723
618
|
diagBlock: "拦截",
|
|
724
|
-
|
|
619
|
+
diagTokenPage: "令牌输入页",
|
|
725
620
|
diagOff: "围栏未启用",
|
|
726
|
-
diagInvalid: "白名单存在无法解析的条目:"
|
|
727
|
-
warnFailClosed: "警告:已启用但令牌与白名单均为空——所有远程访问将被拒绝(含本页之外的设备)。"
|
|
621
|
+
diagInvalid: "白名单存在无法解析的条目:"
|
|
728
622
|
};
|
|
729
623
|
const ownEn = {
|
|
730
624
|
title: "Access Control",
|
|
731
|
-
description: "Web access gate:
|
|
625
|
+
description: "Web access gate merged with official auth: the official cookie is the sole credential; unauthenticated navigations get a launch-token page (works inside the PWA); optional IP fence.",
|
|
732
626
|
enabled: "Enable access gate",
|
|
733
|
-
enabledHint: "Off means full bypass (as if absent); on,
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
tokenUnset: "Not set",
|
|
738
|
-
allowedIps: "Allowed IP allowlist",
|
|
739
|
-
allowedIpsHint: "One per line: exact IP or CIDR (IPv4/IPv6), e.g. 100.86.108.55 or fd7a:115c:a1e0::/48.",
|
|
627
|
+
enabledHint: "Off means full bypass (as if absent); on, unauthenticated traffic hits the token page or is refused.",
|
|
628
|
+
mergedHint: "Credentials are owned by official auth (launch token exchange, 30-day cookie). Get the current token: run dshctl url on the server, or visit /dsh-plus/gate/launch-url locally.",
|
|
629
|
+
allowedIps: "Additional IP fence allowlist",
|
|
630
|
+
allowedIpsHint: "One per line: exact IP or CIDR (IPv4/IPv6). When non-empty only listed sources may pass or log in; empty = no IP restriction (official login only).",
|
|
740
631
|
trustForwardedFor: "Trust x-forwarded-for for client IP",
|
|
741
632
|
trustForwardedForHint: "Enable only when the entry proxy overwrites that header (tailscale serve does); must be off for direct exposure.",
|
|
742
|
-
cookieMaxAgeHours: "Login validity (hours)",
|
|
743
|
-
cookieMaxAgeHoursHint: "Cookie lifetime after a token login.",
|
|
744
|
-
loginFailLimit: "Login failure limit",
|
|
745
|
-
loginFailLimitHint: "Failures per IP within the cooldown window before throttling.",
|
|
746
|
-
loginCooldownMs: "Login cooldown (ms)",
|
|
747
|
-
loginCooldownMsHint: "Cooldown duration after reaching the limit.",
|
|
748
633
|
diagTitle: "This page",
|
|
749
634
|
diagVerdict: "Verdict",
|
|
750
635
|
diagClientIp: "Client IP",
|
|
751
636
|
diagReason: "Reason",
|
|
752
637
|
diagLocal: "loopback",
|
|
753
|
-
|
|
754
|
-
|
|
638
|
+
diagCookie: "official cookie",
|
|
639
|
+
diagOfficial: "Official login",
|
|
640
|
+
diagOfficialYes: "authenticated",
|
|
641
|
+
diagOfficialNo: "not authenticated",
|
|
755
642
|
diagPass: "pass",
|
|
756
643
|
diagBlock: "blocked",
|
|
757
|
-
|
|
644
|
+
diagTokenPage: "token page",
|
|
758
645
|
diagOff: "gate disabled",
|
|
759
|
-
diagInvalid: "Allowlist entries that failed to parse:"
|
|
760
|
-
warnFailClosed: "Warning: enabled with no token and an empty allowlist — all remote access will be refused."
|
|
646
|
+
diagInvalid: "Allowlist entries that failed to parse:"
|
|
761
647
|
};
|
|
762
648
|
const zh = mergeDict(commonZh, ownZh);
|
|
763
649
|
const en = mergeDict(commonEn, ownEn);
|
package/lib/index.d.ts
CHANGED
|
@@ -3,26 +3,18 @@ import z from "@deepseek-ai/schemastery";
|
|
|
3
3
|
//#region src/config.d.ts
|
|
4
4
|
declare const Config: z<Schemastery.ObjectS<{
|
|
5
5
|
enabled: z<boolean, boolean>;
|
|
6
|
-
token: z<string, string>;
|
|
7
6
|
allowedIps: z<string[], string[]>;
|
|
8
7
|
trustForwardedFor: z<boolean, boolean>;
|
|
9
|
-
cookieMaxAgeHours: z<number, number>;
|
|
10
|
-
loginFailLimit: z<number, number>;
|
|
11
|
-
loginCooldownMs: z<number, number>;
|
|
12
8
|
}>, Schemastery.ObjectT<{
|
|
13
9
|
enabled: z<boolean, boolean>;
|
|
14
|
-
token: z<string, string>;
|
|
15
10
|
allowedIps: z<string[], string[]>;
|
|
16
11
|
trustForwardedFor: z<boolean, boolean>;
|
|
17
|
-
cookieMaxAgeHours: z<number, number>;
|
|
18
|
-
loginFailLimit: z<number, number>;
|
|
19
|
-
loginCooldownMs: z<number, number>;
|
|
20
12
|
}>>;
|
|
21
13
|
type AccessGateConfig = Schemastery.TypeT<typeof Config>;
|
|
22
14
|
//#endregion
|
|
23
15
|
//#region src/index.d.ts
|
|
24
16
|
declare const name = "dsh-plus-access-gate";
|
|
25
|
-
declare const inject: readonly ["webServer"];
|
|
17
|
+
declare const inject: readonly ["webServer", "connection"];
|
|
26
18
|
declare module '@deepseek-ai/cordis' {
|
|
27
19
|
interface Context {
|
|
28
20
|
accessGate: AccessGateService;
|