mirror of
https://github.com/wassname/talk.git
synced 2026-07-03 07:28:29 +08:00
21e1a5cbef
* fix: renamed snake case to camel case * fix: changed case for mutators * fix: renamed all snake case to camel case for db * feat: added support for comment revisions + split comment actions * fix: updated tests * feat: implemented CommentModerationAction * fix: fixed case issues * feat: enabled WeakMap for wordList processsing * chore: npm audit
206 lines
5.8 KiB
TypeScript
206 lines
5.8 KiB
TypeScript
import { Db } from "mongodb";
|
|
|
|
import { Omit } from "talk-common/types";
|
|
import {
|
|
createComment,
|
|
CreateCommentInput,
|
|
editComment,
|
|
EditCommentInput,
|
|
getLatestRevision,
|
|
pushChildCommentIDOntoParent,
|
|
retrieveComment,
|
|
} from "talk-server/models/comment";
|
|
import {
|
|
retrieveStory,
|
|
updateCommentStatusCount,
|
|
} from "talk-server/models/story";
|
|
import { Tenant } from "talk-server/models/tenant";
|
|
import { User } from "talk-server/models/user";
|
|
import {
|
|
addCommentActions,
|
|
CreateAction,
|
|
} from "talk-server/services/comments/actions";
|
|
import { processForModeration } from "talk-server/services/comments/moderation";
|
|
import { Request } from "talk-server/types/express";
|
|
|
|
export type CreateComment = Omit<
|
|
CreateCommentInput,
|
|
"status" | "metadata" | "grandparentIDs"
|
|
>;
|
|
|
|
export async function create(
|
|
mongo: Db,
|
|
tenant: Tenant,
|
|
author: User,
|
|
input: CreateComment,
|
|
req?: Request
|
|
) {
|
|
// Grab the story that we'll use to check moderation pieces with.
|
|
const story = await retrieveStory(mongo, tenant.id, input.storyID);
|
|
if (!story) {
|
|
// TODO: (wyattjoh) return better error.
|
|
throw new Error("story referenced does not exist");
|
|
}
|
|
|
|
// TODO: (wyattjoh) Check that the story was visible.
|
|
|
|
const grandparentIDs: string[] = [];
|
|
if (input.parentID) {
|
|
// Check to see that the reference parent ID exists.
|
|
const parent = await retrieveComment(mongo, tenant.id, input.parentID);
|
|
if (!parent) {
|
|
// TODO: (wyattjoh) return better error.
|
|
throw new Error("parent comment referenced does not exist");
|
|
}
|
|
|
|
// TODO: (wyattjoh) Check that the parent comment was visible.
|
|
|
|
// Push the parent's parent id's into the comment's grandparent id's.
|
|
grandparentIDs.push(...parent.grandparentIDs);
|
|
if (parent.parentID) {
|
|
// If this parent has a parent, push it down as well.
|
|
grandparentIDs.push(parent.parentID);
|
|
}
|
|
}
|
|
|
|
// Run the comment through the moderation phases.
|
|
const { actions, status, metadata } = await processForModeration({
|
|
story,
|
|
tenant,
|
|
comment: input,
|
|
author,
|
|
req,
|
|
});
|
|
|
|
// Create the comment!
|
|
let comment = await createComment(mongo, tenant.id, {
|
|
...input,
|
|
status,
|
|
grandparentIDs,
|
|
metadata,
|
|
});
|
|
|
|
if (actions.length > 0) {
|
|
// The actions coming from the moderation phases didn't know the commentID
|
|
// at the time, and we didn't want the repetitive nature of adding the
|
|
// item_type each time, so this mapping function adds them!
|
|
const inputs = actions.map(
|
|
(action): CreateAction => ({
|
|
...action,
|
|
commentID: comment.id,
|
|
commentRevisionID: getLatestRevision(comment!).id,
|
|
|
|
// Store the Story ID on the action.
|
|
storyID: story.id,
|
|
})
|
|
);
|
|
|
|
// Insert and handle creating the actions.
|
|
comment = await addCommentActions(mongo, tenant, comment, inputs);
|
|
}
|
|
|
|
if (input.parentID) {
|
|
// Push the child's ID onto the parent.
|
|
await pushChildCommentIDOntoParent(
|
|
mongo,
|
|
tenant.id,
|
|
input.parentID,
|
|
comment.id
|
|
);
|
|
}
|
|
|
|
// Increment the status count for the particular status on the Story.
|
|
await updateCommentStatusCount(mongo, tenant.id, story.id, {
|
|
[status]: 1,
|
|
});
|
|
|
|
return comment;
|
|
}
|
|
|
|
export type EditComment = Omit<
|
|
EditCommentInput,
|
|
"status" | "authorID" | "lastEditableCommentCreatedAt"
|
|
>;
|
|
|
|
export async function edit(
|
|
mongo: Db,
|
|
tenant: Tenant,
|
|
author: User,
|
|
input: EditComment,
|
|
req?: Request
|
|
) {
|
|
// Get the comment that we're editing.
|
|
const comment = await retrieveComment(mongo, tenant.id, input.id);
|
|
if (!comment) {
|
|
// TODO: replace to match error returned by the models/comments.ts
|
|
throw new Error("comment not found");
|
|
}
|
|
|
|
// Grab the story that we'll use to check moderation pieces with.
|
|
const story = await retrieveStory(mongo, tenant.id, comment.storyID);
|
|
if (!story) {
|
|
// TODO: (wyattjoh) return better error.
|
|
throw new Error("story referenced does not exist");
|
|
}
|
|
|
|
// Run the comment through the moderation phases.
|
|
const { status, metadata, actions } = await processForModeration({
|
|
story,
|
|
tenant,
|
|
comment: input,
|
|
author,
|
|
req,
|
|
});
|
|
|
|
let editedComment = await editComment(mongo, tenant.id, {
|
|
id: input.id,
|
|
authorID: author.id,
|
|
body: input.body,
|
|
status,
|
|
metadata,
|
|
// The editable time is based on the current time, and the edit window
|
|
// length. By subtracting the current date from the edit window length, we
|
|
// get the maximum value for the `created_at` time that would be permitted
|
|
// for the comment edit to succeed.
|
|
lastEditableCommentCreatedAt: new Date(
|
|
Date.now() - tenant.editCommentWindowLength
|
|
),
|
|
});
|
|
if (!comment) {
|
|
// TODO: replace to match error returned by the models/comments.ts
|
|
throw new Error("comment not found");
|
|
}
|
|
|
|
if (actions.length > 0) {
|
|
// The actions coming from the moderation phases didn't know the commentID
|
|
// at the time, and we didn't want the repetitive nature of adding the
|
|
// item_type each time, so this mapping function adds them!
|
|
const inputs = actions.map(
|
|
(action): CreateAction => ({
|
|
...action,
|
|
// Strict null check seems to have failed here... Null checking was done
|
|
// above where we errored if the comment was falsely.
|
|
commentID: comment!.id,
|
|
commentRevisionID: getLatestRevision(comment!).id,
|
|
|
|
// Store the Story ID on the action.
|
|
storyID: story.id,
|
|
})
|
|
);
|
|
|
|
// Insert and handle creating the actions.
|
|
editedComment = await addCommentActions(mongo, tenant, comment, inputs);
|
|
}
|
|
|
|
if (comment.status !== editedComment.status) {
|
|
// Increment the status count for the particular status on the Story, and
|
|
// decrement the status on the comment's previous status.
|
|
await updateCommentStatusCount(mongo, tenant.id, story.id, {
|
|
[comment.status]: -1,
|
|
[editedComment.status]: 1,
|
|
});
|
|
}
|
|
|
|
return editedComment;
|
|
}
|