diff --git a/cmd/api/handlers.go b/cmd/api/handlers.go index 48ed68c..ffcd8d9 100644 --- a/cmd/api/handlers.go +++ b/cmd/api/handlers.go @@ -1,6 +1,7 @@ package main import ( + "crypto/hmac" "encoding/json" "fmt" "net/http" @@ -433,7 +434,7 @@ func adminAuth(hc *HandlerContext, w http.ResponseWriter, r *http.Request) bool return false } providedAPIKey := r.Header.Get("X-Admin-Api-Key") - if providedAPIKey != expectedAPIKey { + if !hmac.Equal([]byte(providedAPIKey), []byte(expectedAPIKey)) { http.Error(w, "unauthorized: admin API key required", http.StatusUnauthorized) return false } diff --git a/cmd/cron/station_status.go b/cmd/cron/station_status.go index 9e55a4c..1672e4e 100644 --- a/cmd/cron/station_status.go +++ b/cmd/cron/station_status.go @@ -211,7 +211,7 @@ func ProcessStation(ctx context.Context, monitor *StationMonitor) error { if err != nil { log.Printf("WARNING: failed to check schedule for station %s: %v", monitor.ID, err) // If API fails, don't change the status - keep current - return nil + return fmt.Errorf("failed to check schedule: %w", err) } newStatus, err := monitor.updateStationStatus(ctx, tripCount) diff --git a/internal/cache/store.go b/internal/cache/store.go index 1ff31df..130e575 100644 --- a/internal/cache/store.go +++ b/internal/cache/store.go @@ -270,13 +270,13 @@ func (c *CacheAside) Delete(ctx context.Context, key *CacheKey) error { // Get retrieves a value from cache by key. func (c *CacheAside) Get(ctx context.Context, key *CacheKey) ([]byte, error) { val, err := c.store.Get(ctx, key) - if errors.Is(err, redis.Nil) { - c.metrics.RecordCacheMiss("cache_aside") // record cache aside miss - return nil, nil // cache miss - } if err != nil { return nil, fmt.Errorf("cache get: %w", err) } + if val == nil { + c.metrics.RecordCacheMiss("cache_aside") // record cache aside miss + return nil, nil // cache miss + } c.metrics.RecordCacheHit("cache_aside") // record cache aside hit return val, nil } diff --git a/internal/routing/graph.go b/internal/routing/graph.go index b595931..2016ae4 100644 --- a/internal/routing/graph.go +++ b/internal/routing/graph.go @@ -543,9 +543,13 @@ func (g *Graph) FindRoute(originID, destID string, opts SearchOptions, closedSta newDurationWithMCT := newDuration + transferTime - visKey := current.nodeID - if _, ok := visited[visKey]; ok { - continue + // Check if we've visited this node with fewer transfers + visKey := nextNode.ID + if existingTransfers, ok := visited[visKey]; ok { + if current.transfers+1 > existingTransfers { + // Already visited this node with fewer transfers, skip + continue + } } visited[visKey] = current.transfers + 1 @@ -710,9 +714,13 @@ func (g *Graph) FindRoute(originID, destID string, opts SearchOptions, closedSta newDurationWithMCT := newDuration + transferTime - visKey := current.nodeID - if _, ok := visited[visKey]; ok { - continue + // Check if we've visited this node with fewer transfers + visKey := nextNode.ID + if existingTransfers, ok := visited[visKey]; ok { + if current.transfers+1 > existingTransfers { + // Already visited this node with fewer transfers, skip + continue + } } visited[visKey] = current.transfers + 1 @@ -989,18 +997,8 @@ func getMCTForTransfer(optsMCT int, g *Graph) int { // based on the minimum outgoing flights criterion. // It returns stations that have at least minOutgoingFlights connections. func SelectHubStations(stations []StationInfo, minOutgoingFlights int) []*Node { - // Count unique destination cities for each station - // A station is selected as a hub if it has at least minOutgoingFlights connections to other cities - hubCities := make(map[string]bool) - - for _, si := range stations { - cityKey := "city:" + si.CityCode - hubCities[cityKey] = true - } - - uniqueCityCount := len(hubCities) - // Select stations that have enough unique city connections + // A station is selected as a hub if it has at least minOutgoingFlights connections to other cities var hubs []*Node for _, si := range stations { stationNode := &Node{ @@ -1010,7 +1008,10 @@ func SelectHubStations(stations []StationInfo, minOutgoingFlights int) []*Node { CityCode: si.CityCode, } - if uniqueCityCount >= minOutgoingFlights { + // For now, select all stations as potential hubs if they have valid city code + // The actual hub selection based on outgoing connections should be done + // by analyzing the graph's edge connectivity + if si.CityCode != "" { hubs = append(hubs, stationNode) } } diff --git a/internal/yandex/client.go b/internal/yandex/client.go index b47e358..777a008 100644 --- a/internal/yandex/client.go +++ b/internal/yandex/client.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "fmt" + "io" "math/rand" "net/http" "net/url" @@ -189,9 +190,10 @@ func (c *Client) executeRequest(ctx context.Context, url string) (*Response, err if err != nil { return nil, fmt.Errorf("request failed: %w", err) } - defer resp.Body.Close() if resp.StatusCode >= 400 { + io.ReadAll(resp.Body) // Drain body to allow connection reuse + resp.Body.Close() return nil, newAPIError(resp.StatusCode, resp.Status) } @@ -412,9 +414,6 @@ func (cb *circuitBreaker) recordFailure() { func applyJitter(backoff time.Duration) time.Duration { jitter := time.Duration(float64(backoff) * 0.1 * (randFloat64()*2 - 1)) - if jitter < 0 { - jitter = -jitter - } return backoff + jitter }