HotTRDealsBackend/services/comment.service.js
2026-02-09 21:47:55 +00:00

230 lines
7.0 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const userDB = require("../db/user.db")
const commentDB = require("../db/comment.db")
const {
addCommentToRedis,
removeCommentFromRedis,
getCommentsForDeal,
} = require("./redis/commentCache.service")
const { getOrCacheDeal, getDealIdByCommentId } = require("./redis/dealCache.service")
const { generateCommentId } = require("./redis/commentId.service")
const {
queueCommentCreate,
queueCommentDelete,
queueNotificationCreate,
} = require("./redis/dbSync.service")
const { publishNotification } = require("./redis/notificationPubsub.service")
const { trackUserCategoryInterest, USER_INTEREST_ACTIONS } = require("./userInterest.service")
const { sanitizeOptionalPlainText } = require("../utils/inputSanitizer")
function parseParentId(value) {
if (value === undefined || value === null || value === "" || value === "null") return null
const pid = Number(value)
if (!Number.isInteger(pid) || pid <= 0) throw new Error("Gecersiz parentId.")
return pid
}
function normalizeSort(value) {
const normalized = String(value || "new").trim().toLowerCase()
if (["top", "best", "liked"].includes(normalized)) return "TOP"
return "NEW"
}
async function ensureDealCached(dealId) {
return getOrCacheDeal(dealId, { ttlSeconds: 15 * 60 })
}
async function getCommentsByDealId(dealId, { parentId, page, limit, sort, viewer } = {}) {
const id = Number(dealId)
const deal = await ensureDealCached(id)
if (!deal) throw new Error("Deal bulunamadi.")
return getCommentsForDeal({
dealId: id,
deal,
parentId: parseParentId(parentId),
page,
limit,
sort: normalizeSort(sort),
viewerId: viewer?.userId ?? null,
})
}
async function createComment({ dealId, userId, text, parentId = null }) {
const normalizedText = sanitizeOptionalPlainText(text, { maxLength: 2000 })
if (!normalizedText) {
throw new Error("Yorum bos olamaz.")
}
const deal = await ensureDealCached(dealId)
if (!deal) throw new Error("Deal bulunamadi.")
if (deal.status !== "ACTIVE" && deal.status !== "EXPIRED") {
throw new Error("Bu deal icin yorum acilamaz.")
}
let parent = null
if (parentId != null) {
const pid = parseParentId(parentId)
const comments = Array.isArray(deal.comments) ? deal.comments : []
const cachedParent = comments.find((c) => Number(c.id) === Number(pid))
if (!cachedParent || cachedParent.deletedAt) throw new Error("Yanıtlanan yorum bulunamadi.")
if (Number(cachedParent.dealId) !== Number(dealId)) {
throw new Error("Yanıtlanan yorum bu deal'a ait degil.")
}
parent = {
id: cachedParent.id,
dealId: cachedParent.dealId,
userId: Number(cachedParent.userId) || null,
}
}
const user = await userDB.findUser(
{ id: userId },
{ select: { id: true, username: true, avatarUrl: true } }
)
if (!user) throw new Error("Kullanici bulunamadi.")
const createdAt = new Date()
const commentId = await generateCommentId()
const comment = {
id: commentId,
text: normalizedText,
userId,
dealId,
parentId: parent ? parent.id : null,
createdAt,
likeCount: 0,
repliesCount: 0,
user,
}
await addCommentToRedis({
...comment,
repliesCount: 0,
})
queueCommentCreate({
commentId,
dealId,
userId,
text: normalizedText,
parentId: parent ? parent.id : null,
createdAt: createdAt.toISOString(),
}).catch((err) => console.error("DB sync comment create failed:", err?.message || err))
trackUserCategoryInterest({
userId,
categoryId: deal.categoryId,
action: USER_INTEREST_ACTIONS.COMMENT_CREATE,
}).catch((err) => console.error("User interest track failed:", err?.message || err))
const parentUserId = Number(parent?.userId)
if (
parent &&
Number.isInteger(parentUserId) &&
parentUserId > 0 &&
parentUserId !== Number(userId)
) {
const notificationPayload = {
userId: parentUserId,
message: "Yorumuna cevap geldi.",
type: "COMMENT_REPLY",
extras: {
dealId: Number(dealId),
commentId: Number(commentId),
parentCommentId: Number(parent.id),
},
createdAt: createdAt.toISOString(),
}
queueNotificationCreate(notificationPayload).catch((err) =>
console.error("DB sync comment reply notification failed:", err?.message || err)
)
publishNotification(notificationPayload).catch((err) =>
console.error("Comment reply notification publish failed:", err?.message || err)
)
}
return comment
}
async function deleteComment(commentId, userId) {
const cid = Number(commentId)
if (!Number.isInteger(cid) || cid <= 0) throw new Error("Gecersiz commentId.")
let dealId = await getDealIdByCommentId(cid)
let dbFallback = null
if (!dealId) {
dbFallback = await commentDB.findComment(
{ id: cid },
{ select: { id: true, dealId: true, userId: true, parentId: true, deletedAt: true } }
)
if (!dbFallback || dbFallback.deletedAt) throw new Error("Yorum bulunamadi.")
dealId = dbFallback.dealId
}
const deal = await ensureDealCached(dealId)
if (!deal) throw new Error("Yorum bulunamadi.")
const comments = Array.isArray(deal.comments) ? deal.comments : []
const comment = comments.find((c) => Number(c.id) === cid)
const effective = comment || dbFallback
if (!effective || effective.deletedAt) throw new Error("Yorum bulunamadi.")
if (Number(effective.userId) !== Number(userId)) throw new Error("Bu yorumu silme yetkin yok.")
queueCommentDelete({
commentId: cid,
dealId: effective.dealId,
createdAt: new Date().toISOString(),
}).catch((err) => console.error("DB sync comment delete failed:", err?.message || err))
await removeCommentFromRedis({
commentId: cid,
dealId: effective.dealId,
})
return { message: "Yorum silindi." }
}
async function deleteCommentAsMod(commentId) {
const cid = Number(commentId)
if (!Number.isInteger(cid) || cid <= 0) throw new Error("Gecersiz commentId.")
let dealId = await getDealIdByCommentId(cid)
let dbFallback = null
if (!dealId) {
dbFallback = await commentDB.findComment(
{ id: cid },
{ select: { id: true, dealId: true, userId: true, parentId: true, deletedAt: true } }
)
if (!dbFallback || dbFallback.deletedAt) throw new Error("Yorum bulunamadi.")
dealId = dbFallback.dealId
}
const deal = await ensureDealCached(dealId)
if (!deal) throw new Error("Yorum bulunamadi.")
const comments = Array.isArray(deal.comments) ? deal.comments : []
const comment = comments.find((c) => Number(c.id) === cid)
const effective = comment || dbFallback
if (!effective || effective.deletedAt) throw new Error("Yorum bulunamadi.")
queueCommentDelete({
commentId: cid,
dealId: effective.dealId,
createdAt: new Date().toISOString(),
}).catch((err) => console.error("DB sync comment delete failed:", err?.message || err))
await removeCommentFromRedis({
commentId: cid,
dealId: effective.dealId,
})
return { message: "Yorum silindi." }
}
module.exports = {
getCommentsByDealId,
createComment,
deleteComment,
deleteCommentAsMod,
}