@lobehub/chat 1.36.46 → 1.37.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 (78) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/README.ja-JP.md +8 -8
  3. package/README.md +8 -8
  4. package/README.zh-CN.md +8 -8
  5. package/changelog/v1.json +9 -0
  6. package/next.config.mjs +4 -1
  7. package/package.json +5 -3
  8. package/scripts/migrateClientDB/compile-migrations.ts +14 -0
  9. package/src/app/(main)/(mobile)/me/(home)/layout.tsx +2 -0
  10. package/src/app/(main)/chat/_layout/Desktop/index.tsx +3 -2
  11. package/src/app/(main)/chat/_layout/Mobile.tsx +5 -3
  12. package/src/app/(main)/chat/features/Migration/DBReader.ts +290 -0
  13. package/src/app/(main)/chat/features/Migration/UpgradeButton.tsx +4 -8
  14. package/src/app/(main)/chat/features/Migration/index.tsx +26 -15
  15. package/src/app/(main)/settings/_layout/Desktop/index.tsx +2 -0
  16. package/src/app/loading/Client/Content.tsx +11 -1
  17. package/src/app/loading/Client/Error.tsx +27 -0
  18. package/src/app/loading/stage.ts +8 -0
  19. package/src/components/FullscreenLoading/index.tsx +4 -3
  20. package/src/const/version.ts +1 -0
  21. package/src/database/client/db.test.ts +172 -0
  22. package/src/database/client/db.ts +246 -0
  23. package/src/database/client/migrations.json +289 -0
  24. package/src/features/InitClientDB/EnableModal.tsx +111 -0
  25. package/src/features/InitClientDB/ErrorResult.tsx +125 -0
  26. package/src/features/InitClientDB/InitIndicator.tsx +124 -0
  27. package/src/features/InitClientDB/PGliteSVG.tsx +22 -0
  28. package/src/features/InitClientDB/index.tsx +37 -0
  29. package/src/hooks/useCheckPluginsIsInstalled.ts +2 -2
  30. package/src/hooks/useFetchInstalledPlugins.ts +2 -2
  31. package/src/hooks/useFetchMessages.ts +2 -2
  32. package/src/hooks/useFetchSessions.ts +2 -2
  33. package/src/hooks/useFetchThreads.ts +2 -2
  34. package/src/hooks/useFetchTopics.ts +2 -2
  35. package/src/layout/GlobalProvider/StoreInitialization.tsx +2 -2
  36. package/src/services/baseClientService/index.ts +9 -0
  37. package/src/services/debug.ts +32 -34
  38. package/src/services/file/index.ts +6 -2
  39. package/src/services/file/pglite.test.ts +198 -0
  40. package/src/services/file/pglite.ts +84 -0
  41. package/src/services/file/type.ts +4 -3
  42. package/src/services/github.ts +17 -0
  43. package/src/services/import/index.ts +6 -2
  44. package/src/services/import/pglite.test.ts +997 -0
  45. package/src/services/import/pglite.ts +34 -0
  46. package/src/services/message/client.ts +2 -0
  47. package/src/services/message/index.ts +6 -2
  48. package/src/services/message/pglite.test.ts +430 -0
  49. package/src/services/message/pglite.ts +118 -0
  50. package/src/services/message/server.ts +9 -9
  51. package/src/services/message/type.ts +3 -4
  52. package/src/services/plugin/index.ts +6 -2
  53. package/src/services/plugin/pglite.test.ts +175 -0
  54. package/src/services/plugin/pglite.ts +51 -0
  55. package/src/services/session/client.ts +1 -1
  56. package/src/services/session/index.ts +6 -2
  57. package/src/services/session/pglite.test.ts +411 -0
  58. package/src/services/session/pglite.ts +184 -0
  59. package/src/services/session/type.ts +14 -1
  60. package/src/services/topic/index.ts +6 -3
  61. package/src/services/topic/pglite.test.ts +212 -0
  62. package/src/services/topic/pglite.ts +85 -0
  63. package/src/services/user/client.test.ts +0 -1
  64. package/src/services/user/index.ts +8 -2
  65. package/src/services/user/pglite.test.ts +98 -0
  66. package/src/services/user/pglite.ts +92 -0
  67. package/src/store/chat/slices/builtinTool/action.test.ts +3 -4
  68. package/src/store/global/actions/clientDb.ts +51 -0
  69. package/src/store/global/initialState.ts +13 -0
  70. package/src/store/global/selectors.ts +24 -3
  71. package/src/store/global/store.ts +3 -1
  72. package/src/store/session/slices/sessionGroup/reducer.test.ts +6 -6
  73. package/src/store/user/slices/common/action.ts +2 -4
  74. package/src/types/clientDB.ts +29 -0
  75. package/src/types/importer.ts +17 -5
  76. package/src/types/meta.ts +0 -9
  77. package/src/types/session/sessionGroup.ts +3 -3
  78. package/src/services/message/index.test.ts +0 -48
