This commit is contained in:
afkarxyz
2026-04-02 18:42:35 +07:00
parent f13359df7f
commit 2bc2c0bf03
48 changed files with 3409 additions and 1038 deletions
+215
View File
@@ -0,0 +1,215 @@
package backend
import (
"encoding/json"
"fmt"
"path/filepath"
"sort"
"strings"
"sync"
"time"
bolt "go.etcd.io/bbolt"
)
const (
providerPriorityDBFile = "provider_priority.db"
providerPriorityBucket = "ProviderPriority"
)
type providerPriorityEntry struct {
Service string `json:"service"`
Provider string `json:"provider"`
LastOutcome string `json:"last_outcome"`
LastAttempt int64 `json:"last_attempt"`
LastSuccess int64 `json:"last_success"`
LastFailure int64 `json:"last_failure"`
SuccessCount int64 `json:"success_count"`
FailureCount int64 `json:"failure_count"`
}
var (
providerPriorityDB *bolt.DB
providerPriorityDBMu sync.Mutex
)
func InitProviderPriorityDB() error {
providerPriorityDBMu.Lock()
defer providerPriorityDBMu.Unlock()
if providerPriorityDB != nil {
return nil
}
appDir, err := EnsureAppDir()
if err != nil {
return err
}
dbPath := filepath.Join(appDir, providerPriorityDBFile)
db, err := bolt.Open(dbPath, 0o600, &bolt.Options{Timeout: 1 * time.Second})
if err != nil {
return err
}
if err := db.Update(func(tx *bolt.Tx) error {
_, err := tx.CreateBucketIfNotExists([]byte(providerPriorityBucket))
return err
}); err != nil {
db.Close()
return err
}
providerPriorityDB = db
return nil
}
func CloseProviderPriorityDB() {
providerPriorityDBMu.Lock()
defer providerPriorityDBMu.Unlock()
if providerPriorityDB != nil {
_ = providerPriorityDB.Close()
providerPriorityDB = nil
}
}
func prioritizeProviders(service string, providers []string) []string {
ordered := append([]string(nil), providers...)
if len(ordered) < 2 {
return ordered
}
if err := InitProviderPriorityDB(); err != nil {
fmt.Printf("Warning: failed to init provider priority DB: %v\n", err)
return ordered
}
serviceKey := strings.TrimSpace(strings.ToLower(service))
entries := make(map[string]providerPriorityEntry, len(ordered))
if err := providerPriorityDB.View(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(providerPriorityBucket))
if bucket == nil {
return nil
}
for _, provider := range ordered {
if raw := bucket.Get([]byte(providerPriorityKey(serviceKey, provider))); len(raw) > 0 {
var entry providerPriorityEntry
if err := json.Unmarshal(raw, &entry); err != nil {
return err
}
entries[provider] = entry
}
}
return nil
}); err != nil {
fmt.Printf("Warning: failed to read provider priority DB: %v\n", err)
return ordered
}
originalIndex := make(map[string]int, len(ordered))
for idx, provider := range ordered {
originalIndex[provider] = idx
}
sort.SliceStable(ordered, func(i, j int) bool {
left := entries[ordered[i]]
right := entries[ordered[j]]
leftRank := providerOutcomeRank(left.LastOutcome)
rightRank := providerOutcomeRank(right.LastOutcome)
if leftRank != rightRank {
return leftRank > rightRank
}
if left.LastSuccess != right.LastSuccess {
return left.LastSuccess > right.LastSuccess
}
if left.LastAttempt != right.LastAttempt {
return left.LastAttempt > right.LastAttempt
}
return originalIndex[ordered[i]] < originalIndex[ordered[j]]
})
return ordered
}
func recordProviderSuccess(service string, provider string) {
recordProviderOutcome(service, provider, true)
}
func recordProviderFailure(service string, provider string) {
recordProviderOutcome(service, provider, false)
}
func recordProviderOutcome(service string, provider string, success bool) {
if strings.TrimSpace(service) == "" || strings.TrimSpace(provider) == "" {
return
}
if err := InitProviderPriorityDB(); err != nil {
fmt.Printf("Warning: failed to init provider priority DB: %v\n", err)
return
}
serviceKey := strings.TrimSpace(strings.ToLower(service))
providerKey := providerPriorityKey(serviceKey, provider)
now := time.Now().Unix()
if err := providerPriorityDB.Update(func(tx *bolt.Tx) error {
bucket, err := tx.CreateBucketIfNotExists([]byte(providerPriorityBucket))
if err != nil {
return err
}
entry := providerPriorityEntry{
Service: serviceKey,
Provider: provider,
}
if raw := bucket.Get([]byte(providerKey)); len(raw) > 0 {
if err := json.Unmarshal(raw, &entry); err != nil {
return err
}
}
entry.LastAttempt = now
if success {
entry.LastOutcome = "success"
entry.LastSuccess = now
entry.SuccessCount++
} else {
entry.LastOutcome = "failure"
entry.LastFailure = now
entry.FailureCount++
}
payload, err := json.Marshal(entry)
if err != nil {
return err
}
return bucket.Put([]byte(providerKey), payload)
}); err != nil {
fmt.Printf("Warning: failed to update provider priority DB: %v\n", err)
}
}
func providerOutcomeRank(outcome string) int {
switch strings.TrimSpace(strings.ToLower(outcome)) {
case "success":
return 2
case "":
return 1
default:
return 0
}
}
func providerPriorityKey(service string, provider string) string {
return strings.TrimSpace(strings.ToLower(service)) + "|" + strings.TrimSpace(provider)
}