Files
kazeyukiro 9cf0df62d3 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
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
2026-09-20 00:55:00 +08:00

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
})
}