114 lines
3.1 KiB
Go
114 lines
3.1 KiB
Go
package server
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"github.com/go-chi/chi/v5"
|
|
"github.com/go-chi/chi/v5/middleware"
|
|
oapimiddleware "github.com/oapi-codegen/nethttp-middleware"
|
|
"gitlab.mareshq.com/hq/backoffice/backoffice-api/internal/training"
|
|
"log"
|
|
"net/http"
|
|
"time"
|
|
)
|
|
|
|
type Server struct {
|
|
logger *log.Logger
|
|
hostname string
|
|
|
|
trainingRepository training.Repository
|
|
|
|
router *chi.Mux
|
|
srv *http.Server
|
|
tls *TLS
|
|
}
|
|
|
|
type TLS struct {
|
|
CertFile string
|
|
KeyFile string
|
|
}
|
|
|
|
func NewServer(hostname string, logger *log.Logger, trainingRepository training.Repository) *Server {
|
|
return &Server{
|
|
logger: logger,
|
|
hostname: hostname,
|
|
trainingRepository: trainingRepository,
|
|
router: chi.NewRouter(),
|
|
tls: nil,
|
|
}
|
|
}
|
|
|
|
func NewServerWithTLS(hostname string, tls *TLS, logger *log.Logger, trainingRepository training.Repository) *Server {
|
|
s := NewServer(hostname, logger, trainingRepository)
|
|
s.tls = tls
|
|
return s
|
|
}
|
|
|
|
func (s *Server) Run(ctx context.Context) error {
|
|
swagger, err := GetSwagger()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// 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.
|
|
swagger.Servers = nil
|
|
|
|
s.router.Use(middleware.Logger)
|
|
// middleware.Recoverer recovers from panics, logs the panic (and a stack trace),
|
|
s.router.Use(middleware.Recoverer)
|
|
// we trust headers, since we are running on Kubernetes with Ingress Controller (Ingress-NGINX)
|
|
// and behind an L4 load balancer (on Hetzner Cloud)
|
|
s.router.Use(middleware.RealIP)
|
|
// middleware.RequestID generates a request ID and adds it to request context
|
|
// if the request has an `X-Request-ID` header, it will use that as the request ID
|
|
s.router.Use(middleware.RequestID)
|
|
s.router.Use(middleware.Timeout(10 * time.Second))
|
|
s.router.Use(oapimiddleware.OapiRequestValidator(swagger))
|
|
|
|
// create handler
|
|
h := NewStrictHandler(s, nil)
|
|
|
|
// register endpoints
|
|
HandlerFromMux(h, s.router)
|
|
|
|
s.srv = &http.Server{
|
|
Addr: ":8080", // TODO: make port configurable
|
|
Handler: s.router,
|
|
}
|
|
|
|
go func() {
|
|
var err error
|
|
if s.tls != nil {
|
|
s.logger.Printf("Starting HTTPS server on: %s\n", s.srv.Addr)
|
|
err = s.srv.ListenAndServeTLS(s.tls.CertFile, s.tls.KeyFile)
|
|
} else {
|
|
s.logger.Printf("Starting HTTP server on: %s\n", s.srv.Addr)
|
|
err = s.srv.ListenAndServe()
|
|
}
|
|
// suppress the error if the server was closed gracefully
|
|
if err != nil && !errors.Is(err, http.ErrServerClosed) {
|
|
s.logger.Printf("error: %v\n", err)
|
|
}
|
|
}()
|
|
|
|
// wait for the context to be done
|
|
// context done means the server is shutting down
|
|
<-ctx.Done()
|
|
|
|
// TODO: make graceful shutdown period configurable
|
|
timeout := 10 * time.Second
|
|
timeoutCtx, cancel := context.WithTimeout(context.Background(), timeout)
|
|
defer cancel()
|
|
|
|
s.logger.Printf("Shutting down server in %s seconds\n", timeout.String())
|
|
err = s.srv.Shutdown(timeoutCtx)
|
|
// suppress the error if the server was closed gracefully
|
|
if err != nil && !errors.Is(err, http.ErrServerClosed) {
|
|
return err
|
|
}
|
|
|
|
s.logger.Println("Server shutdown successfully")
|
|
|
|
return nil
|
|
}
|