@lobehub/chat 1.76.0 → 1.77.0

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 (135) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/Dockerfile +3 -2
  3. package/Dockerfile.database +3 -1
  4. package/Dockerfile.pglite +3 -1
  5. package/changelog/v1.json +18 -0
  6. package/locales/ar/common.json +12 -1
  7. package/locales/ar/error.json +10 -0
  8. package/locales/ar/models.json +12 -6
  9. package/locales/ar/setting.json +28 -0
  10. package/locales/bg-BG/common.json +12 -1
  11. package/locales/bg-BG/error.json +10 -0
  12. package/locales/bg-BG/models.json +12 -6
  13. package/locales/bg-BG/setting.json +28 -0
  14. package/locales/de-DE/common.json +12 -1
  15. package/locales/de-DE/error.json +10 -0
  16. package/locales/de-DE/models.json +12 -6
  17. package/locales/de-DE/setting.json +28 -0
  18. package/locales/en-US/common.json +12 -1
  19. package/locales/en-US/error.json +10 -0
  20. package/locales/en-US/models.json +12 -6
  21. package/locales/en-US/setting.json +28 -0
  22. package/locales/es-ES/common.json +12 -1
  23. package/locales/es-ES/error.json +10 -0
  24. package/locales/es-ES/models.json +12 -6
  25. package/locales/es-ES/setting.json +28 -0
  26. package/locales/fa-IR/common.json +12 -1
  27. package/locales/fa-IR/error.json +10 -0
  28. package/locales/fa-IR/models.json +12 -6
  29. package/locales/fa-IR/setting.json +28 -0
  30. package/locales/fr-FR/common.json +12 -1
  31. package/locales/fr-FR/error.json +10 -0
  32. package/locales/fr-FR/models.json +12 -6
  33. package/locales/fr-FR/setting.json +28 -0
  34. package/locales/it-IT/common.json +12 -1
  35. package/locales/it-IT/error.json +10 -0
  36. package/locales/it-IT/models.json +12 -6
  37. package/locales/it-IT/setting.json +28 -0
  38. package/locales/ja-JP/common.json +12 -1
  39. package/locales/ja-JP/error.json +10 -0
  40. package/locales/ja-JP/models.json +12 -6
  41. package/locales/ja-JP/setting.json +28 -0
  42. package/locales/ko-KR/common.json +12 -1
  43. package/locales/ko-KR/error.json +10 -0
  44. package/locales/ko-KR/models.json +12 -6
  45. package/locales/ko-KR/setting.json +28 -0
  46. package/locales/nl-NL/common.json +12 -1
  47. package/locales/nl-NL/error.json +10 -0
  48. package/locales/nl-NL/models.json +12 -6
  49. package/locales/nl-NL/setting.json +28 -0
  50. package/locales/pl-PL/common.json +12 -1
  51. package/locales/pl-PL/error.json +10 -0
  52. package/locales/pl-PL/models.json +12 -6
  53. package/locales/pl-PL/setting.json +28 -0
  54. package/locales/pt-BR/common.json +12 -1
  55. package/locales/pt-BR/error.json +10 -0
  56. package/locales/pt-BR/models.json +12 -6
  57. package/locales/pt-BR/setting.json +28 -0
  58. package/locales/ru-RU/common.json +12 -1
  59. package/locales/ru-RU/error.json +10 -0
  60. package/locales/ru-RU/models.json +12 -6
  61. package/locales/ru-RU/setting.json +28 -0
  62. package/locales/tr-TR/common.json +12 -1
  63. package/locales/tr-TR/error.json +10 -0
  64. package/locales/tr-TR/models.json +12 -6
  65. package/locales/tr-TR/setting.json +28 -0
  66. package/locales/vi-VN/common.json +12 -1
  67. package/locales/vi-VN/error.json +10 -0
  68. package/locales/vi-VN/models.json +12 -6
  69. package/locales/vi-VN/setting.json +28 -0
  70. package/locales/zh-CN/common.json +12 -1
  71. package/locales/zh-CN/error.json +10 -0
  72. package/locales/zh-CN/models.json +12 -6
  73. package/locales/zh-CN/setting.json +28 -0
  74. package/locales/zh-TW/common.json +12 -1
  75. package/locales/zh-TW/error.json +10 -0
  76. package/locales/zh-TW/models.json +12 -6
  77. package/locales/zh-TW/setting.json +28 -0
  78. package/package.json +1 -1
  79. package/src/app/[variants]/(main)/(mobile)/me/data/features/Category.tsx +1 -1
  80. package/src/app/[variants]/(main)/chat/features/Migration/UpgradeButton.tsx +2 -1
  81. package/src/app/[variants]/(main)/settings/common/features/Common.tsx +0 -44
  82. package/src/app/[variants]/(main)/settings/hooks/useCategory.tsx +40 -14
  83. package/src/app/[variants]/(main)/settings/storage/Advanced.tsx +133 -0
  84. package/src/app/[variants]/(main)/settings/storage/IndexedDBStorage.tsx +55 -0
  85. package/src/app/[variants]/(main)/settings/storage/page.tsx +17 -0
  86. package/src/app/[variants]/(main)/settings/tts/features/const.tsx +4 -0
  87. package/src/components/GroupIcon/index.tsx +25 -0
  88. package/src/components/IndexCard/index.tsx +143 -0
  89. package/src/components/ProgressItem/index.tsx +75 -0
  90. package/src/config/aiModels/openai.ts +10 -0
  91. package/src/database/repositories/dataExporter/index.test.ts +330 -0
  92. package/src/database/repositories/dataExporter/index.ts +216 -0
  93. package/src/database/repositories/dataImporter/__tests__/fixtures/agents.json +65 -0
  94. package/src/database/repositories/dataImporter/__tests__/fixtures/agentsToSessions.json +541 -0
  95. package/src/database/repositories/dataImporter/__tests__/fixtures/topic.json +269 -0
  96. package/src/database/repositories/dataImporter/__tests__/fixtures/userSettings.json +18 -0
  97. package/src/database/repositories/dataImporter/__tests__/fixtures/with-client-id.json +778 -0
  98. package/src/database/repositories/dataImporter/__tests__/index.test.ts +120 -880
  99. package/src/database/repositories/dataImporter/deprecated/__tests__/index.test.ts +940 -0
  100. package/src/database/repositories/dataImporter/deprecated/index.ts +326 -0
  101. package/src/database/repositories/dataImporter/index.ts +684 -289
  102. package/src/features/DataImporter/ImportDetail.tsx +203 -0
  103. package/src/features/DataImporter/SuccessResult.tsx +22 -6
  104. package/src/features/DataImporter/_deprecated.ts +43 -0
  105. package/src/features/DataImporter/config.ts +21 -0
  106. package/src/features/DataImporter/index.tsx +112 -31
  107. package/src/features/DevPanel/PostgresViewer/DataTable/index.tsx +6 -0
  108. package/src/features/User/UserPanel/useMenu.tsx +0 -35
  109. package/src/features/User/__tests__/useMenu.test.tsx +0 -2
  110. package/src/locales/default/common.ts +11 -0
  111. package/src/locales/default/error.ts +10 -0
  112. package/src/locales/default/setting.ts +28 -0
  113. package/src/server/routers/lambda/exporter.ts +25 -0
  114. package/src/server/routers/lambda/importer.ts +19 -3
  115. package/src/server/routers/lambda/index.ts +2 -0
  116. package/src/services/config.ts +80 -135
  117. package/src/services/export/_deprecated.ts +155 -0
  118. package/src/services/export/client.ts +15 -0
  119. package/src/services/export/index.ts +6 -0
  120. package/src/services/export/server.ts +9 -0
  121. package/src/services/export/type.ts +5 -0
  122. package/src/services/import/_deprecated.ts +42 -1
  123. package/src/services/import/client.test.ts +1 -1
  124. package/src/services/import/client.ts +30 -1
  125. package/src/services/import/server.ts +70 -2
  126. package/src/services/import/type.ts +10 -0
  127. package/src/store/global/initialState.ts +1 -0
  128. package/src/types/export.ts +11 -0
  129. package/src/types/exportConfig.ts +2 -0
  130. package/src/types/importer.ts +15 -0
  131. package/src/types/user/settings/tts.ts +1 -1
  132. package/src/utils/client/exportFile.ts +21 -0
  133. package/vitest.config.ts +1 -1
  134. package/src/utils/config.ts +0 -109
  135. /package/src/database/repositories/dataImporter/{__tests__ → deprecated/__tests__}/fixtures/messages.json +0 -0
