mirror of
https://github.com/tiennm99/tiennm99bot.git
synced 2026-10-11 03:13:46 +00:00
fix(gold): switch to a managed VNAppMob key when the env key is rejected
This commit is contained in:
1 parent
72b2b21912
commit
83a5b8135f
2 files changed
+54
-3
No files matched your search
@@ -12,6 +12,7 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/tiennm99/miti99bot/internal/log"
|
||||
@@ -40,6 +41,10 @@ type VNAppMobClient struct {
|
||||
Token string // optional env override (GOLD_VNAPP_API_KEY)
|
||||
cache storage.DocStore[apiKeyCache] // module-scoped typed cache store
|
||||
|
||||
// tokenRejected is set once VNAppMob answers 401/403 to Token, so later
|
||||
// calls use the self-managed key instead of the dead env override.
|
||||
tokenRejected atomic.Bool
|
||||
|
||||
nowFn func() time.Time
|
||||
mu sync.Mutex
|
||||
}
|
||||
@@ -92,6 +97,12 @@ func (c *VNAppMobClient) FetchSJCPrice(ctx context.Context) (buy, sell float64,
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
if c.Token != "" && key == c.Token {
|
||||
// Without this the retry below would get the same rejected env key back
|
||||
// from getKey, and an expired GOLD_VNAPP_API_KEY could never recover.
|
||||
c.tokenRejected.Store(true)
|
||||
log.Warn("vnappmob_env_key_rejected", "status", statusErr.StatusCode, "msg", "GOLD_VNAPP_API_KEY rejected; switching to self-managed key")
|
||||
}
|
||||
log.Warn("vnappmob_sjc_auth_failed", "status", statusErr.StatusCode, "msg", "refreshing key after auth failure")
|
||||
if refreshErr := c.refreshKey(ctx); refreshErr != nil {
|
||||
return 0, 0, fmt.Errorf("vnappmob: 403 refresh failed: %w", refreshErr)
|
||||
@@ -165,10 +176,11 @@ type httpStatusError struct {
|
||||
|
||||
func (e *httpStatusError) Error() string { return e.msg }
|
||||
|
||||
// getKey returns a valid API key, refreshing from the typed cache store or the
|
||||
// remote endpoint when the current key is missing or close to expiry.
|
||||
// getKey returns the env-provided Token when set and not yet rejected. Otherwise it returns the
|
||||
// cached key, requesting a new one from the remote endpoint when the cached key
|
||||
// is missing or close to expiry.
|
||||
func (c *VNAppMobClient) getKey(ctx context.Context) (string, error) {
|
||||
if c.Token != "" {
|
||||
if c.Token != "" && !c.tokenRejected.Load() {
|
||||
return c.Token, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -303,3 +303,42 @@ func TestFetchSJCPrice_InvalidValues(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// A rejected GOLD_VNAPP_API_KEY must not be retried: the client switches to a
|
||||
// self-managed key for this call and every later one.
|
||||
func TestFetchSJCPrice_RejectedEnvTokenSwitchesToManagedKey(t *testing.T) {
|
||||
exp := time.Unix(1000, 0).Add(14 * 24 * time.Hour).Unix()
|
||||
managed := makeJWT(exp)
|
||||
var envHits, refreshHits int32
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.URL.Path {
|
||||
case "/api/request_api_key":
|
||||
atomic.AddInt32(&refreshHits, 1)
|
||||
fmt.Fprint(w, managed)
|
||||
case "/api/v2/gold/sjc":
|
||||
if r.Header.Get("Authorization") != "Bearer "+managed {
|
||||
atomic.AddInt32(&envHits, 1)
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
_, _ = w.Write([]byte(`{"results":[{"buy_1l":"90000000.0","sell_1l":"91000000.0"}]}`))
|
||||
default:
|
||||
http.NotFound(w, r)
|
||||
}
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
c := newTestVNAppMobClient(srv, newTestColl())
|
||||
c.Token = "expired-env-key"
|
||||
for i := range 2 {
|
||||
if _, _, err := c.FetchSJCPrice(context.Background()); err != nil {
|
||||
t.Fatalf("FetchSJCPrice call %d: %v", i+1, err)
|
||||
}
|
||||
}
|
||||
if got := atomic.LoadInt32(&envHits); got != 1 {
|
||||
t.Fatalf("requests with env key: got %d, want 1", got)
|
||||
}
|
||||
if got := atomic.LoadInt32(&refreshHits); got != 1 {
|
||||
t.Fatalf("refresh hits: got %d, want 1", got)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user