| |
| |
|
|
| package main |
|
|
| import ( |
| "context" |
| "log" |
| "net/http" |
| "sync" |
| "time" |
|
|
| "github.com/Wei-Shaw/sub2api/ent" |
| "github.com/Wei-Shaw/sub2api/internal/config" |
| "github.com/Wei-Shaw/sub2api/internal/handler" |
| "github.com/Wei-Shaw/sub2api/internal/repository" |
| "github.com/Wei-Shaw/sub2api/internal/server" |
| "github.com/Wei-Shaw/sub2api/internal/server/middleware" |
| "github.com/Wei-Shaw/sub2api/internal/service" |
|
|
| "github.com/google/wire" |
| "github.com/redis/go-redis/v9" |
| ) |
|
|
| type Application struct { |
| Server *http.Server |
| Cleanup func() |
| } |
|
|
| func initializeApplication(buildInfo handler.BuildInfo) (*Application, error) { |
| wire.Build( |
| |
| config.ProviderSet, |
|
|
| |
| repository.ProviderSet, |
| service.ProviderSet, |
| middleware.ProviderSet, |
| handler.ProviderSet, |
|
|
| |
| server.ProviderSet, |
|
|
| |
| providePrivacyClientFactory, |
|
|
| |
| provideServiceBuildInfo, |
|
|
| |
| provideCleanup, |
|
|
| |
| wire.Struct(new(Application), "Server", "Cleanup"), |
| ) |
| return nil, nil |
| } |
|
|
| func providePrivacyClientFactory() service.PrivacyClientFactory { |
| return repository.CreatePrivacyReqClient |
| } |
|
|
| func provideServiceBuildInfo(buildInfo handler.BuildInfo) service.BuildInfo { |
| return service.BuildInfo{ |
| Version: buildInfo.Version, |
| BuildType: buildInfo.BuildType, |
| } |
| } |
|
|
| func provideCleanup( |
| entClient *ent.Client, |
| rdb *redis.Client, |
| opsMetricsCollector *service.OpsMetricsCollector, |
| opsAggregation *service.OpsAggregationService, |
| opsAlertEvaluator *service.OpsAlertEvaluatorService, |
| opsCleanup *service.OpsCleanupService, |
| opsScheduledReport *service.OpsScheduledReportService, |
| opsSystemLogSink *service.OpsSystemLogSink, |
| soraMediaCleanup *service.SoraMediaCleanupService, |
| schedulerSnapshot *service.SchedulerSnapshotService, |
| tokenRefresh *service.TokenRefreshService, |
| accountExpiry *service.AccountExpiryService, |
| subscriptionExpiry *service.SubscriptionExpiryService, |
| usageCleanup *service.UsageCleanupService, |
| idempotencyCleanup *service.IdempotencyCleanupService, |
| pricing *service.PricingService, |
| emailQueue *service.EmailQueueService, |
| billingCache *service.BillingCacheService, |
| usageRecordWorkerPool *service.UsageRecordWorkerPool, |
| subscriptionService *service.SubscriptionService, |
| oauth *service.OAuthService, |
| openaiOAuth *service.OpenAIOAuthService, |
| geminiOAuth *service.GeminiOAuthService, |
| antigravityOAuth *service.AntigravityOAuthService, |
| openAIGateway *service.OpenAIGatewayService, |
| scheduledTestRunner *service.ScheduledTestRunnerService, |
| backupSvc *service.BackupService, |
| ) func() { |
| return func() { |
| ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) |
| defer cancel() |
|
|
| type cleanupStep struct { |
| name string |
| fn func() error |
| } |
|
|
| |
| parallelSteps := []cleanupStep{ |
| {"OpsScheduledReportService", func() error { |
| if opsScheduledReport != nil { |
| opsScheduledReport.Stop() |
| } |
| return nil |
| }}, |
| {"OpsCleanupService", func() error { |
| if opsCleanup != nil { |
| opsCleanup.Stop() |
| } |
| return nil |
| }}, |
| {"OpsSystemLogSink", func() error { |
| if opsSystemLogSink != nil { |
| opsSystemLogSink.Stop() |
| } |
| return nil |
| }}, |
| {"SoraMediaCleanupService", func() error { |
| if soraMediaCleanup != nil { |
| soraMediaCleanup.Stop() |
| } |
| return nil |
| }}, |
| {"OpsAlertEvaluatorService", func() error { |
| if opsAlertEvaluator != nil { |
| opsAlertEvaluator.Stop() |
| } |
| return nil |
| }}, |
| {"OpsAggregationService", func() error { |
| if opsAggregation != nil { |
| opsAggregation.Stop() |
| } |
| return nil |
| }}, |
| {"OpsMetricsCollector", func() error { |
| if opsMetricsCollector != nil { |
| opsMetricsCollector.Stop() |
| } |
| return nil |
| }}, |
| {"SchedulerSnapshotService", func() error { |
| if schedulerSnapshot != nil { |
| schedulerSnapshot.Stop() |
| } |
| return nil |
| }}, |
| {"UsageCleanupService", func() error { |
| if usageCleanup != nil { |
| usageCleanup.Stop() |
| } |
| return nil |
| }}, |
| {"IdempotencyCleanupService", func() error { |
| if idempotencyCleanup != nil { |
| idempotencyCleanup.Stop() |
| } |
| return nil |
| }}, |
| {"TokenRefreshService", func() error { |
| tokenRefresh.Stop() |
| return nil |
| }}, |
| {"AccountExpiryService", func() error { |
| accountExpiry.Stop() |
| return nil |
| }}, |
| {"SubscriptionExpiryService", func() error { |
| subscriptionExpiry.Stop() |
| return nil |
| }}, |
| {"SubscriptionService", func() error { |
| if subscriptionService != nil { |
| subscriptionService.Stop() |
| } |
| return nil |
| }}, |
| {"PricingService", func() error { |
| pricing.Stop() |
| return nil |
| }}, |
| {"EmailQueueService", func() error { |
| emailQueue.Stop() |
| return nil |
| }}, |
| {"BillingCacheService", func() error { |
| billingCache.Stop() |
| return nil |
| }}, |
| {"UsageRecordWorkerPool", func() error { |
| if usageRecordWorkerPool != nil { |
| usageRecordWorkerPool.Stop() |
| } |
| return nil |
| }}, |
| {"OAuthService", func() error { |
| oauth.Stop() |
| return nil |
| }}, |
| {"OpenAIOAuthService", func() error { |
| openaiOAuth.Stop() |
| return nil |
| }}, |
| {"GeminiOAuthService", func() error { |
| geminiOAuth.Stop() |
| return nil |
| }}, |
| {"AntigravityOAuthService", func() error { |
| antigravityOAuth.Stop() |
| return nil |
| }}, |
| {"OpenAIWSPool", func() error { |
| if openAIGateway != nil { |
| openAIGateway.CloseOpenAIWSPool() |
| } |
| return nil |
| }}, |
| {"ScheduledTestRunnerService", func() error { |
| if scheduledTestRunner != nil { |
| scheduledTestRunner.Stop() |
| } |
| return nil |
| }}, |
| {"BackupService", func() error { |
| if backupSvc != nil { |
| backupSvc.Stop() |
| } |
| return nil |
| }}, |
| } |
|
|
| infraSteps := []cleanupStep{ |
| {"Redis", func() error { |
| if rdb == nil { |
| return nil |
| } |
| return rdb.Close() |
| }}, |
| {"Ent", func() error { |
| if entClient == nil { |
| return nil |
| } |
| return entClient.Close() |
| }}, |
| } |
|
|
| runParallel := func(steps []cleanupStep) { |
| var wg sync.WaitGroup |
| for i := range steps { |
| step := steps[i] |
| wg.Add(1) |
| go func() { |
| defer wg.Done() |
| if err := step.fn(); err != nil { |
| log.Printf("[Cleanup] %s failed: %v", step.name, err) |
| return |
| } |
| log.Printf("[Cleanup] %s succeeded", step.name) |
| }() |
| } |
| wg.Wait() |
| } |
|
|
| runSequential := func(steps []cleanupStep) { |
| for i := range steps { |
| step := steps[i] |
| if err := step.fn(); err != nil { |
| log.Printf("[Cleanup] %s failed: %v", step.name, err) |
| continue |
| } |
| log.Printf("[Cleanup] %s succeeded", step.name) |
| } |
| } |
|
|
| runParallel(parallelSteps) |
| runSequential(infraSteps) |
|
|
| |
| select { |
| case <-ctx.Done(): |
| log.Printf("[Cleanup] Warning: cleanup timed out after 10 seconds") |
| default: |
| log.Printf("[Cleanup] All cleanup steps completed") |
| } |
| } |
| } |
|
|