mirror of
https://github.com/router-for-me/CLIProxyAPI.git
synced 2026-02-18 20:30:51 +08:00
feat(auth): prevent filestore writes on unchanged metadata
- Added `metadataEqualIgnoringTimestamps` to compare metadata while ignoring volatile fields. - Prevented redundant writes caused by changes in timestamp-related fields. - Improved efficiency in filestore operations by skipping unnecessary updates.
This commit is contained in:
@@ -8,6 +8,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@@ -72,9 +73,12 @@ func (s *FileTokenStore) Save(ctx context.Context, auth *cliproxyauth.Auth) (str
|
|||||||
if errMarshal != nil {
|
if errMarshal != nil {
|
||||||
return "", fmt.Errorf("auth filestore: marshal metadata failed: %w", errMarshal)
|
return "", fmt.Errorf("auth filestore: marshal metadata failed: %w", errMarshal)
|
||||||
}
|
}
|
||||||
if _, errRead := os.ReadFile(path); errRead == nil {
|
if existing, errRead := os.ReadFile(path); errRead == nil {
|
||||||
// Use metadataEqualIgnoringTimestamps to skip writes when only timestamp fields change.
|
// Use metadataEqualIgnoringTimestamps to skip writes when only timestamp fields change.
|
||||||
// This prevents the token refresh loop caused by timestamp/expired/expires_in changes.
|
// This prevents the token refresh loop caused by timestamp/expired/expires_in changes.
|
||||||
|
if metadataEqualIgnoringTimestamps(existing, raw) {
|
||||||
|
return path, nil
|
||||||
|
}
|
||||||
file, errOpen := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0o600)
|
file, errOpen := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0o600)
|
||||||
if errOpen != nil {
|
if errOpen != nil {
|
||||||
return "", fmt.Errorf("auth filestore: open existing failed: %w", errOpen)
|
return "", fmt.Errorf("auth filestore: open existing failed: %w", errOpen)
|
||||||
@@ -295,3 +299,29 @@ func (s *FileTokenStore) baseDirSnapshot() string {
|
|||||||
defer s.dirLock.RUnlock()
|
defer s.dirLock.RUnlock()
|
||||||
return s.baseDir
|
return s.baseDir
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// metadataEqualIgnoringTimestamps compares two metadata JSON blobs, ignoring volatile fields that
|
||||||
|
// change on every refresh but don't affect authentication logic.
|
||||||
|
func metadataEqualIgnoringTimestamps(a, b []byte) bool {
|
||||||
|
var objA map[string]any
|
||||||
|
var objB map[string]any
|
||||||
|
if errUnmarshalA := json.Unmarshal(a, &objA); errUnmarshalA != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if errUnmarshalB := json.Unmarshal(b, &objB); errUnmarshalB != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
stripVolatileMetadataFields(objA)
|
||||||
|
stripVolatileMetadataFields(objB)
|
||||||
|
return reflect.DeepEqual(objA, objB)
|
||||||
|
}
|
||||||
|
|
||||||
|
func stripVolatileMetadataFields(metadata map[string]any) {
|
||||||
|
if metadata == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// These fields change on refresh and would otherwise trigger watcher reload loops.
|
||||||
|
for _, field := range []string{"timestamp", "expired", "expires_in", "last_refresh", "access_token"} {
|
||||||
|
delete(metadata, field)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user