"use server"; import { revalidatePath } from "next/cache"; import { auth } from "@/lib/auth"; import { prisma } from "@/lib/prisma"; // AtomCMS validates the application body with `min:10`. Mirror that floor and // cap the write defensively (column is TEXT, but we keep applications sane). const CONTENT_MIN = 10; const CONTENT_MAX = 5000; /** * Submit a STAFF application for an open position. * * Faithful to AtomCMS's StaffApplicationsController@store: * - the applicant (user_id) is re-read from the session via auth() and is * NEVER trusted from the submitted FormData; * - rank_id is the open position's permission id (the rank being applied for); * - a user may only apply once per rank (idempotency guard); * - content must be at least 10 characters. */ export async function applyStaff(formData: FormData): Promise { const session = await auth(); const userId = Number(session?.user?.id); if (!Number.isInteger(userId) || userId <= 0) return; // rank_id comes from the open position's permission_id (an Int in the schema). const rankId = Number(formData.get("rankId")); if (!Number.isInteger(rankId) || rankId <= 0) return; const content = String(formData.get("content") ?? "") .normalize("NFC") .trim() .slice(0, CONTENT_MAX); if (content.length < CONTENT_MIN) return; try { // Block duplicate applications for the same rank (AtomCMS hasAppliedForPosition). const existing = await prisma.websiteStaffApplications.findFirst({ where: { userId, rankId }, select: { id: true }, }); if (existing) return; const now = new Date(); await prisma.websiteStaffApplications.create({ data: { userId, rankId, content, createdAt: now, updatedAt: now }, }); } catch { // DB unavailable — fail soft; nothing to persist. return; } revalidatePath("/apply/staff"); } /** * Submit a TEAM application. * * The Prisma `website_staff_applications` slice has no dedicated team column, so * (per the conversion brief) team applications REUSE the staff-applications * table with the team acting as the rank: rank_id carries the team id. The * applicant is re-read from the session, never trusted from the form, and a user * may only apply once per team. */ export async function applyTeam(formData: FormData): Promise { const session = await auth(); const userId = Number(session?.user?.id); if (!Number.isInteger(userId) || userId <= 0) return; // website_teams.id is a BigInt; rank_id on the application is an Int. The team // id is the application's rank flag. const rankId = Number(formData.get("teamId")); if (!Number.isInteger(rankId) || rankId <= 0) return; const content = String(formData.get("content") ?? "") .normalize("NFC") .trim() .slice(0, CONTENT_MAX); if (content.length < CONTENT_MIN) return; try { const existing = await prisma.websiteStaffApplications.findFirst({ where: { userId, rankId }, select: { id: true }, }); if (existing) return; const now = new Date(); await prisma.websiteStaffApplications.create({ data: { userId, rankId, content, createdAt: now, updatedAt: now }, }); } catch { return; } revalidatePath("/apply/team"); }