From e85bc0bc079d7854e0259e3e5c1ab9597e3d77bb Mon Sep 17 00:00:00 2001 From: Michelle Chen Date: Mon, 13 May 2024 17:37:16 -0400 Subject: [PATCH] remove all of redirect --- .../hydrogen/src/cart/cartSetIdDefault.ts | 7 +- .../hydrogen/src/cart/createCartHandler.ts | 10 ++- templates/skeleton/app/routes/account.$.tsx | 6 +- .../skeleton/app/routes/account_.logout.tsx | 9 +- templates/skeleton/app/routes/cart.$lines.tsx | 86 +++++++++++-------- templates/skeleton/app/routes/cart.tsx | 14 ++- .../skeleton/app/routes/discount.$code.tsx | 79 +++++++++-------- 7 files changed, 119 insertions(+), 92 deletions(-) diff --git a/packages/hydrogen/src/cart/cartSetIdDefault.ts b/packages/hydrogen/src/cart/cartSetIdDefault.ts index 485020ba09..a12353a9be 100644 --- a/packages/hydrogen/src/cart/cartSetIdDefault.ts +++ b/packages/hydrogen/src/cart/cartSetIdDefault.ts @@ -1,3 +1,5 @@ +import {type ResponseStub} from '@remix-run/server-runtime/dist/single-fetch'; +import {HydrogenSession} from '../hydrogen'; import {stringify} from 'worktop/cookie'; export type CookieOptions = { @@ -11,8 +13,8 @@ export type CookieOptions = { }; export const cartSetIdDefault = (cookieOptions?: CookieOptions) => { - return (cartId: string) => { - const headers = new Headers(); + return (cartId: string, response?: ResponseStub) => { + const headers = response ? response.headers : new Headers(); headers.append( 'Set-Cookie', stringify('cart', cartId.split('/').pop() || '', { @@ -20,6 +22,7 @@ export const cartSetIdDefault = (cookieOptions?: CookieOptions) => { ...cookieOptions, }), ); + return headers; }; }; diff --git a/packages/hydrogen/src/cart/createCartHandler.ts b/packages/hydrogen/src/cart/createCartHandler.ts index f8a5af11d8..ee0a1fb380 100644 --- a/packages/hydrogen/src/cart/createCartHandler.ts +++ b/packages/hydrogen/src/cart/createCartHandler.ts @@ -1,3 +1,5 @@ +import {type ResponseStub} from '@remix-run/server-runtime/dist/single-fetch'; + import {Storefront} from '../storefront'; import type {CustomerAccount} from '../customer/types'; import {type CartGetFunction, cartGetDefault} from './queries/cartGetDefault'; @@ -50,7 +52,7 @@ export type CartHandlerOptions = { storefront: Storefront; customerAccount?: CustomerAccount; getCartId: () => string | undefined; - setCartId: (cartId: string) => Headers; + setCartId: (cartId: string, response?: ResponseStub) => Headers; cartQueryFragment?: string; cartMutateFragment?: string; }; @@ -65,7 +67,7 @@ export type CartHandlerOptionsWithCustom< export type HydrogenCart = { get: ReturnType; getCartId: () => string | undefined; - setCartId: (cartId: string) => Headers; + setCartId: (cartId: string, response?: ResponseStub) => Headers; create: ReturnType; addLines: ReturnType; updateLines: ReturnType; @@ -202,7 +204,7 @@ export type CartHandlerOptionsForDocs< /** * A function that sets the cart ID. */ - setCartId: (cartId: string) => Headers; + setCartId: (cartId: string, response?: ResponseStub) => Headers; /** * The storefront client instance created by [`createStorefrontClient`](docs/api/hydrogen/2024-04/utilities/createstorefrontclient). */ @@ -255,7 +257,7 @@ export type HydrogenCartForDocs = { * Sets the unique identifier of the cart. * By default, it sets the ID in the header cookie. */ - setCartId?: (cartId: string) => Headers; + setCartId?: (cartId: string, response?: ResponseStub) => Headers; /** * Adds extra information (metafields) to the cart. * If the cart doesn't exist, a new one will be created. diff --git a/templates/skeleton/app/routes/account.$.tsx b/templates/skeleton/app/routes/account.$.tsx index ca52f2b68e..5d630aba61 100644 --- a/templates/skeleton/app/routes/account.$.tsx +++ b/templates/skeleton/app/routes/account.$.tsx @@ -1,11 +1,13 @@ -import {redirect, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; +import {type LoaderFunctionArgs} from '@shopify/remix-oxygen'; // fallback wild card for all unauthenticated routes in account section export async function loader({context}: LoaderFunctionArgs) { await context.customerAccount.handleAuthStatus(); - return redirect('/account', { + return new Response(null, { + status: 302, headers: { + Location: '/account', 'Set-Cookie': await context.session.commit(), }, }); diff --git a/templates/skeleton/app/routes/account_.logout.tsx b/templates/skeleton/app/routes/account_.logout.tsx index 2ca7531e47..4fb4b3e719 100644 --- a/templates/skeleton/app/routes/account_.logout.tsx +++ b/templates/skeleton/app/routes/account_.logout.tsx @@ -1,8 +1,13 @@ -import {redirect, type ActionFunctionArgs} from '@shopify/remix-oxygen'; +import {type ActionFunctionArgs} from '@shopify/remix-oxygen'; // if we dont implement this, /account/logout will get caught by account.$.tsx to do login export async function loader() { - return redirect('/'); + return new Response(null, { + status: 302, + headers: { + Location: '/', + }, + }); } export async function action({context}: ActionFunctionArgs) { diff --git a/templates/skeleton/app/routes/cart.$lines.tsx b/templates/skeleton/app/routes/cart.$lines.tsx index fab617e264..26e15622b7 100644 --- a/templates/skeleton/app/routes/cart.$lines.tsx +++ b/templates/skeleton/app/routes/cart.$lines.tsx @@ -1,4 +1,4 @@ -import {redirect, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; +import {unstable_defineLoader as defineLoader} from '@shopify/remix-oxygen'; /** * Automatically creates a new cart based on the URL and redirects straight to checkout. @@ -18,51 +18,61 @@ import {redirect, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; * * ``` */ -export async function loader({request, context, params}: LoaderFunctionArgs) { - const {cart} = context; - const {lines} = params; - if (!lines) return redirect('/cart'); - const linesMap = lines.split(',').map((line) => { - const lineDetails = line.split(':'); - const variantId = lineDetails[0]; - const quantity = parseInt(lineDetails[1], 10); +export const loader = defineLoader( + async ({request, context, params, response}) => { + const {cart} = context; + const {lines} = params; + if (!lines) + throw new Response(null, { + status: 302, + headers: { + Location: '/cart', + }, + }); + const linesMap = lines.split(',').map((line) => { + const lineDetails = line.split(':'); + const variantId = lineDetails[0]; + const quantity = parseInt(lineDetails[1], 10); - return { - merchandiseId: `gid://shopify/ProductVariant/${variantId}`, - quantity, - }; - }); + return { + merchandiseId: `gid://shopify/ProductVariant/${variantId}`, + quantity, + }; + }); - const url = new URL(request.url); - const searchParams = new URLSearchParams(url.search); + const url = new URL(request.url); + const searchParams = new URLSearchParams(url.search); - const discount = searchParams.get('discount'); - const discountArray = discount ? [discount] : []; + const discount = searchParams.get('discount'); + const discountArray = discount ? [discount] : []; - // create a cart - const result = await cart.create({ - lines: linesMap, - discountCodes: discountArray, - }); + // create a cart + const result = await cart.create({ + lines: linesMap, + discountCodes: discountArray, + }); - const cartResult = result.cart; + const cartResult = result.cart; - if (result.errors?.length || !cartResult) { - throw new Response('Link may be expired. Try checking the URL.', { - status: 410, - }); - } + if (result.errors?.length || !cartResult) { + throw new Response('Link may be expired. Try checking the URL.', { + status: 410, + }); + } - // Update cart id in cookie - const headers = cart.setCartId(cartResult.id); + // Update cart id in cookie + cart.setCartId(cartResult.id, response); - // redirect to checkout - if (cartResult.checkoutUrl) { - return redirect(cartResult.checkoutUrl, {headers}); - } else { - throw new Error('No checkout URL found'); - } -} + // redirect to checkout + if (cartResult.checkoutUrl) { + response.status = 302; + response.headers.set('Location', cartResult.checkoutUrl); + return null; + } else { + throw new Error('No checkout URL found'); + } + }, +); export default function Component() { return null; diff --git a/templates/skeleton/app/routes/cart.tsx b/templates/skeleton/app/routes/cart.tsx index b4381691a3..312298e972 100644 --- a/templates/skeleton/app/routes/cart.tsx +++ b/templates/skeleton/app/routes/cart.tsx @@ -21,7 +21,7 @@ export const action = defineAction(async ({request, context, response}) => { throw new Error('No action provided'); } - let status = 200; + response.status = 200; let result: CartQueryDataReturn; switch (action) { @@ -59,20 +59,16 @@ export const action = defineAction(async ({request, context, response}) => { } const cartId = result.cart.id; - const headers = cart.setCartId(result.cart.id); + + cart.setCartId(cartId, response); const {cart: cartResult, errors} = result; const redirectTo = formData.get('redirectTo') ?? null; if (typeof redirectTo === 'string') { - status = 303; - headers.set('Location', redirectTo); + response.status = 303; + response.headers.set('Location', redirectTo); } - headers.append('Set-Cookie', await context.session.commit()); - - response.status = status; - response?.headers.append('Set-Cookie', await context.session.commit()); - return { cart: cartResult, errors, diff --git a/templates/skeleton/app/routes/discount.$code.tsx b/templates/skeleton/app/routes/discount.$code.tsx index c613c82ea7..ee24578d5a 100644 --- a/templates/skeleton/app/routes/discount.$code.tsx +++ b/templates/skeleton/app/routes/discount.$code.tsx @@ -1,4 +1,4 @@ -import {redirect, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; +import {unstable_defineLoader as defineLoader} from '@shopify/remix-oxygen'; /** * Automatically applies a discount found on the url @@ -11,37 +11,46 @@ import {redirect, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; * * ``` */ -export async function loader({request, context, params}: LoaderFunctionArgs) { - const {cart} = context; - const {code} = params; - - const url = new URL(request.url); - const searchParams = new URLSearchParams(url.search); - let redirectParam = - searchParams.get('redirect') || searchParams.get('return_to') || '/'; - - if (redirectParam.includes('//')) { - // Avoid redirecting to external URLs to prevent phishing attacks - redirectParam = '/'; - } - - searchParams.delete('redirect'); - searchParams.delete('return_to'); - - const redirectUrl = `${redirectParam}?${searchParams}`; - - if (!code) { - return redirect(redirectUrl); - } - - const result = await cart.updateDiscountCodes([code]); - const headers = cart.setCartId(result.cart.id); - - // Using set-cookie on a 303 redirect will not work if the domain origin have port number (:3000) - // If there is no cart id and a new cart id is created in the progress, it will not be set in the cookie - // on localhost:3000 - return redirect(redirectUrl, { - status: 303, - headers, - }); -} +export const loader = defineLoader( + async ({request, context, params, response}) => { + const {cart} = context; + const {code} = params; + + const url = new URL(request.url); + const searchParams = new URLSearchParams(url.search); + let redirectParam = + searchParams.get('redirect') || searchParams.get('return_to') || '/'; + + if (redirectParam.includes('//')) { + // Avoid redirecting to external URLs to prevent phishing attacks + redirectParam = '/'; + } + + searchParams.delete('redirect'); + searchParams.delete('return_to'); + + const redirectUrl = `${redirectParam}?${searchParams}`; + + if (!code) { + throw new Response(null, { + status: 302, + headers: { + Location: redirectUrl, + }, + }); + } + + const result = await cart.updateDiscountCodes([code]); + cart.setCartId(result.cart.id, response); + + // Using set-cookie on a 303 redirect will not work if the domain origin have port number (:3000) + // If there is no cart id and a new cart id is created in the progress, it will not be set in the cookie + // on localhost:3000 + return new Response(null, { + status: 303, + headers: { + Location: redirectUrl, + }, + }); + }, +);