package app import ( "context" "fmt" "net/http" "os" "os/signal" "strconv" "syscall" "time" "git.linuxforward.com/byop/byop-engine/auth" "git.linuxforward.com/byop/byop-engine/cloud" "git.linuxforward.com/byop/byop-engine/config" "git.linuxforward.com/byop/byop-engine/dbmanager" "git.linuxforward.com/byop/byop-engine/handlers" mw "git.linuxforward.com/byop/byop-engine/middleware" "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[dbmanager.Entity] dbFactory *dbmanager.DbManagerFactory // Services authService auth.Service tokenStore auth.TokenStore // Common Handlers authHandler *handlers.AuthHandler clientHandler *handlers.ClientHandler // Resource Handlers providerHandler *handlers.ProviderHandler deploymentHandler *handlers.DeploymentHandler templateHandler *handlers.TemplateHandler ticketHandler *handlers.TicketHandler monitoringHandler *handlers.MonitoringHandler } 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() app.rtr.Use(gin.Recovery()) app.rtr.Use(mw.Logger) // Initialize database connection if err := app.initDatabase(); err != nil { return nil, errors.Wrap(err, "initialize database") } // Initialize services and handlers if err := app.initServices(); 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...") // 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 } func (a *App) initServices() error { // Initialize token store a.tokenStore = auth.NewMemoryTokenStore(time.Duration(a.cnf.Auth.CleanupInterval)) // Initialize authentication service a.authService = auth.NewJWTService( []byte(a.cnf.Auth.PrivateKey), time.Duration(a.cnf.Auth.TokenDuration), a.tokenStore, ) // Initialize providers if err := a.loadProviders(); err != nil { return errors.Wrap(err, "load providers") } a.entry.Info("Services initialized successfully") return nil } func (a *App) initHandlers() error { // Initialize authentication handler a.authHandler = handlers.NewAuthHandler(a.authService) // Initialize resource handlers a.providerHandler = handlers.NewProviderHandler() // Create managers for each entity type clientDbManager, err := a.dbFactory.CreateClientManager() if err != nil { return fmt.Errorf("create client db manager: %w", err) } if err := clientDbManager.Connect(); err != nil { return fmt.Errorf("connect to client database: %w", err) } a.clientHandler = handlers.NewClientHandler(clientDbManager) // Initialize other handlers... a.entry.Info("Handlers initialized successfully") return nil } func (a *App) loadProviders() error { for name, config := range a.cnf.Providers { provider, ok := cloud.GetProvider(name) if !ok { return fmt.Errorf("provider %s not found", name) } err := provider.Initialize(config) if err != nil { return fmt.Errorf("initialize provider %s: %w", name, err) } a.entry.WithField("provider", name).Info("Provider initialized") } a.entry.Info("All providers loaded successfully") return nil } func (a *App) setupRoutes() { // API version group v1 := a.rtr.Group("/api/v1") // Auth routes - no middleware required a.authHandler.RegisterRoutes(v1) // Protected routes - require authentication protected := v1.Group("/") protected.Use(mw.Auth(a.authService)) // Auth middleware with service dependency // Register resource routes providers := protected.Group("/providers") a.providerHandler.RegisterRoutes(providers) clients := protected.Group("/clients") a.clientHandler.RegisterRoutes(clients) // Register other resource routes... a.entry.Info("Routes configured successfully") } func (a *App) initDatabase() error { a.dbFactory = dbmanager.NewDbManagerFactory(a.cnf.Database.Type) a.entry.Info("Database factory initialized successfully") return nil }