mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-07-16 17:12:25 +08:00
fix: enhance Exchange configuration security and UI display
修复交易所配置的显示问题,并加强API接口安全性: 🎨 UI改进: - 优化交易所配置信息的编辑和显示逻辑 - 改进前端交易所配置组件的交互体验 🛡️ 安全加固: - 修复交易所配置接口中的敏感信息泄露漏洞 - handleGetExchangeConfigs: 清空返回数据中的敏感密钥 - handleGetSupportedExchanges: 加固无认证公开接口安全性 📋 密钥过滤策略: - aster: 清空 asterPrivateKey 私钥 - binance: 清空 secretKey API密钥 - hyperliquid: 清空 apiKey API密钥 🔒 影响范围: - GET /api/exchanges (需认证) - GET /api/supported-exchanges (公开接口) - 交易所配置前端组件 Co-Authored-By: tinkle-community <tinklefund@gmail.com>
This commit is contained in:
@@ -697,6 +697,21 @@ func (s *Server) handleGetExchangeConfigs(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
log.Printf("✅ 找到 %d 个交易所配置", len(exchanges))
|
log.Printf("✅ 找到 %d 个交易所配置", len(exchanges))
|
||||||
|
|
||||||
|
// 🛡️ 安全过滤:根据交易所类型清空对应的敏感密钥字段
|
||||||
|
for _, exchange := range exchanges {
|
||||||
|
switch exchange.ID {
|
||||||
|
case "aster":
|
||||||
|
// Aster交易所:清空私钥
|
||||||
|
exchange.AsterPrivateKey = ""
|
||||||
|
case "binance":
|
||||||
|
// Binance交易所:清空Secret Key
|
||||||
|
exchange.SecretKey = ""
|
||||||
|
case "hyperliquid":
|
||||||
|
// Hyperliquid交易所:清空API Key
|
||||||
|
exchange.APIKey = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
c.JSON(http.StatusOK, exchanges)
|
c.JSON(http.StatusOK, exchanges)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1458,6 +1473,21 @@ func (s *Server) handleGetSupportedExchanges(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 🛡️ 安全过滤:根据交易所类型清空对应的敏感密钥字段(此接口无需认证,风险更高)
|
||||||
|
for _, exchange := range exchanges {
|
||||||
|
switch exchange.ID {
|
||||||
|
case "aster":
|
||||||
|
// Aster交易所:清空私钥
|
||||||
|
exchange.AsterPrivateKey = ""
|
||||||
|
case "binance":
|
||||||
|
// Binance交易所:清空Secret Key
|
||||||
|
exchange.SecretKey = ""
|
||||||
|
case "hyperliquid":
|
||||||
|
// Hyperliquid交易所:清空API Key
|
||||||
|
exchange.APIKey = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
c.JSON(http.StatusOK, exchanges)
|
c.JSON(http.StatusOK, exchanges)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -812,6 +812,7 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
|
|||||||
{showExchangeModal && (
|
{showExchangeModal && (
|
||||||
<ExchangeConfigModal
|
<ExchangeConfigModal
|
||||||
allExchanges={supportedExchanges}
|
allExchanges={supportedExchanges}
|
||||||
|
configuredExchanges={allExchanges}
|
||||||
editingExchangeId={editingExchange}
|
editingExchangeId={editingExchange}
|
||||||
onSave={handleSaveExchangeConfig}
|
onSave={handleSaveExchangeConfig}
|
||||||
onDelete={handleDeleteExchangeConfig}
|
onDelete={handleDeleteExchangeConfig}
|
||||||
@@ -1144,6 +1145,7 @@ function ModelConfigModal({
|
|||||||
// Exchange Configuration Modal Component
|
// Exchange Configuration Modal Component
|
||||||
function ExchangeConfigModal({
|
function ExchangeConfigModal({
|
||||||
allExchanges,
|
allExchanges,
|
||||||
|
configuredExchanges,
|
||||||
editingExchangeId,
|
editingExchangeId,
|
||||||
onSave,
|
onSave,
|
||||||
onDelete,
|
onDelete,
|
||||||
@@ -1151,6 +1153,7 @@ function ExchangeConfigModal({
|
|||||||
language
|
language
|
||||||
}: {
|
}: {
|
||||||
allExchanges: Exchange[];
|
allExchanges: Exchange[];
|
||||||
|
configuredExchanges: Exchange[];
|
||||||
editingExchangeId: string | null;
|
editingExchangeId: string | null;
|
||||||
onSave: (exchangeId: string, apiKey: string, secretKey?: string, testnet?: boolean, hyperliquidWalletAddr?: string, asterUser?: string, asterSigner?: string, asterPrivateKey?: string) => Promise<void>;
|
onSave: (exchangeId: string, apiKey: string, secretKey?: string, testnet?: boolean, hyperliquidWalletAddr?: string, asterUser?: string, asterSigner?: string, asterPrivateKey?: string) => Promise<void>;
|
||||||
onDelete: (exchangeId: string) => void;
|
onDelete: (exchangeId: string) => void;
|
||||||
@@ -1171,8 +1174,12 @@ function ExchangeConfigModal({
|
|||||||
const [asterSigner, setAsterSigner] = useState('');
|
const [asterSigner, setAsterSigner] = useState('');
|
||||||
const [asterPrivateKey, setAsterPrivateKey] = useState('');
|
const [asterPrivateKey, setAsterPrivateKey] = useState('');
|
||||||
|
|
||||||
// 获取当前编辑的交易所信息
|
// 获取当前选择的交易所信息
|
||||||
const selectedExchange = allExchanges?.find(e => e.id === selectedExchangeId);
|
// 编辑模式:从 configuredExchanges 查找(包含用户配置的 apiKey、secretKey 等)
|
||||||
|
// 新增模式:从 allExchanges 查找(系统支持的交易所列表)
|
||||||
|
const selectedExchange = editingExchangeId
|
||||||
|
? configuredExchanges?.find(e => e.id === selectedExchangeId)
|
||||||
|
: allExchanges?.find(e => e.id === selectedExchangeId);
|
||||||
|
|
||||||
// 如果是编辑现有交易所,初始化表单数据
|
// 如果是编辑现有交易所,初始化表单数据
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -1181,10 +1188,10 @@ function ExchangeConfigModal({
|
|||||||
setSecretKey(selectedExchange.secretKey || '');
|
setSecretKey(selectedExchange.secretKey || '');
|
||||||
setPassphrase(''); // Don't load existing passphrase for security
|
setPassphrase(''); // Don't load existing passphrase for security
|
||||||
setTestnet(selectedExchange.testnet || false);
|
setTestnet(selectedExchange.testnet || false);
|
||||||
|
|
||||||
// Hyperliquid 字段
|
// Hyperliquid 字段
|
||||||
setHyperliquidWalletAddr(selectedExchange.hyperliquidWalletAddr || '');
|
setHyperliquidWalletAddr(selectedExchange.hyperliquidWalletAddr || '');
|
||||||
|
|
||||||
// Aster 字段
|
// Aster 字段
|
||||||
setAsterUser(selectedExchange.asterUser || '');
|
setAsterUser(selectedExchange.asterUser || '');
|
||||||
setAsterSigner(selectedExchange.asterSigner || '');
|
setAsterSigner(selectedExchange.asterSigner || '');
|
||||||
|
|||||||
Reference in New Issue
Block a user