fix: sanitize API error messages to prevent sensitive info exposure

This commit is contained in:
tinkle-community
2026-01-03 13:11:15 +08:00
parent e07dc0de86
commit 7f7c4ea2a7
5 changed files with 221 additions and 165 deletions

View File

@@ -486,7 +486,7 @@ func (s *Server) handleCreateTrader(c *gin.Context) {
userID := c.GetString("user_id")
var req CreateTraderRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
SafeBadRequest(c, "Invalid request parameters")
return
}
@@ -682,7 +682,7 @@ func (s *Server) handleCreateTrader(c *gin.Context) {
err = s.store.Trader().Create(traderRecord)
if err != nil {
logger.Infof("❌ Failed to create trader: %v", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Failed to create trader: %v", err)})
SafeInternalError(c, "Failed to create trader", err)
return
}
logger.Infof("🔧 DEBUG: CreateTrader succeeded")
@@ -732,7 +732,7 @@ func (s *Server) handleUpdateTrader(c *gin.Context) {
var req UpdateTraderRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
SafeBadRequest(c, "Invalid request parameters")
return
}
@@ -823,7 +823,7 @@ func (s *Server) handleUpdateTrader(c *gin.Context) {
traderRecord.ID, traderRecord.Name, traderRecord.AIModelID, traderRecord.StrategyID, req.StrategyID)
err = s.store.Trader().Update(traderRecord)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Failed to update trader: %v", err)})
SafeInternalError(c, "Failed to update trader", err)
return
}
@@ -854,7 +854,7 @@ func (s *Server) handleDeleteTrader(c *gin.Context) {
// Delete from database
err := s.store.Trader().Delete(userID, traderID)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Failed to delete trader: %v", err)})
SafeInternalError(c, "Failed to delete trader", err)
return
}
@@ -1012,14 +1012,14 @@ func (s *Server) handleUpdateTraderPrompt(c *gin.Context) {
}
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
SafeBadRequest(c, "Invalid request parameters")
return
}
// Update database
err := s.store.Trader().UpdateCustomPrompt(userID, traderID, req.CustomPrompt, req.OverrideBasePrompt)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Failed to update custom prompt: %v", err)})
SafeInternalError(c, "Failed to update custom prompt", err)
return
}
@@ -1044,14 +1044,14 @@ func (s *Server) handleToggleCompetition(c *gin.Context) {
}
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
SafeBadRequest(c, "Invalid request parameters")
return
}
// Update database
err := s.store.Trader().UpdateShowInCompetition(userID, traderID, req.ShowInCompetition)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Failed to update competition visibility: %v", err)})
SafeInternalError(c, "Update competition visibility", err)
return
}
@@ -1150,7 +1150,7 @@ func (s *Server) handleSyncBalance(c *gin.Context) {
if createErr != nil {
logger.Infof("⚠️ Failed to create temporary trader: %v", createErr)
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Failed to connect to exchange: %v", createErr)})
SafeInternalError(c, "Failed to connect to exchange", createErr)
return
}
@@ -1158,7 +1158,7 @@ func (s *Server) handleSyncBalance(c *gin.Context) {
balanceInfo, balanceErr := tempTrader.GetBalance()
if balanceErr != nil {
logger.Infof("⚠️ Failed to query exchange balance: %v", balanceErr)
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Failed to query balance: %v", balanceErr)})
SafeInternalError(c, "Failed to query balance", balanceErr)
return
}
@@ -1302,7 +1302,7 @@ func (s *Server) handleClosePosition(c *gin.Context) {
if createErr != nil {
logger.Infof("⚠️ Failed to create temporary trader: %v", createErr)
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Failed to connect to exchange: %v", createErr)})
SafeInternalError(c, "Failed to connect to exchange", createErr)
return
}
@@ -1344,7 +1344,7 @@ func (s *Server) handleClosePosition(c *gin.Context) {
if closeErr != nil {
logger.Infof("❌ Close position failed: symbol=%s, side=%s, error=%v", req.Symbol, req.Side, closeErr)
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Failed to close position: %v", closeErr)})
SafeInternalError(c, "Failed to close position", closeErr)
return
}
@@ -1582,7 +1582,7 @@ func (s *Server) handleGetModelConfigs(c *gin.Context) {
models, err := s.store.AIModel().List(userID)
if err != nil {
logger.Infof("❌ Failed to get AI model configs: %v", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Failed to get AI model configs: %v", err)})
SafeInternalError(c, "Failed to get AI model configs", err)
return
}
@@ -1684,7 +1684,7 @@ func (s *Server) handleUpdateModelConfigs(c *gin.Context) {
for modelID, modelData := range req.Models {
err := s.store.AIModel().Update(userID, modelID, modelData.Enabled, modelData.APIKey, modelData.CustomAPIURL, modelData.CustomModelName)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Failed to update model %s: %v", modelID, err)})
SafeInternalError(c, fmt.Sprintf("Update model %s", modelID), err)
return
}
}
@@ -1706,8 +1706,7 @@ func (s *Server) handleGetExchangeConfigs(c *gin.Context) {
logger.Infof("🔍 Querying exchange configs for user %s", userID)
exchanges, err := s.store.Exchange().List(userID)
if err != nil {
logger.Infof("❌ Failed to get exchange configs: %v", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Failed to get exchange configs: %v", err)})
SafeInternalError(c, "Failed to get exchange configs", err)
return
}
@@ -1805,7 +1804,7 @@ func (s *Server) handleUpdateExchangeConfigs(c *gin.Context) {
for exchangeID, exchangeData := range req.Exchanges {
err := s.store.Exchange().Update(userID, exchangeID, exchangeData.Enabled, exchangeData.APIKey, exchangeData.SecretKey, exchangeData.Passphrase, exchangeData.Testnet, exchangeData.HyperliquidWalletAddr, exchangeData.AsterUser, exchangeData.AsterSigner, exchangeData.AsterPrivateKey, exchangeData.LighterWalletAddr, exchangeData.LighterPrivateKey, exchangeData.LighterAPIKeyPrivateKey, exchangeData.LighterAPIKeyIndex)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Failed to update exchange %s: %v", exchangeID, err)})
SafeInternalError(c, fmt.Sprintf("Update exchange %s", exchangeID), err)
return
}
}
@@ -1910,7 +1909,7 @@ func (s *Server) handleCreateExchange(c *gin.Context) {
)
if err != nil {
logger.Infof("❌ Failed to create exchange account: %v", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Failed to create exchange account: %v", err)})
SafeInternalError(c, "Failed to create exchange account", err)
return
}
@@ -1953,7 +1952,7 @@ func (s *Server) handleDeleteExchange(c *gin.Context) {
err = s.store.Exchange().Delete(userID, exchangeID)
if err != nil {
logger.Infof("❌ Failed to delete exchange account: %v", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Failed to delete exchange account: %v", err)})
SafeInternalError(c, "Failed to delete exchange account", err)
return
}
@@ -1966,7 +1965,7 @@ func (s *Server) handleTraderList(c *gin.Context) {
userID := c.GetString("user_id")
traders, err := s.store.Trader().List(userID)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Failed to get trader list: %v", err)})
SafeInternalError(c, "Failed to get trader list", err)
return
}
@@ -2019,7 +2018,7 @@ func (s *Server) handleGetTraderConfig(c *gin.Context) {
fullCfg, err := s.store.Trader().GetFullConfig(userID, traderID)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": fmt.Sprintf("Failed to get trader config: %v", err)})
SafeNotFound(c, "Trader config")
return
}
traderConfig := fullCfg.Trader
@@ -2062,13 +2061,13 @@ func (s *Server) handleGetTraderConfig(c *gin.Context) {
func (s *Server) handleStatus(c *gin.Context) {
_, traderID, err := s.getTraderFromQuery(c)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
SafeBadRequest(c, "Invalid trader ID")
return
}
trader, err := s.traderManager.GetTrader(traderID)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
SafeNotFound(c, "Trader")
return
}
@@ -2080,23 +2079,20 @@ func (s *Server) handleStatus(c *gin.Context) {
func (s *Server) handleAccount(c *gin.Context) {
_, traderID, err := s.getTraderFromQuery(c)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
SafeBadRequest(c, "Invalid trader ID")
return
}
trader, err := s.traderManager.GetTrader(traderID)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
SafeNotFound(c, "Trader")
return
}
logger.Infof("📊 Received account info request [%s]", trader.GetName())
account, err := trader.GetAccountInfo()
if err != nil {
logger.Infof("❌ Failed to get account info [%s]: %v", trader.GetName(), err)
c.JSON(http.StatusInternalServerError, gin.H{
"error": fmt.Sprintf("Failed to get account info: %v", err),
})
SafeInternalError(c, "Get account info", err)
return
}
@@ -2113,21 +2109,19 @@ func (s *Server) handleAccount(c *gin.Context) {
func (s *Server) handlePositions(c *gin.Context) {
_, traderID, err := s.getTraderFromQuery(c)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
SafeBadRequest(c, "Invalid trader ID")
return
}
trader, err := s.traderManager.GetTrader(traderID)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
SafeNotFound(c, "Trader")
return
}
positions, err := trader.GetPositions()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"error": fmt.Sprintf("Failed to get position list: %v", err),
})
SafeInternalError(c, "Get positions", err)
return
}
@@ -2138,13 +2132,13 @@ func (s *Server) handlePositions(c *gin.Context) {
func (s *Server) handlePositionHistory(c *gin.Context) {
_, traderID, err := s.getTraderFromQuery(c)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
SafeBadRequest(c, "Invalid trader ID")
return
}
trader, err := s.traderManager.GetTrader(traderID)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
SafeNotFound(c, "Trader")
return
}
@@ -2165,9 +2159,7 @@ func (s *Server) handlePositionHistory(c *gin.Context) {
// Get closed positions
positions, err := store.Position().GetClosedPositions(trader.GetID(), limit)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"error": fmt.Sprintf("Failed to get position history: %v", err),
})
SafeInternalError(c, "Get position history", err)
return
}
@@ -2192,13 +2184,13 @@ func (s *Server) handlePositionHistory(c *gin.Context) {
func (s *Server) handleTrades(c *gin.Context) {
_, traderID, err := s.getTraderFromQuery(c)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
SafeBadRequest(c, "Invalid trader ID")
return
}
trader, err := s.traderManager.GetTrader(traderID)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
SafeNotFound(c, "Trader")
return
}
@@ -2224,9 +2216,7 @@ func (s *Server) handleTrades(c *gin.Context) {
allTrades, err := store.Position().GetRecentTrades(trader.GetID(), limit)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"error": fmt.Sprintf("Failed to get trades: %v", err),
})
SafeInternalError(c, "Get trades", err)
return
}
@@ -2249,13 +2239,13 @@ func (s *Server) handleTrades(c *gin.Context) {
func (s *Server) handleOrders(c *gin.Context) {
_, traderID, err := s.getTraderFromQuery(c)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
SafeBadRequest(c, "Invalid trader ID")
return
}
trader, err := s.traderManager.GetTrader(traderID)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
SafeNotFound(c, "Trader")
return
}
@@ -2283,9 +2273,7 @@ func (s *Server) handleOrders(c *gin.Context) {
// Get all orders for this trader
allOrders, err := store.Order().GetTraderOrders(trader.GetID(), limit)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"error": fmt.Sprintf("Failed to get orders: %v", err),
})
SafeInternalError(c, "Get orders", err)
return
}
@@ -2317,13 +2305,13 @@ func (s *Server) handleOrderFills(c *gin.Context) {
_, traderID, err := s.getTraderFromQuery(c)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
SafeBadRequest(c, "Invalid trader ID")
return
}
trader, err := s.traderManager.GetTrader(traderID)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
SafeNotFound(c, "Trader")
return
}
@@ -2336,9 +2324,7 @@ func (s *Server) handleOrderFills(c *gin.Context) {
// Get fills for this order
fills, err := store.Order().GetOrderFills(orderID)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"error": fmt.Sprintf("Failed to get order fills: %v", err),
})
SafeInternalError(c, "Get order fills", err)
return
}
@@ -2376,30 +2362,21 @@ func (s *Server) handleKlines(c *gin.Context) {
// US Stocks via Alpaca
klines, err = s.getKlinesFromAlpaca(symbol, interval, limit)
if err != nil {
logger.Errorf("❌ Alpaca API failed for %s: %v", symbol, err)
c.JSON(http.StatusInternalServerError, gin.H{
"error": fmt.Sprintf("Failed to get klines from Alpaca: %v", err),
})
SafeInternalError(c, "Get klines from Alpaca", err)
return
}
case "forex", "metals":
// Forex and Metals via Twelve Data
klines, err = s.getKlinesFromTwelveData(symbol, interval, limit)
if err != nil {
logger.Errorf("❌ TwelveData API failed for %s: %v", symbol, err)
c.JSON(http.StatusInternalServerError, gin.H{
"error": fmt.Sprintf("Failed to get klines from TwelveData: %v", err),
})
SafeInternalError(c, "Get klines from TwelveData", err)
return
}
case "hyperliquid", "hyperliquid-xyz", "xyz":
// Hyperliquid native API - supports both crypto perps and stock perps (xyz dex)
klines, err = s.getKlinesFromHyperliquid(symbol, interval, limit)
if err != nil {
logger.Errorf("❌ Hyperliquid API failed for %s: %v", symbol, err)
c.JSON(http.StatusInternalServerError, gin.H{
"error": fmt.Sprintf("Failed to get klines from Hyperliquid: %v", err),
})
SafeInternalError(c, "Get klines from Hyperliquid", err)
return
}
default:
@@ -2407,10 +2384,7 @@ func (s *Server) handleKlines(c *gin.Context) {
symbol = market.Normalize(symbol)
klines, err = s.getKlinesFromCoinank(symbol, interval, exchange, limit)
if err != nil {
logger.Errorf("❌ CoinAnk API failed for %s on %s: %v", symbol, exchange, err)
c.JSON(http.StatusInternalServerError, gin.H{
"error": fmt.Sprintf("Failed to get klines from CoinAnk: %v", err),
})
SafeInternalError(c, "Get klines from CoinAnk", err)
return
}
}
@@ -2728,22 +2702,20 @@ func (s *Server) handleSymbols(c *gin.Context) {
func (s *Server) handleDecisions(c *gin.Context) {
_, traderID, err := s.getTraderFromQuery(c)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
SafeBadRequest(c, "Invalid trader ID")
return
}
trader, err := s.traderManager.GetTrader(traderID)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
SafeNotFound(c, "Trader")
return
}
// Get all historical decision records (unlimited)
records, err := trader.GetStore().Decision().GetLatestRecords(trader.GetID(), 10000)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"error": fmt.Sprintf("Failed to get decision log: %v", err),
})
SafeInternalError(c, "Get decision log", err)
return
}
@@ -2754,13 +2726,13 @@ func (s *Server) handleDecisions(c *gin.Context) {
func (s *Server) handleLatestDecisions(c *gin.Context) {
_, traderID, err := s.getTraderFromQuery(c)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
SafeBadRequest(c, "Invalid trader ID")
return
}
trader, err := s.traderManager.GetTrader(traderID)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
SafeNotFound(c, "Trader")
return
}
@@ -2777,9 +2749,7 @@ func (s *Server) handleLatestDecisions(c *gin.Context) {
records, err := trader.GetStore().Decision().GetLatestRecords(trader.GetID(), limit)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"error": fmt.Sprintf("Failed to get decision log: %v", err),
})
SafeInternalError(c, "Get decision log", err)
return
}
@@ -2796,21 +2766,19 @@ func (s *Server) handleLatestDecisions(c *gin.Context) {
func (s *Server) handleStatistics(c *gin.Context) {
_, traderID, err := s.getTraderFromQuery(c)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
SafeBadRequest(c, "Invalid trader ID")
return
}
trader, err := s.traderManager.GetTrader(traderID)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
SafeNotFound(c, "Trader")
return
}
stats, err := trader.GetStore().Decision().GetStatistics(trader.GetID())
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"error": fmt.Sprintf("Failed to get statistics: %v", err),
})
SafeInternalError(c, "Get statistics", err)
return
}
@@ -2829,9 +2797,7 @@ func (s *Server) handleCompetition(c *gin.Context) {
competition, err := s.traderManager.GetCompetitionData()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"error": fmt.Sprintf("Failed to get competition data: %v", err),
})
SafeInternalError(c, "Get competition data", err)
return
}
@@ -2843,7 +2809,7 @@ func (s *Server) handleCompetition(c *gin.Context) {
func (s *Server) handleEquityHistory(c *gin.Context) {
_, traderID, err := s.getTraderFromQuery(c)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
SafeBadRequest(c, "Invalid trader ID")
return
}
@@ -2851,9 +2817,7 @@ func (s *Server) handleEquityHistory(c *gin.Context) {
// Every 3 minutes per cycle: 10000 records = about 20 days of data
snapshots, err := s.store.Equity().GetLatest(traderID, 10000)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"error": fmt.Sprintf("Failed to get historical data: %v", err),
})
SafeInternalError(c, "Get historical data", err)
return
}
@@ -2931,7 +2895,8 @@ func (s *Server) authMiddleware() gin.HandlerFunc {
// Validate JWT token
claims, err := auth.ValidateJWT(tokenString)
if err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid token: " + err.Error()})
logger.Errorf("[Auth] Invalid token: %v", err)
c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid or expired token"})
c.Abort()
return
}
@@ -2999,7 +2964,7 @@ func (s *Server) handleRegister(c *gin.Context) {
}
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
SafeBadRequest(c, "Invalid request parameters")
return
}
@@ -3036,7 +3001,7 @@ func (s *Server) handleRegister(c *gin.Context) {
err = s.store.User().Create(user)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create user: " + err.Error()})
SafeInternalError(c, "Failed to create user", err)
return
}
@@ -3059,14 +3024,14 @@ func (s *Server) handleCompleteRegistration(c *gin.Context) {
}
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
SafeBadRequest(c, "Invalid request parameters")
return
}
// Get user information
user, err := s.store.User().GetByID(req.UserID)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "User does not exist"})
SafeNotFound(c, "User")
return
}
@@ -3112,7 +3077,7 @@ func (s *Server) handleLogin(c *gin.Context) {
}
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
SafeBadRequest(c, "Invalid request parameters")
return
}
@@ -3156,14 +3121,14 @@ func (s *Server) handleVerifyOTP(c *gin.Context) {
}
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
SafeBadRequest(c, "Invalid request parameters")
return
}
// Get user information
user, err := s.store.User().GetByID(req.UserID)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "User does not exist"})
SafeNotFound(c, "User")
return
}
@@ -3197,7 +3162,7 @@ func (s *Server) handleResetPassword(c *gin.Context) {
}
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
SafeBadRequest(c, "Invalid request parameters")
return
}
@@ -3326,9 +3291,7 @@ func (s *Server) handlePublicTraderList(c *gin.Context) {
// Get trader information from all users
competition, err := s.traderManager.GetCompetitionData()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"error": fmt.Sprintf("Failed to get trader list: %v", err),
})
SafeInternalError(c, "Get trader list", err)
return
}
@@ -3371,9 +3334,7 @@ func (s *Server) handlePublicTraderList(c *gin.Context) {
func (s *Server) handlePublicCompetition(c *gin.Context) {
competition, err := s.traderManager.GetCompetitionData()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"error": fmt.Sprintf("Failed to get competition data: %v", err),
})
SafeInternalError(c, "Get competition data", err)
return
}
@@ -3384,9 +3345,7 @@ func (s *Server) handlePublicCompetition(c *gin.Context) {
func (s *Server) handleTopTraders(c *gin.Context) {
topTraders, err := s.traderManager.GetTopTradersData()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"error": fmt.Sprintf("Failed to get top 10 trader data: %v", err),
})
SafeInternalError(c, "Get top traders data", err)
return
}
@@ -3409,9 +3368,7 @@ func (s *Server) handleEquityHistoryBatch(c *gin.Context) {
// If no trader_ids specified, return historical data for top 5
topTraders, err := s.traderManager.GetTopTradersData()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"error": fmt.Sprintf("Failed to get top 5 traders: %v", err),
})
SafeInternalError(c, "Get top traders", err)
return
}
@@ -3506,7 +3463,8 @@ func (s *Server) getEquityHistoryForTraders(traderIDs []string, hours int) map[s
snapshots, err = s.store.Equity().GetLatest(traderID, 500)
}
if err != nil {
errors[traderID] = fmt.Sprintf("Failed to get historical data: %v", err)
logger.Errorf("[API] Failed to get equity history for %s: %v", traderID, err)
errors[traderID] = "Failed to get historical data"
continue
}