@hyzyn/dsh-docker 0.7.1 → 0.7.3

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.
@@ -1981,6 +1981,19 @@
1981
1981
  /* 删除按钮靠右,不跟着列宽被拉长 */
1982
1982
  .dk_targetRow > .dk_btnDanger { justify-self: end; }
1983
1983
 
1984
+ /*
1985
+ * 失效的连接簿引用(引用的条目名不在 tty 连接簿里):标黄整行 + 左侧色条。
1986
+ * 为什么必须可见:book 下拉的候选项来自 ttyBooks,失效的名字没有对应 option,
1987
+ * 下拉会渲染成**空白**——界面上看不出错,直到真去连才报「引用的连接簿条目不存在」。
1988
+ */
1989
+ .dk_targetRow[data-stale] {
1990
+ border-color: var(--dk-warn);
1991
+ box-shadow: inset 2px 0 0 var(--dk-warn);
1992
+ }
1993
+ .dk_hintWarn {
1994
+ color: var(--dk-warn);
1995
+ }
1996
+
1984
1997
  .dk_targetInline {
1985
1998
  display: grid;
1986
1999
  grid-column: 1 / -1;
@@ -17,7 +17,7 @@
17
17
  * (宿主只回 passwordSet / passphraseSet 布尔)。
18
18
  */
19
19
  import dockerCss from './docker.css'
20
- import { bookSessionHost, pickTargetByHost, sessionHostPort } from './session-target.js'
20
+ import { bookSessionHost, pickTargetByHost, sessionHostPort, staleBookRef } from './session-target.js'
21
21
  import { currentSessionIdOf } from './current-session.js'
22
22
 
23
23
  const API = '/api/dsh-docker'
@@ -5958,7 +5958,15 @@ window.__ModuleLoader__.load({
5958
5958
  jsx('span', { className: 'dk_hint', children: 'docker socket 等价于目标主机的 root 权限。开启后,浏览器面板与 agent 都能执行对应操作,请只在可信环境下打开。' }),
5959
5959
 
5960
5960
  sectionTitle('目标'),
5961
- ...form.targets.map((item, index) => jsxs('div', { className: 'dk_targetRow', children: [
5961
+ ...form.targets.map((item, index) => {
5962
+ /*
5963
+ * 失效引用(引用的条目名不在 tty 连接簿里):下拉会**渲染成空白**——没有任何
5964
+ * option 与 item.book 匹配,界面上看不出"这里引用错了",要等真去连才报错。
5965
+ * 这里两件事一起做:给那一行标记 data-stale(标黄),并把失效的名字补成一个
5966
+ * 显式 option,免得用户看到一个空白下拉、以为是"没选"。
5967
+ */
5968
+ const stale = staleBookRef(item, form.ttyBooks)
5969
+ return jsxs('div', { className: 'dk_targetRow', 'data-stale': stale !== undefined ? '1' : undefined, children: [
5962
5970
  jsx('input', { className: 'dk_input', value: item.name, placeholder: '目标名', onChange: (event) => patchTarget(index, { name: event.target.value }) }),
5963
5971
  jsx('select', { className: 'dk_select', value: item.kind, onChange: (event) => patchTarget(index, { kind: event.target.value }), children: [
5964
5972
  jsx('option', { value: 'local', children: '本机' }),
@@ -5971,11 +5979,17 @@ window.__ModuleLoader__.load({
5971
5979
  className: 'dk_select',
5972
5980
  value: item.book ?? '',
5973
5981
  onChange: (event) => patchTarget(index, { book: event.target.value }),
5982
+ title: stale !== undefined ? `引用的连接簿条目「${stale}」不存在——请改选一个已有条目,或清空改为手填` : undefined,
5974
5983
  children: [
5975
5984
  jsx('option', { value: '', children: form.ttyBooks.length === 0 ? '(无 tty 连接簿,请填内联信息)' : '(不用连接簿,手填)' }),
5985
+ // 失效的名字排在最前并显式标注:否则它没有对应 option,下拉显示空白
5986
+ ...(stale !== undefined ? [jsx('option', { value: stale, children: '⚠ 条目已不存在:' + stale }, stale)] : []),
5976
5987
  ...form.ttyBooks.map((name) => jsx('option', { value: name, children: '连接簿:' + name }, name)),
5977
5988
  ],
5978
5989
  }),
5990
+ stale !== undefined
5991
+ ? jsx('span', { className: 'dk_hint dk_hintWarn', children: `引用的条目「${stale}」不在 tty 连接簿里——请改选,或清空后手填` })
5992
+ : null,
5979
5993
  ] }),
5980
5994
  jsx('button', { type: 'button', className: 'dk_btn dk_btnDanger', onClick: () => removeTarget(index), children: '删除' }),
5981
5995
  /*
@@ -5995,7 +6009,8 @@ window.__ModuleLoader__.load({
5995
6009
  (item.auth ?? 'agent') === 'password' ? jsx('input', { className: 'dk_input dk_credential', type: 'password', placeholder: item.passwordSet === true ? '(已设置,留空保持不变)' : 'env:SSH_PASSWORD', value: item.password ?? '', onChange: (event) => patchTarget(index, { password: event.target.value }) }) : null,
5996
6010
  jsx('label', { className: 'dk_check', children: [jsx('input', { type: 'checkbox', checked: item.agentForward === true, onChange: (event) => patchTarget(index, { agentForward: event.target.checked }) }), 'agent forwarding'] }),
5997
6011
  ] }) : null,
5998
- ] }, String(index) + item.name)),
6012
+ ] }, String(index))
6013
+ }),
5999
6014
  jsxs('div', { className: 'dk_row', children: [
6000
6015
  jsx('button', { type: 'button', className: 'dk_btn', onClick: addTarget, children: '添加目标' }),
6001
6016
  jsx('span', { className: 'dk_hint', children: 'SSH 目标推荐直接选 tty 终端面板的连接簿条目(凭证只需维护一处);手填时密码 / 口令建议写 env:NAME(凭据引用:由官方凭据存储解析,缺失时退回环境变量)。' }),
@@ -90,3 +90,33 @@ export function bookSessionHost(bookName, rows) {
90
90
  }
91
91
  return undefined
92
92
  }
93
+
94
+ /**
95
+ * 目标引用的连接簿条目是否**已失效**(引用的名字在 tty 连接簿里不存在)。
96
+ *
97
+ * 为什么需要它:`book` 那个下拉的候选项来自 `ttyBooks`,一旦引用的名字不在其中,
98
+ * 下拉会**渲染成空白**(没有任何 option 与之匹配)——界面上完全看不出"这里引用错了",
99
+ * 直到真的去连才报「引用的连接簿条目不存在」。用户改了 tty 侧条目名(改名 / 重装 /
100
+ * 换机器)之后就会落到这个状态,且无从发现。
101
+ *
102
+ * 判定只针对 **ssh + 有 book** 的目标:本机目标不看连接簿;没填 book(走内联
103
+ * host/username)也不算失效——那是另一种合法配置。
104
+ *
105
+ * @param target 配置里的目标条目
106
+ * @param ttyBooks `/config.ttyBooks`:tty 连接簿的条目名数组
107
+ * @returns 引用的条目名(失效时),否则 undefined
108
+ */
109
+ export function staleBookRef(target, ttyBooks) {
110
+ if (target === null || typeof target !== 'object') return undefined
111
+ if (target.kind !== 'ssh') return undefined
112
+ const book = typeof target.book === 'string' ? target.book.trim() : ''
113
+ if (book === '') return undefined
114
+ const books = Array.isArray(ttyBooks) ? ttyBooks : []
115
+ // tty 未安装 / 连接簿为空时**不算**引用失效:那是"宿主没装 tty"或"条目还没建",
116
+ // 与"引用了一个不存在的名字"是两回事,标黄会误导(此时整体提示已在卡片顶部给过)。
117
+ if (books.length === 0) return undefined
118
+ for (const name of books) {
119
+ if (name === book) return undefined
120
+ }
121
+ return book
122
+ }