diff --git a/DOCKER_DEPLOY.en.md b/DOCKER_DEPLOY.en.md index 06464b32..bd909273 100644 --- a/DOCKER_DEPLOY.en.md +++ b/DOCKER_DEPLOY.en.md @@ -97,7 +97,7 @@ docker compose up -d Once deployed, open your browser and visit: - **Web Interface**: http://localhost:3000 -- **API Health Check**: http://localhost:8080/health +- **API Health Check**: http://localhost:8080/api/health ## 📊 Service Management @@ -280,7 +280,7 @@ docker inspect nofx-backend | jq '.[0].State.Health' docker inspect nofx-frontend | jq '.[0].State.Health' # Manually test health endpoints -curl http://localhost:8080/health +curl http://localhost:8080/api/health curl http://localhost:3000/health ``` diff --git a/DOCKER_DEPLOY.md b/DOCKER_DEPLOY.md index cc268261..49667356 100644 --- a/DOCKER_DEPLOY.md +++ b/DOCKER_DEPLOY.md @@ -100,7 +100,7 @@ docker compose up -d 部署成功后,打开浏览器访问: - **Web 界面**: http://localhost:3000 -- **API 文档**: http://localhost:8080/health +- **API 文档**: http://localhost:8080/api/health ## 📊 服务管理 @@ -281,7 +281,7 @@ docker inspect nofx-backend | jq '.[0].State.Health' docker inspect nofx-frontend | jq '.[0].State.Health' # 手动测试健康端点 -curl http://localhost:8080/health +curl http://localhost:8080/api/health curl http://localhost:3000/health ``` diff --git a/PM2_DEPLOYMENT.md b/PM2_DEPLOYMENT.md index c9806ea3..3668a29e 100644 --- a/PM2_DEPLOYMENT.md +++ b/PM2_DEPLOYMENT.md @@ -79,7 +79,7 @@ npm install -g pm2 - **前端 Web 界面**: http://localhost:3000 - **后端 API**: http://localhost:8080 -- **健康检查**: http://localhost:8080/health +- **健康检查**: http://localhost:8080/api/health --- diff --git a/README.md b/README.md index ec3432d9..7ac4f409 100644 --- a/README.md +++ b/README.md @@ -887,7 +887,7 @@ Open your web browser and visit: ```bash # In a new terminal window -curl http://localhost:8080/health +curl http://localhost:8080/api/health ``` Should return: `{"status":"ok"}` @@ -1137,7 +1137,7 @@ GET /api/performance?trader_id=xxx # AI performance analysis ### System Endpoints ```bash -GET /health # Health check +GET /api/health # Health check ``` --- diff --git a/README.ru.md b/README.ru.md index b93a3e14..2d2e6ff3 100644 --- a/README.ru.md +++ b/README.ru.md @@ -796,7 +796,7 @@ VITE v5.x.x ready in xxx ms ```bash # В новом окне терминала -curl http://localhost:8080/health +curl http://localhost:8080/api/health ``` Должно вернуть: `{"status":"ok"}` diff --git a/README.uk.md b/README.uk.md index ce751669..0ec6d5df 100644 --- a/README.uk.md +++ b/README.uk.md @@ -796,7 +796,7 @@ VITE v5.x.x ready in xxx ms ```bash # У новому вікні терміналу -curl http://localhost:8080/health +curl http://localhost:8080/api/health ``` Повинно повернути: `{"status":"ok"}` diff --git a/README.zh-CN.md b/README.zh-CN.md index e4be410d..75be2f64 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -869,7 +869,7 @@ VITE v5.x.x ready in xxx ms ```bash # 在新终端窗口中 -curl http://localhost:8080/health +curl http://localhost:8080/api/health ``` 应返回:`{"status":"ok"}` @@ -1076,7 +1076,7 @@ GET /api/statistics?trader_id=xxx # 统计信息 ### 系统接口 ```bash -GET /health # 健康检查 +GET /api/health # 健康检查 GET /api/config # 系统配置 ``` diff --git a/api/server.go b/api/server.go index 110f0a9c..ad16470a 100644 --- a/api/server.go +++ b/api/server.go @@ -66,12 +66,12 @@ func corsMiddleware() gin.HandlerFunc { // setupRoutes 设置路由 func (s *Server) setupRoutes() { - // 健康检查 - s.router.Any("/health", s.handleHealth) - // API路由组 api := s.router.Group("/api") { + // 健康检查 + api.Any("/health", s.handleHealth) + // 认证相关路由(无需认证) api.POST("/register", s.handleRegister) api.POST("/login", s.handleLogin) @@ -84,6 +84,10 @@ func (s *Server) setupRoutes() { // 系统配置(无需认证) api.GET("/config", s.handleGetSystemConfig) + + // 系统提示词模板管理(无需认证) + api.GET("/prompt-templates", s.handleGetPromptTemplates) + api.GET("/prompt-templates/:name", s.handleGetPromptTemplate) // 需要认证的路由 protected := api.Group("/", s.authMiddleware()) @@ -110,9 +114,6 @@ func (s *Server) setupRoutes() { protected.GET("/user/signal-sources", s.handleGetUserSignalSource) protected.POST("/user/signal-sources", s.handleSaveUserSignalSource) - // 系统提示词模板管理 - protected.GET("/prompt-templates", s.handleGetPromptTemplates) - protected.GET("/prompt-templates/:name", s.handleGetPromptTemplate) // 竞赛总览 protected.GET("/competition", s.handleCompetition) @@ -1398,7 +1399,7 @@ func (s *Server) Start() error { addr := fmt.Sprintf(":%d", s.port) log.Printf("🌐 API服务器启动在 http://localhost%s", addr) log.Printf("📊 API文档:") - log.Printf(" • GET /health - 健康检查") + log.Printf(" • GET /api/health - 健康检查") log.Printf(" • GET /api/traders - AI交易员列表") log.Printf(" • POST /api/traders - 创建新的AI交易员") log.Printf(" • DELETE /api/traders/:id - 删除AI交易员") diff --git a/config/database.go b/config/database.go index 624433bc..c5eef755 100644 --- a/config/database.go +++ b/config/database.go @@ -853,7 +853,12 @@ func (d *Database) GetTraderConfig(userID, traderID string) (*TraderRecord, *AIM err := d.db.QueryRow(` SELECT - t.id, t.user_id, t.name, t.ai_model_id, t.exchange_id, t.initial_balance, t.scan_interval_minutes, t.is_running, t.created_at, t.updated_at, + t.id, t.user_id, t.name, t.ai_model_id, t.exchange_id, t.initial_balance, t.scan_interval_minutes, t.is_running, + COALESCE(t.btc_eth_leverage, 5) as btc_eth_leverage, COALESCE(t.altcoin_leverage, 5) as altcoin_leverage, + COALESCE(t.trading_symbols, '') as trading_symbols, COALESCE(t.use_coin_pool, 0) as use_coin_pool, + COALESCE(t.use_oi_top, 0) as use_oi_top, COALESCE(t.custom_prompt, '') as custom_prompt, + COALESCE(t.override_base_prompt, 0) as override_base_prompt, COALESCE(t.is_cross_margin, 1) as is_cross_margin, + t.created_at, t.updated_at, a.id, a.user_id, a.name, a.provider, a.enabled, a.api_key, a.created_at, a.updated_at, e.id, e.user_id, e.name, e.type, e.enabled, e.api_key, e.secret_key, e.testnet, COALESCE(e.hyperliquid_wallet_addr, '') as hyperliquid_wallet_addr, @@ -868,6 +873,8 @@ func (d *Database) GetTraderConfig(userID, traderID string) (*TraderRecord, *AIM `, traderID, userID).Scan( &trader.ID, &trader.UserID, &trader.Name, &trader.AIModelID, &trader.ExchangeID, &trader.InitialBalance, &trader.ScanIntervalMinutes, &trader.IsRunning, + &trader.BTCETHLeverage, &trader.AltcoinLeverage, &trader.TradingSymbols, &trader.UseCoinPool, + &trader.UseOITop, &trader.CustomPrompt, &trader.OverrideBasePrompt, &trader.IsCrossMargin, &trader.CreatedAt, &trader.UpdatedAt, &aiModel.ID, &aiModel.UserID, &aiModel.Name, &aiModel.Provider, &aiModel.Enabled, &aiModel.APIKey, &aiModel.CreatedAt, &aiModel.UpdatedAt, diff --git a/docker-compose.yml b/docker-compose.yml index 612f1466..e2f6c905 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -19,7 +19,7 @@ services: networks: - nofx-network healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:8080/health"] + test: ["CMD", "curl", "-f", "http://localhost:8080/api/health"] interval: 30s timeout: 10s retries: 3 diff --git a/docker/Dockerfile.backend b/docker/Dockerfile.backend index 18387f67..c25700f2 100644 --- a/docker/Dockerfile.backend +++ b/docker/Dockerfile.backend @@ -63,6 +63,6 @@ COPY --from=backend-builder /app/nofx . EXPOSE 8080 HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \ - CMD wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1 + CMD wget --no-verbose --tries=1 --spider http://localhost:8080/api/health || exit 1 CMD ["./nofx"] diff --git a/start.sh b/start.sh index 04f0478c..47cb2536 100755 --- a/start.sh +++ b/start.sh @@ -227,7 +227,7 @@ status() { $COMPOSE_CMD ps echo "" print_info "健康检查:" - curl -s "http://localhost:${NOFX_BACKEND_PORT}/health" | jq '.' || echo "后端未响应" + curl -s "http://localhost:${NOFX_BACKEND_PORT}/api/health" | jq '.' || echo "后端未响应" } # ------------------------------------------------------------------------ diff --git a/web/src/components/AITradersPage.tsx b/web/src/components/AITradersPage.tsx index b031340c..d5937fb0 100644 --- a/web/src/components/AITradersPage.tsx +++ b/web/src/components/AITradersPage.tsx @@ -101,12 +101,15 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) { // Aster 交易所需要特殊字段 if (e.id === 'aster') { - return e.asterUser && e.asterSigner && e.asterPrivateKey; + return e.asterUser && e.asterUser.trim() !== '' && + e.asterSigner && e.asterSigner.trim() !== '' && + e.asterPrivateKey && e.asterPrivateKey.trim() !== ''; } - // Hyperliquid 只需要私钥(作为apiKey),不需要secretKey + // Hyperliquid 只需要私钥(作为apiKey)和钱包地址 if (e.id === 'hyperliquid') { - return e.apiKey && e.hyperliquidWalletAddr; + return e.apiKey && e.apiKey.trim() !== '' && + e.hyperliquidWalletAddr && e.hyperliquidWalletAddr.trim() !== ''; } // Binance 等其他交易所需要 apiKey 和 secretKey @@ -372,11 +375,31 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) { if (existingExchange) { // 更新现有配置 updatedExchanges = allExchanges?.map(e => - e.id === exchangeId ? { ...e, apiKey, secretKey, testnet, hyperliquidWalletAddr, asterUser, asterSigner, asterPrivateKey, enabled: true } : e + e.id === exchangeId ? { + ...e, + apiKey, + secretKey, + testnet, + hyperliquidWalletAddr, + asterUser, + asterSigner, + asterPrivateKey, + enabled: true + } : e ) || []; } else { // 添加新配置 - const newExchange = { ...exchangeToUpdate, apiKey, secretKey, testnet, hyperliquidWalletAddr, asterUser, asterSigner, asterPrivateKey, enabled: true }; + const newExchange = { + ...exchangeToUpdate, + apiKey, + secretKey, + testnet, + hyperliquidWalletAddr, + asterUser, + asterSigner, + asterPrivateKey, + enabled: true + }; updatedExchanges = [...(allExchanges || []), newExchange]; } @@ -1121,6 +1144,14 @@ function ExchangeConfigModal({ const [secretKey, setSecretKey] = useState(''); const [passphrase, setPassphrase] = useState(''); const [testnet, setTestnet] = 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); @@ -1132,6 +1163,14 @@ function ExchangeConfigModal({ setSecretKey(selectedExchange.secretKey || ''); setPassphrase(''); // Don't load existing passphrase for security setTestnet(selectedExchange.testnet || false); + + // Hyperliquid 字段 + setHyperliquidWalletAddr(selectedExchange.hyperliquidWalletAddr || ''); + + // Aster 字段 + setAsterUser(selectedExchange.asterUser || ''); + setAsterSigner(selectedExchange.asterSigner || ''); + setAsterPrivateKey(''); // Don't load existing private key for security } }, [editingExchangeId, selectedExchange]); @@ -1142,11 +1181,21 @@ function ExchangeConfigModal({ // 根据交易所类型验证不同字段 if (selectedExchange?.id === 'binance') { if (!apiKey.trim() || !secretKey.trim()) return; + await onSave(selectedExchangeId, apiKey.trim(), secretKey.trim(), testnet); + } else if (selectedExchange?.id === 'hyperliquid') { + if (!apiKey.trim() || !hyperliquidWalletAddr.trim()) return; + await onSave(selectedExchangeId, apiKey.trim(), '', testnet, hyperliquidWalletAddr.trim()); + } else if (selectedExchange?.id === 'aster') { + if (!asterUser.trim() || !asterSigner.trim() || !asterPrivateKey.trim()) return; + await onSave(selectedExchangeId, '', '', testnet, undefined, asterUser.trim(), asterSigner.trim(), asterPrivateKey.trim()); } else if (selectedExchange?.id === 'okx') { if (!apiKey.trim() || !secretKey.trim() || !passphrase.trim()) return; + await onSave(selectedExchangeId, apiKey.trim(), secretKey.trim(), testnet); + } else { + // 默认情况(其他CEX交易所) + if (!apiKey.trim() || !secretKey.trim()) return; + await onSave(selectedExchangeId, apiKey.trim(), secretKey.trim(), testnet); } - - await onSave(selectedExchangeId, apiKey.trim(), secretKey.trim(), testnet, undefined, undefined, undefined, undefined); }; // 可选择的交易所列表(所有支持的交易所) @@ -1217,51 +1266,147 @@ function ExchangeConfigModal({ {selectedExchange && ( <> -
+ {traderData.trader_name} 的配置信息 +
+