Skip to content

Commit

Permalink
remove all of redirect
Browse files Browse the repository at this point in the history
  • Loading branch information
michenly committed May 13, 2024
1 parent 825a967 commit e85bc0b
Show file tree
Hide file tree
Showing 7 changed files with 119 additions and 92 deletions.
7 changes: 5 additions & 2 deletions packages/hydrogen/src/cart/cartSetIdDefault.ts
Original file line number Diff line number Diff line change
@@ -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 = {
Expand All @@ -11,15 +13,16 @@ 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() || '', {
path: '/',
...cookieOptions,
}),
);

return headers;
};
};
10 changes: 6 additions & 4 deletions packages/hydrogen/src/cart/createCartHandler.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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;
};
Expand All @@ -65,7 +67,7 @@ export type CartHandlerOptionsWithCustom<
export type HydrogenCart = {
get: ReturnType<typeof cartGetDefault>;
getCartId: () => string | undefined;
setCartId: (cartId: string) => Headers;
setCartId: (cartId: string, response?: ResponseStub) => Headers;
create: ReturnType<typeof cartCreateDefault>;
addLines: ReturnType<typeof cartLinesAddDefault>;
updateLines: ReturnType<typeof cartLinesUpdateDefault>;
Expand Down Expand Up @@ -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).
*/
Expand Down Expand Up @@ -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.
Expand Down
6 changes: 4 additions & 2 deletions templates/skeleton/app/routes/account.$.tsx
Original file line number Diff line number Diff line change
@@ -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(),
},
});
Expand Down
9 changes: 7 additions & 2 deletions templates/skeleton/app/routes/account_.logout.tsx
Original file line number Diff line number Diff line change
@@ -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) {
Expand Down
86 changes: 48 additions & 38 deletions templates/skeleton/app/routes/cart.$lines.tsx
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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;
Expand Down
14 changes: 5 additions & 9 deletions templates/skeleton/app/routes/cart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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,
Expand Down
79 changes: 44 additions & 35 deletions templates/skeleton/app/routes/discount.$code.tsx
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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,
},
});
},
);

0 comments on commit e85bc0b

Please sign in to comment.