@lobehub/chat 1.76.1 → 1.77.1

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 (131) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/changelog/v1.json +18 -0
  3. package/locales/ar/common.json +13 -2
  4. package/locales/ar/error.json +10 -0
  5. package/locales/ar/models.json +9 -6
  6. package/locales/ar/setting.json +28 -0
  7. package/locales/bg-BG/common.json +13 -2
  8. package/locales/bg-BG/error.json +10 -0
  9. package/locales/bg-BG/models.json +9 -6
  10. package/locales/bg-BG/setting.json +28 -0
  11. package/locales/de-DE/common.json +13 -2
  12. package/locales/de-DE/error.json +10 -0
  13. package/locales/de-DE/models.json +9 -6
  14. package/locales/de-DE/setting.json +28 -0
  15. package/locales/en-US/common.json +13 -2
  16. package/locales/en-US/error.json +10 -0
  17. package/locales/en-US/models.json +9 -6
  18. package/locales/en-US/setting.json +28 -0
  19. package/locales/es-ES/common.json +13 -2
  20. package/locales/es-ES/error.json +10 -0
  21. package/locales/es-ES/models.json +9 -6
  22. package/locales/es-ES/setting.json +28 -0
  23. package/locales/fa-IR/common.json +13 -2
  24. package/locales/fa-IR/error.json +10 -0
  25. package/locales/fa-IR/models.json +9 -6
  26. package/locales/fa-IR/setting.json +28 -0
  27. package/locales/fr-FR/common.json +13 -2
  28. package/locales/fr-FR/error.json +10 -0
  29. package/locales/fr-FR/models.json +9 -6
  30. package/locales/fr-FR/setting.json +28 -0
  31. package/locales/it-IT/common.json +13 -2
  32. package/locales/it-IT/error.json +10 -0
  33. package/locales/it-IT/models.json +9 -6
  34. package/locales/it-IT/setting.json +28 -0
  35. package/locales/ja-JP/common.json +13 -2
  36. package/locales/ja-JP/error.json +10 -0
  37. package/locales/ja-JP/models.json +9 -6
  38. package/locales/ja-JP/setting.json +28 -0
  39. package/locales/ko-KR/common.json +13 -2
  40. package/locales/ko-KR/error.json +10 -0
  41. package/locales/ko-KR/models.json +9 -6
  42. package/locales/ko-KR/setting.json +28 -0
  43. package/locales/nl-NL/common.json +13 -2
  44. package/locales/nl-NL/error.json +10 -0
  45. package/locales/nl-NL/models.json +9 -6
  46. package/locales/nl-NL/setting.json +28 -0
  47. package/locales/pl-PL/common.json +13 -2
  48. package/locales/pl-PL/error.json +10 -0
  49. package/locales/pl-PL/models.json +9 -6
  50. package/locales/pl-PL/setting.json +28 -0
  51. package/locales/pt-BR/common.json +13 -2
  52. package/locales/pt-BR/error.json +10 -0
  53. package/locales/pt-BR/models.json +9 -6
  54. package/locales/pt-BR/setting.json +28 -0
  55. package/locales/ru-RU/common.json +13 -2
  56. package/locales/ru-RU/error.json +10 -0
  57. package/locales/ru-RU/models.json +9 -6
  58. package/locales/ru-RU/setting.json +28 -0
  59. package/locales/tr-TR/common.json +13 -2
  60. package/locales/tr-TR/error.json +10 -0
  61. package/locales/tr-TR/models.json +9 -6
  62. package/locales/tr-TR/setting.json +28 -0
  63. package/locales/vi-VN/common.json +13 -2
  64. package/locales/vi-VN/error.json +10 -0
  65. package/locales/vi-VN/models.json +9 -6
  66. package/locales/vi-VN/setting.json +28 -0
  67. package/locales/zh-CN/common.json +13 -2
  68. package/locales/zh-CN/error.json +10 -0
  69. package/locales/zh-CN/models.json +10 -7
  70. package/locales/zh-CN/setting.json +28 -0
  71. package/locales/zh-TW/common.json +13 -2
  72. package/locales/zh-TW/error.json +10 -0
  73. package/locales/zh-TW/models.json +9 -6
  74. package/locales/zh-TW/setting.json +28 -0
  75. package/package.json +1 -1
  76. package/src/app/[variants]/(main)/(mobile)/me/data/features/Category.tsx +2 -2
  77. package/src/app/[variants]/(main)/chat/features/Migration/UpgradeButton.tsx +2 -1
  78. package/src/app/[variants]/(main)/settings/common/features/Common.tsx +0 -44
  79. package/src/app/[variants]/(main)/settings/hooks/useCategory.tsx +40 -14
  80. package/src/app/[variants]/(main)/settings/storage/Advanced.tsx +108 -0
  81. package/src/app/[variants]/(main)/settings/storage/IndexedDBStorage.tsx +55 -0
  82. package/src/app/[variants]/(main)/settings/storage/page.tsx +17 -0
  83. package/src/components/GroupIcon/index.tsx +25 -0
  84. package/src/components/IndexCard/index.tsx +143 -0
  85. package/src/components/ProgressItem/index.tsx +75 -0
  86. package/src/database/models/__tests__/session.test.ts +21 -0
  87. package/src/database/repositories/dataExporter/index.test.ts +330 -0
  88. package/src/database/repositories/dataExporter/index.ts +216 -0
  89. package/src/database/repositories/dataImporter/__tests__/fixtures/agents.json +65 -0
  90. package/src/database/repositories/dataImporter/__tests__/fixtures/agentsToSessions.json +541 -0
  91. package/src/database/repositories/dataImporter/__tests__/fixtures/topic.json +269 -0
  92. package/src/database/repositories/dataImporter/__tests__/fixtures/userSettings.json +18 -0
  93. package/src/database/repositories/dataImporter/__tests__/fixtures/with-client-id.json +778 -0
  94. package/src/database/repositories/dataImporter/__tests__/index.test.ts +120 -880
  95. package/src/database/repositories/dataImporter/deprecated/__tests__/index.test.ts +940 -0
  96. package/src/database/repositories/dataImporter/deprecated/index.ts +326 -0
  97. package/src/database/repositories/dataImporter/index.ts +684 -289
  98. package/src/database/server/models/session.ts +85 -9
  99. package/src/features/DataImporter/ImportDetail.tsx +203 -0
  100. package/src/features/DataImporter/SuccessResult.tsx +22 -6
  101. package/src/features/DataImporter/_deprecated.ts +43 -0
  102. package/src/features/DataImporter/config.ts +21 -0
  103. package/src/features/DataImporter/index.tsx +112 -31
  104. package/src/features/DevPanel/PostgresViewer/DataTable/index.tsx +6 -0
  105. package/src/features/User/UserPanel/useMenu.tsx +1 -36
  106. package/src/features/User/__tests__/useMenu.test.tsx +0 -2
  107. package/src/locales/default/common.ts +12 -1
  108. package/src/locales/default/error.ts +10 -0
  109. package/src/locales/default/setting.ts +28 -0
  110. package/src/server/routers/lambda/exporter.ts +25 -0
  111. package/src/server/routers/lambda/importer.ts +19 -3
  112. package/src/server/routers/lambda/index.ts +2 -0
  113. package/src/services/config.ts +80 -135
  114. package/src/services/export/_deprecated.ts +155 -0
  115. package/src/services/export/client.ts +15 -0
  116. package/src/services/export/index.ts +6 -0
  117. package/src/services/export/server.ts +9 -0
  118. package/src/services/export/type.ts +5 -0
  119. package/src/services/import/_deprecated.ts +42 -1
  120. package/src/services/import/client.test.ts +1 -1
  121. package/src/services/import/client.ts +30 -1
  122. package/src/services/import/server.ts +70 -2
  123. package/src/services/import/type.ts +10 -0
  124. package/src/store/global/initialState.ts +1 -0
  125. package/src/types/export.ts +11 -0
  126. package/src/types/exportConfig.ts +2 -0
  127. package/src/types/importer.ts +15 -0
  128. package/src/utils/client/exportFile.ts +21 -0
  129. package/vitest.config.ts +1 -1
  130. package/src/utils/config.ts +0 -109
  131. /package/src/database/repositories/dataImporter/{__tests__ → deprecated/__tests__}/fixtures/messages.json +0 -0
