143 lines
3.9 KiB
Go
143 lines
3.9 KiB
Go
package service
|
|
|
|
import (
|
|
"time"
|
|
|
|
"github.com/Wei-Shaw/sub2api/internal/pkg/ip"
|
|
)
|
|
|
|
// API Key status constants
|
|
const (
|
|
StatusAPIKeyActive = "active"
|
|
StatusAPIKeyDisabled = "disabled"
|
|
StatusAPIKeyQuotaExhausted = "quota_exhausted"
|
|
StatusAPIKeyExpired = "expired"
|
|
)
|
|
|
|
// Rate limit window durations
|
|
const (
|
|
RateLimitWindow5h = 5 * time.Hour
|
|
RateLimitWindow1d = 24 * time.Hour
|
|
RateLimitWindow7d = 7 * 24 * time.Hour
|
|
)
|
|
|
|
// IsWindowExpired returns true if the window starting at windowStart has exceeded the given duration.
|
|
func IsWindowExpired(windowStart *time.Time, duration time.Duration) bool {
|
|
return windowStart != nil && time.Since(*windowStart) >= duration
|
|
}
|
|
|
|
type APIKey struct {
|
|
ID int64
|
|
UserID int64
|
|
Key string
|
|
Name string
|
|
GroupID *int64
|
|
Status string
|
|
IPWhitelist []string
|
|
IPBlacklist []string
|
|
// 预编译的 IP 规则,用于认证热路径避免重复 ParseIP/ParseCIDR。
|
|
CompiledIPWhitelist *ip.CompiledIPRules `json:"-"`
|
|
CompiledIPBlacklist *ip.CompiledIPRules `json:"-"`
|
|
LastUsedAt *time.Time
|
|
CreatedAt time.Time
|
|
UpdatedAt time.Time
|
|
User *User
|
|
Group *Group
|
|
|
|
// Quota fields
|
|
Quota float64 // Quota limit in USD (0 = unlimited)
|
|
QuotaUsed float64 // Used quota amount
|
|
ExpiresAt *time.Time // Expiration time (nil = never expires)
|
|
|
|
// Rate limit fields
|
|
RateLimit5h float64 // Rate limit in USD per 5h (0 = unlimited)
|
|
RateLimit1d float64 // Rate limit in USD per 1d (0 = unlimited)
|
|
RateLimit7d float64 // Rate limit in USD per 7d (0 = unlimited)
|
|
Usage5h float64 // Used amount in current 5h window
|
|
Usage1d float64 // Used amount in current 1d window
|
|
Usage7d float64 // Used amount in current 7d window
|
|
Window5hStart *time.Time // Start of current 5h window
|
|
Window1dStart *time.Time // Start of current 1d window
|
|
Window7dStart *time.Time // Start of current 7d window
|
|
}
|
|
|
|
func (k *APIKey) IsActive() bool {
|
|
return k.Status == StatusActive
|
|
}
|
|
|
|
// HasRateLimits returns true if any rate limit window is configured
|
|
func (k *APIKey) HasRateLimits() bool {
|
|
return k.RateLimit5h > 0 || k.RateLimit1d > 0 || k.RateLimit7d > 0
|
|
}
|
|
|
|
// IsExpired checks if the API key has expired
|
|
func (k *APIKey) IsExpired() bool {
|
|
if k.ExpiresAt == nil {
|
|
return false
|
|
}
|
|
return time.Now().After(*k.ExpiresAt)
|
|
}
|
|
|
|
// IsQuotaExhausted checks if the API key quota is exhausted
|
|
func (k *APIKey) IsQuotaExhausted() bool {
|
|
if k.Quota <= 0 {
|
|
return false // unlimited
|
|
}
|
|
return k.QuotaUsed >= k.Quota
|
|
}
|
|
|
|
// GetQuotaRemaining returns remaining quota (-1 for unlimited)
|
|
func (k *APIKey) GetQuotaRemaining() float64 {
|
|
if k.Quota <= 0 {
|
|
return -1 // unlimited
|
|
}
|
|
remaining := k.Quota - k.QuotaUsed
|
|
if remaining < 0 {
|
|
return 0
|
|
}
|
|
return remaining
|
|
}
|
|
|
|
// GetDaysUntilExpiry returns days until expiry (-1 for never expires)
|
|
func (k *APIKey) GetDaysUntilExpiry() int {
|
|
if k.ExpiresAt == nil {
|
|
return -1 // never expires
|
|
}
|
|
duration := time.Until(*k.ExpiresAt)
|
|
if duration < 0 {
|
|
return 0
|
|
}
|
|
return int(duration.Hours() / 24)
|
|
}
|
|
|
|
// EffectiveUsage5h returns the 5h window usage, or 0 if the window has expired.
|
|
func (k *APIKey) EffectiveUsage5h() float64 {
|
|
if IsWindowExpired(k.Window5hStart, RateLimitWindow5h) {
|
|
return 0
|
|
}
|
|
return k.Usage5h
|
|
}
|
|
|
|
// EffectiveUsage1d returns the 1d window usage, or 0 if the window has expired.
|
|
func (k *APIKey) EffectiveUsage1d() float64 {
|
|
if IsWindowExpired(k.Window1dStart, RateLimitWindow1d) {
|
|
return 0
|
|
}
|
|
return k.Usage1d
|
|
}
|
|
|
|
// EffectiveUsage7d returns the 7d window usage, or 0 if the window has expired.
|
|
func (k *APIKey) EffectiveUsage7d() float64 {
|
|
if IsWindowExpired(k.Window7dStart, RateLimitWindow7d) {
|
|
return 0
|
|
}
|
|
return k.Usage7d
|
|
}
|
|
|
|
// APIKeyListFilters holds optional filtering parameters for listing API keys.
|
|
type APIKeyListFilters struct {
|
|
Search string
|
|
Status string
|
|
GroupID *int64 // nil=不筛选, 0=无分组, >0=指定分组
|
|
}
|