diff --git a/go.mod b/go.mod index 3712119..a4c1147 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/antchfx/htmlquery v1.2.5 github.com/gin-contrib/multitemplate v0.0.0-20220705015713-e21a0ba39de3 github.com/gin-gonic/gin v1.8.1 + github.com/go-playground/validator/v10 v10.11.0 github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1 github.com/spf13/viper v1.12.0 golang.org/x/net v0.0.0-20220722155237-a158d28d115b @@ -19,7 +20,6 @@ require ( github.com/gin-contrib/sse v0.1.0 // indirect github.com/go-playground/locales v0.14.0 // indirect github.com/go-playground/universal-translator v0.18.0 // indirect - github.com/go-playground/validator/v10 v10.11.0 // indirect github.com/goccy/go-json v0.9.10 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/hashicorp/hcl v1.0.0 // indirect diff --git a/main.go b/main.go index 647157d..f3d507f 100644 --- a/main.go +++ b/main.go @@ -38,17 +38,12 @@ func (web Web) newWatch(c *gin.Context) { } func (web Web) createWatch(c *gin.Context) { - interval, err := strconv.Atoi(c.PostForm("interval")) + var watch Watch + errMap, err := bindAndValidateWatch(&watch, c) if err != nil { - c.Redirect(http.StatusSeeOther, "/watch/new") - return + c.HTML(http.StatusBadRequest, "500", errMap) } - - watch := &Watch{ - Name: c.PostForm("name"), - Interval: interval, - } - web.db.Create(watch) + web.db.Create(&watch) c.Redirect(http.StatusSeeOther, fmt.Sprintf("/watch/view/%d", watch.ID)) } @@ -72,29 +67,15 @@ func (web Web) viewWatch(c *gin.Context) { } func (web Web) createURL(c *gin.Context) { - watch_id, err := strconv.ParseUint(c.PostForm("watch_id"), 10, 64) + var url URL + errMap, err := bindAndValidateURL(&url, c) if err != nil { - c.Redirect(http.StatusSeeOther, "/watch/new") - return // TODO response - } - name := c.PostForm("name") - if name == "" { - c.Redirect(http.StatusSeeOther, "/watch/new") + log.Print(err) + c.HTML(http.StatusInternalServerError, "500", errMap) return } - url := c.PostForm("url") - if url == "" { - c.Redirect(http.StatusSeeOther, "/watch/new") - return - } - - url_model := &URL{ - WatchID: uint(watch_id), - Name: name, - URL: url, - } - web.db.Create(url_model) - c.Redirect(http.StatusSeeOther, fmt.Sprintf("/watch/view/%d", watch_id)) + web.db.Create(url) + c.Redirect(http.StatusSeeOther, fmt.Sprintf("/watch/view/%d", url.WatchID)) } func (web Web) createQuery(c *gin.Context) { @@ -253,6 +234,8 @@ func main() { templates.AddFromFiles("newWatch", "templates/base.html", "templates/newWatch.html") templates.AddFromFiles("viewWatch", "templates/base.html", "templates/viewWatch.html") templates.AddFromFiles("editQuery", "templates/base.html", "templates/editQuery.html") + + templates.AddFromFiles("500", "templates/base.html", "templates/500.html") router.HTMLRender = templates router.GET("/", web.index) diff --git a/models.go b/models.go index 1ca9e9f..ff88d1a 100644 --- a/models.go +++ b/models.go @@ -6,36 +6,36 @@ import ( type Watch struct { gorm.Model - Name string - Interval int + Name string `form:"name" yaml:"name" binding:"required" validate:"min=1"` + Interval int `form:"interval" yaml:"interval" binding:"required"` URLs []URL } type URL struct { gorm.Model - WatchID uint - Watch Watch - Name string - URL string + WatchID uint `form:"watch_id" yaml:"watch_id" binding:"required"` + Watch *Watch `form:"watch" yaml:"watch" validate:"omitempty"` + Name string `form:"name" yaml:"name" binding:"required"` + URL string `form:"url" yaml:"url" binding:"required,url"` Queries []Query } type Query struct { gorm.Model - URLID uint + URLID uint `form:"url_id" yaml:"url_id" binding:"required"` URL URL - Name string - Type string - Query string + Name string `form:"name" yaml:"name" binding:"required"` + Type string `form:"type" yaml:"type" binding:"required"` + Query string `form:"query" yaml:"query" binding:"required"` Filters []Filter } type Filter struct { gorm.Model - QueryID uint + QueryID uint `form:"query_id" yaml:"query_id" binding:"required"` Query Query - Name string - Type string - From string - To string + Name string `form:"name" yaml:"name" binding:"required"` + Type string `form:"type" yaml:"type" binding:"required"` + From string `form:"from" yaml:"from" binding:"required"` + To string `form:"to" yaml:"to" binding:"required"` } diff --git a/templates/500.html b/templates/500.html new file mode 100644 index 0000000..fb8268e --- /dev/null +++ b/templates/500.html @@ -0,0 +1,3 @@ +{{define "content"}} +

Something went wrong, try again.

+{{end}} \ No newline at end of file diff --git a/templates/newWatch.html b/templates/newWatch.html index 1938519..052a24e 100644 --- a/templates/newWatch.html +++ b/templates/newWatch.html @@ -1,12 +1,3 @@ -{{template "base" .}} -{{define "title"}} -{{end}} -{{define "navbar"}} -{{end}} -{{define "head"}} -{{end}} -{{define "left"}} -{{end}} {{define "content"}}
@@ -19,6 +10,5 @@
-{{end}} -{{define "right"}} +{{ . }} {{end}} \ No newline at end of file diff --git a/util.go b/util.go new file mode 100644 index 0000000..e61c596 --- /dev/null +++ b/util.go @@ -0,0 +1,40 @@ +package main + +import ( + "errors" + + "github.com/gin-gonic/gin" + "github.com/go-playground/validator/v10" +) + +func bindAndValidateWatch(watch *Watch, c *gin.Context) (map[string]string, error) { + err := c.ShouldBind(watch) + return validate(err), err +} + +func bindAndValidateURL(url *URL, c *gin.Context) (map[string]string, error) { + err := c.ShouldBind(url) + return validate(err), err +} + +func prettyError(fieldError validator.FieldError) string { + switch fieldError.Tag() { + case "required": + return fieldError.Field() + " is required" + default: + return "No prettyError for " + fieldError.Tag() + } +} + +func validate(err error) map[string]string { + out := make(map[string]string) + if err != nil { + var ve validator.ValidationErrors + if errors.As(err, &ve) { + for _, fe := range ve { + out[fe.Field()] = prettyError(fe) + } + } + } + return out +}