| package service
|
|
|
| import (
|
| "errors"
|
| "opus-api/internal/model"
|
| "sync"
|
| "time"
|
|
|
| "gorm.io/gorm"
|
| )
|
|
|
|
|
| type RotationStrategy string
|
|
|
| const (
|
| StrategyRoundRobin RotationStrategy = "round_robin"
|
| StrategyPriority RotationStrategy = "priority"
|
| StrategyLeastUsed RotationStrategy = "least_used"
|
| )
|
|
|
| var (
|
| ErrNoCookiesAvailable = errors.New("no valid cookies available")
|
| )
|
|
|
|
|
| type CookieRotator struct {
|
| service *CookieService
|
| strategy RotationStrategy
|
| mu sync.RWMutex
|
| index int
|
| }
|
|
|
|
|
| func NewCookieRotator(service *CookieService, strategy RotationStrategy) *CookieRotator {
|
| if strategy == "" {
|
| strategy = StrategyRoundRobin
|
| }
|
| return &CookieRotator{
|
| service: service,
|
| strategy: strategy,
|
| index: 0,
|
| }
|
| }
|
|
|
|
|
| func (r *CookieRotator) NextCookie() (interface{}, error) {
|
| r.mu.Lock()
|
| defer r.mu.Unlock()
|
|
|
| cookies, err := r.service.GetAllValidCookies()
|
| if err != nil {
|
| return nil, err
|
| }
|
|
|
| if len(cookies) == 0 {
|
| return nil, ErrNoCookiesAvailable
|
| }
|
|
|
| var selected *model.MorphCookie
|
|
|
| switch r.strategy {
|
| case StrategyRoundRobin:
|
| selected = r.roundRobin(cookies)
|
| case StrategyPriority:
|
| selected = r.priority(cookies)
|
| case StrategyLeastUsed:
|
| selected = r.leastUsed(cookies)
|
| default:
|
| selected = r.roundRobin(cookies)
|
| }
|
|
|
| if selected == nil {
|
| return nil, ErrNoCookiesAvailable
|
| }
|
|
|
| return selected, nil
|
| }
|
|
|
|
|
| func (r *CookieRotator) roundRobin(cookies []model.MorphCookie) *model.MorphCookie {
|
| if len(cookies) == 0 {
|
| return nil
|
| }
|
|
|
| r.index = r.index % len(cookies)
|
| selected := &cookies[r.index]
|
| r.index++
|
|
|
| return selected
|
| }
|
|
|
|
|
| func (r *CookieRotator) priority(cookies []model.MorphCookie) *model.MorphCookie {
|
| if len(cookies) == 0 {
|
| return nil
|
| }
|
|
|
|
|
| return &cookies[0]
|
| }
|
|
|
|
|
| func (r *CookieRotator) leastUsed(cookies []model.MorphCookie) *model.MorphCookie {
|
| if len(cookies) == 0 {
|
| return nil
|
| }
|
|
|
|
|
| return &cookies[0]
|
| }
|
|
|
|
|
| func (r *CookieRotator) MarkUsed(cookieID uint) error {
|
| db := r.service.GetDB()
|
| return db.Model(&model.MorphCookie{}).
|
| Where("id = ?", cookieID).
|
| Updates(map[string]interface{}{
|
| "usage_count": gorm.Expr("usage_count + ?", 1),
|
| "last_used": time.Now(),
|
| }).Error
|
| }
|
|
|
|
|
| func (r *CookieRotator) MarkInvalid(cookieID uint) error {
|
| db := r.service.GetDB()
|
| return db.Model(&model.MorphCookie{}).
|
| Where("id = ?", cookieID).
|
| Updates(map[string]interface{}{
|
| "is_valid": false,
|
| }).Error
|
| }
|
|
|
|
|
| func (r *CookieRotator) MarkError(cookieID uint) error {
|
| db := r.service.GetDB()
|
|
|
| var cookie model.MorphCookie
|
| if err := db.First(&cookie, cookieID).Error; err != nil {
|
| return err
|
| }
|
|
|
| updates := map[string]interface{}{
|
| "error_count": gorm.Expr("error_count + ?", 1),
|
| }
|
|
|
|
|
| if cookie.ErrorCount >= 5 {
|
| updates["is_valid"] = false
|
| }
|
|
|
| return db.Model(&model.MorphCookie{}).
|
| Where("id = ?", cookieID).
|
| Updates(updates).Error
|
| }
|
|
|
|
|
| func (r *CookieRotator) GetStrategy() RotationStrategy {
|
| return r.strategy
|
| }
|
|
|
|
|
| func (r *CookieRotator) SetStrategy(strategy RotationStrategy) {
|
| r.mu.Lock()
|
| defer r.mu.Unlock()
|
| r.strategy = strategy
|
| r.index = 0
|
| } |