feat: implement issues #5-#8 — error states, header search, delete/toggle visibility, auth-aware UI
This commit is contained in:
111
src/app/api/comparisons/[id]/route.ts
Normal file
111
src/app/api/comparisons/[id]/route.ts
Normal file
@@ -0,0 +1,111 @@
|
||||
import { db } from "@/lib/db";
|
||||
import { comparisons, sessions, users } from "@/lib/db/schema";
|
||||
import { eq, and, gt } from "drizzle-orm";
|
||||
import { headers } from "next/headers";
|
||||
|
||||
async function getAuthedUserId(): Promise<string | null> {
|
||||
const hdrs = await headers();
|
||||
const cookieHeader = hdrs.get("cookie") ?? "";
|
||||
const cookieMatch = cookieHeader
|
||||
.split(";")
|
||||
.map((c) => c.trim())
|
||||
.find(
|
||||
(c) =>
|
||||
c.startsWith("__Secure-better-auth.session_token=") ||
|
||||
c.startsWith("better-auth.session_token=")
|
||||
);
|
||||
const token = cookieMatch
|
||||
?.split("=")
|
||||
?.slice(1)
|
||||
?.join("=")
|
||||
?.trim()
|
||||
.split(".")[0];
|
||||
if (!token) return null;
|
||||
|
||||
const sessionRows = await db
|
||||
.select()
|
||||
.from(sessions)
|
||||
.where(and(eq(sessions.token, token), gt(sessions.expiresAt, new Date())))
|
||||
.limit(1);
|
||||
if (!sessionRows.length) return null;
|
||||
|
||||
const userRows = await db
|
||||
.select()
|
||||
.from(users)
|
||||
.where(eq(users.id, sessionRows[0].userId))
|
||||
.limit(1);
|
||||
if (!userRows.length) return null;
|
||||
|
||||
return userRows[0].id;
|
||||
}
|
||||
|
||||
export async function DELETE(
|
||||
_request: Request,
|
||||
{ params }: { params: Promise<{ id: string }> }
|
||||
) {
|
||||
const userId = await getAuthedUserId();
|
||||
if (!userId) {
|
||||
return Response.json({ error: "Unauthorized" }, { status: 401 });
|
||||
}
|
||||
|
||||
const { id } = await params;
|
||||
|
||||
const existing = await db
|
||||
.select({ userId: comparisons.userId })
|
||||
.from(comparisons)
|
||||
.where(eq(comparisons.id, id))
|
||||
.limit(1);
|
||||
|
||||
if (!existing.length) {
|
||||
return Response.json({ error: "Not found" }, { status: 404 });
|
||||
}
|
||||
|
||||
if (existing[0].userId !== userId) {
|
||||
return Response.json({ error: "Forbidden" }, { status: 403 });
|
||||
}
|
||||
|
||||
await db.delete(comparisons).where(eq(comparisons.id, id));
|
||||
|
||||
return Response.json({ success: true });
|
||||
}
|
||||
|
||||
export async function PATCH(
|
||||
request: Request,
|
||||
{ params }: { params: Promise<{ id: string }> }
|
||||
) {
|
||||
const userId = await getAuthedUserId();
|
||||
if (!userId) {
|
||||
return Response.json({ error: "Unauthorized" }, { status: 401 });
|
||||
}
|
||||
|
||||
const { id } = await params;
|
||||
|
||||
const existing = await db
|
||||
.select({ userId: comparisons.userId, isPublic: comparisons.isPublic })
|
||||
.from(comparisons)
|
||||
.where(eq(comparisons.id, id))
|
||||
.limit(1);
|
||||
|
||||
if (!existing.length) {
|
||||
return Response.json({ error: "Not found" }, { status: 404 });
|
||||
}
|
||||
|
||||
if (existing[0].userId !== userId) {
|
||||
return Response.json({ error: "Forbidden" }, { status: 403 });
|
||||
}
|
||||
|
||||
const body = await request.json();
|
||||
const newIsPublic =
|
||||
typeof body.isPublic === "boolean" ? body.isPublic : !existing[0].isPublic;
|
||||
|
||||
const [updated] = await db
|
||||
.update(comparisons)
|
||||
.set({ isPublic: newIsPublic, updatedAt: new Date() })
|
||||
.where(eq(comparisons.id, id))
|
||||
.returning();
|
||||
|
||||
return Response.json({
|
||||
id: updated.id,
|
||||
isPublic: updated.isPublic,
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user