| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280 |
- package service
- import (
- "context"
- "math/rand/v2"
- "sync"
- "time"
- "github.com/Wei-Shaw/sub2api/internal/config"
- "github.com/Wei-Shaw/sub2api/internal/model"
- "github.com/Wei-Shaw/sub2api/internal/pkg/logger"
- "github.com/Wei-Shaw/sub2api/internal/pkg/tlsfingerprint"
- )
- // TLSFingerprintProfileRepository 定义 TLS 指纹模板的数据访问接口
- type TLSFingerprintProfileRepository interface {
- List(ctx context.Context) ([]*model.TLSFingerprintProfile, error)
- GetByID(ctx context.Context, id int64) (*model.TLSFingerprintProfile, error)
- Create(ctx context.Context, profile *model.TLSFingerprintProfile) (*model.TLSFingerprintProfile, error)
- Update(ctx context.Context, profile *model.TLSFingerprintProfile) (*model.TLSFingerprintProfile, error)
- Delete(ctx context.Context, id int64) error
- }
- // TLSFingerprintProfileCache 定义 TLS 指纹模板的缓存接口
- type TLSFingerprintProfileCache interface {
- Get(ctx context.Context) ([]*model.TLSFingerprintProfile, bool)
- Set(ctx context.Context, profiles []*model.TLSFingerprintProfile) error
- Invalidate(ctx context.Context) error
- NotifyUpdate(ctx context.Context) error
- SubscribeUpdates(ctx context.Context, handler func())
- }
- // TLSFingerprintProfileService TLS 指纹模板管理服务
- type TLSFingerprintProfileService struct {
- repo TLSFingerprintProfileRepository
- cache TLSFingerprintProfileCache
- cfg *config.Config
- // 本地 ID→Profile 映射缓存,用于 DoWithTLS 热路径快速查找
- localCache map[int64]*model.TLSFingerprintProfile
- localMu sync.RWMutex
- }
- // NewTLSFingerprintProfileService 创建 TLS 指纹模板服务
- func NewTLSFingerprintProfileService(
- repo TLSFingerprintProfileRepository,
- cache TLSFingerprintProfileCache,
- cfgs ...*config.Config,
- ) *TLSFingerprintProfileService {
- var cfg *config.Config
- if len(cfgs) > 0 {
- cfg = cfgs[0]
- }
- svc := &TLSFingerprintProfileService{
- repo: repo,
- cache: cache,
- cfg: cfg,
- localCache: make(map[int64]*model.TLSFingerprintProfile),
- }
- ctx := context.Background()
- if err := svc.reloadFromDB(ctx); err != nil {
- logger.LegacyPrintf("service.tls_fp_profile", "[TLSFPProfileService] Failed to load profiles from DB on startup: %v", err)
- if fallbackErr := svc.refreshLocalCache(ctx); fallbackErr != nil {
- logger.LegacyPrintf("service.tls_fp_profile", "[TLSFPProfileService] Failed to load profiles from cache fallback on startup: %v", fallbackErr)
- }
- }
- if cache != nil {
- cache.SubscribeUpdates(ctx, func() {
- if err := svc.refreshLocalCache(context.Background()); err != nil {
- logger.LegacyPrintf("service.tls_fp_profile", "[TLSFPProfileService] Failed to refresh cache on notification: %v", err)
- }
- })
- }
- return svc
- }
- // --- CRUD ---
- // List 获取所有模板
- func (s *TLSFingerprintProfileService) List(ctx context.Context) ([]*model.TLSFingerprintProfile, error) {
- return s.repo.List(ctx)
- }
- // GetByID 根据 ID 获取模板
- func (s *TLSFingerprintProfileService) GetByID(ctx context.Context, id int64) (*model.TLSFingerprintProfile, error) {
- return s.repo.GetByID(ctx, id)
- }
- // Create 创建模板
- func (s *TLSFingerprintProfileService) Create(ctx context.Context, profile *model.TLSFingerprintProfile) (*model.TLSFingerprintProfile, error) {
- if err := profile.Validate(); err != nil {
- return nil, err
- }
- created, err := s.repo.Create(ctx, profile)
- if err != nil {
- return nil, err
- }
- refreshCtx, cancel := s.newCacheRefreshContext()
- defer cancel()
- s.invalidateAndNotify(refreshCtx)
- return created, nil
- }
- // Update 更新模板
- func (s *TLSFingerprintProfileService) Update(ctx context.Context, profile *model.TLSFingerprintProfile) (*model.TLSFingerprintProfile, error) {
- if err := profile.Validate(); err != nil {
- return nil, err
- }
- updated, err := s.repo.Update(ctx, profile)
- if err != nil {
- return nil, err
- }
- refreshCtx, cancel := s.newCacheRefreshContext()
- defer cancel()
- s.invalidateAndNotify(refreshCtx)
- return updated, nil
- }
- // Delete 删除模板
- func (s *TLSFingerprintProfileService) Delete(ctx context.Context, id int64) error {
- if err := s.repo.Delete(ctx, id); err != nil {
- return err
- }
- refreshCtx, cancel := s.newCacheRefreshContext()
- defer cancel()
- s.invalidateAndNotify(refreshCtx)
- return nil
- }
- // --- 热路径:运行时 Profile 查找 ---
- // GetProfileByID 根据 ID 从本地缓存获取 Profile(用于 DoWithTLS 热路径)
- // 返回 nil 表示未找到,调用方应 fallback 到内置默认 Profile
- func (s *TLSFingerprintProfileService) GetProfileByID(id int64) *tlsfingerprint.Profile {
- s.localMu.RLock()
- p, ok := s.localCache[id]
- s.localMu.RUnlock()
- if ok && p != nil {
- return p.ToTLSProfile()
- }
- return nil
- }
- // getRandomProfile 从本地缓存中随机选择一个 Profile
- func (s *TLSFingerprintProfileService) getRandomProfile() *tlsfingerprint.Profile {
- s.localMu.RLock()
- defer s.localMu.RUnlock()
- if len(s.localCache) == 0 {
- return nil
- }
- // 收集所有 profile
- profiles := make([]*model.TLSFingerprintProfile, 0, len(s.localCache))
- for _, p := range s.localCache {
- if p != nil {
- profiles = append(profiles, p)
- }
- }
- if len(profiles) == 0 {
- return nil
- }
- return profiles[rand.IntN(len(profiles))].ToTLSProfile()
- }
- // ResolveTLSProfile 根据 Account 的配置解析出运行时 TLS Profile
- //
- // 逻辑:
- // 1. 未启用 TLS 指纹 → 返回 nil(不伪装)
- // 2. 启用 + 绑定了 profile_id → 从缓存查找对应 profile
- // 3. 启用 + 未绑定或找不到 → 返回空 Profile(使用代码内置默认值)
- func (s *TLSFingerprintProfileService) ResolveTLSProfile(account *Account) *tlsfingerprint.Profile {
- if account == nil || !account.IsTLSFingerprintEnabled() {
- return nil
- }
- // The account flag is an explicit opt-in, while the global config is the
- // deployment-level kill switch. Keep the nil-config behavior for isolated
- // tests and non-server callers that do not load application config.
- if s != nil && s.cfg != nil && !s.cfg.Gateway.TLSFingerprint.Enabled {
- return nil
- }
- if account.Extra != nil {
- if name, ok := account.Extra["tls_fingerprint_builtin"].(string); ok {
- if p := tlsfingerprint.BuiltinProfile(name); p != nil {
- return p
- }
- }
- }
- id := account.GetTLSFingerprintProfileID()
- if id > 0 {
- if p := s.GetProfileByID(id); p != nil {
- return p
- }
- }
- if id == -1 {
- // 随机选择一个 profile
- if p := s.getRandomProfile(); p != nil {
- return p
- }
- }
- // TLS 启用但无绑定 profile → 空 Profile → dialer 使用内置默认值
- return &tlsfingerprint.Profile{Name: "Built-in Default (Node.js 24.x)"}
- }
- // --- 缓存管理 ---
- func (s *TLSFingerprintProfileService) refreshLocalCache(ctx context.Context) error {
- if s.cache != nil {
- if profiles, ok := s.cache.Get(ctx); ok {
- s.setLocalCache(profiles)
- return nil
- }
- }
- return s.reloadFromDB(ctx)
- }
- func (s *TLSFingerprintProfileService) reloadFromDB(ctx context.Context) error {
- profiles, err := s.repo.List(ctx)
- if err != nil {
- return err
- }
- if s.cache != nil {
- if err := s.cache.Set(ctx, profiles); err != nil {
- logger.LegacyPrintf("service.tls_fp_profile", "[TLSFPProfileService] Failed to set cache: %v", err)
- }
- }
- s.setLocalCache(profiles)
- return nil
- }
- func (s *TLSFingerprintProfileService) setLocalCache(profiles []*model.TLSFingerprintProfile) {
- m := make(map[int64]*model.TLSFingerprintProfile, len(profiles))
- for _, p := range profiles {
- m[p.ID] = p
- }
- s.localMu.Lock()
- s.localCache = m
- s.localMu.Unlock()
- }
- func (s *TLSFingerprintProfileService) newCacheRefreshContext() (context.Context, context.CancelFunc) {
- return context.WithTimeout(context.Background(), 3*time.Second)
- }
- func (s *TLSFingerprintProfileService) invalidateAndNotify(ctx context.Context) {
- if s.cache != nil {
- if err := s.cache.Invalidate(ctx); err != nil {
- logger.LegacyPrintf("service.tls_fp_profile", "[TLSFPProfileService] Failed to invalidate cache: %v", err)
- }
- }
- if err := s.reloadFromDB(ctx); err != nil {
- logger.LegacyPrintf("service.tls_fp_profile", "[TLSFPProfileService] Failed to refresh local cache: %v", err)
- s.localMu.Lock()
- s.localCache = make(map[int64]*model.TLSFingerprintProfile)
- s.localMu.Unlock()
- }
- if s.cache != nil {
- if err := s.cache.NotifyUpdate(ctx); err != nil {
- logger.LegacyPrintf("service.tls_fp_profile", "[TLSFPProfileService] Failed to notify cache update: %v", err)
- }
- }
- }
|