ghost
Version:
The professional publishing platform
162 lines (146 loc) • 4.46 kB
JavaScript
const models = require('../../models');
const commentsService = require('../../services/comments');
const errors = require('@tryghost/errors');
function handleCacheHeaders(model, frame) {
if (model) {
const postId = model.get('post_id');
const parentId = model.get('parent_id');
const pathsToInvalidate = [
postId ? `/api/members/comments/post/${postId}/` : null,
parentId ? `/api/members/comments/${parentId}/replies/` : null
].filter(path => path !== null);
frame.setHeader('X-Cache-Invalidate', pathsToInvalidate.join(', '));
}
}
function validateCommentData(data) {
if (!data.post_id && !data.parent_id) {
throw new errors.ValidationError({
message: 'Either post_id (for top-level comments) or parent_id (for replies) must be provided'
});
}
}
function validateCreatedAt(createdAt) {
if (!createdAt) {
return undefined;
}
// Only accept string or Date objects, reject other types like numbers
if (typeof createdAt !== 'string' && !(createdAt instanceof Date)) {
return undefined;
}
const date = new Date(createdAt);
// Check if the date is valid and not in the future
if (!isNaN(date.getTime()) && date <= new Date()) {
return date;
}
return undefined;
}
/** @type {import('@tryghost/api-framework').Controller} */
const controller = {
docName: 'comments',
edit: {
headers: {
cacheInvalidate: false
},
options: [
'id'
],
validation: {
options: {
id: {
required: true
}
}
},
permissions: true,
async query(frame) {
const result = await models.Comment.edit({
id: frame.data.comments[0].id,
status: frame.data.comments[0].status
}, frame.options);
handleCacheHeaders(result, frame);
return result;
}
},
browse: {
headers: {
cacheInvalidate: false
},
options: [
'post_id',
'include',
'page',
'limit',
'fields',
'filter',
'order',
'debug',
'impersonate_member_uuid'
],
validation: {
options: {
post_id: {
required: true
}
}
},
permissions: true,
async query(frame) {
const result = await commentsService.controller.adminBrowse(frame);
return result;
}
},
add: {
statusCode: 201,
headers: {
cacheInvalidate: false
},
options: [
'include'
],
validation: {
options: {
include: {
values: ['post', 'member', 'replies', 'replies.member']
}
},
data: {
member_id: {
required: true
},
html: {
required: true
}
}
},
permissions: true,
async query(frame) {
const data = frame.data.comments[0];
validateCommentData(data);
const validatedCreatedAt = validateCreatedAt(data.created_at);
// Set internal context to prevent notifications
if (!frame.options.context) {
frame.options.context = {};
}
frame.options.context.internal = true;
const result = data.parent_id
? await commentsService.api.replyToComment(
data.parent_id,
data.in_reply_to_id,
data.member_id,
data.html,
frame.options,
validatedCreatedAt
)
: await commentsService.api.commentOnPost(
data.post_id,
data.member_id,
data.html,
frame.options,
validatedCreatedAt
);
handleCacheHeaders(result, frame);
return result;
}
}
};
module.exports = controller;