/* eslint-disable import/no-absolute-path */ import { Meteor } from 'meteor/meteor'; import { check } from 'meteor/check'; import Comments from '/imports/api/Comments/Comments'; import getMediaFromContent from '/imports/api/Comments/mediaAnalyzers'; import rateLimit from '/imports/modules/rate-limit'; import onCommentAdd from '/imports/api/Comments/server/onCommentAdd'; const MAX_LEN = 5000; function usernameOf(user) { return (user && user.profile && user.profile.name && user.profile.name.first) ? user.profile.name.first : ''; } async function requireOwner(commentId, userId) { const comment = await Comments.findOneAsync(commentId); if (!comment) throw new Meteor.Error('404', 'Comment not found'); if (comment.userId !== userId) throw new Meteor.Error('403', 'Not your comment'); return comment; } async function toggle(commentId, field, otherField, userId) { check(commentId, String); if (!userId) throw new Meteor.Error('403', 'Login required'); const comment = await Comments.findOneAsync(commentId); if (!comment) throw new Meteor.Error('404', 'Comment not found'); const has = (comment[field] || []).includes(userId); const modifier = has ? { $pull: { [field]: userId } } : { $addToSet: { [field]: userId }, $pull: { [otherField]: userId } }; await Comments.updateAsync(commentId, modifier); } Meteor.methods({ 'comments.insert': async function commentsInsert(referenceId, content) { check(referenceId, String); check(content, String); if (!this.userId) throw new Meteor.Error('403', 'Login required to comment'); const text = content.trim(); if (!text) throw new Meteor.Error('400', 'Empty comment'); if (text.length > MAX_LEN) throw new Meteor.Error('400', 'Comment too long'); const now = new Date(); const doc = { referenceId, content: text, userId: this.userId, username: usernameOf(await Meteor.users.findOneAsync(this.userId)), media: getMediaFromContent(text), likes: [], dislikes: [], status: 'approved', createdAt: now, updatedAt: now }; const _id = await Comments.insertAsync(doc); // Fire-and-forget: notify other commenters of this fire. Never let a mail // failure break the insert. try { onCommentAdd({ ...doc, _id }); } catch (e) { console.warn(`comments onCommentAdd failed: ${e}`); } return _id; }, 'comments.edit': async function commentsEdit(commentId, content) { check(commentId, String); check(content, String); if (!this.userId) throw new Meteor.Error('403', 'Login required'); await requireOwner(commentId, this.userId); const text = content.trim(); if (!text) throw new Meteor.Error('400', 'Empty comment'); if (text.length > MAX_LEN) throw new Meteor.Error('400', 'Comment too long'); await Comments.updateAsync(commentId, { $set: { content: text, media: getMediaFromContent(text), updatedAt: new Date() } }); }, 'comments.remove': async function commentsRemove(commentId) { check(commentId, String); if (!this.userId) throw new Meteor.Error('403', 'Login required'); await requireOwner(commentId, this.userId); await Comments.removeAsync(commentId); }, 'comments.like': async function commentsLike(commentId) { await toggle(commentId, 'likes', 'dislikes', this.userId); }, 'comments.dislike': async function commentsDislike(commentId) { await toggle(commentId, 'dislikes', 'likes', this.userId); } }); rateLimit({ methods: ['comments.insert', 'comments.edit', 'comments.remove', 'comments.like', 'comments.dislike'], limit: 5, timeRange: 1000 });