create CMS error handling

This commit is contained in:
Mazyar
2026-05-31 02:17:46 +03:30
parent bca94cd69a
commit 31627a1bbe
6 changed files with 199 additions and 33 deletions
+136
View File
@@ -0,0 +1,136 @@
package cms
import (
"fmt"
"regexp"
"strings"
"github.com/asaskevich/govalidator"
"github.com/labstack/echo/v4"
)
var (
objectIDPattern = regexp.MustCompile(`^[a-fA-F0-9]{24}$`)
filePathPattern = regexp.MustCompile(`^/[^/]*/v\d+/files/[a-fA-F0-9]{24}(/download)?$`)
)
// ValidationService registers CMS-specific govalidator rules.
type ValidationService struct{}
// NewValidationService registers custom validators used by CMS forms.
func NewValidationService() ValidationService {
govalidator.CustomTypeTagMap.Set("mediaRef", govalidator.CustomTypeValidator(func(i interface{}, _ interface{}) bool {
value, ok := i.(string)
if !ok {
return false
}
return isMediaReference(value)
}))
return ValidationService{}
}
func isMediaReference(value string) bool {
if value == "" {
return true
}
if govalidator.IsURL(value) {
return true
}
if objectIDPattern.MatchString(value) {
return true
}
return filePathPattern.MatchString(value)
}
// ValidateCMSForm validates a CMS create/update payload.
func ValidateCMSForm(form *CMSForm) error {
_, err := govalidator.ValidateStruct(form)
return err
}
// ParseForm binds and validates a CMS form from the request.
func ParseForm(c echo.Context) (*CMSForm, error) {
var form CMSForm
if err := c.Bind(&form); err != nil {
return nil, fmt.Errorf("invalid request format: %w", err)
}
if err := ValidateCMSForm(&form); err != nil {
return nil, err
}
return &form, nil
}
// FormatValidationErrors converts govalidator errors into user-facing messages.
func FormatValidationErrors(err error) string {
if err == nil {
return ""
}
errs, ok := err.(govalidator.Errors)
if !ok {
return humanizeValidationError(err.Error())
}
messages := make([]string, 0, len(errs))
for _, item := range errs {
messages = append(messages, humanizeValidationError(item.Error()))
}
return strings.Join(messages, "; ")
}
func humanizeValidationError(raw string) string {
field, rule, ok := parseGovalidatorError(raw)
if !ok {
return raw
}
label := fieldLabel(field)
lowerRule := strings.ToLower(rule)
switch {
case strings.Contains(lowerRule, "mediaref"):
return label + " must be a valid URL or uploaded file"
case strings.Contains(lowerRule, "url"):
return label + " must be a valid URL"
case strings.Contains(lowerRule, "email"):
return label + " must be a valid email address"
case strings.Contains(lowerRule, "length"):
return label + " has an invalid length"
case strings.Contains(lowerRule, "range"):
return label + " is out of the allowed range"
case strings.Contains(lowerRule, "in("):
return label + " has an invalid value"
default:
return label + " is invalid"
}
}
func parseGovalidatorError(raw string) (field, rule string, ok bool) {
parts := strings.SplitN(raw, ": ", 2)
if len(parts) != 2 {
return "", "", false
}
return parts[0], parts[1], true
}
func fieldLabel(field string) string {
field = strings.TrimPrefix(field, "CMSForm.")
replacer := strings.NewReplacer(
"Hero.", "Hero section: ",
"Chart.", "Chart section: ",
"Features.", "Features section: ",
"Challenges.", "Challenges section: ",
"Advantages.", "Advantages section: ",
"Contact.", "Contact section: ",
"Footer.", "Footer section: ",
".", " ",
)
label := replacer.Replace(field)
label = strings.ReplaceAll(label, "Cards 0", "card 1")
label = strings.ReplaceAll(label, "Cards 1", "card 2")
label = strings.ReplaceAll(label, "Cards 2", "card 3")
label = strings.ReplaceAll(label, "Cards 3", "card 4")
label = strings.ReplaceAll(label, "Cards 4", "card 5")
return label
}