Add domain name validation

- Validate domain name format: letters, numbers, dots, hyphens only
- Must have at least one dot (TLD required)
- Cannot start/end with dot or hyphen
- Label length 1-63 characters
- Return ValidationError with descriptive message
This commit is contained in:
Christoph Haas 2026-03-22 23:52:37 +01:00
parent 118b709275
commit 9dab3bc12c

View file

@ -2,7 +2,9 @@ package handlers
import (
"net/http"
"regexp"
"strconv"
"strings"
"github.com/gin-gonic/gin"
"github.com/imc-vibe/backend/internal/db"
@ -98,13 +100,19 @@ func (h *DomainHandler) Create(c *gin.Context) {
return
}
existing, err := h.db.GetDomainByName(req.Name)
name := strings.ToLower(strings.TrimSpace(req.Name))
if err := validateDomainName(name); err != nil {
Error(c, http.StatusBadRequest, err.Error())
return
}
existing, err := h.db.GetDomainByName(name)
if err == nil && existing != nil {
Error(c, http.StatusConflict, "domain already exists")
return
}
domain, err := h.db.CreateDomain(req.Name)
domain, err := h.db.CreateDomain(name)
if err != nil {
Error(c, http.StatusInternalServerError, "failed to create domain")
return
@ -113,6 +121,46 @@ func (h *DomainHandler) Create(c *gin.Context) {
Created(c, domain)
}
func validateDomainName(name string) error {
if len(name) < 3 || len(name) > 253 {
return &ValidationError{Message: "domain name must be between 3 and 253 characters"}
}
if strings.Contains(name, "..") {
return &ValidationError{Message: "domain name cannot contain consecutive dots"}
}
if strings.HasPrefix(name, ".") || strings.HasSuffix(name, ".") {
return &ValidationError{Message: "domain name cannot start or end with a dot"}
}
if strings.HasPrefix(name, "-") || strings.HasSuffix(name, "-") {
return &ValidationError{Message: "domain name cannot start or end with a hyphen"}
}
domainRegex := regexp.MustCompile(`^[a-z0-9]([a-z0-9.-]*[a-z0-9])?$`)
if !domainRegex.MatchString(name) {
return &ValidationError{Message: "domain name can only contain letters, numbers, dots, and hyphens"}
}
labels := strings.Split(name, ".")
for _, label := range labels {
if len(label) < 1 || len(label) > 63 {
return &ValidationError{Message: "each part of the domain name must be between 1 and 63 characters"}
}
}
return nil
}
type ValidationError struct {
Message string
}
func (e *ValidationError) Error() string {
return e.Message
}
func (h *DomainHandler) Delete(c *gin.Context) {
authCtx := GetAuthContext(c)
if authCtx == nil || !authCtx.IsAdmin() {