app.go 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. package app
  2. import (
  3. "context"
  4. "fmt"
  5. "net/http"
  6. "os"
  7. "os/signal"
  8. "syscall"
  9. "time"
  10. "git.linuxforward.com/byom/byom-core-gateway/config"
  11. "git.linuxforward.com/byom/byom-core-gateway/handlers"
  12. "git.linuxforward.com/byom/byom-core-gateway/logger"
  13. "git.linuxforward.com/byom/byom-core-gateway/middleware"
  14. "git.linuxforward.com/byom/byom-core-gateway/store"
  15. "gorm.io/driver/sqlite"
  16. "gorm.io/gorm"
  17. "github.com/gin-contrib/cors"
  18. "github.com/gin-gonic/gin"
  19. "github.com/sirupsen/logrus"
  20. )
  21. type App struct {
  22. entry *logrus.Entry
  23. cnf *config.Config
  24. ctx context.Context
  25. cancel context.CancelFunc
  26. dbHandler *store.DataStore
  27. userHandler *handlers.UserHandler
  28. router *gin.Engine
  29. }
  30. func NewApp(cnf *config.Config) (*App, error) {
  31. ctx, cancel := context.WithCancel(context.Background())
  32. // Create logger
  33. entry := logger.NewLogger("app")
  34. // Create app instance
  35. app := &App{
  36. entry: entry,
  37. cnf: cnf,
  38. ctx: ctx,
  39. cancel: cancel,
  40. }
  41. // Initialize services
  42. if err := app.initServices(); err != nil {
  43. cancel()
  44. return nil, fmt.Errorf("failed to initialize services: %w", err)
  45. }
  46. // Initialize router and middleware
  47. if err := app.initRouter(); err != nil {
  48. cancel()
  49. return nil, fmt.Errorf("init router: %w", err)
  50. }
  51. return app, nil
  52. }
  53. func (a *App) initRouter() error {
  54. rtr := gin.Default()
  55. coreRtr := rtr.Group("/api/v1/gw")
  56. // Add core middleware
  57. coreRtr.Use(middleware.Recovery(a.entry.Logger))
  58. coreRtr.Use(middleware.RequestID())
  59. coreRtr.Use(middleware.RequestLogger(a.entry.Logger))
  60. coreRtr.Use(middleware.ErrorHandler())
  61. // Add security middleware
  62. coreRtr.Use(middleware.SecurityHeaders())
  63. coreRtr.Use(middleware.RequestSanitizer())
  64. // Configure request timeout
  65. timeout := 30 * time.Second
  66. coreRtr.Use(middleware.TimeoutMiddleware(timeout))
  67. //Configure CORS
  68. coreRtr.Use(cors.New(cors.Config{
  69. AllowOrigins: []string{"http://172.27.28.86:5173"},
  70. AllowMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"},
  71. AllowHeaders: []string{"Origin", "Content-Type", "Accept", "Authorization", "X-Request-ID"},
  72. ExposeHeaders: []string{"Content-Length", "Content-Type", "X-Request-ID", "Location"},
  73. AllowCredentials: true,
  74. }))
  75. // Add health check endpoint
  76. coreRtr.GET("/health", func(c *gin.Context) {
  77. c.JSON(200, gin.H{"status": "ok"})
  78. })
  79. // Add user routes
  80. // userGroup := rtr.Group("/user", gin.BasicAuth(gin.Accounts{
  81. // a.cnf.BasicAuth.Username: a.cnf.BasicAuth.Password,
  82. // }))
  83. userGroup := coreRtr.Group("/user")
  84. {
  85. userGroup.GET("/host", a.userHandler.GetUserHost)
  86. userGroup.POST("/host", a.userHandler.AddUserHost)
  87. userGroup.DELETE("/host", a.userHandler.DeleteUserHost)
  88. userGroup.PUT("/host", a.userHandler.UpdateUserHost)
  89. }
  90. a.router = rtr
  91. return nil
  92. }
  93. func (a *App) initServices() error {
  94. // Initialize database
  95. dbConn, err := initDBConn(&a.cnf.Database)
  96. if err != nil {
  97. return fmt.Errorf("init database: %w", err)
  98. }
  99. dbHandler := store.NewDataStore(dbConn)
  100. a.dbHandler = dbHandler
  101. // Initialize user handler
  102. userHandler := handlers.NewUserHandler(dbHandler, a.cnf.Homepage)
  103. a.userHandler = userHandler
  104. return nil
  105. }
  106. func initDBConn(cnf *config.DatabaseConfig) (*gorm.DB, error) {
  107. db, err := gorm.Open(sqlite.Open(cnf.File), &gorm.Config{})
  108. if err != nil {
  109. return nil, fmt.Errorf("database: %w", err)
  110. }
  111. //Tune the DB
  112. sqlDB, err := db.DB()
  113. if err != nil {
  114. return nil, fmt.Errorf("database: %w", err)
  115. }
  116. sqlDB.SetMaxOpenConns(cnf.MaxOpenConns)
  117. sqlDB.SetMaxIdleConns(cnf.MaxIdleConns)
  118. sqlDB.SetConnMaxLifetime(time.Duration(cnf.ConnMaxLifetime) * time.Second)
  119. for _, pragma := range cnf.Pragma {
  120. tx := db.Exec("PRAGMA " + pragma)
  121. if tx.Error != nil {
  122. return nil, fmt.Errorf("database: %w", tx.Error)
  123. }
  124. }
  125. return db, nil
  126. }
  127. func (a *App) Run() error {
  128. a.entry.Info("Starting server...")
  129. // Configure server with timeouts
  130. srv := &http.Server{
  131. Addr: ":" + a.cnf.Server.ListeningPort,
  132. Handler: a.router,
  133. ReadTimeout: 15 * time.Second,
  134. WriteTimeout: 15 * time.Second,
  135. IdleTimeout: 60 * time.Second,
  136. }
  137. // Start server
  138. go func() {
  139. var err error
  140. if a.cnf.Server.TlsConfig.Enabled {
  141. a.entry.Infof("Starting server on port %s with TLS", a.cnf.Server.ListeningPort)
  142. err = srv.ListenAndServeTLS(
  143. a.cnf.Server.TlsConfig.CertFile,
  144. a.cnf.Server.TlsConfig.KeyFile,
  145. )
  146. } else {
  147. a.entry.Infof("Starting server on port %s without TLS", a.cnf.Server.ListeningPort)
  148. err = srv.ListenAndServe()
  149. }
  150. if err != nil && err != http.ErrServerClosed {
  151. a.entry.WithError(err).Error("Server error")
  152. // Signal shutdown on critical error
  153. a.cancel()
  154. }
  155. }()
  156. // Graceful shutdown
  157. quit := make(chan os.Signal, 1)
  158. signal.Notify(quit, os.Interrupt, syscall.SIGTERM)
  159. select {
  160. case <-quit:
  161. a.entry.Info("Shutdown signal received...")
  162. case <-a.ctx.Done():
  163. a.entry.Info("Server error occurred, initiating shutdown...")
  164. }
  165. // Create shutdown context with timeout
  166. ctxShutdown, cancel := context.WithTimeout(context.Background(), 30*time.Second)
  167. defer cancel()
  168. // Shutdown server
  169. if err := srv.Shutdown(ctxShutdown); err != nil {
  170. a.entry.WithError(err).Error("Server forced to shutdown")
  171. return fmt.Errorf("server forced to shutdown: %w", err)
  172. }
  173. // Close all services
  174. if err := a.Close(); err != nil {
  175. a.entry.WithError(err).Error("Error during service cleanup")
  176. return fmt.Errorf("service cleanup error: %w", err)
  177. }
  178. a.entry.Info("Server stopped gracefully")
  179. return nil
  180. }
  181. func (a *App) Close() error {
  182. if err := a.dbHandler.Close(); err != nil {
  183. return err
  184. }
  185. return nil
  186. }