package service import ( "context" "math/rand/v2" "sync" "time" "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 // 本地 ID→Profile 映射缓存,用于 DoWithTLS 热路径快速查找 localCache map[int64]*model.TLSFingerprintProfile localMu sync.RWMutex } // NewTLSFingerprintProfileService 创建 TLS 指纹模板服务 func NewTLSFingerprintProfileService( repo TLSFingerprintProfileRepository, cache TLSFingerprintProfileCache, ) *TLSFingerprintProfileService { svc := &TLSFingerprintProfileService{ repo: repo, cache: cache, 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 } 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) } } }