package/CHANGELOG.md CHANGED
@@ -2,6 +2,31 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ## [Version 1.37.0](https://github.com/lobehub/lobe-chat/compare/v1.36.46...v1.37.0)
6
+
7
+ <sup>Released on **2024-12-22**</sup>
8
+
9
+ #### ✨ Features
10
+
11
+ - **misc**: Support to use pglite as client db.
12
+
13
+ <br/>
14
+
15
+ <details>
16
+ <summary><kbd>Improvements and Fixes</kbd></summary>
17
+
18
+ #### What's improved
19
+
20
+ - **misc**: Support to use pglite as client db, closes [#4873](https://github.com/lobehub/lobe-chat/issues/4873) ([4131f20](https://github.com/lobehub/lobe-chat/commit/4131f20))
21
+
22
+ </details>
23
+
24
+ <div align="right">
25
+
26
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
27
+
28
+ </div>
29
+
5
30
  ### [Version 1.36.46](https://github.com/lobehub/lobe-chat/compare/v1.36.45...v1.36.46)
6
31
 
7
32
  <sup>Released on **2024-12-21**</sup>
package/README.ja-JP.md CHANGED
@@ -263,14 +263,14 @@ LobeChat のプラグインエコシステムは、そのコア機能の重要
263
263
 
264
264
  <!-- PLUGIN LIST -->
265
265
 
266
- | 最近追加 | 説明 |
267
- | ------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
268
- | [Google CSE](https://lobechat.com/discover/plugin/google-cse)<br/><sup>By **vsnthdev** on **2024-12-02**</sup> | 公式の CSE API を通じて Google を検索します。<br/>`ウェブ` `検索` |
269
- | [話す](https://lobechat.com/discover/plugin/speak)<br/><sup>By **speak** on **2024-12-02**</sup> | Speak は、AI パワードの言語チューターで、他の言語で何でも言う方法を学ぶことができます。<br/>`教育` `言語` |
270
- | [Tongyi Wanxiang 画像生成器](https://lobechat.com/discover/plugin/alps-tongyi-image)<br/><sup>By **YoungTx** on **2024-08-09**</sup> | このプラグインは、Alibaba Tongyi Wanxiang モデルを使用して、テキストプロンプトに基づいて画像を生成します。<br/>`画像` `トンギ` `ワンシャン` |
271
- | [ショッピングツール](https://lobechat.com/discover/plugin/ShoppingTools)<br/><sup>By **shoppingtools** on **2024-07-19**</sup> | eBay AliExpress で商品を検索し、eBay のイベントやクーポンを見つけます。迅速な例を取得します。<br/>`ショッピング` `e-bay` `ali-express` `クーポン` |
272
-
273
- > 📊 Total plugins: [<kbd>**47**</kbd>](https://lobechat.com/discover/plugins)
266
+ | 最近追加 | 説明 |
267
+ | ------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------- |
268
+ | [PortfolioMeta](https://lobechat.com/discover/plugin/StockData)<br/><sup>By **portfoliometa** on **2024-12-22**</sup> | 株を分析し、包括的なリアルタイムの投資データと分析を取得します。<br/>`stock` |
269
+ | [Google CSE](https://lobechat.com/discover/plugin/google-cse)<br/><sup>By **vsnthdev** on **2024-12-02**</sup> | 公式の CSE API を通じて Google を検索します。<br/>`ウェブ` `検索` |
270
+ | [話す](https://lobechat.com/discover/plugin/speak)<br/><sup>By **speak** on **2024-12-02**</sup> | Speak は、AI パワードの言語チューターで、他の言語で何でも言う方法を学ぶことができます。<br/>`教育` `言語` |
271
+ | [Tongyi Wanxiang 画像生成器](https://lobechat.com/discover/plugin/alps-tongyi-image)<br/><sup>By **YoungTx** on **2024-08-09**</sup> | このプラグインは、Alibaba Tongyi Wanxiang モデルを使用して、テキストプロンプトに基づいて画像を生成します。<br/>`画像` `トンギ` `ワンシャン` |
272
+
273
+ > 📊 Total plugins: [<kbd>**48**</kbd>](https://lobechat.com/discover/plugins)
274
274
 
275
275
  <!-- PLUGIN LIST -->
276
276
 
package/README.md CHANGED
@@ -280,14 +280,14 @@ In addition, these plugins are not limited to news aggregation, but can also ext
280
280
 
281
281
  <!-- PLUGIN LIST -->
282
282
 
283
- | Recent Submits | Description |
284
- | ----------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
285
- | [Google CSE](https://lobechat.com/discover/plugin/google-cse)<br/><sup>By **vsnthdev** on **2024-12-02**</sup> | Searches Google through their official CSE API.<br/>`web` `search` |
286
- | [Speak](https://lobechat.com/discover/plugin/speak)<br/><sup>By **speak** on **2024-12-02**</sup> | Learn how to say anything in another language with Speak, your AI-powered language tutor.<br/>`education` `language` |
287
- | [Tongyi wanxiang Image Generator](https://lobechat.com/discover/plugin/alps-tongyi-image)<br/><sup>By **YoungTx** on **2024-08-09**</sup> | This plugin uses Alibaba's Tongyi Wanxiang model to generate images based on text prompts.<br/>`image` `tongyi` `wanxiang` |
288
- | [Shopping tools](https://lobechat.com/discover/plugin/ShoppingTools)<br/><sup>By **shoppingtools** on **2024-07-19**</sup> | Search for products on eBay & AliExpress, find eBay events & coupons. Get prompt examples.<br/>`shopping` `e-bay` `ali-express` `coupons` |
289
-
290
- > 📊 Total plugins: [<kbd>**47**</kbd>](https://lobechat.com/discover/plugins)
283
+ | Recent Submits | Description |
284
+ | ----------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
285
+ | [PortfolioMeta](https://lobechat.com/discover/plugin/StockData)<br/><sup>By **portfoliometa** on **2024-12-22**</sup> | Analyze stocks and get comprehensive real-time investment data and analytics.<br/>`stock` |
286
+ | [Google CSE](https://lobechat.com/discover/plugin/google-cse)<br/><sup>By **vsnthdev** on **2024-12-02**</sup> | Searches Google through their official CSE API.<br/>`web` `search` |
287
+ | [Speak](https://lobechat.com/discover/plugin/speak)<br/><sup>By **speak** on **2024-12-02**</sup> | Learn how to say anything in another language with Speak, your AI-powered language tutor.<br/>`education` `language` |
288
+ | [Tongyi wanxiang Image Generator](https://lobechat.com/discover/plugin/alps-tongyi-image)<br/><sup>By **YoungTx** on **2024-08-09**</sup> | This plugin uses Alibaba's Tongyi Wanxiang model to generate images based on text prompts.<br/>`image` `tongyi` `wanxiang` |
289
+
290
+ > 📊 Total plugins: [<kbd>**48**</kbd>](https://lobechat.com/discover/plugins)
291
291
 
292
292
  <!-- PLUGIN LIST -->
293
293
 
package/README.zh-CN.md CHANGED
@@ -273,14 +273,14 @@ LobeChat 的插件生态系统是其核心功能的重要扩展,它极大地
273
273
 
274
274
  <!-- PLUGIN LIST -->
275
275
 
276
- | 最近新增 | 描述 |
277
- | ---------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
278
- | [谷歌自定义搜索引擎](https://lobechat.com/discover/plugin/google-cse)<br/><sup>By **vsnthdev** on **2024-12-02**</sup> | 通过他们的官方自定义搜索引擎 API 搜索谷歌。<br/>`网络` `搜索` |
279
- | [Speak](https://lobechat.com/discover/plugin/speak)<br/><sup>By **speak** on **2024-12-02**</sup> | 使用 Speak,您的 AI 语言导师,学习如何用另一种语言说任何事情。<br/>`教育` `语言` |
280
- | [通义万象图像生成器](https://lobechat.com/discover/plugin/alps-tongyi-image)<br/><sup>By **YoungTx** on **2024-08-09**</sup> | 此插件使用阿里巴巴的通义万象模型根据文本提示生成图像。<br/>`图像` `通义` `万象` |
281
- | [购物工具](https://lobechat.com/discover/plugin/ShoppingTools)<br/><sup>By **shoppingtools** on **2024-07-19**</sup> | 在 eBay 和 AliExpress 上搜索产品,查找 eBay 活动和优惠券。获取快速示例。<br/>`购物` `e-bay` `ali-express` `优惠券` |
282
-
283
- > 📊 Total plugins: [<kbd>**47**</kbd>](https://lobechat.com/discover/plugins)
276
+ | 最近新增 | 描述 |
277
+ | ---------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- |
278
+ | [PortfolioMeta](https://lobechat.com/discover/plugin/StockData)<br/><sup>By **portfoliometa** on **2024-12-22**</sup> | 分析股票并获取全面的实时投资数据和分析。<br/>`股票` |
279
+ | [谷歌自定义搜索引擎](https://lobechat.com/discover/plugin/google-cse)<br/><sup>By **vsnthdev** on **2024-12-02**</sup> | 通过他们的官方自定义搜索引擎 API 搜索谷歌。<br/>`网络` `搜索` |
280
+ | [Speak](https://lobechat.com/discover/plugin/speak)<br/><sup>By **speak** on **2024-12-02**</sup> | 使用 Speak,您的 AI 语言导师,学习如何用另一种语言说任何事情。<br/>`教育` `语言` |
281
+ | [通义万象图像生成器](https://lobechat.com/discover/plugin/alps-tongyi-image)<br/><sup>By **YoungTx** on **2024-08-09**</sup> | 此插件使用阿里巴巴的通义万象模型根据文本提示生成图像。<br/>`图像` `通义` `万象` |
282
+
283
+ > 📊 Total plugins: [<kbd>**48**</kbd>](https://lobechat.com/discover/plugins)
284
284
 
285
285
  <!-- PLUGIN LIST -->
286
286
 
package/changelog/v1.json CHANGED
@@ -1,4 +1,13 @@
1
1
  [
2
+ {
3
+ "children": {
4
+ "features": [
5
+ "Support to use pglite as client db."
6
+ ]
7
+ },
8
+ "date": "2024-12-22",
9
+ "version": "1.37.0"
10
+ },
2
11
  {
3
12
  "children": {
4
13
  "improvements": [
package/next.config.mjs CHANGED
@@ -6,6 +6,7 @@ import ReactComponentName from 'react-scan/react-component-name/webpack';
6
6
  const isProd = process.env.NODE_ENV === 'production';
7
7
  const buildWithDocker = process.env.DOCKER === 'true';
8
8
  const enableReactScan = !!process.env.REACT_SCAN_MONITOR_API_KEY;
9
+ const isUsePglite = process.env.NEXT_PUBLIC_CLIENT_DB === 'pglite';
9
10
 
10
11
  // if you need to proxy the api endpoint to remote server
11
12
  const API_PROXY_ENDPOINT = process.env.API_PROXY_ENDPOINT || '';
@@ -26,6 +27,7 @@ const nextConfig = {
26
27
  'gpt-tokenizer',
27
28
  'chroma-js',
28
29
  ],
30
+ serverComponentsExternalPackages: ['@electric-sql/pglite'],
29
31
  webVitalsAttribution: ['CLS', 'LCP'],
30
32
  },
31
33
 
@@ -180,7 +182,8 @@ const nextConfig = {
180
182
  layers: true,
181
183
  };
182
184
 
183
- if (enableReactScan) {
185
+ // 开启该插件会导致 pglite 的 fs bundler 被改表
186
+ if (enableReactScan && !isUsePglite) {
184
187
  config.plugins.push(ReactComponentName({}));
185
188
  }
186
189
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/chat",
3
- "version": "1.36.46",
3
+ "version": "1.37.0",
4
4
  "description": "Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
5
5
  "keywords": [
6
6
  "framework",
@@ -32,7 +32,8 @@
32
32
  "build-sitemap": "tsx ./scripts/buildSitemapIndex/index.ts",
33
33
  "build:analyze": "ANALYZE=true next build",
34
34
  "build:docker": "DOCKER=true next build && npm run build-sitemap",
35
- "db:generate": "drizzle-kit generate",
35
+ "db:generate": "drizzle-kit generate && npm run db:generate-client",
36
+ "db:generate-client": "tsx ./scripts/migrateClientDB/compile-migrations.ts",
36
37
  "db:migrate": "MIGRATION_DB=1 tsx ./scripts/migrateServerDB/index.ts",
37
38
  "db:push": "drizzle-kit push",
38
39
  "db:push-test": "NODE_ENV=test drizzle-kit push",
@@ -117,6 +118,7 @@
117
118
  "@clerk/themes": "^2.1.37",
118
119
  "@codesandbox/sandpack-react": "^2.19.9",
119
120
  "@cyntler/react-doc-viewer": "^1.17.0",
121
+ "@electric-sql/pglite": "0.2.13",
120
122
  "@google/generative-ai": "^0.21.0",
121
123
  "@huggingface/inference": "^2.8.1",
122
124
  "@icons-pack/react-simple-icons": "9.6.0",
@@ -307,7 +309,7 @@
307
309
  "vitest": "~1.2.2",
308
310
  "vitest-canvas-mock": "^0.3.3"
309
311
  },
310
- "packageManager": "pnpm@9.15.0",
312
+ "packageManager": "pnpm@9.15.1",
311
313
  "publishConfig": {
312
314
  "access": "public",
313
315
  "registry": "https://registry.npmjs.org"
@@ -0,0 +1,14 @@
1
+ import { readMigrationFiles } from 'drizzle-orm/migrator';
2
+ import { writeFileSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+
5
+ const dbBase = join(__dirname, '../../src/database');
6
+ const migrationsFolder = join(dbBase, './migrations');
7
+ const migrations = readMigrationFiles({ migrationsFolder: migrationsFolder });
8
+
9
+ writeFileSync(
10
+ join(dbBase, './client/migrations.json'),
11
+ JSON.stringify(migrations, null, 2), // null, 2 adds indentation for better readability
12
+ );
13
+
14
+ console.log('🏁 client migrations.json compiled!');
@@ -1,6 +1,7 @@
1
1
  import { PropsWithChildren } from 'react';
2
2
 
3
3
  import MobileContentLayout from '@/components/server/MobileNavLayout';
4
+ import InitClientDB from '@/features/InitClientDB';
4
5
 
5
6
  import Header from './features/Header';
6
7
 
@@ -8,6 +9,7 @@ const Layout = ({ children }: PropsWithChildren) => {
8
9
  return (
9
10
  <MobileContentLayout header={<Header />} withNav>
10
11
  {children}
12
+ <InitClientDB />
11
13
  </MobileContentLayout>
12
14
  );
13
15
  };
@@ -1,6 +1,7 @@
1
1
  import { Flexbox } from 'react-layout-kit';
2
2
 
3
- import Migration from '../../features/Migration';
3
+ import InitClientDB from '@/features/InitClientDB';
4
+
4
5
  import { LayoutProps } from '../type';
5
6
  import SessionPanel from './SessionPanel';
6
7
 
@@ -18,7 +19,7 @@ const Layout = ({ children, session }: LayoutProps) => {
18
19
  {children}
19
20
  </Flexbox>
20
21
  </Flexbox>
21
- <Migration />
22
+ <InitClientDB bottom={60} />
22
23
  {/* ↓ cloud slot ↓ */}
23
24
 
24
25
  {/* ↑ cloud slot ↑ */}
@@ -1,10 +1,10 @@
1
1
  'use client';
2
2
 
3
3
  import { createStyles } from 'antd-style';
4
- import { memo } from 'react';
4
+ import { Suspense, memo } from 'react';
5
5
  import { Flexbox } from 'react-layout-kit';
6
6
 
7
- import Migration from '@/app/(main)/chat/features/Migration';
7
+ import InitClientDB from '@/features/InitClientDB';
8
8
  import { useQuery } from '@/hooks/useQuery';
9
9
 
10
10
  import { LayoutProps } from './type';
@@ -39,7 +39,9 @@ const Layout = memo<LayoutProps>(({ children, session }) => {
39
39
  >
40
40
  {children}
41
41
  </Flexbox>
42
- <Migration />
42
+ <Suspense fallback={null}>
43
+ <InitClientDB bottom={100} />
44
+ </Suspense>
43
45
  </>
44
46
  );
45
47
  });
@@ -0,0 +1,290 @@
1
+ import {
2
+ ImportMessage,
3
+ ImportSession,
4
+ ImportSessionGroup,
5
+ ImportTopic,
6
+ ImporterEntryData,
7
+ } from '@/types/importer';
8
+
9
+ interface V2DB_File {
10
+ /**
11
+ * create Time
12
+ */
13
+ createdAt: number;
14
+ /**
15
+ * file data array buffer
16
+ */
17
+ data: ArrayBuffer;
18
+ /**
19
+ * file type
20
+ * @example 'image/png'
21
+ */
22
+ fileType: string;
23
+ id: string;
24
+ metadata: any;
25
+ /**
26
+ * file name
27
+ * @example 'test.png'
28
+ */
29
+ name: string;
30
+ /**
31
+ * the mode database save the file
32
+ * local mean save the raw file into data
33
+ * url mean upload the file to a cdn and then save the url
34
+ */
35
+ saveMode: 'local' | 'url';
36
+ /**
37
+ * file size
38
+ */
39
+ size: number;
40
+ /**
41
+ * file url if saveMode is url
42
+ */
43
+ url: string;
44
+ }
45
+
46
+ interface V2DB_MESSAGE {
47
+ content: string;
48
+ createdAt: number;
49
+ error?: any;
50
+ favorite: 0 | 1;
51
+ files?: string[];
52
+ fromModel?: string;
53
+ fromProvider?: string;
54
+ id: string;
55
+ observationId?: string;
56
+ // foreign keys
57
+ parentId?: string;
58
+ plugin?: any;
59
+ pluginError?: any;
60
+ pluginState?: any;
61
+
62
+ quotaId?: string;
63
+ role: string;
64
+ sessionId?: string;
65
+ tool_call_id?: string;
66
+ tools?: object[];
67
+ topicId?: string;
68
+
69
+ traceId?: string;
70
+ translate?: object | false;
71
+ tts?: any;
72
+ updatedAt: number;
73
+ }
74
+
75
+ interface DB_Plugin {
76
+ createdAt: number;
77
+ id: string;
78
+ identifier: string;
79
+ manifest?: object;
80
+ settings?: object;
81
+ type: 'plugin' | 'customPlugin';
82
+ updatedAt: number;
83
+ }
84
+
85
+ interface DB_Session {
86
+ config: object;
87
+ createdAt: number;
88
+ group?: string;
89
+ // 原 Agent 类型
90
+ id: string;
91
+ meta: object;
92
+ pinned?: number;
93
+ type?: 'agent' | 'group';
94
+ updatedAt: number;
95
+ }
96
+
97
+ interface DB_SessionGroup {
98
+ createdAt: number;
99
+ id: string;
100
+ name: string;
101
+ sort?: number;
102
+ updatedAt: number;
103
+ }
104
+
105
+ interface DB_Topic {
106
+ createdAt: number;
107
+ favorite?: number;
108
+ id: string;
109
+ sessionId?: string;
110
+ title: string;
111
+ updatedAt: number;
112
+ }
113
+
114
+ interface DB_User {
115
+ avatar?: string;
116
+ createdAt: number;
117
+ id: string;
118
+ settings: object;
119
+ updatedAt: number;
120
+ uuid: string;
121
+ }
122
+
123
+ interface MigrationData {
124
+ files: V2DB_File[];
125
+ messages: V2DB_MESSAGE[];
126
+ plugins: DB_Plugin[];
127
+ sessionGroups: DB_SessionGroup[];
128
+ sessions: DB_Session[];
129
+ topics: DB_Topic[];
130
+ users: DB_User[];
131
+ }
132
+
133
+ const LOBE_CHAT_LOCAL_DB_NAME = 'LOBE_CHAT_DB';
134
+
135
+ const V2DB_LASET_SCHEMA_VERSION = 7;
136
+ export class V2DBReader {
137
+ private dbName: string = LOBE_CHAT_LOCAL_DB_NAME;
138
+ private storeNames: string[];
139
+
140
+ constructor(storeNames: string[]) {
141
+ this.storeNames = storeNames;
142
+ }
143
+
144
+ /**
145
+ * 读取所有数据
146
+ */
147
+ async readAllData(): Promise<MigrationData> {
148
+ try {
149
+ // 打开数据库连接
150
+ const db = await this.openDB();
151
+
152
+ // 并行读取所有表的数据
153
+ const results = await Promise.all(
154
+ this.storeNames.map((storeName) => this.readStore(db, storeName)),
155
+ );
156
+
157
+ // 构建返回结果
158
+ const migrationData = this.storeNames.reduce((acc, storeName, index) => {
159
+ // @ts-expect-error
160
+ acc[storeName] = results[index];
161
+ return acc;
162
+ }, {} as MigrationData);
163
+
164
+ // 关闭数据库连接
165
+ db.close();
166
+
167
+ return migrationData;
168
+ } catch (error) {
169
+ console.error('读取数据库失败:', error);
170
+ throw error;
171
+ }
172
+ }
173
+
174
+ async convertToImportData(data: MigrationData): Promise<ImporterEntryData> {
175
+ // 转换 messages
176
+ const messages = data.messages.map(
177
+ (msg): ImportMessage => ({
178
+ // 使用原有的 id
179
+ content: msg.content,
180
+ createdAt: msg.createdAt,
181
+ // 处理 error
182
+ error: msg.error || msg.pluginError,
183
+
184
+ // 处理额外信息
185
+ extra: {
186
+ fromModel: msg.fromModel,
187
+ fromProvider: msg.fromProvider,
188
+ translate: msg.translate as any,
189
+ tts: msg.tts,
190
+ },
191
+
192
+ files: msg.files,
193
+ id: msg.id,
194
+
195
+ // 复制原有字段
196
+ observationId: msg.observationId,
197
+ parentId: msg.parentId,
198
+ plugin: msg.plugin,
199
+ pluginState: msg.pluginState,
200
+ quotaId: msg.quotaId,
201
+ role: msg.role as any,
202
+ sessionId: msg.sessionId,
203
+ tool_call_id: msg.tool_call_id,
204
+ tools: msg.tools as any,
205
+
206
+ topicId: msg.topicId,
207
+
208
+ traceId: msg.traceId,
209
+
210
+ updatedAt: msg.updatedAt,
211
+ }),
212
+ );
213
+
214
+ // 转换 sessionGroups
215
+ const sessionGroups = data.sessionGroups.map(
216
+ (group): ImportSessionGroup => ({
217
+ createdAt: group.createdAt,
218
+ id: group.id,
219
+ // 使用原有的 id
220
+ name: group.name,
221
+ sort: group.sort || null,
222
+ updatedAt: group.updatedAt,
223
+ }),
224
+ );
225
+
226
+ // 转换 sessions
227
+ const sessions = data.sessions.map(
228
+ (session): ImportSession => ({
229
+ // 使用原有的 id
230
+ config: session.config as any,
231
+ createdAt: new Date(session.createdAt).toString(),
232
+ group: session.group,
233
+ id: session.id,
234
+ meta: session.meta as any,
235
+ pinned: session.pinned ? true : undefined,
236
+ type: session.type || 'agent',
237
+ updatedAt: new Date(session.updatedAt).toString(),
238
+ }),
239
+ );
240
+
241
+ const topics = data.topics.map(
242
+ (topic): ImportTopic => ({
243
+ ...topic,
244
+ favorite: topic.favorite ? true : undefined,
245
+ }),
246
+ );
247
+
248
+ return {
249
+ messages,
250
+ sessionGroups,
251
+ sessions,
252
+ topics,
253
+ version: V2DB_LASET_SCHEMA_VERSION,
254
+ };
255
+ }
256
+
257
+ /**
258
+ * 打开数据库
259
+ */
260
+ private openDB(): Promise<IDBDatabase> {
261
+ return new Promise((resolve, reject) => {
262
+ const request = indexedDB.open(this.dbName);
263
+
264
+ // eslint-disable-next-line unicorn/prefer-add-event-listener
265
+ request.onerror = () => {
266
+ reject(request.error);
267
+ };
268
+ request.onsuccess = () => resolve(request.result);
269
+ });
270
+ }
271
+
272
+ /**
273
+ * 读取单个存储对象的所有数据
274
+ */
275
+ private readStore(db: IDBDatabase, storeName: string): Promise<any[]> {
276
+ return new Promise((resolve, reject) => {
277
+ try {
278
+ const transaction = db.transaction(storeName, 'readonly');
279
+ const store = transaction.objectStore(storeName);
280
+ const request = store.getAll();
281
+
282
+ // eslint-disable-next-line unicorn/prefer-add-event-listener
283
+ request.onerror = () => reject(request.error);
284
+ request.onsuccess = () => resolve(request.result);
285
+ } catch (error) {
286
+ reject(error);
287
+ }
288
+ });
289
+ }
290
+ }
@@ -1,14 +1,12 @@
1
1
  import { Button } from 'antd';
2
- import { createStore, set } from 'idb-keyval';
3
2
  import { ReactNode, memo } from 'react';
4
3
  import { useTranslation } from 'react-i18next';
5
4
 
6
- import { Migration } from '@/migrations';
7
5
  import { configService } from '@/services/config';
8
6
  import { useChatStore } from '@/store/chat';
9
7
  import { useSessionStore } from '@/store/session';
10
8
 
11
- import { MIGRATE_KEY, MigrationError, UpgradeStatus, V1DB_NAME, V1DB_TABLE_NAME } from './const';
9
+ import { MigrationError, UpgradeStatus } from './const';
12
10
 
13
11
  export interface UpgradeButtonProps {
14
12
  children?: ReactNode;
@@ -31,21 +29,19 @@ const UpgradeButton = memo<UpgradeButtonProps>(
31
29
 
32
30
  const upgrade = async () => {
33
31
  try {
34
- const data = Migration.migrate({ state, version: 1 });
35
-
36
32
  setUpgradeStatus(UpgradeStatus.UPGRADING);
37
33
 
38
34
  await configService.importConfigState({
39
35
  exportType: 'sessions',
40
- state: data.state,
41
- version: 2,
36
+ state: state,
37
+ version: 7,
42
38
  });
43
39
 
44
40
  await refreshSession();
45
41
  await refreshMessages();
46
42
  await refreshTopic();
47
43
 
48
- await set(MIGRATE_KEY, true, createStore(V1DB_NAME, V1DB_TABLE_NAME));
44
+ localStorage.setItem('V2DB_IS_MIGRATED', '1');
49
45
 
50
46
  setUpgradeStatus(UpgradeStatus.UPGRADED);
51
47
 
@@ -1,11 +1,14 @@
1
1
  'use client';
2
2
 
3
3
  import { Spin } from 'antd';
4
- import { createStore, getMany } from 'idb-keyval';
5
4
  import dynamic from 'next/dynamic';
6
5
  import { memo, useEffect, useState } from 'react';
7
6
 
8
- import { MIGRATE_KEY, V1DB_NAME, V1DB_TABLE_NAME } from './const';
7
+ import { isServerMode } from '@/const/version';
8
+ import { useGlobalStore } from '@/store/global';
9
+ import { systemStatusSelectors } from '@/store/global/selectors';
10
+
11
+ import { V2DBReader } from './DBReader';
9
12
 
10
13
  const Modal = dynamic(() => import('./Modal'), { loading: () => <Spin fullscreen />, ssr: false });
11
14
 
@@ -13,25 +16,33 @@ const Migration = memo(() => {
13
16
  const [dbState, setDbState] = useState(null);
14
17
  const [open, setOpen] = useState(false);
15
18
 
16
- const checkMigration = async () => {
17
- const [state, migrated] = await getMany(
18
- ['state', MIGRATE_KEY],
19
- createStore(V1DB_NAME, V1DB_TABLE_NAME),
20
- );
19
+ const isPgliteInited = useGlobalStore(systemStatusSelectors.isPgliteInited);
21
20
 
21
+ const checkMigration = async () => {
22
+ const isMigrated = localStorage.getItem('V2DB_IS_MIGRATED');
22
23
  // if db have migrated already, don't show modal
23
- if (migrated) return;
24
-
25
- // if db doesn't exist state key,it means a new user
26
- if (!state) return;
27
-
28
- setDbState(state);
24
+ if (isMigrated || isServerMode) return;
25
+
26
+ const dbReader = new V2DBReader([
27
+ 'messages',
28
+ 'files',
29
+ 'plugins',
30
+ 'sessionGroups',
31
+ 'sessions',
32
+ 'topics',
33
+ 'users',
34
+ ]);
35
+ const data = await dbReader.readAllData();
36
+ console.log('migration data:', data);
37
+ const state = await dbReader.convertToImportData(data);
38
+ console.log('import state', state);
39
+ setDbState(state as any);
29
40
  setOpen(true);
30
41
  };
31
42
 
32
43
  useEffect(() => {
33
- checkMigration();
34
- }, []);
44
+ if (isPgliteInited) checkMigration();
45
+ }, [isPgliteInited]);
35
46
 
36
47
  return open && <Modal open={open} setOpen={setOpen} state={dbState} />;
37
48
  });