'use client'; import { useEffect, useMemo, useState } from 'react'; import { useQuery } from '@tanstack/react-query'; import { Copy } from 'lucide-react'; import { toast } from 'sonner'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from '@/components/ui/card'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { type NodeItem, StatusService } from '@/lib/services/openflare'; import { buildRelayDockerInstallCommand, buildRelayInstallCommand, buildTunnelDockerInstallCommand, buildTunnelInstallCommand, getServerUrl, } from './node-utils'; type InstallVariant = 'relay' | 'tunnel'; const variantMeta: Record< InstallVariant, { title: string; description: string; tokenLabel: string; scriptLabel: string; dockerLabel: string; } > = { relay: { title: '中继部署命令', description: '使用 Discovery Token 将 frps 中继节点接入控制端。', tokenLabel: 'Discovery Token', scriptLabel: '一键脚本部署 (Linux / macOS)', dockerLabel: 'Docker 容器部署', }, tunnel: { title: '隧道部署命令', description: '使用 Tunnel Token 将 openflared 客户端接入控制端。', tokenLabel: 'Tunnel Token', scriptLabel: '一键脚本部署 (Linux / macOS)', dockerLabel: 'Docker 容器部署', }, }; export function InstallCommand({ node, variant, }: { node: NodeItem; variant: InstallVariant; }) { const [serverUrl, setServerUrl] = useState(''); const meta = variantMeta[variant]; const statusQuery = useQuery({ queryKey: ['openflare', 'public-status'], queryFn: () => StatusService.getPublicStatus(), staleTime: 5 * 60 * 1000, }); const serverVersion = statusQuery.data?.version; useEffect(() => { if (typeof window !== 'undefined' && !serverUrl) { setServerUrl(window.location.origin); } }, [serverUrl]); const normalizedServerUrl = getServerUrl(serverUrl); const scriptCommand = useMemo(() => { if (!normalizedServerUrl || !node.access_token) { return ''; } return variant === 'relay' ? buildRelayInstallCommand(normalizedServerUrl, node.access_token) : buildTunnelInstallCommand(normalizedServerUrl, node.access_token); }, [normalizedServerUrl, node.access_token, variant]); const dockerCommand = useMemo(() => { if (!normalizedServerUrl || !node.access_token) { return ''; } return variant === 'relay' ? buildRelayDockerInstallCommand( normalizedServerUrl, node.access_token, serverVersion, ) : buildTunnelDockerInstallCommand( normalizedServerUrl, node.access_token, serverVersion, ); }, [normalizedServerUrl, node.access_token, variant, serverVersion]); const handleCopy = async (value: string, message: string) => { try { await navigator.clipboard.writeText(value); toast.success(message); } catch { toast.error('复制失败,请手动选择命令文本。'); } }; return (
{meta.title} {meta.description}
{scriptCommand ? ( ) : null} {dockerCommand ? ( ) : null}

Node ID

{node.node_id}

{meta.tokenLabel}

{node.access_token || '暂无'}

setServerUrl(event.target.value)} placeholder='https://openflare.example.com' />

部署脚本和容器将使用此地址连接 OpenFlare 控制端。

{!node.access_token ? (

节点尚未生成接入 Token,请稍后刷新。

) : !normalizedServerUrl ? (

请填写有效的 Server URL 以生成部署命令。

) : ( <>

{meta.scriptLabel}

                {scriptCommand}
              

{meta.dockerLabel}

                {dockerCommand}
              
)}
); }