comment.js 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. const md = require('markdown-it')
  2. const mdEmoji = require('markdown-it-emoji')
  3. const { JSDOM } = require('jsdom')
  4. const createDOMPurify = require('dompurify')
  5. const _ = require('lodash')
  6. const { AkismetClient } = require('akismet-api')
  7. const moment = require('moment')
  8. const window = new JSDOM('').window
  9. const DOMPurify = createDOMPurify(window)
  10. let akismetClient = null
  11. const mkdown = md({
  12. html: false,
  13. breaks: true,
  14. linkify: true,
  15. highlight(str, lang) {
  16. return `<pre><code class="language-${lang}">${_.escape(str)}</code></pre>`
  17. }
  18. })
  19. mkdown.use(mdEmoji)
  20. // ------------------------------------
  21. // Default Comment Provider
  22. // ------------------------------------
  23. module.exports = {
  24. /**
  25. * Init
  26. */
  27. async init (config) {
  28. WIKI.logger.info('(COMMENTS/DEFAULT) Initializing...')
  29. if (WIKI.data.commentProvider.config.akismet && WIKI.data.commentProvider.config.akismet.length > 2) {
  30. akismetClient = new AkismetClient({
  31. key: WIKI.data.commentProvider.config.akismet,
  32. blog: WIKI.config.host,
  33. lang: WIKI.config.lang.namespacing ? WIKI.config.lang.namespaces.join(', ') : WIKI.config.lang.code,
  34. charset: 'UTF-8'
  35. })
  36. try {
  37. const isValid = await akismetClient.verifyKey()
  38. if (!isValid) {
  39. akismetClient = null
  40. WIKI.logger.warn('(COMMENTS/DEFAULT) Akismet Key is invalid! [ DISABLED ]')
  41. } else {
  42. WIKI.logger.info('(COMMENTS/DEFAULT) Akismet key is valid. [ OK ]')
  43. }
  44. } catch (err) {
  45. akismetClient = null
  46. WIKI.logger.warn('(COMMENTS/DEFAULT) Unable to verify Akismet Key: ' + err.message)
  47. }
  48. } else {
  49. akismetClient = null
  50. }
  51. WIKI.logger.info('(COMMENTS/DEFAULT) Initialization completed.')
  52. },
  53. /**
  54. * Create New Comment
  55. */
  56. async create ({ page, replyTo, content, user }) {
  57. // -> Build New Comment
  58. const newComment = {
  59. content,
  60. render: DOMPurify.sanitize(mkdown.render(content)),
  61. replyTo,
  62. pageId: page.id,
  63. authorId: user.id,
  64. name: user.name,
  65. email: user.email,
  66. ip: user.ip
  67. }
  68. // -> Check for Spam with Akismet
  69. if (akismetClient) {
  70. let userRole = 'user'
  71. if (user.groups.indexOf(1) >= 0) {
  72. userRole = 'administrator'
  73. } else if (user.groups.indexOf(2) >= 0) {
  74. userRole = 'guest'
  75. }
  76. let isSpam = false
  77. try {
  78. isSpam = await akismetClient.checkSpam({
  79. ip: user.ip,
  80. useragent: user.agentagent,
  81. content,
  82. name: user.name,
  83. email: user.email,
  84. permalink: `${WIKI.config.host}/${page.locale}/${page.path}`,
  85. permalinkDate: page.updatedAt,
  86. type: (replyTo > 0) ? 'reply' : 'comment',
  87. role: userRole
  88. })
  89. } catch (err) {
  90. WIKI.logger.warn('Akismet Comment Validation: [ FAILED ]')
  91. WIKI.logger.warn(err)
  92. }
  93. if (isSpam) {
  94. throw new Error('Comment was rejected because it is marked as spam.')
  95. }
  96. }
  97. // -> Check for minimum delay between posts
  98. if (WIKI.data.commentProvider.config.minDelay > 0) {
  99. const lastComment = await WIKI.db.comments.query().select('updatedAt').findOne('authorId', user.id).orderBy('updatedAt', 'desc')
  100. if (lastComment && moment().subtract(WIKI.data.commentProvider.config.minDelay, 'seconds').isBefore(lastComment.updatedAt)) {
  101. throw new Error('Your administrator has set a time limit before you can post another comment. Try again later.')
  102. }
  103. }
  104. // -> Save Comment to DB
  105. const cm = await WIKI.db.comments.query().insert(newComment)
  106. // -> Return Comment ID
  107. return cm.id
  108. },
  109. /**
  110. * Update an existing comment
  111. */
  112. async update ({ id, content, user }) {
  113. const renderedContent = DOMPurify.sanitize(mkdown.render(content))
  114. await WIKI.db.comments.query().findById(id).patch({
  115. render: renderedContent
  116. })
  117. return renderedContent
  118. },
  119. /**
  120. * Delete an existing comment by ID
  121. */
  122. async remove ({ id, user }) {
  123. return WIKI.db.comments.query().findById(id).delete()
  124. },
  125. /**
  126. * Get the page ID from a comment ID
  127. */
  128. async getPageIdFromCommentId (id) {
  129. const result = await WIKI.db.comments.query().select('pageId').findById(id)
  130. return (result) ? result.pageId : false
  131. },
  132. /**
  133. * Get a comment by ID
  134. */
  135. async getCommentById (id) {
  136. return WIKI.db.comments.query().findById(id)
  137. },
  138. /**
  139. * Get the total comments count for a page ID
  140. */
  141. async count (pageId) {
  142. const result = await WIKI.db.comments.query().count('* as total').where('pageId', pageId).first()
  143. return _.toSafeInteger(result.total)
  144. }
  145. }