Files
Sanaei f727d04f65
Release 3X-UI for Docker / build (push) Canceled after 0s
Release 3X-UI / build (386) (push) Canceled after 0s
Release 3X-UI / build (amd64) (push) Canceled after 0s
Release 3X-UI / build (arm64) (push) Canceled after 0s
Release 3X-UI / build (armv5) (push) Canceled after 0s
Release 3X-UI / build (armv6) (push) Canceled after 0s
Release 3X-UI / build (armv7) (push) Canceled after 0s
Release 3X-UI / build (s390x) (push) Canceled after 0s
Release 3X-UI / Build for Windows (amd64) (push) Canceled after 0s
Release 3X-UI / Publish rolling dev release (push) Canceled after 0s
v3.7.0
2026-08-24 15:07:15 +02:00

111 lines
2.4 KiB
Go

package middleware
import (
"errors"
"net/http"
"reflect"
"strings"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
"github.com/go-playground/validator/v10"
"github.com/mhsanaei/3x-ui/v3/internal/web/entity"
)
var validate = validator.New(validator.WithRequiredStructEnabled())
func BindAndValidate[T any](c *gin.Context) (*T, bool) {
var dst T
if err := c.ShouldBind(&dst); err != nil {
writeBindFailure(c, err)
return nil, false
}
if err := validate.Struct(&dst); err != nil {
writeBindFailure(c, err)
return nil, false
}
return &dst, true
}
func BindAndValidateInto(c *gin.Context, dst any) bool {
if err := c.ShouldBind(dst); err != nil {
writeBindFailure(c, err)
return false
}
if err := validate.Struct(dst); err != nil {
writeBindFailure(c, err)
return false
}
return true
}
func BindJSONAndValidate[T any](c *gin.Context) (*T, bool) {
var dst T
if err := c.ShouldBindWith(&dst, binding.JSON); err != nil {
writeBindFailure(c, err)
return nil, false
}
if err := validate.Struct(&dst); err != nil {
writeBindFailure(c, err)
return nil, false
}
return &dst, true
}
func BindJSONAndValidateInto(c *gin.Context, dst any) bool {
if err := c.ShouldBindWith(dst, binding.JSON); err != nil {
writeBindFailure(c, err)
return false
}
if err := validate.Struct(dst); err != nil {
writeBindFailure(c, err)
return false
}
return true
}
type FieldIssue struct {
Field string `json:"field"`
Rule string `json:"rule"`
Param string `json:"param,omitempty"`
Message string `json:"message"`
}
type ValidationPayload struct {
Issues []FieldIssue `json:"issues"`
Message string `json:"message"`
}
func writeBindFailure(c *gin.Context, err error) {
payload := ValidationPayload{Issues: []FieldIssue{}, Message: err.Error()}
if ve, ok := errors.AsType[validator.ValidationErrors](err); ok {
payload.Issues = make([]FieldIssue, 0, len(ve))
for _, fe := range ve {
payload.Issues = append(payload.Issues, FieldIssue{
Field: fe.Field(),
Rule: fe.Tag(),
Param: fe.Param(),
Message: fe.Error(),
})
}
}
c.AbortWithStatusJSON(http.StatusOK, entity.Msg{
Success: false,
Msg: "request body failed validation",
Obj: payload,
})
}
func init() {
validate.RegisterTagNameFunc(func(fld reflect.StructField) string {
name, _, _ := strings.Cut(fld.Tag.Get("json"), ",")
if name == "-" || name == "" {
return fld.Name
}
return name
})
}