@wenbin_wb/dsh-bridge 2.10.4 → 2.10.5
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/CHANGELOG.md +10 -0
- package/client/client.js +14 -1
- package/client/index.js +18 -2
- package/lib/auth/manager.js +5 -4
- package/lib/bridge-rpc.js +19 -0
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,16 @@
|
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
+
## [v2.10.5] - 2026-09-06
|
|
8
|
+
|
|
9
|
+
### 🐞 修复
|
|
10
|
+
|
|
11
|
+
- **修复「仅密码登录」模式自我锁死**:`password_only` 模式在尚未设置任何密码时,服务端会拒绝一切登录(防空白登录),而管理员因进不去面板也无法设置密码 → 永久死锁。现未设密码时放行(与「扫码+密码」默认模式一致——没有可校验的哈希即无秘密可保护),让管理员能进入面板完成密码初始化;设置密码后门禁才真正生效(错误密码会被拒绝)。
|
|
12
|
+
- **修复无密码时找不到设置密码入口**:此前"外部访问门禁"卡片(含访客访问密码输入框)仅在安全防护已开启时渲染;未开启防护时面板没有密码输入框,用户先开防护再想设密码就会被卡。现只要系统**尚未设置任何密码**,密码设置区就始终显示。
|
|
13
|
+
- **切换「仅密码」模式 / 开启防护的引导**:UI 在切换到 `password_only` 或开启防护时若尚未设密码,会就地弹窗引导先设置访问密码;服务端 `authUpdateConfig` 同步增加守卫——无密码时禁止开启 `password_only` 防护(除非同请求携带新密码),双保险杜绝再次自我锁死。
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
7
17
|
## [v2.10.4] - 2026-09-05
|
|
8
18
|
|
|
9
19
|
### 🐞 修复
|
package/client/client.js
CHANGED
|
@@ -1820,6 +1820,11 @@ var AccessAuthCard = React.memo(function AccessAuthCard2({ auth, rpcCall, onUpda
|
|
|
1820
1820
|
const next = !enabled;
|
|
1821
1821
|
const noPasswordYet = !auth?.hasPassword && !auth?.hasAdminPassword;
|
|
1822
1822
|
if (next && noPasswordYet && mode !== "token_only") {
|
|
1823
|
+
if (mode === "password_only") {
|
|
1824
|
+
window.alert("\u300C\u4EC5\u5BC6\u7801 / PIN \u7801\u767B\u5F55\u300D\u6A21\u5F0F\u5FC5\u987B\u5148\u8BBE\u7F6E\u8BBF\u95EE\u5BC6\u7801\u624D\u80FD\u5F00\u542F\u5B89\u5168\u9632\u62A4\u3002\n\n\u8BF7\u5148\u5728\u4E0B\u65B9\u300C\u8BBE\u7F6E\u5916\u90E8\u8BBF\u5BA2\u8BBF\u95EE\u5BC6\u7801 / PIN \u7801\u300D\u8F93\u5165\u5BC6\u7801\u5E76\u70B9\u51FB\u300C\u4FDD\u5B58\u8BBF\u95EE\u5BC6\u7801\u300D\uFF0C\u7136\u540E\u518D\u5F00\u542F\u3002");
|
|
1825
|
+
setTopMsg({ ok: false, text: "\u8BF7\u5148\u8BBE\u7F6E\u8BBF\u5BA2\u8BBF\u95EE\u5BC6\u7801\uFF0C\u518D\u5F00\u542F\u5B89\u5168\u9632\u62A4" });
|
|
1826
|
+
return;
|
|
1827
|
+
}
|
|
1823
1828
|
const go = window.confirm(
|
|
1824
1829
|
'\u26A0\uFE0F \u60A8\u5C1A\u672A\u8BBE\u7F6E\u4EFB\u4F55\u8BBF\u95EE\u5BC6\u7801\u6216\u7BA1\u7406\u5BC6\u7801\u3002\n\n\u5F00\u542F\u5B89\u5168\u9632\u62A4\u540E\uFF0C\u4EFB\u4F55\u77E5\u9053\u5C40\u57DF\u7F51 IP / \u96A7\u9053\u5730\u5740\u7684\u8BBF\u5BA2\u4ECD\u53EF\u76F4\u63A5\u8FDB\u5165\uFF08\u5F53\u524D\u76F8\u5F53\u4E8E"\u514D\u5BC6\u5F00\u653E"\u72B6\u6001\uFF09\u3002\n\n\u662F\u5426\u4ECD\u8981\u5F00\u542F\uFF1F\u5EFA\u8BAE\u5148\u5173\u95ED\uFF0C\u5728\u4E0B\u65B9\u300C\u8BBE\u7F6E\u5916\u90E8\u8BBF\u5BA2\u8BBF\u95EE\u5BC6\u7801\u300D\u5904\u8BBE\u7F6E\u5BC6\u7801\u540E\u518D\u5F00\u542F\u3002'
|
|
1825
1830
|
);
|
|
@@ -1851,6 +1856,11 @@ var AccessAuthCard = React.memo(function AccessAuthCard2({ auth, rpcCall, onUpda
|
|
|
1851
1856
|
}
|
|
1852
1857
|
};
|
|
1853
1858
|
const handleChangeMode = async (m) => {
|
|
1859
|
+
if (m === "password_only" && !auth?.hasPassword && !auth?.hasAdminPassword) {
|
|
1860
|
+
window.alert("\u300C\u4EC5\u5BC6\u7801 / PIN \u7801\u767B\u5F55\u300D\u9700\u8981\u5148\u8BBE\u7F6E\u8BBF\u95EE\u5BC6\u7801\u3002\n\n\u8BF7\u5728\u4E0B\u65B9\u300C\u8BBE\u7F6E\u5916\u90E8\u8BBF\u5BA2\u8BBF\u95EE\u5BC6\u7801 / PIN \u7801\u300D\u5904\u8F93\u5165\u5BC6\u7801\u5E76\u70B9\u51FB\u300C\u4FDD\u5B58\u8BBF\u95EE\u5BC6\u7801\u300D\uFF0C\u7136\u540E\u518D\u5207\u6362\u5230\u6B64\u6A21\u5F0F\u6216\u5F00\u542F\u5B89\u5168\u9632\u62A4\u3002");
|
|
1861
|
+
setTopMsg({ ok: false, text: "\u8BF7\u5148\u5728\u4E0B\u65B9\u8BBE\u7F6E\u8BBF\u5BA2\u8BBF\u95EE\u5BC6\u7801\uFF0C\u518D\u5207\u6362\u4E3A\u300C\u4EC5\u5BC6\u7801\u767B\u5F55\u300D\u6A21\u5F0F" });
|
|
1862
|
+
return;
|
|
1863
|
+
}
|
|
1854
1864
|
const prev = mode;
|
|
1855
1865
|
setMode(m);
|
|
1856
1866
|
try {
|
|
@@ -2064,7 +2074,10 @@ var AccessAuthCard = React.memo(function AccessAuthCard2({ auth, rpcCall, onUpda
|
|
|
2064
2074
|
// =========================================================================
|
|
2065
2075
|
// ---- 第一道防线:外部访问门禁(控制谁能进入 Web 界面使用 AI) ----
|
|
2066
2076
|
// =========================================================================
|
|
2067
|
-
|
|
2077
|
+
// 第一道防线卡片:已开启防护,或【尚未设置任何密码】时始终显示——
|
|
2078
|
+
// 未设密码时必须给出密码输入框,否则用户开启防护(尤其 password_only)后
|
|
2079
|
+
// 会因无密码被锁在登录墙外且找不到设密码入口(自我锁死,v2.10.5 修复)。
|
|
2080
|
+
(enabled || !auth?.hasPassword) && React.createElement(
|
|
2068
2081
|
"div",
|
|
2069
2082
|
{ style: s.card },
|
|
2070
2083
|
React.createElement(
|
package/client/index.js
CHANGED
|
@@ -758,9 +758,15 @@ const AccessAuthCard = React.memo(function AccessAuthCard({ auth, rpcCall, onUpd
|
|
|
758
758
|
const handleToggleEnabled = async () => {
|
|
759
759
|
const prev = enabled;
|
|
760
760
|
const next = !enabled;
|
|
761
|
-
//
|
|
761
|
+
// 开启安全防护但尚未设置任何密码
|
|
762
762
|
const noPasswordYet = !auth?.hasPassword && !auth?.hasAdminPassword;
|
|
763
763
|
if (next && noPasswordYet && mode !== 'token_only') {
|
|
764
|
+
if (mode === 'password_only') {
|
|
765
|
+
// password_only 模式必须已有密码才能开启(服务端同样强制),就地引导设密码
|
|
766
|
+
window.alert('「仅密码 / PIN 码登录」模式必须先设置访问密码才能开启安全防护。\n\n请先在下方「设置外部访客访问密码 / PIN 码」输入密码并点击「保存访问密码」,然后再开启。');
|
|
767
|
+
setTopMsg({ ok: false, text: '请先设置访客访问密码,再开启安全防护' });
|
|
768
|
+
return;
|
|
769
|
+
}
|
|
764
770
|
const go = window.confirm(
|
|
765
771
|
'⚠️ 您尚未设置任何访问密码或管理密码。\n\n开启安全防护后,任何知道局域网 IP / 隧道地址的访客仍可直接进入(当前相当于"免密开放"状态)。\n\n是否仍要开启?建议先关闭,在下方「设置外部访客访问密码」处设置密码后再开启。'
|
|
766
772
|
);
|
|
@@ -798,6 +804,13 @@ const AccessAuthCard = React.memo(function AccessAuthCard({ auth, rpcCall, onUpd
|
|
|
798
804
|
};
|
|
799
805
|
|
|
800
806
|
const handleChangeMode = async (m) => {
|
|
807
|
+
// 切换到「仅密码登录」但尚未设置任何密码:提前引导先设密码,避免切过去后
|
|
808
|
+
// 无密码可登录(服务端同样有守卫拒绝,双保险防自我锁死)
|
|
809
|
+
if (m === 'password_only' && !auth?.hasPassword && !auth?.hasAdminPassword) {
|
|
810
|
+
window.alert('「仅密码 / PIN 码登录」需要先设置访问密码。\n\n请在下方「设置外部访客访问密码 / PIN 码」处输入密码并点击「保存访问密码」,然后再切换到此模式或开启安全防护。');
|
|
811
|
+
setTopMsg({ ok: false, text: '请先在下方设置访客访问密码,再切换为「仅密码登录」模式' });
|
|
812
|
+
return;
|
|
813
|
+
}
|
|
801
814
|
const prev = mode;
|
|
802
815
|
setMode(m);
|
|
803
816
|
try {
|
|
@@ -974,7 +987,10 @@ const AccessAuthCard = React.memo(function AccessAuthCard({ auth, rpcCall, onUpd
|
|
|
974
987
|
// =========================================================================
|
|
975
988
|
// ---- 第一道防线:外部访问门禁(控制谁能进入 Web 界面使用 AI) ----
|
|
976
989
|
// =========================================================================
|
|
977
|
-
|
|
990
|
+
// 第一道防线卡片:已开启防护,或【尚未设置任何密码】时始终显示——
|
|
991
|
+
// 未设密码时必须给出密码输入框,否则用户开启防护(尤其 password_only)后
|
|
992
|
+
// 会因无密码被锁在登录墙外且找不到设密码入口(自我锁死,v2.10.5 修复)。
|
|
993
|
+
(enabled || !auth?.hasPassword) && React.createElement('div', { style: s.card },
|
|
978
994
|
React.createElement('div', { style: { marginBottom: 14 } },
|
|
979
995
|
React.createElement('div', { style: { ...s.label, fontSize: 14, display: 'flex', alignItems: 'center', gap: 6 } },
|
|
980
996
|
'🛡️ 第一道防线:外部访问门禁(控制谁能使用 AI)',
|
package/lib/auth/manager.js
CHANGED
|
@@ -365,10 +365,11 @@ export class AuthManager {
|
|
|
365
365
|
}
|
|
366
366
|
|
|
367
367
|
if (!this.hasPassword) {
|
|
368
|
-
//
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
368
|
+
// 管理员尚未设置任何访问密码:无哈希可校验,放行(与 token_and_password 一致)。
|
|
369
|
+
// 历史实现曾在此拒绝 password_only 模式的空白登录,但那会形成死锁:
|
|
370
|
+
// 无密码 → 登录墙永远 401 → 进不去面板 → 看不到设密码表单 → 永远无法设密码。
|
|
371
|
+
// 未设密码 = 没有受保护秘密,应先让管理员进入完成密码初始化;
|
|
372
|
+
// 设置密码后,墙才会真正生效(此时任意/错误密码均被拒绝)。
|
|
372
373
|
return { success: true }
|
|
373
374
|
}
|
|
374
375
|
|
package/lib/bridge-rpc.js
CHANGED
|
@@ -119,6 +119,25 @@ export function installBridgeRpc(ctx, { service, authManager, platformManager, l
|
|
|
119
119
|
const adminErr = checkAdminAuth(authManager, payload);
|
|
120
120
|
if (adminErr) return adminErr;
|
|
121
121
|
}
|
|
122
|
+
|
|
123
|
+
// 防自我锁死守卫(v2.10.5):
|
|
124
|
+
// password_only(仅密码)模式下若从未设置任何密码,开启防护或维持该模式会把
|
|
125
|
+
// 管理员锁在登录墙外(无哈希可校验、密码登录被拒 → 进不去面板设密码 → 死锁)。
|
|
126
|
+
// 因此:无任何密码时禁止单独开启 enabled(除非本次同请求携带 password),
|
|
127
|
+
// 也禁止单独切换到 password_only(除非已设密码或本次带 password)。
|
|
128
|
+
const willHaveNoPassword = !authManager.hasPassword && !authManager.hasAdminPassword
|
|
129
|
+
&& (password === undefined || !password);
|
|
130
|
+
const nextMode = mode ?? authManager.mode;
|
|
131
|
+
const nextEnabled = enabled ?? authManager.enabled;
|
|
132
|
+
if (willHaveNoPassword && nextMode === 'password_only') {
|
|
133
|
+
if (nextEnabled) {
|
|
134
|
+
return fail('bad-request', '仅密码模式需要先设置访问密码:请先在下方「设置外部访客访问密码」处设置密码,再开启安全防护');
|
|
135
|
+
}
|
|
136
|
+
if (mode !== undefined) {
|
|
137
|
+
return fail('bad-request', '仅密码模式需要先设置访问密码:请先在下方「设置外部访客访问密码」处设置密码后再切换');
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
122
141
|
if (enabled != null) await authManager.setEnabled(enabled);
|
|
123
142
|
if (mode != null) await authManager.setMode(mode);
|
|
124
143
|
if (scope != null) await authManager.setScope(scope);
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wenbin_wb/dsh-bridge",
|
|
3
|
-
"version": "2.10.
|
|
3
|
+
"version": "2.10.5",
|
|
4
4
|
"description": "手机扫码即可在移动端/公网继续用 DeepSeek Harness,人不在电脑前也能接着干。一键局域网二维码、Cloudflare 公网隧道、自建隧道与微信 / QQ / 飞书 / Telegram Bot(多工作区/会话持久化/媒体/卡片审批/流式输出),无需自己搭公网服务器。",
|
|
5
|
-
"releaseNotes": "【v2.10.
|
|
5
|
+
"releaseNotes": "【v2.10.5】\n• 修复:「仅密码登录」模式在尚未设置密码时会把管理员锁在登录墙外(死锁:无密码 → 无法登录 → 进不去面板设密码)——现未设密码时放行让管理员完成初始化,设置后门禁才真正生效\n• 修复:未开启安全防护时设置面板没有访问密码输入框——现未设密码时始终显示密码设置区,避免开启后找不到设密码入口\n• 修复:UI 层切换到「仅密码」/ 开启安全防护时若尚未设密码,就地引导先设置访问密码(服务端同步守卫,双保险)",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "lib/index.js",
|
|
8
8
|
"exports": {
|