comment.js 4.63 KB
Newer Older
1 2 3 4 5 6
const md = require('markdown-it')
const mdEmoji = require('markdown-it-emoji')
const { JSDOM } = require('jsdom')
const createDOMPurify = require('dompurify')
const _ = require('lodash')
const { AkismetClient } = require('akismet-api')
7
const moment = require('moment')
8

9 10
/* global WIKI */

11 12 13 14 15
const window = new JSDOM('').window
const DOMPurify = createDOMPurify(window)

let akismetClient = null

NGPixel's avatar
NGPixel committed
16 17 18 19 20 21 22 23 24 25 26
const mkdown = md({
  html: false,
  breaks: true,
  linkify: true,
  highlight(str, lang) {
    return `<pre><code class="language-${lang}">${_.escape(str)}</code></pre>`
  }
})

mkdown.use(mdEmoji)

27 28 29 30 31
// ------------------------------------
// Default Comment Provider
// ------------------------------------

module.exports = {
32 33 34 35
  /**
   * Init
   */
  async init (config) {
36
    WIKI.logger.info('(COMMENTS/DEFAULT) Initializing...')
37 38 39 40 41 42 43 44 45 46
    if (WIKI.data.commentProvider.config.akismet && WIKI.data.commentProvider.config.akismet.length > 2) {
      akismetClient = new AkismetClient({
        key: WIKI.data.commentProvider.config.akismet,
        blog: WIKI.config.host,
        lang: WIKI.config.lang.namespacing ? WIKI.config.lang.namespaces.join(', ') : WIKI.config.lang.code,
        charset: 'UTF-8'
      })
      try {
        const isValid = await akismetClient.verifyKey()
        if (!isValid) {
47 48 49 50
          akismetClient = null
          WIKI.logger.warn('(COMMENTS/DEFAULT) Akismet Key is invalid! [ DISABLED ]')
        } else {
          WIKI.logger.info('(COMMENTS/DEFAULT) Akismet key is valid. [ OK ]')
51 52
        }
      } catch (err) {
53 54
        akismetClient = null
        WIKI.logger.warn('(COMMENTS/DEFAULT) Unable to verify Akismet Key: ' + err.message)
55 56 57 58
      }
    } else {
      akismetClient = null
    }
59
    WIKI.logger.info('(COMMENTS/DEFAULT) Initialization completed.')
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
  },
  /**
   * Create New Comment
   */
  async create ({ page, replyTo, content, user }) {
    // -> Build New Comment
    const newComment = {
      content,
      render: DOMPurify.sanitize(mkdown.render(content)),
      replyTo,
      pageId: page.id,
      authorId: user.id,
      name: user.name,
      email: user.email,
      ip: user.ip
    }

77
    // -> Check for Spam with Akismet
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
    if (akismetClient) {
      let userRole = 'user'
      if (user.groups.indexOf(1) >= 0) {
        userRole = 'administrator'
      } else if (user.groups.indexOf(2) >= 0) {
        userRole = 'guest'
      }

      let isSpam = false
      try {
        isSpam = await akismetClient.checkSpam({
          ip: user.ip,
          useragent: user.agentagent,
          content,
          name: user.name,
          email: user.email,
          permalink: `${WIKI.config.host}/${page.localeCode}/${page.path}`,
          permalinkDate: page.updatedAt,
          type: (replyTo > 0) ? 'reply' : 'comment',
          role: userRole
        })
      } catch (err) {
        WIKI.logger.warn('Akismet Comment Validation: [ FAILED ]')
        WIKI.logger.warn(err)
      }

      if (isSpam) {
        throw new Error('Comment was rejected because it is marked as spam.')
      }
    }

109 110 111 112 113 114 115 116
    // -> 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.')
      }
    }

117 118 119 120 121
    // -> Save Comment to DB
    const cm = await WIKI.models.comments.query().insert(newComment)

    // -> Return Comment ID
    return cm.id
122
  },
NGPixel's avatar
NGPixel committed
123 124 125 126 127 128 129 130 131
  /**
   * Update an existing comment
   */
  async update ({ id, content, user }) {
    const renderedContent = DOMPurify.sanitize(mkdown.render(content))
    await WIKI.models.comments.query().findById(id).patch({
      render: renderedContent
    })
    return renderedContent
132
  },
133 134 135
  /**
   * Delete an existing comment by ID
   */
NGPixel's avatar
NGPixel committed
136
  async remove ({ id, user }) {
137
    return WIKI.models.comments.query().findById(id).delete()
138
  },
139 140 141 142 143 144 145
  /**
   * Get the page ID from a comment ID
   */
  async getPageIdFromCommentId (id) {
    const result = await WIKI.models.comments.query().select('pageId').findById(id)
    return (result) ? result.pageId : false
  },
NGPixel's avatar
NGPixel committed
146 147 148 149 150 151
  /**
   * Get a comment by ID
   */
  async getCommentById (id) {
    return WIKI.models.comments.query().findById(id)
  },
152 153 154 155 156 157
  /**
   * Get the total comments count for a page ID
   */
  async count (pageId) {
    const result = await WIKI.models.comments.query().count('* as total').where('pageId', pageId).first()
    return _.toSafeInteger(result.total)
158 159
  }
}