From f8e36551ea6181a81bbe0a4967a34364e15e419a Mon Sep 17 00:00:00 2001 From: Aleksandr Melnikov Date: Sat, 24 Nov 2018 00:47:42 +0100 Subject: [PATCH] added maximum votes limit per comment (#223) * added maximum votes limit per comment * added 'max-votes' param description into README --- README.md | 1 + backend/app/cmd/server.go | 2 ++ backend/app/store/service/service.go | 11 +++++++++++ backend/app/store/service/service_test.go | 18 ++++++++++++++++++ 4 files changed, 32 insertions(+) diff --git a/README.md b/README.md index 33dd626a..84776d78 100644 --- a/README.md +++ b/README.md @@ -124,6 +124,7 @@ _this is the recommended way to run remark42_ | ssl.cert | SSL_CERT | | path to cert.pem file | | ssl.key | SSL_KEY | | path to key.pem file | | max-comment | MAX_COMMENT_SIZE | 2048 | comment's size limit | +| max-votes | MAX_VOTES | `-1` | votes limit per comment, `-1` - unlimited | | low-score | LOW_SCORE | `-5` | low score threshold | | critical-score | CRITICAL_SCORE | `-10` | critical score threshold | | edit-time | EDIT_TIME | `5m` | edit window | diff --git a/backend/app/cmd/server.go b/backend/app/cmd/server.go index d43d8e8e..ef6930c3 100644 --- a/backend/app/cmd/server.go +++ b/backend/app/cmd/server.go @@ -45,6 +45,7 @@ type ServerCommand struct { MaxBackupFiles int `long:"max-back" env:"MAX_BACKUP_FILES" default:"10" description:"max backups to keep"` ImageProxy bool `long:"img-proxy" env:"IMG_PROXY" description:"enable image proxy"` MaxCommentSize int `long:"max-comment" env:"MAX_COMMENT_SIZE" default:"2048" description:"max comment size"` + MaxVotes int `long:"max-votes" env:"MAX_VOTES" default:"-1" description:"maximum number of votes per comment"` LowScore int `long:"low-score" env:"LOW_SCORE" default:"-5" description:"low score threshold"` CriticalScore int `long:"critical-score" env:"CRITICAL_SCORE" default:"-10" description:"critical score threshold"` ReadOnlyAge int `long:"read-age" env:"READONLY_AGE" default:"0" description:"read-only age of comments"` @@ -206,6 +207,7 @@ func (s *ServerCommand) newServerApp() (*serverApp, error) { EditDuration: s.EditDuration, AdminStore: adminStore, MaxCommentSize: s.MaxCommentSize, + MaxVotes: s.MaxVotes, } loadingCache, err := s.makeCache() diff --git a/backend/app/store/service/service.go b/backend/app/store/service/service.go index 2d0ae63a..876622d8 100644 --- a/backend/app/store/service/service.go +++ b/backend/app/store/service/service.go @@ -18,6 +18,7 @@ type DataStore struct { EditDuration time.Duration AdminStore admin.Store MaxCommentSize int + MaxVotes int // granular locks scopedLocks struct { @@ -28,6 +29,7 @@ type DataStore struct { } const defaultCommentMaxSize = 2000 +const defaultVotesLimit = -1 // unlimited // Create prepares comment and forward to Interface.Create func (s *DataStore) Create(comment store.Comment) (commentID string, err error) { @@ -97,6 +99,15 @@ func (s *DataStore) Vote(locator store.Locator, commentID string, userID string, return comment, errors.Errorf("user %s already voted for %s", userID, commentID) } + maxVotes := s.MaxVotes + if s.MaxVotes <= 0 { + maxVotes = defaultVotesLimit + } + + if maxVotes >= 0 && len(comment.Votes) >= maxVotes { + return comment, errors.Errorf("maximum number of votes exceeded for comment %s", commentID) + } + // reset vote if user changed to opposite if voted && v != val { delete(comment.Votes, userID) diff --git a/backend/app/store/service/service_test.go b/backend/app/store/service/service_test.go index 55442340..99fcef35 100644 --- a/backend/app/store/service/service_test.go +++ b/backend/app/store/service/service_test.go @@ -116,6 +116,24 @@ func TestService_Vote(t *testing.T) { assert.Equal(t, map[string]bool{}, res[0].Votes, "vote reset ok") } +func TestService_VoteLimit(t *testing.T) { + defer os.Remove(testDb) + b := DataStore{Interface: prepStoreEngine(t), AdminStore: admin.NewStaticKeyStore("secret 123"), MaxVotes: 2} + + _, err := b.Vote(store.Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, "id-1", "user2", true) + assert.Nil(t, err) + + _, err = b.Vote(store.Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, "id-1", "user3", true) + assert.Nil(t, err) + + _, err = b.Vote(store.Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, "id-1", "user4", true) + assert.NotNil(t, err, "vote limit reached") + assert.True(t, strings.HasPrefix(err.Error(), "maximum number of votes exceeded for comment id-1")) + + _, err = b.Vote(store.Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, "id-2", "user4", true) + assert.Nil(t, err) +} + func TestService_VoteAggressive(t *testing.T) { defer os.Remove(testDb) b := DataStore{Interface: prepStoreEngine(t), AdminStore: admin.NewStaticKeyStore("secret 123")}