mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-07-06 20:41:14 +08:00
fix: Bybit shared HTTP client, auth token bugs, consistent safe.ReadAllLimited
- Bybit: replace http.Get/http.DefaultClient with shared bybitHTTPClient (30s timeout, connection pooling)
- Frontend: fix resetPassword missing auth token (endpoint now requires auth)
- Frontend: fix SettingsPage using wrong localStorage key ('token' → 'auth_token')
- Agent: migrate remaining io.ReadAll(io.LimitReader()) to safe.ReadAllLimited for consistency
- Remove unused 'io' imports from brain.go and sentinel.go
This commit is contained in:
@@ -3,7 +3,6 @@ package agent
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"nofx/safe"
|
||||
@@ -87,7 +86,7 @@ func (b *Brain) scanNews(seen map[string]bool) {
|
||||
b.logger.Debug("news API non-200", "status", resp.StatusCode)
|
||||
return
|
||||
}
|
||||
body, err := io.ReadAll(io.LimitReader(resp.Body, 1024*1024)) // 1MB limit
|
||||
body, err := safe.ReadAllLimited(resp.Body, 1024*1024) // 1MB limit
|
||||
if err != nil { return }
|
||||
|
||||
var result struct {
|
||||
@@ -167,7 +166,7 @@ func (b *Brain) sendBrief(hour int) {
|
||||
for _, sym := range []string{"BTCUSDT", "ETHUSDT"} {
|
||||
resp, err := b.http.Get(fmt.Sprintf("https://fapi.binance.com/fapi/v1/ticker/24hr?symbol=%s", sym))
|
||||
if err != nil { continue }
|
||||
body, readErr := io.ReadAll(io.LimitReader(resp.Body, 64*1024)) // 64KB limit
|
||||
body, readErr := safe.ReadAllLimited(resp.Body, 64*1024) // 64KB limit
|
||||
statusOK := resp.StatusCode == http.StatusOK
|
||||
resp.Body.Close()
|
||||
if readErr != nil || !statusOK { continue }
|
||||
|
||||
@@ -3,7 +3,6 @@ package agent
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log/slog"
|
||||
"math"
|
||||
"net/http"
|
||||
@@ -120,7 +119,7 @@ func (s *Sentinel) check(symbol string) {
|
||||
s.logger.Debug("sentinel ticker non-200", "symbol", symbol, "status", resp.StatusCode)
|
||||
return
|
||||
}
|
||||
body, err := io.ReadAll(io.LimitReader(resp.Body, 256*1024)) // 256KB limit
|
||||
body, err := safe.ReadAllLimited(resp.Body, 256*1024) // 256KB limit
|
||||
if err != nil { return }
|
||||
var t map[string]interface{}
|
||||
if err := json.Unmarshal(body, &t); err != nil { return }
|
||||
|
||||
@@ -71,8 +71,7 @@ func (t *BybitTrader) getTradesViaHTTP(startTime time.Time, limit int) ([]BybitT
|
||||
req.Header.Set("X-BAPI-RECV-WINDOW", recvWindow)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
// Use http.DefaultClient for the request
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
resp, err := bybitHTTPClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to call Bybit API: %w", err)
|
||||
}
|
||||
|
||||
@@ -15,6 +15,17 @@ import (
|
||||
bybit "github.com/bybit-exchange/bybit.go.api"
|
||||
)
|
||||
|
||||
// bybitHTTPClient is a shared HTTP client for direct Bybit API calls (public + signed).
|
||||
// Reused across requests to benefit from connection pooling; 30s timeout prevents hangs.
|
||||
var bybitHTTPClient = &http.Client{
|
||||
Timeout: 30 * time.Second,
|
||||
Transport: &http.Transport{
|
||||
MaxIdleConns: 20,
|
||||
MaxIdleConnsPerHost: 10,
|
||||
IdleConnTimeout: 90 * time.Second,
|
||||
},
|
||||
}
|
||||
|
||||
// BybitTrader Bybit USDT Perpetual Futures Trader
|
||||
type BybitTrader struct {
|
||||
client *bybit.Client
|
||||
@@ -94,7 +105,7 @@ func (t *BybitTrader) getQtyStep(symbol string) float64 {
|
||||
|
||||
// Call public API directly to get contract information
|
||||
url := fmt.Sprintf("https://api.bybit.com/v5/market/instruments-info?category=linear&symbol=%s", symbol)
|
||||
resp, err := http.Get(url)
|
||||
resp, err := bybitHTTPClient.Get(url)
|
||||
if err != nil {
|
||||
logger.Infof("⚠️ [Bybit] Failed to get precision info for %s: %v", symbol, err)
|
||||
return 1 // Default to integer
|
||||
|
||||
@@ -127,8 +127,7 @@ func (t *BybitTrader) getClosedPnLViaHTTP(startTime time.Time, limit int) ([]typ
|
||||
req.Header.Set("X-BAPI-RECV-WINDOW", recvWindow)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
// Use http.DefaultClient for the request
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
resp, err := bybitHTTPClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to call Bybit API: %w", err)
|
||||
}
|
||||
|
||||
@@ -690,7 +690,7 @@ func (t *BybitTrader) GetOrderBook(symbol string, depth int) (bids, asks [][]flo
|
||||
|
||||
// Use HTTP request directly since the SDK doesn't expose GetOrderbook
|
||||
url := fmt.Sprintf("https://api.bybit.com/v5/market/orderbook?category=linear&symbol=%s&limit=%d", symbol, depth)
|
||||
resp, err := http.Get(url)
|
||||
resp, err := bybitHTTPClient.Get(url)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to get order book: %w", err)
|
||||
}
|
||||
|
||||
@@ -250,10 +250,12 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
|
||||
|
||||
const resetPassword = async (email: string, newPassword: string) => {
|
||||
try {
|
||||
const savedToken = token || localStorage.getItem('auth_token')
|
||||
const response = await fetch('/api/reset-password', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...(savedToken ? { Authorization: `Bearer ${savedToken}` } : {}),
|
||||
},
|
||||
body: JSON.stringify({
|
||||
email,
|
||||
|
||||
@@ -64,7 +64,7 @@ export function SettingsPage() {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${localStorage.getItem('token') || ''}`,
|
||||
Authorization: `Bearer ${localStorage.getItem('auth_token') || ''}`,
|
||||
},
|
||||
body: JSON.stringify({ new_password: newPassword }),
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user