package services import ( "fmt" "io" "log" "net/http" "os" "sync/atomic" ) type tomTomKeyManager struct { keys []string current atomic.Int32 } var tomTomKeys = initTomTomKeyManager() func initTomTomKeyManager() *tomTomKeyManager { m := &tomTomKeyManager{} seen := map[string]bool{} candidates := []string{ os.Getenv("TOMTOM_API_KEY"), os.Getenv("TOMTOM_API_KEY_1"), os.Getenv("TOMTOM_API_KEY_2"), os.Getenv("TOMTOM_API_KEY_3"), } for _, k := range candidates { if k != "" && !seen[k] { seen[k] = true m.keys = append(m.keys, k) } } log.Printf("🔑 [TOMTOM] %d clé(s) API configurée(s)", len(m.keys)) return m } // currentKey retourne la clé active et son index. func (m *tomTomKeyManager) currentKey() (string, int) { n := len(m.keys) if n == 0 { return "", -1 } idx := int(m.current.Load()) % n return m.keys[idx], idx } // rotate passe à la clé suivante. func (m *tomTomKeyManager) rotate(fromIdx int) { n := len(m.keys) if n <= 1 { return } next := int32((fromIdx + 1) % n) m.current.CompareAndSwap(int32(fromIdx), next) log.Printf("🔄 [TOMTOM] Rotation clé %d → clé %d (quota atteint)", fromIdx+1, next+1) } // Do exécute la requête en rotant automatiquement sur 403/429. // buildReq doit construire une nouvelle *http.Request pour la clé donnée. func (m *tomTomKeyManager) Do(client *http.Client, buildReq func(key string) (*http.Request, error)) (*http.Response, error) { n := len(m.keys) if n == 0 { return nil, fmt.Errorf("aucune clé TomTom configurée (TOMTOM_API_KEY / TOMTOM_API_KEY_1..3)") } _, startIdx := m.currentKey() for attempt := range n { idx := (startIdx + attempt) % n key := m.keys[idx] req, err := buildReq(key) if err != nil { return nil, err } resp, err := client.Do(req) if err != nil { return nil, err } if resp.StatusCode == http.StatusForbidden || resp.StatusCode == http.StatusTooManyRequests { io.Copy(io.Discard, resp.Body) resp.Body.Close() m.rotate(idx) continue } return resp, nil } return nil, fmt.Errorf("toutes les clés TomTom ont atteint leur quota (%d clé(s) testée(s))", n) }