@@ -348,6 +348,33 @@ export default {
348
348
  },
349
349
  title: '主题设置',
350
350
  },
351
+ storage: {
352
+ actions: {
353
+ export: {
354
+ button: '导出',
355
+ exportType: {
356
+ agent: '导出助手设定',
357
+ agentWithMessage: '导出助手和消息',
358
+ all: '导出全局设置和所有助手数据',
359
+ allAgent: '导出所有助手设定',
360
+ allAgentWithMessage: '导出所有助手和消息',
361
+ globalSetting: '导出全局设置',
362
+ },
363
+ title: '导出数据',
364
+ },
365
+ import: {
366
+ button: '导入',
367
+ title: '导入数据',
368
+ },
369
+ title: '高级操作',
370
+ },
371
+ desc: '当前浏览器中的存储用量',
372
+ embeddings: {
373
+ used: '向量存储',
374
+ },
375
+ title: '数据存储',
376
+ used: '存储用量',
377
+ },
351
378
  submitAgentModal: {
352
379
  button: '提交助手',
353
380
  identifier: 'identifier 助手标识符',
@@ -441,6 +468,7 @@ export default {
441
468
  'hotkey': '快捷键',
442
469
  'llm': '语言模型',
443
470
  'provider': 'AI 服务商',
471
+ 'storage': '数据存储',
444
472
  'sync': '云端同步',
445
473
  'system-agent': '系统助手',
446
474
  'tts': '语音服务',
@@ -0,0 +1,25 @@
1
+ import { DrizzleMigrationModel } from '@/database/models/drizzleMigration';
2
+ import { DataExporterRepos } from '@/database/repositories/dataExporter';
3
+ import { serverDB } from '@/database/server';
4
+ import { authedProcedure, router } from '@/libs/trpc';
5
+ import { ExportDatabaseData } from '@/types/export';
6
+
7
+ const exportProcedure = authedProcedure.use(async (opts) => {
8
+ const { ctx } = opts;
9
+ const dataExporterRepos = new DataExporterRepos(serverDB, ctx.userId);
10
+ const drizzleMigration = new DrizzleMigrationModel(serverDB);
11
+
12
+ return opts.next({
13
+ ctx: { dataExporterRepos, drizzleMigration },
14
+ });
15
+ });
16
+
17
+ export const exporterRouter = router({
18
+ exportData: exportProcedure.mutation(async ({ ctx }): Promise<ExportDatabaseData> => {
19
+ const data = await ctx.dataExporterRepos.export(5);
20
+
21
+ const schemaHash = await ctx.drizzleMigration.getLatestMigrationHash();
22
+
23
+ return { data, schemaHash };
24
+ }),
25
+ });
@@ -5,7 +5,8 @@ import { DataImporterRepos } from '@/database/repositories/dataImporter';
5
5
  import { serverDB } from '@/database/server';
6
6
  import { authedProcedure, router } from '@/libs/trpc';
7
7
  import { S3 } from '@/server/modules/S3';
8
- import { ImportResults, ImporterEntryData } from '@/types/importer';
8
+ import { ImportPgDataStructure } from '@/types/export';
9
+ import { ImportResultData, ImporterEntryData } from '@/types/importer';
9
10
 
10
11
  const importProcedure = authedProcedure.use(async (opts) => {
11
12
  const { ctx } = opts;
@@ -19,7 +20,7 @@ const importProcedure = authedProcedure.use(async (opts) => {
19
20
  export const importerRouter = router({
20
21
  importByFile: importProcedure
21
22
  .input(z.object({ pathname: z.string() }))
22
- .mutation(async ({ input, ctx }): Promise<ImportResults> => {
23
+ .mutation(async ({ input, ctx }): Promise<ImportResultData> => {
23
24
  let data: ImporterEntryData | undefined;
24
25
 
25
26
  try {
@@ -37,6 +38,10 @@ export const importerRouter = router({
37
38
  });
38
39
  }
39
40
 
41
+ if ('schemaHash' in data) {
42
+ return ctx.dataImporterService.importPgData(data as unknown as ImportPgDataStructure);
43
+ }
44
+
40
45
  return ctx.dataImporterService.importData(data);
41
46
  }),
42
47
 
@@ -52,7 +57,18 @@ export const importerRouter = router({
52
57
  }),
53
58
  }),
54
59
  )
55
- .mutation(async ({ input, ctx }): Promise<ImportResults> => {
60
+ .mutation(async ({ input, ctx }): Promise<ImportResultData> => {
56
61
  return ctx.dataImporterService.importData(input.data);
57
62
  }),
63
+ importPgByPost: importProcedure
64
+ .input(
65
+ z.object({
66
+ data: z.record(z.string(), z.array(z.any())),
67
+ mode: z.enum(['pglite', 'postgres']),
68
+ schemaHash: z.string(),
69
+ }),
70
+ )
71
+ .mutation(async ({ input, ctx }): Promise<ImportResultData> => {
72
+ return ctx.dataImporterService.importPgData(input);
73
+ }),
58
74
  });
@@ -7,6 +7,7 @@ import { agentRouter } from './agent';
7
7
  import { aiModelRouter } from './aiModel';
8
8
  import { aiProviderRouter } from './aiProvider';
9
9
  import { chunkRouter } from './chunk';
10
+ import { exporterRouter } from './exporter';
10
11
  import { fileRouter } from './file';
11
12
  import { importerRouter } from './importer';
12
13
  import { knowledgeBaseRouter } from './knowledgeBase';
@@ -24,6 +25,7 @@ export const lambdaRouter = router({
24
25
  aiModel: aiModelRouter,
25
26
  aiProvider: aiProviderRouter,
26
27
  chunk: chunkRouter,
28
+ exporter: exporterRouter,
27
29
  file: fileRouter,
28
30
  healthcheck: publicProcedure.query(() => "i'm live!"),
29
31
  importer: importerRouter,
@@ -1,162 +1,107 @@
1
- import { importService } from '@/services/import';
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 { ConfigFile } from '@/types/exportConfig';
10
- import { ImportStage, OnImportCallbacks } from '@/types/importer';
11
- import { createConfigFile, exportConfigFile } from '@/utils/config';
12
-
13
- export interface ImportResult {
14
- added: number;
15
- errors: number;
16
- skips: number;
17
- }
18
- export interface ImportResults {
19
- messages?: ImportResult;
20
- sessionGroups?: ImportResult;
21
- sessions?: ImportResult;
22
- topics?: ImportResult;
23
- type?: string;
24
- }
1
+ import dayjs from 'dayjs';
2
+
3
+ import { BRANDING_NAME } from '@/const/branding';
4
+ import { isDeprecatedEdition, isServerMode } from '@/const/version';
5
+ import { CURRENT_CONFIG_VERSION } from '@/migrations';
6
+ import { ImportPgDataStructure } from '@/types/export';
7
+ import { downloadFile } from '@/utils/client/downloadFile';
8
+ import { exportJSONFile } from '@/utils/client/exportFile';
9
+
10
+ import { exportService } from './export';
11
+ import { configService as deprecatedExportService } from './export/_deprecated';
25
12
 
26
- /**
27
- * @deprecated
28
- */
29
13
  class ConfigService {
30
- importConfigState = async (config: ConfigFile, callbacks?: OnImportCallbacks): Promise<void> => {
31
- if (config.exportType === 'settings') {
32
- await importService.importSettings(config.state.settings);
33
- callbacks?.onStageChange?.(ImportStage.Success);
14
+ exportAll = async () => {
15
+ // TODO: remove this in V2
16
+ if (isDeprecatedEdition) {
17
+ const config = await deprecatedExportService.exportAll();
18
+ const filename = `${BRANDING_NAME}-config-v${CURRENT_CONFIG_VERSION}.json`;
19
+ exportJSONFile(config, filename);
34
20
  return;
35
21
  }
36
22
 
37
- if (config.exportType === 'all') {
38
- await importService.importSettings(config.state.settings);
39
- }
40
-
41
- await importService.importData(
42
- {
43
- messages: (config.state as any).messages || [],
44
- sessionGroups: (config.state as any).sessionGroups || [],
45
- sessions: (config.state as any).sessions || [],
46
- topics: (config.state as any).topics || [],
47
- version: config.version,
48
- },
49
- callbacks,
50
- );
51
- };
52
-
53
- // TODO: Separate export feature into a new service like importService
54
-
55
- /**
56
- * export all agents
57
- */
58
- exportAgents = async () => {
59
- const agents = await sessionService.getSessionsByType('agent');
60
- const sessionGroups = await sessionService.getSessionGroups();
61
-
62
- const config = createConfigFile('agents', { sessionGroups, sessions: agents });
63
-
64
- exportConfigFile(config, 'agents');
65
- };
23
+ const { data, url } = await exportService.exportData();
24
+ const filename = `${dayjs().format('YYYY-MM-DD-hh-mm')}_${BRANDING_NAME}-data.json`;
66
25
 
67
- /**
68
- * export all sessions
69
- */
70
- exportSessions = async () => {
71
- const sessions = await sessionService.getSessionsByType();
72
- const sessionGroups = await sessionService.getSessionGroups();
73
- const messages = await messageService.getAllMessages();
74
- const topics = await topicService.getAllTopics();
26
+ // if url exists, means export data from server and upload the data to S3
27
+ // just need to download the file
28
+ if (url) {
29
+ await downloadFile(url, filename);
30
+ return;
31
+ }
75
32
 
76
- const config = createConfigFile('sessions', { messages, sessionGroups, sessions, topics });
33
+ // or export to file with the data
34
+ const result = await this.createDataStructure(data, isServerMode ? 'postgres' : 'pglite');
77
35
 
78
- exportConfigFile(config, 'sessions');
36
+ exportJSONFile(result, filename);
79
37
  };
80
38
 
81
- /**
82
- * export a session
83
- */
84
- exportSingleSession = async (id: string) => {
85
- const session = this.getSession(id);
86
- if (!session) return;
87
-
88
- const messages = await messageService.getAllMessagesInSession(id);
89
- const topics = await topicService.getTopics({ sessionId: id });
90
-
91
- const config = createConfigFile('singleSession', { messages, sessions: [session], topics });
92
-
93
- exportConfigFile(config, `${session.meta?.title}-session`);
39
+ exportAgents = async () => {
40
+ // TODO: remove this in V2
41
+ if (isDeprecatedEdition) {
42
+ const config = await deprecatedExportService.exportAgents();
43
+ const filename = `${BRANDING_NAME}-agents-v${CURRENT_CONFIG_VERSION}.json`;
44
+ exportJSONFile(config, filename);
45
+ return;
46
+ }
94
47
  };
95
48
 
96
- /**
97
- * export a topic
98
- */
99
- exportSingleTopic = async (sessionId: string, topicId: string) => {
100
- const session = this.getSession(sessionId);
101
- if (!session) return;
102
-
103
- const messages = await messageService.getMessages(sessionId, topicId);
104
- const topics = await topicService.getTopics({ sessionId });
105
-
106
- const topic = topics.find((item) => item.id === topicId);
107
- if (!topic) return;
49
+ exportSingleAgent = async (agentId: string) => {
50
+ // TODO: remove this in V2
51
+ if (isDeprecatedEdition) {
52
+ const result = await deprecatedExportService.exportSingleAgent(agentId);
53
+ if (!result) return;
108
54
 
109
- const config = createConfigFile('singleSession', {
110
- messages,
111
- sessions: [session],
112
- topics: [topic],
113
- });
114
-
115
- exportConfigFile(config, `${topic.title}-topic`);
55
+ const filename = `${BRANDING_NAME}-${result.title}-v${CURRENT_CONFIG_VERSION}.json`;
56
+ exportJSONFile(result.config, filename);
57
+ return;
58
+ }
116
59
  };
117
60
 
118
- exportSingleAgent = async (id: string) => {
119
- const agent = this.getAgent(id);
120
- if (!agent) return;
121
-
122
- const config = createConfigFile('agents', { sessionGroups: [], sessions: [agent] });
123
-
124
- exportConfigFile(config, agent.meta?.title || 'agent');
61
+ exportSessions = async () => {
62
+ // TODO: remove this in V2
63
+ if (isDeprecatedEdition) {
64
+ const config = await deprecatedExportService.exportSessions();
65
+ const filename = `${BRANDING_NAME}-sessions-v${CURRENT_CONFIG_VERSION}.json`;
66
+ exportJSONFile(config, filename);
67
+ return;
68
+ }
125
69
  };
126
70
 
127
- /**
128
- * export settings
129
- */
130
71
  exportSettings = async () => {
131
- const settings = this.getSettings();
132
-
133
- const config = createConfigFile('settings', { settings });
134
-
135
- exportConfigFile(config, 'settings');
72
+ // TODO: remove this in V2
73
+ if (isDeprecatedEdition) {
74
+ const config = await deprecatedExportService.exportSessions();
75
+ const filename = `${BRANDING_NAME}-settings-v${CURRENT_CONFIG_VERSION}.json`;
76
+ exportJSONFile(config, filename);
77
+ return;
78
+ }
136
79
  };
137
80
 
138
- /**
139
- * export all data
140
- */
141
- exportAll = async () => {
142
- const sessions = await sessionService.getSessionsByType();
143
- const sessionGroups = await sessionService.getSessionGroups();
144
- const messages = await messageService.getAllMessages();
145
- const topics = await topicService.getAllTopics();
146
- const settings = this.getSettings();
147
-
148
- const config = createConfigFile('all', { messages, sessionGroups, sessions, settings, topics });
81
+ exportSingleSession = async (sessionId: string) => {
82
+ // TODO: remove this in V2
83
+ if (isDeprecatedEdition) {
84
+ const data = await deprecatedExportService.exportSingleSession(sessionId);
85
+ if (!data) return;
149
86
 
150
- exportConfigFile(config, 'config');
87
+ const filename = `${BRANDING_NAME}-${data.title}-v${CURRENT_CONFIG_VERSION}.json`;
88
+ exportJSONFile(data.config, filename);
89
+ return;
90
+ }
151
91
  };
152
92
 
153
- private getSettings = () => settingsSelectors.exportSettings(useUserStore.getState());
154
-
155
- private getSession = (id: string) =>
156
- sessionSelectors.getSessionById(id)(useSessionStore.getState());
93
+ private createDataStructure = async (
94
+ data: any,
95
+ mode: 'pglite' | 'postgres',
96
+ ): Promise<ImportPgDataStructure> => {
97
+ const { default: json } = await import('@/database/client/migrations.json');
98
+ const latestHash = json.at(-1)?.hash;
99
+ if (!latestHash) {
100
+ throw new Error('Not find database sql hash');
101
+ }
157
102
 
158
- private getAgent = (id: string) =>
159
- sessionSelectors.getSessionById(id)(useSessionStore.getState());
103
+ return { data, mode, schemaHash: latestHash };
104
+ };
160
105
  }
161
106
 
162
107
  export const configService = new ConfigService();
@@ -0,0 +1,155 @@
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({ sessionId: 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();
@@ -0,0 +1,15 @@
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
+ }
@@ -0,0 +1,6 @@
1
+ import { isServerMode } from '@/const/version';
2
+
3
+ import { ClientService } from './client';
4
+ import { ServerService } from './server';
5
+
6
+ export const exportService = isServerMode ? new ServerService() : new ClientService();
@@ -0,0 +1,9 @@
1
+ import { lambdaClient } from '@/libs/trpc/client';
2
+
3
+ import { IExportService } from './type';
4
+
5
+ export class ServerService implements IExportService {
6
+ exportData: IExportService['exportData'] = async () => {
7
+ return await lambdaClient.exporter.exportData.mutate();
8
+ };
9
+ }
@@ -0,0 +1,5 @@
1
+ import { ExportDatabaseData } from '@/types/export';
2
+
3
+ export interface IExportService {
4
+ exportData(): Promise<ExportDatabaseData>;
5
+ }
@@ -2,11 +2,25 @@ import { MessageModel } from '@/database/_deprecated/models/message';
2
2
  import { SessionModel } from '@/database/_deprecated/models/session';
3
3
  import { SessionGroupModel } from '@/database/_deprecated/models/sessionGroup';
4
4
  import { TopicModel } from '@/database/_deprecated/models/topic';
5
- import { ImportResult, ImportResults } from '@/services/config';
6
5
  import { useUserStore } from '@/store/user';
6
+ import { ConfigFile } from '@/types/exportConfig';
7
7
  import { ImportStage, ImporterEntryData, OnImportCallbacks } from '@/types/importer';
8
8
  import { UserSettings } from '@/types/user/settings';
9
9
 
10
+ export interface ImportResult {
11
+ added: number;
12
+ errors: number;
13
+ skips: number;
14
+ updated?: number;
15
+ }
16
+ export interface ImportResults {
17
+ messages?: ImportResult;
18
+ sessionGroups?: ImportResult;
19
+ sessions?: ImportResult;
20
+ topics?: ImportResult;
21
+ type?: string;
22
+ }
23
+
10
24
  export class ClientService {
11
25
  importSettings = async (settings: UserSettings) => {
12
26
  await useUserStore.getState().importAppSettings(settings);
@@ -71,4 +85,31 @@ export class ClientService {
71
85
  skips: input.skips.length,
72
86
  };
73
87
  };
88
+
89
+ importConfigState = async (config: ConfigFile, callbacks?: OnImportCallbacks): Promise<void> => {
90
+ if (config.exportType === 'settings') {
91
+ await this.importSettings(config.state.settings);
92
+ callbacks?.onStageChange?.(ImportStage.Success);
93
+ return;
94
+ }
95
+
96
+ if (config.exportType === 'all') {
97
+ await this.importSettings(config.state.settings);
98
+ }
99
+
100
+ await this.importData(
101
+ {
102
+ messages: (config.state as any).messages || [],
103
+ sessionGroups: (config.state as any).sessionGroups || [],
104
+ sessions: (config.state as any).sessions || [],
105
+ topics: (config.state as any).topics || [],
106
+ version: config.version,
107
+ },
108
+ callbacks,
109
+ );
110
+ };
111
+
112
+ importPgData = async () => {
113
+ throw new Error('Not implemented');
114
+ };
74
115
  }
@@ -2,7 +2,7 @@ import { eq, inArray } from 'drizzle-orm';
2
2
  import { beforeEach, describe, expect, it, vi } from 'vitest';
3
3
 
4
4
  import { clientDB, initializeDB } from '@/database/client/db';
5
- import mockImportData from '@/database/repositories/dataImporter/__tests__/fixtures/messages.json';
5
+ import mockImportData from '@/database/repositories/dataImporter/deprecated/__tests__/fixtures/messages.json';
6
6
  import {
7
7
  agents,
8
8
  agentsToSessions,