You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

159 lines
4.6 KiB

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