diff --git a/middleware/example.go b/config/middleware.go similarity index 93% rename from middleware/example.go rename to config/middleware.go index f358cd4..ba8a948 100644 --- a/middleware/example.go +++ b/config/middleware.go @@ -1,4 +1,4 @@ -package middleware +package config import ( "log" diff --git a/config/config.go b/config/views.go similarity index 94% rename from config/config.go rename to config/views.go index 1d37b5e..3648a6b 100644 --- a/config/config.go +++ b/config/views.go @@ -25,7 +25,7 @@ var ViewMap = map[string][]string{ ), } -var FuncMap = map[string]any{ +var ViewFormatters = map[string]any{ "uppercase": func(s string) string { return strings.ToUpper(s) }, "firstWord": func(s string) string { words := strings.Fields(s) diff --git a/forms/formatters.go b/forms/formatters.go index 7d31664..580d4f3 100644 --- a/forms/formatters.go +++ b/forms/formatters.go @@ -4,6 +4,15 @@ import ( "strings" ) +type Formatter func(string) string + +var Formatters = map[string]Formatter{ + "trim": strings.TrimSpace, + "lower": strings.ToLower, + "upper": strings.ToUpper, + "capitalize": capitalize, +} + func capitalize(s string) string { words := strings.Fields(s) for i, word := range words { diff --git a/forms/forms.go b/forms/forms.go index 09e2fa5..72036de 100644 --- a/forms/forms.go +++ b/forms/forms.go @@ -9,23 +9,6 @@ import ( "strings" ) -type Formatter func(string) string - -var Formatters = map[string]Formatter{ - "trim": strings.TrimSpace, - "lower": strings.ToLower, - "upper": strings.ToUpper, - "capitalize": capitalize, -} - -type Validator func(fieldName string, value any, param string) error - -var Validators = map[string]Validator{ - "nonzero": nonzero, - "minlen": minlen, - "email": email, -} - func FormToStruct[T any](r *http.Request) (T, error) { var target T if err := r.ParseForm(); err != nil { @@ -184,8 +167,8 @@ func parseValidators(tag string) []struct { Name string Param string } - parts := strings.SplitSeq(tag, ",") - for part := range parts { + parts := strings.Split(tag, ",") + for _, part := range parts { pair := strings.SplitN(part, ":", 2) if len(pair) == 2 { result = append(result, struct{ Name, Param string }{pair[0], pair[1]}) diff --git a/forms/validators.go b/forms/validators.go index ed9364d..1615233 100644 --- a/forms/validators.go +++ b/forms/validators.go @@ -4,20 +4,32 @@ import ( "fmt" "reflect" "strconv" - "strings" + "net/mail" ) +type Validator func(fieldName string, value any, param string) error + +var Validators = map[string]Validator{ + "nonzero": nonzero, + "minlen": minlen, + "email": email, +} + func nonzero(field string, value any, _ string) error { v := reflect.ValueOf(value) + if v.Kind() == reflect.String && v.Len() == 0 { return fmt.Errorf("field '%s' must not be empty", field) } + if v.Kind() == reflect.Slice && v.Len() == 0 { return fmt.Errorf("field '%s' must not be empty", field) } + if v.Kind() >= reflect.Int && v.Kind() <= reflect.Float64 && v.IsZero() { return fmt.Errorf("field '%s' must be non-zero", field) } + return nil } @@ -26,16 +38,24 @@ func minlen(field string, value any, param string) error { if err != nil { return fmt.Errorf("invalid minlen param for field '%s'", field) } + if str, ok := value.(string); ok && len(str) < min { return fmt.Errorf("field '%s' must be at least %d characters", field, min) } + return nil } func email(field string, value any, _ string) error { str, ok := value.(string) - if !ok || !strings.Contains(str, "@") { + if !ok { return fmt.Errorf("field '%s' must be a valid email address", field) } + + _, err := mail.ParseAddress(str) + if err != nil { + return fmt.Errorf("invalid email address: %v", err) + } + return nil } diff --git a/handlers/sampleHandlers.go b/handlers/example_handlers.go similarity index 100% rename from handlers/sampleHandlers.go rename to handlers/example_handlers.go diff --git a/main.go b/main.go index 5167a7f..5763788 100644 --- a/main.go +++ b/main.go @@ -39,7 +39,7 @@ func init() { } func main() { - err := views.Init(viewFS, config.ViewMap, config.FuncMap) + err := views.Init(viewFS, config.ViewMap, config.ViewFormatters) if err != nil { log.Fatalf("failed to initialize templates: %v", err) } diff --git a/routes.go b/routes.go index f01cf82..e896aaa 100644 --- a/routes.go +++ b/routes.go @@ -3,6 +3,7 @@ package main import ( "net/http" + "git.tavo.one/tavo/axiom/config" "git.tavo.one/tavo/axiom/handlers" "git.tavo.one/tavo/axiom/middleware" ) @@ -11,7 +12,7 @@ func routes(handler *handlers.Handler) *http.ServeMux { router := http.NewServeMux() protectedStack := middleware.Stack( - middleware.AuthCheck, + config.AuthCheck, ) router.HandleFunc(