Harden CMS security and theme contrast

This commit is contained in:
Simo committed 2026-07-11 20:27:20 +02:00
1 parent 2465ff2170
commit 4a1e1115b3
57 files changed
+1023 -231

No files matched your search

+22 -24
View File
@@ -4,9 +4,10 @@
// into website_help_center_ticket_replies. The target ticket must exist and
// belong to the authed user. Fail-soft: never a 500.
import { apiError, apiJson } from "@/lib/api";
import { apiError, apiJson, positiveBigInt } from "@/lib/api";
import { bearerUserId } from "@/lib/api-auth";
import { prisma } from "@/lib/prisma";
import { createOwnedTicketReply } from "@/lib/services/ticket-replies";
export const dynamic = "force-dynamic";
@@ -16,8 +17,8 @@ export async function POST(req: Request, { params }: { params: Promise<{ id: str
if (!uid) return apiError("Unauthorized", 401);
const { id } = await params;
if (!/^\d+$/.test(id)) return apiError("Invalid ticket id");
const ticketId = BigInt(id);
const ticketId = positiveBigInt(id);
if (!ticketId) return apiError("Invalid ticket id", 422);
const body = (await req.json().catch(() => ({}))) as { content?: unknown };
const content = String(body.content ?? "")
@@ -27,28 +28,25 @@ export async function POST(req: Request, { params }: { params: Promise<{ id: str
try {
// Ownership check — only the ticket owner may reply.
const ticket = await prisma.websiteHelpCenterTickets.findUnique({
where: { id: ticketId },
select: { id: true, userId: true },
});
if (!ticket || ticket.userId !== uid) return apiError("Ticket not found", 404);
const now = new Date();
const reply = await prisma.websiteHelpCenterTicketReplies.create({
data: {
ticketId,
userId: uid,
content,
createdAt: now,
updatedAt: now,
const reply = await prisma.$transaction((tx) => createOwnedTicketReply(
{
findTicket: (ticketId) => tx.websiteHelpCenterTickets.findUnique({
where: { id: ticketId },
select: { id: true, userId: true },
}),
createReply: (data) => tx.websiteHelpCenterTicketReplies.create({
data,
select: { id: true, userId: true, content: true, createdAt: true },
}),
touchTicket: (ticketId, updatedAt) => tx.websiteHelpCenterTickets.update({
where: { id: ticketId },
data: { updatedAt },
select: { id: true },
}),
},
select: { id: true, userId: true, content: true, createdAt: true },
});
// Touch the parent ticket so its updatedAt reflects the latest activity.
prisma.websiteHelpCenterTickets
.update({ where: { id: ticketId }, data: { updatedAt: now }, select: { id: true } })
.catch(() => {});
{ ticketId, userId: uid, content },
));
if (!reply) return apiError("Ticket not found", 404);
return apiJson(
{
+3 -3
View File
@@ -4,7 +4,7 @@
// together with its replies; reply author usernames are resolved in a single
// users lookup. Fail-soft: never a 500.
import { apiError, apiJson } from "@/lib/api";
import { apiError, apiJson, positiveBigInt } from "@/lib/api";
import { bearerUserId } from "@/lib/api-auth";
import { prisma } from "@/lib/prisma";
@@ -16,8 +16,8 @@ export async function GET(req: Request, { params }: { params: Promise<{ id: stri
if (!uid) return apiError("Unauthorized", 401);
const { id } = await params;
if (!/^\d+$/.test(id)) return apiError("Invalid ticket id");
const ticketId = BigInt(id);
const ticketId = positiveBigInt(id);
if (!ticketId) return apiError("Invalid ticket id", 422);
try {
const ticket = await prisma.websiteHelpCenterTickets.findUnique({
+3 -3
View File
@@ -4,7 +4,7 @@
// Backed by website_help_center_tickets (WebsiteHelpCenterTickets). Fail-soft:
// DB errors return an apiError envelope, never a 500.
import { apiError, apiJson } from "@/lib/api";
import { apiError, apiJson, positiveBigInt } from "@/lib/api";
import { bearerUserId } from "@/lib/api-auth";
import { prisma } from "@/lib/prisma";
@@ -59,8 +59,8 @@ export async function POST(req: Request) {
// value, otherwise leave it null.
let categoryId: bigint | null = null;
if (body.categoryId !== undefined && body.categoryId !== null && body.categoryId !== "") {
const raw = String(body.categoryId);
if (/^\d+$/.test(raw)) categoryId = BigInt(raw);
categoryId = positiveBigInt(String(body.categoryId));
if (!categoryId) return apiError("A valid category id is required", 422);
}
try {