Files
live-hub-py/web/frontend/src/pages/ProxyPage.tsx
T
2026-06-22 14:21:39 +08:00

191 lines
6.1 KiB
TypeScript

import { useEffect, useState, useRef } from 'react';
import { Form, Input, Switch, Button, Card, message, Row, Col } from 'antd';
import { proxyApi } from '../api/modules';
const WS_BASE = `ws://${window.location.hostname}:8000`;
export default function ProxyPage() {
const [form] = Form.useForm();
const [loading, setLoading] = useState(false);
const [testing, setTesting] = useState(false);
const [testingWl, setTestingWl] = useState(false);
const [configLoaded, setConfigLoaded] = useState(false);
const [logs, setLogs] = useState<{ level: string; message: string }[]>([]);
const wsRef = useRef<WebSocket | null>(null);
const loadConfig = async () => {
try {
const data = await proxyApi.get();
form.setFieldsValue({
enabled: data.enabled ?? false,
api_url: data.api_url ?? '',
http: data.http ?? '',
https: data.https ?? '',
whitelist_enabled: data.whitelist_enabled ?? false,
whitelist_uid: data.whitelist_uid ?? '',
whitelist_ukey: data.whitelist_ukey ?? '',
});
} catch (e: any) {
message.error(e.message);
} finally {
setConfigLoaded(true);
}
};
useEffect(() => {
loadConfig();
return () => {
wsRef.current?.close();
};
}, []);
const appendLog = (level: string, msg: string) => {
setLogs((prev) => [...prev, { level, message: msg }]);
};
const connectWs = (testId: string) => {
wsRef.current?.close();
setLogs([]);
const ws = new WebSocket(`${WS_BASE}/api/proxy/ws/test/${testId}`);
wsRef.current = ws;
ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
if (msg.level === 'heartbeat') return;
if (msg.level === 'result') return;
appendLog(msg.level, msg.message);
};
ws.onclose = () => {
wsRef.current = null;
};
};
const handleSave = async () => {
setLoading(true);
try {
const values = await form.validateFields();
await proxyApi.update(values);
message.success('已保存');
} catch (e: any) {
message.error(e.message);
} finally {
setLoading(false);
}
};
const handleTestProxy = async () => {
setTesting(true);
setLogs([]);
try {
const result = await proxyApi.test();
if (result.test_id) connectWs(result.test_id);
} catch (e: any) {
message.error(e.message);
setTesting(false);
}
};
const handleTestWhitelist = async () => {
setTestingWl(true);
setLogs([]);
try {
const result = await proxyApi.testWhitelist();
if (result.test_id) connectWs(result.test_id);
} catch (e: any) {
message.error(e.message);
setTestingWl(false);
}
};
// WebSocket 关闭后恢复按钮
useEffect(() => {
if (!wsRef.current) {
setTesting(false);
setTestingWl(false);
}
}, [logs.length === 0]);
const logColors: Record<string, string> = {
error: '#ff4d4f',
success: '#52c41a',
warning: '#faad14',
info: '#333',
};
return (
<div style={{ display: 'flex', flexDirection: 'column', height: '100%', gap: 8 }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', flexShrink: 0 }}>
<h2 style={{ margin: 0 }}>代理配置</h2>
<Button type="primary" onClick={handleSave} loading={loading}>保存配置</Button>
</div>
<Form
form={form}
layout="vertical"
disabled={!configLoaded}
size="small"
initialValues={{
enabled: false,
whitelist_enabled: false,
api_url: '',
http: '',
https: '',
whitelist_uid: '',
whitelist_ukey: '',
}}
style={{ flexShrink: 0 }}
>
<Row gutter={12}>
<Col span={12}>
<Card title="代理设置" size="small" styles={{ body: { paddingBottom: 8 } }}>
<Form.Item name="enabled" label="启用代理" valuePropName="checked" style={{ marginBottom: 8 }}>
<Switch />
</Form.Item>
<Form.Item name="api_url" label="代理API地址" style={{ marginBottom: 8 }}>
<Input placeholder="http://op.xiequ.cn/...?act=get" />
</Form.Item>
<Form.Item name="http" label="静态HTTP代理" style={{ marginBottom: 8 }}>
<Input placeholder="http://ip:port" />
</Form.Item>
<Form.Item name="https" label="静态HTTPS代理" style={{ marginBottom: 8 }}>
<Input placeholder="http://ip:port" />
</Form.Item>
<Button size="small" onClick={handleTestProxy} loading={testing}>测试代理</Button>
</Card>
</Col>
<Col span={12}>
<Card title="白名单管理" size="small" styles={{ body: { paddingBottom: 8 } }}>
<Form.Item name="whitelist_enabled" label="启用白名单自动管理" valuePropName="checked" style={{ marginBottom: 8 }}>
<Switch />
</Form.Item>
<Form.Item name="whitelist_uid" label="协固UID" style={{ marginBottom: 8 }}>
<Input placeholder="如: 99769" />
</Form.Item>
<Form.Item name="whitelist_ukey" label="协固UKEY" style={{ marginBottom: 8 }}>
<Input placeholder="如: C99371082B965B70F46DCAA87A04618B" />
</Form.Item>
<Button size="small" onClick={handleTestWhitelist} loading={testingWl}>测试白名单</Button>
</Card>
</Col>
</Row>
</Form>
<Card
title="实时日志"
size="small"
style={{ flex: 1, overflow: 'hidden' }}
styles={{ body: { height: '100%', overflow: 'auto', fontFamily: 'monospace', fontSize: 12, padding: '8px 16px' } }}
>
{logs.length === 0 ? (
<span style={{ color: '#999' }}>点击"测试代理""测试白名单"查看日志</span>
) : (
logs.map((log, i) => (
<div key={i} style={{ color: logColors[log.level] || '#333', lineHeight: '20px' }}>
{log.message}
</div>
))
)}
</Card>
</div>
);
}