"use server"; import { and, eq, max } from "drizzle-orm"; import { revalidatePath } from "next/cache"; import { requirePermission } from "@/lib/admin/guard"; import { db, UsersBadges } from "@/lib/db"; import { PERMS } from "@/lib/permissions"; import { rcon } from "@/lib/services/rcon"; export async function giveBadge(formData: FormData): Promise { await requirePermission(PERMS.CATALOG_EDIT); // Support comma-separated userIds and/or codes for bulk operations const userIdInput = String(formData.get("userId") ?? "").trim(); const codeInput = String(formData.get("code") ?? "").trim(); const userIds = userIdInput ? userIdInput .split(",") .map((s) => s.trim()) .filter(Boolean) : []; const codes = codeInput ? codeInput .split(",") .map((s) => s.trim()) .filter(Boolean) : []; if (userIds.length === 0 || codes.length === 0) { return; } // Process each user/code combination let granted = false; for (const userId of userIds) { for (const code of codes) { if (!(Number(userId) > 0) || code.length === 0) continue; // Truncate badge code to 32 characters. const truncatedCode = code.slice(0, 32); // Fire the emulator command so the badge appears live for online users. try { await rcon.giveBadge(Number(userId), truncatedCode); } catch { // ignore rcon errors per pair } // Persist the badge directly so it survives a relog / offline grant. // users_badges has no unique (user_id, badge_code) constraint, so guard // against duplicates and compute the next free slot ourselves. try { const [existing] = await db .select({ id: UsersBadges.id }) .from(UsersBadges) .where( and( eq(UsersBadges.userId, Number(userId)), eq(UsersBadges.badgeCode, truncatedCode), ), ) .limit(1); if (!existing) { const [agg] = await db .select({ maxSlot: max(UsersBadges.slotId) }) .from(UsersBadges) .where(eq(UsersBadges.userId, Number(userId))); const slotId = (agg?.maxSlot ?? 0) + 1; await db.insert(UsersBadges).values({ userId: Number(userId), slotId, badgeCode: truncatedCode, }); } } catch { // Best-effort: the RCON grant already succeeded for online users. } granted = true; } } if (granted) { revalidatePath("/admin/badges"); } }