diff --git a/controller/controller.go b/controller/controller.go index 6b1baec..a460fc1 100644 --- a/controller/controller.go +++ b/controller/controller.go @@ -6,6 +6,7 @@ import ( "net/http" "reflect" + "goweb/htmx" "goweb/middleware" "goweb/msg" "goweb/services" @@ -153,7 +154,11 @@ func (c *Controller) cachePage(ctx echo.Context, page Page, html *bytes.Buffer) // Redirect redirects to a given route name with optional route parameters func (c *Controller) Redirect(ctx echo.Context, route string, routeParams ...interface{}) error { - return ctx.Redirect(http.StatusFound, ctx.Echo().Reverse(route, routeParams)) + url := ctx.Echo().Reverse(route, routeParams) + h := htmx.Response{} + h.Redirect = url + h.Apply(ctx) + return ctx.Redirect(http.StatusFound, url) } func (c *Controller) Fail(ctx echo.Context, err error, log string) error { diff --git a/controller/form.go b/controller/form.go index e236001..4dbb9f9 100644 --- a/controller/form.go +++ b/controller/form.go @@ -1,8 +1,6 @@ package controller import ( - "reflect" - "github.com/go-playground/validator/v10" "github.com/labstack/echo/v4" @@ -78,21 +76,9 @@ func (f *FormSubmission) setErrorMessages(form interface{}, err error) { return } - formType := reflect.TypeOf(form) - for _, ve := range ves { var message string - // Default the field form name to the name of the struct field - fieldName := ve.StructField() - - // Attempt to get the form field name from the field's struct tag - if field, ok := formType.FieldByName(ve.Field()); ok { - if fieldNameTag := field.Tag.Get("form"); fieldNameTag != "" { - fieldName = fieldNameTag - } - } - // Provide better error messages depending on the failed validation tag // This should be expanded as you use additional tags in your validation switch ve.Tag() { @@ -107,6 +93,6 @@ func (f *FormSubmission) setErrorMessages(form interface{}, err error) { } // Add the error - f.SetFieldError(fieldName, message) + f.SetFieldError(ve.Field(), message) } } diff --git a/routes/contact.go b/routes/contact.go index faf409a..a8ed302 100644 --- a/routes/contact.go +++ b/routes/contact.go @@ -27,15 +27,17 @@ func (c *Contact) Get(ctx echo.Context) error { p.Form = ContactForm{} if form := ctx.Get(context.FormKey); form != nil { - p.Form = form.(ContactForm) + p.Form = form.(*ContactForm) } return c.RenderPage(ctx, p) } func (c *Contact) Post(ctx echo.Context) error { - // Parse the form values var form ContactForm + ctx.Set(context.FormKey, &form) + + // Parse the form values if err := ctx.Bind(&form); err != nil { return c.Fail(ctx, err, "unable to bind form") } @@ -44,7 +46,7 @@ func (c *Contact) Post(ctx echo.Context) error { return c.Fail(ctx, err, "unable to process form submission") } - ctx.Set(context.FormKey, form) + //ctx.Set(context.FormKey, form) if !form.Submission.HasErrors() { if err := c.Container.Mail.Send(ctx, form.Email, "Hello!"); err != nil { diff --git a/routes/login.go b/routes/login.go index 2219dee..a83d4d2 100644 --- a/routes/login.go +++ b/routes/login.go @@ -18,73 +18,76 @@ type ( } LoginForm struct { - Email string `form:"email" validate:"required,email" label:"Email address"` - Password string `form:"password" validate:"required" label:"Password"` + Email string `form:"email" validate:"required,email" label:"Email address"` + Password string `form:"password" validate:"required" label:"Password"` + Submission controller.FormSubmission } ) -func (l *Login) Get(c echo.Context) error { - p := controller.NewPage(c) - p.Layout = "auth" - p.Name = "login" - p.Title = "Log in" - p.Data = LoginForm{} +func (c *Login) Get(ctx echo.Context) error { + page := controller.NewPage(ctx) + page.Layout = "auth" + page.Name = "login" + page.Title = "Log in" + page.Form = LoginForm{} - if form := c.Get(context.FormKey); form != nil { - p.Data = form.(LoginForm) + if form := ctx.Get(context.FormKey); form != nil { + page.Form = form.(*LoginForm) } - return l.RenderPage(c, p) + return c.RenderPage(ctx, page) } -func (l *Login) Post(c echo.Context) error { - fail := func(message string, err error) error { - c.Logger().Errorf("%s: %v", message, err) - msg.Danger(c, "An error occurred. Please try again.") - return l.Get(c) +func (c *Login) Post(ctx echo.Context) error { + var form LoginForm + ctx.Set(context.FormKey, &form) + + authFailed := func() error { + form.Submission.SetFieldError("Email", "") + form.Submission.SetFieldError("Password", "") + msg.Danger(ctx, "Invalid credentials. Please try again.") + return c.Get(ctx) } // Parse the form values - var form LoginForm - if err := c.Bind(&form); err != nil { - return fail("unable to parse login form", err) + if err := ctx.Bind(&form); err != nil { + return c.Fail(ctx, err, "unable to parse login form") } - c.Set(context.FormKey, form) - // Validate the form - if err := c.Validate(form); err != nil { - l.SetValidationErrorMessages(c, err, form) - return l.Get(c) + if err := form.Submission.Process(ctx, form); err != nil { + return c.Fail(ctx, err, "unable to process form submission") + } + + if form.Submission.HasErrors() { + return c.Get(ctx) } // Attempt to load the user - u, err := l.Container.ORM.User. + u, err := c.Container.ORM.User. Query(). Where(user.Email(form.Email)). - Only(c.Request().Context()) + Only(ctx.Request().Context()) switch err.(type) { case *ent.NotFoundError: - msg.Danger(c, "Invalid credentials. Please try again.") - return l.Get(c) + return authFailed() case nil: default: - return fail("error querying user during login", err) + return c.Fail(ctx, err, "error querying user during login") } // Check if the password is correct - err = l.Container.Auth.CheckPassword(form.Password, u.Password) + err = c.Container.Auth.CheckPassword(form.Password, u.Password) if err != nil { - msg.Danger(c, "Invalid credentials. Please try again.") - return l.Get(c) + return authFailed() } // Log the user in - err = l.Container.Auth.Login(c, u.ID) + err = c.Container.Auth.Login(ctx, u.ID) if err != nil { - return fail("unable to log in user", err) + return c.Fail(ctx, err, "unable to log in user") } - msg.Success(c, fmt.Sprintf("Welcome back, %s. You are now logged in.", u.Name)) - return l.Redirect(c, "home") + msg.Success(ctx, fmt.Sprintf("Welcome back, %s. You are now logged in.", u.Name)) + return c.Redirect(ctx, "home") } diff --git a/templates/pages/contact.gohtml b/templates/pages/contact.gohtml index 8e80036..53ca9cc 100644 --- a/templates/pages/contact.gohtml +++ b/templates/pages/contact.gohtml @@ -26,17 +26,17 @@
- +
- {{template "field-errors" (.Form.Submission.GetFieldErrors "email")}} + {{template "field-errors" (.Form.Submission.GetFieldErrors "Email")}}
- +
- {{template "field-errors" (.Form.Submission.GetFieldErrors "message")}} + {{template "field-errors" (.Form.Submission.GetFieldErrors "Message")}}
diff --git a/templates/pages/login.gohtml b/templates/pages/login.gohtml index af18c6c..49747d5 100644 --- a/templates/pages/login.gohtml +++ b/templates/pages/login.gohtml @@ -3,13 +3,15 @@
- + + {{template "field-errors" (.Form.Submission.GetFieldErrors "Email")}}
- + + {{template "field-errors" (.Form.Submission.GetFieldErrors "Password")}}