chore: build
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
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 := 0; attempt < n; attempt++ {
|
||||
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)
|
||||
}
|
||||
Reference in New Issue
Block a user