package app import ( "context" "errors" "gitea.pena/PenaSide/heruvym/internal/initialize" "gitea.pena/PenaSide/heruvym/internal/server/http" "gitea.pena/PenaSide/heruvym/internal/utils/middleware" "gitea.pena/PenaSide/heruvym/pkg/closer" "gitea.pena/PenaSide/hlog" "go.uber.org/zap" "strconv" "time" ) var zapOptions = []zap.Option{ zap.AddCaller(), zap.AddCallerSkip(2), zap.AddStacktrace(zap.ErrorLevel), } type Build struct { Commit string Version string BuildTime int64 } type InfoSvcStarted struct{} func Run(ctx context.Context, cfg initialize.Config, build Build) error { var logger *zap.Logger var err error if cfg.IsProd { logger, err = zap.NewProduction(zapOptions...) if err != nil { return err } } else { logger, err = zap.NewDevelopment(zapOptions...) if err != nil { return err } } logger = logger.With( zap.String("SvcCommit", build.Commit), zap.String("SvcVersion", build.Version), zap.String("SvcBuildTime", strconv.FormatInt(build.BuildTime, 10)), ) hlogger := hlog.New(logger) hlogger.Emit(InfoSvcStarted{}) defer func() { if r := recover(); r != nil { logger.Error("Recovered from a panic", zap.Any("error", r)) } }() ctx, cancel := context.WithCancel(ctx) defer cancel() shutdownGroup := closer.NewCloserGroup() mw := middleware.NewMiddleware( hlogger, "*", nil, ) mdb, err := initialize.MongoDB(ctx, cfg) if err != nil { logger.Error("Error initializing MongoDB", zap.Error(err)) return err } minioClient, err := initialize.Minio(cfg) if err != nil { logger.Error("Error initializing Minio", zap.Error(err)) return err } redisClient, err := initialize.Redis(ctx, cfg) if err != nil { logger.Error("Error initializing Redis", zap.Error(err)) return err } tgBot, err := initialize.NewTgBot(cfg) if err != nil { logger.Error("Error initializing Telegram", zap.Error(err)) return err } repositories, err := initialize.NewRepositories(ctx, initialize.DepsRepositories{ MinioClient: minioClient, MongoDatabase: mdb, HLogger: hlogger, Cfg: cfg, }) if err != nil { logger.Error("Error initializing Repositories", zap.Error(err)) return err } controllers := initialize.NewControllers(initialize.ControllersDeps{ Reps: repositories, RedisClient: redisClient, Notifier: tgBot, TgChatID: cfg.TelegramChannelID, HLogger: hlogger, ZapLogger: logger, }) srv := http.NewServer(http.ServerConfig{ Logger: logger, Controllers: []http.Controller{controllers.Tickets, controllers.Other}, Hlogger: hlogger, MW: mw, }) go func() { if err := srv.Start(cfg.HTTPUrl); err != nil { logger.Error("Server startup error", zap.Error(err)) cancel() } }() srv.ListRoutes() shutdownGroup.Add(closer.CloserFunc(srv.Shutdown)) shutdownGroup.Add(closer.CloserFunc(mdb.Client().Disconnect)) <-ctx.Done() timeoutCtx, timeoutCancel := context.WithTimeout(context.Background(), 10*time.Second) defer timeoutCancel() if err := shutdownGroup.Call(timeoutCtx); err != nil { if errors.Is(err, context.DeadlineExceeded) { logger.Error("Shutdown timed out", zap.Error(err)) } else { logger.Error("Failed to shutdown services gracefully", zap.Error(err)) } return err } logger.Info("Application has stopped") return nil }