package server import ( "context" "fmt" "github.com/jackc/pgx/v5/pgxpool" "time" fiberzap "github.com/gofiber/contrib/fiberzap/v2" "github.com/gofiber/contrib/swagger" "github.com/gofiber/fiber/v2" middleware "github.com/oapi-codegen/fiber-middleware" "go.uber.org/zap" ) type Server struct { port int logger *zap.Logger apiHandlers *APIHandlers pool *pgxpool.Pool } func NewServer(apiHandlers *APIHandlers, port int, logger *zap.Logger, pool *pgxpool.Pool) *Server { return &Server{ apiHandlers: apiHandlers, port: port, logger: logger, pool: pool, } } func (s *Server) Run(ctx context.Context) { middlewares := make([]fiber.Handler, 0) loggingMiddleware := fiberzap.New(fiberzap.Config{ Logger: s.logger, }) middlewares = append(middlewares, loggingMiddleware) app := setupServer(s.apiHandlers, middlewares, s.logger, false) go func() { fiberErr := app.Listen(fmt.Sprintf(":%d", s.port)) if fiberErr != nil { panic(fiberErr) } }() s.logger.Info("HTTP server is now running", zap.Int("port", s.port)) <-ctx.Done() shutdownBegan := time.Now() s.logger.Info("Shutting HTTP server down gracefully...") err := app.ShutdownWithTimeout(10 * time.Second) if err != nil { panic(err) } // gracefully shutdown/close database pool if s.pool != nil { s.pool.Close() } s.logger.Info("HTTP server shut down gracefully.", zap.Duration("duration", time.Since(shutdownBegan))) } func NewTestingServer(apiHandlers *APIHandlers) *fiber.App { return setupServer(apiHandlers, nil, zap.NewNop(), true) } func setupServer(apiHandlers *APIHandlers, middlewares []fiber.Handler, logger *zap.Logger, testing bool) *fiber.App { config := fiber.Config{ DisableStartupMessage: true, IdleTimeout: 10 * time.Second, ReadTimeout: 10 * time.Second, WriteTimeout: 10 * time.Second, } app := fiber.New(config) for _, mw := range middlewares { app.Use(mw) } swaggerConfig := swagger.Config{ BasePath: "/", FilePath: "./api/v1/openapi.yaml", Path: "/swagger", Title: "Swagger API Docs", } if testing { swaggerConfig = swagger.Config{ BasePath: "/", FilePath: "../../api/v1/openapi.yaml", Path: "/swagger", Title: "Swagger API Docs", } } app.Use(swagger.New(swaggerConfig)) serverSwaggerSpec, err := GetSwagger() if err != nil { logger.Fatal("Error getting swagger", zap.Error(err)) } // See: https://github.com/deepmap/oapi-codegen/blob/master/examples/petstore-expanded/fiber/petstore.go#L41 // Clear out the servers array in the swagger spec, that skips validating // that server names match. We don't know how this thing will be run. serverSwaggerSpec.Servers = nil app.Use(middleware.OapiRequestValidator(serverSwaggerSpec)) handlers := NewStrictHandler(apiHandlers, nil) RegisterHandlers(app, handlers) return app }