mirror of
https://github.com/Dvorinka/MyClubServer.git
synced 2026-06-04 18:52:56 +00:00
dev day #65
This commit is contained in:
@@ -0,0 +1,171 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-playground/validator/v10"
|
||||
)
|
||||
|
||||
// ValidationHelper provides validation utilities
|
||||
type ValidationHelper struct {
|
||||
validator *validator.Validate
|
||||
}
|
||||
|
||||
// ValidationError represents a validation error
|
||||
type ValidationError struct {
|
||||
Field string `json:"field"`
|
||||
Message string `json:"message"`
|
||||
Tag string `json:"tag"`
|
||||
}
|
||||
|
||||
// NewValidationHelper creates a new ValidationHelper instance
|
||||
func NewValidationHelper() *ValidationHelper {
|
||||
v := validator.New()
|
||||
|
||||
// Register custom validators
|
||||
_ = v.RegisterValidation("slug", validateSlug)
|
||||
_ = v.RegisterValidation("color", validateColor)
|
||||
|
||||
return &ValidationHelper{
|
||||
validator: v,
|
||||
}
|
||||
}
|
||||
|
||||
// Validate validates a struct and returns user-friendly errors
|
||||
func (vh *ValidationHelper) Validate(data interface{}) []ValidationError {
|
||||
err := vh.validator.Struct(data)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var validationErrors []ValidationError
|
||||
for _, err := range err.(validator.ValidationErrors) {
|
||||
validationErrors = append(validationErrors, ValidationError{
|
||||
Field: strings.ToLower(err.Field()),
|
||||
Message: vh.getErrorMessage(err),
|
||||
Tag: err.Tag(),
|
||||
})
|
||||
}
|
||||
|
||||
return validationErrors
|
||||
}
|
||||
|
||||
// ValidateAndRespond validates data and sends error response if invalid
|
||||
// Returns true if valid, false if invalid (and response already sent)
|
||||
func (vh *ValidationHelper) ValidateAndRespond(c *gin.Context, data interface{}) bool {
|
||||
errors := vh.Validate(data)
|
||||
if len(errors) > 0 {
|
||||
Respond.ValidationError(c, errors)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// getErrorMessage returns a user-friendly error message for a validation error
|
||||
func (vh *ValidationHelper) getErrorMessage(err validator.FieldError) string {
|
||||
field := err.Field()
|
||||
|
||||
switch err.Tag() {
|
||||
case "required":
|
||||
return fmt.Sprintf("%s is required", field)
|
||||
case "email":
|
||||
return fmt.Sprintf("%s must be a valid email address", field)
|
||||
case "min":
|
||||
return fmt.Sprintf("%s must be at least %s characters", field, err.Param())
|
||||
case "max":
|
||||
return fmt.Sprintf("%s must be at most %s characters", field, err.Param())
|
||||
case "len":
|
||||
return fmt.Sprintf("%s must be exactly %s characters", field, err.Param())
|
||||
case "url":
|
||||
return fmt.Sprintf("%s must be a valid URL", field)
|
||||
case "slug":
|
||||
return fmt.Sprintf("%s must be a valid slug (lowercase, alphanumeric, hyphens)", field)
|
||||
case "color":
|
||||
return fmt.Sprintf("%s must be a valid hex color code", field)
|
||||
case "oneof":
|
||||
return fmt.Sprintf("%s must be one of: %s", field, err.Param())
|
||||
case "gte":
|
||||
return fmt.Sprintf("%s must be greater than or equal to %s", field, err.Param())
|
||||
case "lte":
|
||||
return fmt.Sprintf("%s must be less than or equal to %s", field, err.Param())
|
||||
case "gt":
|
||||
return fmt.Sprintf("%s must be greater than %s", field, err.Param())
|
||||
case "lt":
|
||||
return fmt.Sprintf("%s must be less than %s", field, err.Param())
|
||||
default:
|
||||
return fmt.Sprintf("%s is invalid", field)
|
||||
}
|
||||
}
|
||||
|
||||
// IsValidEmail checks if a string is a valid email
|
||||
func (vh *ValidationHelper) IsValidEmail(email string) bool {
|
||||
return vh.validator.Var(email, "required,email") == nil
|
||||
}
|
||||
|
||||
// IsValidURL checks if a string is a valid URL
|
||||
func (vh *ValidationHelper) IsValidURL(url string) bool {
|
||||
return vh.validator.Var(url, "required,url") == nil
|
||||
}
|
||||
|
||||
// IsValidSlug checks if a string is a valid slug
|
||||
func (vh *ValidationHelper) IsValidSlug(slug string) bool {
|
||||
return vh.validator.Var(slug, "required,slug") == nil
|
||||
}
|
||||
|
||||
// SanitizeString removes leading/trailing whitespace and normalizes spaces
|
||||
func (vh *ValidationHelper) SanitizeString(s string) string {
|
||||
// Trim whitespace
|
||||
s = strings.TrimSpace(s)
|
||||
// Replace multiple spaces with single space
|
||||
s = regexp.MustCompile(`\s+`).ReplaceAllString(s, " ")
|
||||
return s
|
||||
}
|
||||
|
||||
// SanitizeEmail normalizes an email address
|
||||
func (vh *ValidationHelper) SanitizeEmail(email string) string {
|
||||
return strings.ToLower(strings.TrimSpace(email))
|
||||
}
|
||||
|
||||
// SanitizeSlug normalizes a slug
|
||||
func (vh *ValidationHelper) SanitizeSlug(slug string) string {
|
||||
slug = strings.ToLower(strings.TrimSpace(slug))
|
||||
// Replace spaces with hyphens
|
||||
slug = regexp.MustCompile(`\s+`).ReplaceAllString(slug, "-")
|
||||
// Remove non-alphanumeric characters except hyphens
|
||||
slug = regexp.MustCompile(`[^a-z0-9-]`).ReplaceAllString(slug, "")
|
||||
// Replace multiple hyphens with single hyphen
|
||||
slug = regexp.MustCompile(`-+`).ReplaceAllString(slug, "-")
|
||||
// Remove leading/trailing hyphens
|
||||
slug = strings.Trim(slug, "-")
|
||||
return slug
|
||||
}
|
||||
|
||||
// Custom validator functions
|
||||
|
||||
// validateSlug validates that a string is a valid slug
|
||||
func validateSlug(fl validator.FieldLevel) bool {
|
||||
slug := fl.Field().String()
|
||||
if slug == "" {
|
||||
return true // Let 'required' handle empty values
|
||||
}
|
||||
// Valid slug: lowercase alphanumeric and hyphens, no leading/trailing hyphens
|
||||
matched, _ := regexp.MatchString(`^[a-z0-9]+(-[a-z0-9]+)*$`, slug)
|
||||
return matched
|
||||
}
|
||||
|
||||
// validateColor validates that a string is a valid hex color code
|
||||
func validateColor(fl validator.FieldLevel) bool {
|
||||
color := fl.Field().String()
|
||||
if color == "" {
|
||||
return true // Let 'required' handle empty values
|
||||
}
|
||||
// Valid color: #RGB, #RRGGBB, #RGBA, #RRGGBBAA
|
||||
matched, _ := regexp.MatchString(`^#([A-Fa-f0-9]{3}|[A-Fa-f0-9]{6}|[A-Fa-f0-9]{4}|[A-Fa-f0-9]{8})$`, color)
|
||||
return matched
|
||||
}
|
||||
|
||||
// Global ValidationHelper instance for convenience
|
||||
var Validator = NewValidationHelper()
|
||||
Reference in New Issue
Block a user