mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-07-14 00:07:01 +08:00
feat: exchange api security handle
This commit is contained in:
@@ -399,6 +399,19 @@ type ExchangeConfig struct {
|
|||||||
Testnet bool `json:"testnet,omitempty"`
|
Testnet bool `json:"testnet,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SafeExchangeConfig 安全的交易所配置响应结构(不包含敏感信息)
|
||||||
|
type SafeExchangeConfig struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
UserID string `json:"user_id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Enabled bool `json:"enabled"`
|
||||||
|
Testnet bool `json:"testnet"`
|
||||||
|
Deleted bool `json:"deleted"`
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
type UpdateModelConfigRequest struct {
|
type UpdateModelConfigRequest struct {
|
||||||
Models map[string]struct {
|
Models map[string]struct {
|
||||||
Enabled bool `json:"enabled"`
|
Enabled bool `json:"enabled"`
|
||||||
@@ -1023,7 +1036,23 @@ func (s *Server) handleGetExchangeConfigs(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
log.Printf("✅ 找到 %d 个交易所配置", len(exchanges))
|
log.Printf("✅ 找到 %d 个交易所配置", len(exchanges))
|
||||||
|
|
||||||
c.JSON(http.StatusOK, exchanges)
|
// 转换为安全的响应结构,过滤敏感信息
|
||||||
|
safeExchanges := make([]SafeExchangeConfig, len(exchanges))
|
||||||
|
for i, exchange := range exchanges {
|
||||||
|
safeExchanges[i] = SafeExchangeConfig{
|
||||||
|
ID: exchange.ID,
|
||||||
|
UserID: exchange.UserID,
|
||||||
|
Name: exchange.Name,
|
||||||
|
Type: exchange.Type,
|
||||||
|
Enabled: exchange.Enabled,
|
||||||
|
Testnet: exchange.Testnet,
|
||||||
|
Deleted: exchange.Deleted,
|
||||||
|
CreatedAt: exchange.CreatedAt,
|
||||||
|
UpdatedAt: exchange.UpdatedAt,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, safeExchanges)
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleUpdateExchangeConfigs 更新交易所配置
|
// handleUpdateExchangeConfigs 更新交易所配置
|
||||||
|
|||||||
@@ -150,30 +150,9 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
|
|||||||
allExchanges?.filter((e) => {
|
allExchanges?.filter((e) => {
|
||||||
if (!e.enabled) return false
|
if (!e.enabled) return false
|
||||||
|
|
||||||
// Aster 交易所需要特殊字段
|
// 由于API不再返回敏感字段信息,只能基于enabled状态判断
|
||||||
if (e.id === 'aster') {
|
// 实际的配置验证将在后端进行
|
||||||
return (
|
return true
|
||||||
e.asterUser &&
|
|
||||||
e.asterUser.trim() !== '' &&
|
|
||||||
e.asterSigner &&
|
|
||||||
e.asterSigner.trim() !== '' &&
|
|
||||||
e.asterPrivateKey &&
|
|
||||||
e.asterPrivateKey.trim() !== ''
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hyperliquid 只需要私钥(作为apiKey),钱包地址会自动从私钥生成
|
|
||||||
if (e.id === 'hyperliquid') {
|
|
||||||
return e.apiKey && e.apiKey.trim() !== ''
|
|
||||||
}
|
|
||||||
|
|
||||||
// Binance 等其他交易所需要 apiKey 和 secretKey
|
|
||||||
return (
|
|
||||||
e.apiKey &&
|
|
||||||
e.apiKey.trim() !== '' &&
|
|
||||||
e.secretKey &&
|
|
||||||
e.secretKey.trim() !== ''
|
|
||||||
)
|
|
||||||
}) || []
|
}) || []
|
||||||
|
|
||||||
// 检查模型是否正在被运行中的交易员使用
|
// 检查模型是否正在被运行中的交易员使用
|
||||||
@@ -818,7 +797,7 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className={`w-2.5 h-2.5 md:w-3 md:h-3 rounded-full flex-shrink-0 ${exchange.enabled && exchange.apiKey ? 'bg-green-400' : 'bg-gray-500'}`}
|
className={`w-2.5 h-2.5 md:w-3 md:h-3 rounded-full flex-shrink-0 ${exchange.enabled ? 'bg-green-400' : 'bg-gray-500'}`}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
@@ -1691,21 +1670,18 @@ function ExchangeConfigModal({
|
|||||||
? t('hyperliquidExchangeName', language)
|
? t('hyperliquidExchangeName', language)
|
||||||
: undefined
|
: undefined
|
||||||
|
|
||||||
// 如果是编辑现有交易所,初始化表单数据
|
// 如果是编辑现有交易所,清空所有敏感字段以保证安全
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (editingExchangeId && selectedExchange) {
|
if (editingExchangeId && selectedExchange) {
|
||||||
setApiKey(selectedExchange.apiKey || '')
|
// 编辑模式下清空所有敏感字段,用户需要重新输入
|
||||||
setSecretKey(selectedExchange.secretKey || '')
|
setApiKey('')
|
||||||
setPassphrase('') // Don't load existing passphrase for security
|
setSecretKey('')
|
||||||
|
setPassphrase('')
|
||||||
setTestnet(selectedExchange.testnet || false)
|
setTestnet(selectedExchange.testnet || false)
|
||||||
|
setHyperliquidWalletAddr('')
|
||||||
// Hyperliquid 字段
|
setAsterUser('')
|
||||||
setHyperliquidWalletAddr(selectedExchange.hyperliquidWalletAddr || '')
|
setAsterSigner('')
|
||||||
|
setAsterPrivateKey('')
|
||||||
// Aster 字段
|
|
||||||
setAsterUser(selectedExchange.asterUser || '')
|
|
||||||
setAsterSigner(selectedExchange.asterSigner || '')
|
|
||||||
setAsterPrivateKey('') // Don't load existing private key for security
|
|
||||||
}
|
}
|
||||||
}, [editingExchangeId, selectedExchange])
|
}, [editingExchangeId, selectedExchange])
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,11 @@ export class CryptoService {
|
|||||||
private static publicKeyPEM: string | null = null;
|
private static publicKeyPEM: string | null = null;
|
||||||
|
|
||||||
static async initialize(publicKeyPEM: string) {
|
static async initialize(publicKeyPEM: string) {
|
||||||
|
// 检查 Web Crypto API 是否可用
|
||||||
|
if (!window.crypto || !window.crypto.subtle) {
|
||||||
|
throw new Error('Web Crypto API is not available. Please use HTTPS or localhost to access the application.');
|
||||||
|
}
|
||||||
|
|
||||||
if (this.publicKey && this.publicKeyPEM === publicKeyPEM) {
|
if (this.publicKey && this.publicKeyPEM === publicKeyPEM) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -108,19 +108,14 @@ export interface AIModel {
|
|||||||
|
|
||||||
export interface Exchange {
|
export interface Exchange {
|
||||||
id: string
|
id: string
|
||||||
|
user_id: string
|
||||||
name: string
|
name: string
|
||||||
type: 'cex' | 'dex'
|
type: 'cex' | 'dex'
|
||||||
enabled: boolean
|
enabled: boolean
|
||||||
apiKey?: string
|
|
||||||
secretKey?: string
|
|
||||||
testnet?: boolean
|
testnet?: boolean
|
||||||
// Hyperliquid 特定字段
|
deleted: boolean
|
||||||
hyperliquidWalletAddr?: string
|
created_at: string
|
||||||
// Aster 特定字段
|
updated_at: string
|
||||||
asterUser?: string
|
|
||||||
asterSigner?: string
|
|
||||||
asterPrivateKey?: string
|
|
||||||
deleted?: boolean
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CreateTraderRequest {
|
export interface CreateTraderRequest {
|
||||||
|
|||||||
Reference in New Issue
Block a user