|
|
|
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')
|
|
|
|
const moment = require('moment')
|
|
|
|
|
|
|
|
/* global WIKI */
|
|
|
|
|
|
|
|
const window = new JSDOM('').window
|
|
|
|
const DOMPurify = createDOMPurify(window)
|
|
|
|
|
|
|
|
let akismetClient = null
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
|
|
|
// ------------------------------------
|
|
|
|
// Default Comment Provider
|
|
|
|
// ------------------------------------
|
|
|
|
|
|
|
|
module.exports = {
|
|
|
|
/**
|
|
|
|
* Init
|
|
|
|
*/
|
|
|
|
async init (config) {
|
|
|
|
WIKI.logger.info('(COMMENTS/DEFAULT) Initializing...')
|
|
|
|
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) {
|
|
|
|
akismetClient = null
|
|
|
|
WIKI.logger.warn('(COMMENTS/DEFAULT) Akismet Key is invalid! [ DISABLED ]')
|
|
|
|
} else {
|
|
|
|
WIKI.logger.info('(COMMENTS/DEFAULT) Akismet key is valid. [ OK ]')
|
|
|
|
}
|
|
|
|
} catch (err) {
|
|
|
|
akismetClient = null
|
|
|
|
WIKI.logger.warn('(COMMENTS/DEFAULT) Unable to verify Akismet Key: ' + err.message)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
akismetClient = null
|
|
|
|
}
|
|
|
|
WIKI.logger.info('(COMMENTS/DEFAULT) Initialization completed.')
|
|
|
|
},
|
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
}
|
|
|
|
|
|
|
|
// -> Check for Spam with Akismet
|
|
|
|
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.')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// -> 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)
|
|
|
|
|
|
|
|
// -> Return Comment ID
|
|
|
|
return cm.id
|
|
|
|
},
|
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
},
|
|
|
|
/**
|
|
|
|
* Delete an existing comment by ID
|
|
|
|
*/
|
|
|
|
async remove ({ id, user }) {
|
|
|
|
return WIKI.models.comments.query().findById(id).delete()
|
|
|
|
},
|
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
},
|
|
|
|
/**
|
|
|
|
* Get a comment by ID
|
|
|
|
*/
|
|
|
|
async getCommentById (id) {
|
|
|
|
return WIKI.models.comments.query().findById(id)
|
|
|
|
},
|
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
}
|
|
|
|
}
|