feat: shop buy, forum replies, tickets, messages, sessions, and UX hardening
Deploy / release (push) Skipped
Deploy / deploy (push) Failing after 4m23s

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:
SimoandCursor committed 2026-07-21 21:21:02 +02:00
1 parent ed7db6e048
commit 803e8f36c1
47 files changed
+2793 -358

No files matched your search

+33 -2
View File
@@ -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;