package app import ( "context" "fmt" "net/http" "os" "os/signal" "strconv" "sync" "syscall" "time" "git.linuxforward.com/byop/byop-engine/auth" "git.linuxforward.com/byop/byop-engine/config" "git.linuxforward.com/byop/byop-engine/dbmanager" "git.linuxforward.com/byop/byop-engine/dbstore" "git.linuxforward.com/byop/byop-engine/handlers" mw "git.linuxforward.com/byop/byop-engine/middleware" "git.linuxforward.com/byop/byop-engine/services" "github.com/gin-gonic/gin" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) type App struct { entry *logrus.Entry cnf *config.Config ctx context.Context cancelFunc context.CancelFunc rtr *gin.Engine // Database dbManager dbmanager.DbManager // Services authService auth.Service tokenStore auth.TokenStore // Modules authHandler *handlers.AuthHandler userModule *UserModule clientModule *ClientModule componentModule *ComponentModule // formerly appModule blueprintModule *BlueprintModule // formerly templateModule deploymentModule *DeploymentModule // Resource Handlers providerHandler *handlers.ProviderHandler // ticketHandler *handlers.TicketHandler stopped bool wg sync.WaitGroup // monitoringHandler *handlers.MonitoringHandler } type UserModule struct { Store *dbstore.UserStore Service *services.UserService Handler *handlers.UserHandler } type ClientModule struct { Store *dbstore.ClientStore Service *services.ClientService Handler *handlers.ClientHandler } type ComponentModule struct { Store *dbstore.ComponentStore Service *services.ComponentService Handler *handlers.ComponentHandler } type BlueprintModule struct { Store *dbstore.BlueprintStore Service *services.BlueprintService Handler *handlers.BlueprintHandler } type DeploymentModule struct { Store *dbstore.DeploymentStore Service *services.DeploymentService Handler *handlers.DeploymentHandler } func NewApp(cnf *config.Config) (*App, error) { ctx, cancelFunc := context.WithCancel(context.Background()) app := &App{ entry: logrus.WithField("component", "app"), cnf: cnf, ctx: ctx, cancelFunc: cancelFunc, } // Initialize router first if cnf.Debug { gin.SetMode(gin.DebugMode) } else { // Set gin to release mode for production // This will disable debug logs and enable performance optimizations gin.SetMode(gin.ReleaseMode) } app.rtr = gin.New() // Disable automatic redirection of trailing slashes // This prevents 301 redirects that can cause CORS issues app.rtr.RedirectTrailingSlash = false app.rtr.RedirectFixedPath = false app.rtr.Use(gin.Recovery()) app.rtr.Use(mw.Logger) // Add CORS middleware to handle cross-origin requests app.rtr.Use(mw.CORS()) // Initialize services and handlers if err := app.initCommonServices(); err != nil { return nil, errors.Wrap(err, "initialize services") } if err := app.initHandlers(); err != nil { return nil, errors.Wrap(err, "initialize handlers") } // Set up routes after all handlers are initialized app.setupRoutes() return app, nil } func (a *App) Run() error { srv := &http.Server{ Addr: fmt.Sprintf(":%s", strconv.Itoa(a.cnf.Server.Port)), Handler: a.rtr, } go func() { a.entry.WithField("address", srv.Addr).Info("Starting server on port " + strconv.Itoa(a.cnf.Server.Port)) // Handle TLS if configured if a.cnf.Server.Tls.Enabled { a.entry.Info("Starting server with TLS...") err := srv.ListenAndServeTLS(a.cnf.Server.Tls.CertFile, a.cnf.Server.Tls.KeyFile) if err != nil && err != http.ErrServerClosed { a.entry.WithError(err).Fatal("Failed to start server") } } else { err := srv.ListenAndServe() if err != nil && err != http.ErrServerClosed { a.entry.WithError(err).Fatal("Failed to start server") } } a.entry.Info("Server stopped") }() quit := make(chan os.Signal, 1) signal.Notify(quit, os.Interrupt, syscall.SIGTERM) <-quit a.entry.Info("Stopping server...") ctxTimeout, cancelFunc := context.WithTimeout(context.Background(), 30*time.Second) defer cancelFunc() err := srv.Shutdown(ctxTimeout) if err != nil { return fmt.Errorf("shutdown server: %w", err) } a.entry.Info("Server stopped successfully") return nil }