- traffic.DefaultInterval 10s → 5s for faster concurrent IP enforcement - sub_pull_limit: max successful sub fetches per rolling 24h (0=unlimited) - Users form + i18n; 429 when limit reached feat: 5s IP-limit poll; per-user subscription pull limit (24h) - traffic.DefaultInterval 10s → 5s for faster concurrent IP enforcement - sub_pull_limit: max successful sub fetches per rolling 24h (0=unlimited) - Users form + i18n; 429 when limit reached feat: 5s IP-limit poll; per-user subscription pull limit (24h) - traffic.DefaultInterval 10s → 5s for faster concurrent IP enforcement - sub_pull_limit: max successful sub fetches per rolling 24h (0=unlimited) - Users form + i18n; 429 when limit reached feat: 5s IP-limit poll; per-user subscription pull limit (24h) - traffic.DefaultInterval 10s → 5s for faster concurrent IP enforcement - sub_pull_limit: max successful sub fetches per rolling 24h (0=unlimited) - Users form + i18n; 429 when limit reached feat: 5s IP-limit poll; per-user subscription pull limit (24h) - traffic.DefaultInterval 10s → 5s for faster concurrent IP enforcement - sub_pull_limit: max successful sub fetches per rolling 24h (0=unlimited) - Users form + i18n; 429 when limit reached feat: 5s IP-limit poll; per-user subscription pull limit (24h) - traffic.DefaultInterval 10s → 5s for faster concurrent IP enforcement - sub_pull_limit: max successful sub fetches per rolling 24h (0=unlimited) - Users form + i18n; 429 when limit reached feat: 5s IP-limit poll; per-user subscription pull limit (24h) - traffic.DefaultInterval 10s → 5s for faster concurrent IP enforcement - sub_pull_limit: max successful sub fetches per rolling 24h (0=unlimited) - Users form + i18n; 429 when limit reached feat: 5s IP-limit poll; per-user subscription pull limit (24h) - traffic.DefaultInterval 10s → 5s for faster concurrent IP enforcement - sub_pull_limit: max successful sub fetches per rolling 24h (0=unlimited) - Users form + i18n; 429 when limit reached feat: 5s IP-limit poll; per-user subscription pull limit (24h) - traffic.DefaultInterval 10s → 5s for faster concurrent IP enforcement - sub_pull_limit: max successful sub fetches per rolling 24h (0=unlimited) - Users form + i18n; 429 when limit reached feat: 5s IP-limit poll; per-user subscription pull limit (24h) - traffic.DefaultInterval 10s → 5s for faster concurrent IP enforcement - sub_pull_limit: max successful sub fetches per rolling 24h (0=unlimited) - Users form + i18n; 429 when limit reached feat: 5s IP-limit poll; per-user subscription pull limit (24h) - traffic.DefaultInterval 10s → 5s for faster concurrent IP enforcement - sub_pull_limit: max successful sub fetches per rolling 24h (0=unlimited) - Users form + i18n; 429 when limit reached feat: 5s IP-limit poll; per-user subscription pull limit (24h) - traffic.DefaultInterval 10s → 5s for faster concurrent IP enforcement - sub_pull_limit: max successful sub fetches per rolling 24h (0=unlimited) - Users form + i18n; 429 when limit reached
54 lines
1.3 KiB
Go
54 lines
1.3 KiB
Go
package subpull
|
|
|
|
import (
|
|
"errors"
|
|
"time"
|
|
|
|
"github.com/kazeyukiro/3m-ui/backend/internal/database/models"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
const window = 24 * time.Hour
|
|
|
|
var ErrLimitReached = errors.New("subscription pull limit reached")
|
|
|
|
// Enforce increments the user's successful subscription-pull counter when
|
|
// limit > 0. limit <= 0 means unlimited. Uses a rolling 24h window from the
|
|
// first pull in the window.
|
|
func Enforce(db *gorm.DB, userID uint, limit int) error {
|
|
if db == nil || userID == 0 || limit <= 0 {
|
|
return nil
|
|
}
|
|
now := time.Now().UTC()
|
|
return db.Transaction(func(tx *gorm.DB) error {
|
|
var u models.ProxyUser
|
|
if err := tx.
|
|
Select("id", "sub_pull_limit", "sub_pull_count", "sub_pull_window_start").
|
|
First(&u, userID).Error; err != nil {
|
|
return err
|
|
}
|
|
// Prefer DB value if caller passed stale limit; fall back to argument.
|
|
lim := u.SubPullLimit
|
|
if lim <= 0 {
|
|
lim = limit
|
|
}
|
|
if lim <= 0 {
|
|
return nil
|
|
}
|
|
count := u.SubPullCount
|
|
start := u.SubPullWindowStart
|
|
if start == nil || now.Sub(*start) >= window {
|
|
count = 0
|
|
start = &now
|
|
}
|
|
if count >= lim {
|
|
return ErrLimitReached
|
|
}
|
|
count++
|
|
return tx.Model(&models.ProxyUser{}).Where("id = ?", userID).Updates(map[string]interface{}{
|
|
"sub_pull_count": count,
|
|
"sub_pull_window_start": start,
|
|
}).Error
|
|
})
|
|
}
|