feat: one-line install for agents and probes from one shared script (curl | bash, zsh, PowerShell) [CI]
ci/woodpecker/push/main-amd64-build Pipeline was successful
ci/woodpecker/push/main-amd64-build Pipeline was successful
This commit is contained in:
1 parent
5528753d45
commit
af2962fc58
29 files changed
+558
-124
No files matched your search
@@ -6,7 +6,8 @@ import type { AgentDto, CreatedJoinTokenDto, JoinTokenDto, Page } from '@shared/
|
||||
import { fmtAgo, fmtTime } from '@shared/format'
|
||||
import { useQuery } from '@shared/hooks'
|
||||
import { NodeTag, useNodeInfos } from '@shared/nodes'
|
||||
import { Chip, CommandTabs, Confirm, CopyButton, Dialog, Dot, Empty, ErrorNotice, Field, guessPlatform, Segmented, Spinner, useAction } from '@shared/ui'
|
||||
import { Chip, Confirm, Dialog, Dot, Empty, ErrorNotice, Field, Segmented, Spinner, useAction } from '@shared/ui'
|
||||
import { JoinTokenCreated } from './JoinTokenCreated'
|
||||
|
||||
export const STATUS: Record<string, { label: string; tone: 'ok' | 'warn' | 'bad' }> = {
|
||||
active: { label: '已启用', tone: 'ok' },
|
||||
@@ -33,43 +34,7 @@ function AddAgentDialog({ onClose, onCreated }: { onClose: () => void; onCreated
|
||||
const [hours, setHours] = useState(24)
|
||||
const [created, setCreated] = useState<CreatedJoinTokenDto>()
|
||||
|
||||
if (created) {
|
||||
return (
|
||||
<Dialog
|
||||
title="加入令牌已创建"
|
||||
onClose={onClose}
|
||||
actions={
|
||||
<button type="button" className="btn primary" onClick={onClose}>
|
||||
完成
|
||||
</button>
|
||||
}
|
||||
>
|
||||
<div className="stack">
|
||||
<div className="notice warn">令牌明文只显示这一次,请现在就复制。</div>
|
||||
<Field label="在要加入的机器上运行">
|
||||
<CommandTabs
|
||||
storageKey="moshiro.install_tab"
|
||||
initial={guessPlatform()}
|
||||
tabs={[
|
||||
{ id: 'linux', label: 'Linux', command: created.install_commands.linux },
|
||||
{ id: 'macos', label: 'macOS', command: created.install_commands.macos },
|
||||
{ id: 'windows', label: 'Windows', command: created.install_commands.windows },
|
||||
]}
|
||||
/>
|
||||
</Field>
|
||||
<Field label="令牌">
|
||||
<pre className="code">{created.plaintext}</pre>
|
||||
<div>
|
||||
<CopyButton text={created.plaintext} label="复制令牌" />
|
||||
</div>
|
||||
</Field>
|
||||
<p className="muted">
|
||||
{created.token.auto_approve ? '使用这个令牌注册的 Agent 会自动启用。' : '使用这个令牌注册的 Agent 需要你在列表里审批之后才会启用。'} 可用 {created.token.max_uses} 次,{fmtTime(created.token.expires_at)} 过期。
|
||||
</p>
|
||||
</div>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
if (created) return <JoinTokenCreated created={created} kind="Agent" onClose={onClose} />
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
import type { CreatedJoinTokenDto } from '@shared/api/types'
|
||||
import { fmtTime } from '@shared/format'
|
||||
import { CommandTabs, CopyButton, Dialog, Field, guessPlatform } from '@shared/ui'
|
||||
|
||||
/**
|
||||
* 加入令牌创建之后的对话框:Agent 和 Probe 共用同一份,一行命令装好。
|
||||
* 命令里只有一次性代码,角色由代码决定;节点名字默认取主机名。
|
||||
*/
|
||||
export function JoinTokenCreated({ created, kind, onClose }: { created: CreatedJoinTokenDto; kind: 'Agent' | 'Probe'; onClose: () => void }) {
|
||||
const commands = created.install_commands
|
||||
return (
|
||||
<Dialog
|
||||
title="加入令牌已创建"
|
||||
onClose={onClose}
|
||||
actions={
|
||||
<button type="button" className="btn primary" onClick={onClose}>
|
||||
完成
|
||||
</button>
|
||||
}
|
||||
>
|
||||
<div className="stack">
|
||||
<div className="notice warn">命令里的代码只显示这一次,请现在就复制。</div>
|
||||
<Field
|
||||
label={`在要加入的机器上运行(${kind})`}
|
||||
hint="需要 Docker 或 Podman。命令里已经填好面板地址和一次性代码,节点名字默认取主机名;身份和配置存在命名卷里,重启、换新镜像重建容器都不用再给参数。名字想自己定,或者提示名字被占用,在命令里加上 MOSHIRO_NAME=名字(Linux 和 macOS 写在 bash 或 zsh 前面,Windows 先执行 $env:MOSHIRO_NAME='名字')。"
|
||||
>
|
||||
<CommandTabs
|
||||
storageKey="moshiro.install_tab"
|
||||
initial={guessPlatform()}
|
||||
tabs={[
|
||||
{ id: 'linux', label: 'Linux', command: commands.linux },
|
||||
{ id: 'macos', label: 'macOS', command: commands.macos },
|
||||
{ id: 'windows', label: 'Windows', command: commands.windows },
|
||||
]}
|
||||
/>
|
||||
</Field>
|
||||
<Field label="代码">
|
||||
<pre className="code">{created.plaintext}</pre>
|
||||
<div>
|
||||
<CopyButton text={created.plaintext} label="复制代码" />
|
||||
</div>
|
||||
</Field>
|
||||
<p className="muted">
|
||||
{kind === 'Agent' ? (created.token.auto_approve ? '使用它注册的 Agent 会自动启用。' : '使用它注册的 Agent 需要你在列表里审批之后才会启用。') : ''}
|
||||
可用 {created.token.max_uses} 次,{fmtTime(created.token.expires_at)} 过期。
|
||||
</p>
|
||||
</div>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
@@ -7,7 +7,8 @@ import { fmtAgo, fmtTime } from '@shared/format'
|
||||
import { useQuery } from '@shared/hooks'
|
||||
import { NodeTag, useNodeInfos } from '@shared/nodes'
|
||||
import { IPV6_STATES } from '@shared/ipVersions'
|
||||
import { Chip, CommandTabs, Confirm, CopyButton, Dialog, Empty, ErrorNotice, Field, guessPlatform, Spinner, useAction } from '@shared/ui'
|
||||
import { Chip, Confirm, Dialog, Empty, ErrorNotice, Field, Spinner, useAction } from '@shared/ui'
|
||||
import { JoinTokenCreated } from './JoinTokenCreated'
|
||||
|
||||
/**
|
||||
* 线路:哪个 Probe 连哪个 Agent。自动连接开着时所有 Probe 和 Agent 两两连,关着时只连手动选中的;
|
||||
@@ -96,41 +97,7 @@ function AddProbeDialog({ onClose, onCreated }: { onClose: () => void; onCreated
|
||||
const [hours, setHours] = useState(24)
|
||||
const [created, setCreated] = useState<CreatedJoinTokenDto>()
|
||||
|
||||
if (created) {
|
||||
return (
|
||||
<Dialog
|
||||
title="加入令牌已创建"
|
||||
onClose={onClose}
|
||||
actions={
|
||||
<button type="button" className="btn primary" onClick={onClose}>
|
||||
完成
|
||||
</button>
|
||||
}
|
||||
>
|
||||
<div className="stack">
|
||||
<div className="notice warn">令牌明文只显示这一次,请现在就复制。</div>
|
||||
<Field label="在家宽机器上运行" hint="把 <node-name> 换成这台 Probe 的名字,例如 home。Probe 会自己生成密钥并注册;Agent 列表和更新都由面板下发。">
|
||||
<CommandTabs
|
||||
storageKey="moshiro.install_tab"
|
||||
initial={guessPlatform()}
|
||||
tabs={[
|
||||
{ id: 'linux', label: 'Linux', command: created.install_commands.linux },
|
||||
{ id: 'macos', label: 'macOS', command: created.install_commands.macos },
|
||||
{ id: 'windows', label: 'Windows', command: created.install_commands.windows },
|
||||
]}
|
||||
/>
|
||||
</Field>
|
||||
<Field label="令牌">
|
||||
<pre className="code">{created.plaintext}</pre>
|
||||
<div>
|
||||
<CopyButton text={created.plaintext} label="复制令牌" />
|
||||
</div>
|
||||
</Field>
|
||||
<p className="muted">一次性令牌,{fmtTime(created.token.expires_at)} 过期。</p>
|
||||
</div>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
if (created) return <JoinTokenCreated created={created} kind="Probe" onClose={onClose} />
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
|
||||
@@ -953,14 +953,14 @@ export interface paths {
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/install.sh": {
|
||||
"/install": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
/** Installer for Linux nodes */
|
||||
/** One-line installer for Linux and macOS nodes (agent or probe, decided by the code) */
|
||||
get: operations["install_sh"];
|
||||
put?: never;
|
||||
post?: never;
|
||||
@@ -977,7 +977,7 @@ export interface paths {
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
/** Installer for Windows nodes */
|
||||
/** One-line installer for Windows nodes (agent or probe, decided by the code) */
|
||||
get: operations["install_ps1"];
|
||||
put?: never;
|
||||
post?: never;
|
||||
@@ -5115,15 +5115,24 @@ export interface operations {
|
||||
};
|
||||
install_sh: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
query?: {
|
||||
code?: string;
|
||||
};
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
/** @description Not Implemented */
|
||||
501: {
|
||||
/** @description OK */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content?: never;
|
||||
};
|
||||
/** @description Not Found */
|
||||
404: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
@@ -5135,15 +5144,24 @@ export interface operations {
|
||||
};
|
||||
install_ps1: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
query?: {
|
||||
code?: string;
|
||||
};
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
/** @description Not Implemented */
|
||||
501: {
|
||||
/** @description OK */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content?: never;
|
||||
};
|
||||
/** @description Not Found */
|
||||
404: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
|
||||
@@ -765,6 +765,7 @@ pre.code {
|
||||
font-size: 12.5px;
|
||||
line-height: 1.6;
|
||||
word-break: break-all;
|
||||
white-space: pre-wrap; /* 容器命令是多行的(行尾反斜杠续行) */
|
||||
user-select: all;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user