package router import ( "fmt" "io" "net/http" "os" "github.com/gin-gonic/gin" "github.com/gin-gonic/gin/binding" "github.com/go-playground/validator/v10" "github.com/iliadenisov/galaxy/internal/router/handler" ) const ( ISO8601 = "2006-01-02 15:04:05.0 -07:00" ) type Router struct { r *gin.Engine executor handler.CommandExecutor } func (r Router) Run() error { return r.r.Run() } func NewRouter() Router { gin.SetMode(gin.ReleaseMode) return NewRouterExecutor(handler.NewDefaultExecutor()) } func NewRouterExecutor(executor handler.CommandExecutor) Router { return Router{r: setupRouter(executor)} } func setupRouter(executor handler.CommandExecutor) *gin.Engine { r := gin.New() // Logger middleware will write the logs to gin.DefaultWriter even if you set with GIN_MODE=release. logConfig := &gin.LoggerConfig{Formatter: logFormatter} if gin.Mode() != gin.DebugMode { logConfig.Output = io.Discard } r.Use(gin.LoggerWithConfig(*logConfig)) // Recovery middleware recovers from any panics and writes a 500 if there was one. r.Use(gin.CustomRecovery(recoveryHandler)) if v, ok := binding.Validator.Engine().(*validator.Validate); ok { if err := v.RegisterValidation("notblank", notBlankStringValidator); err != nil { panic(err) } if err := v.RegisterValidation("ammoWeapons", armamentWithWeaponsValidator); err != nil { panic(err) } if err := v.RegisterValidation("entity", entityNameStringValidator); err != nil { panic(err) } if err := v.RegisterValidation("subject", productionTypeStringValidator); err != nil { panic(err) } } groupV1 := r.Group("/api/v1") groupV1.GET("/status", func(ctx *gin.Context) { handler.StatusHandler(ctx, executor) }) groupV1.POST("/init", func(ctx *gin.Context) { handler.InitHandler(ctx, executor) }) groupV1.PUT("/command", LimitMiddleware(1), func(ctx *gin.Context) { handler.CommandHandler(ctx, executor) }) groupV1.PUT("/turn", func(ctx *gin.Context) { handler.TurnHandler(ctx, executor) }) return r } func logFormatter(param gin.LogFormatterParams) string { return fmt.Sprintf("[%s] \"%s %s %s %d %s\"\n", param.TimeStamp.Format(ISO8601), param.Method, param.Path, param.Request.Proto, param.StatusCode, param.Latency, ) } func recoveryHandler(c *gin.Context, recovered any) { if err, ok := recovered.(string); ok { fmt.Fprintf(os.Stderr, "recovered: %s", err) } c.AbortWithStatus(http.StatusInternalServerError) }