diff --git a/projects/app/src/web/core/chat/context/useChatStore.ts b/projects/app/src/web/core/chat/context/useChatStore.ts index 5cd0f49934ab..7d808feace40 100644 --- a/projects/app/src/web/core/chat/context/useChatStore.ts +++ b/projects/app/src/web/core/chat/context/useChatStore.ts @@ -23,13 +23,25 @@ type State = { const createCustomStorage = () => { const sessionKeys = ['source', 'chatId', 'appId']; + // 从 URL 中获取 appId 作为存储键的一部分 + const getStorageKey = (name: string) => { + let appId = ''; + if (typeof window !== 'undefined') { + const urlParams = new URLSearchParams(window.location.search); + appId = urlParams.get('appId') || ''; + } + return appId ? `${name}_${appId}` : name; + }; + return { getItem: (name: string) => { - const sessionData = JSON.parse(sessionStorage.getItem(name) || '{}'); - const localData = JSON.parse(localStorage.getItem(name) || '{}'); + const storageKey = getStorageKey(name); + const sessionData = JSON.parse(sessionStorage.getItem(storageKey) || '{}'); + const localData = JSON.parse(localStorage.getItem(storageKey) || '{}'); return JSON.stringify({ ...localData, ...sessionData }); }, setItem: (name: string, value: string) => { + const storageKey = getStorageKey(name); const data = JSON.parse(value); // 分离 session 和 local 数据 @@ -42,15 +54,16 @@ const createCustomStorage = () => { // 分别存储 if (Object.keys(sessionData).length > 0) { - sessionStorage.setItem(name, JSON.stringify({ state: sessionData, version: 0 })); + sessionStorage.setItem(storageKey, JSON.stringify({ state: sessionData, version: 0 })); } if (Object.keys(localData).length > 0) { - localStorage.setItem(name, JSON.stringify({ state: localData, version: 0 })); + localStorage.setItem(storageKey, JSON.stringify({ state: localData, version: 0 })); } }, removeItem: (name: string) => { - sessionStorage.removeItem(name); - localStorage.removeItem(name); + const storageKey = getStorageKey(name); + sessionStorage.removeItem(storageKey); + localStorage.removeItem(storageKey); } }; }; @@ -125,3 +138,5 @@ export const useChatStore = create()( ) ) ); + +export { createCustomStorage }; diff --git a/test/cases/web/core/chat/context/useChatStore.test.ts b/test/cases/web/core/chat/context/useChatStore.test.ts new file mode 100644 index 000000000000..9e17c873ed4d --- /dev/null +++ b/test/cases/web/core/chat/context/useChatStore.test.ts @@ -0,0 +1,170 @@ +import { vi, describe, it, expect, beforeEach } from 'vitest'; +import { useChatStore, createCustomStorage } from '@/web/core/chat/context/useChatStore'; +import { ChatSourceEnum } from '@fastgpt/global/core/chat/constants'; +import { getNanoid } from '@fastgpt/global/common/string/tools'; + +vi.mock('@fastgpt/global/common/string/tools', () => ({ + getNanoid: vi.fn().mockReturnValue('test-nanoid') +})); + +const mockStorage = () => { + const store = new Map(); + return { + getItem: (key: string) => store.get(key) || null, + setItem: (key: string, value: string) => store.set(key, value), + clear: () => store.clear(), + removeItem: (key: string) => store.delete(key) + }; +}; + +const mockWindow = () => { + const windowMock = { + location: { + search: '?appId=test123' + }, + sessionStorage: mockStorage(), + localStorage: mockStorage() + }; + + vi.stubGlobal('window', windowMock); + global.sessionStorage = windowMock.sessionStorage; + global.localStorage = windowMock.localStorage; +}; + +beforeEach(() => { + vi.resetModules(); + vi.clearAllMocks(); + mockWindow(); + const store = useChatStore.getState(); + store.source = undefined; + store.appId = ''; + store.chatId = ''; + store.lastChatId = ''; + store.lastChatAppId = ''; + store.outLinkAuthData = {}; + sessionStorage.clear(); + localStorage.clear(); +}); + +describe('useChatStore', () => { + it('should set source and restore last chat if available', () => { + const store = useChatStore.getState(); + store.lastChatAppId = 'app123'; + store.lastChatId = `${ChatSourceEnum.share}-chat123`; + + store.setSource(ChatSourceEnum.share); + + const updatedStore = useChatStore.getState(); + expect(updatedStore.source).toBe(ChatSourceEnum.share); + expect(updatedStore.chatId).toBe('chat123'); + expect(updatedStore.lastChatAppId).toBe('app123'); + }); + + it('should generate new chatId when source changes', () => { + const store = useChatStore.getState(); + store.source = ChatSourceEnum.share; + store.chatId = 'old-id'; + + store.setSource(ChatSourceEnum.api); + const updatedStore = useChatStore.getState(); + + expect(updatedStore.chatId).toBe('test-nanoid'); + expect(updatedStore.chatId).not.toBe('old-id'); + }); + + it('should set appId and lastChatAppId', () => { + const store = useChatStore.getState(); + store.setAppId('test123'); + const updatedStore = useChatStore.getState(); + + expect(updatedStore.appId).toBe('test123'); + expect(updatedStore.lastChatAppId).toBe('test123'); + }); + + it('should not set empty appId', () => { + const store = useChatStore.getState(); + store.setAppId('test123'); + store.setAppId(''); + const updatedStore = useChatStore.getState(); + + expect(updatedStore.appId).toBe('test123'); + expect(updatedStore.lastChatAppId).toBe('test123'); + }); + + it('should set chatId and lastChatId', () => { + const store = useChatStore.getState(); + store.source = ChatSourceEnum.share; + store.setChatId('test-id'); + const updatedStore = useChatStore.getState(); + + expect(updatedStore.chatId).toBe('test-id'); + expect(updatedStore.lastChatId).toBe(`${ChatSourceEnum.share}-test-id`); + }); + + it('should generate new chatId if none provided', () => { + const store = useChatStore.getState(); + store.source = ChatSourceEnum.share; + store.setChatId(); + const updatedStore = useChatStore.getState(); + + expect(updatedStore.chatId).toBe('test-nanoid'); + }); + + it('should set outLinkAuthData', () => { + const store = useChatStore.getState(); + const authData = { apikey: 'test-key' }; + store.setOutLinkAuthData(authData); + const updatedStore = useChatStore.getState(); + + expect(updatedStore.outLinkAuthData).toEqual(authData); + }); +}); + +describe('createCustomStorage', () => { + it('should create storage with appId in key', () => { + const storage = createCustomStorage(); + const testData = { + state: { + source: ChatSourceEnum.share, + chatId: '123', + appId: 'app123', + lastChatId: 'last123', + lastChatAppId: 'lastApp123' + }, + version: 0 + }; + + storage.setItem('test', JSON.stringify(testData)); + + const sessionResult = JSON.parse(sessionStorage.getItem('test_test123') || '{}'); + const localResult = JSON.parse(localStorage.getItem('test_test123') || '{}'); + + expect(sessionResult.state).toEqual({ + source: ChatSourceEnum.share, + chatId: '123', + appId: 'app123' + }); + + expect(localResult.state).toEqual({ + lastChatId: 'last123', + lastChatAppId: 'lastApp123' + }); + }); + + it('should remove items from both storages', () => { + const storage = createCustomStorage(); + const testData = { + state: { + source: ChatSourceEnum.share, + chatId: '123' + }, + version: 0 + }; + + storage.setItem('test', JSON.stringify(testData)); + storage.removeItem('test'); + + expect(sessionStorage.getItem('test_test123')).toBeNull(); + expect(localStorage.getItem('test_test123')).toBeNull(); + }); +}); diff --git a/v8-compile-cache-0/x64/11.3.244.8-node.19/zSusrzSlocalzSlibzSnode_moduleszScorepackzSdistzSpnpm.js.BLOB b/v8-compile-cache-0/x64/11.3.244.8-node.19/zSusrzSlocalzSlibzSnode_moduleszScorepackzSdistzSpnpm.js.BLOB new file mode 100644 index 000000000000..a7a9179db135 Binary files /dev/null and b/v8-compile-cache-0/x64/11.3.244.8-node.19/zSusrzSlocalzSlibzSnode_moduleszScorepackzSdistzSpnpm.js.BLOB differ diff --git a/v8-compile-cache-0/x64/11.3.244.8-node.19/zSusrzSlocalzSlibzSnode_moduleszScorepackzSdistzSpnpm.js.MAP b/v8-compile-cache-0/x64/11.3.244.8-node.19/zSusrzSlocalzSlibzSnode_moduleszScorepackzSdistzSpnpm.js.MAP new file mode 100644 index 000000000000..68f2e3345eeb --- /dev/null +++ b/v8-compile-cache-0/x64/11.3.244.8-node.19/zSusrzSlocalzSlibzSnode_moduleszScorepackzSdistzSpnpm.js.MAP @@ -0,0 +1 @@ +{"/root/.cache/node/corepack/v1/pnpm/9.15.5/bin/pnpm.cjs":["f5286335ac1397138eff043e0d78e29501577055",0,1472],"/root/.cache/node/corepack/v1/pnpm/9.15.5/dist/pnpm.cjs":["0b2b22796df6e249cd89f4cdf06786d40a52564e",1472,886352]} \ No newline at end of file