package app import ( "context" "go.uber.org/zap" "codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/config" "codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/internal/interface/http" "codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/internal/scheduler" "codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/pkg/leaderelection" ) // Application represents the main application type Application struct { cfg *config.Config logger *zap.Logger server *http.Server leaderElection leaderelection.LeaderElection quotaScheduler *scheduler.QuotaResetScheduler ipCleanupScheduler *scheduler.IPCleanupScheduler } // ProvideApplication creates a new Application func ProvideApplication( cfg *config.Config, logger *zap.Logger, server *http.Server, leaderElection leaderelection.LeaderElection, quotaScheduler *scheduler.QuotaResetScheduler, ipCleanupScheduler *scheduler.IPCleanupScheduler, ) *Application { return &Application{ cfg: cfg, logger: logger, server: server, leaderElection: leaderElection, quotaScheduler: quotaScheduler, ipCleanupScheduler: ipCleanupScheduler, } } // Run starts the application func (a *Application) Run(ctx context.Context) error { a.logger.Info("") a.logger.Info("╔═══════════════════════════════════════════════╗") a.logger.Info("║ MaplePress Backend Starting... ║") a.logger.Info("╚═══════════════════════════════════════════════╝") a.logger.Info("", zap.String("environment", a.cfg.App.Environment), zap.String("version", a.cfg.App.Version)) a.logger.Info("") // Start leader election in background if enabled if a.cfg.LeaderElection.Enabled { go func() { a.logger.Info("starting leader election") if err := a.leaderElection.Start(ctx); err != nil { a.logger.Error("leader election stopped", zap.Error(err)) } }() } else { a.logger.Warn("leader election is DISABLED - all schedulers will run on every instance") } // Start quota reset scheduler if err := a.quotaScheduler.Start(); err != nil { a.logger.Error("failed to start quota scheduler", zap.Error(err)) return err } // Start IP cleanup scheduler for GDPR compliance if err := a.ipCleanupScheduler.Start(); err != nil { a.logger.Error("failed to start IP cleanup scheduler", zap.Error(err)) return err } return a.server.Start() } // Shutdown gracefully shuts down the application func (a *Application) Shutdown(ctx context.Context) error { a.logger.Info("shutting down MaplePress Backend") // Stop leader election first if enabled if a.cfg.LeaderElection.Enabled { a.logger.Info("stopping leader election") if err := a.leaderElection.Stop(); err != nil { a.logger.Error("failed to stop leader election", zap.Error(err)) } } // Stop quota scheduler a.quotaScheduler.Stop() // Stop IP cleanup scheduler a.ipCleanupScheduler.Stop() if err := a.server.Shutdown(ctx); err != nil { a.logger.Error("failed to shutdown server", zap.Error(err)) return err } // Sync logger before exit a.logger.Sync() return nil }