diff --git a/controllers/blog.go b/controllers/blog.go index d95df4a..3b37652 100644 --- a/controllers/blog.go +++ b/controllers/blog.go @@ -27,14 +27,22 @@ import ( "gorm.io/gorm" ) +type CommentDto struct { + Url string +} + var ( - db *gorm.DB - apiKey string + db *gorm.DB + apiKey string + recentPosters map[string]uint ) func RegisterBlogGroup(r *gin.RouterGroup) { + // Initialize recent posters cache + recentPosters = make(map[string]uint) // Load dsn and initialize database dsn := utils.LoadDSN("dsn") + // Load and initialize api key apiKey = utils.LoadApiKey("api_key.pem") var err error db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{}) @@ -46,6 +54,8 @@ func RegisterBlogGroup(r *gin.RouterGroup) { r.GET("/post/:url", RenderIndividualBlogPost) r.POST("/post", CreateBlogPost) r.POST("/post/comment", CreateComment) + // Clear the commenters cache every 15 minutes + go utils.TimedClearMap(&recentPosters, 180000*5) } func RenderBlogPage(c *gin.Context) { @@ -103,6 +113,10 @@ func CreateComment(c *gin.Context) { a := []string{c.Request.Form.Get("author"), c.Request.Form.Get("content"), c.Request.Form.Get("url")} + if GetNumberOfRecentPosts(c) > 3 { + c.HTML(http.StatusOK, "comment_post_spam.tmpl", CommentDto{Url: a[2]}) + return + } for _, v := range a { if len(v) == 0 { c.Status(http.StatusNotAcceptable) @@ -115,8 +129,13 @@ func CreateComment(c *gin.Context) { AssociatedPost: a[2], } db.Create(&comment) + recentPosters[c.ClientIP()]++ // Pass the associated post to the template to add to the href - c.HTML(http.StatusOK, "comment_post.tmpl", struct{ Url string }{Url: comment.AssociatedPost}) + c.HTML(http.StatusOK, "comment_post.tmpl", CommentDto{Url: comment.AssociatedPost}) +} + +func GetNumberOfRecentPosts(c *gin.Context) uint { + return recentPosters[c.ClientIP()] } func GetAllBlogPosts() *[]models.BlogPost { diff --git a/templates/comment_post.tmpl b/templates/comment_post.tmpl index 799b080..84a6168 100644 --- a/templates/comment_post.tmpl +++ b/templates/comment_post.tmpl @@ -1,5 +1,11 @@ {{ template "header.tmpl" . }}

Your comment is being posted...

-

Click here to return to the blog post.

+

You will be redirected or, click here to return to the blog post.

+ + diff --git a/templates/comment_post_spam.tmpl b/templates/comment_post_spam.tmpl new file mode 100644 index 0000000..b5ea891 --- /dev/null +++ b/templates/comment_post_spam.tmpl @@ -0,0 +1,5 @@ +{{ template "header.tmpl" . }} +

You've commented too many times, too quickly. Your comment will not be posted.

+

Click here to return to the post.

+ + diff --git a/utils/timed_clear.go b/utils/timed_clear.go new file mode 100644 index 0000000..2ee4536 --- /dev/null +++ b/utils/timed_clear.go @@ -0,0 +1,19 @@ +package utils + +import ( + "strconv" + "time" +) + +func TimedClearMap(m *map[string]uint, timer uint) { + for { + t, err := time.ParseDuration(strconv.FormatUint(uint64(timer), 10) + "ms") + if err != nil { + panic("Something went wrong initializing time!!") + } + time.Sleep(t) + for key := range *m { + delete(*m, key) + } + } +}