dsh-remote-panel 0.1.0 → 0.1.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/deploy/templates/Caddyfile.tmpl +17 -3
- package/lib/client.js +38 -10
- package/lib/index.js +1 -1
- package/lib/pair.js +11 -2
- package/package.json +1 -1
|
@@ -16,6 +16,14 @@
|
|
|
16
16
|
# 第一层:来源白名单
|
|
17
17
|
@lan remote_ip 127.0.0.1 ::1 @@LAN_CIDR@@ @@VIRTUAL_IP@@
|
|
18
18
|
handle @lan {
|
|
19
|
+
# 配对入口:路径匹配(可靠),返回同源页面做一次「同站跳转」。
|
|
20
|
+
# 跨站导航不会带 SameSite=Strict 的 DSH Cookie,必须由同源页面自己发起。
|
|
21
|
+
handle /pair {
|
|
22
|
+
header Set-Cookie "dsh_gate=@@ACCESS_KEY@@; Max-Age=31536000; Path=/; HttpOnly; SameSite=Lax"
|
|
23
|
+
header Content-Type "text/html; charset=utf-8"
|
|
24
|
+
respond `<!doctype html><meta charset="utf-8"><title>正在连接…</title><script>const t=new URLSearchParams(location.search).get("token");location.replace(t?"/?token="+encodeURIComponent(t):"/")</script>` 200
|
|
25
|
+
}
|
|
26
|
+
|
|
19
27
|
# 诊断用:不经过任何认证
|
|
20
28
|
handle /healthz {
|
|
21
29
|
respond "ok" 200
|
|
@@ -26,9 +34,15 @@
|
|
|
26
34
|
handle @key {
|
|
27
35
|
# Cookie 值就是口令本身(24 位随机),而不是常量:常量能被手工伪造
|
|
28
36
|
header Set-Cookie "dsh_gate=@@ACCESS_KEY@@; Max-Age=31536000; Path=/; HttpOnly; SameSite=Lax"
|
|
29
|
-
|
|
30
|
-
#
|
|
31
|
-
|
|
37
|
+
|
|
38
|
+
# 一张二维码走完两步:带 token 时转发到令牌地址,不带则回首页
|
|
39
|
+
@withToken query token=*
|
|
40
|
+
handle @withToken {
|
|
41
|
+
redir * /?token={query.token} 303
|
|
42
|
+
}
|
|
43
|
+
handle {
|
|
44
|
+
redir * / 303
|
|
45
|
+
}
|
|
32
46
|
}
|
|
33
47
|
|
|
34
48
|
# Caddy 默认没有 cookie 匹配器,用 Header 通配匹配替代
|
package/lib/client.js
CHANGED
|
@@ -651,12 +651,19 @@ window.__ModuleLoader__.load({
|
|
|
651
651
|
|
|
652
652
|
function PairCard() {
|
|
653
653
|
const [state, setState] = useState({ kind: 'loading' })
|
|
654
|
+
const [round, setRound] = useState(0)
|
|
655
|
+
|
|
656
|
+
// DSH 每次重启都会换令牌;面板开着时自动刷新,避免扫到过期的码
|
|
657
|
+
useEffect(() => {
|
|
658
|
+
const timer = setInterval(() => setRound((value) => value + 1), 60000)
|
|
659
|
+
return () => clearInterval(timer)
|
|
660
|
+
}, [])
|
|
654
661
|
|
|
655
662
|
useEffect(() => {
|
|
656
663
|
callApi('/pair')
|
|
657
664
|
.then((data) => setState({ kind: 'ok', data }))
|
|
658
665
|
.catch((error) => setState({ kind: 'error', message: error.message }))
|
|
659
|
-
}, [])
|
|
666
|
+
}, [round])
|
|
660
667
|
|
|
661
668
|
const data = state.kind === 'ok' ? state.data : null
|
|
662
669
|
return jsxs(Card, {
|
|
@@ -667,6 +674,7 @@ window.__ModuleLoader__.load({
|
|
|
667
674
|
jsx(Dot, { ok: data?.found === true }),
|
|
668
675
|
jsx('strong', { style: styles.title, children: '手机配对' }),
|
|
669
676
|
jsx('span', { style: styles.muted, children: state.kind === 'loading' ? '读取中…' : data?.found ? '扫码即可' : '未找到令牌' }),
|
|
677
|
+
jsx('button', { type: 'button', style: styles.button, onClick: () => setRound((value) => value + 1), children: '刷新' }),
|
|
670
678
|
],
|
|
671
679
|
}),
|
|
672
680
|
state.kind === 'error' && jsx('p', { style: styles.muted, children: state.message }),
|
|
@@ -685,20 +693,40 @@ window.__ModuleLoader__.load({
|
|
|
685
693
|
jsxs('div', {
|
|
686
694
|
style: { display: 'flex', flexDirection: 'column', gap: 6, flex: 1, minWidth: 210 },
|
|
687
695
|
children: [
|
|
688
|
-
jsx('span', { style: styles.hint, children: '
|
|
696
|
+
jsx('span', { style: styles.hint, children: '扫这一张即可:先种门禁(一年),再自动跳到令牌' }),
|
|
697
|
+
// 二维码编的就是这条组合链接,显示同一个值,别再换成别的
|
|
689
698
|
jsx('code', {
|
|
690
|
-
style: { ...styles.code, maxHeight:
|
|
691
|
-
children: data.urls.
|
|
699
|
+
style: { ...styles.code, maxHeight: 72 },
|
|
700
|
+
children: data.urls.combo ?? data.urls.token,
|
|
692
701
|
}),
|
|
693
|
-
jsx('span', { style: styles.hint, children: '② 令牌(30 天 Cookie)' }),
|
|
694
|
-
jsx('code', { style: { ...styles.code, maxHeight: 56 }, children: data.urls.token }),
|
|
695
702
|
jsxs('div', {
|
|
696
703
|
style: styles.row,
|
|
697
704
|
children: [
|
|
698
|
-
jsx(CopyButton, { text: data.urls.token, label: '
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
705
|
+
jsx(CopyButton, { text: data.urls.combo ?? data.urls.token, label: '复制配对链接' }),
|
|
706
|
+
],
|
|
707
|
+
}),
|
|
708
|
+
jsx('details', {
|
|
709
|
+
children: [
|
|
710
|
+
jsx('summary', { style: styles.hint, children: '手动备用(门禁 / 令牌分开用)' }),
|
|
711
|
+
jsxs('div', {
|
|
712
|
+
style: { display: 'flex', flexDirection: 'column', gap: 6, marginTop: 6 },
|
|
713
|
+
children: [
|
|
714
|
+
jsx('code', {
|
|
715
|
+
style: { ...styles.code, maxHeight: 56 },
|
|
716
|
+
children: data.urls.gate ?? '(回填码未含口令)',
|
|
717
|
+
}),
|
|
718
|
+
jsx('code', { style: { ...styles.code, maxHeight: 56 }, children: data.urls.token }),
|
|
719
|
+
jsxs('div', {
|
|
720
|
+
style: styles.row,
|
|
721
|
+
children: [
|
|
722
|
+
jsx(CopyButton, { text: data.urls.token, label: '复制令牌链接' }),
|
|
723
|
+
data.urls.gate
|
|
724
|
+
? jsx(CopyButton, { text: data.urls.gate, label: '复制门禁链接' })
|
|
725
|
+
: null,
|
|
726
|
+
],
|
|
727
|
+
}),
|
|
728
|
+
],
|
|
729
|
+
}),
|
|
702
730
|
],
|
|
703
731
|
}),
|
|
704
732
|
],
|
package/lib/index.js
CHANGED
package/lib/pair.js
CHANGED
|
@@ -92,9 +92,18 @@ export async function findToken(options) {
|
|
|
92
92
|
*/
|
|
93
93
|
export function pairingUrls({ ip, port, accessKey, token }) {
|
|
94
94
|
const base = `http://${ip}:${port}`
|
|
95
|
+
const gate = accessKey === undefined ? undefined : `${base}/?k=${accessKey}`
|
|
96
|
+
const tokenUrl = token === undefined ? undefined : `${base}/?token=${token}`
|
|
95
97
|
return {
|
|
96
|
-
gate
|
|
97
|
-
token:
|
|
98
|
+
gate,
|
|
99
|
+
token: tokenUrl,
|
|
100
|
+
// 一张二维码走完两步:先种门禁 Cookie,再由 Caddy 转发到令牌地址
|
|
101
|
+
// 走专用路径:Caddy 会回一个同源页面,由它发起跳转——
|
|
102
|
+
// 跨站导航不会携带 SameSite=Strict 的 DSH Cookie,非要同源页面自己发起才行
|
|
103
|
+
combo:
|
|
104
|
+
accessKey !== undefined && token !== undefined
|
|
105
|
+
? `${base}/pair?k=${accessKey}&token=${token}`
|
|
106
|
+
: undefined,
|
|
98
107
|
}
|
|
99
108
|
}
|
|
100
109
|
|