|
|
|
|
@ -1,5 +1,5 @@
|
|
|
|
|
import React, { useState } from 'react';
|
|
|
|
|
import { Card, Row, Col, Form, Input, Button, Select, Switch, InputNumber, Alert, Divider, Tabs } from 'antd';
|
|
|
|
|
import { Card, Row, Col, Form, Input, Button, Select, Switch, InputNumber, Alert, Divider, Tabs, message } from 'antd';
|
|
|
|
|
import { DatabaseOutlined, KeyOutlined, SettingOutlined, SaveOutlined, ToolOutlined } from '@ant-design/icons';
|
|
|
|
|
import './AdminConfig.css';
|
|
|
|
|
|
|
|
|
|
@ -78,10 +78,43 @@ const AdminConfig = () => {
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 数据源配置
|
|
|
|
|
const dataSourceConfig = {
|
|
|
|
|
// TQSDK配置
|
|
|
|
|
tqsdk: {
|
|
|
|
|
enabled: true,
|
|
|
|
|
username: '',
|
|
|
|
|
password: '',
|
|
|
|
|
timeout: 30000,
|
|
|
|
|
retries: 3,
|
|
|
|
|
maxConnections: 5
|
|
|
|
|
},
|
|
|
|
|
// Wind配置
|
|
|
|
|
wind: {
|
|
|
|
|
enabled: false,
|
|
|
|
|
apiKey: '',
|
|
|
|
|
apiSecret: '',
|
|
|
|
|
url: 'https://api.wind.com.cn',
|
|
|
|
|
timeout: 30000,
|
|
|
|
|
retries: 3
|
|
|
|
|
},
|
|
|
|
|
// 新浪财经配置
|
|
|
|
|
sina: {
|
|
|
|
|
enabled: false,
|
|
|
|
|
url: 'https://finance.sina.com.cn',
|
|
|
|
|
timeout: 10000,
|
|
|
|
|
retries: 3,
|
|
|
|
|
refreshInterval: 60000
|
|
|
|
|
},
|
|
|
|
|
// 默认数据源
|
|
|
|
|
defaultDataSource: 'tqsdk'
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const [config, setConfig] = useState({
|
|
|
|
|
database: databaseConfig,
|
|
|
|
|
server: serverConfig,
|
|
|
|
|
security: securityConfig
|
|
|
|
|
security: securityConfig,
|
|
|
|
|
dataSource: dataSourceConfig
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 处理配置变更
|
|
|
|
|
@ -109,42 +142,193 @@ const AdminConfig = () => {
|
|
|
|
|
}));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 处理数据源配置变更
|
|
|
|
|
const handleDataSourceConfigChange = (dsType, key, value) => {
|
|
|
|
|
setConfig(prev => ({
|
|
|
|
|
...prev,
|
|
|
|
|
dataSource: {
|
|
|
|
|
...prev.dataSource,
|
|
|
|
|
[dsType]: {
|
|
|
|
|
...prev.dataSource[dsType],
|
|
|
|
|
[key]: value
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 处理表单提交
|
|
|
|
|
const handleSubmit = (values) => {
|
|
|
|
|
console.log('配置保存:', values);
|
|
|
|
|
// 模拟保存操作
|
|
|
|
|
Alert.success('配置已保存');
|
|
|
|
|
message.success('配置已保存');
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 测试数据库连接
|
|
|
|
|
const testDatabaseConnection = (dbType) => {
|
|
|
|
|
console.log(`测试${dbType}连接`);
|
|
|
|
|
// 模拟测试操作
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
Alert.success(`${dbType}连接测试成功`);
|
|
|
|
|
}, 1000);
|
|
|
|
|
const testDatabaseConnection = async (dbType) => {
|
|
|
|
|
try {
|
|
|
|
|
const response = await fetch('http://localhost:3005/api/config/test-database', {
|
|
|
|
|
method: 'POST',
|
|
|
|
|
headers: {
|
|
|
|
|
'Content-Type': 'application/json'
|
|
|
|
|
},
|
|
|
|
|
body: JSON.stringify({
|
|
|
|
|
dbType,
|
|
|
|
|
config: config.database[dbType.toLowerCase()]
|
|
|
|
|
})
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const result = await response.json();
|
|
|
|
|
if (result.success) {
|
|
|
|
|
message.success(result.message);
|
|
|
|
|
} else {
|
|
|
|
|
message.error(result.message);
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('测试数据库连接失败:', error);
|
|
|
|
|
message.error('测试数据库连接失败,请检查网络连接');
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 测试数据源连接
|
|
|
|
|
const testDataSourceConnection = async (dsType) => {
|
|
|
|
|
try {
|
|
|
|
|
const response = await fetch('http://localhost:3005/api/config/test-datasource', {
|
|
|
|
|
method: 'POST',
|
|
|
|
|
headers: {
|
|
|
|
|
'Content-Type': 'application/json'
|
|
|
|
|
},
|
|
|
|
|
body: JSON.stringify({
|
|
|
|
|
dsType,
|
|
|
|
|
config: config.dataSource[dsType.toLowerCase()]
|
|
|
|
|
})
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const result = await response.json();
|
|
|
|
|
if (result.success) {
|
|
|
|
|
message.success(result.message);
|
|
|
|
|
} else {
|
|
|
|
|
message.error(result.message);
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('测试数据源连接失败:', error);
|
|
|
|
|
message.error('测试数据源连接失败,请检查网络连接');
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="admin-config">
|
|
|
|
|
<h2>管理配置</h2>
|
|
|
|
|
<Alert
|
|
|
|
|
message="警告:此页面包含敏感配置信息,请谨慎操作"
|
|
|
|
|
title="警告:此页面包含敏感配置信息,请谨慎操作"
|
|
|
|
|
type="warning"
|
|
|
|
|
style={{ marginBottom: 24 }}
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
<Form form={form} layout="vertical" onFinish={handleSubmit}>
|
|
|
|
|
<Tabs defaultActiveKey="database">
|
|
|
|
|
{/* 数据库配置 */}
|
|
|
|
|
<TabPane tab={<span><DatabaseOutlined /> 数据库配置</span>} key="database">
|
|
|
|
|
<Form
|
|
|
|
|
form={form}
|
|
|
|
|
layout="vertical"
|
|
|
|
|
onFinish={handleSubmit}
|
|
|
|
|
initialValues={{
|
|
|
|
|
database: {
|
|
|
|
|
mongoDB: {
|
|
|
|
|
host: config.database.mongoDB.host,
|
|
|
|
|
port: config.database.mongoDB.port,
|
|
|
|
|
database: config.database.mongoDB.database,
|
|
|
|
|
username: config.database.mongoDB.username,
|
|
|
|
|
password: config.database.mongoDB.password,
|
|
|
|
|
authSource: config.database.mongoDB.authSource,
|
|
|
|
|
ssl: config.database.mongoDB.ssl,
|
|
|
|
|
enabled: config.database.mongoDB.enabled
|
|
|
|
|
},
|
|
|
|
|
postgreSQL: {
|
|
|
|
|
host: config.database.postgreSQL.host,
|
|
|
|
|
port: config.database.postgreSQL.port,
|
|
|
|
|
database: config.database.postgreSQL.database,
|
|
|
|
|
username: config.database.postgreSQL.username,
|
|
|
|
|
password: config.database.postgreSQL.password,
|
|
|
|
|
ssl: config.database.postgreSQL.ssl,
|
|
|
|
|
enabled: config.database.postgreSQL.enabled
|
|
|
|
|
},
|
|
|
|
|
redis: {
|
|
|
|
|
host: config.database.redis.host,
|
|
|
|
|
port: config.database.redis.port,
|
|
|
|
|
password: config.database.redis.password,
|
|
|
|
|
db: config.database.redis.db,
|
|
|
|
|
enabled: config.database.redis.enabled
|
|
|
|
|
},
|
|
|
|
|
influxDB: {
|
|
|
|
|
host: config.database.influxDB.host,
|
|
|
|
|
port: config.database.influxDB.port,
|
|
|
|
|
database: config.database.influxDB.database,
|
|
|
|
|
username: config.database.influxDB.username,
|
|
|
|
|
password: config.database.influxDB.password,
|
|
|
|
|
ssl: config.database.influxDB.ssl,
|
|
|
|
|
enabled: config.database.influxDB.enabled
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
server: {
|
|
|
|
|
port: config.server.port,
|
|
|
|
|
host: config.server.host,
|
|
|
|
|
environment: config.server.environment,
|
|
|
|
|
debug: config.server.debug,
|
|
|
|
|
timeout: config.server.timeout,
|
|
|
|
|
maxBodySize: config.server.maxBodySize
|
|
|
|
|
},
|
|
|
|
|
security: {
|
|
|
|
|
jwtSecret: config.security.jwtSecret,
|
|
|
|
|
jwtExpiresIn: config.security.jwtExpiresIn,
|
|
|
|
|
rateLimit: {
|
|
|
|
|
windowMs: config.security.rateLimit.windowMs,
|
|
|
|
|
max: config.security.rateLimit.max
|
|
|
|
|
},
|
|
|
|
|
cors: {
|
|
|
|
|
origin: config.security.cors.origin,
|
|
|
|
|
methods: config.security.cors.methods.join(', '),
|
|
|
|
|
allowedHeaders: config.security.cors.allowedHeaders.join(', ')
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
dataSource: {
|
|
|
|
|
defaultDataSource: config.dataSource.defaultDataSource,
|
|
|
|
|
tqsdk: {
|
|
|
|
|
enabled: config.dataSource.tqsdk.enabled,
|
|
|
|
|
username: config.dataSource.tqsdk.username,
|
|
|
|
|
password: config.dataSource.tqsdk.password,
|
|
|
|
|
timeout: config.dataSource.tqsdk.timeout,
|
|
|
|
|
retries: config.dataSource.tqsdk.retries,
|
|
|
|
|
maxConnections: config.dataSource.tqsdk.maxConnections
|
|
|
|
|
},
|
|
|
|
|
wind: {
|
|
|
|
|
enabled: config.dataSource.wind.enabled,
|
|
|
|
|
apiKey: config.dataSource.wind.apiKey,
|
|
|
|
|
apiSecret: config.dataSource.wind.apiSecret,
|
|
|
|
|
url: config.dataSource.wind.url,
|
|
|
|
|
timeout: config.dataSource.wind.timeout,
|
|
|
|
|
retries: config.dataSource.wind.retries
|
|
|
|
|
},
|
|
|
|
|
sina: {
|
|
|
|
|
enabled: config.dataSource.sina.enabled,
|
|
|
|
|
url: config.dataSource.sina.url,
|
|
|
|
|
timeout: config.dataSource.sina.timeout,
|
|
|
|
|
retries: config.dataSource.sina.retries,
|
|
|
|
|
refreshInterval: config.dataSource.sina.refreshInterval
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<Tabs
|
|
|
|
|
defaultActiveKey="database"
|
|
|
|
|
items={[
|
|
|
|
|
{
|
|
|
|
|
label: <span><DatabaseOutlined /> 数据库配置</span>,
|
|
|
|
|
key: 'database',
|
|
|
|
|
children: (
|
|
|
|
|
<>
|
|
|
|
|
{/* MongoDB配置 */}
|
|
|
|
|
<Card title="MongoDB配置" className="admin-config-card" style={{ marginBottom: 24 }}>
|
|
|
|
|
<Row gutter={[16, 16]}>
|
|
|
|
|
<Col span={6}>
|
|
|
|
|
<Item label="主机" name="database.mongoDB.host">
|
|
|
|
|
<Input
|
|
|
|
|
defaultValue={config.database.mongoDB.host}
|
|
|
|
|
onChange={(e) => handleDatabaseConfigChange('mongoDB', 'host', e.target.value)}
|
|
|
|
|
/>
|
|
|
|
|
</Item>
|
|
|
|
|
@ -152,7 +336,6 @@ const AdminConfig = () => {
|
|
|
|
|
<Col span={6}>
|
|
|
|
|
<Item label="端口" name="database.mongoDB.port">
|
|
|
|
|
<InputNumber
|
|
|
|
|
defaultValue={config.database.mongoDB.port}
|
|
|
|
|
min={1}
|
|
|
|
|
max={65535}
|
|
|
|
|
onChange={(value) => handleDatabaseConfigChange('mongoDB', 'port', value)}
|
|
|
|
|
@ -162,7 +345,6 @@ const AdminConfig = () => {
|
|
|
|
|
<Col span={6}>
|
|
|
|
|
<Item label="数据库" name="database.mongoDB.database">
|
|
|
|
|
<Input
|
|
|
|
|
defaultValue={config.database.mongoDB.database}
|
|
|
|
|
onChange={(e) => handleDatabaseConfigChange('mongoDB', 'database', e.target.value)}
|
|
|
|
|
/>
|
|
|
|
|
</Item>
|
|
|
|
|
@ -170,7 +352,6 @@ const AdminConfig = () => {
|
|
|
|
|
<Col span={6}>
|
|
|
|
|
<Item label="认证源" name="database.mongoDB.authSource">
|
|
|
|
|
<Input
|
|
|
|
|
defaultValue={config.database.mongoDB.authSource}
|
|
|
|
|
onChange={(e) => handleDatabaseConfigChange('mongoDB', 'authSource', e.target.value)}
|
|
|
|
|
/>
|
|
|
|
|
</Item>
|
|
|
|
|
@ -178,7 +359,7 @@ const AdminConfig = () => {
|
|
|
|
|
<Col span={6}>
|
|
|
|
|
<Item label="用户名" name="database.mongoDB.username">
|
|
|
|
|
<Input
|
|
|
|
|
defaultValue={config.database.mongoDB.username}
|
|
|
|
|
|
|
|
|
|
onChange={(e) => handleDatabaseConfigChange('mongoDB', 'username', e.target.value)}
|
|
|
|
|
/>
|
|
|
|
|
</Item>
|
|
|
|
|
@ -186,7 +367,7 @@ const AdminConfig = () => {
|
|
|
|
|
<Col span={6}>
|
|
|
|
|
<Item label="密码" name="database.mongoDB.password">
|
|
|
|
|
<Input.Password
|
|
|
|
|
defaultValue={config.database.mongoDB.password}
|
|
|
|
|
|
|
|
|
|
onChange={(e) => handleDatabaseConfigChange('mongoDB', 'password', e.target.value)}
|
|
|
|
|
/>
|
|
|
|
|
</Item>
|
|
|
|
|
@ -240,7 +421,7 @@ const AdminConfig = () => {
|
|
|
|
|
<Col span={6}>
|
|
|
|
|
<Item label="主机" name="database.postgreSQL.host">
|
|
|
|
|
<Input
|
|
|
|
|
defaultValue={config.database.postgreSQL.host}
|
|
|
|
|
|
|
|
|
|
onChange={(e) => handleDatabaseConfigChange('postgreSQL', 'host', e.target.value)}
|
|
|
|
|
/>
|
|
|
|
|
</Item>
|
|
|
|
|
@ -248,7 +429,7 @@ const AdminConfig = () => {
|
|
|
|
|
<Col span={6}>
|
|
|
|
|
<Item label="端口" name="database.postgreSQL.port">
|
|
|
|
|
<InputNumber
|
|
|
|
|
defaultValue={config.database.postgreSQL.port}
|
|
|
|
|
|
|
|
|
|
min={1}
|
|
|
|
|
max={65535}
|
|
|
|
|
onChange={(value) => handleDatabaseConfigChange('postgreSQL', 'port', value)}
|
|
|
|
|
@ -258,7 +439,7 @@ const AdminConfig = () => {
|
|
|
|
|
<Col span={6}>
|
|
|
|
|
<Item label="数据库" name="database.postgreSQL.database">
|
|
|
|
|
<Input
|
|
|
|
|
defaultValue={config.database.postgreSQL.database}
|
|
|
|
|
|
|
|
|
|
onChange={(e) => handleDatabaseConfigChange('postgreSQL', 'database', e.target.value)}
|
|
|
|
|
/>
|
|
|
|
|
</Item>
|
|
|
|
|
@ -266,7 +447,7 @@ const AdminConfig = () => {
|
|
|
|
|
<Col span={6}>
|
|
|
|
|
<Item label="用户名" name="database.postgreSQL.username">
|
|
|
|
|
<Input
|
|
|
|
|
defaultValue={config.database.postgreSQL.username}
|
|
|
|
|
|
|
|
|
|
onChange={(e) => handleDatabaseConfigChange('postgreSQL', 'username', e.target.value)}
|
|
|
|
|
/>
|
|
|
|
|
</Item>
|
|
|
|
|
@ -274,7 +455,7 @@ const AdminConfig = () => {
|
|
|
|
|
<Col span={6}>
|
|
|
|
|
<Item label="密码" name="database.postgreSQL.password">
|
|
|
|
|
<Input.Password
|
|
|
|
|
defaultValue={config.database.postgreSQL.password}
|
|
|
|
|
|
|
|
|
|
onChange={(e) => handleDatabaseConfigChange('postgreSQL', 'password', e.target.value)}
|
|
|
|
|
/>
|
|
|
|
|
</Item>
|
|
|
|
|
@ -327,7 +508,7 @@ const AdminConfig = () => {
|
|
|
|
|
<Col span={6}>
|
|
|
|
|
<Item label="主机" name="database.redis.host">
|
|
|
|
|
<Input
|
|
|
|
|
defaultValue={config.database.redis.host}
|
|
|
|
|
|
|
|
|
|
onChange={(e) => handleDatabaseConfigChange('redis', 'host', e.target.value)}
|
|
|
|
|
/>
|
|
|
|
|
</Item>
|
|
|
|
|
@ -335,7 +516,7 @@ const AdminConfig = () => {
|
|
|
|
|
<Col span={6}>
|
|
|
|
|
<Item label="端口" name="database.redis.port">
|
|
|
|
|
<InputNumber
|
|
|
|
|
defaultValue={config.database.redis.port}
|
|
|
|
|
|
|
|
|
|
min={1}
|
|
|
|
|
max={65535}
|
|
|
|
|
onChange={(value) => handleDatabaseConfigChange('redis', 'port', value)}
|
|
|
|
|
@ -345,7 +526,7 @@ const AdminConfig = () => {
|
|
|
|
|
<Col span={6}>
|
|
|
|
|
<Item label="密码" name="database.redis.password">
|
|
|
|
|
<Input.Password
|
|
|
|
|
defaultValue={config.database.redis.password}
|
|
|
|
|
|
|
|
|
|
onChange={(e) => handleDatabaseConfigChange('redis', 'password', e.target.value)}
|
|
|
|
|
/>
|
|
|
|
|
</Item>
|
|
|
|
|
@ -353,7 +534,7 @@ const AdminConfig = () => {
|
|
|
|
|
<Col span={6}>
|
|
|
|
|
<Item label="数据库" name="database.redis.db">
|
|
|
|
|
<InputNumber
|
|
|
|
|
defaultValue={config.database.redis.db}
|
|
|
|
|
|
|
|
|
|
min={0}
|
|
|
|
|
max={15}
|
|
|
|
|
onChange={(value) => handleDatabaseConfigChange('redis', 'db', value)}
|
|
|
|
|
@ -398,7 +579,7 @@ const AdminConfig = () => {
|
|
|
|
|
<Col span={6}>
|
|
|
|
|
<Item label="主机" name="database.influxDB.host">
|
|
|
|
|
<Input
|
|
|
|
|
defaultValue={config.database.influxDB.host}
|
|
|
|
|
|
|
|
|
|
onChange={(e) => handleDatabaseConfigChange('influxDB', 'host', e.target.value)}
|
|
|
|
|
/>
|
|
|
|
|
</Item>
|
|
|
|
|
@ -406,7 +587,7 @@ const AdminConfig = () => {
|
|
|
|
|
<Col span={6}>
|
|
|
|
|
<Item label="端口" name="database.influxDB.port">
|
|
|
|
|
<InputNumber
|
|
|
|
|
defaultValue={config.database.influxDB.port}
|
|
|
|
|
|
|
|
|
|
min={1}
|
|
|
|
|
max={65535}
|
|
|
|
|
onChange={(value) => handleDatabaseConfigChange('influxDB', 'port', value)}
|
|
|
|
|
@ -416,7 +597,7 @@ const AdminConfig = () => {
|
|
|
|
|
<Col span={6}>
|
|
|
|
|
<Item label="数据库" name="database.influxDB.database">
|
|
|
|
|
<Input
|
|
|
|
|
defaultValue={config.database.influxDB.database}
|
|
|
|
|
|
|
|
|
|
onChange={(e) => handleDatabaseConfigChange('influxDB', 'database', e.target.value)}
|
|
|
|
|
/>
|
|
|
|
|
</Item>
|
|
|
|
|
@ -424,7 +605,7 @@ const AdminConfig = () => {
|
|
|
|
|
<Col span={6}>
|
|
|
|
|
<Item label="用户名" name="database.influxDB.username">
|
|
|
|
|
<Input
|
|
|
|
|
defaultValue={config.database.influxDB.username}
|
|
|
|
|
|
|
|
|
|
onChange={(e) => handleDatabaseConfigChange('influxDB', 'username', e.target.value)}
|
|
|
|
|
/>
|
|
|
|
|
</Item>
|
|
|
|
|
@ -432,7 +613,7 @@ const AdminConfig = () => {
|
|
|
|
|
<Col span={6}>
|
|
|
|
|
<Item label="密码" name="database.influxDB.password">
|
|
|
|
|
<Input.Password
|
|
|
|
|
defaultValue={config.database.influxDB.password}
|
|
|
|
|
|
|
|
|
|
onChange={(e) => handleDatabaseConfigChange('influxDB', 'password', e.target.value)}
|
|
|
|
|
/>
|
|
|
|
|
</Item>
|
|
|
|
|
@ -478,16 +659,19 @@ const AdminConfig = () => {
|
|
|
|
|
</Button>
|
|
|
|
|
</div>
|
|
|
|
|
</Card>
|
|
|
|
|
</TabPane>
|
|
|
|
|
|
|
|
|
|
{/* 服务器配置 */}
|
|
|
|
|
<TabPane tab={<span><ToolOutlined /> 服务器配置</span>} key="server">
|
|
|
|
|
</>
|
|
|
|
|
)
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
label: <span><ToolOutlined /> 服务器配置</span>,
|
|
|
|
|
key: 'server',
|
|
|
|
|
children: (
|
|
|
|
|
<Card title="服务器配置" className="admin-config-card" style={{ marginBottom: 24 }}>
|
|
|
|
|
<Row gutter={[16, 16]}>
|
|
|
|
|
<Col span={6}>
|
|
|
|
|
<Item label="端口" name="server.port">
|
|
|
|
|
<InputNumber
|
|
|
|
|
defaultValue={config.server.port}
|
|
|
|
|
|
|
|
|
|
min={1}
|
|
|
|
|
max={65535}
|
|
|
|
|
onChange={(value) => handleConfigChange('server', 'port', value)}
|
|
|
|
|
@ -497,7 +681,7 @@ const AdminConfig = () => {
|
|
|
|
|
<Col span={6}>
|
|
|
|
|
<Item label="主机" name="server.host">
|
|
|
|
|
<Input
|
|
|
|
|
defaultValue={config.server.host}
|
|
|
|
|
|
|
|
|
|
onChange={(e) => handleConfigChange('server', 'host', e.target.value)}
|
|
|
|
|
/>
|
|
|
|
|
</Item>
|
|
|
|
|
@ -505,7 +689,7 @@ const AdminConfig = () => {
|
|
|
|
|
<Col span={6}>
|
|
|
|
|
<Item label="环境" name="server.environment">
|
|
|
|
|
<Select
|
|
|
|
|
defaultValue={config.server.environment}
|
|
|
|
|
|
|
|
|
|
onChange={(value) => handleConfigChange('server', 'environment', value)}
|
|
|
|
|
>
|
|
|
|
|
<Option value="development">开发环境</Option>
|
|
|
|
|
@ -525,7 +709,7 @@ const AdminConfig = () => {
|
|
|
|
|
<Col span={6}>
|
|
|
|
|
<Item label="超时时间(ms)" name="server.timeout">
|
|
|
|
|
<InputNumber
|
|
|
|
|
defaultValue={config.server.timeout}
|
|
|
|
|
|
|
|
|
|
min={1000}
|
|
|
|
|
max={60000}
|
|
|
|
|
step={1000}
|
|
|
|
|
@ -536,24 +720,27 @@ const AdminConfig = () => {
|
|
|
|
|
<Col span={6}>
|
|
|
|
|
<Item label="最大请求体大小" name="server.maxBodySize">
|
|
|
|
|
<Input
|
|
|
|
|
defaultValue={config.server.maxBodySize}
|
|
|
|
|
|
|
|
|
|
onChange={(e) => handleConfigChange('server', 'maxBodySize', e.target.value)}
|
|
|
|
|
/>
|
|
|
|
|
</Item>
|
|
|
|
|
</Col>
|
|
|
|
|
</Row>
|
|
|
|
|
</Card>
|
|
|
|
|
</TabPane>
|
|
|
|
|
|
|
|
|
|
{/* 安全配置 */}
|
|
|
|
|
<TabPane tab={<span><KeyOutlined /> 安全配置</span>} key="security">
|
|
|
|
|
)
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
label: <span><KeyOutlined /> 安全配置</span>,
|
|
|
|
|
key: 'security',
|
|
|
|
|
children: (
|
|
|
|
|
<>
|
|
|
|
|
{/* JWT配置 */}
|
|
|
|
|
<Card title="JWT配置" className="admin-config-card" style={{ marginBottom: 24 }}>
|
|
|
|
|
<Row gutter={[16, 16]}>
|
|
|
|
|
<Col span={12}>
|
|
|
|
|
<Item label="JWT密钥" name="security.jwtSecret">
|
|
|
|
|
<Input.Password
|
|
|
|
|
defaultValue={config.security.jwtSecret}
|
|
|
|
|
|
|
|
|
|
onChange={(e) => handleConfigChange('security', 'jwtSecret', e.target.value)}
|
|
|
|
|
/>
|
|
|
|
|
</Item>
|
|
|
|
|
@ -561,7 +748,7 @@ const AdminConfig = () => {
|
|
|
|
|
<Col span={12}>
|
|
|
|
|
<Item label="过期时间" name="security.jwtExpiresIn">
|
|
|
|
|
<Input
|
|
|
|
|
defaultValue={config.security.jwtExpiresIn}
|
|
|
|
|
|
|
|
|
|
onChange={(e) => handleConfigChange('security', 'jwtExpiresIn', e.target.value)}
|
|
|
|
|
/>
|
|
|
|
|
</Item>
|
|
|
|
|
@ -575,7 +762,7 @@ const AdminConfig = () => {
|
|
|
|
|
<Col span={12}>
|
|
|
|
|
<Item label="时间窗口(ms)" name="security.rateLimit.windowMs">
|
|
|
|
|
<InputNumber
|
|
|
|
|
defaultValue={config.security.rateLimit.windowMs}
|
|
|
|
|
|
|
|
|
|
min={1000}
|
|
|
|
|
max={3600000}
|
|
|
|
|
step={1000}
|
|
|
|
|
@ -586,7 +773,7 @@ const AdminConfig = () => {
|
|
|
|
|
<Col span={12}>
|
|
|
|
|
<Item label="最大请求数" name="security.rateLimit.max">
|
|
|
|
|
<InputNumber
|
|
|
|
|
defaultValue={config.security.rateLimit.max}
|
|
|
|
|
|
|
|
|
|
min={1}
|
|
|
|
|
max={1000}
|
|
|
|
|
step={10}
|
|
|
|
|
@ -603,7 +790,7 @@ const AdminConfig = () => {
|
|
|
|
|
<Col span={24}>
|
|
|
|
|
<Item label="允许的源" name="security.cors.origin">
|
|
|
|
|
<Input
|
|
|
|
|
defaultValue={config.security.cors.origin}
|
|
|
|
|
|
|
|
|
|
onChange={(e) => handleConfigChange('security', 'cors', { ...config.security.cors, origin: e.target.value })}
|
|
|
|
|
/>
|
|
|
|
|
</Item>
|
|
|
|
|
@ -611,7 +798,7 @@ const AdminConfig = () => {
|
|
|
|
|
<Col span={24}>
|
|
|
|
|
<Item label="允许的方法" name="security.cors.methods">
|
|
|
|
|
<Input
|
|
|
|
|
defaultValue={config.security.cors.methods.join(', ')}
|
|
|
|
|
|
|
|
|
|
onChange={(e) => handleConfigChange('security', 'cors', { ...config.security.cors, methods: e.target.value.split(', ').map(m => m.trim()) })}
|
|
|
|
|
/>
|
|
|
|
|
</Item>
|
|
|
|
|
@ -619,15 +806,286 @@ const AdminConfig = () => {
|
|
|
|
|
<Col span={24}>
|
|
|
|
|
<Item label="允许的头部" name="security.cors.allowedHeaders">
|
|
|
|
|
<Input
|
|
|
|
|
defaultValue={config.security.cors.allowedHeaders.join(', ')}
|
|
|
|
|
|
|
|
|
|
onChange={(e) => handleConfigChange('security', 'cors', { ...config.security.cors, allowedHeaders: e.target.value.split(', ').map(h => h.trim()) })}
|
|
|
|
|
/>
|
|
|
|
|
</Item>
|
|
|
|
|
</Col>
|
|
|
|
|
</Row>
|
|
|
|
|
</Card>
|
|
|
|
|
</TabPane>
|
|
|
|
|
</Tabs>
|
|
|
|
|
</>
|
|
|
|
|
)
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
label: <span><DatabaseOutlined /> 数据源配置</span>,
|
|
|
|
|
key: 'dataSource',
|
|
|
|
|
children: (
|
|
|
|
|
<>
|
|
|
|
|
{/* 默认数据源配置 */}
|
|
|
|
|
<Card title="默认数据源" className="admin-config-card" style={{ marginBottom: 24 }}>
|
|
|
|
|
<Row gutter={[16, 16]}>
|
|
|
|
|
<Col span={12}>
|
|
|
|
|
<Item label="默认数据源">
|
|
|
|
|
<Select
|
|
|
|
|
|
|
|
|
|
onChange={(value) => handleConfigChange('dataSource', 'defaultDataSource', value)}
|
|
|
|
|
>
|
|
|
|
|
<Option value="tqsdk">TQSDK</Option>
|
|
|
|
|
<Option value="wind">Wind</Option>
|
|
|
|
|
<Option value="sina">新浪财经</Option>
|
|
|
|
|
</Select>
|
|
|
|
|
</Item>
|
|
|
|
|
</Col>
|
|
|
|
|
</Row>
|
|
|
|
|
</Card>
|
|
|
|
|
|
|
|
|
|
{/* TQSDK配置 */}
|
|
|
|
|
<Card title="TQSDK配置" className="admin-config-card" style={{ marginBottom: 24 }}>
|
|
|
|
|
<Row gutter={[16, 16]}>
|
|
|
|
|
<Col span={6}>
|
|
|
|
|
<Item label="启用">
|
|
|
|
|
<Switch
|
|
|
|
|
defaultChecked={config.dataSource.tqsdk.enabled}
|
|
|
|
|
onChange={(checked) => handleDataSourceConfigChange('tqsdk', 'enabled', checked)}
|
|
|
|
|
/>
|
|
|
|
|
</Item>
|
|
|
|
|
</Col>
|
|
|
|
|
<Col span={12}>
|
|
|
|
|
<Item label="用户名">
|
|
|
|
|
<Input
|
|
|
|
|
|
|
|
|
|
onChange={(e) => handleDataSourceConfigChange('tqsdk', 'username', e.target.value)}
|
|
|
|
|
/>
|
|
|
|
|
</Item>
|
|
|
|
|
</Col>
|
|
|
|
|
<Col span={12}>
|
|
|
|
|
<Item label="密码">
|
|
|
|
|
<Input.Password
|
|
|
|
|
|
|
|
|
|
onChange={(e) => handleDataSourceConfigChange('tqsdk', 'password', e.target.value)}
|
|
|
|
|
/>
|
|
|
|
|
</Item>
|
|
|
|
|
</Col>
|
|
|
|
|
<Col span={6}>
|
|
|
|
|
<Item label="超时时间(ms)">
|
|
|
|
|
<InputNumber
|
|
|
|
|
|
|
|
|
|
min={1000}
|
|
|
|
|
max={60000}
|
|
|
|
|
step={1000}
|
|
|
|
|
onChange={(value) => handleDataSourceConfigChange('tqsdk', 'timeout', value)}
|
|
|
|
|
/>
|
|
|
|
|
</Item>
|
|
|
|
|
</Col>
|
|
|
|
|
<Col span={6}>
|
|
|
|
|
<Item label="重试次数">
|
|
|
|
|
<InputNumber
|
|
|
|
|
|
|
|
|
|
min={0}
|
|
|
|
|
max={10}
|
|
|
|
|
step={1}
|
|
|
|
|
onChange={(value) => handleDataSourceConfigChange('tqsdk', 'retries', value)}
|
|
|
|
|
/>
|
|
|
|
|
</Item>
|
|
|
|
|
</Col>
|
|
|
|
|
<Col span={6}>
|
|
|
|
|
<Item label="最大连接数">
|
|
|
|
|
<InputNumber
|
|
|
|
|
|
|
|
|
|
min={1}
|
|
|
|
|
max={20}
|
|
|
|
|
step={1}
|
|
|
|
|
onChange={(value) => handleDataSourceConfigChange('tqsdk', 'maxConnections', value)}
|
|
|
|
|
/>
|
|
|
|
|
</Item>
|
|
|
|
|
</Col>
|
|
|
|
|
</Row>
|
|
|
|
|
<div style={{ marginTop: 16 }}>
|
|
|
|
|
<Button
|
|
|
|
|
type="primary"
|
|
|
|
|
onClick={() => testDataSourceConnection('TQSDK')}
|
|
|
|
|
style={{ marginRight: 8 }}
|
|
|
|
|
>
|
|
|
|
|
测试连接
|
|
|
|
|
</Button>
|
|
|
|
|
<Button
|
|
|
|
|
type="default"
|
|
|
|
|
onClick={() => {
|
|
|
|
|
handleDataSourceConfigChange('tqsdk', 'enabled', true);
|
|
|
|
|
handleDataSourceConfigChange('tqsdk', 'username', '');
|
|
|
|
|
handleDataSourceConfigChange('tqsdk', 'password', '');
|
|
|
|
|
handleDataSourceConfigChange('tqsdk', 'timeout', 30000);
|
|
|
|
|
handleDataSourceConfigChange('tqsdk', 'retries', 3);
|
|
|
|
|
handleDataSourceConfigChange('tqsdk', 'maxConnections', 5);
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
恢复默认
|
|
|
|
|
</Button>
|
|
|
|
|
</div>
|
|
|
|
|
</Card>
|
|
|
|
|
|
|
|
|
|
{/* Wind配置 */}
|
|
|
|
|
<Card title="Wind配置" className="admin-config-card" style={{ marginBottom: 24 }}>
|
|
|
|
|
<Row gutter={[16, 16]}>
|
|
|
|
|
<Col span={6}>
|
|
|
|
|
<Item label="启用">
|
|
|
|
|
<Switch
|
|
|
|
|
defaultChecked={config.dataSource.wind.enabled}
|
|
|
|
|
onChange={(checked) => handleDataSourceConfigChange('wind', 'enabled', checked)}
|
|
|
|
|
/>
|
|
|
|
|
</Item>
|
|
|
|
|
</Col>
|
|
|
|
|
<Col span={12}>
|
|
|
|
|
<Item label="API密钥">
|
|
|
|
|
<Input.Password
|
|
|
|
|
|
|
|
|
|
onChange={(e) => handleDataSourceConfigChange('wind', 'apiKey', e.target.value)}
|
|
|
|
|
/>
|
|
|
|
|
</Item>
|
|
|
|
|
</Col>
|
|
|
|
|
<Col span={12}>
|
|
|
|
|
<Item label="API密钥密钥">
|
|
|
|
|
<Input.Password
|
|
|
|
|
|
|
|
|
|
onChange={(e) => handleDataSourceConfigChange('wind', 'apiSecret', e.target.value)}
|
|
|
|
|
/>
|
|
|
|
|
</Item>
|
|
|
|
|
</Col>
|
|
|
|
|
<Col span={12}>
|
|
|
|
|
<Item label="API URL">
|
|
|
|
|
<Input
|
|
|
|
|
|
|
|
|
|
onChange={(e) => handleDataSourceConfigChange('wind', 'url', e.target.value)}
|
|
|
|
|
/>
|
|
|
|
|
</Item>
|
|
|
|
|
</Col>
|
|
|
|
|
<Col span={6}>
|
|
|
|
|
<Item label="超时时间(ms)">
|
|
|
|
|
<InputNumber
|
|
|
|
|
|
|
|
|
|
min={1000}
|
|
|
|
|
max={60000}
|
|
|
|
|
step={1000}
|
|
|
|
|
onChange={(value) => handleDataSourceConfigChange('wind', 'timeout', value)}
|
|
|
|
|
/>
|
|
|
|
|
</Item>
|
|
|
|
|
</Col>
|
|
|
|
|
<Col span={6}>
|
|
|
|
|
<Item label="重试次数">
|
|
|
|
|
<InputNumber
|
|
|
|
|
|
|
|
|
|
min={0}
|
|
|
|
|
max={10}
|
|
|
|
|
step={1}
|
|
|
|
|
onChange={(value) => handleDataSourceConfigChange('wind', 'retries', value)}
|
|
|
|
|
/>
|
|
|
|
|
</Item>
|
|
|
|
|
</Col>
|
|
|
|
|
</Row>
|
|
|
|
|
<div style={{ marginTop: 16 }}>
|
|
|
|
|
<Button
|
|
|
|
|
type="primary"
|
|
|
|
|
onClick={() => testDataSourceConnection('Wind')}
|
|
|
|
|
style={{ marginRight: 8 }}
|
|
|
|
|
>
|
|
|
|
|
测试连接
|
|
|
|
|
</Button>
|
|
|
|
|
<Button
|
|
|
|
|
type="default"
|
|
|
|
|
onClick={() => {
|
|
|
|
|
handleDataSourceConfigChange('wind', 'enabled', false);
|
|
|
|
|
handleDataSourceConfigChange('wind', 'apiKey', '');
|
|
|
|
|
handleDataSourceConfigChange('wind', 'apiSecret', '');
|
|
|
|
|
handleDataSourceConfigChange('wind', 'url', 'https://api.wind.com.cn');
|
|
|
|
|
handleDataSourceConfigChange('wind', 'timeout', 30000);
|
|
|
|
|
handleDataSourceConfigChange('wind', 'retries', 3);
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
恢复默认
|
|
|
|
|
</Button>
|
|
|
|
|
</div>
|
|
|
|
|
</Card>
|
|
|
|
|
|
|
|
|
|
{/* 新浪财经配置 */}
|
|
|
|
|
<Card title="新浪财经配置" className="admin-config-card" style={{ marginBottom: 24 }}>
|
|
|
|
|
<Row gutter={[16, 16]}>
|
|
|
|
|
<Col span={6}>
|
|
|
|
|
<Item label="启用">
|
|
|
|
|
<Switch
|
|
|
|
|
defaultChecked={config.dataSource.sina.enabled}
|
|
|
|
|
onChange={(checked) => handleDataSourceConfigChange('sina', 'enabled', checked)}
|
|
|
|
|
/>
|
|
|
|
|
</Item>
|
|
|
|
|
</Col>
|
|
|
|
|
<Col span={12}>
|
|
|
|
|
<Item label="API URL">
|
|
|
|
|
<Input
|
|
|
|
|
|
|
|
|
|
onChange={(e) => handleDataSourceConfigChange('sina', 'url', e.target.value)}
|
|
|
|
|
/>
|
|
|
|
|
</Item>
|
|
|
|
|
</Col>
|
|
|
|
|
<Col span={6}>
|
|
|
|
|
<Item label="超时时间(ms)">
|
|
|
|
|
<InputNumber
|
|
|
|
|
|
|
|
|
|
min={1000}
|
|
|
|
|
max={60000}
|
|
|
|
|
step={1000}
|
|
|
|
|
onChange={(value) => handleDataSourceConfigChange('sina', 'timeout', value)}
|
|
|
|
|
/>
|
|
|
|
|
</Item>
|
|
|
|
|
</Col>
|
|
|
|
|
<Col span={6}>
|
|
|
|
|
<Item label="重试次数">
|
|
|
|
|
<InputNumber
|
|
|
|
|
|
|
|
|
|
min={0}
|
|
|
|
|
max={10}
|
|
|
|
|
step={1}
|
|
|
|
|
onChange={(value) => handleDataSourceConfigChange('sina', 'retries', value)}
|
|
|
|
|
/>
|
|
|
|
|
</Item>
|
|
|
|
|
</Col>
|
|
|
|
|
<Col span={6}>
|
|
|
|
|
<Item label="刷新间隔(ms)">
|
|
|
|
|
<InputNumber
|
|
|
|
|
|
|
|
|
|
min={1000}
|
|
|
|
|
max={300000}
|
|
|
|
|
step={1000}
|
|
|
|
|
onChange={(value) => handleDataSourceConfigChange('sina', 'refreshInterval', value)}
|
|
|
|
|
/>
|
|
|
|
|
</Item>
|
|
|
|
|
</Col>
|
|
|
|
|
</Row>
|
|
|
|
|
<div style={{ marginTop: 16 }}>
|
|
|
|
|
<Button
|
|
|
|
|
type="primary"
|
|
|
|
|
onClick={() => testDataSourceConnection('新浪财经')}
|
|
|
|
|
style={{ marginRight: 8 }}
|
|
|
|
|
>
|
|
|
|
|
测试连接
|
|
|
|
|
</Button>
|
|
|
|
|
<Button
|
|
|
|
|
type="default"
|
|
|
|
|
onClick={() => {
|
|
|
|
|
handleDataSourceConfigChange('sina', 'enabled', false);
|
|
|
|
|
handleDataSourceConfigChange('sina', 'url', 'https://finance.sina.com.cn');
|
|
|
|
|
handleDataSourceConfigChange('sina', 'timeout', 10000);
|
|
|
|
|
handleDataSourceConfigChange('sina', 'retries', 3);
|
|
|
|
|
handleDataSourceConfigChange('sina', 'refreshInterval', 60000);
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
恢复默认
|
|
|
|
|
</Button>
|
|
|
|
|
</div>
|
|
|
|
|
</Card>
|
|
|
|
|
</>
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
]}
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
<Divider />
|
|
|
|
|
|
|
|
|
|
|