@dshn/agent 0.1.4 → 0.1.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.
Files changed (2) hide show
  1. package/client.js +42 -38
  2. package/package.json +1 -1
package/client.js CHANGED
@@ -331,12 +331,12 @@ window.__ModuleLoader__.load({
331
331
  .dshn-err { color: var(--dsw-alias-state-error-primary, #e5484d); font-size: 11.5px; margin-bottom: 9px; }
332
332
  .dshn-hint { color: var(--dsw-alias-label-tertiary, #8b9099); font-size: 10.5px; margin-top: 3px; }
333
333
  .dshn-note { color: var(--dsw-alias-state-warn-primary, #d98324); font-size: 10.5px; margin: 2px 0 10px; }
334
- .dshn-adv { margin: 2px 0 12px; }
335
- .dshn-adv-toggle { border: 0; background: transparent; cursor: pointer; padding: 4px 0; font-size: 11.5px;
336
- color: var(--dsw-alias-label-tertiary, #8b9099); font-family: inherit; }
337
- .dshn-adv-toggle:hover { color: var(--dsw-alias-label-primary, #1c1e21); }
338
- .dshn-adv-body { display: flex; flex-direction: column; gap: 10px; margin-top: 6px; padding: 11px 12px; border-radius: 10px;
339
- border: 1px solid var(--dsw-alias-border-l2, rgba(128,134,142,.25)); background: var(--dsw-alias-bg-layer-2, #f4f5f7); }
334
+ .dshn-modeseg { display: flex; gap: 4px; padding: 3px; border-radius: 9px; background: var(--dsw-alias-bg-layer-2, #f4f5f7);
335
+ border: 1px solid var(--dsw-alias-border-l2, rgba(128,134,142,.22)); }
336
+ .dshn-modebtn { flex: 1; padding: 6px 10px; border: 0; border-radius: 6px; cursor: pointer; font-size: 12.5px; font-family: inherit;
337
+ background: transparent; color: var(--dsw-alias-label-secondary, #4a4f57); }
338
+ .dshn-modebtn.dshn-on { background: var(--dsw-alias-bg-layer-1, #fff); color: var(--dsw-alias-label-primary, #1c1e21);
339
+ box-shadow: 0 1px 2px rgba(0,0,0,.08); }
340
340
  .dshn-ca { min-height: 54px; resize: vertical; font-family: ui-monospace, Menlo, monospace; font-size: 11px; line-height: 1.4; }
341
341
  .dshn-e2e-box { margin: 4px 0 12px; padding: 11px 12px; border-radius: 10px;
342
342
  border: 1px solid var(--dsw-alias-border-l2, rgba(128,134,142,.25)); background: var(--dsw-alias-bg-layer-2, #f4f5f7); }
@@ -392,8 +392,8 @@ window.__ModuleLoader__.load({
392
392
  openSettings: '打开设置', open: '打开', addrLabel: '公网地址',
393
393
  dcTitle: '确认断开公网转发?', dcWarn: '断开会立即切断公网访问。云端不保存你的密码、无法找回;若你没有另存密码,之后可能无法用同一前缀重新连接。',
394
394
  dcConfirm: '确认断开', dcCancel: '取消', dcCopyFirst: '先复制密码',
395
- advanced: '高级 · 自托管中继', relayHost: '中继地址',
396
- relayHostHint: '自托管时填你自己的中继,如 wss://tunnel.example.com;留空即用默认。改动后点“连接/重新连接”生效。',
395
+ mode: '模式', modeOfficial: '官方 ds.hn', modeSelf: '自托管', yourDomain: '你的域名', relayHost: '中继地址',
396
+ relayHostHint: '填你自己的 @dshn/relay,如 wss://tunnel.example.com。子域会挂在它的域名下。',
397
397
  relayCa: '中继证书(自签名,可选)',
398
398
  relayCaHint: '仅当你的中继用自签名证书时:粘贴其 PEM 证书以固定信任(公有证书/套 Cloudflare 时留空)。' }
399
399
  : { brand: 'Public forwarding · ds.hn', connecting: 'connecting…', live: 'live', off: 'off', notset: 'not set up',
@@ -416,8 +416,8 @@ window.__ModuleLoader__.load({
416
416
  openSettings: 'open settings', open: 'open', addrLabel: 'Public address',
417
417
  dcTitle: 'Disconnect public forwarding?', dcWarn: 'This immediately cuts off public access. The cloud does not store your password and cannot recover it — if you have not saved it elsewhere, you may not be able to reconnect with the same prefix.',
418
418
  dcConfirm: 'Disconnect', dcCancel: 'Cancel', dcCopyFirst: 'Copy password first',
419
- advanced: 'Advanced · self-hosted relay', relayHost: 'Relay host',
420
- relayHostHint: 'For self-hosting, your own relay, e.g. wss://tunnel.example.com; blank uses the default. Connect / Reconnect to apply.',
419
+ mode: 'Mode', modeOfficial: 'Official ds.hn', modeSelf: 'Self-hosted', yourDomain: 'your-domain', relayHost: 'Relay host',
420
+ relayHostHint: 'Your own @dshn/relay, e.g. wss://tunnel.example.com. Your subdomain lives under its domain.',
421
421
  relayCa: 'Relay CA (self-signed, optional)',
422
422
  relayCaHint: 'Only when your relay uses a self-signed cert: paste its PEM to pin trust (leave blank for a public cert / behind Cloudflare).' }
423
423
 
@@ -494,11 +494,12 @@ window.__ModuleLoader__.load({
494
494
  const [e2eBusy, setE2eBusy] = react.useState(false)
495
495
  const [e2eErr, setE2eErr] = react.useState('')
496
496
  const [e2eMsg, setE2eMsg] = react.useState('')
497
- // Self-hosted relay overrides (advanced). Pre-filled from the saved settings.
497
+ // Mode: 'official' (ds.hn) vs 'selfhost' (your own @dshn/relay). Both share
498
+ // the subdomain + password + e2e; self-hosted just adds the relay location.
498
499
  const rs0 = s.relaySettings || {}
500
+ const [mode, setMode] = react.useState(rs0.relayHost ? 'selfhost' : 'official')
499
501
  const [relayHost, setRelayHost] = react.useState(rs0.relayHost || '')
500
502
  const [originCa, setOriginCa] = react.useState(rs0.originCa || '')
501
- const [showAdvanced, setShowAdvanced] = react.useState(!!rs0.relayHost)
502
503
 
503
504
  // When the saved passwords arrive from a later status poll, fill them once.
504
505
  react.useEffect(() => {
@@ -512,21 +513,21 @@ window.__ModuleLoader__.load({
512
513
  react.useEffect(() => {
513
514
  const rs = s.relaySettings
514
515
  if (!rs) return
515
- if (rs.relayHost && relayHost === '') { setRelayHost(rs.relayHost); setShowAdvanced(true) }
516
+ if (rs.relayHost && relayHost === '') { setRelayHost(rs.relayHost); setMode('selfhost') }
516
517
  if (rs.originCa && originCa === '') setOriginCa(rs.originCa)
517
518
  }, [s.relaySettings && s.relaySettings.relayHost])
518
519
 
519
520
  // The domain suffix reflects the self-hosted relay AS YOU TYPE it (a
520
521
  // client-side mirror of the server's apex derivation), so the preview shows
521
- // YOUR domain not the default ds.hn before you've connected. Empty →
522
- // the server's current apex.
522
+ // YOUR domain before connecting. Official mode the server's apex (ds.hn).
523
523
  const apex = (() => {
524
+ if (mode !== 'selfhost') return s.apex || 'ds.hn'
524
525
  const rh = relayHost.trim()
525
- if (rh === '') return s.apex || 'ds.hn'
526
+ if (rh === '') return T.yourDomain
526
527
  const bare = rh.replace(/^wss?:\/\//, '').replace(/:\d+$/, '').replace(/\/.*$/, '')
527
528
  const parts = bare.split('.')
528
529
  if (parts.length > 2 && (parts[0] === 'relay' || parts[0] === 'origin')) return parts.slice(1).join('.')
529
- return bare || (s.apex || 'ds.hn')
530
+ return bare || T.yourDomain
530
531
  })()
531
532
 
532
533
  const copyText = (text, tag) => {
@@ -551,7 +552,8 @@ window.__ModuleLoader__.load({
551
552
  // whole thing is one flow. Once configured, e2e moves to its own dedicated
552
553
  // control below (its own apply button, independent of the connection).
553
554
  const e2eRegOk = configured || e2e.trim() === '' || e2e.trim().length >= MIN_PW
554
- const canSubmit = prefix.trim().length > 0 && !busy && e2eRegOk
555
+ const selfhostOk = mode !== 'selfhost' || relayHost.trim() !== ''
556
+ const canSubmit = prefix.trim().length > 0 && !busy && e2eRegOk && selfhostOk
555
557
  && (configured ? pw.length >= MIN_PW : (st.ok && confirm.length > 0 && matches))
556
558
 
557
559
  const submit = () => {
@@ -559,7 +561,8 @@ window.__ModuleLoader__.load({
559
561
  setBusy(true); setErr('')
560
562
  const e2eVal = e2e.trim()
561
563
  fetch('/dshn/configure', { method: 'POST', headers: { 'content-type': 'application/json' },
562
- body: JSON.stringify({ subdomain: prefix.trim().toLowerCase(), password: pw, relayHost: relayHost.trim(), originCa: originCa.trim() }) })
564
+ body: JSON.stringify({ subdomain: prefix.trim().toLowerCase(), password: pw,
565
+ relayHost: mode === 'selfhost' ? relayHost.trim() : '', originCa: mode === 'selfhost' ? originCa.trim() : '' }) })
563
566
  .then((r) => r.json().then((j) => ({ ok: r.ok, j })))
564
567
  .then(async ({ ok, j }) => {
565
568
  if (!ok) { setBusy(false); setErr((j && j.error) || 'failed'); return }
@@ -620,10 +623,29 @@ window.__ModuleLoader__.load({
620
623
 
621
624
  liveErr ? h('div', { className: 'dshn-err' }, liveErr) : null,
622
625
 
626
+ // Mode: official ds.hn vs self-hosted. The subdomain + password + e2e
627
+ // below are shared; self-hosted just adds where your own relay lives.
628
+ h('div', { className: 'dshn-field' },
629
+ h('span', null, T.mode),
630
+ h('div', { className: 'dshn-modeseg' },
631
+ h('button', { type: 'button', className: 'dshn-modebtn' + (mode === 'official' ? ' dshn-on' : ''), onClick: () => setMode('official') }, T.modeOfficial),
632
+ h('button', { type: 'button', className: 'dshn-modebtn' + (mode === 'selfhost' ? ' dshn-on' : ''), onClick: () => setMode('selfhost') }, T.modeSelf))),
633
+
634
+ mode === 'selfhost' ? h('label', { className: 'dshn-field' },
635
+ h('span', null, T.relayHost),
636
+ h('input', { className: 'dshn-input', value: relayHost, placeholder: 'wss://tunnel.example.com',
637
+ autoComplete: 'off', spellCheck: false, autoFocus: !configured, onChange: (ev) => setRelayHost(ev.target.value) }),
638
+ h('div', { className: 'dshn-hint' }, T.relayHostHint)) : null,
639
+ mode === 'selfhost' ? h('label', { className: 'dshn-field' },
640
+ h('span', null, T.relayCa),
641
+ h('textarea', { className: 'dshn-input dshn-ca', value: originCa, rows: 3, spellCheck: false,
642
+ placeholder: '-----BEGIN CERTIFICATE-----', onChange: (ev) => setOriginCa(ev.target.value) }),
643
+ h('div', { className: 'dshn-hint' }, T.relayCaHint)) : null,
644
+
623
645
  h('label', { className: 'dshn-field' },
624
646
  h('span', null, T.prefix),
625
647
  h('div', { className: 'dshn-prefixwrap' },
626
- h('input', { className: 'dshn-input', value: prefix, placeholder: 'alice', autoFocus: !configured,
648
+ h('input', { className: 'dshn-input', value: prefix, placeholder: 'alice', autoFocus: !configured && mode !== 'selfhost',
627
649
  onChange: (e) => setPrefix(e.target.value.toLowerCase()) }),
628
650
  h('span', { className: 'dshn-apex' }, '.' + apex)),
629
651
  !configured ? h('div', { className: 'dshn-hint' }, T.prefixHint) : null),
@@ -697,24 +719,6 @@ window.__ModuleLoader__.load({
697
719
  h('div', { className: 'dshn-hint', style: { marginTop: '7px' } }, T.e2eHint + ' ' + T.e2eIndep))
698
720
  })() : null,
699
721
 
700
- // Advanced — self-hosted relay. Point the agent at your own @dshn/relay
701
- // instead of the default ds.hn. Collapsed unless a custom relay is already
702
- // set; applied on Connect/Reconnect (changing it reconnects the tunnel).
703
- h('div', { className: 'dshn-adv' },
704
- h('button', { type: 'button', className: 'dshn-adv-toggle', onClick: () => setShowAdvanced(!showAdvanced) },
705
- (showAdvanced ? '▾ ' : '▸ ') + T.advanced),
706
- showAdvanced ? h('div', { className: 'dshn-adv-body' },
707
- h('label', { className: 'dshn-field' },
708
- h('span', null, T.relayHost),
709
- h('input', { className: 'dshn-input', value: relayHost, placeholder: s.defaultRelayHost || 'relay.ds.hn',
710
- autoComplete: 'off', spellCheck: false, onChange: (ev) => setRelayHost(ev.target.value) }),
711
- h('div', { className: 'dshn-hint' }, T.relayHostHint)),
712
- h('label', { className: 'dshn-field' },
713
- h('span', null, T.relayCa),
714
- h('textarea', { className: 'dshn-input dshn-ca', value: originCa, rows: 3, spellCheck: false,
715
- placeholder: '-----BEGIN CERTIFICATE-----', onChange: (ev) => setOriginCa(ev.target.value) }),
716
- h('div', { className: 'dshn-hint' }, T.relayCaHint))) : null),
717
-
718
722
  // Disconnecting severs public access and the password is unrecoverable
719
723
  // from the cloud — so it takes an explicit, spelled-out confirmation.
720
724
  confirmDc ? h('div', { className: 'dshn-dcwarn' },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dshn/agent",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "Forward a local dsh web service to the public internet over ds.hn (bundled).",
5
5
  "keywords": [
6
6
  "dsh",