@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,155 +0,0 @@
1
- import { Migration } from '@/migrations';
2
- import { messageService } from '@/services/message';
3
- import { sessionService } from '@/services/session';
4
- import { topicService } from '@/services/topic';
5
- import { useSessionStore } from '@/store/session';
6
- import { sessionSelectors } from '@/store/session/selectors';
7
- import { useUserStore } from '@/store/user';
8
- import { settingsSelectors } from '@/store/user/selectors';
9
- import {
10
- ConfigFileAgents,
11
- ConfigFileAll,
12
- ConfigFileSessions,
13
- ConfigFileSettings,
14
- ConfigFileSingleSession,
15
- ConfigModelMap,
16
- ExportType,
17
- } from '@/types/exportConfig';
18
-
19
- type CreateConfigFileState<T extends ExportType> = ConfigModelMap[T]['state'];
20
-
21
- type CreateConfigFile<T extends ExportType> = ConfigModelMap[T]['file'];
22
-
23
- const createConfigFile = <T extends ExportType>(
24
- type: T,
25
- state: CreateConfigFileState<T>,
26
- ): CreateConfigFile<T> => {
27
- switch (type) {
28
- case 'agents': {
29
- return {
30
- exportType: 'agents',
31
- state,
32
- version: Migration.targetVersion,
33
- } as ConfigFileAgents;
34
- }
35
-
36
- case 'sessions': {
37
- return {
38
- exportType: 'sessions',
39
- state,
40
- version: Migration.targetVersion,
41
- } as ConfigFileSessions;
42
- }
43
-
44
- case 'settings': {
45
- return {
46
- exportType: 'settings',
47
- state,
48
- version: Migration.targetVersion,
49
- } as ConfigFileSettings;
50
- }
51
-
52
- case 'singleSession': {
53
- return {
54
- exportType: 'sessions',
55
- state,
56
- version: Migration.targetVersion,
57
- } as ConfigFileSingleSession;
58
- }
59
-
60
- case 'all': {
61
- return {
62
- exportType: 'all',
63
- state,
64
- version: Migration.targetVersion,
65
- } as ConfigFileAll;
66
- }
67
- }
68
-
69
- throw new Error('缺少正确的导出类型,请检查实现...');
70
- };
71
-
72
- /**
73
- * @deprecated
74
- */
75
- export class ConfigService {
76
- /**
77
- * export all agents
78
- */
79
- exportAgents = async () => {
80
- const agents = await sessionService.getSessionsByType('agent');
81
- const sessionGroups = await sessionService.getSessionGroups();
82
-
83
- return createConfigFile('agents', { sessionGroups, sessions: agents });
84
- };
85
-
86
- /**
87
- * export all sessions
88
- */
89
- exportSessions = async () => {
90
- const sessions = await sessionService.getSessionsByType();
91
- const sessionGroups = await sessionService.getSessionGroups();
92
- const messages = await messageService.getAllMessages();
93
- const topics = await topicService.getAllTopics();
94
-
95
- return createConfigFile('sessions', { messages, sessionGroups, sessions, topics });
96
- };
97
-
98
- /**
99
- * export a session
100
- */
101
- exportSingleSession = async (id: string) => {
102
- const session = this.getSession(id);
103
- if (!session) return;
104
-
105
- const messages = await messageService.getAllMessagesInSession(id);
106
- const topics = await topicService.getTopics({ containerId: id });
107
-
108
- const config = createConfigFile('singleSession', { messages, sessions: [session], topics });
109
- return { config, title: `${session.meta?.title}-session` };
110
- };
111
-
112
- exportSingleAgent = async (id: string) => {
113
- const agent = this.getAgent(id);
114
- if (!agent) return;
115
-
116
- const config = createConfigFile('agents', { sessionGroups: [], sessions: [agent] });
117
-
118
- return { config, title: `${agent.meta?.title}-session` };
119
- };
120
-
121
- /**
122
- * export settings
123
- */
124
- exportSettings = async () => {
125
- const settings = this.getSettings();
126
-
127
- return createConfigFile('settings', { settings });
128
- };
129
-
130
- /**
131
- * export all data
132
- */
133
- exportAll = async () => {
134
- const sessions = await sessionService.getSessionsByType();
135
- const sessionGroups = await sessionService.getSessionGroups();
136
- const messages = await messageService.getAllMessages();
137
- const topics = await topicService.getAllTopics();
138
- const settings = this.getSettings();
139
-
140
- return createConfigFile('all', { messages, sessionGroups, sessions, settings, topics });
141
- };
142
-
143
- private getSettings = () => settingsSelectors.exportSettings(useUserStore.getState());
144
-
145
- private getSession = (id: string) =>
146
- sessionSelectors.getSessionById(id)(useSessionStore.getState());
147
-
148
- private getAgent = (id: string) =>
149
- sessionSelectors.getSessionById(id)(useSessionStore.getState());
150
- }
151
-
152
- /**
153
- * @deprecated
154
- */
155
- export const configService = new ConfigService();
@@ -1,15 +0,0 @@
1
- import { clientDB } from '@/database/client/db';
2
- import { DataExporterRepos } from '@/database/repositories/dataExporter';
3
- import { BaseClientService } from '@/services/baseClientService';
4
-
5
- export class ClientService extends BaseClientService {
6
- private get dataExporterRepos(): DataExporterRepos {
7
- return new DataExporterRepos(clientDB as any, this.userId);
8
- }
9
-
10
- exportData = async () => {
11
- const data = await this.dataExporterRepos.export();
12
-
13
- return { data, url: undefined };
14
- };
15
- }
@@ -1,119 +0,0 @@
1
- import { Mock, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest';
2
-
3
- import { FileModel } from '@/database/_deprecated/models/file';
4
- import { DB_File } from '@/database/_deprecated/schemas/files';
5
- import { fileEnv } from '@/envs/file';
6
- import { clientS3Storage } from '@/services/file/ClientS3';
7
- import { serverConfigSelectors } from '@/store/serverConfig/selectors';
8
- import { createServerConfigStore } from '@/store/serverConfig/store';
9
-
10
- import { ClientService } from './_deprecated';
11
-
12
- const fileService = new ClientService();
13
-
14
- beforeAll(() => {
15
- createServerConfigStore();
16
- });
17
- // Mocks for the FileModel
18
- vi.mock('@/database/_deprecated/models/file', () => ({
19
- FileModel: {
20
- create: vi.fn(),
21
- delete: vi.fn(),
22
- findById: vi.fn(),
23
- clear: vi.fn(),
24
- },
25
- }));
26
-
27
- let s3Domain: string;
28
-
29
- vi.mock('@/config/file', () => ({
30
- fileEnv: {
31
- get NEXT_PUBLIC_S3_DOMAIN() {
32
- return s3Domain;
33
- },
34
- },
35
- }));
36
-
37
- // Mocks for the URL and Blob objects
38
- global.URL.createObjectURL = vi.fn();
39
- global.Blob = vi.fn();
40
-
41
- beforeEach(() => {
42
- // Reset all mocks before each test
43
- vi.resetAllMocks();
44
- s3Domain = '';
45
- });
46
-
47
- describe('FileService', () => {
48
- it('createFile should save the file to the database', async () => {
49
- const localFile = {
50
- name: 'test',
51
- fileType: 'image/png',
52
- url: 'client-s3://123',
53
- size: 1,
54
- hash: '123',
55
- };
56
-
57
- await clientS3Storage.putObject(
58
- '123',
59
- new File([new ArrayBuffer(1)], 'test.png', { type: 'image/png' }),
60
- );
61
-
62
- (FileModel.create as Mock).mockResolvedValue(localFile);
63
-
64
- const result = await fileService.createFile(localFile);
65
-
66
- expect(result).toEqual({ url: 'data:image/png;base64,AA==' });
67
- });
68
-
69
- it('removeFile should delete the file from the database', async () => {
70
- const fileId = '1';
71
- (FileModel.delete as Mock).mockResolvedValue(true);
72
-
73
- const result = await fileService.removeFile(fileId);
74
-
75
- expect(FileModel.delete).toHaveBeenCalledWith(fileId);
76
- expect(result).toBe(true);
77
- });
78
-
79
- describe('getFile', () => {
80
- it('should retrieve and convert local file info to FilePreview', async () => {
81
- const fileId = '1';
82
- const fileData = {
83
- name: 'test',
84
- data: new ArrayBuffer(1),
85
- fileType: 'image/png',
86
- saveMode: 'local',
87
- size: 1,
88
- createdAt: 1,
89
- updatedAt: 2,
90
- } as DB_File;
91
-
92
- (FileModel.findById as Mock).mockResolvedValue(fileData);
93
- (global.URL.createObjectURL as Mock).mockReturnValue('blob:test');
94
- (global.Blob as Mock).mockImplementation(() => ['test']);
95
-
96
- const result = await fileService.getFile(fileId);
97
-
98
- expect(FileModel.findById).toHaveBeenCalledWith(fileId);
99
- expect(result).toEqual({
100
- createdAt: new Date(1),
101
- id: '1',
102
- size: 1,
103
- type: 'image/png',
104
- name: 'test',
105
- url: 'blob:test',
106
- updatedAt: new Date(2),
107
- });
108
- });
109
-
110
- it('should throw an error when the file is not found', async () => {
111
- const fileId = 'non-existent';
112
- (FileModel.findById as Mock).mockResolvedValue(null);
113
-
114
- const getFilePromise = fileService.getFile(fileId);
115
-
116
- await expect(getFilePromise).rejects.toThrow('file not found');
117
- });
118
- });
119
- });
@@ -1,80 +0,0 @@
1
- import { FileModel } from '@/database/_deprecated/models/file';
2
- import { clientS3Storage } from '@/services/file/ClientS3';
3
- import { FileItem, UploadFileParams } from '@/types/files';
4
-
5
- import { IFileService } from './type';
6
-
7
- export class ClientService implements IFileService {
8
- async createFile(file: UploadFileParams) {
9
- // save to local storage
10
- // we may want to save to a remote server later
11
- const res = await FileModel.create({
12
- createdAt: Date.now(),
13
- data: undefined,
14
- fileHash: file.hash,
15
- fileType: file.fileType,
16
- metadata: file.metadata,
17
- name: file.name,
18
- saveMode: 'url',
19
- size: file.size,
20
- url: file.url,
21
- } as any);
22
-
23
- // get file to base64 url
24
- const base64 = await this.getBase64ByFileHash(file.hash!);
25
-
26
- return {
27
- id: res.id,
28
- url: `data:${file.fileType};base64,${base64}`,
29
- };
30
- }
31
-
32
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
33
- async checkFileHash(_hash: string) {
34
- return { isExist: false, metadata: {}, url: '' };
35
- }
36
-
37
- async getFile(id: string): Promise<FileItem> {
38
- const item = await FileModel.findById(id);
39
- if (!item) {
40
- throw new Error('file not found');
41
- }
42
-
43
- // arrayBuffer to blob or base64 to blob
44
- const blob = !!item.data
45
- ? new Blob([item.data!], { type: item.fileType })
46
- : // @ts-ignore
47
- new Blob([Buffer.from(item.base64!, 'base64')], { type: item.fileType });
48
-
49
- const url = URL.createObjectURL(blob);
50
-
51
- return {
52
- createdAt: new Date(item.createdAt),
53
- id,
54
- name: item.name,
55
- size: item.size,
56
- type: item.fileType,
57
- updatedAt: new Date(item.updatedAt),
58
- url,
59
- };
60
- }
61
-
62
- async removeFile(id: string) {
63
- return FileModel.delete(id);
64
- }
65
-
66
- async removeFiles(ids: string[]) {
67
- await Promise.all(ids.map((id) => FileModel.delete(id)));
68
- }
69
-
70
- async removeAllFiles() {
71
- return FileModel.clear();
72
- }
73
-
74
- private async getBase64ByFileHash(hash: string) {
75
- const fileItem = await clientS3Storage.getObject(hash);
76
- if (!fileItem) throw new Error('file not found');
77
-
78
- return Buffer.from(await fileItem.arrayBuffer()).toString('base64');
79
- }
80
- }
@@ -1,199 +0,0 @@
1
- import { eq } from 'drizzle-orm';
2
- import { beforeEach, describe, expect, it } from 'vitest';
3
-
4
- import { clientDB, initializeDB } from '@/database/client/db';
5
- import { files, globalFiles, users } from '@/database/schemas';
6
- import { clientS3Storage } from '@/services/file/ClientS3';
7
- import { UploadFileParams } from '@/types/files';
8
-
9
- import { ClientService } from './client';
10
-
11
- const userId = 'file-user';
12
-
13
- const fileService = new ClientService(userId);
14
-
15
- const mockFile = {
16
- name: 'mock.png',
17
- fileType: 'image/png',
18
- size: 1,
19
- url: '',
20
- };
21
-
22
- beforeEach(async () => {
23
- await initializeDB();
24
-
25
- await clientDB.delete(users);
26
- await clientDB.delete(globalFiles);
27
- // 创建测试数据
28
- await clientDB.transaction(async (tx) => {
29
- await tx.insert(users).values({ id: userId });
30
- });
31
- });
32
-
33
- describe('FileService', () => {
34
- describe('createFile', () => {
35
- it('createFile should save the file to the database', async () => {
36
- const localFile: UploadFileParams = {
37
- name: 'test',
38
- fileType: 'image/png',
39
- url: '',
40
- size: 1,
41
- hash: '123',
42
- };
43
-
44
- await clientS3Storage.putObject(
45
- '123',
46
- new File([new ArrayBuffer(1)], 'test.png', { type: 'image/png' }),
47
- );
48
-
49
- const result = await fileService.createFile(localFile);
50
-
51
- expect(result).toMatchObject({ url: 'data:image/png;base64,AA==' });
52
- });
53
-
54
- it('should throw error when file is not found in storage during base64 conversion', async () => {
55
- const localFile: UploadFileParams = {
56
- name: 'test',
57
- fileType: 'image/png',
58
- url: '',
59
- size: 1,
60
- hash: 'non-existing-hash',
61
- };
62
-
63
- // 不调用 clientS3Storage.putObject,模拟文件不存在的情况
64
-
65
- const promise = fileService.createFile(localFile);
66
-
67
- await expect(promise).rejects.toThrow('file not found');
68
- });
69
- });
70
-
71
- it('removeFile should delete the file from the database', async () => {
72
- const fileId = '1';
73
- await clientDB.insert(files).values({ id: fileId, userId, ...mockFile });
74
-
75
- await fileService.removeFile(fileId);
76
-
77
- const result = await clientDB.query.files.findFirst({
78
- where: eq(files.id, fileId),
79
- });
80
-
81
- expect(result).toBeUndefined();
82
- });
83
-
84
- describe('getFile', () => {
85
- it('should retrieve and convert local file info to FilePreview', async () => {
86
- const fileId = 'rwlijweled';
87
- const file = {
88
- fileType: 'image/png',
89
- size: 1,
90
- name: 'test.png',
91
- url: 'idb://12312/abc.png',
92
- hashId: '123tttt',
93
- };
94
-
95
- await clientDB.insert(globalFiles).values({ ...file, creator: userId });
96
-
97
- await clientDB.insert(files).values({
98
- id: fileId,
99
- userId,
100
- ...file,
101
- createdAt: new Date(1),
102
- updatedAt: new Date(2),
103
- fileHash: file.hashId,
104
- });
105
-
106
- await clientS3Storage.putObject(
107
- file.hashId,
108
- new File([new ArrayBuffer(1)], file.name, { type: file.fileType }),
109
- );
110
-
111
- const result = await fileService.getFile(fileId);
112
-
113
- expect(result).toMatchObject({
114
- createdAt: new Date(1),
115
- id: 'rwlijweled',
116
- size: 1,
117
- type: 'image/png',
118
- name: 'test.png',
119
- updatedAt: new Date(2),
120
- });
121
- });
122
-
123
- it('should throw an error when the file is not found', async () => {
124
- const fileId = 'non-existent';
125
-
126
- const getFilePromise = fileService.getFile(fileId);
127
-
128
- await expect(getFilePromise).rejects.toThrow('file not found');
129
- });
130
- });
131
-
132
- describe('removeFiles', () => {
133
- it('should delete multiple files from the database', async () => {
134
- const fileIds = ['1', '2', '3'];
135
-
136
- // 插入测试文件数据
137
- await Promise.all(
138
- fileIds.map((id) => clientDB.insert(files).values({ id, userId, ...mockFile })),
139
- );
140
-
141
- await fileService.removeFiles(fileIds);
142
-
143
- // 验证所有文件都被删除
144
- const remainingFiles = await clientDB.query.files.findMany({
145
- where: (fields, { inArray }) => inArray(fields.id, fileIds),
146
- });
147
-
148
- expect(remainingFiles).toHaveLength(0);
149
- });
150
- });
151
-
152
- describe('removeAllFiles', () => {
153
- it('should clear all files for the user', async () => {
154
- // 插入测试文件数据
155
- await Promise.all([
156
- clientDB.insert(files).values({ id: '1', userId, ...mockFile }),
157
- clientDB.insert(files).values({ id: '2', userId, ...mockFile }),
158
- ]);
159
-
160
- await fileService.removeAllFiles();
161
-
162
- // 验证用户的所有文件都被删除
163
- const remainingFiles = await clientDB.query.files.findMany({
164
- where: eq(files.userId, userId),
165
- });
166
-
167
- expect(remainingFiles).toHaveLength(0);
168
- });
169
- });
170
-
171
- describe('checkFileHash', () => {
172
- it('should return true if file hash exists', async () => {
173
- const hash = 'existing-hash';
174
- await clientDB.insert(globalFiles).values({
175
- ...mockFile,
176
- hashId: hash,
177
- creator: userId,
178
- });
179
- await clientDB.insert(files).values({
180
- id: '1',
181
- userId,
182
- ...mockFile,
183
- fileHash: hash,
184
- });
185
-
186
- const exists = await fileService.checkFileHash(hash);
187
-
188
- expect(exists).toMatchObject({ isExist: true });
189
- });
190
-
191
- it('should return false if file hash does not exist', async () => {
192
- const hash = 'non-existing-hash';
193
-
194
- const exists = await fileService.checkFileHash(hash);
195
-
196
- expect(exists).toEqual({ isExist: false });
197
- });
198
- });
199
- });
@@ -1,85 +0,0 @@
1
- import { clientDB } from '@/database/client/db';
2
- import { FileModel } from '@/database/models/file';
3
- import { BaseClientService } from '@/services/baseClientService';
4
- import { clientS3Storage } from '@/services/file/ClientS3';
5
-
6
- import { IFileService } from './type';
7
-
8
- export class ClientService extends BaseClientService implements IFileService {
9
- private get fileModel(): FileModel {
10
- return new FileModel(clientDB as any, this.userId);
11
- }
12
-
13
- createFile: IFileService['createFile'] = async (file) => {
14
- const { isExist } = await this.fileModel.checkHash(file.hash!);
15
-
16
- // save to local storage
17
- // we may want to save to a remote server later
18
- const res = await this.fileModel.create(
19
- {
20
- fileHash: file.hash,
21
- fileType: file.fileType,
22
- knowledgeBaseId: file.knowledgeBaseId,
23
- metadata: file.metadata,
24
- name: file.name,
25
- size: file.size,
26
- url: file.url!,
27
- },
28
- !isExist,
29
- );
30
-
31
- // get file to base64 url
32
- const base64 = await this.getBase64ByFileHash(file.hash!);
33
-
34
- return {
35
- id: res.id,
36
- url: `data:${file.fileType};base64,${base64}`,
37
- };
38
- };
39
-
40
- getFile: IFileService['getFile'] = async (id) => {
41
- const item = await this.fileModel.findById(id);
42
- if (!item) {
43
- throw new Error('file not found');
44
- }
45
-
46
- // arrayBuffer to url
47
- const fileItem = await clientS3Storage.getObject(item.fileHash!);
48
- if (!fileItem) throw new Error('file not found');
49
-
50
- const url = URL.createObjectURL(fileItem);
51
-
52
- return {
53
- createdAt: new Date(item.createdAt),
54
- id,
55
- name: item.name,
56
- size: item.size,
57
- type: item.fileType,
58
- updatedAt: new Date(item.updatedAt),
59
- url,
60
- };
61
- };
62
-
63
- removeFile: IFileService['removeFile'] = async (id) => {
64
- await this.fileModel.delete(id, false);
65
- };
66
-
67
- removeFiles: IFileService['removeFiles'] = async (ids) => {
68
- await this.fileModel.deleteMany(ids, false);
69
- };
70
-
71
- removeAllFiles: IFileService['removeAllFiles'] = async () => {
72
- return this.fileModel.clear();
73
- };
74
-
75
- checkFileHash: IFileService['checkFileHash'] = async (hash) => {
76
- return this.fileModel.checkHash(hash);
77
- };
78
-
79
- private getBase64ByFileHash = async (hash: string) => {
80
- const fileItem = await clientS3Storage.getObject(hash);
81
- if (!fileItem) throw new Error('file not found');
82
-
83
- return Buffer.from(await fileItem.arrayBuffer()).toString('base64');
84
- };
85
- }