@lobehub/lobehub 2.0.0-next.13 → 2.0.0-next.15

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 (219) hide show
  1. package/.github/workflows/desktop-pr-build.yml +6 -6
  2. package/.github/workflows/release-desktop-beta.yml +4 -4
  3. package/.github/workflows/release.yml +1 -2
  4. package/.github/workflows/test.yml +4 -5
  5. package/.nvmrc +1 -1
  6. package/CHANGELOG.md +42 -0
  7. package/apps/desktop/tsconfig.json +0 -1
  8. package/changelog/v1.json +14 -0
  9. package/e2e/tsconfig.json +0 -1
  10. package/package.json +58 -58
  11. package/packages/const/src/version.ts +3 -3
  12. package/packages/database/src/repositories/dataImporter/deprecated/__tests__/index.test.ts +2 -1
  13. package/packages/database/src/repositories/dataImporter/deprecated/index.ts +7 -1
  14. package/packages/web-crawler/tsconfig.json +0 -1
  15. package/src/app/[variants]/(main)/(mobile)/me/(home)/__tests__/useCategory.test.tsx +9 -0
  16. package/src/app/[variants]/(main)/(mobile)/me/(home)/layout.tsx +0 -2
  17. package/src/app/[variants]/(main)/chat/@session/features/SessionListContent/List/Item/Actions.tsx +3 -28
  18. package/src/app/[variants]/(main)/chat/_layout/Desktop/index.tsx +0 -2
  19. package/src/app/[variants]/(main)/chat/_layout/Mobile.tsx +1 -5
  20. package/src/app/[variants]/(main)/chat/settings/features/HeaderContent.tsx +2 -62
  21. package/src/app/[variants]/(main)/image/page.tsx +0 -2
  22. package/src/app/[variants]/(main)/profile/_layout/Desktop/index.tsx +23 -24
  23. package/src/app/[variants]/(main)/profile/_layout/Mobile/index.tsx +5 -9
  24. package/src/app/[variants]/(main)/settings/_layout/Desktop/index.tsx +0 -2
  25. package/src/app/[variants]/(main)/settings/_layout/Mobile/index.tsx +0 -2
  26. package/src/app/[variants]/(main)/settings/provider/(list)/ProviderGrid/Card.tsx +1 -1
  27. package/src/app/[variants]/loading/index.tsx +1 -10
  28. package/src/components/Link.tsx +12 -0
  29. package/src/envs/app.ts +5 -8
  30. package/src/features/DataImporter/index.tsx +15 -60
  31. package/src/features/DevPanel/PostgresViewer/usePgTable.ts +3 -2
  32. package/src/hooks/useInterceptingRoutes.test.ts +21 -3
  33. package/src/libs/trpc/client/index.ts +0 -1
  34. package/src/libs/trpc/client/lambda.ts +8 -5
  35. package/src/server/routers/desktop/mcp.ts +1 -3
  36. package/src/server/routers/lambda/config/index.test.ts +2 -2
  37. package/src/server/routers/tools/mcp.ts +2 -3
  38. package/src/server/routers/tools/search.test.ts +1 -7
  39. package/src/server/routers/tools/search.ts +1 -4
  40. package/src/services/__tests__/tool.test.ts +0 -3
  41. package/src/services/aiModel/index.test.ts +0 -3
  42. package/src/services/aiModel/index.ts +1 -7
  43. package/src/services/aiProvider/index.test.ts +0 -3
  44. package/src/services/aiProvider/index.ts +1 -7
  45. package/src/services/chatGroup/index.ts +1 -10
  46. package/src/services/config.ts +1 -65
  47. package/src/services/export/index.ts +1 -4
  48. package/src/services/file/index.ts +1 -11
  49. package/src/services/import/index.ts +1 -7
  50. package/src/services/message/index.ts +1 -11
  51. package/src/services/plugin/index.ts +1 -11
  52. package/src/services/session/index.ts +1 -11
  53. package/src/services/tableViewer/client.ts +12 -15
  54. package/src/services/thread/index.ts +1 -7
  55. package/src/services/topic/index.ts +1 -11
  56. package/src/services/user/index.ts +1 -13
  57. package/src/store/chat/slices/aiChat/actions/__tests__/generateAIChat.test.ts +0 -241
  58. package/src/store/chat/slices/aiChat/actions/__tests__/generateAIChatV2.test.ts +26 -1
  59. package/src/store/chat/slices/aiChat/actions/__tests__/helpers.ts +3 -1
  60. package/src/store/chat/slices/aiChat/actions/generateAIChat.ts +1 -138
  61. package/src/store/user/slices/common/action.test.ts +1 -4
  62. package/tsconfig.json +0 -1
  63. package/src/app/(backend)/trpc/edge/[trpc]/route.ts +0 -26
  64. package/src/app/[variants]/(main)/(mobile)/me/data/features/Category.tsx +0 -48
  65. package/src/app/[variants]/(main)/(mobile)/me/data/features/Header.tsx +0 -33
  66. package/src/app/[variants]/(main)/(mobile)/me/data/layout.tsx +0 -13
  67. package/src/app/[variants]/(main)/(mobile)/me/data/loading.tsx +0 -5
  68. package/src/app/[variants]/(main)/(mobile)/me/data/page.tsx +0 -29
  69. package/src/app/[variants]/(main)/chat/features/Migration/DBReader.ts +0 -290
  70. package/src/app/[variants]/(main)/chat/features/Migration/ExportConfigButton.tsx +0 -35
  71. package/src/app/[variants]/(main)/chat/features/Migration/Failed.tsx +0 -120
  72. package/src/app/[variants]/(main)/chat/features/Migration/Modal.tsx +0 -81
  73. package/src/app/[variants]/(main)/chat/features/Migration/Start.tsx +0 -108
  74. package/src/app/[variants]/(main)/chat/features/Migration/UpgradeButton.tsx +0 -71
  75. package/src/app/[variants]/(main)/chat/features/Migration/const.ts +0 -15
  76. package/src/app/[variants]/(main)/chat/features/Migration/index.tsx +0 -50
  77. package/src/app/[variants]/loading/Client/Content.tsx +0 -48
  78. package/src/app/[variants]/loading/Client/Error.tsx +0 -27
  79. package/src/app/[variants]/loading/Client/Redirect.tsx +0 -47
  80. package/src/app/[variants]/loading/Client/index.tsx +0 -22
  81. package/src/components/InnerLink.tsx +0 -20
  82. package/src/database/_deprecated/core/__tests__/db-upgrade.test.ts +0 -42
  83. package/src/database/_deprecated/core/__tests__/db.test.ts +0 -79
  84. package/src/database/_deprecated/core/__tests__/model.test.ts +0 -55
  85. package/src/database/_deprecated/core/db.ts +0 -246
  86. package/src/database/_deprecated/core/index.ts +0 -2
  87. package/src/database/_deprecated/core/migrations/migrateSettingsToUser/fixtures/input.json +0 -55
  88. package/src/database/_deprecated/core/migrations/migrateSettingsToUser/fixtures/output.json +0 -60
  89. package/src/database/_deprecated/core/migrations/migrateSettingsToUser/index.test.ts +0 -14
  90. package/src/database/_deprecated/core/migrations/migrateSettingsToUser/index.ts +0 -22
  91. package/src/database/_deprecated/core/migrations/migrateSettingsToUser/type.ts +0 -105
  92. package/src/database/_deprecated/core/model.ts +0 -218
  93. package/src/database/_deprecated/core/schemas.ts +0 -88
  94. package/src/database/_deprecated/core/types/db.ts +0 -15
  95. package/src/database/_deprecated/models/__DEBUG.ts +0 -124
  96. package/src/database/_deprecated/models/__tests__/file.test.ts +0 -83
  97. package/src/database/_deprecated/models/__tests__/message.test.ts +0 -426
  98. package/src/database/_deprecated/models/__tests__/plugin.test.ts +0 -81
  99. package/src/database/_deprecated/models/__tests__/session.test.ts +0 -253
  100. package/src/database/_deprecated/models/__tests__/sessionGroup.test.ts +0 -220
  101. package/src/database/_deprecated/models/__tests__/topic.test.ts +0 -523
  102. package/src/database/_deprecated/models/__tests__/user.test.ts +0 -82
  103. package/src/database/_deprecated/models/file.ts +0 -51
  104. package/src/database/_deprecated/models/message.ts +0 -277
  105. package/src/database/_deprecated/models/plugin.ts +0 -62
  106. package/src/database/_deprecated/models/session.ts +0 -271
  107. package/src/database/_deprecated/models/sessionGroup.ts +0 -93
  108. package/src/database/_deprecated/models/topic.ts +0 -250
  109. package/src/database/_deprecated/models/user.ts +0 -69
  110. package/src/database/_deprecated/schemas/files.ts +0 -39
  111. package/src/database/_deprecated/schemas/message.ts +0 -50
  112. package/src/database/_deprecated/schemas/plugin.ts +0 -12
  113. package/src/database/_deprecated/schemas/session.ts +0 -54
  114. package/src/database/_deprecated/schemas/sessionGroup.ts +0 -8
  115. package/src/database/_deprecated/schemas/topic.ts +0 -12
  116. package/src/database/_deprecated/schemas/user.ts +0 -40
  117. package/src/features/DataImporter/_deprecated.ts +0 -43
  118. package/src/features/InitClientDB/EnableModal.tsx +0 -118
  119. package/src/features/InitClientDB/ErrorResult.tsx +0 -143
  120. package/src/features/InitClientDB/InitIndicator.tsx +0 -124
  121. package/src/features/InitClientDB/PGliteIcon.tsx +0 -28
  122. package/src/features/InitClientDB/features/DatabaseRepair/Backup.tsx +0 -75
  123. package/src/features/InitClientDB/features/DatabaseRepair/Diagnosis.tsx +0 -98
  124. package/src/features/InitClientDB/features/DatabaseRepair/Repair.tsx +0 -218
  125. package/src/features/InitClientDB/features/DatabaseRepair/index.tsx +0 -91
  126. package/src/features/InitClientDB/index.tsx +0 -37
  127. package/src/libs/trpc/client/edge.ts +0 -26
  128. package/src/libs/trpc/edge/context.ts +0 -71
  129. package/src/libs/trpc/edge/index.ts +0 -45
  130. package/src/libs/trpc/edge/init.ts +0 -26
  131. package/src/libs/trpc/edge/middleware/jwtPayload.test.ts +0 -75
  132. package/src/libs/trpc/edge/middleware/jwtPayload.ts +0 -14
  133. package/src/migrations/FromV0ToV1.ts +0 -10
  134. package/src/migrations/FromV1ToV2/fixtures/input-v1-session.json +0 -191
  135. package/src/migrations/FromV1ToV2/fixtures/output-v2.json +0 -202
  136. package/src/migrations/FromV1ToV2/index.ts +0 -82
  137. package/src/migrations/FromV1ToV2/migrations.test.ts +0 -224
  138. package/src/migrations/FromV1ToV2/types/v1.ts +0 -78
  139. package/src/migrations/FromV1ToV2/types/v2.ts +0 -52
  140. package/src/migrations/FromV2ToV3/fixtures/input-v2-session.json +0 -72
  141. package/src/migrations/FromV2ToV3/fixtures/output-v3-from-v1.json +0 -203
  142. package/src/migrations/FromV2ToV3/fixtures/output-v3.json +0 -74
  143. package/src/migrations/FromV2ToV3/index.ts +0 -30
  144. package/src/migrations/FromV2ToV3/migrations.test.ts +0 -42
  145. package/src/migrations/FromV2ToV3/types/v3.ts +0 -27
  146. package/src/migrations/FromV3ToV4/fixtures/azure-input-v3.json +0 -79
  147. package/src/migrations/FromV3ToV4/fixtures/azure-output-v4.json +0 -75
  148. package/src/migrations/FromV3ToV4/fixtures/ollama-input-v3.json +0 -85
  149. package/src/migrations/FromV3ToV4/fixtures/ollama-output-v4.json +0 -86
  150. package/src/migrations/FromV3ToV4/fixtures/openai-input-v3.json +0 -77
  151. package/src/migrations/FromV3ToV4/fixtures/openai-output-v4.json +0 -77
  152. package/src/migrations/FromV3ToV4/fixtures/openrouter-input-v3.json +0 -82
  153. package/src/migrations/FromV3ToV4/fixtures/openrouter-output-v4.json +0 -85
  154. package/src/migrations/FromV3ToV4/fixtures/output-v4-from-v1.json +0 -203
  155. package/src/migrations/FromV3ToV4/index.ts +0 -102
  156. package/src/migrations/FromV3ToV4/migrations.test.ts +0 -195
  157. package/src/migrations/FromV3ToV4/types/v3.ts +0 -52
  158. package/src/migrations/FromV3ToV4/types/v4.ts +0 -37
  159. package/src/migrations/FromV4ToV5/fixtures/from-v1-to-v5-output.json +0 -245
  160. package/src/migrations/FromV4ToV5/fixtures/function-input-v4.json +0 -96
  161. package/src/migrations/FromV4ToV5/fixtures/function-output-v5.json +0 -120
  162. package/src/migrations/FromV4ToV5/index.ts +0 -58
  163. package/src/migrations/FromV4ToV5/migrations.test.ts +0 -49
  164. package/src/migrations/FromV4ToV5/types/v4.ts +0 -21
  165. package/src/migrations/FromV4ToV5/types/v5.ts +0 -27
  166. package/src/migrations/FromV5ToV6/fixtures/from-v1-to-v6-output.json +0 -247
  167. package/src/migrations/FromV5ToV6/fixtures/session-input-v5.json +0 -81
  168. package/src/migrations/FromV5ToV6/fixtures/session-output-v6.json +0 -85
  169. package/src/migrations/FromV5ToV6/index.ts +0 -61
  170. package/src/migrations/FromV5ToV6/migrations.test.ts +0 -50
  171. package/src/migrations/FromV5ToV6/types/v5.ts +0 -48
  172. package/src/migrations/FromV5ToV6/types/v6.ts +0 -63
  173. package/src/migrations/FromV6ToV7/fixtures/output-v7-from-v1.json +0 -203
  174. package/src/migrations/FromV6ToV7/fixtures/provider-input-v6.json +0 -103
  175. package/src/migrations/FromV6ToV7/fixtures/provider-output-v7.json +0 -118
  176. package/src/migrations/FromV6ToV7/index.ts +0 -101
  177. package/src/migrations/FromV6ToV7/migrations.test.ts +0 -64
  178. package/src/migrations/FromV6ToV7/types/v6.ts +0 -61
  179. package/src/migrations/FromV6ToV7/types/v7.ts +0 -69
  180. package/src/migrations/VersionController.test.ts +0 -88
  181. package/src/migrations/VersionController.ts +0 -67
  182. package/src/migrations/index.ts +0 -61
  183. package/src/server/routers/edge/appStatus.ts +0 -3
  184. package/src/server/routers/edge/index.ts +0 -14
  185. package/src/server/routers/edge/upload.ts +0 -16
  186. package/src/services/aiModel/client.ts +0 -70
  187. package/src/services/aiProvider/client.ts +0 -58
  188. package/src/services/baseClientService/index.ts +0 -9
  189. package/src/services/chatGroup/client.ts +0 -63
  190. package/src/services/export/_deprecated.ts +0 -155
  191. package/src/services/export/client.ts +0 -15
  192. package/src/services/file/_deprecated.test.ts +0 -119
  193. package/src/services/file/_deprecated.ts +0 -80
  194. package/src/services/file/client.test.ts +0 -199
  195. package/src/services/file/client.ts +0 -85
  196. package/src/services/import/_deprecated.ts +0 -115
  197. package/src/services/import/client.test.ts +0 -1015
  198. package/src/services/import/client.ts +0 -64
  199. package/src/services/message/_deprecated.test.ts +0 -398
  200. package/src/services/message/_deprecated.ts +0 -168
  201. package/src/services/message/client.test.ts +0 -410
  202. package/src/services/message/client.ts +0 -192
  203. package/src/services/plugin/_deprecated.test.ts +0 -162
  204. package/src/services/plugin/_deprecated.ts +0 -42
  205. package/src/services/plugin/client.test.ts +0 -177
  206. package/src/services/plugin/client.ts +0 -46
  207. package/src/services/session/_deprecated.test.ts +0 -440
  208. package/src/services/session/_deprecated.ts +0 -190
  209. package/src/services/session/client.test.ts +0 -413
  210. package/src/services/session/client.ts +0 -193
  211. package/src/services/thread/client.ts +0 -51
  212. package/src/services/topic/_deprecated.test.ts +0 -245
  213. package/src/services/topic/_deprecated.ts +0 -75
  214. package/src/services/topic/client.ts +0 -89
  215. package/src/services/topic/pglite.test.ts +0 -212
  216. package/src/services/user/_deprecated.test.ts +0 -101
  217. package/src/services/user/_deprecated.ts +0 -70
  218. package/src/services/user/client.test.ts +0 -111
  219. 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
- }