feat: the node itself requires an owner passkey (shared by agent and probe) and reports it; the panel derives the account and passkey it is bound to [CI]
This commit is contained in:
1 parent
8c37db1577
commit
2ae63653c4
23 files changed
+328
-10
No files matched your search
@@ -1,7 +1,7 @@
|
||||
import { Download, KeyRound, Plus, Trash2 } from 'lucide-react'
|
||||
import { useEffect, useMemo, useState, type ReactNode } from 'react'
|
||||
import { api } from '@shared/api/client'
|
||||
import type { AgentDto, ApiTokenDto, BuildInfoDto, NodeBinaryDto, CapabilityCatalogDto, MeDto, PasskeyDto, NodeUpdateDto, ReleaseDto, CreatedApiTokenDto, AuditDto, EventDto, Page, ProbeDto, RawChunkMetaDto, RecomputeDto, SettingDto, UserDto } from '@shared/api/types'
|
||||
import type { AgentDto, ApiTokenDto, BuildInfoDto, NodeBinaryDto, NodeOwnerDto, CapabilityCatalogDto, MeDto, PasskeyDto, NodeUpdateDto, ReleaseDto, CreatedApiTokenDto, AuditDto, EventDto, Page, ProbeDto, RawChunkMetaDto, RecomputeDto, SettingDto, UserDto } from '@shared/api/types'
|
||||
import { fmtBuiltAt, frontendBuild } from '@shared/buildInfo'
|
||||
import { fmtNum, fmtTime } from '@shared/format'
|
||||
import { translateSetting } from '@shared/i18n'
|
||||
@@ -192,6 +192,7 @@ function BuildRow({ name, kind, version, tag, commit, builtAt, platform, update
|
||||
<td className="muted">{platform || dash}</td>
|
||||
<td>{update ?? dash}</td>
|
||||
<td>{dash}</td>
|
||||
<td>{dash}</td>
|
||||
</tr>
|
||||
)
|
||||
}
|
||||
@@ -210,6 +211,23 @@ const UPDATE_STATE: Record<string, { text: string; tone: '' | 'ok' | 'warn' | 'b
|
||||
* 节点二进制的绑定:第一次握手成功时绑定它正在运行的二进制的哈希,之后对不上又不在 CI 签名的更新清单里就拒绝连接。
|
||||
* 「已签名」表示哈希出现在一份 CI 签名的清单里;否则只是首次握手时记下的。不符时可以确认当前的二进制是自己要的(比如手工升级了)。
|
||||
*/
|
||||
/**
|
||||
* 节点的属主:节点自己要求有属主(passkey),安装时固定在本地;面板顺着它报上来的凭证 id 反查出绑定在哪个账户的哪把 passkey 上。
|
||||
* 以后给节点更多接口和操作(web ssh、加密内容)时,只认这些 passkey。
|
||||
*/
|
||||
function OwnerCell({ owners }: { owners?: NodeOwnerDto[] | null }) {
|
||||
if (!owners || owners.length === 0) return <span className="faint">未报告</span>
|
||||
return (
|
||||
<span className="row" style={{ gap: 6, flexWrap: 'wrap' }}>
|
||||
{owners.map((o) => (
|
||||
<Chip key={o.credential_id} tone={o.passkey ? '' : 'warn'}>
|
||||
<span title={o.credential_id}>{o.passkey ? `${o.passkey}${o.account ? ` · ${o.account}` : ''}` : '未知凭证'}</span>
|
||||
</Chip>
|
||||
))}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
function BinaryCell({ binary, busy, onAccept }: { binary?: NodeBinaryDto | null; busy: boolean; onAccept: () => void }) {
|
||||
if (!binary) return <span className="faint">未绑定</span>
|
||||
const short = binary.sha256.slice(0, 10)
|
||||
@@ -293,6 +311,7 @@ function Versions() {
|
||||
<th>系统</th>
|
||||
<th>更新</th>
|
||||
<th>二进制</th>
|
||||
<th>属主</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -317,6 +336,9 @@ function Versions() {
|
||||
<td>
|
||||
<BinaryCell binary={n.binary} busy={busy} onAccept={() => void run(async () => { await api.del(`/api/admin/nodes/${n.id}/binary`); info.reload() }, '已清除绑定,节点下一次连接时重新绑定')} />
|
||||
</td>
|
||||
<td>
|
||||
<OwnerCell owners={n.owners} />
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
|
||||
Reference in new issue
Block a user