@@ -9,7 +9,6 @@ import {
9
9
  Feather,
10
10
  FileClockIcon,
11
11
  HardDriveDownload,
12
- HardDriveUpload,
13
12
  LifeBuoy,
14
13
  LogOut,
15
14
  Mail,
@@ -32,10 +31,8 @@ import {
32
31
  UTM_SOURCE,
33
32
  mailTo,
34
33
  } from '@/const/url';
35
- import { isServerMode } from '@/const/version';
36
34
  import DataImporter from '@/features/DataImporter';
37
35
  import { usePWAInstall } from '@/hooks/usePWAInstall';
38
- import { configService } from '@/services/config';
39
36
  import { featureFlagsSelectors, useServerConfigStore } from '@/store/serverConfig';
40
37
  import { useUserStore } from '@/store/user';
41
38
  import { authSelectors } from '@/store/user/selectors';
@@ -119,40 +116,8 @@ export const useMenu = () => {
119
116
  {
120
117
  icon: <Icon icon={HardDriveDownload} />,
121
118
  key: 'import',
122
- label: <DataImporter>{t('import')}</DataImporter>,
119
+ label: <DataImporter>{t('importData')}</DataImporter>,
123
120
  },
124
- isServerMode
125
- ? null
126
- : {
127
- children: [
128
- {
129
- key: 'allAgent',
130
- label: t('exportType.allAgent'),
131
- onClick: configService.exportAgents,
132
- },
133
- {
134
- key: 'allAgentWithMessage',
135
- label: t('exportType.allAgentWithMessage'),
136
- onClick: configService.exportSessions,
137
- },
138
- {
139
- key: 'globalSetting',
140
- label: t('exportType.globalSetting'),
141
- onClick: configService.exportSettings,
142
- },
143
- {
144
- type: 'divider',
145
- },
146
- {
147
- key: 'all',
148
- label: t('exportType.all'),
149
- onClick: configService.exportAll,
150
- },
151
- ],
152
- icon: <Icon icon={HardDriveUpload} />,
153
- key: 'export',
154
- label: t('export'),
155
- },
156
121
  {
157
122
  type: 'divider',
158
123
  },
@@ -81,7 +81,6 @@ describe('useMenu', () => {
81
81
  expect(mainItems?.some((item) => item?.key === 'profile')).toBe(true);
82
82
  expect(mainItems?.some((item) => item?.key === 'setting')).toBe(true);
83
83
  expect(mainItems?.some((item) => item?.key === 'import')).toBe(true);
84
- expect(mainItems?.some((item) => item?.key === 'export')).toBe(true);
85
84
  expect(mainItems?.some((item) => item?.key === 'changelog')).toBe(true);
86
85
  expect(logoutItems.some((item) => item?.key === 'logout')).toBe(true);
87
86
  });
@@ -100,7 +99,6 @@ describe('useMenu', () => {
100
99
  expect(mainItems?.some((item) => item?.key === 'profile')).toBe(true);
101
100
  expect(mainItems?.some((item) => item?.key === 'setting')).toBe(true);
102
101
  expect(mainItems?.some((item) => item?.key === 'import')).toBe(true);
103
- expect(mainItems?.some((item) => item?.key === 'export')).toBe(true);
104
102
  expect(mainItems?.some((item) => item?.key === 'changelog')).toBe(true);
105
103
  expect(logoutItems.some((item) => item?.key === 'logout')).toBe(false);
106
104
  });
@@ -186,7 +186,7 @@ export default {
186
186
  },
187
187
  fullscreen: '全屏模式',
188
188
  historyRange: '历史范围',
189
- import: '导入配置',
189
+ importData: '导入数据',
190
190
  importModal: {
191
191
  error: {
192
192
  desc: '非常抱歉,数据导入过程发生异常。请尝试重新导入,或 <1>提交问题</1>,我们将会第一时间帮你排查问题。',
@@ -209,6 +209,7 @@ export default {
209
209
  skips: '重复跳过',
210
210
  topics: '话题',
211
211
  type: '数据类型',
212
+ update: '记录更新',
212
213
  },
213
214
  title: '导入数据',
214
215
  uploading: {
@@ -217,6 +218,16 @@ export default {
217
218
  speed: '上传速度',
218
219
  },
219
220
  },
221
+ importPreview: {
222
+ confirmImport: '确认导入',
223
+ tables: {
224
+ count: '记录数',
225
+ name: '表名',
226
+ },
227
+ title: '导入数据预览',
228
+ totalRecords: '总计将导入 {{count}} 条记录',
229
+ totalTables: '{{count}} 个表',
230
+ },
220
231
  information: '社区与资讯',
221
232
  installPWA: '安装浏览器应用 (PWA)',
222
233
  lang: {
@@ -16,6 +16,16 @@ export default {
16
16
  detail: '错误详情',
17
17
  title: '请求失败',
18
18
  },
19
+ import: {
20
+ importConfigFile: {
21
+ description: '出错原因: {{reason}}',
22
+ title: '导入失败',
23
+ },
24
+ incompatible: {
25
+ description: '该文件由更高版本导出,请尝试升级到最新版本后再重新导入',
26
+ title: '当前应用不支持导入该文件',
27
+ },
28
+ },
19
29
  loginRequired: {
20
30
  desc: '即将自动跳转到登录页面',
21
31
  title: '请登录后使用该功能',
@@ -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
+ }