Files
MoShiro/web/admin/src/pages/JoinTokenCreated.tsx
T
justme 15f0023614 web: unify Agent management and add a draggable link board
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.
2026-10-21 11:55:50 +08:00

117 lines
7.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 &amp;&amp; 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>
)
}