Merge branch 'beta' into nofxos-beta

# Conflicts:
#	config/database.go
#	config/database_pg.go
#	docker-compose.yml
#	main.go
#	manager/trader_manager.go
#	web/src/components/AITradersPage.tsx
This commit is contained in:
icy
2025-11-06 02:06:05 +08:00
2 changed files with 80 additions and 4 deletions

View File

@@ -742,6 +742,48 @@ func (d *Database) GetExchanges(userID string) ([]*ExchangeConfig, error) {
return exchanges, nil
}
// GetExchangesForAPI 获取交易所配置专用于API返回排除敏感字段
func (d *Database) GetExchangesForAPI(userID string) ([]*ExchangeConfig, error) {
rows, err := d.db.Query(`
SELECT id, user_id, name, type, enabled,
CASE
WHEN type = 'hyperliquid' THEN ''
ELSE COALESCE(api_key, '')
END as api_key,
'' as secret_key,
testnet,
COALESCE(hyperliquid_wallet_addr, '') as hyperliquid_wallet_addr,
COALESCE(aster_user, '') as aster_user,
COALESCE(aster_signer, '') as aster_signer,
'' as aster_private_key,
created_at, updated_at
FROM exchanges WHERE user_id = ? ORDER BY id
`, userID)
if err != nil {
return nil, err
}
defer rows.Close()
// 初始化为空切片而不是nil确保JSON序列化为[]而不是null
exchanges := make([]*ExchangeConfig, 0)
for rows.Next() {
var exchange ExchangeConfig
err := rows.Scan(
&exchange.ID, &exchange.UserID, &exchange.Name, &exchange.Type,
&exchange.Enabled, &exchange.APIKey, &exchange.SecretKey, &exchange.Testnet,
&exchange.HyperliquidWalletAddr, &exchange.AsterUser,
&exchange.AsterSigner, &exchange.AsterPrivateKey,
&exchange.CreatedAt, &exchange.UpdatedAt,
)
if err != nil {
return nil, err
}
exchanges = append(exchanges, &exchange)
}
return exchanges, nil
}
// UpdateExchange 更新交易所配置,如果不存在则创建用户特定配置
func (d *Database) UpdateExchange(userID, id string, enabled bool, apiKey, secretKey string, testnet bool, hyperliquidWalletAddr, asterUser, asterSigner, asterPrivateKey string) error {
log.Printf("🔧 UpdateExchange: userID=%s, id=%s, enabled=%v", userID, id, enabled)

View File

@@ -1092,6 +1092,7 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
{showExchangeModal && (
<ExchangeConfigModal
allExchanges={supportedExchanges}
configuredExchanges={allExchanges}
editingExchangeId={editingExchange}
onSave={handleSaveExchangeConfig}
onDelete={handleDeleteExchangeConfig}
@@ -1560,6 +1561,7 @@ function ModelConfigModal({
// Exchange Configuration Modal Component
function ExchangeConfigModal({
allExchanges,
configuredExchanges,
editingExchangeId,
onSave,
onDelete,
@@ -1567,6 +1569,7 @@ function ExchangeConfigModal({
language,
}: {
allExchanges: Exchange[]
configuredExchanges: Exchange[]
editingExchangeId: string | null
onSave: (
exchangeId: string,
@@ -1600,15 +1603,20 @@ function ExchangeConfigModal({
// 币安配置指南展开状态
const [showBinanceGuide, setShowBinanceGuide] = useState(false)
// Hyperliquid 特定字段
const [hyperliquidWalletAddr, setHyperliquidWalletAddr] = useState('')
// Aster 特定字段
const [asterUser, setAsterUser] = useState('')
const [asterSigner, setAsterSigner] = 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(() => {
@@ -1618,6 +1626,9 @@ function ExchangeConfigModal({
setPassphrase('') // Don't load existing passphrase for security
setTestnet(selectedExchange.testnet || false)
// Hyperliquid 字段
setHyperliquidWalletAddr(selectedExchange.hyperliquidWalletAddr || '')
// Aster 字段
setAsterUser(selectedExchange.asterUser || '')
setAsterSigner(selectedExchange.asterSigner || '')
@@ -2069,6 +2080,29 @@ function ExchangeConfigModal({
<div className="text-xs mt-1" style={{ color: '#848E9C' }}>
{t('hyperliquidPrivateKeyDesc', language)}
</div>
</div>
<div>
<label
className="block text-sm font-semibold mb-2"
style={{ color: '#EAECEF' }}
>
</label>
<input
type="text"
value={hyperliquidWalletAddr}
onChange={(e) => setHyperliquidWalletAddr(e.target.value)}
placeholder="钱包地址(可选,通常由私钥自动生成)"
className="w-full px-3 py-2 rounded"
style={{
background: '#0B0E11',
border: '1px solid #2B3139',
color: '#EAECEF',
}}
/>
<div className="text-xs mt-1" style={{ color: '#848E9C' }}>
</div>
</div>
</>
)}