Переглянути джерело

feat: comments post min delay

NGPixel 5 роки тому
батько
коміт
e74605501f

+ 120 - 100
client/components/comments.vue

@@ -131,40 +131,51 @@ export default {
   methods: {
     onIntersect (entries, observer, isIntersecting) {
       if (isIntersecting) {
-        this.fetch()
+        this.fetch(true)
       }
     },
-    async fetch () {
+    async fetch (silent = false) {
       this.isLoading = true
-      const results = await this.$apollo.query({
-        query: gql`
-          query ($locale: String!, $path: String!) {
-            comments {
-              list(locale: $locale, path: $path) {
-                id
-                render
-                authorName
-                createdAt
-                updatedAt
+      try {
+        const results = await this.$apollo.query({
+          query: gql`
+            query ($locale: String!, $path: String!) {
+              comments {
+                list(locale: $locale, path: $path) {
+                  id
+                  render
+                  authorName
+                  createdAt
+                  updatedAt
+                }
               }
             }
+          `,
+          variables: {
+            locale: this.$store.get('page/locale'),
+            path: this.$store.get('page/path')
+          },
+          fetchPolicy: 'network-only'
+        })
+        this.comments = _.get(results, 'data.comments.list', []).map(c => {
+          const nameParts = c.authorName.toUpperCase().split(' ')
+          let initials = _.head(nameParts).charAt(0)
+          if (nameParts.length > 1) {
+            initials += _.last(nameParts).charAt(0)
           }
-        `,
-        variables: {
-          locale: this.$store.get('page/locale'),
-          path: this.$store.get('page/path')
-        },
-        fetchPolicy: 'network-only'
-      })
-      this.comments = _.get(results, 'data.comments.list', []).map(c => {
-        const nameParts = c.authorName.toUpperCase().split(' ')
-        let initials = _.head(nameParts).charAt(0)
-        if (nameParts.length > 1) {
-          initials += _.last(nameParts).charAt(0)
+          c.initials = initials
+          return c
+        })
+      } catch (err) {
+        console.warn(err)
+        if (!silent) {
+          this.$store.commit('showNotification', {
+            style: 'red',
+            message: err.message,
+            icon: 'alert'
+          })
         }
-        c.initials = initials
-        return c
-      })
+      }
       this.isLoading = false
       this.hasLoadedOnce = true
     },
@@ -214,59 +225,63 @@ export default {
         return
       }
 
-      const resp = await this.$apollo.mutate({
-        mutation: gql`
-          mutation (
-            $pageId: Int!
-            $replyTo: Int
-            $content: String!
-            $guestName: String
-            $guestEmail: String
-          ) {
-            comments {
-              create (
-                pageId: $pageId
-                replyTo: $replyTo
-                content: $content
-                guestName: $guestName
-                guestEmail: $guestEmail
-              ) {
-                responseResult {
-                  succeeded
-                  errorCode
-                  slug
-                  message
+      try {
+        const resp = await this.$apollo.mutate({
+          mutation: gql`
+            mutation (
+              $pageId: Int!
+              $replyTo: Int
+              $content: String!
+              $guestName: String
+              $guestEmail: String
+            ) {
+              comments {
+                create (
+                  pageId: $pageId
+                  replyTo: $replyTo
+                  content: $content
+                  guestName: $guestName
+                  guestEmail: $guestEmail
+                ) {
+                  responseResult {
+                    succeeded
+                    errorCode
+                    slug
+                    message
+                  }
+                  id
                 }
-                id
               }
             }
+          `,
+          variables: {
+            pageId: this.pageId,
+            replyTo: 0,
+            content: this.newcomment,
+            guestName: this.guestName,
+            guestEmail: this.guestEmail
           }
-        `,
-        variables: {
-          pageId: this.pageId,
-          replyTo: 0,
-          content: this.newcomment,
-          guestName: this.guestName,
-          guestEmail: this.guestEmail
-        }
-      })
-
-      if (_.get(resp, 'data.comments.create.responseResult.succeeded', false)) {
-        this.$store.commit('showNotification', {
-          style: 'success',
-          message: 'New comment posted successfully.',
-          icon: 'check'
         })
 
-        this.newcomment = ''
-        await this.fetch()
-        this.$nextTick(() => {
-          this.$vuetify.goTo(`#comment-post-id-${_.get(resp, 'data.comments.create.id', 0)}`, this.scrollOpts)
-        })
-      } else {
+        if (_.get(resp, 'data.comments.create.responseResult.succeeded', false)) {
+          this.$store.commit('showNotification', {
+            style: 'success',
+            message: 'New comment posted successfully.',
+            icon: 'check'
+          })
+
+          this.newcomment = ''
+          await this.fetch()
+          this.$nextTick(() => {
+            this.$vuetify.goTo(`#comment-post-id-${_.get(resp, 'data.comments.create.id', 0)}`, this.scrollOpts)
+          })
+        } else {
+          throw new Error(_.get(resp, 'data.comments.create.responseResult.message', 'An unexpected error occured.'))
+        }
+      } catch (err) {
         this.$store.commit('showNotification', {
           style: 'red',
-          message: _.get(resp, 'data.comments.create.responseResult.message', 'An unexpected error occured.'),
+          message: err.message,
           icon: 'alert'
         })
       }
@@ -286,42 +301,46 @@ export default {
       this.isBusy = true
       this.deleteCommentDialogShown = false
 
-      const resp = await this.$apollo.mutate({
-        mutation: gql`
-          mutation (
-            $id: Int!
-          ) {
-            comments {
-              delete (
-                id: $id
-              ) {
-                responseResult {
-                  succeeded
-                  errorCode
-                  slug
-                  message
+      try {
+        const resp = await this.$apollo.mutate({
+          mutation: gql`
+            mutation (
+              $id: Int!
+            ) {
+              comments {
+                delete (
+                  id: $id
+                ) {
+                  responseResult {
+                    succeeded
+                    errorCode
+                    slug
+                    message
+                  }
                 }
               }
             }
+          `,
+          variables: {
+            id: this.commentToDelete.id
           }
-        `,
-        variables: {
-          id: this.commentToDelete.id
-        }
-      })
-
-      if (_.get(resp, 'data.comments.delete.responseResult.succeeded', false)) {
-        this.$store.commit('showNotification', {
-          style: 'success',
-          message: 'Comment was deleted successfully.',
-          icon: 'check'
         })
 
-        this.comments = _.reject(this.comments, ['id', this.commentToDelete.id])
-      } else {
+        if (_.get(resp, 'data.comments.delete.responseResult.succeeded', false)) {
+          this.$store.commit('showNotification', {
+            style: 'success',
+            message: 'Comment was deleted successfully.',
+            icon: 'check'
+          })
+
+          this.comments = _.reject(this.comments, ['id', this.commentToDelete.id])
+        } else {
+          throw new Error(_.get(resp, 'data.comments.delete.responseResult.message', 'An unexpected error occured.'))
+        }
+      } catch (err) {
         this.$store.commit('showNotification', {
           style: 'red',
-          message: _.get(resp, 'data.comments.delete.responseResult.message', 'An unexpected error occured.'),
+          message: err.message,
           icon: 'alert'
         })
       }
@@ -362,6 +381,7 @@ export default {
 
     img {
       max-width: 100%;
+      border-radius: 5px;
     }
 
     code {

+ 1 - 1
server/graph/schemas/comment.graphql

@@ -42,7 +42,7 @@ type CommentMutation {
     content: String!
     guestName: String
     guestEmail: String
-  ): CommentCreateResponse @auth(requires: ["write:comments", "manage:system"])
+  ): CommentCreateResponse @auth(requires: ["write:comments", "manage:system"]) @rateLimit(limit: 1, duration: 15)
 
   update(
     id: Int!

+ 9 - 0
server/modules/comments/default/comment.js

@@ -4,6 +4,7 @@ const { JSDOM } = require('jsdom')
 const createDOMPurify = require('dompurify')
 const _ = require('lodash')
 const { AkismetClient } = require('akismet-api')
+const moment = require('moment')
 
 /* global WIKI */
 
@@ -106,6 +107,14 @@ module.exports = {
       }
     }
 
+    // -> Check for minimum delay between posts
+    if (WIKI.data.commentProvider.config.minDelay > 0) {
+      const lastComment = await WIKI.models.comments.query().select('updatedAt').findOne('authorId', user.id).orderBy('updatedAt', 'desc')
+      if (lastComment && moment().subtract(WIKI.data.commentProvider.config.minDelay, 'seconds').isBefore(lastComment.updatedAt)) {
+        throw new Error('Your administrator has set a time limit before you can post another comment. Try again later.')
+      }
+    }
+
     // -> Save Comment to DB
     const cm = await WIKI.models.comments.query().insert(newComment)
 

+ 2 - 1
server/modules/comments/default/definition.yml

@@ -12,11 +12,12 @@ props:
     title: Akismet API Key
     default: ''
     hint: 'Prevent spam by using the Akismet service. Enter your API key here to enable. Leave empty to disable.'
+    maxWidth: 650
     order: 1
   minDelay:
     type: Number
     title: Post delay
     default: 30
-    hint: 'Minimum delay (in seconds) between comments per IP address.'
+    hint: 'Minimum delay (in seconds) between comments per account. Note that all guests are considered as a single account.'
     maxWidth: 400
     order: 2