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

+7 -1
View File
@@ -5,6 +5,7 @@ import { redirect } from "next/navigation";
import type { Prisma } from "@/generated/prisma/client";
import { auth } from "@/lib/auth";
import { prisma } from "@/lib/prisma";
import { clientIp, rateLimit } from "@/lib/rate-limit";
import { rcon } from "@/lib/services/rcon";
import { siteSettings } from "@/lib/services/site-settings";
@@ -55,10 +56,14 @@ export async function buyBadge(formData: FormData): Promise<void> {
.trim();
if (!/^\d+$/.test(rawId)) redirect("/draw-badge?error=invalid");
let outcome: "bought" | "invalid" | "credits" | "fail";
let outcome: "bought" | "invalid" | "credits" | "ratelimit" | "fail";
let boughtCode = "";
try {
await clientIp();
if (!(await rateLimit(`draw-badge-buy:${userId}`, 5, 60_000)).ok) {
outcome = "ratelimit";
} else {
const badge = await prisma.websiteDrawbadges.findUnique({
where: { id: BigInt(rawId) },
select: { id: true, badgePath: true, published: true },
@@ -115,6 +120,7 @@ export async function buyBadge(formData: FormData): Promise<void> {
}
}
}
}
} catch {
outcome = "fail";
}