11package svc
22
33import (
4+ "bytes"
45 "context"
56 "errors"
67 "fmt"
78 "mime/multipart"
89 "path/filepath"
910 "time"
1011
12+ "github.com/Narasimha1997/ratelimiter"
1113 "github.com/chaitin/koalaqa/model"
1214 "github.com/chaitin/koalaqa/pkg/glog"
1315 "github.com/chaitin/koalaqa/pkg/mq"
@@ -41,6 +43,7 @@ type Discussion struct {
4143
4244 logger * glog.Logger
4345 webhookType map [model.DiscussionType ]message.Type
46+ limiter * ratelimiter.AttributeBasedLimiter
4447}
4548
4649func newDiscussion (in discussionIn ) * Discussion {
@@ -52,6 +55,7 @@ func newDiscussion(in discussionIn) *Discussion {
5255 model .DiscussionTypeFeedback : message .TypeNewFeedback ,
5356 model .DiscussionTypeBlog : message .TypeNewBlog ,
5457 },
58+ limiter : ratelimiter .NewAttributeBasedLimiter (false ),
5559 }
5660}
5761
@@ -73,7 +77,30 @@ func (d *Discussion) generateUUID() string {
7377 return util .RandomString (16 )
7478}
7579
80+ func (d * Discussion ) limitKey (args ... any ) string {
81+ if len (args ) == 0 {
82+ return ""
83+ }
84+
85+ buff := bytes .NewBufferString ("%v" )
86+ for i := 1 ; i < len (args ); i ++ {
87+ buff .WriteString ("-%v" )
88+ }
89+
90+ return fmt .Sprintf (buff .String (), args ... )
91+ }
92+
93+ func (d * Discussion ) allow (args ... any ) bool {
94+ return d .limiter .MustShouldAllow (d .limitKey (args ... ), 1 , 3 , time .Minute )
95+ }
96+
97+ var errRatelimit = errors .New ("ratelimit" )
98+
7699func (d * Discussion ) Create (ctx context.Context , req DiscussionCreateReq ) (string , error ) {
100+ if ! d .allow ("discussion" , req .UserID ) {
101+ return "" , errRatelimit
102+ }
103+
77104 if len (req .GroupIDs ) > 0 {
78105 err := d .in .GroupItemRepo .FilterIDs (ctx , & req .GroupIDs )
79106 if err != nil {
@@ -292,7 +319,7 @@ func (d *Discussion) IncrementComment(uuid string, updateTime bool) {
292319func (d * Discussion ) DecrementComment (uuid string ) {
293320 ctx := context .Background ()
294321 d .in .DiscRepo .Update (ctx , map [string ]any {
295- "comment" : gorm .Expr ("comment-1 " ),
322+ "comment" : gorm .Expr ("CASE WHEN comment>0 THEN comment-1 END " ),
296323 }, repo .QueryWithEqual ("uuid" , uuid ))
297324
298325 go d .RecalculateHot (uuid )
@@ -321,6 +348,10 @@ func (d *Discussion) RecalculateHot(uuid string) {
321348}
322349
323350func (d * Discussion ) LikeDiscussion (ctx context.Context , discUUID string , user model.UserInfo ) error {
351+ if ! d .allow ("like" , discUUID , user .UID ) {
352+ return errRatelimit
353+ }
354+
324355 if err := d .in .DiscRepo .LikeDiscussion (ctx , discUUID , user .UID ); err != nil {
325356 return err
326357 }
@@ -344,6 +375,10 @@ func (d *Discussion) LikeDiscussion(ctx context.Context, discUUID string, user m
344375}
345376
346377func (d * Discussion ) RevokeLikeDiscussion (ctx context.Context , discUUID string , uid uint ) error {
378+ if ! d .allow ("like" , discUUID , uid ) {
379+ return errRatelimit
380+ }
381+
347382 if err := d .in .DiscRepo .RevokeLikeDiscussion (ctx , discUUID , uid ); err != nil {
348383 return err
349384 }
@@ -406,6 +441,12 @@ type CommentCreateReq struct {
406441}
407442
408443func (d * Discussion ) CreateComment (ctx context.Context , uid uint , discUUID string , req CommentCreateReq ) (uint , error ) {
444+ if ! req .Bot {
445+ if ! d .allow ("comment" , discUUID , uid ) {
446+ return 0 , errRatelimit
447+ }
448+ }
449+
409450 disc , err := d .in .DiscRepo .GetByUUID (ctx , discUUID )
410451 if err != nil {
411452 return 0 , err
@@ -568,6 +609,10 @@ func (d *Discussion) UploadFile(ctx context.Context, req DiscussUploadFileReq) (
568609}
569610
570611func (d * Discussion ) AcceptComment (ctx context.Context , user model.UserInfo , discUUID string , commentID uint ) error {
612+ if ! d .allow ("accept" , discUUID , user .UID ) {
613+ return errRatelimit
614+ }
615+
571616 disc , err := d .in .DiscRepo .GetByUUID (ctx , discUUID )
572617 if err != nil {
573618 return err
@@ -645,6 +690,10 @@ func (d *Discussion) AcceptComment(ctx context.Context, user model.UserInfo, dis
645690}
646691
647692func (d * Discussion ) LikeComment (ctx context.Context , userInfo model.UserInfo , discUUID string , commentID uint ) error {
693+ if ! d .allow ("like" , discUUID , userInfo .UID ) {
694+ return errRatelimit
695+ }
696+
648697 disc , err := d .in .DiscRepo .GetByUUID (ctx , discUUID )
649698 if err != nil {
650699 return err
@@ -682,6 +731,10 @@ func (d *Discussion) LikeComment(ctx context.Context, userInfo model.UserInfo, d
682731}
683732
684733func (d * Discussion ) DislikeComment (ctx context.Context , userInfo model.UserInfo , discUUID string , commentID uint ) error {
734+ if ! d .allow ("like" , discUUID , userInfo .UID ) {
735+ return errRatelimit
736+ }
737+
685738 disc , err := d .in .DiscRepo .GetByUUID (ctx , discUUID )
686739 if err != nil {
687740 return err
@@ -718,7 +771,11 @@ func (d *Discussion) DislikeComment(ctx context.Context, userInfo model.UserInfo
718771 return nil
719772}
720773
721- func (d * Discussion ) RevokeLike (ctx context.Context , uid uint , commentID uint ) error {
774+ func (d * Discussion ) RevokeLike (ctx context.Context , uid uint , discUUID string , commentID uint ) error {
775+ if ! d .allow ("like" , discUUID , uid ) {
776+ return errRatelimit
777+ }
778+
722779 ok , err := d .in .CommRepo .ExistByID (ctx , commentID )
723780 if err != nil {
724781 return err
0 commit comments