-
Notifications
You must be signed in to change notification settings - Fork 1
fix cart security promo validation and chat fallback #8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| import { NextResponse } from "next/server"; | ||
| import { supabase } from "../../../lib/supabase"; | ||
|
|
||
| export async function GET(request: Request) { | ||
| const { searchParams } = new URL(request.url); | ||
| const userId = searchParams.get("userId"); | ||
|
|
||
| if (!userId) { | ||
| return NextResponse.json({ error: "userId required" }, { status: 400 }); | ||
| } | ||
|
|
||
| try { | ||
| const { data: customer, error } = await supabase | ||
| .from("customers") | ||
| .select("id") | ||
| .eq("user_id", userId) | ||
| .single(); | ||
|
|
||
| if (error && error.code !== "PGRST116") { | ||
| throw error; | ||
| } | ||
|
|
||
| if (customer?.id) { | ||
| return NextResponse.json({ customerId: customer.id }); | ||
| } | ||
|
|
||
| const { data: newCustomer, error: createError } = await supabase | ||
| .from("customers") | ||
| .upsert([{ user_id: userId }], { onConflict: "user_id" }) | ||
| .select("id") | ||
| .single(); | ||
|
|
||
| if (createError) { | ||
| throw createError; | ||
| } | ||
|
|
||
| return NextResponse.json({ customerId: newCustomer?.id ?? null }); | ||
| } catch (err) { | ||
| console.error("Error resolving customer:", err); | ||
| return NextResponse.json( | ||
| { | ||
| error: "Failed to resolve customer", | ||
| details: err instanceof Error ? err.message : String(err), | ||
| }, | ||
| { status: 500 } | ||
| ); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,12 +4,24 @@ import { supabase } from "../../lib/supabase"; | |
| export async function GET(request: Request) { | ||
| const { searchParams } = new URL(request.url); | ||
| const customerId = searchParams.get("customerId"); | ||
| const userId = searchParams.get("userId"); | ||
|
|
||
| if (!customerId) { | ||
| return NextResponse.json({ error: "customerId required" }, { status: 400 }); | ||
| if (!customerId || !userId) { | ||
| return NextResponse.json({ error: "customerId and userId required" }, { status: 400 }); | ||
| } | ||
|
Comment on lines
6
to
11
|
||
|
|
||
| try { | ||
| const { data: customer, error: customerError } = await supabase | ||
| .from("customers") | ||
| .select("id") | ||
| .eq("id", customerId) | ||
| .eq("user_id", userId) | ||
| .maybeSingle(); | ||
| if (customerError) throw customerError; | ||
| if (!customer) { | ||
| return NextResponse.json({ error: "Forbidden" }, { status: 403 }); | ||
| } | ||
|
|
||
| const { data, error } = await supabase | ||
| .from("carts") | ||
| .select("items") | ||
|
|
@@ -33,19 +45,31 @@ export async function GET(request: Request) { | |
| export async function POST(request: Request) { | ||
| const body = (await request.json()) as { | ||
| customerId?: string; | ||
| userId?: string; | ||
| restaurantId?: string; | ||
| items?: unknown[]; | ||
| }; | ||
| const { customerId, restaurantId, items } = body; | ||
| const { customerId, userId, restaurantId, items } = body; | ||
|
|
||
| if (!customerId || !restaurantId) { | ||
| if (!customerId || !userId || !restaurantId) { | ||
| return NextResponse.json( | ||
| { error: "customerId and restaurantId required" }, | ||
| { error: "customerId, userId and restaurantId required" }, | ||
| { status: 400 } | ||
| ); | ||
| } | ||
|
|
||
| try { | ||
| const { data: customer, error: customerError } = await supabase | ||
| .from("customers") | ||
| .select("id") | ||
| .eq("id", customerId) | ||
| .eq("user_id", userId) | ||
| .maybeSingle(); | ||
| if (customerError) throw customerError; | ||
| if (!customer) { | ||
| return NextResponse.json({ error: "Forbidden" }, { status: 403 }); | ||
| } | ||
|
|
||
| const { error } = await supabase.from("carts").upsert( | ||
| { | ||
| customer_id: customerId, | ||
|
|
@@ -71,12 +95,24 @@ export async function POST(request: Request) { | |
| export async function DELETE(request: Request) { | ||
| const { searchParams } = new URL(request.url); | ||
| const customerId = searchParams.get("customerId"); | ||
| const userId = searchParams.get("userId"); | ||
|
|
||
| if (!customerId) { | ||
| return NextResponse.json({ error: "customerId required" }, { status: 400 }); | ||
| if (!customerId || !userId) { | ||
| return NextResponse.json({ error: "customerId and userId required" }, { status: 400 }); | ||
| } | ||
|
|
||
| try { | ||
| const { data: customer, error: customerError } = await supabase | ||
| .from("customers") | ||
| .select("id") | ||
| .eq("id", customerId) | ||
| .eq("user_id", userId) | ||
| .maybeSingle(); | ||
| if (customerError) throw customerError; | ||
| if (!customer) { | ||
| return NextResponse.json({ error: "Forbidden" }, { status: 403 }); | ||
| } | ||
|
|
||
| const { error } = await supabase | ||
| .from("carts") | ||
| .delete() | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This endpoint accepts an arbitrary
userIdand will return/create acustomerIdfor it. Combined with the cart/orders routes that also trustuserId, this allows any caller to obtain another user’s customer record and then read/write their cart/orders. Require server-verified authentication and deriveuserIdfrom the session instead of a query param (and avoid creating customer records for unauthenticated callers).