feat: shop buy, forum replies, tickets, messages, sessions, and UX hardening
Complete remaining product gaps: credit-based shop purchases, guild thread replies, help ticket detail/reply/close, offline message compose, sign-out-everywhere via JWT version, ads delete confirm, soft-fail feedback, rate limits, loading states, and single auth() in site layout. Co-authored-by: Cursor <[email protected]>
This commit is contained in:
1 parent
ed7db6e048
commit
803e8f36c1
47 files changed
+2793
-358
No files matched your search
@@ -2,10 +2,13 @@
|
||||
|
||||
import { revalidatePath } from "next/cache";
|
||||
import { redirect } from "next/navigation";
|
||||
import { z } from "zod";
|
||||
import { requirePermission } from "@/lib/admin/guard";
|
||||
import { PERMS } from "@/lib/permissions";
|
||||
import { formPositiveBigInt } from "@/lib/form-data";
|
||||
import { PERMS } from "@/lib/permissions";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { adminAction } from "@/lib/safe-action";
|
||||
import { ActionError, actionOk } from "@/lib/safe-action-shared";
|
||||
import { logStaffActivity } from "@/lib/services/staff-activity";
|
||||
|
||||
// CRUD for website advertisements (website_ads). Emulator does not own this
|
||||
@@ -70,7 +73,35 @@ export async function updateAd(formData: FormData): Promise<void> {
|
||||
redirect("/admin/ads");
|
||||
}
|
||||
|
||||
export async function deleteAd(formData: FormData): Promise<void> {
|
||||
const deleteAdInput = z.object({
|
||||
id: z
|
||||
.union([z.string(), z.number(), z.bigint()])
|
||||
.transform((v) => BigInt(String(v))),
|
||||
});
|
||||
|
||||
export const deleteAd = adminAction(
|
||||
{ permission: PERMS.PAGES_EDIT, schema: deleteAdInput },
|
||||
async (ctx) => {
|
||||
const id = ctx.data.id;
|
||||
try {
|
||||
await prisma.websiteAds.delete({ where: { id } });
|
||||
} catch {
|
||||
throw new ActionError("Advertisement not found");
|
||||
}
|
||||
await logStaffActivity({
|
||||
staffId: Number(ctx.session.user.id),
|
||||
action: "ad_delete",
|
||||
description: `Deleted advertisement #${id}`,
|
||||
targetType: "website_ad",
|
||||
targetId: Number(id),
|
||||
});
|
||||
revalidatePath("/admin/ads");
|
||||
return actionOk();
|
||||
},
|
||||
);
|
||||
|
||||
/** Legacy form POST delete — kept for compatibility; prefer client deleteAd action. */
|
||||
export async function deleteAdForm(formData: FormData): Promise<void> {
|
||||
const staff = await requirePermission(PERMS.PAGES_EDIT);
|
||||
const id = formPositiveBigInt(formData, "id");
|
||||
if (!id) return;
|
||||
|
||||
Reference in new issue
Block a user