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
+18
-3
@@ -17,7 +17,12 @@ import {
|
||||
// ── User actions (authenticated, no admin perms needed) ──────────────
|
||||
|
||||
export const createTicket = authAction(
|
||||
{ schema: createTicketSchema },
|
||||
{
|
||||
schema: createTicketSchema,
|
||||
rateLimitKey: "ticket-create",
|
||||
rateLimitMax: 5,
|
||||
rateLimitWindowMs: 60_000,
|
||||
},
|
||||
async (ctx) => {
|
||||
const ticket = await prisma.websiteTicket.create({
|
||||
data: {
|
||||
@@ -49,7 +54,12 @@ export const createTicket = authAction(
|
||||
);
|
||||
|
||||
export const userReplyTicket = authAction(
|
||||
{ schema: replyTicketSchema },
|
||||
{
|
||||
schema: replyTicketSchema,
|
||||
rateLimitKey: "ticket-reply",
|
||||
rateLimitMax: 10,
|
||||
rateLimitWindowMs: 60_000,
|
||||
},
|
||||
async (ctx) => {
|
||||
const ticket = await prisma.websiteTicket.findUnique({
|
||||
where: { id: ctx.data.ticketId },
|
||||
@@ -82,7 +92,12 @@ export const userReplyTicket = authAction(
|
||||
);
|
||||
|
||||
export const closeTicketByUser = authAction(
|
||||
{ schema: replyTicketSchema.pick({ ticketId: true }) },
|
||||
{
|
||||
schema: replyTicketSchema.pick({ ticketId: true }),
|
||||
rateLimitKey: "ticket-close",
|
||||
rateLimitMax: 10,
|
||||
rateLimitWindowMs: 60_000,
|
||||
},
|
||||
async (ctx) => {
|
||||
const ticket = await prisma.websiteTicket.findUnique({
|
||||
where: { id: ctx.data.ticketId },
|
||||
|
||||
Reference in new issue
Block a user