Files
sub2api/backend/internal/service/proxy.go
T
DaydreamCoding af19d44327 feat(proxies): 代理有效期与失败回退
- schema/迁移: 代理有效期、提醒天数、失败回退配置 + 账号 fallback 来源字段
- service/repo/DTO/handler: CRUD 透传新字段 + 校验
- fallback 目标解析纯函数(链式解析 + 环检测 + 兜底)
- SweepExpiredProxies 到期改投账号 + outbox 失效
- ProxyExpiryService 后台到期扫描任务 + wire 注册
- 账号侧手动回切原代理 + fallback 来源徽章/按钮
- 前端: 创建/编辑表单、列表到期徽章、类型/API/i18n
- ops 告警: proxy_expired_count / proxy_expiring_soon_count 指标
- 导入导出携带有效期/回退字段(备用按 name 映射)
- 补全测试 stub + 集成测试 + review 问题修复
2026-06-08 00:01:30 +08:00

78 lines
1.5 KiB
Go

package service
import (
"net"
"net/url"
"strconv"
"time"
)
const (
FallbackModeNone = "none"
FallbackModeProxy = "proxy"
FallbackModeDirect = "direct"
)
type Proxy struct {
ID int64
Name string
Protocol string
Host string
Port int
Username string
Password string
Status string
CreatedAt time.Time
UpdatedAt time.Time
ExpiresAt *time.Time
FallbackMode string
BackupProxyID *int64
ExpiryWarnDays int
}
func (p *Proxy) IsActive() bool {
return p.Status == StatusActive
}
// IsExpired 报告代理是否已过期(基于 expires_at,与 status 无关)。
func (p *Proxy) IsExpired(now time.Time) bool {
return p.ExpiresAt != nil && !p.ExpiresAt.After(now)
}
func (p *Proxy) URL() string {
u := &url.URL{
Scheme: p.Protocol,
Host: net.JoinHostPort(p.Host, strconv.Itoa(p.Port)),
}
if p.Username != "" && p.Password != "" {
u.User = url.UserPassword(p.Username, p.Password)
}
return u.String()
}
type ProxyWithAccountCount struct {
Proxy
AccountCount int64
LatencyMs *int64
LatencyStatus string
LatencyMessage string
IPAddress string
Country string
CountryCode string
Region string
City string
QualityStatus string
QualityScore *int
QualityGrade string
QualitySummary string
QualityChecked *int64
}
type ProxyAccountSummary struct {
ID int64
Name string
Platform string
Type string
Notes *string
}