package server import ( "bytes" "context" "encoding/json" "os" "os/signal" "syscall" "time" "github.com/go-redis/redis/v8" "github.com/gofiber/fiber/v2" "github.com/jackc/pgx/v5/pgxpool" "github.com/streadway/amqp" def "git.pbiernat.dev/egommerce/api-entities/http" discovery "git.pbiernat.dev/egommerce/go-api-pkg/consul" "git.pbiernat.dev/egommerce/go-api-pkg/fluentd" ) type Server struct { *fiber.App conf *Config log *fluentd.Logger db *pgxpool.Pool cache *redis.Client ebCh *amqp.Channel discovery *discovery.Service name string addr string kvNmspc string } type Headers struct { RequestID string `reqHeader:"x-request-id"` } func NewServer(conf *Config, logger *fluentd.Logger, db *pgxpool.Pool, cache *redis.Client, ebCh *amqp.Channel) *Server { consul, err := discovery.NewService(conf.RegistryAddr, conf.AppID, conf.AppName, conf.AppID, conf.AppDomain, conf.PathPrefix, conf.Port) if err != nil { logger.Log("Error connecting to %s: %v", conf.RegistryAddr, err) } logger.Log("Registering service with name: %s, address: %s", consul.Name, consul.Address) err = consul.Register() if err != nil { logger.Log("register error: %v", err) } cnf := fiber.Config{ AppName: conf.AppName, ServerHeader: conf.AppName, ReadTimeout: time.Millisecond * 50, WriteTimeout: time.Millisecond * 50, IdleTimeout: time.Millisecond * 50, } s := &Server{ fiber.New(cnf), conf, logger, db, cache, ebCh, consul, conf.AppName, conf.NetAddr, conf.KVNamespace, } go func(s *Server) { // Consul KV updater interval := time.Second * 15 ticker := time.NewTicker(interval) for range ticker.C { s.updateKVConfig() } }(s) go func(s *Server) { // Server metadata cache updater ticker := time.NewTicker(time.Second * 5) for range ticker.C { s.cacheMetadata() } }(s) SetupMiddlewares(s) SetupRoutes(s) return s } // func (s *Server) Start() { // err := s.Listen(s.addr) // s.log.Log("Starting error: %v", err) // } func (s *Server) StartWithGracefulShutdown(forever chan struct{}) { go func() { sigint := make(chan os.Signal, 1) signal.Notify(sigint, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) <-sigint if err := s.gracefulShutdown(); err != nil { s.log.Log("Server is not shutting down! Reason: %v", err) } close(forever) }() if err := s.Listen(s.addr); err != nil { s.log.Log("Server is not running! Reason: %v", err) } <-forever } func (s *Server) GetRequestID(c *fiber.Ctx) (string, error) { var hdr = new(Headers) if err := c.ReqHeaderParser(hdr); err != nil { return "", err } return hdr.RequestID, nil } func (s *Server) Error400(c *fiber.Ctx, msg string) error { return c.Status(fiber.StatusBadRequest).JSON(&def.ErrorResponse{Error: msg}) } func (s *Server) Error404(c *fiber.Ctx, msg string) error { return c.Status(fiber.StatusNotFound).JSON(&def.ErrorResponse{Error: msg}) } func (s *Server) updateKVConfig() { // FIXME: duplicated in cmd/worker/main.go config, _, err := s.discovery.KV().Get(s.kvNmspc, nil) if err != nil || config == nil { return } kvCnf := bytes.NewBuffer(config.Value) decoder := json.NewDecoder(kvCnf) if err := decoder.Decode(&s.conf); err != nil { return } } func (s *Server) cacheMetadata() { ctx := context.Background() key, address := s.getMetadataIPsKey(), s.conf.AppID pos := s.cache.LPos(ctx, key, address, redis.LPosArgs{}).Val() if pos >= 0 { s.cache.LRem(ctx, key, 0, address) } s.cache.LPush(ctx, key, address).Err() } func (s *Server) clearMetadataCache() { ctx := context.Background() key, address := s.getMetadataIPsKey(), s.conf.AppID s.cache.LRem(ctx, key, 0, address) } func (s *Server) getMetadataIPsKey() string { return "internal__" + s.conf.AppName + "__ips" } func (s *Server) gracefulShutdown() error { s.log.Log("Server is going down... Unregistering service: %s", s.discovery.GetID()) s.discovery.Unregister() s.clearMetadataCache() s.ebCh.Close() s.db.Close() s.log.Close() return s.Shutdown() }