@lobehub/lobehub 2.0.0-next.12 → 2.0.0-next.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (212) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/changelog/v1.json +18 -0
  3. package/package.json +1 -1
  4. package/packages/const/src/version.ts +3 -3
  5. package/packages/database/src/repositories/dataImporter/deprecated/__tests__/index.test.ts +2 -1
  6. package/packages/database/src/repositories/dataImporter/deprecated/index.ts +7 -1
  7. package/src/app/[variants]/(main)/(mobile)/me/(home)/__tests__/useCategory.test.tsx +9 -0
  8. package/src/app/[variants]/(main)/(mobile)/me/(home)/layout.tsx +0 -2
  9. package/src/app/[variants]/(main)/chat/@session/features/SessionListContent/List/Item/Actions.tsx +3 -28
  10. package/src/app/[variants]/(main)/chat/_layout/Desktop/index.tsx +0 -2
  11. package/src/app/[variants]/(main)/chat/_layout/Mobile.tsx +1 -5
  12. package/src/app/[variants]/(main)/chat/settings/features/HeaderContent.tsx +2 -62
  13. package/src/app/[variants]/(main)/image/features/PromptInput/index.tsx +1 -1
  14. package/src/app/[variants]/(main)/image/page.tsx +0 -2
  15. package/src/app/[variants]/(main)/profile/_layout/Desktop/index.tsx +23 -24
  16. package/src/app/[variants]/(main)/profile/_layout/Mobile/index.tsx +5 -9
  17. package/src/app/[variants]/(main)/settings/_layout/Desktop/index.tsx +0 -2
  18. package/src/app/[variants]/(main)/settings/_layout/Mobile/index.tsx +0 -2
  19. package/src/app/[variants]/(main)/settings/provider/(list)/ProviderGrid/Card.tsx +1 -1
  20. package/src/app/[variants]/loading/index.tsx +1 -10
  21. package/src/components/Link.tsx +12 -0
  22. package/src/envs/app.ts +5 -8
  23. package/src/features/DataImporter/index.tsx +15 -60
  24. package/src/features/DevPanel/PostgresViewer/usePgTable.ts +3 -2
  25. package/src/hooks/useInterceptingRoutes.test.ts +21 -3
  26. package/src/libs/trpc/client/index.ts +0 -1
  27. package/src/libs/trpc/client/lambda.ts +8 -5
  28. package/src/libs/trpc/lambda/context.ts +4 -1
  29. package/src/server/routers/desktop/mcp.ts +1 -3
  30. package/src/server/routers/lambda/config/index.test.ts +2 -2
  31. package/src/server/routers/tools/mcp.ts +2 -3
  32. package/src/server/routers/tools/search.test.ts +1 -7
  33. package/src/server/routers/tools/search.ts +1 -4
  34. package/src/services/__tests__/tool.test.ts +0 -3
  35. package/src/services/aiModel/index.test.ts +0 -3
  36. package/src/services/aiModel/index.ts +1 -7
  37. package/src/services/aiProvider/index.test.ts +0 -3
  38. package/src/services/aiProvider/index.ts +1 -7
  39. package/src/services/chatGroup/index.ts +1 -10
  40. package/src/services/config.ts +1 -65
  41. package/src/services/export/index.ts +1 -4
  42. package/src/services/file/index.ts +1 -11
  43. package/src/services/import/index.ts +1 -7
  44. package/src/services/message/index.ts +1 -11
  45. package/src/services/plugin/index.ts +1 -11
  46. package/src/services/session/index.ts +1 -11
  47. package/src/services/tableViewer/client.ts +12 -15
  48. package/src/services/thread/index.ts +1 -7
  49. package/src/services/topic/index.ts +1 -11
  50. package/src/services/user/index.ts +1 -13
  51. package/src/store/chat/slices/aiChat/actions/__tests__/generateAIChat.test.ts +0 -241
  52. package/src/store/chat/slices/aiChat/actions/__tests__/generateAIChatV2.test.ts +26 -1
  53. package/src/store/chat/slices/aiChat/actions/__tests__/helpers.ts +3 -1
  54. package/src/store/chat/slices/aiChat/actions/generateAIChat.ts +1 -138
  55. package/src/store/user/slices/common/action.test.ts +1 -4
  56. package/src/app/(backend)/trpc/edge/[trpc]/route.ts +0 -26
  57. package/src/app/[variants]/(main)/(mobile)/me/data/features/Category.tsx +0 -48
  58. package/src/app/[variants]/(main)/(mobile)/me/data/features/Header.tsx +0 -33
  59. package/src/app/[variants]/(main)/(mobile)/me/data/layout.tsx +0 -13
  60. package/src/app/[variants]/(main)/(mobile)/me/data/loading.tsx +0 -5
  61. package/src/app/[variants]/(main)/(mobile)/me/data/page.tsx +0 -29
  62. package/src/app/[variants]/(main)/chat/features/Migration/DBReader.ts +0 -290
  63. package/src/app/[variants]/(main)/chat/features/Migration/ExportConfigButton.tsx +0 -35
  64. package/src/app/[variants]/(main)/chat/features/Migration/Failed.tsx +0 -120
  65. package/src/app/[variants]/(main)/chat/features/Migration/Modal.tsx +0 -81
  66. package/src/app/[variants]/(main)/chat/features/Migration/Start.tsx +0 -108
  67. package/src/app/[variants]/(main)/chat/features/Migration/UpgradeButton.tsx +0 -71
  68. package/src/app/[variants]/(main)/chat/features/Migration/const.ts +0 -15
  69. package/src/app/[variants]/(main)/chat/features/Migration/index.tsx +0 -50
  70. package/src/app/[variants]/loading/Client/Content.tsx +0 -48
  71. package/src/app/[variants]/loading/Client/Error.tsx +0 -27
  72. package/src/app/[variants]/loading/Client/Redirect.tsx +0 -47
  73. package/src/app/[variants]/loading/Client/index.tsx +0 -22
  74. package/src/components/InnerLink.tsx +0 -20
  75. package/src/database/_deprecated/core/__tests__/db-upgrade.test.ts +0 -42
  76. package/src/database/_deprecated/core/__tests__/db.test.ts +0 -79
  77. package/src/database/_deprecated/core/__tests__/model.test.ts +0 -55
  78. package/src/database/_deprecated/core/db.ts +0 -246
  79. package/src/database/_deprecated/core/index.ts +0 -2
  80. package/src/database/_deprecated/core/migrations/migrateSettingsToUser/fixtures/input.json +0 -55
  81. package/src/database/_deprecated/core/migrations/migrateSettingsToUser/fixtures/output.json +0 -60
  82. package/src/database/_deprecated/core/migrations/migrateSettingsToUser/index.test.ts +0 -14
  83. package/src/database/_deprecated/core/migrations/migrateSettingsToUser/index.ts +0 -22
  84. package/src/database/_deprecated/core/migrations/migrateSettingsToUser/type.ts +0 -105
  85. package/src/database/_deprecated/core/model.ts +0 -218
  86. package/src/database/_deprecated/core/schemas.ts +0 -88
  87. package/src/database/_deprecated/core/types/db.ts +0 -15
  88. package/src/database/_deprecated/models/__DEBUG.ts +0 -124
  89. package/src/database/_deprecated/models/__tests__/file.test.ts +0 -83
  90. package/src/database/_deprecated/models/__tests__/message.test.ts +0 -426
  91. package/src/database/_deprecated/models/__tests__/plugin.test.ts +0 -81
  92. package/src/database/_deprecated/models/__tests__/session.test.ts +0 -253
  93. package/src/database/_deprecated/models/__tests__/sessionGroup.test.ts +0 -220
  94. package/src/database/_deprecated/models/__tests__/topic.test.ts +0 -523
  95. package/src/database/_deprecated/models/__tests__/user.test.ts +0 -82
  96. package/src/database/_deprecated/models/file.ts +0 -51
  97. package/src/database/_deprecated/models/message.ts +0 -277
  98. package/src/database/_deprecated/models/plugin.ts +0 -62
  99. package/src/database/_deprecated/models/session.ts +0 -271
  100. package/src/database/_deprecated/models/sessionGroup.ts +0 -93
  101. package/src/database/_deprecated/models/topic.ts +0 -250
  102. package/src/database/_deprecated/models/user.ts +0 -69
  103. package/src/database/_deprecated/schemas/files.ts +0 -39
  104. package/src/database/_deprecated/schemas/message.ts +0 -50
  105. package/src/database/_deprecated/schemas/plugin.ts +0 -12
  106. package/src/database/_deprecated/schemas/session.ts +0 -54
  107. package/src/database/_deprecated/schemas/sessionGroup.ts +0 -8
  108. package/src/database/_deprecated/schemas/topic.ts +0 -12
  109. package/src/database/_deprecated/schemas/user.ts +0 -40
  110. package/src/features/DataImporter/_deprecated.ts +0 -43
  111. package/src/features/InitClientDB/EnableModal.tsx +0 -118
  112. package/src/features/InitClientDB/ErrorResult.tsx +0 -143
  113. package/src/features/InitClientDB/InitIndicator.tsx +0 -124
  114. package/src/features/InitClientDB/PGliteIcon.tsx +0 -28
  115. package/src/features/InitClientDB/features/DatabaseRepair/Backup.tsx +0 -75
  116. package/src/features/InitClientDB/features/DatabaseRepair/Diagnosis.tsx +0 -98
  117. package/src/features/InitClientDB/features/DatabaseRepair/Repair.tsx +0 -218
  118. package/src/features/InitClientDB/features/DatabaseRepair/index.tsx +0 -91
  119. package/src/features/InitClientDB/index.tsx +0 -37
  120. package/src/libs/trpc/client/edge.ts +0 -26
  121. package/src/libs/trpc/edge/context.ts +0 -71
  122. package/src/libs/trpc/edge/index.ts +0 -45
  123. package/src/libs/trpc/edge/init.ts +0 -26
  124. package/src/libs/trpc/edge/middleware/jwtPayload.test.ts +0 -75
  125. package/src/libs/trpc/edge/middleware/jwtPayload.ts +0 -14
  126. package/src/migrations/FromV0ToV1.ts +0 -10
  127. package/src/migrations/FromV1ToV2/fixtures/input-v1-session.json +0 -191
  128. package/src/migrations/FromV1ToV2/fixtures/output-v2.json +0 -202
  129. package/src/migrations/FromV1ToV2/index.ts +0 -82
  130. package/src/migrations/FromV1ToV2/migrations.test.ts +0 -224
  131. package/src/migrations/FromV1ToV2/types/v1.ts +0 -78
  132. package/src/migrations/FromV1ToV2/types/v2.ts +0 -52
  133. package/src/migrations/FromV2ToV3/fixtures/input-v2-session.json +0 -72
  134. package/src/migrations/FromV2ToV3/fixtures/output-v3-from-v1.json +0 -203
  135. package/src/migrations/FromV2ToV3/fixtures/output-v3.json +0 -74
  136. package/src/migrations/FromV2ToV3/index.ts +0 -30
  137. package/src/migrations/FromV2ToV3/migrations.test.ts +0 -42
  138. package/src/migrations/FromV2ToV3/types/v3.ts +0 -27
  139. package/src/migrations/FromV3ToV4/fixtures/azure-input-v3.json +0 -79
  140. package/src/migrations/FromV3ToV4/fixtures/azure-output-v4.json +0 -75
  141. package/src/migrations/FromV3ToV4/fixtures/ollama-input-v3.json +0 -85
  142. package/src/migrations/FromV3ToV4/fixtures/ollama-output-v4.json +0 -86
  143. package/src/migrations/FromV3ToV4/fixtures/openai-input-v3.json +0 -77
  144. package/src/migrations/FromV3ToV4/fixtures/openai-output-v4.json +0 -77
  145. package/src/migrations/FromV3ToV4/fixtures/openrouter-input-v3.json +0 -82
  146. package/src/migrations/FromV3ToV4/fixtures/openrouter-output-v4.json +0 -85
  147. package/src/migrations/FromV3ToV4/fixtures/output-v4-from-v1.json +0 -203
  148. package/src/migrations/FromV3ToV4/index.ts +0 -102
  149. package/src/migrations/FromV3ToV4/migrations.test.ts +0 -195
  150. package/src/migrations/FromV3ToV4/types/v3.ts +0 -52
  151. package/src/migrations/FromV3ToV4/types/v4.ts +0 -37
  152. package/src/migrations/FromV4ToV5/fixtures/from-v1-to-v5-output.json +0 -245
  153. package/src/migrations/FromV4ToV5/fixtures/function-input-v4.json +0 -96
  154. package/src/migrations/FromV4ToV5/fixtures/function-output-v5.json +0 -120
  155. package/src/migrations/FromV4ToV5/index.ts +0 -58
  156. package/src/migrations/FromV4ToV5/migrations.test.ts +0 -49
  157. package/src/migrations/FromV4ToV5/types/v4.ts +0 -21
  158. package/src/migrations/FromV4ToV5/types/v5.ts +0 -27
  159. package/src/migrations/FromV5ToV6/fixtures/from-v1-to-v6-output.json +0 -247
  160. package/src/migrations/FromV5ToV6/fixtures/session-input-v5.json +0 -81
  161. package/src/migrations/FromV5ToV6/fixtures/session-output-v6.json +0 -85
  162. package/src/migrations/FromV5ToV6/index.ts +0 -61
  163. package/src/migrations/FromV5ToV6/migrations.test.ts +0 -50
  164. package/src/migrations/FromV5ToV6/types/v5.ts +0 -48
  165. package/src/migrations/FromV5ToV6/types/v6.ts +0 -63
  166. package/src/migrations/FromV6ToV7/fixtures/output-v7-from-v1.json +0 -203
  167. package/src/migrations/FromV6ToV7/fixtures/provider-input-v6.json +0 -103
  168. package/src/migrations/FromV6ToV7/fixtures/provider-output-v7.json +0 -118
  169. package/src/migrations/FromV6ToV7/index.ts +0 -101
  170. package/src/migrations/FromV6ToV7/migrations.test.ts +0 -64
  171. package/src/migrations/FromV6ToV7/types/v6.ts +0 -61
  172. package/src/migrations/FromV6ToV7/types/v7.ts +0 -69
  173. package/src/migrations/VersionController.test.ts +0 -88
  174. package/src/migrations/VersionController.ts +0 -67
  175. package/src/migrations/index.ts +0 -61
  176. package/src/server/routers/edge/appStatus.ts +0 -3
  177. package/src/server/routers/edge/index.ts +0 -14
  178. package/src/server/routers/edge/upload.ts +0 -16
  179. package/src/services/aiModel/client.ts +0 -70
  180. package/src/services/aiProvider/client.ts +0 -58
  181. package/src/services/baseClientService/index.ts +0 -9
  182. package/src/services/chatGroup/client.ts +0 -63
  183. package/src/services/export/_deprecated.ts +0 -155
  184. package/src/services/export/client.ts +0 -15
  185. package/src/services/file/_deprecated.test.ts +0 -119
  186. package/src/services/file/_deprecated.ts +0 -80
  187. package/src/services/file/client.test.ts +0 -199
  188. package/src/services/file/client.ts +0 -85
  189. package/src/services/import/_deprecated.ts +0 -115
  190. package/src/services/import/client.test.ts +0 -1015
  191. package/src/services/import/client.ts +0 -64
  192. package/src/services/message/_deprecated.test.ts +0 -398
  193. package/src/services/message/_deprecated.ts +0 -168
  194. package/src/services/message/client.test.ts +0 -410
  195. package/src/services/message/client.ts +0 -192
  196. package/src/services/plugin/_deprecated.test.ts +0 -162
  197. package/src/services/plugin/_deprecated.ts +0 -42
  198. package/src/services/plugin/client.test.ts +0 -177
  199. package/src/services/plugin/client.ts +0 -46
  200. package/src/services/session/_deprecated.test.ts +0 -440
  201. package/src/services/session/_deprecated.ts +0 -190
  202. package/src/services/session/client.test.ts +0 -413
  203. package/src/services/session/client.ts +0 -193
  204. package/src/services/thread/client.ts +0 -51
  205. package/src/services/topic/_deprecated.test.ts +0 -245
  206. package/src/services/topic/_deprecated.ts +0 -75
  207. package/src/services/topic/client.ts +0 -89
  208. package/src/services/topic/pglite.test.ts +0 -212
  209. package/src/services/user/_deprecated.test.ts +0 -101
  210. package/src/services/user/_deprecated.ts +0 -70
  211. package/src/services/user/client.test.ts +0 -111
  212. package/src/services/user/client.ts +0 -104
