Bladeren bron

游客和登录用户 token管理修改;删除redux中的user

fogwind 2 dagen geleden
bovenliggende
commit
ab1bb2e1a0

+ 0 - 3
src/app/api/gift/add/route.ts

@@ -1,10 +1,8 @@
 import { NextRequest, NextResponse } from "next/server";
 import { restApiFetch } from "@/utils/bagisto";
-import { getAuthToken } from "@/utils/helper";
 
 export async function POST(req: NextRequest) {
     try {
-        const authorizationToken = getAuthToken(req); // 获取headers中的Authorization的值
         const params = await req.json();
         const response = await restApiFetch<{
             data: any; // 这个是返回结果的数据类型,暂时写成any,具体看后端反的数据结构再改成确定的类型1
@@ -16,7 +14,6 @@ export async function POST(req: NextRequest) {
             method:'POST',
             cache:'no-store',
             variables: params,
-            guestToken: authorizationToken,
         });
         // 打印后端原始返回结构
         console.log('接口原始response.body =', JSON.stringify(response.body, null, 2));

+ 2 - 5
src/app/api/gift/lists/route.ts

@@ -1,15 +1,12 @@
-import { NextRequest, NextResponse } from "next/server";
+import { NextResponse } from "next/server";
 import { restApiFetch } from "@/utils/bagisto";
-import { getAuthToken } from "@/utils/helper";
 import type { GiftListBody,FetchWrap  } from '@/types/api/gift/lists';
-export async function GET(req: NextRequest) {
+export async function GET() {
     try {
-        const guestToken = getAuthToken(req);
         const response = await restApiFetch<FetchWrap<GiftListBody>>({
             api: `/gift/lists`,
             method:'GET',
             cache:'no-store',
-            guestToken,
         });
         return NextResponse.json(response.body,{
             status: response.status,

+ 0 - 3
src/app/api/gift/my-gift-cards/route.ts

@@ -1,10 +1,8 @@
 import { NextRequest, NextResponse } from "next/server";
 import { restApiFetch } from "@/utils/bagisto";
-import { getAuthToken } from "@/utils/helper";
 import type { GiftCardRespBody } from '@/types/api/gift/my-gift-cards';
 export async function GET(req: NextRequest) {
     try {
-        const guestToken = getAuthToken(req);
 
         const searchParams = req.nextUrl.searchParams;
         const page = searchParams.get('page') || '1';
@@ -21,7 +19,6 @@ export async function GET(req: NextRequest) {
             api: `/gift/my-gift-cards`,
             method:'GET',
             cache:'no-store',
-            guestToken,
         });
         return NextResponse.json(response.body,{
             status: response.status,

+ 6 - 9
src/app/api/graphql/route.ts

@@ -1,6 +1,5 @@
 import { NextRequest, NextResponse } from "next/server";
 import { bagistoFetch } from "@/utils/bagisto";
-import { getAuthToken } from "@/utils/helper";
 import {
     CREATE_ADD_PRODUCT_IN_CART,
     REMOVE_CART_ITEM,
@@ -56,9 +55,8 @@ interface FetchOption  {
 }
 
 // 需要authorization的operation
-function authorizationOperations(body: Record<string, any>,req:NextRequest): FetchOption {
+function authorizationOperations(body: Record<string, any>): FetchOption {
     const { operationName, variables } = body;
-    const guestToken = getAuthToken(req);
     const query = ALLOWED_OPERATIONS[operationName];
     const finalVariables = variables;
  
@@ -66,21 +64,20 @@ function authorizationOperations(body: Record<string, any>,req:NextRequest): Fet
         query,
         variables: finalVariables,
         cache: "no-store",
-        guestToken,
-        operationName
+        // operationName
     }
 }
 
 
 // 不需要authorization的operation
 function notAuthorizationOperations(body: Record<string, any>): FetchOption {
-    const { operationName, query: bodyGraphqlQuery, variables } = body;
-    const query = bodyGraphqlQuery;
+    const { query, variables } = body;
+    // const query = bodyGraphqlQuery;
     return {
         query,
         variables,
         cache: "no-store",
-        operationName
+        // operationName
     }
 }
 
@@ -97,7 +94,7 @@ export async function POST(req: NextRequest) {
         }
         let fetchOption: FetchOption = notAuthorizationOperations(body);
         if(ALLOWED_OPERATIONS[operationName]) {
-            fetchOption = authorizationOperations(body,req);
+            fetchOption = authorizationOperations(body);
         }
 
         

+ 4 - 4
src/components/common/button/ReviewButton.tsx

@@ -1,12 +1,12 @@
-import { IS_GUEST } from "@utils/constants";
-import { getCookie } from "@utils/cookie-tools";
+
+import { getIsGuest } from "@utils/cookie-tools";
 import { useRouter } from "next/navigation";
 
 export const ReviewButton = ({ setShowForm, className }: { setShowForm: (show: boolean) => void, className?: string }) => {
-    const IsGuest = getCookie(IS_GUEST);
+    const IsGuest = getIsGuest();
     const router = useRouter();
     const handleAddReview = () => {
-        if (IsGuest === "true" || IsGuest === null) {
+        if (IsGuest) {
             router.push("/customer/login");
         } else {
             setShowForm(true);

+ 14 - 13
src/components/customer/LoginForm.tsx

@@ -12,12 +12,16 @@ import InputText from "@components/common/form/Input";
 import { useCustomToast } from "@/utils/hooks/useToast";
 import { useMergeCart } from "@utils/hooks/useMergeCart";
 
-import { getCookie, setCookie } from "@/utils/cookie-tools";
-
-import { useAppDispatch, useAppSelector } from "@/store/hooks";
-import { setUser } from "@/store/slices/user-slice";
+import { 
+  setCookie, 
+  getGuestCartToken,
+  getGuestCartId,
+  deleteGuestCookie
+} from "@/utils/cookie-tools";
+
+import { useAppSelector } from "@/store/hooks";
 import { useCartDetail } from "@utils/hooks/useCartDetail";
-import { GUEST_CART_ID, GUEST_CART_TOKEN, IS_GUEST } from "@/utils/constants";
+import { IS_GUEST } from "@/utils/constants";
 
 type LoginFormInputs = {
   username: string;
@@ -26,7 +30,6 @@ type LoginFormInputs = {
 
 export default function LoginForm() {
   const router = useRouter();
-  const dispatch = useAppDispatch();
   const { showToast } = useCustomToast();
   const { getCartDetail } = useCartDetail()
   const { mergeCart } = useMergeCart();
@@ -43,8 +46,8 @@ export default function LoginForm() {
   const onSubmit: SubmitHandler<LoginFormInputs> = async (data) => {
     try {
       // First, handle cart merging before sign in
-      const guestCartId = getCookie(GUEST_CART_ID);
-      const guestCartToken = getCookie(GUEST_CART_TOKEN);
+      const guestCartId = getGuestCartId();
+      const guestCartToken = getGuestCartToken();
 
       /**
        * @todo 使用 signInAuth 重写
@@ -72,9 +75,6 @@ export default function LoginForm() {
         console.error('userToken is required');
       }
 
-      if (session?.user) {
-        dispatch(setUser(session.user as any));
-      }
 
       // Only merge cart if user had a guest cart before login
       if (userToken && guestCartId && guestCartToken) {
@@ -86,13 +86,14 @@ export default function LoginForm() {
         } catch (err) {
           console.error("mergeCart failed:", err);
         }
-        setCookie(GUEST_CART_TOKEN, userToken);
+        // 登录成功后不应该保存用户的token
         setCookie(IS_GUEST, "false");
+        deleteGuestCookie();
         await getCartDetail();
       } else if (userToken) {
         // User logged in without a guest cart, just set the token
-        setCookie(GUEST_CART_TOKEN, userToken);
         setCookie(IS_GUEST, "false");
+        deleteGuestCookie();
       }
       setTimeout(() => {
         router.push("/");

+ 9 - 10
src/components/customer/credentials/CredentialModal.tsx

@@ -3,7 +3,7 @@
 import { useDisclosure } from "@heroui/use-disclosure";
 import { AnimatePresence, motion } from "framer-motion";
 import clsx from "clsx";
-import { signOut } from "next-auth/react";
+import { useSession, signOut } from "next-auth/react";
 import Link from "next/link";
 import { Avatar } from "@heroui/avatar";
 import { useForm } from "react-hook-form";
@@ -14,11 +14,13 @@ import OpenAuth from "../OpenAuth";
 import { isObject } from '@/utils/type-guards';
 import LoadingDots from "@components/common/icons/LoadingDots";
 import { logoutAction } from "@utils/actions";
-import { useAppDispatch, useAppSelector } from "@/store/hooks";
-import { clearUser } from "@/store/slices/user-slice";
+import { useAppDispatch } from "@/store/hooks";
 import { clearCart } from "@/store/slices/cart-slice";
-import { setCookie, deleteCookie } from "@utils/cookie-tools";
-import { IS_GUEST,GUEST_CART_TOKEN,GUEST_CART_ID } from "@/utils/constants";
+import { 
+  setCookie, 
+  deleteGuestCookie
+} from "@utils/cookie-tools";
+import { IS_GUEST } from "@/utils/constants";
 
 export default function CredentialModal({
   children,
@@ -59,8 +61,7 @@ export default function CredentialModal({
     formState: { isSubmitting },
   } = useForm();
 
-  const { user } = useAppSelector((state) => state.user);
-  const session = { user };
+  const {data: session} = useSession();
 
   const onSubmit = async () => {
     try {
@@ -75,10 +76,8 @@ export default function CredentialModal({
         redirect: false,
       });
 
-      deleteCookie(GUEST_CART_TOKEN);
-      deleteCookie(GUEST_CART_ID);
+      deleteGuestCookie(); // 这里本质是要删除登录用户的token
       setCookie(IS_GUEST, 'true');
-      dispatch(clearUser());
       dispatch(clearCart());
       showToast("You are logged out successfully!", "success");
       setTimeout(() => {

+ 1 - 32
src/lib/ApolloClientBrowser.ts

@@ -1,4 +1,3 @@
-import { cache as reactCache } from "react";
 import { HttpLink,ApolloLink } from "@apollo/client";
 import { SetContextLink } from "@apollo/client/link/context";
 import {
@@ -8,9 +7,6 @@ import {
 import { ErrorLink } from "@apollo/client/link/error";
 import {CombinedProtocolErrors} from "@apollo/client/errors";
 import {handleErrorForErrorLink} from "@/lib/ApolloErrorHandler";
-import { getSession } from "next-auth/react";
-import { getCartToken } from "@/utils/getCartToken";
-import { BagistoSession } from "@/types/types";
 
 // 这里注册的是客户端使用的apollo client
 
@@ -19,29 +15,6 @@ CombinedProtocolErrors.formatMessage = (errors,{defaultFormatMessage})=>{
 };
 
 
-
-let sessionCache: { session: BagistoSession | null; timestamp: number } | null = null;
-const SESSION_CACHE_TTL = 5000;
-
-const getSessionForRequest = reactCache(async () => {
-  return (await getSession()) as BagistoSession | null;
-});
-
-async function getCachedSession(): Promise<BagistoSession | null> {
-  if (typeof window === "undefined") {
-    return getSessionForRequest();
-  }
-
-  const now = Date.now();
-
-  if (sessionCache && now - sessionCache.timestamp < SESSION_CACHE_TTL) {
-    return sessionCache.session;
-  }
-  const session = (await getSession()) as BagistoSession | null;
-  sessionCache = { session, timestamp: now };
-  return session;
-}
-
 export default function makeClient() {
     const httpLink = new HttpLink({
         uri: "/api/graphql",
@@ -60,15 +33,11 @@ export default function makeClient() {
 
   const authLink = new SetContextLink(async (prevContext) => {
       
-      const session = await getCachedSession();
-      const userToken = session?.user?.accessToken;
-      const guestToken = !userToken ? getCartToken() : null;
-      const token = userToken || guestToken;
   
       return {
         headers: {
           ...prevContext.headers,
-          ...(token && { Authorization: `Bearer ${token}` }),
+          // ...(token && { Authorization: `Bearer ${token}` }),
           "Content-Type": "application/json",
         },
       };

+ 1 - 26
src/lib/restApiClient.ts

@@ -1,36 +1,11 @@
 'use client';
 /**前端客户端组件调rest api 接口 */
-import { getSession } from "next-auth/react";
-import { getCartToken } from "@/utils/getCartToken";
-import { BagistoSession } from "@/types/types";
-
-
-let sessionCache: { session: BagistoSession | null; timestamp: number } | null = null;
-const SESSION_CACHE_TTL = 5000;
-
-async function getCachedSession(): Promise<BagistoSession | null> {
-
-  const now = Date.now();
-
-  if (sessionCache && now - sessionCache.timestamp < SESSION_CACHE_TTL) {
-    return sessionCache.session;
-  }
-  //When called, getSession() will send a request to /api/auth/session and returns a promise with a session object, or null if no session exists.
-  const session = (await getSession()) as BagistoSession | null;
-  sessionCache = { session, timestamp: now };
-  return session;
-}
 
 export async function clientFetch<T = any>(apiUrl: string, options: RequestInit = {}): Promise<T> {
     // 请求的是nextjs的代理接口
 
-    const session = await getCachedSession();
-    const userToken = session?.user?.accessToken;
-    const guestToken = !userToken ? getCartToken() : null;
-    const token = userToken || guestToken;
-
     const headers = {
-        ...(token && { Authorization: `Bearer ${token}` }),
+        //...(token && { Authorization: `Bearer ${token}` }),
         "Content-Type": "application/json",
     };
     if(options.headers) {

+ 0 - 2
src/providers/SessionManager.tsx

@@ -1,13 +1,11 @@
 "use client";
 
 import { SessionProvider } from "next-auth/react";
-import { SessionSync } from "./SessionSync";
 import { ReactNode } from "react";
 
 export function SessionManager({ children }: { children: ReactNode }) {
   return (
     <SessionProvider>
-      <SessionSync />
       {children}
     </SessionProvider>
   );

+ 0 - 21
src/providers/SessionSync.tsx

@@ -1,21 +0,0 @@
-"use client";
-
-import { useSession } from "next-auth/react";
-import { useEffect } from "react";
-import { useAppDispatch } from "@/store/hooks";
-import { setUser, clearUser } from "@/store/slices/user-slice";
-
-export const SessionSync = () => {
-    const { data: session, status } = useSession();
-    const dispatch = useAppDispatch();
-
-    useEffect(() => {
-        if (status === "authenticated" && session?.user) {
-            dispatch(setUser(session.user as any));
-        } else if (status === "unauthenticated") {
-            dispatch(clearUser());
-        }
-    }, [session, status, dispatch]);
-
-    return null;
-}

+ 0 - 1
src/providers/index.ts

@@ -7,6 +7,5 @@ export { SessionManager } from "./SessionManager";
 export { ThemeProvider } from "./ThemeProvider";
 export { ToastProvider, useToast } from "./ToastProvider";
 export { SessionProvider } from "./SessionProvider";
-export { SessionSync } from "./SessionSync";
 export { PayPalWrapper } from "./PaypalWrapper";
 export { ConfigProvider } from "./ConfigProvider";

+ 0 - 30
src/store/slices/user-slice.ts

@@ -1,30 +0,0 @@
-import { PayloadAction, createSlice } from "@reduxjs/toolkit";
-import { BagistoUser } from "@/types/types";
-
-interface UserState {
-    user: BagistoUser | null;
-    isAuthenticated: boolean;
-}
-
-const initialState: UserState = {
-    user: null,
-    isAuthenticated: false,
-};
-
-const userSlice = createSlice({
-    name: "user",
-    initialState,
-    reducers: {
-        setUser: (state, action: PayloadAction<BagistoUser>) => {
-            state.user = action.payload;
-            state.isAuthenticated = true;
-        },
-        clearUser: (state) => {
-            state.user = null;
-            state.isAuthenticated = false;
-        },
-    },
-});
-
-export const { setUser, clearUser } = userSlice.actions;
-export default userSlice.reducer;

+ 0 - 2
src/store/store.ts

@@ -1,7 +1,6 @@
 import { configureStore } from "@reduxjs/toolkit";
 
 import cartSlice, {CartState} from "./slices/cart-slice";
-import userSlice from "./slices/user-slice";
 import addToCartDialogReducer from "./slices/addToCartDialogSlice";
 
 // export const store = configureStore({
@@ -22,7 +21,6 @@ export const makeStore = (preloadedState: PreloadedStateType) => {
     return configureStore({
       reducer: {
         cartDetail: cartSlice,
-        user: userSlice,
         addToCartDialog: addToCartDialogReducer,
       },
       preloadedState

+ 56 - 49
src/utils/bagisto/index.ts

@@ -24,7 +24,6 @@ import {
 } from "@/utils/constants/server";
 import { 
   GUEST_CART_TOKEN,
-  IS_GUEST,
   CURRENT_CURRENCY,
   CURRENT_LOCAL,
   CURRENT_CHANNEL,
@@ -44,14 +43,14 @@ import {
   ThemeCustomizationResponse,
   PageData,
 } from "@/types/theme/theme-customization";
-import { decodeJWT } from "@/utils/jwt-cookie";
 import {FetchGraphqlResult} from "@/types/graphqlFetch/type";
 
 
 type ExtractVariables<T> = T extends { variables: object }
   ? T["variables"]
   : any;
-type ExtractData<T> = T extends { data: infer D } ? D : any;
+type ExtractRestFulData<T> = T extends { data: infer D } ? D : any;
+type ExtractGraphqlData<T> = T extends { data: infer D } ? { data: D } : any;
 
 interface PageByUrlKeyResponse {
   pageByUrlKeypages?: PageData[];
@@ -88,7 +87,29 @@ async function getBaseHeader() {
     }
     return baseHeaders;
 }
+export async function getAuthorizationToken(): Promise<{token: string | null; isGuest: boolean;}> {
+  
+  // 登录用户从nextAuth里获取
+  const authSession = (await getServerSession(
+        authOptions,
+      )) as BagistoSession | null;
+  const accessToken = authSession?.user?.accessToken;
+  if (accessToken) {
+    return {
+      token: accessToken,
+      isGuest: false,
+    };
+  }
 
+  // 游客从cookie里获取
+  const cookieStore = await cookies();
+  const guestToken = cookieStore.get(GUEST_CART_TOKEN)?.value;
+  
+  return {
+    token: guestToken || null,
+    isGuest: true
+  };
+}
 
 // rest api fetch
 export async function restApiFetch<T>({
@@ -98,8 +119,6 @@ export async function restApiFetch<T>({
   headers,
   tags,
   variables,
-  isCookies = true,
-  guestToken,
   revalidate = 60,
 }: {
   api: string;
@@ -111,11 +130,16 @@ export async function restApiFetch<T>({
   isCookies?: boolean;
   guestToken?: string;
   revalidate?: number;
-}): Promise<{ status: number; body: ExtractData<T> } | never> {
+}): Promise<{ status: number; body: ExtractRestFulData<T> } | never> {
   try {
     const apiUrl = api.startsWith("http") ? api : `${REST_API_URL}${api}`;
     const url = new URL(apiUrl);
 
+    const tokenRes = await getAuthorizationToken();
+
+    const headerRes = await getBaseHeader();
+    const baseHeaders: Record<string, string> = {...headerRes};
+    /*
     let accessToken: string | undefined = undefined;
 
     if (isCookies) {
@@ -125,17 +149,19 @@ export async function restApiFetch<T>({
       accessToken = sessions?.user?.accessToken;
     }
 
-    const headerRes = await getBaseHeader();
-    const baseHeaders: Record<string, string> = {...headerRes};
 
     if (accessToken) {
       baseHeaders.Authorization = `Bearer ${accessToken}`;
     } else if (guestToken) {
       baseHeaders.Authorization = `Bearer ${guestToken}`;
     }
+    */
+    if (tokenRes.token) {
+      baseHeaders.Authorization = `Bearer ${tokenRes.token}`;
+    }
 
 
-    if (isCookies && headers) {
+    if (headers) {
       if (headers instanceof Headers) {
         headers.forEach((value, key) => (baseHeaders[key] = value));
       } else {
@@ -196,36 +222,13 @@ export async function serverGraphqlFetch<
   try {
     const queryString = typeof query === "string" ? query : print(query);
 
-    let accessToken: string | undefined = undefined;
-    let guestToken: string | undefined = undefined;
-
-    const cookieStore = await cookies();
-    
-    const isGuest = cookieStore.get(IS_GUEST)?.value;
-    const gt =  cookieStore.get(GUEST_CART_TOKEN)?.value;
-    if(isGuest === 'false') {
-      // 登录用户
-      const sessions = (await getServerSession(
-        authOptions,
-      )) as BagistoSession | null;
-      accessToken = sessions?.user?.accessToken;
-    } else if(gt) {
-        // 游客
-        const jwtRes = decodeJWT<{
-            sessionToken: string;
-            cartId: number;
-            isGuest: boolean;
-        }>(gt, true);
-        guestToken = jwtRes?.sessionToken || '';
-    }
-
+    const tokenRes = await getAuthorizationToken();
     const headerRes = await getBaseHeader();
     const baseHeaders: Record<string, string> = {...headerRes};
 
-    if (accessToken) {
-      baseHeaders['Authorization'] = `Bearer ${accessToken}`;
-    } else if (guestToken) {
-      baseHeaders['Authorization'] = `Bearer ${guestToken}`;
+
+    if (tokenRes.token) {
+      baseHeaders['Authorization'] = `Bearer ${tokenRes.token}`;
     }
 
     if (headers) {
@@ -271,8 +274,8 @@ export async function bagistoFetch<T>({
   query,
   tags,
   variables,
-  isCookies = true,
-  guestToken,
+  // isCookies = true,
+  // guestToken,
   revalidate = 60,
 }: {
   cache?: RequestCache;
@@ -280,15 +283,18 @@ export async function bagistoFetch<T>({
   query: string | DocumentNode;
   tags?: string[];
   variables?: ExtractVariables<T>;
-  isCookies?: boolean;
-  guestToken?: string;
+  // isCookies?: boolean;
+  // guestToken?: string;
   revalidate?: number;
-}): Promise<{ status: number; body: T } | never> {
+}): Promise<{ status: number; body: ExtractGraphqlData<T> } | never> {
   try {
     const queryString =
       typeof query === "string" ? query : print(query);
 
-
+    const tokenRes = await getAuthorizationToken();
+    const headerRes = await getBaseHeader();
+    const baseHeaders: Record<string, string> = {...headerRes};
+    /*
     let accessToken: string | undefined = undefined;
 
     if (isCookies) {
@@ -298,19 +304,20 @@ export async function bagistoFetch<T>({
       )) as BagistoSession | null;
       accessToken = sessions?.user?.accessToken;
     }
-
-    const headerRes = await getBaseHeader();
-    const baseHeaders: Record<string, string> = {...headerRes};
-
+    
     if (accessToken) {
       baseHeaders.Authorization = `Bearer ${accessToken}`;
     } else if (guestToken) {
       baseHeaders.Authorization = `Bearer ${guestToken}`;
     }
+    */
+    if (tokenRes.token) {
+      baseHeaders.Authorization = `Bearer ${tokenRes.token}`;
+    }
 
     
 
-    if (isCookies && headers) {
+    if (headers) {
       if (headers instanceof Headers) {
         headers.forEach((value, key) => (baseHeaders[key] = value));
       } else {
@@ -444,7 +451,7 @@ export async function logoutUser() {
       variables: { input: { token: string } };
     }>({
       query: CUSTOMER_LOGOUT,
-      isCookies: true,
+      // isCookies: true,
       revalidate: 3600,
     });
 
@@ -591,7 +598,7 @@ export async function getPage(input: { urlKey: string }): Promise<PageData[]> {
   }>({
     query: PAGE_BY_URL_KEY,
     cache: "no-store",
-    isCookies: false,
+    // isCookies: false,
     variables: { pageByUrlKey: input.urlKey },
   });
 

+ 10 - 0
src/utils/constants.ts

@@ -106,6 +106,16 @@ export const SortByFields: SortOrderTypes[] = [
   },
 ];
 
+export const DEFAULT_EXPIRES_DAYS = 7; // cookie里的 GUEST_CART_TOKEN GUEST_CART_ID IS_GUEST 的过期时间
+export const GUEST_COOKIE_OPTION = {
+    days: DEFAULT_EXPIRES_DAYS,
+    encode: true,          // 默认编码,更加安全
+    path: '/',
+    // domain:,
+    secure: process.env.NEXT_PUBLIC_APP_ENV !== 'development',
+    sameSite: 'lax',
+};
+
 export const GUEST_CART_TOKEN = "guest_cart_token";
 export const GUEST_CART_ID = "guest_cart_id";
 export const IS_GUEST = "is_guest";

+ 33 - 8
src/utils/cookie-tools.ts

@@ -34,10 +34,9 @@ export const deleteCookie = (name: string) => {
   document.cookie = `${name}=; Max-Age=0; path=/`;
 };
 */
+import {GUEST_COOKIE_OPTION,GUEST_CART_TOKEN,IS_GUEST,GUEST_CART_ID} from "@/utils/constants";
 // cookie.ts
 
-const DEFAULT_EXPIRES_DAYS = 7;
-
 export interface CookieOptions {
   /** 过期天数(从当前时间起算),默认 7 天 */
   days?: number;
@@ -83,15 +82,15 @@ export function setCookie(
   options: CookieOptions = {}
 ): void {
   if (typeof document === 'undefined') return;
-
+  const setOption = Object.assign({},GUEST_COOKIE_OPTION,options);
   const {
-    days = DEFAULT_EXPIRES_DAYS,
-    encode = true,          // 默认编码,更加安全
-    path = '/',
+    days,
+    encode,          // 默认编码,更加安全
+    path,
     domain,
     secure,
     sameSite,
-  } = options;
+  } = setOption;
 
   // 处理过期时间
   const expires = new Date(Date.now() + days * 864e5).toUTCString();
@@ -119,8 +118,14 @@ export function deleteCookie(
   options: Pick<CookieOptions, 'path' | 'domain' | 'secure'> = {}
 ): void {
   if (typeof document === 'undefined') return;
+  const {
+    path: defaultPath,
+    secure: defaultSecure
+  } = GUEST_COOKIE_OPTION;
+  const { domain } = options;
 
-  const { path = '/', domain, secure } = options;
+  const path = options.path || defaultPath;
+  const secure = options.secure || defaultSecure;
 
   // 若删除时指定了 domain 或 secure,需要与设置时一致才能删除成功
   let cookie = `${name}=; Max-Age=0; path=${path}`;
@@ -128,4 +133,24 @@ export function deleteCookie(
   if (secure) cookie += '; secure';
 
   document.cookie = cookie;
+}
+
+export function getGuestCartId(): string | null {
+  const res = getCookie(GUEST_CART_ID);
+  return res;
+}
+export function getIsGuest(): boolean {
+  const res = getCookie(IS_GUEST);
+  return res === 'true' || res === null;
+}
+export const getGuestCartToken = (): string | null => {
+  const raw = getCookie(GUEST_CART_TOKEN);
+  if (!raw) return null;
+
+  return raw;
+};
+
+export function deleteGuestCookie() {
+    deleteCookie(GUEST_CART_ID);
+    deleteCookie(GUEST_CART_TOKEN);
 }

+ 0 - 13
src/utils/getCartToken.ts

@@ -1,13 +0,0 @@
-import { GUEST_CART_TOKEN, IS_GUEST } from "@/utils/constants";
-import { decodeJWT } from "@/utils/jwt-cookie";
-import { getCookie } from "@/utils/cookie-tools";
-// 管理游客的token
-export const getCartToken = (): string | null => {
-  const raw = getCookie(GUEST_CART_TOKEN);
-  if (!raw) return null;
-
-  const isGuest = getCookie(IS_GUEST) !== "false";
-
-  const decoded = decodeJWT<{ sessionToken: string }>(raw, isGuest);
-  return decoded?.sessionToken ?? null;
-};

+ 14 - 17
src/utils/hooks/useAddToCart.ts

@@ -4,9 +4,12 @@ import { useCustomToast } from "./useToast";
 import { useAppDispatch } from "@/store/hooks";
 import { addItem, clearCart } from "@/store/slices/cart-slice";
 import { isObject } from "@utils/type-guards";
-import { getCartToken } from "@utils/getCartToken";
-import { getCookie, setCookie, deleteCookie } from "@utils/cookie-tools";
-import { encodeJWT } from "@/utils/jwt-cookie";
+import { 
+  setCookie, 
+  getIsGuest,
+  getGuestCartToken,
+  deleteGuestCookie
+} from "@utils/cookie-tools";
 import { useGuestCartToken } from "./useGuestCartToken";
 import { IS_GUEST,GUEST_CART_TOKEN,GUEST_CART_ID } from "@/utils/constants";
 import { useMutation } from "@apollo/client/react";
@@ -39,18 +42,12 @@ export const useAddProduct = () => {
           if (responseData.success) {
             /** 兜底代码 start*/
             // 游客加购,然后清空购物车,然后刷新页面,然后再加购,然后再刷新页面,购物车会失效,所以添加兜底代码
-            const isGuest = getCookie(IS_GUEST) !== "false";// 'true' 是游客
+            const isGuest = getIsGuest();// true 是游客
             // 如果之前的cartToken有效,responseCartId和responseCartToken的值是一样的都是cart id; 否则两者不相等
             const responseCartId = responseData.id;
             const responseCartToken = responseData.cartToken;
             if(isGuest && responseCartId !== responseCartToken) {
-                const newToken = encodeJWT({
-                  sessionToken: responseCartToken,
-                  cartId: responseCartId,
-                  isGuest: isGuest,
-                });
-        
-                setCookie(GUEST_CART_TOKEN, newToken,{encode: false});
+                setCookie(GUEST_CART_TOKEN, responseCartToken,{encode: false});
                 setCookie(GUEST_CART_ID, responseCartId);
                 setCookie(IS_GUEST, String(isGuest));
             }
@@ -84,9 +81,10 @@ export const useAddProduct = () => {
   }) => {
     
     // Ensure token exists - create if needed
-    let token = getCartToken(); // 从cookie获取token
+    let token = getGuestCartToken(); // 从cookie获取token
+    const isGuest = getIsGuest();
 
-    if (!token) {
+    if (!token && isGuest) {
       // 没有token,则创建一个并写入cookie
       token = await createGuestToken();
 
@@ -129,10 +127,9 @@ export const useAddProduct = () => {
           if (!responseData?.itemsQty) {
             dispatch(clearCart());
 
-            const isGuest = getCookie(IS_GUEST);
-            if (isGuest === "true") {
-              deleteCookie(GUEST_CART_TOKEN);
-              deleteCookie(GUEST_CART_ID);
+            const isGuest = getIsGuest();
+            if (isGuest) {
+              deleteGuestCookie();
             }
           }
         } else {

+ 14 - 47
src/utils/hooks/useGuestCartToken.ts

@@ -3,8 +3,12 @@
 import { useState, useRef } from "react";
 import { fetchHandler } from "../fetch-handler";
 import { GUEST_CART_ID, GUEST_CART_TOKEN, IS_GUEST } from "@/utils/constants";
-import { encodeJWT, decodeJWT } from "@/utils/jwt-cookie";
-import { setCookie, deleteCookie, getCookie } from "@/utils/cookie-tools";
+import { 
+  setCookie, 
+  getGuestCartToken,
+  getGuestCartId,
+  deleteGuestCookie
+} from "@/utils/cookie-tools";
 import { CREATE_CART_TOKEN } from "@/graphql";
 
 // ---------------------------
@@ -13,41 +17,13 @@ import { CREATE_CART_TOKEN } from "@/graphql";
 export const useGuestCartToken = () => {
 
   const [token, setToken] = useState(() => {
-      const cookieToken = getCookie(GUEST_CART_TOKEN);
-      let guestCartToken: string | null = null;
-
-      if (cookieToken) {
-
-        const isGuest = getCookie(IS_GUEST) !== "false";
-        const decoded = decodeJWT<{
-          sessionToken: string;
-          cartId: number;
-          isGuest: boolean;
-        }>(cookieToken, isGuest);
-
-        if (decoded) {
-          guestCartToken = decoded.sessionToken;
-        }
-      }
+      const guestCartToken = getGuestCartToken();
       return guestCartToken;
   });
   const [cartId, setCartId] = useState(() => {
-    const cookieToken = getCookie(GUEST_CART_TOKEN);
-    let guestCartId: number | null = null;
-    if (cookieToken) {
-
-      const isGuest = getCookie(IS_GUEST) !== "false";
-      const decoded = decodeJWT<{
-        sessionToken: string;
-        cartId: number;
-        isGuest: boolean;
-      }>(cookieToken, isGuest);
-
-      if (decoded) {
-        guestCartId = decoded.cartId;
-      }
-    }
-    return guestCartId;
+    const guestCartId = getGuestCartId();
+    
+    return guestCartId ? Number(guestCartId) : null;
   });
   // const [isReady, setIsReady] = useState(true);
 
@@ -61,11 +37,9 @@ export const useGuestCartToken = () => {
     tokenPromiseRef.current = (async () => {
       if (tokenCreatedRef.current) {
         // Return existing raw token from cookie
-        const cookieVal = getCookie(GUEST_CART_TOKEN);
+        const cookieVal = getGuestCartToken();
         if (cookieVal) {
-          const isGuest = getCookie(IS_GUEST) !== "false";
-          const decoded = decodeJWT<{ sessionToken: string }>(cookieVal, isGuest);
-          return decoded?.sessionToken ?? null;
+          return cookieVal;
         }
         return null;
       }
@@ -87,14 +61,9 @@ export const useGuestCartToken = () => {
           return null;
         }
 
-        const newToken = encodeJWT({
-          sessionToken: cart.sessionToken,
-          cartId: cart.id,
-          isGuest: cart.isGuest,
-        });
         const newCartId = Number(cart.id);
 
-        setCookie(GUEST_CART_TOKEN, newToken,{encode: false});
+        setCookie(GUEST_CART_TOKEN, cart.sessionToken,{encode: false});
         setCookie(GUEST_CART_ID, String(newCartId));
         setCookie(IS_GUEST, String(cart?.isGuest));
 
@@ -123,8 +92,7 @@ export const useGuestCartToken = () => {
     tokenCreatedRef.current = false;
 
     // delete old
-    deleteCookie(GUEST_CART_TOKEN);
-    deleteCookie(GUEST_CART_ID);
+    deleteGuestCookie();
 
     await createGuestToken();
 
@@ -138,6 +106,5 @@ export const useGuestCartToken = () => {
     // isReady,
     createGuestToken,
     resetGuestToken,
-    deleteCookie,
   };
 };

+ 0 - 42
src/utils/jwt-cookie.ts

@@ -1,42 +0,0 @@
-// 把游客token编码
-export const encodeJWT = (payload: object): string => {
-  try {
-    const jsonStr = JSON.stringify(payload);
-    const encodedPayload = btoa(encodeURIComponent(jsonStr));
-
-    const header = btoa(JSON.stringify({ alg: "none", typ: "JWT" }));
-
-    const token = `${header}.${encodedPayload}`;
-
-    return encodeURIComponent(token);
-  } catch (e) {
-    console.error("Error encoding JWT:", e);
-    return "";
-  }
-};
-
-
-export const decodeJWT = <T = any>(token: string, isGuest: boolean = true): T | null => {
-  try {
-    if (!isGuest) {
-      return { sessionToken: token } as unknown as T;
-    }
-
-    const decodedToken = decodeURIComponent(token);
-    const parts = decodedToken.split(".");
-
-    // if (parts.length !== 2) {
-    //   return null;
-    // }
-
-    const payloadPart = parts[1];
-    if (!payloadPart) return null;
-
-    const jsonStr = decodeURIComponent(atob(payloadPart));
-    console.log("jsonStr", jsonStr);
-    return JSON.parse(jsonStr) as T;
-  } catch (e) {
-    console.warn("Error decoding JWT:", e);
-    return null;
-  }
-};