import { useState, useEffect, useCallback } from 'react'; import { json, type ActionFunction, type LoaderFunction } from '@remix-run/node'; import { useLoaderData, useSubmit, useNavigation } from '@remix-run/react'; import { spawn, type ChildProcess } from 'child_process'; type SshxStatus = 'running' | 'stopped'; interface LoaderData { status: SshxStatus; output: string; link: string | null; shell: string | null; } let sshxProcess: ChildProcess | null = null; let sshxOutput = ''; const extractFromOutput = (output: string, regex: RegExp): string | null => { const match = output.match(regex); return match ? match[1] : null; }; const handleProcessOutput = (data: Buffer) => { sshxOutput += data.toString(); }; const handleProcessClose = () => { sshxProcess = null; sshxOutput += '\nSSHX进程已结束。'; }; export const loader: LoaderFunction = async () => { const status: SshxStatus = sshxProcess ? 'running' : 'stopped'; const link = extractFromOutput(sshxOutput, /Link:\s+(https:\/\/sshx\.io\/s\/[^\s]+)/); const shell = extractFromOutput(sshxOutput, /Shell:\s+([^\n]+)/); return json({ status, output: sshxOutput, link, shell }); }; export const action: ActionFunction = async ({ request }) => { const formData = await request.formData(); const action = formData.get('action'); if (action === 'start' && !sshxProcess) { sshxProcess = spawn('/home/pn/sshx/sshx', ['-q']); // 修改这里,添加 '-q' 参数 sshxProcess.stdout?.on('data', handleProcessOutput); sshxProcess.stderr?.on('data', handleProcessOutput); sshxProcess.on('close', handleProcessClose); return json({ status: 'started' }); } else if (action === 'stop' && sshxProcess) { sshxProcess.kill(); handleProcessClose(); return json({ status: 'stopped' }); } return json({ error: '无效操作' }, { status: 400 }); }; export default function Sshx() { const { status, output, link, shell } = useLoaderData(); const submit = useSubmit(); const navigation = useNavigation(); const [localOutput, setLocalOutput] = useState(output); const [startTime, setStartTime] = useState(null); const refreshData = useCallback(() => { submit(null, { method: 'get', replace: true }); }, [submit]); const stopSshx = useCallback(() => { submit({ action: 'stop' }, { method: 'post' }); setStartTime(null); }, [submit]); useEffect(() => { let interval: NodeJS.Timeout | null = null; let timer: NodeJS.Timeout | null = null; if (status === 'running') { interval = setInterval(refreshData, 60000); // 每60秒更新一次 if (startTime === null) { setStartTime(Date.now()); } else { const elapsedTime = Date.now() - startTime; const remainingTime = 600000 - elapsedTime; // 10分钟 = 600000毫秒 if (remainingTime > 0) { timer = setTimeout(stopSshx, remainingTime); } else { stopSshx(); } } } else { setStartTime(null); } return () => { if (interval) clearInterval(interval); if (timer) clearTimeout(timer); }; }, [refreshData, status, startTime, stopSshx]); useEffect(() => { setLocalOutput(output); }, [output]); const isLoading = navigation.state === 'submitting' || navigation.state === 'loading'; const handleAction = (action: 'start' | 'stop') => { if (action === 'start') { setStartTime(Date.now()); } else { setStartTime(null); } submit({ action }, { method: 'post' }); setTimeout(refreshData, 100); }; // 计算剩余时间 const remainingTime = startTime ? Math.max(0, 600 - Math.floor((Date.now() - startTime) / 1000)) : 0; return (

SSHX控制面板

状态: {status === 'running' ? '运行中' : '已停止'}

{status === 'running' && (

链接:{' '} {link ? ( {link} ) : ( 暂不可用 )}

Shell: {shell || '暂不可用'}

剩余时间: {remainingTime} 秒

)}

输出:

        {localOutput}
      
); }