validator.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. // Package validator предоставляет кастомные валидаторы на базе go-playground/validator.
  2. // Реализованы валидации: uuid, slug, latitude, longitude, place_type, user_role,
  3. // currency, user_status, place_status, service_status, booking_status, datetime.
  4. // Экспортирует функции Validate (структуры) и ValidateVar (одно поле).
  5. package validator
  6. import (
  7. "regexp"
  8. "time"
  9. "github.com/go-playground/validator/v10"
  10. )
  11. var (
  12. validate *validator.Validate
  13. uuidRegex = regexp.MustCompile(`^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$`)
  14. slugRegex = regexp.MustCompile(`^[a-z0-9-]+$`)
  15. currencyRegex = regexp.MustCompile(`^[A-Z]{3}$`)
  16. )
  17. func init() {
  18. validate = validator.New()
  19. // Custom validators
  20. _ = validate.RegisterValidation("uuid", validateUUID)
  21. _ = validate.RegisterValidation("slug", validateSlug)
  22. _ = validate.RegisterValidation("latitude", validateLatitude)
  23. _ = validate.RegisterValidation("longitude", validateLongitude)
  24. _ = validate.RegisterValidation("place_type", validatePlaceType)
  25. _ = validate.RegisterValidation("user_role", validateUserRole)
  26. _ = validate.RegisterValidation("currency", validateCurrency)
  27. _ = validate.RegisterValidation("user_status", validateUserStatus)
  28. _ = validate.RegisterValidation("place_status", validatePlaceStatus)
  29. _ = validate.RegisterValidation("service_status", validateServiceStatus)
  30. _ = validate.RegisterValidation("booking_status", validateBookingStatus)
  31. _ = validate.RegisterValidation("datetime", validateDateTime)
  32. }
  33. // Validate validates a struct and returns ValidationErrors if any
  34. func Validate(s interface{}) error {
  35. return validate.Struct(s)
  36. }
  37. // ValidateVar validates a single variable
  38. func ValidateVar(field interface{}, tag string) error {
  39. return validate.Var(field, tag)
  40. }
  41. // ValidationErrors wraps validator.ValidationErrors for easier handling
  42. type ValidationErrors validator.ValidationErrors
  43. func (ve ValidationErrors) Error() string {
  44. return validator.ValidationErrors(ve).Error()
  45. }
  46. // FieldErrors returns a map of field -> error message
  47. func (ve ValidationErrors) FieldErrors() map[string]string {
  48. errs := make(map[string]string)
  49. for _, fe := range ve {
  50. errs[fe.Field()] = fe.Tag()
  51. }
  52. return errs
  53. }
  54. // Custom validators
  55. func validateUUID(fl validator.FieldLevel) bool {
  56. return uuidRegex.MatchString(fl.Field().String())
  57. }
  58. func validateSlug(fl validator.FieldLevel) bool {
  59. return slugRegex.MatchString(fl.Field().String())
  60. }
  61. func validateLatitude(fl validator.FieldLevel) bool {
  62. lat := fl.Field().Float()
  63. return lat >= -90 && lat <= 90
  64. }
  65. func validateLongitude(fl validator.FieldLevel) bool {
  66. lng := fl.Field().Float()
  67. return lng >= -180 && lng <= 180
  68. }
  69. func validatePlaceType(fl validator.FieldLevel) bool {
  70. v := fl.Field().String()
  71. return v == "place" || v == "studio"
  72. }
  73. func validateUserRole(fl validator.FieldLevel) bool {
  74. roles := map[string]bool{
  75. "customer": true, "landlord": true, "executor": true,
  76. "moderator": true, "superadmin": true,
  77. }
  78. return roles[fl.Field().String()]
  79. }
  80. func validateCurrency(fl validator.FieldLevel) bool {
  81. return currencyRegex.MatchString(fl.Field().String())
  82. }
  83. func validateUserStatus(fl validator.FieldLevel) bool {
  84. statuses := map[string]bool{"active": true, "banned": true, "pending_verification": true}
  85. return statuses[fl.Field().String()]
  86. }
  87. func validatePlaceStatus(fl validator.FieldLevel) bool {
  88. statuses := map[string]bool{
  89. "draft": true, "pending_moderation": true, "published": true,
  90. "rejected": true, "archived": true,
  91. }
  92. return statuses[fl.Field().String()]
  93. }
  94. func validateServiceStatus(fl validator.FieldLevel) bool {
  95. statuses := map[string]bool{"draft": true, "published": true, "archived": true}
  96. return statuses[fl.Field().String()]
  97. }
  98. func validateBookingStatus(fl validator.FieldLevel) bool {
  99. statuses := map[string]bool{"pending": true, "confirmed": true, "cancelled": true, "completed": true}
  100. return statuses[fl.Field().String()]
  101. }
  102. func validateDateTime(fl validator.FieldLevel) bool {
  103. _, err := time.Parse(time.RFC3339, fl.Field().String())
  104. return err == nil
  105. }