From 90d3b35069bf3966f9c26ef9b3b5ab95ec875d69 Mon Sep 17 00:00:00 2001 From: kurihada Date: Thu, 26 Feb 2026 20:09:40 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E6=88=BF=E9=97=B4=20swipe/undo/reset=20?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E6=B7=BB=E5=8A=A0=E6=88=90=E5=91=98=E8=BA=AB?= =?UTF-8?q?=E4=BB=BD=E6=A0=A1=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - swipe/undo:校验 userId 在 data.users 中,非成员返回 403 - reset:要求传 userId,校验为房间成员或创建者 - 客户端 handleReset/handleNarrow 传入 userId --- src/app/api/room/[id]/reset/route.ts | 19 +++++++++++-------- src/app/api/room/[id]/swipe/route.ts | 4 ++++ src/app/api/room/[id]/undo/route.ts | 4 ++++ src/app/room/[id]/page.tsx | 12 ++++++++---- 4 files changed, 27 insertions(+), 12 deletions(-) diff --git a/src/app/api/room/[id]/reset/route.ts b/src/app/api/room/[id]/reset/route.ts index 1570906..031ace1 100644 --- a/src/app/api/room/[id]/reset/route.ts +++ b/src/app/api/room/[id]/reset/route.ts @@ -1,22 +1,25 @@ import { NextResponse } from "next/server"; import { atomicUpdateRoom } from "@/lib/store"; import { notify } from "@/lib/roomEvents"; -import { apiHandler, ApiError } from "@/lib/api"; +import { apiHandler, ApiError, requireUserId } from "@/lib/api"; export const POST = apiHandler(async (req, { params }) => { const { id } = await params; + const body = await req.json().catch(() => ({})); + const userId = body?.userId; + requireUserId(userId); + let restaurantIds: string[] | undefined; - try { - const body = await req.json().catch(() => null); - if (body?.restaurantIds && Array.isArray(body.restaurantIds)) { - restaurantIds = body.restaurantIds; - } - } catch { - // No body or invalid JSON — plain reset + if (body?.restaurantIds && Array.isArray(body.restaurantIds)) { + restaurantIds = body.restaurantIds; } const updated = await atomicUpdateRoom(id, (data) => { + if (!data.users.includes(userId) && data.creatorId !== userId) { + throw new ApiError("只有房间成员可以重置", 403); + } + if (restaurantIds && restaurantIds.length > 0) { const idSet = new Set(restaurantIds); data.restaurants = data.restaurants.filter((r) => idSet.has(r.id)); diff --git a/src/app/api/room/[id]/swipe/route.ts b/src/app/api/room/[id]/swipe/route.ts index bc5325c..879eb7a 100644 --- a/src/app/api/room/[id]/swipe/route.ts +++ b/src/app/api/room/[id]/swipe/route.ts @@ -15,6 +15,10 @@ export const POST = apiHandler(async (req, { params }) => { const rid = String(restaurantId); const updated = await atomicUpdateRoom(id, (data) => { + if (!data.users.includes(userId)) { + throw new ApiError("你不是该房间的成员", 403); + } + const restaurantIndex = data.restaurants.findIndex((r) => r.id === rid); const alreadySwiped = restaurantIndex >= 0 && diff --git a/src/app/api/room/[id]/undo/route.ts b/src/app/api/room/[id]/undo/route.ts index 92273a4..8b37b18 100644 --- a/src/app/api/room/[id]/undo/route.ts +++ b/src/app/api/room/[id]/undo/route.ts @@ -13,6 +13,10 @@ export const POST = apiHandler(async (req, { params }) => { const rid = String(restaurantId); const updated = await atomicUpdateRoom(id, (data) => { + if (!data.users.includes(userId)) { + throw new ApiError("你不是该房间的成员", 403); + } + if (data.likes[rid]) { data.likes[rid] = data.likes[rid].filter((u) => u !== userId); if (data.likes[rid].length === 0) { diff --git a/src/app/room/[id]/page.tsx b/src/app/room/[id]/page.tsx index f2e9f69..0bdc340 100644 --- a/src/app/room/[id]/page.tsx +++ b/src/app/room/[id]/page.tsx @@ -74,18 +74,22 @@ export default function RoomPage() { }, []); const handleReset = useCallback(async () => { - await fetch(`/api/room/${roomId}/reset`, { method: "POST" }); + await fetch(`/api/room/${roomId}/reset`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ userId }), + }); await mutate(); - }, [roomId, mutate]); + }, [roomId, userId, mutate]); const handleNarrow = useCallback(async (restaurantIds: string[]) => { await fetch(`/api/room/${roomId}/reset`, { method: "POST", headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ restaurantIds }), + body: JSON.stringify({ userId, restaurantIds }), }); await mutate(); - }, [roomId, mutate]); + }, [roomId, userId, mutate]); if (notFound || joinFailed) { return (