"use server"; import { db } from "@/db"; import { submissions, answers } from "@/db/schema"; import { eq } from "drizzle-orm"; import { revalidatePath } from "next/cache"; import { getServerSession } from "next-auth"; import { authOptions } from "@/app/api/auth/[...nextauth]/route"; export async function submitForm( formId: string, answersList: { questionId: string; value: string }[] ) { const session = await getServerSession(authOptions); if (!session?.user) throw new Error("Not authenticated"); const discordId = (session.user as { discordId?: string }).discordId; if (!discordId) throw new Error("No Discord ID found"); const form = await db.query.forms.findFirst({ where: (f, { eq }) => eq(f.id, formId), }); if (!form) throw new Error("Form not found"); if (!form.isOpen) throw new Error("This form is disabled for now"); const existingSubmission = await db.query.submissions.findFirst({ where: (s, { eq, and }) => and(eq(s.formId, formId), eq(s.userDiscordId, discordId)), }); let submissionId = ""; const isUpdate = !!existingSubmission; if (existingSubmission) { submissionId = existingSubmission.id; // Fetch old answers const oldAnswersList = await db.query.answers.findMany({ where: (a, { eq }) => eq(a.submissionId, submissionId), }); const oldAnswersDict = oldAnswersList.reduce((acc, curr) => { acc[curr.questionId] = curr.value; return acc; }, {} as Record); // Ensure editHistory is an array let history = existingSubmission.editHistory; if (!Array.isArray(history)) { history = []; } // Push the old answers to history history.push({ editedAt: new Date().toISOString(), oldAnswers: oldAnswersDict, }); // Update submission record await db .update(submissions) .set({ editHistory: history, }) .where(eq(submissions.id, submissionId)); // Delete old answers await db.delete(answers).where(eq(answers.submissionId, submissionId)); } else { // Insert new submission const [inserted] = await db .insert(submissions) .values({ formId, userDiscordId: discordId, userName: session.user.name ?? null, }) .returning(); submissionId = inserted.id; } // Insert new/updated answers if (answersList.length > 0) { await db.insert(answers).values( answersList.map((a) => ({ submissionId, questionId: a.questionId, value: a.value, })) ); } // Send Discord Webhook const templateToUse = isUpdate ? (form as any).discordWebhookUpdateTemplate : form.discordWebhookTemplate; if (form.discordWebhookUrl && templateToUse) { try { const questionsList = await db.query.questions.findMany({ where: (q, { eq }) => eq(q.formId, formId), orderBy: (q, { asc }) => asc(q.displayOrder) }); const questionMap = questionsList.reduce((acc, q, index) => { acc[q.id] = String(index + 1); return acc; }, {} as Record); const answersDict = answersList.reduce((acc, a) => { const idx = questionMap[a.questionId]; if (idx) { acc[idx] = a.value; } return acc; }, {} as Record); let message = templateToUse; // Replace {1}, {2}, etc. with value, and new variables message = message.replace(/\{([^}]+)\}/g, (match: string, p1: string) => { const key = p1.toLowerCase(); if (key === "username" || key === "displayname") { return session.user?.name || "Unknown User"; } if (key === "id") { return discordId || "Unknown ID"; } return answersDict[p1] || "Not Provided"; }); await fetch(form.discordWebhookUrl, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ content: message, }), }); } catch (e) { console.error("Failed to send webhook", e); } } revalidatePath("/form/admin/result"); return { ok: true }; } export async function deleteSubmission(id: string) { const session = await getServerSession(authOptions); if (!session?.user) throw new Error("Not authenticated"); await db.delete(submissions).where(eq(submissions.id, id)); revalidatePath("/form/admin/result"); return { ok: true }; } export async function deleteAllSubmissions(formId: string) { const session = await getServerSession(authOptions); if (!session?.user) throw new Error("Not authenticated"); await db.delete(submissions).where(eq(submissions.formId, formId)); revalidatePath(`/admin/form/${formId}/result`); return { ok: true }; } export async function deleteOwnSubmission(formId: string) { const session = await getServerSession(authOptions); if (!session?.user) throw new Error("Not authenticated"); const discordId = (session.user as { discordId?: string }).discordId; if (!discordId) throw new Error("No Discord ID found"); const { and } = await import("drizzle-orm"); await db.delete(submissions).where( and( eq(submissions.formId, formId), eq(submissions.userDiscordId, discordId) ) ); revalidatePath(`/form`); revalidatePath("/form/admin/result"); return { ok: true }; }