Built via two parallel agent workflows reading the real Prisma schema, then integrated + verified. Public: /shop (categories + storefront), /community hub, /rankings (top by credits), /staff, /photos (camera_web gallery), /help (categories + rules), /help/tickets (auth-gated user tickets + create), /settings (auth-gated, update motto via RCON). Admin: /admin/catalog (+[id] items), /admin/radio (shouts/apps/schedules + delete shout), /admin/teams (CRUD), /admin/permissions (read), /admin/wordfilter (CRUD + RCON push), /admin/ip (whitelist/blacklist CRUD), /admin/applications (list + dismiss), /admin/logs (chat/command/alert read), /admin/achievements (read). Server actions all staff-gated. Header + admin nav extended. Verified: tsc exit 0, vitest 48/48, next build exit 0 (33 routes); curl-probed every route — public 200/<main>, auth+admin correctly 307-redirect when unauthorized.
41 lines
1.1 KiB
TypeScript
41 lines
1.1 KiB
TypeScript
"use server";
|
|
|
|
import { revalidatePath } from "next/cache";
|
|
import { auth } from "@/lib/auth";
|
|
import { prisma } from "@/lib/prisma";
|
|
import { rcon } from "@/lib/services/rcon";
|
|
|
|
// Emulator motto column is VARCHAR(127); keep the CMS-side write within bounds.
|
|
const MOTTO_MAX = 127;
|
|
|
|
/**
|
|
* Update the SIGNED-IN user's motto. The id is taken from the session
|
|
* (re-fetched via auth()), never from the submitted FormData, so a crafted
|
|
* form cannot mutate another account. Mirrors AtomCMS: persist + RCON setmotto
|
|
* so an online user sees the change live.
|
|
*/
|
|
export async function updateMotto(formData: FormData): Promise<void> {
|
|
const session = await auth();
|
|
if (!session?.user?.id) return;
|
|
|
|
const id = Number(session.user.id);
|
|
if (!Number.isFinite(id)) return;
|
|
|
|
const motto = String(formData.get("motto") ?? "").slice(0, MOTTO_MAX);
|
|
|
|
try {
|
|
await prisma.user.update({ where: { id }, data: { motto } });
|
|
} catch {
|
|
// DB unavailable — fail soft; nothing to persist.
|
|
return;
|
|
}
|
|
|
|
try {
|
|
await rcon.setMotto(id, motto);
|
|
} catch {
|
|
// RCON is best-effort; the change is already persisted.
|
|
}
|
|
|
|
revalidatePath("/settings");
|
|
}
|