From 8cd900f11e0f00a13b9b8a939a37bfa362c66fd9 Mon Sep 17 00:00:00 2001 From: Arpit Gupta Date: Tue, 31 Mar 2026 00:56:33 +0530 Subject: [PATCH 1/7] feat: add chat artist lookup endpoint --- app/api/chats/[id]/artist/route.ts | 35 +++++ .../__tests__/getChatArtistHandler.test.ts | 139 ++++++++++++++++++ lib/chats/getChatArtistHandler.ts | 86 +++++++++++ 3 files changed, 260 insertions(+) create mode 100644 app/api/chats/[id]/artist/route.ts create mode 100644 lib/chats/__tests__/getChatArtistHandler.test.ts create mode 100644 lib/chats/getChatArtistHandler.ts diff --git a/app/api/chats/[id]/artist/route.ts b/app/api/chats/[id]/artist/route.ts new file mode 100644 index 00000000..2ec3c371 --- /dev/null +++ b/app/api/chats/[id]/artist/route.ts @@ -0,0 +1,35 @@ +import type { NextRequest } from "next/server"; +import { NextResponse } from "next/server"; +import { getCorsHeaders } from "@/lib/networking/getCorsHeaders"; +import { getChatArtistHandler } from "@/lib/chats/getChatArtistHandler"; + +/** + * OPTIONS handler for CORS preflight requests. + * + * @returns A NextResponse with CORS headers. + */ +export async function OPTIONS() { + return new NextResponse(null, { + status: 200, + headers: getCorsHeaders(), + }); +} + +/** + * GET /api/chats/[id]/artist + * + * Retrieves the artist associated with a chat room. + * Returns 404 when the room does not exist or is not accessible by the caller. + * + * @param request - The incoming request object. + * @param root0 - The route context object. + * @param root0.params - The dynamic route params containing chat id. + * @returns A NextResponse with artist linkage data or an error. + */ +export async function GET( + request: NextRequest, + { params }: { params: Promise<{ id: string }> }, +): Promise { + const { id } = await params; + return getChatArtistHandler(request, id); +} diff --git a/lib/chats/__tests__/getChatArtistHandler.test.ts b/lib/chats/__tests__/getChatArtistHandler.test.ts new file mode 100644 index 00000000..692ca5e7 --- /dev/null +++ b/lib/chats/__tests__/getChatArtistHandler.test.ts @@ -0,0 +1,139 @@ +import { describe, expect, it, vi, beforeEach } from "vitest"; +import { NextRequest, NextResponse } from "next/server"; +import { getChatArtistHandler } from "@/lib/chats/getChatArtistHandler"; +import { validateAuthContext } from "@/lib/auth/validateAuthContext"; +import { canAccessAccount } from "@/lib/organizations/canAccessAccount"; +import selectRoom from "@/lib/supabase/rooms/selectRoom"; + +vi.mock("@/lib/auth/validateAuthContext", () => ({ + validateAuthContext: vi.fn(), +})); + +vi.mock("@/lib/organizations/canAccessAccount", () => ({ + canAccessAccount: vi.fn(), +})); + +vi.mock("@/lib/supabase/rooms/selectRoom", () => ({ + default: vi.fn(), +})); + +const createRequest = () => new NextRequest("http://localhost/api/chats/chat-id/artist"); + +describe("getChatArtistHandler", () => { + const accountId = "11111111-1111-1111-1111-111111111111"; + const roomId = "123e4567-e89b-42d3-a456-426614174000"; + const artistId = "123e4567-e89b-42d3-a456-426614174001"; + + beforeEach(() => { + vi.clearAllMocks(); + }); + + it("returns 400 for invalid chat id", async () => { + const response = await getChatArtistHandler(createRequest(), "invalid-id"); + const body = await response.json(); + + expect(response.status).toBe(400); + expect(body).toEqual({ + status: "error", + error: "id must be a valid UUID", + }); + }); + + it("returns auth error response when auth validation fails", async () => { + vi.mocked(validateAuthContext).mockResolvedValue( + NextResponse.json({ status: "error", error: "Unauthorized" }, { status: 401 }), + ); + + const response = await getChatArtistHandler(createRequest(), roomId); + const body = await response.json(); + + expect(response.status).toBe(401); + expect(body).toEqual({ + status: "error", + error: "Unauthorized", + }); + }); + + it("returns 404 when room does not exist", async () => { + vi.mocked(validateAuthContext).mockResolvedValue({ accountId, orgId: null }); + vi.mocked(selectRoom).mockResolvedValue(null); + + const response = await getChatArtistHandler(createRequest(), roomId); + const body = await response.json(); + + expect(response.status).toBe(404); + expect(body).toEqual({ + status: "error", + error: "Chat room not found", + }); + }); + + it("returns 404 when user has no access to another account's room", async () => { + vi.mocked(validateAuthContext).mockResolvedValue({ accountId, orgId: null }); + vi.mocked(selectRoom).mockResolvedValue({ + id: roomId, + account_id: "123e4567-e89b-42d3-a456-426614174002", + artist_id: artistId, + topic: "Test", + updated_at: null, + }); + vi.mocked(canAccessAccount).mockResolvedValue(false); + + const response = await getChatArtistHandler(createRequest(), roomId); + const body = await response.json(); + + expect(canAccessAccount).toHaveBeenCalledWith({ + currentAccountId: accountId, + targetAccountId: "123e4567-e89b-42d3-a456-426614174002", + }); + expect(response.status).toBe(404); + expect(body).toEqual({ + status: "error", + error: "Chat room not found", + }); + }); + + it("returns chat artist for room owner", async () => { + vi.mocked(validateAuthContext).mockResolvedValue({ accountId, orgId: null }); + vi.mocked(selectRoom).mockResolvedValue({ + id: roomId, + account_id: accountId, + artist_id: artistId, + topic: "Test", + updated_at: null, + }); + + const response = await getChatArtistHandler(createRequest(), roomId); + const body = await response.json(); + + expect(response.status).toBe(200); + expect(body).toEqual({ + status: "success", + room_id: roomId, + artist_id: artistId, + artist_exists: true, + }); + }); + + it("returns null artist fields when chat has no artist", async () => { + vi.mocked(validateAuthContext).mockResolvedValue({ accountId, orgId: null }); + vi.mocked(selectRoom).mockResolvedValue({ + id: roomId, + account_id: accountId, + artist_id: null, + topic: "Test", + updated_at: null, + }); + + const response = await getChatArtistHandler(createRequest(), roomId); + const body = await response.json(); + + expect(response.status).toBe(200); + expect(body).toEqual({ + status: "success", + room_id: roomId, + artist_id: null, + artist_exists: false, + }); + }); +}); diff --git a/lib/chats/getChatArtistHandler.ts b/lib/chats/getChatArtistHandler.ts new file mode 100644 index 00000000..c2b0a607 --- /dev/null +++ b/lib/chats/getChatArtistHandler.ts @@ -0,0 +1,86 @@ +import { NextResponse } from "next/server"; +import { z } from "zod"; +import { getCorsHeaders } from "@/lib/networking/getCorsHeaders"; +import { validateAuthContext } from "@/lib/auth/validateAuthContext"; +import { canAccessAccount } from "@/lib/organizations/canAccessAccount"; +import selectRoom from "@/lib/supabase/rooms/selectRoom"; + +const chatIdSchema = z.string().uuid("id must be a valid UUID"); + +/** + * Handles GET /api/chats/[id]/artist. + * + * Returns the artist associated with a chat room if accessible by the caller. + * + * @param request - The incoming request object for auth context. + * @param id - The chat room ID from route params. + * @returns A NextResponse with artist linkage data or an error. + */ +export async function getChatArtistHandler(request: Request, id: string): Promise { + const parsedId = chatIdSchema.safeParse(id); + if (!parsedId.success) { + return NextResponse.json( + { + status: "error", + error: parsedId.error.issues[0]?.message || "Invalid chat ID", + }, + { status: 400, headers: getCorsHeaders() }, + ); + } + + const authResult = await validateAuthContext(request); + if (authResult instanceof NextResponse) { + return authResult; + } + + const { accountId } = authResult; + const room = await selectRoom(parsedId.data); + + if (!room) { + return NextResponse.json( + { + status: "error", + error: "Chat room not found", + }, + { status: 404, headers: getCorsHeaders() }, + ); + } + + const roomAccountId = room.account_id; + if (!roomAccountId) { + return NextResponse.json( + { + status: "error", + error: "Chat room is missing account_id", + }, + { status: 500, headers: getCorsHeaders() }, + ); + } + + if (roomAccountId !== accountId) { + const hasAccess = await canAccessAccount({ + currentAccountId: accountId, + targetAccountId: roomAccountId, + }); + + if (!hasAccess) { + return NextResponse.json( + { + status: "error", + error: "Chat room not found", + }, + { status: 404, headers: getCorsHeaders() }, + ); + } + } + + return NextResponse.json( + { + status: "success", + room_id: room.id, + artist_id: room.artist_id, + artist_exists: Boolean(room.artist_id), + }, + { status: 200, headers: getCorsHeaders() }, + ); +} From acbe6fa9c72373b65bd45c4c11b1afa35bfa0c1e Mon Sep 17 00:00:00 2001 From: Arpit Gupta Date: Tue, 31 Mar 2026 01:06:20 +0530 Subject: [PATCH 2/7] refactor: share chat access resolver for chat artist endpoint --- lib/chats/getChatArtistHandler.ts | 71 +++---------------------- lib/chats/resolveAccessibleRoom.ts | 85 ++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 64 deletions(-) create mode 100644 lib/chats/resolveAccessibleRoom.ts diff --git a/lib/chats/getChatArtistHandler.ts b/lib/chats/getChatArtistHandler.ts index c2b0a607..c029d33a 100644 --- a/lib/chats/getChatArtistHandler.ts +++ b/lib/chats/getChatArtistHandler.ts @@ -1,11 +1,6 @@ import { NextResponse } from "next/server"; -import { z } from "zod"; import { getCorsHeaders } from "@/lib/networking/getCorsHeaders"; -import { validateAuthContext } from "@/lib/auth/validateAuthContext"; -import { canAccessAccount } from "@/lib/organizations/canAccessAccount"; -import selectRoom from "@/lib/supabase/rooms/selectRoom"; - -const chatIdSchema = z.string().uuid("id must be a valid UUID"); +import { resolveAccessibleRoom } from "@/lib/chats/resolveAccessibleRoom"; /** * Handles GET /api/chats/[id]/artist. @@ -17,69 +12,17 @@ const chatIdSchema = z.string().uuid("id must be a valid UUID"); * @returns A NextResponse with artist linkage data or an error. */ export async function getChatArtistHandler(request: Request, id: string): Promise { - const parsedId = chatIdSchema.safeParse(id); - if (!parsedId.success) { - return NextResponse.json( - { - status: "error", - error: parsedId.error.issues[0]?.message || "Invalid chat ID", - }, - { status: 400, headers: getCorsHeaders() }, - ); - } - - const authResult = await validateAuthContext(request); - if (authResult instanceof NextResponse) { - return authResult; - } - - const { accountId } = authResult; - const room = await selectRoom(parsedId.data); - - if (!room) { - return NextResponse.json( - { - status: "error", - error: "Chat room not found", - }, - { status: 404, headers: getCorsHeaders() }, - ); - } - - const roomAccountId = room.account_id; - if (!roomAccountId) { - return NextResponse.json( - { - status: "error", - error: "Chat room is missing account_id", - }, - { status: 500, headers: getCorsHeaders() }, - ); - } - - if (roomAccountId !== accountId) { - const hasAccess = await canAccessAccount({ - currentAccountId: accountId, - targetAccountId: roomAccountId, - }); - - if (!hasAccess) { - return NextResponse.json( - { - status: "error", - error: "Chat room not found", - }, - { status: 404, headers: getCorsHeaders() }, - ); - } + const roomResult = await resolveAccessibleRoom(request, id); + if (roomResult instanceof NextResponse) { + return roomResult; } return NextResponse.json( { status: "success", - room_id: room.id, - artist_id: room.artist_id, - artist_exists: Boolean(room.artist_id), + room_id: roomResult.room.id, + artist_id: roomResult.room.artist_id, + artist_exists: Boolean(roomResult.room.artist_id), }, { status: 200, headers: getCorsHeaders() }, ); diff --git a/lib/chats/resolveAccessibleRoom.ts b/lib/chats/resolveAccessibleRoom.ts new file mode 100644 index 00000000..1ebcd4f5 --- /dev/null +++ b/lib/chats/resolveAccessibleRoom.ts @@ -0,0 +1,85 @@ +import { NextResponse } from "next/server"; +import { z } from "zod"; +import { getCorsHeaders } from "@/lib/networking/getCorsHeaders"; +import { validateAuthContext } from "@/lib/auth/validateAuthContext"; +import { canAccessAccount } from "@/lib/organizations/canAccessAccount"; +import selectRoom from "@/lib/supabase/rooms/selectRoom"; +import type { Tables } from "@/types/database.types"; + +const chatIdSchema = z.string().uuid("id must be a valid UUID"); + +interface ResolveAccessibleRoomResult { + room: Tables<"rooms">; + accountId: string; +} + +/** + * Resolves a chat room and validates access for the authenticated caller. + * + * @param request - The incoming request object for auth context. + * @param id - The chat room ID from route params. + * @returns The room and authenticated accountId, or an error NextResponse. + */ +export async function resolveAccessibleRoom( + request: Request, + id: string, +): Promise { + const parsedId = chatIdSchema.safeParse(id); + if (!parsedId.success) { + return NextResponse.json( + { + status: "error", + error: parsedId.error.issues[0]?.message || "Invalid chat ID", + }, + { status: 400, headers: getCorsHeaders() }, + ); + } + + const authResult = await validateAuthContext(request); + if (authResult instanceof NextResponse) { + return authResult; + } + + const { accountId } = authResult; + const room = await selectRoom(parsedId.data); + + if (!room) { + return NextResponse.json( + { + status: "error", + error: "Chat room not found", + }, + { status: 404, headers: getCorsHeaders() }, + ); + } + + const roomAccountId = room.account_id; + if (!roomAccountId) { + return NextResponse.json( + { + status: "error", + error: "Chat room is missing account_id", + }, + { status: 500, headers: getCorsHeaders() }, + ); + } + + if (roomAccountId !== accountId) { + const hasAccess = await canAccessAccount({ + currentAccountId: accountId, + targetAccountId: roomAccountId, + }); + + if (!hasAccess) { + return NextResponse.json( + { + status: "error", + error: "Chat room not found", + }, + { status: 404, headers: getCorsHeaders() }, + ); + } + } + + return { room, accountId }; +} From 28d4632e11f941aae7dc1e6f33ba5318f756d626 Mon Sep 17 00:00:00 2001 From: Arpit Gupta Date: Tue, 31 Mar 2026 01:13:32 +0530 Subject: [PATCH 3/7] refactor: align chat artist endpoint with shared room resolver --- .../__tests__/getChatArtistHandler.test.ts | 31 +++++++++++------- lib/chats/resolveAccessibleRoom.ts | 32 ++++++------------- 2 files changed, 28 insertions(+), 35 deletions(-) diff --git a/lib/chats/__tests__/getChatArtistHandler.test.ts b/lib/chats/__tests__/getChatArtistHandler.test.ts index 692ca5e7..c88ce650 100644 --- a/lib/chats/__tests__/getChatArtistHandler.test.ts +++ b/lib/chats/__tests__/getChatArtistHandler.test.ts @@ -2,21 +2,21 @@ import { describe, expect, it, vi, beforeEach } from "vitest"; import { NextRequest, NextResponse } from "next/server"; import { getChatArtistHandler } from "@/lib/chats/getChatArtistHandler"; import { validateAuthContext } from "@/lib/auth/validateAuthContext"; -import { canAccessAccount } from "@/lib/organizations/canAccessAccount"; import selectRoom from "@/lib/supabase/rooms/selectRoom"; +import { buildGetChatsParams } from "@/lib/chats/buildGetChatsParams"; vi.mock("@/lib/auth/validateAuthContext", () => ({ validateAuthContext: vi.fn(), })); -vi.mock("@/lib/organizations/canAccessAccount", () => ({ - canAccessAccount: vi.fn(), -})); - vi.mock("@/lib/supabase/rooms/selectRoom", () => ({ default: vi.fn(), })); +vi.mock("@/lib/chats/buildGetChatsParams", () => ({ + buildGetChatsParams: vi.fn(), +})); + const createRequest = () => new NextRequest("http://localhost/api/chats/chat-id/artist"); describe("getChatArtistHandler", () => { @@ -77,19 +77,18 @@ describe("getChatArtistHandler", () => { topic: "Test", updated_at: null, }); - vi.mocked(canAccessAccount).mockResolvedValue(false); + vi.mocked(buildGetChatsParams).mockResolvedValue({ + params: { account_ids: [accountId] }, + error: null, + }); const response = await getChatArtistHandler(createRequest(), roomId); const body = await response.json(); - expect(canAccessAccount).toHaveBeenCalledWith({ - currentAccountId: accountId, - targetAccountId: "123e4567-e89b-42d3-a456-426614174002", - }); - expect(response.status).toBe(404); + expect(response.status).toBe(403); expect(body).toEqual({ status: "error", - error: "Chat room not found", + error: "Access denied to this chat", }); }); @@ -102,6 +101,10 @@ describe("getChatArtistHandler", () => { topic: "Test", updated_at: null, }); + vi.mocked(buildGetChatsParams).mockResolvedValue({ + params: { account_ids: [accountId] }, + error: null, + }); const response = await getChatArtistHandler(createRequest(), roomId); const body = await response.json(); @@ -124,6 +127,10 @@ describe("getChatArtistHandler", () => { topic: "Test", updated_at: null, }); + vi.mocked(buildGetChatsParams).mockResolvedValue({ + params: { account_ids: [accountId] }, + error: null, + }); const response = await getChatArtistHandler(createRequest(), roomId); const body = await response.json(); diff --git a/lib/chats/resolveAccessibleRoom.ts b/lib/chats/resolveAccessibleRoom.ts index 1ebcd4f5..6c3be38b 100644 --- a/lib/chats/resolveAccessibleRoom.ts +++ b/lib/chats/resolveAccessibleRoom.ts @@ -2,8 +2,8 @@ import { NextResponse } from "next/server"; import { z } from "zod"; import { getCorsHeaders } from "@/lib/networking/getCorsHeaders"; import { validateAuthContext } from "@/lib/auth/validateAuthContext"; -import { canAccessAccount } from "@/lib/organizations/canAccessAccount"; import selectRoom from "@/lib/supabase/rooms/selectRoom"; +import { buildGetChatsParams } from "@/lib/chats/buildGetChatsParams"; import type { Tables } from "@/types/database.types"; const chatIdSchema = z.string().uuid("id must be a valid UUID"); @@ -53,30 +53,16 @@ export async function resolveAccessibleRoom( ); } - const roomAccountId = room.account_id; - if (!roomAccountId) { - return NextResponse.json( - { - status: "error", - error: "Chat room is missing account_id", - }, - { status: 500, headers: getCorsHeaders() }, - ); - } - - if (roomAccountId !== accountId) { - const hasAccess = await canAccessAccount({ - currentAccountId: accountId, - targetAccountId: roomAccountId, - }); + const { params } = await buildGetChatsParams({ + account_id: accountId, + }); - if (!hasAccess) { + // If params.account_ids is undefined, it means admin access (all records) + if (params.account_ids && room.account_id) { + if (!params.account_ids.includes(room.account_id)) { return NextResponse.json( - { - status: "error", - error: "Chat room not found", - }, - { status: 404, headers: getCorsHeaders() }, + { status: "error", error: "Access denied to this chat" }, + { status: 403, headers: getCorsHeaders() }, ); } } From 73d334a7ca659fd805d0063f37679dc224b24371 Mon Sep 17 00:00:00 2001 From: Arpit Gupta Date: Tue, 31 Mar 2026 02:01:47 +0530 Subject: [PATCH 4/7] refactor: rename room resolver to validateChatAccess --- lib/chats/getChatArtistHandler.ts | 4 ++-- .../{resolveAccessibleRoom.ts => validateChatAccess.ts} | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) rename lib/chats/{resolveAccessibleRoom.ts => validateChatAccess.ts} (89%) diff --git a/lib/chats/getChatArtistHandler.ts b/lib/chats/getChatArtistHandler.ts index c029d33a..c7a8c21e 100644 --- a/lib/chats/getChatArtistHandler.ts +++ b/lib/chats/getChatArtistHandler.ts @@ -1,6 +1,6 @@ import { NextResponse } from "next/server"; import { getCorsHeaders } from "@/lib/networking/getCorsHeaders"; -import { resolveAccessibleRoom } from "@/lib/chats/resolveAccessibleRoom"; +import { validateChatAccess } from "@/lib/chats/validateChatAccess"; /** * Handles GET /api/chats/[id]/artist. @@ -12,7 +12,7 @@ import { resolveAccessibleRoom } from "@/lib/chats/resolveAccessibleRoom"; * @returns A NextResponse with artist linkage data or an error. */ export async function getChatArtistHandler(request: Request, id: string): Promise { - const roomResult = await resolveAccessibleRoom(request, id); + const roomResult = await validateChatAccess(request, id); if (roomResult instanceof NextResponse) { return roomResult; } diff --git a/lib/chats/resolveAccessibleRoom.ts b/lib/chats/validateChatAccess.ts similarity index 89% rename from lib/chats/resolveAccessibleRoom.ts rename to lib/chats/validateChatAccess.ts index 6c3be38b..62696042 100644 --- a/lib/chats/resolveAccessibleRoom.ts +++ b/lib/chats/validateChatAccess.ts @@ -8,22 +8,22 @@ import type { Tables } from "@/types/database.types"; const chatIdSchema = z.string().uuid("id must be a valid UUID"); -interface ResolveAccessibleRoomResult { +interface ValidatedChatAccess { room: Tables<"rooms">; accountId: string; } /** - * Resolves a chat room and validates access for the authenticated caller. + * Validates that the authenticated caller can access a chat room. * * @param request - The incoming request object for auth context. * @param id - The chat room ID from route params. * @returns The room and authenticated accountId, or an error NextResponse. */ -export async function resolveAccessibleRoom( +export async function validateChatAccess( request: Request, id: string, -): Promise { +): Promise { const parsedId = chatIdSchema.safeParse(id); if (!parsedId.success) { return NextResponse.json( From c59b00b4818ec32ea2afeaa275a0b6b83c9165f1 Mon Sep 17 00:00:00 2001 From: Arpit Gupta Date: Tue, 31 Mar 2026 02:41:04 +0530 Subject: [PATCH 5/7] fix: use NextRequest in chat artist handler --- lib/chats/getChatArtistHandler.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/chats/getChatArtistHandler.ts b/lib/chats/getChatArtistHandler.ts index c7a8c21e..ad04d8dc 100644 --- a/lib/chats/getChatArtistHandler.ts +++ b/lib/chats/getChatArtistHandler.ts @@ -1,3 +1,4 @@ +import type { NextRequest } from "next/server"; import { NextResponse } from "next/server"; import { getCorsHeaders } from "@/lib/networking/getCorsHeaders"; import { validateChatAccess } from "@/lib/chats/validateChatAccess"; @@ -11,7 +12,10 @@ import { validateChatAccess } from "@/lib/chats/validateChatAccess"; * @param id - The chat room ID from route params. * @returns A NextResponse with artist linkage data or an error. */ -export async function getChatArtistHandler(request: Request, id: string): Promise { +export async function getChatArtistHandler( + request: NextRequest, + id: string, +): Promise { const roomResult = await validateChatAccess(request, id); if (roomResult instanceof NextResponse) { return roomResult; From 04a6fab3156cf3a1281ecd30b1763c27938bb01b Mon Sep 17 00:00:00 2001 From: Arpit Gupta Date: Tue, 31 Mar 2026 02:47:26 +0530 Subject: [PATCH 6/7] chore: retrigger vercel deployment From e50bb16d5772d5d60684f8e0f83e1f2ea7d2038f Mon Sep 17 00:00:00 2001 From: Arpit Gupta Date: Tue, 31 Mar 2026 02:50:18 +0530 Subject: [PATCH 7/7] fix: use NextRequest in validate chat access --- lib/chats/validateChatAccess.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/chats/validateChatAccess.ts b/lib/chats/validateChatAccess.ts index 62696042..0be42739 100644 --- a/lib/chats/validateChatAccess.ts +++ b/lib/chats/validateChatAccess.ts @@ -1,3 +1,4 @@ +import type { NextRequest } from "next/server"; import { NextResponse } from "next/server"; import { z } from "zod"; import { getCorsHeaders } from "@/lib/networking/getCorsHeaders"; @@ -21,7 +22,7 @@ interface ValidatedChatAccess { * @returns The room and authenticated accountId, or an error NextResponse. */ export async function validateChatAccess( - request: Request, + request: NextRequest, id: string, ): Promise { const parsedId = chatIdSchema.safeParse(id);