218 lines
7.5 KiB
TypeScript
218 lines
7.5 KiB
TypeScript
import { useEffect, useState, useCallback } from 'react';
|
|
import { Form, Input, Switch, Button, Card, Select, Row, Col } from 'antd';
|
|
import { message } from '../utils/antdMessage';
|
|
import { proxyApi, type ProxyConfig, type PlatformInfo } from '../api/modules';
|
|
import RealtimeLogPanel from '../components/RealtimeLogPanel';
|
|
import { useWebSocketLogs } from '../hooks/useWebSocketLogs';
|
|
import { getErrorMessage } from '../utils/error';
|
|
|
|
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, connect: connectLogs, close: closeLogs } = useWebSocketLogs();
|
|
|
|
// 平台相关状态
|
|
const [platforms, setPlatforms] = useState<PlatformInfo[]>([]);
|
|
const [selectedPlatform, setSelectedPlatform] = useState<string>('xiequ');
|
|
const [credentials, setCredentials] = useState<Record<string, string>>({});
|
|
|
|
// 加载平台列表
|
|
useEffect(() => {
|
|
proxyApi.getPlatforms().then(setPlatforms).catch(() => {});
|
|
}, []);
|
|
|
|
// 当前平台的凭据字段定义
|
|
const currentPlatformFields = platforms.find(p => p.name === selectedPlatform)?.credential_fields ?? [];
|
|
|
|
const loadConfig = useCallback(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,
|
|
});
|
|
|
|
// 恢复平台和凭据
|
|
const platform = data.whitelist_platform || 'xiequ';
|
|
setSelectedPlatform(platform);
|
|
if (data.whitelist_credentials && Object.keys(data.whitelist_credentials).length > 0) {
|
|
setCredentials(data.whitelist_credentials);
|
|
} else if (data.whitelist_uid || data.whitelist_ukey) {
|
|
// 向后兼容:旧字段迁移到凭据
|
|
setCredentials({ uid: data.whitelist_uid || '', ukey: data.whitelist_ukey || '' });
|
|
} else {
|
|
setCredentials({});
|
|
}
|
|
} catch (e: unknown) {
|
|
message.error(getErrorMessage(e));
|
|
} finally {
|
|
setConfigLoaded(true);
|
|
}
|
|
}, [form]);
|
|
|
|
useEffect(() => {
|
|
loadConfig();
|
|
return () => {
|
|
closeLogs();
|
|
};
|
|
}, [loadConfig, closeLogs]);
|
|
|
|
const connectWs = (testId: string) => {
|
|
connectLogs(`/api/proxy/ws/test/${testId}`, {
|
|
onClose: () => {
|
|
setTesting(false);
|
|
setTestingWl(false);
|
|
},
|
|
onResult: () => {
|
|
setTesting(false);
|
|
setTestingWl(false);
|
|
},
|
|
});
|
|
};
|
|
|
|
const handleSave = async () => {
|
|
setLoading(true);
|
|
try {
|
|
const values = await form.validateFields();
|
|
const submitData: ProxyConfig = {
|
|
...values,
|
|
whitelist_platform: selectedPlatform,
|
|
whitelist_credentials: credentials,
|
|
// 旧字段:协固平台双写,其他平台清空
|
|
whitelist_uid: selectedPlatform === 'xiequ' ? (credentials.uid || '') : '',
|
|
whitelist_ukey: selectedPlatform === 'xiequ' ? (credentials.ukey || '') : '',
|
|
};
|
|
await proxyApi.update(submitData);
|
|
message.success('已保存');
|
|
} catch (e: unknown) {
|
|
message.error(getErrorMessage(e));
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
const handleTestProxy = async () => {
|
|
setTesting(true);
|
|
try {
|
|
const result = await proxyApi.test();
|
|
if (result.test_id) connectWs(result.test_id);
|
|
} catch (e: unknown) {
|
|
message.error(getErrorMessage(e));
|
|
setTesting(false);
|
|
}
|
|
};
|
|
|
|
const handleTestWhitelist = async () => {
|
|
setTestingWl(true);
|
|
try {
|
|
const result = await proxyApi.testWhitelist();
|
|
if (result.test_id) connectWs(result.test_id);
|
|
} catch (e: unknown) {
|
|
message.error(getErrorMessage(e));
|
|
setTestingWl(false);
|
|
}
|
|
};
|
|
|
|
const handlePlatformChange = (value: string) => {
|
|
setSelectedPlatform(value);
|
|
setCredentials({});
|
|
};
|
|
|
|
const handleCredentialChange = (key: string, value: string) => {
|
|
setCredentials(prev => ({ ...prev, [key]: value }));
|
|
};
|
|
|
|
|
|
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: '',
|
|
}}
|
|
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="代理提取API地址" />
|
|
</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>
|
|
|
|
{/* 平台选择 */}
|
|
<div style={{ marginBottom: 8 }}>
|
|
<div style={{ marginBottom: 4, fontSize: 13, color: 'rgba(0,0,0,0.88)' }}>代理平台</div>
|
|
<Select
|
|
value={selectedPlatform}
|
|
onChange={handlePlatformChange}
|
|
style={{ width: '100%' }}
|
|
options={platforms.map(p => ({ value: p.name, label: p.label }))}
|
|
/>
|
|
</div>
|
|
|
|
{/* 动态凭据字段 */}
|
|
{currentPlatformFields.map(field => (
|
|
<div key={field.key} style={{ marginBottom: 8 }}>
|
|
<div style={{ marginBottom: 4, fontSize: 13, color: 'rgba(0,0,0,0.88)' }}>{field.label}</div>
|
|
<Input
|
|
placeholder={field.placeholder}
|
|
value={credentials[field.key] || ''}
|
|
onChange={(e) => handleCredentialChange(field.key, e.target.value)}
|
|
/>
|
|
</div>
|
|
))}
|
|
|
|
<Button size="small" onClick={handleTestWhitelist} loading={testingWl}>测试白名单</Button>
|
|
</Card>
|
|
</Col>
|
|
</Row>
|
|
</Form>
|
|
|
|
<RealtimeLogPanel
|
|
mode="card"
|
|
logs={logs}
|
|
title="实时日志"
|
|
emptyText={'点击"测试代理"或"测试白名单"查看日志'}
|
|
height="100%"
|
|
bodyStyle={{ minHeight: 160 }}
|
|
style={{ flex: 1 }}
|
|
/>
|
|
</div>
|
|
);
|
|
}
|