160 lines
3.6 KiB
Go
160 lines
3.6 KiB
Go
package api
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"git.itzana.me/StrafesNET/dev-service/pkg/api/middleware"
|
|
"git.itzana.me/strafesnet/maps-service/docs"
|
|
"git.itzana.me/strafesnet/maps-service/pkg/public_api/handlers"
|
|
"github.com/gin-gonic/gin"
|
|
log "github.com/sirupsen/logrus"
|
|
swaggerfiles "github.com/swaggo/files"
|
|
ginSwagger "github.com/swaggo/gin-swagger"
|
|
"github.com/urfave/cli/v2"
|
|
"google.golang.org/grpc"
|
|
"net/http"
|
|
"time"
|
|
)
|
|
|
|
// Option defines a function that configures a Router
|
|
type Option func(*RouterConfig)
|
|
|
|
// RouterConfig holds all router configuration
|
|
type RouterConfig struct {
|
|
port int
|
|
devClient *grpc.ClientConn
|
|
mapsClient *grpc.ClientConn
|
|
context *cli.Context
|
|
shutdownTimeout time.Duration
|
|
}
|
|
|
|
// WithPort sets the port for the server£
|
|
func WithPort(port int) Option {
|
|
return func(cfg *RouterConfig) {
|
|
cfg.port = port
|
|
}
|
|
}
|
|
|
|
// WithContext sets the context for the server
|
|
func WithContext(ctx *cli.Context) Option {
|
|
return func(cfg *RouterConfig) {
|
|
cfg.context = ctx
|
|
}
|
|
}
|
|
|
|
// WithDevClient sets the dev gRPC client
|
|
func WithDevClient(conn *grpc.ClientConn) Option {
|
|
return func(cfg *RouterConfig) {
|
|
cfg.devClient = conn
|
|
}
|
|
}
|
|
|
|
// WithMapsClient sets the data gRPC client
|
|
func WithMapsClient(conn *grpc.ClientConn) Option {
|
|
return func(cfg *RouterConfig) {
|
|
cfg.mapsClient = conn
|
|
}
|
|
}
|
|
|
|
// WithShutdownTimeout sets the graceful shutdown timeout
|
|
func WithShutdownTimeout(timeout time.Duration) Option {
|
|
return func(cfg *RouterConfig) {
|
|
cfg.shutdownTimeout = timeout
|
|
}
|
|
}
|
|
|
|
func setupRoutes(cfg *RouterConfig) (*gin.Engine, error) {
|
|
r := gin.Default()
|
|
r.ForwardedByClientIP = true
|
|
r.Use(gin.Logger())
|
|
r.Use(gin.Recovery())
|
|
|
|
handlerOptions := []handlers.HandlerOption{
|
|
handlers.WithMapsClient(cfg.mapsClient),
|
|
}
|
|
|
|
// Maps handler
|
|
mapsHandler, err := handlers.NewMapHandler(handlerOptions...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
docs.SwaggerInfo.BasePath = "/public-api/v1"
|
|
public_api := r.Group("/public-api")
|
|
{
|
|
v1 := public_api.Group("/v1")
|
|
{
|
|
// Auth middleware
|
|
v1.Use(middleware.ValidateRequest("Maps", "Read", cfg.devClient))
|
|
|
|
// Maps
|
|
v1.GET("/map", mapsHandler.List)
|
|
v1.GET("/map/:id", mapsHandler.Get)
|
|
}
|
|
|
|
// Docs
|
|
public_api.GET("/docs/*any", ginSwagger.WrapHandler(swaggerfiles.Handler))
|
|
public_api.GET("/", func(ctx *gin.Context) {
|
|
ctx.Redirect(http.StatusPermanentRedirect, "/public-api/docs/index.html")
|
|
})
|
|
}
|
|
|
|
return r, nil
|
|
}
|
|
|
|
// NewRouter creates a new router with the given options
|
|
func NewRouter(options ...Option) error {
|
|
// Default configuration
|
|
cfg := &RouterConfig{
|
|
port: 8080, // Default port
|
|
context: nil,
|
|
shutdownTimeout: 5 * time.Second,
|
|
}
|
|
|
|
// Apply options
|
|
for _, option := range options {
|
|
option(cfg)
|
|
}
|
|
|
|
// Validate configuration
|
|
if cfg.context == nil {
|
|
return errors.New("context is required")
|
|
}
|
|
|
|
if cfg.devClient == nil {
|
|
return errors.New("dev client is required")
|
|
}
|
|
|
|
routes, err := setupRoutes(cfg)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
log.Info("Starting server")
|
|
|
|
return runServer(cfg.context.Context, fmt.Sprint(":", cfg.port), routes, cfg.shutdownTimeout)
|
|
}
|
|
|
|
func runServer(ctx context.Context, addr string, r *gin.Engine, shutdownTimeout time.Duration) error {
|
|
srv := &http.Server{
|
|
Addr: addr,
|
|
Handler: r,
|
|
}
|
|
|
|
// Run the server in a separate goroutine
|
|
go func() {
|
|
if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
|
log.WithError(err).Fatal("web server exit")
|
|
}
|
|
}()
|
|
|
|
// Wait for a shutdown signal
|
|
<-ctx.Done()
|
|
|
|
// Shutdown server gracefully
|
|
ctxShutdown, cancel := context.WithTimeout(context.Background(), shutdownTimeout)
|
|
defer cancel()
|
|
return srv.Shutdown(ctxShutdown)
|
|
}
|