Replace Probe pages with directed Agent links, shared deployment controls and unified display data. Add node dragging, canvas pan and zoom, saved layouts, and per-direction link controls.
117 lines
7.5 KiB
TypeScript
117 lines
7.5 KiB
TypeScript
import type { CreatedJoinTokenDto } from '@shared/api/types'
|
||
import { fmtTime } from '@shared/format'
|
||
import { CommandTabs, CopyButton, Dialog, Field, guessPlatform, type CommandTab } from '@shared/ui'
|
||
|
||
/**
|
||
* 加入令牌创建之后的对话框:Agent 共用同一份。
|
||
* Podman 系(Linux)首选 Quadlet(Podman 4.6+):env、单元文件(核对哈希)、启动用户服务;单元 Pull=always,节点发现新版本后自己退出重启,所以默认自动更新。
|
||
* Docker(Linux、macOS、Windows)用 compose:把面板给的 .env 存下来,下载 compose.yaml,`compose up -d`;compose 不会自动更新,手动 `compose pull && compose up -d`。
|
||
* env/.env 里只有公开的值(面板地址、一次性码、信任根指纹和属主),没有任何私钥;一行安装脚本留作备用。
|
||
*/
|
||
export function JoinTokenCreated({ created, onClose }: { created: CreatedJoinTokenDto; onClose: () => void }) {
|
||
const quadlet = created.quadlet
|
||
const compose = created.compose
|
||
const script = created.install_commands
|
||
const composeTabs: CommandTab[] = [
|
||
{ id: 'linux', label: 'Linux · compose', command: compose.linux },
|
||
...(compose.macos ? [{ id: 'macos', label: 'macOS · Docker', command: compose.macos }] : []),
|
||
...(compose.windows ? [{ id: 'windows', label: 'Windows · Docker', command: compose.windows }] : []),
|
||
]
|
||
const initial = composeTabs.some((t) => t.id === guessPlatform()) ? guessPlatform() : 'linux'
|
||
return (
|
||
<Dialog
|
||
title="部署 Agent"
|
||
onClose={onClose}
|
||
actions={
|
||
<button type="button" className="btn primary" onClick={onClose}>
|
||
完成
|
||
</button>
|
||
}
|
||
>
|
||
<div className="stack">
|
||
<p className="muted">所有平台使用同一种 Agent,默认同时监听和拨号。加入后自动与其他可达 Agent 相互探测,可在线路矩阵中按方向调整。</p>
|
||
<div className="notice warn">一次性码只显示这一次,请现在就复制。下面只有公开的值(面板地址、一次性码、信任根指纹和属主),没有任何私钥。</div>
|
||
{script.openwrt && (
|
||
<Field
|
||
label="OpenWrt · 原生二进制(自动更新)"
|
||
hint="以 root 运行,支持 ARM64 和 x86_64,不需要 Docker/Podman。通过 curl 或 OpenWrt 自带的 uclient-fetch 下载,缺少依赖时自动用 opkg/apk 安装。动态 musl 正式包需 libopenssl3。自动注册属主并配置 procd;发布后自动验签、安装,失败回滚。默认限制 20 Mbps,可在 /etc/moshiro/agent.json 修改。"
|
||
>
|
||
<CommandTabs storageKey="moshiro.openwrt_tab" initial="openwrt" tabs={[{ id: 'openwrt', label: 'OpenWrt · procd', command: script.openwrt }]} />
|
||
</Field>
|
||
)}
|
||
<Field
|
||
label={`Linux · Podman(推荐,自动更新)`}
|
||
hint="Linux,Podman 4.6 及以上,用普通用户运行(rootless,不用 sudo)。命令把参数写进 ~/.config/moshiro/(权限 600),下载 Quadlet 单元并核对 SHA-256,一致才启动。退出登录后继续运行需要管理员执行一次 loginctl enable-linger 用户名。节点名字默认取主机名;想自己定,在 env 里加一行 MOSHIRO_NAME=名字(1–64 个字母、数字、点、下划线、横线)。"
|
||
>
|
||
<CommandTabs storageKey="moshiro.quadlet_tab" initial="linux" tabs={[{ id: 'linux', label: 'Linux · Podman Quadlet', command: quadlet.command }]} />
|
||
</Field>
|
||
<p className="muted">
|
||
自动更新默认开:面板发现新版本后,节点运行满 10 分钟就自己退出,Podman 重新启动时先拉最新镜像(<span className="mono">Pull=always</span>)。
|
||
想改成在管理端点「更新」才更新,把 env 里 <span className="mono"># MOSHIRO_UPDATE_POLICY=manual</span> 的 # 去掉。固定版本:把单元里的 <span className="mono">:latest</span> 换成版本号。
|
||
</p>
|
||
<Field
|
||
label="Linux · Docker Compose"
|
||
hint="需要 docker compose(或 podman compose)。命令建一个目录,把参数写进 .env(权限 600),下载 compose.yaml 并核对 SHA-256,一致才启动。compose 不会自动更新(容器自己没法重新拉取):手动 compose pull && compose up -d。"
|
||
>
|
||
<CommandTabs storageKey="moshiro.compose_tab" initial={initial} tabs={composeTabs} />
|
||
</Field>
|
||
<details>
|
||
<summary>分步做:Quadlet 的 env 和单元,compose 的 .env 和 compose.yaml</summary>
|
||
<div className="stack" style={{ marginTop: 8 }}>
|
||
<Field label="Quadlet env(存成 ~/.config/moshiro/ 下的 agent.env)">
|
||
<div className="row" style={{ alignItems: 'flex-start', gap: 8 }}>
|
||
<pre className="code" style={{ flex: 1, whiteSpace: 'pre-wrap', wordBreak: 'break-all', margin: 0 }}>{quadlet.env}</pre>
|
||
<CopyButton text={quadlet.env} />
|
||
</div>
|
||
</Field>
|
||
<Field label="Quadlet 单元(放进 ~/.config/containers/systemd/)">
|
||
<div className="stack">
|
||
<div className="row" style={{ gap: 8 }}>
|
||
<span className="mono" style={{ wordBreak: 'break-all' }}>{quadlet.url}</span>
|
||
<CopyButton text={quadlet.url} />
|
||
</div>
|
||
<div className="muted mono" style={{ wordBreak: 'break-all' }}>SHA-256 {quadlet.sha256}</div>
|
||
</div>
|
||
</Field>
|
||
<Field label=".env(compose:存成当前目录下的 .env)">
|
||
<div className="row" style={{ alignItems: 'flex-start', gap: 8 }}>
|
||
<pre className="code" style={{ flex: 1, whiteSpace: 'pre-wrap', wordBreak: 'break-all', margin: 0 }}>{compose.env}</pre>
|
||
<CopyButton text={compose.env} />
|
||
</div>
|
||
</Field>
|
||
<Field label="compose.yaml">
|
||
<div className="stack">
|
||
<div className="row" style={{ gap: 8 }}>
|
||
<span className="mono" style={{ wordBreak: 'break-all' }}>{compose.url}</span>
|
||
<CopyButton text={compose.url} />
|
||
</div>
|
||
<div className="muted mono" style={{ wordBreak: 'break-all' }}>SHA-256 {compose.sha256}</div>
|
||
</div>
|
||
</Field>
|
||
</div>
|
||
</details>
|
||
<p className="muted">
|
||
compose 更新:<span className="mono">podman compose pull && podman compose up -d</span>(Docker 把 podman 换成 docker);身份和配置在命名卷里,不会动。
|
||
注册成功之后,可以把 env 或 .env 里的 <span className="mono">MOSHIRO_JOIN</span> 一行删掉。
|
||
{created.token.auto_approve ? ' 使用它注册的 Agent 会自动启用。' : ' 使用它注册的 Agent 需要你在列表里审批之后才会启用。'}
|
||
可用 {created.token.max_uses} 次,{fmtTime(created.token.expires_at)} 过期。
|
||
</p>
|
||
<details>
|
||
<summary>一键安装脚本 · Linux / macOS / Windows</summary>
|
||
<div style={{ marginTop: 8 }}>
|
||
<CommandTabs
|
||
storageKey="moshiro.install_tab"
|
||
initial={guessPlatform()}
|
||
tabs={[
|
||
{ id: 'linux', label: 'Linux · 脚本', command: script.linux },
|
||
{ id: 'macos', label: 'macOS · 脚本', command: script.macos },
|
||
{ id: 'windows', label: 'Windows · 脚本', command: script.windows },
|
||
]}
|
||
/>
|
||
</div>
|
||
</details>
|
||
</div>
|
||
</Dialog>
|
||
)
|
||
}
|