package httpserver import ( dbgen "Yimaru-Backend/gen/db" "Yimaru-Backend/internal/config" activitylogservice "Yimaru-Backend/internal/services/activity_log" "Yimaru-Backend/internal/services/arifpay" "Yimaru-Backend/internal/services/assessment" "Yimaru-Backend/internal/services/authentication" cloudconvertservice "Yimaru-Backend/internal/services/cloudconvert" "Yimaru-Backend/internal/services/course_management" minioservice "Yimaru-Backend/internal/services/minio" issuereporting "Yimaru-Backend/internal/services/issue_reporting" notificationservice "Yimaru-Backend/internal/services/notification" "Yimaru-Backend/internal/services/questions" ratingsservice "Yimaru-Backend/internal/services/ratings" rbacservice "Yimaru-Backend/internal/services/rbac" "Yimaru-Backend/internal/services/recommendation" "Yimaru-Backend/internal/services/subscriptions" "Yimaru-Backend/internal/services/team" vimeoservice "Yimaru-Backend/internal/services/vimeo" "Yimaru-Backend/internal/services/settings" "Yimaru-Backend/internal/services/transaction" "Yimaru-Backend/internal/services/user" jwtutil "Yimaru-Backend/internal/web_server/jwt" customvalidator "Yimaru-Backend/internal/web_server/validator" "context" "fmt" "log/slog" "time" "go.uber.org/zap" "github.com/bytedance/sonic" "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/middleware/cors" ) type App struct { assessmentSvc *assessment.Service courseSvc *course_management.Service questionsSvc *questions.Service subscriptionsSvc *subscriptions.Service arifpaySvc *arifpay.ArifpayService issueReportingSvc *issuereporting.Service vimeoSvc *vimeoservice.Service teamSvc *team.Service activityLogSvc *activitylogservice.Service cloudConvertSvc *cloudconvertservice.Service minioSvc *minioservice.Service ratingSvc *ratingsservice.Service fiber *fiber.App recommendationSvc recommendation.RecommendationService cfg *config.Config logger *slog.Logger NotidicationStore *notificationservice.Service port int settingSvc *settings.Service authSvc *authentication.Service userSvc *user.Service transactionSvc *transaction.Service validator *customvalidator.CustomValidator JwtConfig jwtutil.JwtConfig Logger *slog.Logger mongoLoggerSvc *zap.Logger analyticsDB *dbgen.Queries rbacSvc *rbacservice.Service stopPurgeWorker context.CancelFunc } func NewApp( assessmentSvc *assessment.Service, courseSvc *course_management.Service, questionsSvc *questions.Service, subscriptionsSvc *subscriptions.Service, arifpaySvc *arifpay.ArifpayService, issueReportingSvc *issuereporting.Service, vimeoSvc *vimeoservice.Service, teamSvc *team.Service, activityLogSvc *activitylogservice.Service, cloudConvertSvc *cloudconvertservice.Service, minioSvc *minioservice.Service, ratingSvc *ratingsservice.Service, port int, validator *customvalidator.CustomValidator, settingSvc *settings.Service, authSvc *authentication.Service, logger *slog.Logger, JwtConfig jwtutil.JwtConfig, userSvc *user.Service, transactionSvc *transaction.Service, notidicationStore *notificationservice.Service, recommendationSvc recommendation.RecommendationService, cfg *config.Config, mongoLoggerSvc *zap.Logger, analyticsDB *dbgen.Queries, rbacSvc *rbacservice.Service, ) *App { app := fiber.New(fiber.Config{ CaseSensitive: true, DisableHeaderNormalizing: true, JSONEncoder: sonic.Marshal, JSONDecoder: sonic.Unmarshal, BodyLimit: 500 * 1024 * 1024, // 500 MB }) app.Use(cors.New(cors.Config{ AllowOrigins: "*", AllowMethods: "GET,POST,PUT,PATCH,DELETE,OPTIONS", AllowHeaders: "Content-Type,Authorization,platform", // AllowCredentials: true, })) app.Static("/static", "./static") s := &App{ assessmentSvc: assessmentSvc, courseSvc: courseSvc, questionsSvc: questionsSvc, subscriptionsSvc: subscriptionsSvc, arifpaySvc: arifpaySvc, vimeoSvc: vimeoSvc, teamSvc: teamSvc, activityLogSvc: activityLogSvc, cloudConvertSvc: cloudConvertSvc, minioSvc: minioSvc, ratingSvc: ratingSvc, issueReportingSvc: issueReportingSvc, fiber: app, port: port, settingSvc: settingSvc, authSvc: authSvc, validator: validator, logger: logger, JwtConfig: JwtConfig, userSvc: userSvc, transactionSvc: transactionSvc, NotidicationStore: notidicationStore, Logger: logger, recommendationSvc: recommendationSvc, cfg: cfg, mongoLoggerSvc: mongoLoggerSvc, analyticsDB: analyticsDB, rbacSvc: rbacSvc, } s.initAppRoutes() return s } func (a *App) Run() error { a.startAccountDeletionPurgeWorker() defer a.stopAccountDeletionPurgeWorker() return a.fiber.Listen(fmt.Sprintf(":%d", a.port)) } func (a *App) startAccountDeletionPurgeWorker() { if a.cfg == nil || !a.cfg.AccountDeletionPurgeEnabled { a.logger.Info("account deletion purge worker disabled") return } interval := a.cfg.AccountDeletionPurgeInterval if interval <= 0 { interval = time.Hour } batchSize := a.cfg.AccountDeletionPurgeBatchSize if batchSize <= 0 { batchSize = 100 } ctx, cancel := context.WithCancel(context.Background()) a.stopPurgeWorker = cancel a.logger.Info( "starting account deletion purge worker", "interval", interval.String(), "batch_size", batchSize, ) go func() { // Run once on startup so stale due rows are cleaned quickly. a.runAccountDeletionPurgeOnce(ctx, batchSize) ticker := time.NewTicker(interval) defer ticker.Stop() for { select { case <-ctx.Done(): a.logger.Info("account deletion purge worker stopped") return case <-ticker.C: a.runAccountDeletionPurgeOnce(ctx, batchSize) } } }() } func (a *App) stopAccountDeletionPurgeWorker() { if a.stopPurgeWorker != nil { a.stopPurgeWorker() } } func (a *App) runAccountDeletionPurgeOnce(ctx context.Context, batchSize int32) { deletedCount, err := a.userSvc.PurgeDueUserDeletions(ctx, batchSize) if err != nil { a.logger.Error("account deletion purge run failed", "error", err) return } if deletedCount > 0 { a.logger.Info("account deletion purge run completed", "deleted_count", deletedCount, "batch_size", batchSize) } }