From 776f121eae8a299b2ae479eb0673ca1ab492b832 Mon Sep 17 00:00:00 2001 From: Christopher Mayor Date: Tue, 28 Apr 2026 08:31:01 -0700 Subject: [PATCH] fix: merge comparisons [id] and [slug] routes to resolve ambiguous route error --- src/app/(main)/profile/page.tsx | 4 +- src/app/api/comparisons/[id]/route.ts | 111 ----------------------- src/app/api/comparisons/[slug]/route.ts | 116 +++++++++++++++++++++++- 3 files changed, 116 insertions(+), 115 deletions(-) delete mode 100644 src/app/api/comparisons/[id]/route.ts diff --git a/src/app/(main)/profile/page.tsx b/src/app/(main)/profile/page.tsx index 9cd3e78..5db6599 100644 --- a/src/app/(main)/profile/page.tsx +++ b/src/app/(main)/profile/page.tsx @@ -99,7 +99,7 @@ export default function ProfilePage() { const handleToggleVisibility = async (comparison: Comparison) => { try { - const res = await fetch(`/api/comparisons/${comparison.id}`, { + const res = await fetch(`/api/comparisons/${comparison.slug}`, { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ isPublic: !comparison.isPublic }), @@ -116,7 +116,7 @@ export default function ProfilePage() { const handleDelete = async (comparison: Comparison) => { if (!window.confirm(`Delete "${comparison.title}"? This cannot be undone.`)) return try { - const res = await fetch(`/api/comparisons/${comparison.id}`, { + const res = await fetch(`/api/comparisons/${comparison.slug}`, { method: "DELETE", }) if (res.ok) { diff --git a/src/app/api/comparisons/[id]/route.ts b/src/app/api/comparisons/[id]/route.ts deleted file mode 100644 index a5283b3..0000000 --- a/src/app/api/comparisons/[id]/route.ts +++ /dev/null @@ -1,111 +0,0 @@ -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 { - 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, - }); -} diff --git a/src/app/api/comparisons/[slug]/route.ts b/src/app/api/comparisons/[slug]/route.ts index f614324..972135f 100644 --- a/src/app/api/comparisons/[slug]/route.ts +++ b/src/app/api/comparisons/[slug]/route.ts @@ -1,8 +1,45 @@ import { db } from "@/lib/db"; -import { comparisons } from "@/lib/db/schema"; -import { eq, sql } from "drizzle-orm"; +import { comparisons, sessions, users } from "@/lib/db/schema"; +import { eq, sql, and, gt } from "drizzle-orm"; +import { headers } from "next/headers"; import { getComparison } from "@/app/actions/comparison"; +async function getAuthedUserId(): Promise { + 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 GET( _request: Request, { params }: { params: Promise<{ slug: string }> } @@ -22,3 +59,78 @@ export async function GET( return Response.json(comparison); } + +export async function DELETE( + _request: Request, + { params }: { params: Promise<{ slug: string }> } +) { + const userId = await getAuthedUserId(); + if (!userId) { + return Response.json({ error: "Unauthorized" }, { status: 401 }); + } + + const { slug } = await params; + + const existing = await db + .select({ id: comparisons.id, userId: comparisons.userId }) + .from(comparisons) + .where(eq(comparisons.slug, slug)) + .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, existing[0].id)); + + return Response.json({ success: true }); +} + +export async function PATCH( + request: Request, + { params }: { params: Promise<{ slug: string }> } +) { + const userId = await getAuthedUserId(); + if (!userId) { + return Response.json({ error: "Unauthorized" }, { status: 401 }); + } + + const { slug } = await params; + + const existing = await db + .select({ + id: comparisons.id, + userId: comparisons.userId, + isPublic: comparisons.isPublic, + }) + .from(comparisons) + .where(eq(comparisons.slug, slug)) + .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, existing[0].id)) + .returning(); + + return Response.json({ + id: updated.id, + isPublic: updated.isPublic, + }); +}