| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073 |
- package service
- import (
- "context"
- "database/sql"
- "os"
- "time"
- dbent "github.com/Wei-Shaw/sub2api/ent"
- "github.com/Wei-Shaw/sub2api/internal/config"
- "github.com/Wei-Shaw/sub2api/internal/payment"
- "github.com/Wei-Shaw/sub2api/internal/pkg/antigravity"
- "github.com/Wei-Shaw/sub2api/internal/pkg/logger"
- "github.com/Wei-Shaw/sub2api/internal/pkg/xai"
- "github.com/google/wire"
- "github.com/redis/go-redis/v9"
- "go.uber.org/zap"
- )
- func ProvideGrokOAuthService(proxyRepo ProxyRepository, oauthClient GrokOAuthClient, cfg *config.Config, redisClient *redis.Client) *GrokOAuthService {
- svc := NewGrokOAuthService(proxyRepo, oauthClient, cfg)
- // wire.go is depguard-exempt for redis; construct the Redis session store here.
- if redisClient != nil {
- svc = svc.WithSessionStore(xai.NewRedisSessionStore(redisClient))
- }
- return svc
- }
- // BuildInfo contains build information
- type BuildInfo struct {
- Version string
- BuildType string
- }
- // ProvidePricingService creates and initializes PricingService
- func ProvidePricingService(cfg *config.Config, remoteClient PricingRemoteClient) (*PricingService, error) {
- svc := NewPricingService(cfg, remoteClient)
- if err := svc.Initialize(); err != nil {
- // Pricing service initialization failure should not block startup, use fallback prices
- println("[Service] Warning: Pricing service initialization failed:", err.Error())
- }
- return svc, nil
- }
- // ProvideUpdateService creates UpdateService with BuildInfo
- func ProvideUpdateService(cache UpdateCache, githubClient GitHubReleaseClient, buildInfo BuildInfo) *UpdateService {
- return NewUpdateService(cache, githubClient, buildInfo.Version, buildInfo.BuildType)
- }
- // ProvideEmailQueueService creates EmailQueueService with default worker count
- func ProvideEmailQueueService(emailService *EmailService) *EmailQueueService {
- return NewEmailQueueService(emailService, 3)
- }
- // ProvideAuthService wires the optional captcha providers into AuthService while
- // keeping NewAuthService's public constructor compatible with existing tests.
- func ProvideAuthService(
- entClient *dbent.Client,
- userRepo UserRepository,
- redeemRepo RedeemCodeRepository,
- refreshTokenCache RefreshTokenCache,
- cfg *config.Config,
- settingService *SettingService,
- emailService *EmailService,
- turnstileService *TurnstileService,
- tencentCaptchaService *TencentCaptchaService,
- aliyunCaptchaService *AliyunCaptchaService,
- emailQueueService *EmailQueueService,
- promoService *PromoService,
- defaultSubAssigner DefaultSubscriptionAssigner,
- affiliateService *AffiliateService,
- userPlatformQuotaRepo UserPlatformQuotaRepository,
- ) *AuthService {
- svc := NewAuthService(
- entClient,
- userRepo,
- redeemRepo,
- refreshTokenCache,
- cfg,
- settingService,
- emailService,
- turnstileService,
- emailQueueService,
- promoService,
- defaultSubAssigner,
- affiliateService,
- userPlatformQuotaRepo,
- )
- svc.SetTencentCaptchaService(tencentCaptchaService)
- svc.SetAliyunCaptchaService(aliyunCaptchaService)
- return svc
- }
- // ProvideOAuthRefreshAPI creates OAuthRefreshAPI with the default lock TTL.
- func ProvideOAuthRefreshAPI(accountRepo AccountRepository, tokenCache GeminiTokenCache) *OAuthRefreshAPI {
- return NewOAuthRefreshAPI(accountRepo, tokenCache)
- }
- func ProvideBatchImageModelPricingResolver(resolver *ModelPricingResolver) *BatchImageModelPricingResolver {
- return &BatchImageModelPricingResolver{Resolver: resolver}
- }
- func ProvideBatchImageCleanupService(repo BatchImageRepository, accountRepo AccountRepository, cfg *config.Config) *BatchImageCleanupService {
- svc := NewBatchImageCleanupService(repo, accountRepo, cfg)
- svc.Start()
- return svc
- }
- // ProvideOpenAIOAuthService creates OpenAIOAuthService with privacy/account enrichment support.
- func ProvideOpenAIOAuthService(
- proxyRepo ProxyRepository,
- oauthClient OpenAIOAuthClient,
- privacyClientFactory PrivacyClientFactory,
- ) *OpenAIOAuthService {
- svc := NewOpenAIOAuthService(proxyRepo, oauthClient)
- svc.SetPrivacyClientFactory(privacyClientFactory)
- return svc
- }
- // ProvideTokenRefreshService creates and starts TokenRefreshService
- func ProvideTokenRefreshService(
- accountRepo AccountRepository,
- oauthService *OAuthService,
- openaiOAuthService *OpenAIOAuthService,
- geminiOAuthService *GeminiOAuthService,
- antigravityOAuthService *AntigravityOAuthService,
- grokOAuthService *GrokOAuthService,
- cacheInvalidator TokenCacheInvalidator,
- schedulerCache SchedulerCache,
- cfg *config.Config,
- tempUnschedCache TempUnschedCache,
- privacyClientFactory PrivacyClientFactory,
- proxyRepo ProxyRepository,
- refreshAPI *OAuthRefreshAPI,
- runtimeBlocker AccountRuntimeBlocker,
- ) *TokenRefreshService {
- svc := NewTokenRefreshService(accountRepo, oauthService, openaiOAuthService, geminiOAuthService, antigravityOAuthService, cacheInvalidator, schedulerCache, cfg, tempUnschedCache, grokOAuthService)
- // 注入 OpenAI privacy opt-out 依赖
- svc.SetPrivacyDeps(privacyClientFactory, proxyRepo)
- // 注入统一 OAuth 刷新 API(消除 TokenRefreshService 与 TokenProvider 之间的竞争条件)
- svc.SetRefreshAPI(refreshAPI)
- // 调用侧显式注入后台刷新策略,避免策略漂移
- svc.SetRefreshPolicy(DefaultBackgroundRefreshPolicy())
- svc.SetAccountRuntimeBlocker(runtimeBlocker)
- svc.Start()
- return svc
- }
- // ProvideClaudeTokenProvider creates ClaudeTokenProvider with OAuthRefreshAPI injection
- func ProvideClaudeTokenProvider(
- accountRepo AccountRepository,
- tokenCache GeminiTokenCache,
- oauthService *OAuthService,
- refreshAPI *OAuthRefreshAPI,
- ) *ClaudeTokenProvider {
- p := NewClaudeTokenProvider(accountRepo, tokenCache, oauthService)
- executor := NewClaudeTokenRefresher(oauthService)
- p.SetRefreshAPI(refreshAPI, executor)
- p.SetRefreshPolicy(ClaudeProviderRefreshPolicy())
- return p
- }
- // ProvideOpenAITokenProvider creates OpenAITokenProvider with OAuthRefreshAPI injection
- func ProvideOpenAITokenProvider(
- accountRepo AccountRepository,
- tokenCache GeminiTokenCache,
- openaiOAuthService *OpenAIOAuthService,
- refreshAPI *OAuthRefreshAPI,
- ) *OpenAITokenProvider {
- p := NewOpenAITokenProvider(accountRepo, tokenCache, openaiOAuthService)
- executor := NewOpenAITokenRefresher(openaiOAuthService, accountRepo)
- p.SetRefreshAPI(refreshAPI, executor)
- p.SetRefreshPolicy(OpenAIProviderRefreshPolicy())
- return p
- }
- // ProvideOpenAIQuotaService wires the OpenAI quota query/reset service.
- // It depends on the OpenAI token provider for refreshed access tokens and the
- // privacy client factory for the impersonated upstream HTTP client.
- func ProvideOpenAIQuotaService(
- accountRepo AccountRepository,
- proxyRepo ProxyRepository,
- tokenProvider *OpenAITokenProvider,
- privacyClientFactory PrivacyClientFactory,
- openAIGatewayService *OpenAIGatewayService,
- ) *OpenAIQuotaService {
- service := NewOpenAIQuotaService(accountRepo, proxyRepo, tokenProvider, privacyClientFactory)
- service.agentIdentityWS = openAIGatewayService
- return service
- }
- // ProvideOpenAIQuotaAutoResetService 启动账号级自动用卡队列与补偿扫描。
- func ProvideOpenAIQuotaAutoResetService(
- accountRepo AccountRepository,
- quotaService *OpenAIQuotaService,
- rateLimitService *RateLimitService,
- idempotency *IdempotencyCoordinator,
- audit *AuditLogService,
- settingService *SettingService,
- leaderLock LeaderLockCache,
- ) *OpenAIQuotaAutoResetService {
- service := NewOpenAIQuotaAutoResetService(
- accountRepo,
- quotaService,
- rateLimitService,
- idempotency,
- audit,
- settingService,
- leaderLock,
- )
- service.Start()
- return service
- }
- func ProvideAccountUsageService(
- accountRepo AccountRepository,
- usageLogRepo UsageLogRepository,
- usageFetcher ClaudeUsageFetcher,
- geminiQuotaService *GeminiQuotaService,
- antigravityQuotaFetcher *AntigravityQuotaFetcher,
- grokQuotaFetcher *GrokQuotaFetcher,
- grokQuotaService *GrokQuotaService,
- openAIQuotaService *OpenAIQuotaService,
- cache *UsageCache,
- identityCache IdentityCache,
- tlsFPProfileService *TLSFingerprintProfileService,
- openAIGatewayService *OpenAIGatewayService,
- ) *AccountUsageService {
- service := NewAccountUsageService(
- accountRepo,
- usageLogRepo,
- usageFetcher,
- geminiQuotaService,
- antigravityQuotaFetcher,
- grokQuotaFetcher,
- grokQuotaService,
- openAIQuotaService,
- cache,
- identityCache,
- tlsFPProfileService,
- )
- service.agentIdentityWS = openAIGatewayService
- return service
- }
- func ProvideAccountTestService(
- concurrencyService *ConcurrencyService,
- accountRepo AccountRepository,
- geminiTokenProvider *GeminiTokenProvider,
- claudeTokenProvider *ClaudeTokenProvider,
- grokTokenProvider *GrokTokenProvider,
- antigravityGatewayService *AntigravityGatewayService,
- httpUpstream HTTPUpstream,
- cfg *config.Config,
- tlsFPProfileService *TLSFingerprintProfileService,
- openAIGatewayService *OpenAIGatewayService,
- settingService *SettingService,
- pluginManager *PluginManager,
- ) *AccountTestService {
- service := NewAccountTestService(
- accountRepo,
- geminiTokenProvider,
- claudeTokenProvider,
- grokTokenProvider,
- antigravityGatewayService,
- httpUpstream,
- cfg,
- tlsFPProfileService,
- )
- service.concurrencyService = concurrencyService
- service.agentIdentityWS = openAIGatewayService
- service.SetOpenAIGatewayService(openAIGatewayService)
- service.SetSettingService(settingService)
- service.SetPluginManager(pluginManager)
- return service
- }
- func ProvideGrokQuotaService(
- accountRepo AccountRepository,
- proxyRepo ProxyRepository,
- tokenProvider *GrokTokenProvider,
- httpUpstream HTTPUpstream,
- cfg *config.Config,
- usageLogRepo UsageLogRepository,
- settingService *SettingService,
- ) *GrokQuotaService {
- service := NewGrokQuotaService(accountRepo, proxyRepo, tokenProvider, httpUpstream, cfg, usageLogRepo)
- service.SetSettingService(settingService)
- return service
- }
- // ProvideCNProviderQuotaService 构造国产供应商 Coding Plan 额度探测服务。
- func ProvideCNProviderQuotaService(
- accountRepo AccountRepository,
- proxyRepo ProxyRepository,
- httpUpstream HTTPUpstream,
- cfg *config.Config,
- ) *CNProviderQuotaService {
- return NewCNProviderQuotaService(accountRepo, proxyRepo, httpUpstream, cfg)
- }
- // ProvideCNProviderBalanceService 构造国产供应商余额探测服务。
- func ProvideCNProviderBalanceService(
- accountRepo AccountRepository,
- proxyRepo ProxyRepository,
- httpUpstream HTTPUpstream,
- cfg *config.Config,
- ) *CNProviderBalanceService {
- return NewCNProviderBalanceService(accountRepo, proxyRepo, httpUpstream, cfg)
- }
- // ProvideCNProviderBalanceCheckService 构造并启动周期余额/额度检测任务。
- // payg 账号探余额(低余额停调);coding plan 账号探 5h/weekly 滚动窗口
- // (落 extra 快照供调度阈值评估自动停调)。
- // 间隔取自 gateway.cn_providers.balance_check_interval_minutes;<=0 或关闭时不启动。
- func ProvideCNProviderBalanceCheckService(
- accountRepo AccountRepository,
- balanceService *CNProviderBalanceService,
- quotaService *CNProviderQuotaService,
- cfg *config.Config,
- ) *CNProviderBalanceCheckService {
- minutes := 10
- if cfg != nil && cfg.Gateway.CNProviders.BalanceCheckIntervalMinutes > 0 {
- minutes = cfg.Gateway.CNProviders.BalanceCheckIntervalMinutes
- }
- svc := NewCNProviderBalanceCheckService(accountRepo, balanceService, quotaService, cfg, time.Duration(minutes)*time.Minute)
- svc.Start()
- return svc
- }
- // ProvideGeminiTokenProvider creates GeminiTokenProvider with OAuthRefreshAPI injection
- func ProvideGeminiTokenProvider(
- accountRepo AccountRepository,
- tokenCache GeminiTokenCache,
- geminiOAuthService *GeminiOAuthService,
- refreshAPI *OAuthRefreshAPI,
- ) *GeminiTokenProvider {
- p := NewGeminiTokenProvider(accountRepo, tokenCache, geminiOAuthService)
- executor := NewGeminiTokenRefresher(geminiOAuthService)
- p.SetRefreshAPI(refreshAPI, executor)
- p.SetRefreshPolicy(GeminiProviderRefreshPolicy())
- return p
- }
- // ProvideAntigravityTokenProvider creates AntigravityTokenProvider with OAuthRefreshAPI injection
- func ProvideAntigravityTokenProvider(
- accountRepo AccountRepository,
- tokenCache GeminiTokenCache,
- antigravityOAuthService *AntigravityOAuthService,
- refreshAPI *OAuthRefreshAPI,
- tempUnschedCache TempUnschedCache,
- ) *AntigravityTokenProvider {
- p := NewAntigravityTokenProvider(accountRepo, tokenCache, antigravityOAuthService)
- executor := NewAntigravityTokenRefresher(antigravityOAuthService)
- p.SetRefreshAPI(refreshAPI, executor)
- p.SetRefreshPolicy(AntigravityProviderRefreshPolicy())
- p.SetTempUnschedCache(tempUnschedCache)
- return p
- }
- // ProvideGrokTokenProvider creates GrokTokenProvider with OAuthRefreshAPI injection.
- func ProvideGrokTokenProvider(
- accountRepo AccountRepository,
- tokenCache GeminiTokenCache,
- grokOAuthService *GrokOAuthService,
- refreshAPI *OAuthRefreshAPI,
- tempUnschedCache TempUnschedCache,
- ) *GrokTokenProvider {
- p := NewGrokTokenProvider(accountRepo, tokenCache)
- executor := NewGrokTokenRefresher(grokOAuthService)
- p.SetRefreshAPI(refreshAPI, executor)
- p.SetRefreshPolicy(GrokProviderRefreshPolicy())
- p.SetTempUnschedCache(tempUnschedCache)
- return p
- }
- // ProvideDashboardAggregationService 创建并启动仪表盘聚合服务
- func ProvideDashboardAggregationService(repo DashboardAggregationRepository, timingWheel *TimingWheelService, lockCache LeaderLockCache, db *sql.DB, cfg *config.Config) *DashboardAggregationService {
- svc := NewDashboardAggregationService(repo, timingWheel, cfg)
- svc.SetLeaderLock(lockCache, db)
- svc.Start()
- return svc
- }
- // ProvideUsageCleanupService 创建并启动使用记录清理任务服务
- func ProvideUsageCleanupService(repo UsageCleanupRepository, timingWheel *TimingWheelService, dashboardAgg *DashboardAggregationService, cfg *config.Config) *UsageCleanupService {
- svc := NewUsageCleanupService(repo, timingWheel, dashboardAgg, cfg)
- svc.Start()
- return svc
- }
- // ProvideAccountExpiryService creates and starts AccountExpiryService.
- func ProvideAccountExpiryService(accountRepo AccountRepository) *AccountExpiryService {
- svc := NewAccountExpiryService(accountRepo, time.Minute)
- svc.Start()
- return svc
- }
- // ProvideOpenAICodexVersionSyncService creates and starts OpenAICodexVersionSyncService.
- // 出站 Codex 身份的版本号靠它跟随官方发布,无需为了跟版本而发新版本;面板可关闭。
- func ProvideOpenAICodexVersionSyncService(
- settingRepo SettingRepository,
- settingService *SettingService,
- githubClient GitHubReleaseClient,
- ) *OpenAICodexVersionSyncService {
- svc := NewOpenAICodexVersionSyncService(settingRepo, settingService, githubClient, openAICodexVersionSyncInterval)
- svc.Start()
- return svc
- }
- // ProvideProxyExpiryService creates and starts ProxyExpiryService.
- func ProvideProxyExpiryService(proxyRepo ProxyRepository) *ProxyExpiryService {
- svc := NewProxyExpiryService(proxyRepo, time.Minute)
- svc.Start()
- return svc
- }
- // ProvideSubscriptionExpiryService creates and starts SubscriptionExpiryService.
- func ProvideSubscriptionExpiryService(userSubRepo UserSubscriptionRepository, settingRepo SettingRepository, notificationEmailService *NotificationEmailService, lockCache LeaderLockCache, db *sql.DB) *SubscriptionExpiryService {
- svc := NewSubscriptionExpiryService(userSubRepo, time.Minute)
- svc.SetSettingRepository(settingRepo)
- svc.SetNotificationEmailService(notificationEmailService)
- svc.SetLeaderLock(lockCache, db)
- svc.Start()
- return svc
- }
- // ProvideTimingWheelService creates and starts TimingWheelService
- func ProvideTimingWheelService() (*TimingWheelService, error) {
- svc, err := NewTimingWheelService()
- if err != nil {
- return nil, err
- }
- svc.Start()
- return svc, nil
- }
- // ProvideDeferredService creates and starts DeferredService
- func ProvideDeferredService(accountRepo AccountRepository, timingWheel *TimingWheelService) *DeferredService {
- svc := NewDeferredService(accountRepo, timingWheel, 10*time.Second)
- svc.Start()
- return svc
- }
- // ProvideConcurrencyService creates ConcurrencyService and starts slot cleanup worker.
- func ProvideConcurrencyService(cache ConcurrencyCache, accountRepo AccountRepository, cfg *config.Config) *ConcurrencyService {
- svc := NewConcurrencyService(cache)
- if err := svc.CleanupStaleProcessSlots(context.Background()); err != nil {
- logger.LegacyPrintf("service.concurrency", "Warning: startup cleanup stale process slots failed: %v", err)
- }
- if cfg != nil {
- svc.SetAccountLoadBatchCacheTTL(time.Duration(cfg.Gateway.Scheduling.LoadBatchCacheTTLMS) * time.Millisecond)
- svc.StartSlotCleanupWorker(accountRepo, cfg.Gateway.Scheduling.SlotCleanupInterval)
- }
- return svc
- }
- // ProvideUserMessageQueueService 创建用户消息串行队列服务并启动清理 worker
- func ProvideUserMessageQueueService(cache UserMsgQueueCache, rpmCache RPMCache, cfg *config.Config) *UserMessageQueueService {
- svc := NewUserMessageQueueService(cache, rpmCache, &cfg.Gateway.UserMessageQueue)
- if cfg.Gateway.UserMessageQueue.CleanupIntervalSeconds > 0 {
- svc.StartCleanupWorker(time.Duration(cfg.Gateway.UserMessageQueue.CleanupIntervalSeconds) * time.Second)
- }
- return svc
- }
- // ProvideSchedulerSnapshotService creates and starts SchedulerSnapshotService.
- func ProvideSchedulerSnapshotService(
- cache SchedulerCache,
- outboxRepo SchedulerOutboxRepository,
- accountRepo AccountRepository,
- groupRepo GroupRepository,
- cfg *config.Config,
- ) *SchedulerSnapshotService {
- svc := NewSchedulerSnapshotService(cache, outboxRepo, accountRepo, groupRepo, cfg)
- svc.Start()
- return svc
- }
- // ProvideRateLimitService creates RateLimitService with optional dependencies.
- func ProvideRateLimitService(
- accountRepo AccountRepository,
- usageRepo UsageLogRepository,
- cfg *config.Config,
- geminiQuotaService *GeminiQuotaService,
- tempUnschedCache TempUnschedCache,
- timeoutCounterCache TimeoutCounterCache,
- openAI403CounterCache OpenAI403CounterCache,
- settingService *SettingService,
- tokenCacheInvalidator TokenCacheInvalidator,
- ) *RateLimitService {
- svc := NewRateLimitService(accountRepo, usageRepo, cfg, geminiQuotaService, tempUnschedCache)
- if healthCache, ok := tempUnschedCache.(OpenAIAPIKeyHealthCache); ok {
- svc.SetOpenAIAPIKeyHealthCache(healthCache)
- }
- svc.SetTimeoutCounterCache(timeoutCounterCache)
- svc.SetOpenAI403CounterCache(openAI403CounterCache)
- svc.SetSettingService(settingService)
- svc.SetTokenCacheInvalidator(tokenCacheInvalidator)
- return svc
- }
- // ProvideOpsMetricsCollector creates and starts OpsMetricsCollector.
- func ProvideOpsMetricsCollector(
- opsRepo OpsRepository,
- settingRepo SettingRepository,
- accountRepo AccountRepository,
- concurrencyService *ConcurrencyService,
- db *sql.DB,
- redisClient *redis.Client,
- cfg *config.Config,
- ) *OpsMetricsCollector {
- collector := NewOpsMetricsCollector(opsRepo, settingRepo, accountRepo, concurrencyService, db, redisClient, cfg)
- collector.Start()
- return collector
- }
- // ProvideOpsAggregationService creates and starts OpsAggregationService (hourly/daily pre-aggregation).
- func ProvideOpsAggregationService(
- opsRepo OpsRepository,
- settingRepo SettingRepository,
- db *sql.DB,
- redisClient *redis.Client,
- cfg *config.Config,
- ) *OpsAggregationService {
- svc := NewOpsAggregationService(opsRepo, settingRepo, db, redisClient, cfg)
- svc.Start()
- return svc
- }
- // ProvideOpsAlertEvaluatorService creates and starts OpsAlertEvaluatorService.
- func ProvideOpsAlertEvaluatorService(
- opsService *OpsService,
- opsRepo OpsRepository,
- emailService *EmailService,
- redisClient *redis.Client,
- cfg *config.Config,
- proxyRepo ProxyRepository,
- ) *OpsAlertEvaluatorService {
- svc := NewOpsAlertEvaluatorService(opsService, opsRepo, emailService, redisClient, cfg, proxyRepo)
- svc.Start()
- return svc
- }
- // ProvideOpsCleanupService creates and starts OpsCleanupService (cron scheduled).
- // channelMonitorSvc 让维护任务(聚合 + 历史/聚合软删)跟随 ops 清理 cron 一起跑,
- // 共享 leader lock + heartbeat。
- // settingRepo 让 cleanup service 自己读 ops_advanced_settings.data_retention 覆盖 cfg;
- // opsService 用来反向注入 cleanup hook,以便 UI 改清理设置时能 Reload cron。
- func ProvideOpsCleanupService(
- opsRepo OpsRepository,
- db *sql.DB,
- redisClient *redis.Client,
- cfg *config.Config,
- channelMonitorSvc *ChannelMonitorService,
- settingRepo SettingRepository,
- opsService *OpsService,
- ) *OpsCleanupService {
- svc := NewOpsCleanupService(opsRepo, db, redisClient, cfg, channelMonitorSvc, settingRepo)
- svc.Start()
- if opsService != nil {
- opsService.SetCleanupReloader(svc)
- }
- return svc
- }
- func ProvideOpsSystemLogSink(opsRepo OpsRepository) *OpsSystemLogSink {
- sink := NewOpsSystemLogSink(opsRepo)
- sink.Start()
- logger.SetSink(sink)
- return sink
- }
- // ProvideAuditLogService 创建操作审计日志服务并启动异步写入与保留期清理协程。
- // 停止逻辑挂在 cmd/server 的 provideCleanup。
- func ProvideAuditLogService(repo AuditLogRepository, settingService *SettingService) *AuditLogService {
- svc := NewAuditLogService(repo, settingService)
- svc.Start()
- return svc
- }
- func buildIdempotencyConfig(cfg *config.Config) IdempotencyConfig {
- idempotencyCfg := DefaultIdempotencyConfig()
- if cfg != nil {
- if cfg.Idempotency.DefaultTTLSeconds > 0 {
- idempotencyCfg.DefaultTTL = time.Duration(cfg.Idempotency.DefaultTTLSeconds) * time.Second
- }
- if cfg.Idempotency.SystemOperationTTLSeconds > 0 {
- idempotencyCfg.SystemOperationTTL = time.Duration(cfg.Idempotency.SystemOperationTTLSeconds) * time.Second
- }
- if cfg.Idempotency.ProcessingTimeoutSeconds > 0 {
- idempotencyCfg.ProcessingTimeout = time.Duration(cfg.Idempotency.ProcessingTimeoutSeconds) * time.Second
- }
- if cfg.Idempotency.FailedRetryBackoffSeconds > 0 {
- idempotencyCfg.FailedRetryBackoff = time.Duration(cfg.Idempotency.FailedRetryBackoffSeconds) * time.Second
- }
- if cfg.Idempotency.MaxStoredResponseLen > 0 {
- idempotencyCfg.MaxStoredResponseLen = cfg.Idempotency.MaxStoredResponseLen
- }
- idempotencyCfg.ObserveOnly = cfg.Idempotency.ObserveOnly
- }
- return idempotencyCfg
- }
- func ProvideIdempotencyCoordinator(repo IdempotencyRepository, cfg *config.Config) *IdempotencyCoordinator {
- coordinator := NewIdempotencyCoordinator(repo, buildIdempotencyConfig(cfg))
- SetDefaultIdempotencyCoordinator(coordinator)
- return coordinator
- }
- func ProvideSystemOperationLockService(repo IdempotencyRepository, cfg *config.Config) *SystemOperationLockService {
- return NewSystemOperationLockService(repo, buildIdempotencyConfig(cfg))
- }
- func ProvideIdempotencyCleanupService(repo IdempotencyRepository, cfg *config.Config) *IdempotencyCleanupService {
- svc := NewIdempotencyCleanupService(repo, cfg)
- svc.Start()
- return svc
- }
- // ProvideScheduledTestService creates ScheduledTestService.
- func ProvideScheduledTestService(
- planRepo ScheduledTestPlanRepository,
- resultRepo ScheduledTestResultRepository,
- ) *ScheduledTestService {
- return NewScheduledTestService(planRepo, resultRepo)
- }
- // ProvideScheduledTestRunnerService creates and starts ScheduledTestRunnerService.
- func ProvideScheduledTestRunnerService(
- planRepo ScheduledTestPlanRepository,
- scheduledSvc *ScheduledTestService,
- accountTestSvc *AccountTestService,
- rateLimitSvc *RateLimitService,
- cfg *config.Config,
- ) *ScheduledTestRunnerService {
- svc := NewScheduledTestRunnerService(planRepo, scheduledSvc, accountTestSvc, rateLimitSvc, cfg)
- svc.Start()
- return svc
- }
- // ProvideOpsScheduledReportService creates and starts OpsScheduledReportService.
- func ProvideOpsScheduledReportService(
- opsService *OpsService,
- userService *UserService,
- emailService *EmailService,
- redisClient *redis.Client,
- cfg *config.Config,
- ) *OpsScheduledReportService {
- svc := NewOpsScheduledReportService(opsService, userService, emailService, redisClient, cfg)
- svc.Start()
- return svc
- }
- // ProvideAPIKeyAuthCacheInvalidator 提供 API Key 认证缓存失效能力
- func ProvideAPIKeyAuthCacheInvalidator(apiKeyService *APIKeyService) APIKeyAuthCacheInvalidator {
- // Start Pub/Sub subscriber for L1 cache invalidation across instances
- apiKeyService.StartAuthCacheInvalidationSubscriber(context.Background())
- return apiKeyService
- }
- // ProvideImageStorageSettingService 构造异步生图对象存储的后台设置服务。
- //
- // config.yaml 里的 image_storage 作为回落:后台从未保存过设置时沿用它,
- // 使升级前已通过配置文件开启该功能的部署不被打断。
- func ProvideImageStorageSettingService(
- settingRepo SettingRepository,
- encryptor SecretEncryptor,
- backup *BackupService,
- factory ImageStorageFactory,
- cfg *config.Config,
- ) *ImageStorageSettingService {
- if cfg.ImageStorage.Enabled && !cfg.ImageStorage.Active() {
- // 列出具体缺失的键。若这些键其实已在环境变量里设过,说明它们没被读进来,
- // 请确认 setDefaults 中已为其注册默认值(见 config.setEnvReachableDefaults)。
- logger.L().Warn("image_storage.enabled is true in config but object storage is not fully configured; configure it in the admin UI or complete the config file",
- zap.Strings("missing_keys", cfg.ImageStorage.MissingCredentialKeys()))
- }
- return NewImageStorageSettingService(settingRepo, encryptor, backup, factory, cfg.ImageStorage)
- }
- // ProvideImageTaskService 构造异步图片任务服务。
- //
- // 对象存储是异步图片任务的启用前提:仅当开关打开且凭证齐全时功能才可用,否则整体禁用
- // (handler 返回 404,不创建任务、不写 Redis),从而避免大 base64 结果撑爆 Redis。
- // 启用状态由 settings 服务在运行时解析,因此后台改开关后无需重启即可生效。
- func ProvideImageTaskService(store ImageTaskStore, settings *ImageStorageSettingService) *ImageTaskService {
- return NewImageTaskServiceWithResolver(store, settings.Resolver(), defaultImageTaskTTL, defaultImageTaskExecutionTimeout)
- }
- // ProvideBackupService creates and starts BackupService
- func ProvideBackupService(
- settingRepo SettingRepository,
- cfg *config.Config,
- encryptor SecretEncryptor,
- storeFactory BackupObjectStoreFactory,
- dumper DBDumper,
- lockCache LeaderLockCache,
- db *sql.DB,
- ) *BackupService {
- svc := NewBackupService(settingRepo, cfg, encryptor, storeFactory, dumper)
- svc.SetLeaderLock(lockCache, db)
- svc.Start()
- return svc
- }
- // ProvideOpsService constructs OpsService and wires the SettingService-backed quota
- // auto-pause cache sink. Mirrors the SetCleanupReloader pattern: OpsService doesn't
- // hold a *SettingService reference, but wire injects a tiny callback so writes to
- // ops_advanced_settings immediately propagate into the scheduler hot-path cache.
- func ProvideOpsService(
- opsRepo OpsRepository,
- settingRepo SettingRepository,
- cfg *config.Config,
- accountRepo AccountRepository,
- userRepo UserRepository,
- concurrencyService *ConcurrencyService,
- gatewayService *GatewayService,
- openAIGatewayService *OpenAIGatewayService,
- geminiCompatService *GeminiMessagesCompatService,
- antigravityGatewayService *AntigravityGatewayService,
- systemLogSink *OpsSystemLogSink,
- settingService *SettingService,
- authCacheInvalidationWorker *AuthCacheInvalidationWorker,
- apiKeyService *APIKeyService,
- ) *OpsService {
- svc := NewOpsService(
- opsRepo,
- settingRepo,
- cfg,
- accountRepo,
- userRepo,
- concurrencyService,
- gatewayService,
- openAIGatewayService,
- geminiCompatService,
- antigravityGatewayService,
- systemLogSink,
- )
- if settingService != nil {
- svc.SetOpenAIQuotaAutoPauseSettingsSink(settingService.SetOpenAIQuotaAutoPauseSettings)
- // Optional warm-up so the first scheduled request after process start observes
- // a populated cache rather than zero defaults. Best-effort, sync-bounded.
- settingService.WarmOpenAIQuotaAutoPauseSettings(context.Background())
- }
- svc.authCacheInvalidationWorker = authCacheInvalidationWorker
- svc.apiKeyService = apiKeyService
- svc.StartRuntimeSettingsRefresh(context.Background())
- return svc
- }
- // ProvideOpsIngressRejectAggregator starts the bounded security aggregation
- // runtime and attaches it to OpsService, which is the middleware recorder.
- func ProvideOpsIngressRejectAggregator(opsRepo OpsRepository, opsService *OpsService) *OpsIngressRejectAggregator {
- repo, ok := opsRepo.(OpsIngressRejectRepository)
- if !ok {
- return nil
- }
- aggregator := NewOpsIngressRejectAggregator(repo)
- aggregator.Start()
- opsService.SetIngressRejectAggregator(aggregator)
- return aggregator
- }
- // ProvideSettingService wires SettingService with group reader and proxy repo.
- func ProvideSettingService(settingRepo SettingRepository, groupRepo GroupRepository, proxyRepo ProxyRepository, cfg *config.Config) *SettingService {
- svc := NewSettingService(settingRepo, cfg)
- svc.SetDefaultSubscriptionGroupReader(groupRepo)
- svc.SetProxyRepository(proxyRepo)
- if err := svc.LoadForwardedClientIPSettings(context.Background()); err != nil {
- logger.LegacyPrintf("service.setting", "Warning: load forwarded client IP settings failed: %v", err)
- }
- if err := svc.MigrateOpenAIAllowClaudeCodeCodexPluginSetting(context.Background()); err != nil {
- logger.LegacyPrintf("service.setting", "Warning: migrate openai allow Claude Code Codex plugin setting failed: %v", err)
- }
- if err := svc.MigrateCodexBodyFingerprintToSignals(context.Background()); err != nil {
- logger.LegacyPrintf("service.setting", "Warning: migrate codex body fingerprint to signals failed: %v", err)
- }
- if err := svc.MigrateGrokDefaultTextModel(context.Background()); err != nil {
- logger.LegacyPrintf("service.setting", "Warning: migrate Grok default text model failed: %v", err)
- }
- antigravity.SetUserAgentVersionResolver(svc.GetAntigravityUserAgentVersion)
- // enforceCodexIdentityHeaders 是所有 Codex 出站路径共用的纯函数收口点,拿不到 ctx,
- // 故注入无参解析器;解析器内部自带 60s TTL 缓存,热路径不触库。
- SetCodexCanonicalUserAgentResolver(func() string {
- return svc.GetOpenAICodexCanonicalUserAgent(context.Background())
- })
- return svc
- }
- // ProvideBillingCacheService wires BillingCacheService with its RPM dependencies.
- func ProvideBillingCacheService(
- cache BillingCache,
- userRepo UserRepository,
- subRepo UserSubscriptionRepository,
- apiKeyRepo APIKeyRepository,
- rpmCache UserRPMCache,
- rateRepo UserGroupRateRepository,
- cfg *config.Config,
- userPlatformQuotaRepo UserPlatformQuotaRepository,
- ) *BillingCacheService {
- return NewBillingCacheService(cache, userRepo, subRepo, apiKeyRepo, rpmCache, rateRepo, cfg, userPlatformQuotaRepo)
- }
- // ProvideAPIKeyService wires APIKeyService and connects rate-limit cache invalidation.
- func ProvideAPIKeyService(
- apiKeyRepo APIKeyRepository,
- userRepo UserRepository,
- groupRepo GroupRepository,
- userSubRepo UserSubscriptionRepository,
- userGroupRateRepo UserGroupRateRepository,
- cache APIKeyCache,
- cfg *config.Config,
- billingCacheService *BillingCacheService,
- concurrencyService *ConcurrencyService,
- ) *APIKeyService {
- svc := NewAPIKeyService(apiKeyRepo, userRepo, groupRepo, userSubRepo, userGroupRateRepo, cache, cfg)
- svc.SetRateLimitCacheInvalidator(billingCacheService)
- svc.SetConcurrencyService(concurrencyService)
- return svc
- }
- func ProvideIntelligentTestService(repo IntelligentTestRepository, runner *AccountTestService) *IntelligentTestService {
- svc := NewIntelligentTestService(repo, runner)
- svc.Start()
- return svc
- }
- func ProvideTLSFingerprintProfileService(repo TLSFingerprintProfileRepository, cache TLSFingerprintProfileCache, cfg *config.Config) *TLSFingerprintProfileService {
- return NewTLSFingerprintProfileService(repo, cache, cfg)
- }
- // ProviderSet is the Wire provider set for all services
- var ProviderSet = wire.NewSet(
- NewUserCleanupService,
- ProvideIntelligentTestService,
- // Core services
- ProvideAuthService,
- NewPasskeyService,
- NewUserService,
- ProvideAPIKeyService,
- ProvideAPIKeyAuthCacheInvalidator,
- ProvideAuthCacheInvalidationWorker,
- NewGroupService,
- NewCompositeRouteResolver,
- NewAccountService,
- NewProxyService,
- NewRedeemService,
- NewPromoService,
- NewUsageService,
- NewAccountTrafficService,
- NewDashboardService,
- ProvidePricingService,
- NewBillingService,
- ProvideBillingCacheService,
- NewAnnouncementService,
- NewAdminService,
- wire.Bind(new(AntiDegradeStore), new(AdminService)),
- NewGatewayService,
- NewOpenAIGatewayService,
- ProvideImageStorageSettingService,
- ProvideImageTaskService,
- ProvideBatchImageModelPricingResolver,
- NewBatchImagePublicService,
- NewBatchImageDownloadService,
- ProvideBatchImageCleanupService,
- ProvideBatchImageWorkerRuntime,
- wire.Bind(new(AccountRuntimeBlocker), new(*OpenAIGatewayService)),
- NewOAuthService,
- ProvideOpenAIOAuthService,
- ProvideGrokOAuthService,
- wire.Bind(new(GrokOAuthTokenService), new(*GrokOAuthService)),
- NewGeminiOAuthService,
- NewGeminiQuotaService,
- NewCompositeTokenCacheInvalidator,
- wire.Bind(new(TokenCacheInvalidator), new(*CompositeTokenCacheInvalidator)),
- NewAntigravityOAuthService,
- ProvideOAuthRefreshAPI,
- ProvideGeminiTokenProvider,
- NewGeminiMessagesCompatService,
- ProvideAntigravityTokenProvider,
- ProvideGrokTokenProvider,
- ProvideOpenAITokenProvider,
- ProvideOpenAIQuotaService,
- ProvideOpenAIQuotaAutoResetService,
- ProvideGrokQuotaService,
- ProvideCNProviderQuotaService,
- ProvideCNProviderBalanceService,
- ProvideCNProviderBalanceCheckService,
- ProvideClaudeTokenProvider,
- NewAntigravityGatewayService,
- ProvideRateLimitService,
- ProvideAccountUsageService,
- ProvideAccountTestService,
- ProvideUpstreamBillingProbeService,
- ProvideOllamaCloudUsageService,
- ProvideSettingService,
- NewDataManagementService,
- ProvideBackupService,
- ProvideOpsSystemLogSink,
- ProvideOpsService,
- ProvideOpsIngressRejectAggregator,
- ProvideAuditLogService,
- ProvideOpsMetricsCollector,
- ProvideOpsAggregationService,
- ProvideOpsAlertEvaluatorService,
- ProvideOpsCleanupService,
- ProvideOpsScheduledReportService,
- NewEmailService,
- NewNotificationEmailService,
- ProvideEmailQueueService,
- NewTurnstileService,
- NewTencentCaptchaService,
- NewAliyunCaptchaService,
- NewSubscriptionService,
- wire.Bind(new(DefaultSubscriptionAssigner), new(*SubscriptionService)),
- ProvideConcurrencyService,
- ProvideUserMessageQueueService,
- NewUsageRecordWorkerPool,
- ProvideSchedulerSnapshotService,
- NewIdentityService,
- NewCRSSyncService,
- ProvideUpdateService,
- ProvideTokenRefreshService,
- wire.Bind(new(GrokOAuthReconciler), new(*TokenRefreshService)),
- ProvideAccountExpiryService,
- ProvideOpenAICodexVersionSyncService,
- ProvideProxyExpiryService,
- ProvideSubscriptionExpiryService,
- ProvideTimingWheelService,
- ProvideDashboardAggregationService,
- ProvideUsageCleanupService,
- ProvideDeferredService,
- NewAntigravityQuotaFetcher,
- NewGrokQuotaFetcher,
- NewUserAttributeService,
- NewUsageCache,
- NewTotpService,
- NewErrorPassthroughService,
- ProvideTLSFingerprintProfileService,
- NewPluginManager,
- NewDigestSessionStore,
- ProvideIdempotencyCoordinator,
- ProvideSystemOperationLockService,
- ProvideIdempotencyCleanupService,
- ProvideScheduledTestService,
- ProvideScheduledTestRunnerService,
- NewGroupCapacityService,
- NewChannelService,
- wire.Bind(new(ChannelCacheInvalidator), new(*ChannelService)),
- NewModelPricingResolverWithGlobal,
- NewModelPlazaService,
- NewContentModerationService,
- wire.Bind(new(SecurityPolicyModelReviewer), new(*ContentModerationService)),
- NewSecurityPolicyService,
- NewGlobalModelPricingService,
- ProvideAccountHealthService,
- ProvideMarginService,
- NewTieredRoutingService,
- ProvideSpendGuardService,
- NewTicketService,
- NewBillingExportService,
- ProvideAntiDegradeService,
- NewAffiliateService,
- ProvidePaymentConfigService,
- ProvidePaymentService,
- ProvidePaymentOrderExpiryService,
- ProvideBalanceNotifyService,
- ProvideChannelMonitorService,
- ProvideChannelMonitorRunner,
- NewChannelMonitorQuotaFetcher,
- ProvideChannelMonitorV2Service,
- ProvideChannelMonitorV2Aggregator,
- NewChannelMonitorRequestTemplateService,
- ProvideUserPlatformQuotaUsageFlusher,
- )
- func ProvideAntiDegradeService(admin AdminService, cfg *config.Config, plugins *PluginManager) *AntiDegradeService {
- svc := NewAntiDegradeService(admin)
- svc.cfg, svc.pluginManager = cfg, plugins
- return svc
- }
- // ProvideUserPlatformQuotaUsageFlusher 创建并启动 UserPlatformQuotaUsageFlusher。
- func ProvideUserPlatformQuotaUsageFlusher(cfg *config.Config, cache BillingCache, quotaRepo UserPlatformQuotaRepository, tw *TimingWheelService) *UserPlatformQuotaUsageFlusher {
- svc := NewUserPlatformQuotaUsageFlusher(cfg, cache, quotaRepo, tw)
- svc.Start()
- return svc
- }
- // ProvidePaymentConfigService wraps NewPaymentConfigService to accept the named
- // payment.EncryptionKey type instead of raw []byte, avoiding Wire ambiguity.
- func ProvidePaymentConfigService(entClient *dbent.Client, settingRepo SettingRepository, key payment.EncryptionKey) *PaymentConfigService {
- return NewPaymentConfigService(entClient, settingRepo, []byte(key))
- }
- // ProvideBalanceNotifyService creates BalanceNotifyService
- func ProvideBalanceNotifyService(emailService *EmailService, settingRepo SettingRepository, accountRepo AccountRepository, notificationEmailService *NotificationEmailService) *BalanceNotifyService {
- svc := NewBalanceNotifyService(emailService, settingRepo, accountRepo)
- svc.SetNotificationEmailService(notificationEmailService)
- return svc
- }
- // ProvidePaymentService creates PaymentService and attaches notification email delivery.
- func ProvidePaymentService(entClient *dbent.Client, registry *payment.Registry, loadBalancer payment.LoadBalancer, redeemService *RedeemService, subscriptionSvc *SubscriptionService, configService *PaymentConfigService, userRepo UserRepository, groupRepo GroupRepository, affiliateService *AffiliateService, notificationEmailService *NotificationEmailService) *PaymentService {
- svc := NewPaymentService(entClient, registry, loadBalancer, redeemService, subscriptionSvc, configService, userRepo, groupRepo, affiliateService)
- svc.SetNotificationEmailService(notificationEmailService)
- return svc
- }
- // ProvidePaymentOrderExpiryService creates and starts PaymentOrderExpiryService.
- func ProvidePaymentOrderExpiryService(paymentSvc *PaymentService, lockCache LeaderLockCache, db *sql.DB) *PaymentOrderExpiryService {
- svc := NewPaymentOrderExpiryService(paymentSvc, 60*time.Second)
- svc.SetLeaderLock(lockCache, db)
- svc.Start()
- return svc
- }
- // ProvideChannelMonitorService 创建渠道监控服务(CRUD + RunCheck + 用户视图聚合)。
- // 加密器复用 wire 中已注入的 SecretEncryptor(AES-256-GCM)。
- // settingService gates RunCheck via channel_monitor_enabled + channel_monitor_mode.
- func ProvideChannelMonitorService(
- repo ChannelMonitorRepository,
- encryptor SecretEncryptor,
- settingService *SettingService,
- ) *ChannelMonitorService {
- svc := NewChannelMonitorService(repo, encryptor)
- svc.SetRuntimeReader(settingService)
- return svc
- }
- // ProvideChannelMonitorRunner 创建并启动渠道监控调度器。
- // 通过 SetScheduler 注入回 service 后再 Start,确保启动时加载所有 enabled monitor,
- // 后续 CRUD 也能即时同步任务表。Runner.Stop 由 cleanup function 调用。
- // settingService 用于 runner 每次 fire 读取功能开关。
- // quotaFetcher(账号侧用量聚合)也在此注入:accountUsage/CN 服务在 wire 图中
- // 晚于 channelMonitorService 构造,走 setter 注入避免调整既有构造顺序。
- func ProvideChannelMonitorRunner(
- svc *ChannelMonitorService,
- settingService *SettingService,
- quotaFetcher *ChannelMonitorQuotaFetcher,
- ) *ChannelMonitorRunner {
- r := NewChannelMonitorRunner(svc, settingService)
- if svc != nil {
- // Ensure runtime reader is set even if ProvideChannelMonitorService
- // was constructed without settings (tests / alternate providers).
- svc.SetRuntimeReader(settingService)
- svc.SetScheduler(r)
- svc.SetQuotaFetcher(quotaFetcher)
- }
- r.Start()
- return r
- }
- // ProvideChannelMonitorV2Service wires settings for user-facing privacy flags
- // (e.g. hide RPM/TPM throughput).
- func ProvideChannelMonitorV2Service(repo ChannelMonitorV2Repository, settingService *SettingService) *ChannelMonitorV2Service {
- svc := NewChannelMonitorV2Service(repo)
- svc.SetRuntimeReader(settingService)
- return svc
- }
- // ProvideChannelMonitorV2Aggregator starts the passive minute-rollup worker.
- // Aggregation only runs when channel_monitor_enabled=true and mode=v2 (and V2 config enabled).
- // Set CHANNEL_MONITOR_V2_DISABLE_AGGREGATOR=1 to skip Start (local demo with seeded facts).
- func ProvideChannelMonitorV2Aggregator(repo ChannelMonitorV2Repository, db *sql.DB, settingService *SettingService) *ChannelMonitorV2Aggregator {
- aggregator := NewChannelMonitorV2Aggregator(repo, db, settingService)
- if os.Getenv("CHANNEL_MONITOR_V2_DISABLE_AGGREGATOR") == "1" {
- return aggregator
- }
- aggregator.Start()
- return aggregator
- }
|