Fix crashes on unknown menu item / order routes
Both loaders threw a plain Error that the router doesn't treat as a route-level not-found, so visiting /items/<bad-id> or /orders/<bad-id> (or a valid order with a missing/wrong ?token) crashed instead of showing the NotFound page. Menu item lookups now throw notFound() directly; the order loader catches the server function's error and maps both "order not found" and "wrong access token" to notFound(), so a guessed id can't be used to probe which orders exist. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { Link, createFileRoute } from '@tanstack/react-router'
|
||||
import { Link, createFileRoute, notFound } from '@tanstack/react-router'
|
||||
|
||||
import { MenuItemDetail } from '#/components/ordering/MenuItemDetail.tsx'
|
||||
import { useBasket } from '#/lib/basket.tsx'
|
||||
@@ -48,7 +48,7 @@ export const Route = createFileRoute('/items/$menuItemId')({
|
||||
const brand = await getBrand()
|
||||
const menu = await getMenu({ data: { brandId: brand.id } })
|
||||
const item = menu.find((menuItem) => menuItem.id === Number(params.menuItemId))
|
||||
if (!item) throw new Error('Menu item not found')
|
||||
if (!item) throw notFound()
|
||||
return { item }
|
||||
},
|
||||
component: MenuItemRoute,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
import { createFileRoute, notFound } from '@tanstack/react-router'
|
||||
import { z } from 'zod'
|
||||
|
||||
import { OrderStatusView } from '#/components/orders/OrderStatusView.tsx'
|
||||
@@ -6,6 +6,16 @@ import { getOrderStatus } from '#/server/orders.ts'
|
||||
|
||||
const searchSchema = z.object({ token: z.string().optional() })
|
||||
|
||||
// Errors cross the server-function boundary as a plain `Error` carrying only
|
||||
// `.message` (TanStack Start's ShallowErrorPlugin drops the subclass), so an
|
||||
// unknown order and a wrong/missing guest token are told apart by message
|
||||
// rather than `instanceof`. Both collapse to a 404 rather than a real error
|
||||
// page, so a guessed order id can't be used to probe which ones exist.
|
||||
const notFoundMessages = new Set([
|
||||
'Order not found',
|
||||
'You do not have access to this Order',
|
||||
])
|
||||
|
||||
const OrderStatusRoute = () => {
|
||||
const status = Route.useLoaderData()
|
||||
const { token } = Route.useSearch()
|
||||
@@ -17,12 +27,20 @@ export const Route = createFileRoute('/orders/$orderId')({
|
||||
validateSearch: (search: Record<string, unknown>) =>
|
||||
searchSchema.parse(search),
|
||||
loaderDeps: ({ search }) => ({ token: search.token }),
|
||||
loader: ({ params, deps }) =>
|
||||
getOrderStatus({
|
||||
loader: async ({ params, deps }) => {
|
||||
try {
|
||||
return await getOrderStatus({
|
||||
data: {
|
||||
orderId: Number(params.orderId),
|
||||
guestAccessToken: deps.token,
|
||||
},
|
||||
}),
|
||||
})
|
||||
} catch (error) {
|
||||
if (error instanceof Error && notFoundMessages.has(error.message)) {
|
||||
throw notFound()
|
||||
}
|
||||
throw error
|
||||
}
|
||||
},
|
||||
component: OrderStatusRoute,
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user