"use server"; import { revalidatePath } from "next/cache"; import { auth } from "@/lib/auth"; import { prisma } from "@/lib/prisma"; import { isAllowed } from "@/lib/services/moderation"; // website_article_comments.comment is VARCHAR(255); keep the write within bounds. const COMMENT_MAX = 255; /** * Post a comment on a news article as the SIGNED-IN user. The author id is read * from the session (re-fetched via auth()), never from the submitted FormData, * so a crafted form cannot post as another account. The articleId comes from the * form and is validated as a BigInt (website_articles.id is UNSIGNED BIGINT). */ export async function postComment(formData: FormData): Promise { const session = await auth(); if (!session?.user?.id) return; const userId = Number(session.user.id); if (!Number.isFinite(userId)) return; const comment = String(formData.get("comment") ?? "").trim().slice(0, COMMENT_MAX); if (!comment) return; // Block filtered/AI-flagged content before it touches the DB (fail-open). if (!(await isAllowed(comment)).ok) return; const articleIdRaw = String(formData.get("articleId") ?? "").trim(); if (!/^\d+$/.test(articleIdRaw)) return; let articleId: bigint; try { articleId = BigInt(articleIdRaw); } catch { return; } let slug: string | null; try { // Confirm the article exists (and grab its slug for revalidation). const article = await prisma.websiteArticles.findUnique({ where: { id: articleId }, select: { slug: true }, }); if (!article) return; slug = article.slug; const now = new Date(); await prisma.websiteArticleComments.create({ data: { articleId, userId, comment, createdAt: now, updatedAt: now, }, }); } catch { // DB unavailable — fail soft; nothing to persist. return; } if (slug) revalidatePath(`/news/${slug}`); }