No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.
 
 
 
 

146 líneas
4.2 KiB

  1. package server
  2. import (
  3. "context"
  4. "encoding/json"
  5. "fmt"
  6. "io"
  7. "log/slog"
  8. "net/http"
  9. "os"
  10. "sync"
  11. "github.com/AFASystems/presence/internal/pkg/apiclient"
  12. "github.com/AFASystems/presence/internal/pkg/common/appcontext"
  13. "github.com/AFASystems/presence/internal/pkg/config"
  14. "github.com/AFASystems/presence/internal/pkg/database"
  15. "github.com/AFASystems/presence/internal/pkg/kafkaclient"
  16. "github.com/AFASystems/presence/internal/pkg/logger"
  17. "github.com/AFASystems/presence/internal/pkg/model"
  18. "github.com/AFASystems/presence/internal/pkg/service"
  19. "gorm.io/gorm"
  20. )
  21. // ServerApp holds dependencies and state for the server service.
  22. type ServerApp struct {
  23. Cfg *config.Config
  24. DB *gorm.DB
  25. KafkaManager *kafkaclient.KafkaManager
  26. AppState *appcontext.AppState
  27. ChLoc chan model.HTTPLocation
  28. ChEvents chan model.BeaconEvent
  29. ctx context.Context
  30. Server *http.Server
  31. Cleanup func()
  32. wg sync.WaitGroup
  33. }
  34. // New creates a ServerApp: loads config, creates logger, connects DB, creates Kafka manager and writers.
  35. // Caller must call Init(ctx) then Run(ctx) then Shutdown().
  36. func New(cfg *config.Config) (*ServerApp, error) {
  37. srvLogger, cleanup := logger.CreateLogger("server.log")
  38. slog.SetDefault(srvLogger)
  39. db, err := database.Connect(cfg)
  40. if err != nil {
  41. cleanup()
  42. return nil, fmt.Errorf("database: %w", err)
  43. }
  44. appState := appcontext.NewAppState()
  45. kafkaManager := kafkaclient.InitKafkaManager()
  46. writerTopics := []string{"apibeacons", "alert", "mqtt", "settings", "parser"}
  47. kafkaManager.PopulateKafkaManager(cfg.KafkaURL, "", writerTopics)
  48. slog.Info("Kafka writers initialized", "topics", writerTopics)
  49. return &ServerApp{
  50. Cfg: cfg,
  51. DB: db,
  52. KafkaManager: kafkaManager,
  53. AppState: appState,
  54. Cleanup: cleanup,
  55. }, nil
  56. }
  57. // Init loads config from file, seeds DB, runs UpdateDB, adds Kafka readers and starts consumers.
  58. func (a *ServerApp) Init(ctx context.Context) error {
  59. a.ctx = ctx
  60. configFile, err := os.Open(a.Cfg.ConfigPath)
  61. if err != nil {
  62. return fmt.Errorf("config file: %w", err)
  63. }
  64. defer configFile.Close()
  65. b, err := io.ReadAll(configFile)
  66. if err != nil {
  67. return fmt.Errorf("read config: %w", err)
  68. }
  69. var configs []model.Config
  70. if err := json.Unmarshal(b, &configs); err != nil {
  71. return fmt.Errorf("unmarshal config: %w", err)
  72. }
  73. for _, c := range configs {
  74. a.DB.Create(&c)
  75. }
  76. a.DB.Find(&configs)
  77. for _, c := range configs {
  78. kp := model.KafkaParser{ID: "add", Config: c}
  79. if err := service.SendParserConfig(kp, a.KafkaManager.GetWriter("parser"), ctx); err != nil {
  80. slog.Error("sending parser config to kafka", "err", err, "name", c.Name)
  81. }
  82. }
  83. if err := apiclient.UpdateDB(a.DB, ctx, a.Cfg, a.KafkaManager.GetWriter("apibeacons"), a.AppState); err != nil {
  84. slog.Error("UpdateDB", "err", err)
  85. }
  86. readerTopics := []string{"locevents", "alertbeacons"}
  87. a.KafkaManager.PopulateKafkaManager(a.Cfg.KafkaURL, "server", readerTopics)
  88. slog.Info("Kafka readers initialized", "topics", readerTopics)
  89. a.ChLoc = make(chan model.HTTPLocation, config.SMALL_CHANNEL_SIZE)
  90. a.ChEvents = make(chan model.BeaconEvent, config.MEDIUM_CHANNEL_SIZE)
  91. a.wg.Add(2)
  92. go kafkaclient.Consume(a.KafkaManager.GetReader("locevents"), a.ChLoc, ctx, &a.wg)
  93. go kafkaclient.Consume(a.KafkaManager.GetReader("alertbeacons"), a.ChEvents, ctx, &a.wg)
  94. a.Server = &http.Server{
  95. Addr: a.Cfg.HTTPAddr,
  96. Handler: a.RegisterRoutes(),
  97. }
  98. return nil
  99. }
  100. // Run starts the HTTP server and runs the event loop until ctx is cancelled.
  101. func (a *ServerApp) Run(ctx context.Context) {
  102. go func() {
  103. if err := a.Server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
  104. slog.Error("HTTP server", "err", err)
  105. }
  106. }()
  107. RunEventLoop(ctx, a)
  108. }
  109. // Shutdown stops the HTTP server, waits for consumers, and cleans up Kafka and logger.
  110. func (a *ServerApp) Shutdown() {
  111. if a.Server != nil {
  112. if err := a.Server.Shutdown(context.Background()); err != nil {
  113. slog.Error("server shutdown", "err", err)
  114. }
  115. slog.Info("HTTP server stopped")
  116. }
  117. a.wg.Wait()
  118. slog.Info("Kafka consumers stopped")
  119. a.KafkaManager.CleanKafkaReaders()
  120. a.KafkaManager.CleanKafkaWriters()
  121. if a.Cleanup != nil {
  122. a.Cleanup()
  123. }
  124. slog.Info("server shutdown complete")
  125. }