@@ -1,101 +0,0 @@
1
- import type { PartialDeep } from 'type-fest';
2
- import { Mock, beforeEach, describe, expect, it, vi } from 'vitest';
3
-
4
- import { UserModel } from '@/database/_deprecated/models/user';
5
- import { UserPreference } from '@/types/user';
6
- import { UserSettings } from '@/types/user/settings';
7
-
8
- import { ClientService } from './_deprecated';
9
-
10
- vi.mock('@/database/_deprecated/models/user', () => ({
11
- UserModel: {
12
- getUser: vi.fn(),
13
- updateSettings: vi.fn(),
14
- resetSettings: vi.fn(),
15
- updateAvatar: vi.fn(),
16
- },
17
- }));
18
-
19
- const mockUser = {
20
- avatar: 'avatar.png',
21
- settings: { themeMode: 'light' } as unknown as UserSettings,
22
- uuid: 'user-id',
23
- };
24
-
25
- const mockPreference = {
26
- useCmdEnterToSend: true,
27
- } as UserPreference;
28
-
29
- describe('ClientService', () => {
30
- let clientService: ClientService;
31
-
32
- beforeEach(() => {
33
- vi.clearAllMocks();
34
- clientService = new ClientService();
35
- });
36
-
37
- it('should get user state correctly', async () => {
38
- (UserModel.getUser as Mock).mockResolvedValue(mockUser);
39
- const spyOn = vi
40
- .spyOn(clientService['preferenceStorage'], 'getFromLocalStorage')
41
- .mockResolvedValue(mockPreference);
42
-
43
- const userState = await clientService.getUserState();
44
-
45
- expect(userState).toEqual({
46
- avatar: mockUser.avatar,
47
- isOnboard: true,
48
- canEnablePWAGuide: false,
49
- hasConversation: false,
50
- canEnableTrace: false,
51
- preference: mockPreference,
52
- settings: mockUser.settings,
53
- userId: mockUser.uuid,
54
- });
55
- expect(UserModel.getUser).toHaveBeenCalledTimes(1);
56
- expect(spyOn).toHaveBeenCalledTimes(1);
57
- });
58
-
59
- it('should update user settings correctly', async () => {
60
- const settingsPatch: PartialDeep<UserSettings> = { general: { fontSize: 12 } };
61
- (UserModel.updateSettings as Mock).mockResolvedValue(undefined);
62
-
63
- await clientService.updateUserSettings(settingsPatch);
64
-
65
- expect(UserModel.updateSettings).toHaveBeenCalledWith(settingsPatch);
66
- expect(UserModel.updateSettings).toHaveBeenCalledTimes(1);
67
- });
68
-
69
- it('should reset user settings correctly', async () => {
70
- (UserModel.resetSettings as Mock).mockResolvedValue(undefined);
71
-
72
- await clientService.resetUserSettings();
73
-
74
- expect(UserModel.resetSettings).toHaveBeenCalledTimes(1);
75
- });
76
-
77
- it('should update user avatar correctly', async () => {
78
- const newAvatar = 'new-avatar.png';
79
- (UserModel.updateAvatar as Mock).mockResolvedValue(undefined);
80
-
81
- await clientService.updateAvatar(newAvatar);
82
-
83
- expect(UserModel.updateAvatar).toHaveBeenCalledWith(newAvatar);
84
- expect(UserModel.updateAvatar).toHaveBeenCalledTimes(1);
85
- });
86
-
87
- it('should update user preference correctly', async () => {
88
- const newPreference = {
89
- useCmdEnterToSend: false,
90
- } as UserPreference;
91
-
92
- const spyOn = vi
93
- .spyOn(clientService['preferenceStorage'], 'saveToLocalStorage')
94
- .mockResolvedValue(undefined);
95
-
96
- await clientService.updatePreference(newPreference);
97
-
98
- expect(spyOn).toHaveBeenCalledWith(newPreference);
99
- expect(spyOn).toHaveBeenCalledTimes(1);
100
- });
101
- });
@@ -1,70 +0,0 @@
1
- import type { PartialDeep } from 'type-fest';
2
-
3
- import { MessageModel } from '@/database/_deprecated/models/message';
4
- import { SessionModel } from '@/database/_deprecated/models/session';
5
- import { UserModel } from '@/database/_deprecated/models/user';
6
- import { UserGuide, UserInitializationState, UserPreference } from '@/types/user';
7
- import { UserSettings } from '@/types/user/settings';
8
- import { AsyncLocalStorage } from '@/utils/localStorage';
9
-
10
- import { IUserService } from './type';
11
-
12
- export class ClientService implements IUserService {
13
- private preferenceStorage: AsyncLocalStorage<UserPreference>;
14
-
15
- constructor() {
16
- this.preferenceStorage = new AsyncLocalStorage('LOBE_PREFERENCE');
17
- }
18
-
19
- getUserRegistrationDuration = async () => {
20
- throw new Error('Method not implemented.');
21
- };
22
-
23
- async getUserState(): Promise<UserInitializationState> {
24
- const user = await UserModel.getUser();
25
- const messageCount = await MessageModel.count();
26
- const sessionCount = await SessionModel.count();
27
-
28
- return {
29
- avatar: user.avatar,
30
- canEnablePWAGuide: messageCount >= 4,
31
- canEnableTrace: messageCount >= 4,
32
- hasConversation: messageCount > 0 || sessionCount > 0,
33
- isOnboard: true,
34
- preference: await this.preferenceStorage.getFromLocalStorage(),
35
- settings: user.settings as UserSettings,
36
- userId: user.uuid,
37
- };
38
- }
39
-
40
- getUserSSOProviders = async () => {
41
- // Account not exist on next-auth in client mode, no need to implement this method
42
- return [];
43
- };
44
-
45
- unlinkSSOProvider = async () => {
46
- // Account not exist on next-auth in client mode, no need to implement this method
47
- };
48
-
49
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
50
- updateUserSettings = async (patch: PartialDeep<UserSettings>, _?: any) => {
51
- return UserModel.updateSettings(patch);
52
- };
53
-
54
- resetUserSettings = async () => {
55
- return UserModel.resetSettings();
56
- };
57
-
58
- async updateAvatar(avatar: string) {
59
- await UserModel.updateAvatar(avatar);
60
- }
61
-
62
- async updatePreference(preference: Partial<UserPreference>) {
63
- await this.preferenceStorage.saveToLocalStorage(preference);
64
- }
65
-
66
- // eslint-disable-next-line @typescript-eslint/no-unused-vars,unused-imports/no-unused-vars
67
- async updateGuide(guide: Partial<UserGuide>) {
68
- throw new Error('Method not implemented.');
69
- }
70
- }
@@ -1,111 +0,0 @@
1
- import { eq } from 'drizzle-orm';
2
- import type { PartialDeep } from 'type-fest';
3
- import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
4
-
5
- import { clientDB, initializeDB } from '@/database/client/db';
6
- import { userSettings, users } from '@/database/schemas';
7
- import { UserPreference } from '@/types/user';
8
- import { UserSettings } from '@/types/user/settings';
9
-
10
- import { ClientService } from './client';
11
-
12
- const mockUser = {
13
- avatar: 'avatar.png',
14
- settings: { themeMode: 'light' } as unknown as UserSettings,
15
- uuid: 'user-id',
16
- };
17
-
18
- const mockPreference = {
19
- useCmdEnterToSend: true,
20
- } as UserPreference;
21
- const clientService = new ClientService(mockUser.uuid);
22
-
23
- beforeEach(async () => {
24
- vi.clearAllMocks();
25
-
26
- await initializeDB();
27
- await clientDB.delete(users);
28
-
29
- await clientDB
30
- .insert(users)
31
- .values({ id: mockUser.uuid, avatar: 'avatar.png' })
32
- .onConflictDoNothing();
33
- await clientDB
34
- .insert(userSettings)
35
- .values({ id: mockUser.uuid, general: { themeMode: 'light' } });
36
- });
37
-
38
- describe('ClientService', () => {
39
- it('should get user state correctly', async () => {
40
- const spyOn = vi
41
- .spyOn(clientService['preferenceStorage'], 'getFromLocalStorage')
42
- .mockResolvedValue(mockPreference);
43
-
44
- const userState = await clientService.getUserState();
45
-
46
- expect(userState).toMatchObject({
47
- avatar: mockUser.avatar,
48
- isOnboard: true,
49
- canEnablePWAGuide: false,
50
- hasConversation: false,
51
- canEnableTrace: false,
52
- preference: mockPreference,
53
- settings: { general: { themeMode: 'light' } },
54
- userId: mockUser.uuid,
55
- });
56
- expect(spyOn).toHaveBeenCalledTimes(1);
57
- });
58
-
59
- it('should update user settings correctly', async () => {
60
- const settingsPatch: PartialDeep<UserSettings> = { general: { fontSize: 12 } };
61
-
62
- await clientService.updateUserSettings(settingsPatch);
63
-
64
- const result = await clientDB.query.userSettings.findFirst({
65
- where: eq(userSettings.id, mockUser.uuid),
66
- });
67
-
68
- expect(result).toMatchObject(settingsPatch);
69
- });
70
-
71
- it('should reset user settings correctly', async () => {
72
- await clientService.resetUserSettings();
73
-
74
- const result = await clientDB.query.userSettings.findFirst({
75
- where: eq(userSettings.id, mockUser.uuid),
76
- });
77
-
78
- expect(result).toBeUndefined();
79
- });
80
-
81
- it('should update user avatar correctly', async () => {
82
- const newAvatar = 'new-avatar.png';
83
-
84
- await clientService.updateAvatar(newAvatar);
85
- });
86
-
87
- it('should update user preference correctly', async () => {
88
- const newPreference = {
89
- useCmdEnterToSend: false,
90
- } as UserPreference;
91
-
92
- const spyOn = vi
93
- .spyOn(clientService['preferenceStorage'], 'saveToLocalStorage')
94
- .mockResolvedValue(undefined);
95
-
96
- await clientService.updatePreference(newPreference);
97
-
98
- expect(spyOn).toHaveBeenCalledWith(newPreference);
99
- expect(spyOn).toHaveBeenCalledTimes(1);
100
- });
101
-
102
- it('should return empty array for getUserSSOProviders', async () => {
103
- const providers = await clientService.getUserSSOProviders();
104
- expect(providers).toEqual([]);
105
- });
106
-
107
- it('should do nothing when unlinkSSOProvider is called', async () => {
108
- const result = await clientService.unlinkSSOProvider('google', '123');
109
- expect(result).toBeUndefined();
110
- });
111
- });
@@ -1,104 +0,0 @@
1
- import { clientDB } from '@/database/client/db';
2
- import { MessageModel } from '@/database/models/message';
3
- import { SessionModel } from '@/database/models/session';
4
- import { UserModel } from '@/database/models/user';
5
- import { users } from '@/database/schemas';
6
- import { BaseClientService } from '@/services/baseClientService';
7
- import { UserPreference } from '@/types/user';
8
- import { AsyncLocalStorage } from '@/utils/localStorage';
9
-
10
- import { IUserService } from './type';
11
-
12
- export class ClientService extends BaseClientService implements IUserService {
13
- private preferenceStorage: AsyncLocalStorage<UserPreference>;
14
-
15
- private get userModel(): UserModel {
16
- return new UserModel(clientDB as any, this.userId);
17
- }
18
- private get messageModel(): MessageModel {
19
- return new MessageModel(clientDB as any, this.userId);
20
- }
21
- private get sessionModel(): SessionModel {
22
- return new SessionModel(clientDB as any, this.userId);
23
- }
24
-
25
- constructor(userId?: string) {
26
- super(userId);
27
- this.preferenceStorage = new AsyncLocalStorage('LOBE_PREFERENCE');
28
- }
29
-
30
- getUserRegistrationDuration: IUserService['getUserRegistrationDuration'] = async () => {
31
- return this.userModel.getUserRegistrationDuration();
32
- };
33
-
34
- getUserState: IUserService['getUserState'] = async () => {
35
- // if user not exist in the db, create one to make sure the user exist
36
- await this.makeSureUserExist();
37
-
38
- const state = await this.userModel.getUserState((encryptKeyVaultsStr) =>
39
- encryptKeyVaultsStr ? JSON.parse(encryptKeyVaultsStr) : {},
40
- );
41
-
42
- const messageCount = await this.messageModel.count();
43
- const sessionCount = await this.sessionModel.count();
44
-
45
- return {
46
- ...state,
47
- avatar: state.avatar ?? '',
48
- canEnablePWAGuide: messageCount >= 4,
49
- canEnableTrace: messageCount >= 4,
50
- firstName: state.firstName,
51
- fullName: state.fullName,
52
- hasConversation: messageCount > 0 || sessionCount > 0,
53
- isOnboard: true,
54
- lastName: state.lastName,
55
- preference: await this.preferenceStorage.getFromLocalStorage(),
56
- username: state.username,
57
- };
58
- };
59
-
60
- getUserSSOProviders: IUserService['getUserSSOProviders'] = async () => {
61
- // Account not exist on next-auth in client mode, no need to implement this method
62
- return [];
63
- };
64
-
65
- unlinkSSOProvider: IUserService['unlinkSSOProvider'] = async () => {
66
- // Account not exist on next-auth in client mode, no need to implement this method
67
- };
68
-
69
- updateUserSettings: IUserService['updateUserSettings'] = async (value) => {
70
- const { keyVaults, ...res } = value;
71
-
72
- return this.userModel.updateSetting({ ...res, keyVaults: JSON.stringify(keyVaults) });
73
- };
74
-
75
- resetUserSettings: IUserService['resetUserSettings'] = async () => {
76
- return this.userModel.deleteSetting();
77
- };
78
-
79
- updateAvatar = async (avatar: string) => {
80
- await this.userModel.updateUser({ avatar });
81
- };
82
-
83
- updatePreference: IUserService['updatePreference'] = async (preference) => {
84
- await this.preferenceStorage.saveToLocalStorage(preference);
85
- };
86
-
87
- updateGuide: IUserService['updateGuide'] = async () => {
88
- throw new Error('Method not implemented.');
89
- };
90
-
91
- makeSureUserExist = async () => {
92
- const existUsers = await clientDB.query.users.findMany();
93
-
94
- let user: { id: string };
95
- if (existUsers.length === 0) {
96
- const result = await clientDB.insert(users).values({ id: this.userId }).returning();
97
- user = result[0];
98
- } else {
99
- user = existUsers[0];
100
- }
101
-
102
- return user;
103
- };
104
- }