"use server"; import { revalidatePath } from "next/cache"; import { redirect } from "next/navigation"; import { requireStaff } from "@/lib/admin/guard"; import { prisma } from "@/lib/prisma"; import { logStaffActivity } from "@/lib/services/staff-activity"; // users_currency.type values for the non-credits currencies (mirror send-currency.ts). // Credits live on users.credits; pixels/points live on the users row too; // duckets/diamonds live in users_currency keyed by (user_id, type). const DUCKETS_TYPE = 0; const DIAMONDS_TYPE = 5; function toInt(value: FormDataEntryValue | null, min = 0): number | null { if (value == null) return null; const raw = String(value).trim(); if (raw === "") return null; const n = Number(raw); if (!Number.isFinite(n)) return null; const i = Math.trunc(n); return i < min ? min : i; } /** * Edit the SAFE website-managed fields of a users row (and the duckets/diamonds * balances in users_currency). Never touches the password. Re-reads the staff * user from the session and logs the action. emulator-owned users.id is Int. */ export async function updateUser(formData: FormData): Promise { // Never trust the client: re-check staff inside the action. const staff = await requireStaff(); const userId = Number(formData.get("id")); if (!Number.isInteger(userId) || userId <= 0) return; const existing = await prisma.user.findUnique({ where: { id: userId }, select: { id: true }, }); if (!existing) return; // users row — only existing, safe columns. const mailRaw = String(formData.get("mail") ?? "").trim(); const motto = String(formData.get("motto") ?? "").slice(0, 127); const look = String(formData.get("look") ?? "").slice(0, 256); const rank = toInt(formData.get("rank"), 1); const credits = toInt(formData.get("credits"), 0); const pixels = toInt(formData.get("pixels"), 0); const points = toInt(formData.get("points"), 0); await prisma.user.update({ where: { id: userId }, data: { mail: mailRaw === "" ? null : mailRaw.slice(0, 500), motto, look, ...(rank != null ? { rank } : {}), ...(credits != null ? { credits } : {}), ...(pixels != null ? { pixels } : {}), ...(points != null ? { points } : {}), }, }); // users_currency — set exact balances for duckets / diamonds. const duckets = toInt(formData.get("duckets"), 0); const diamonds = toInt(formData.get("diamonds"), 0); if (duckets != null) { await prisma.usersCurrency.upsert({ where: { userId_type: { userId, type: DUCKETS_TYPE } }, update: { amount: duckets }, create: { userId, type: DUCKETS_TYPE, amount: duckets }, }); } if (diamonds != null) { await prisma.usersCurrency.upsert({ where: { userId_type: { userId, type: DIAMONDS_TYPE } }, update: { amount: diamonds }, create: { userId, type: DIAMONDS_TYPE, amount: diamonds }, }); } await logStaffActivity({ staffId: staff.id, action: "user_edit", description: `Edited account fields of user #${userId}`, targetType: "user", targetId: userId, }); revalidatePath(`/admin/users/${userId}`); revalidatePath(`/admin/users/${userId}/edit`); redirect(`/admin/users/${userId}`); }