@lobehub/lobehub 2.0.0-next.340 â 2.0.0-next.342
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.
- package/CHANGELOG.md +58 -0
- package/apps/desktop/src/main/controllers/RemoteServerConfigCtr.ts +2 -1
- package/apps/desktop/src/main/controllers/__tests__/RemoteServerConfigCtr.test.ts +8 -8
- package/changelog/v1.json +14 -0
- package/locales/en-US/common.json +6 -0
- package/locales/zh-CN/common.json +6 -0
- package/package.json +1 -1
- package/packages/electron-client-ipc/src/types/dataSync.ts +1 -1
- package/src/app/[variants]/(main)/settings/about/features/Version.tsx +13 -2
- package/src/features/PageEditor/Copilot/Toolbar.tsx +1 -1
- package/src/hooks/useUserAvatar.test.ts +2 -2
- package/src/layout/GlobalProvider/ServerVersionOutdatedAlert.tsx +131 -0
- package/src/layout/GlobalProvider/StoreInitialization.tsx +8 -1
- package/src/layout/GlobalProvider/index.tsx +4 -0
- package/src/locales/default/common.ts +8 -0
- package/src/services/global.ts +25 -0
- package/src/store/global/actions/general.ts +46 -0
- package/src/store/global/initialState.ts +9 -0
- package/src/styles/antdOverride.ts +9 -0
- package/src/server/routers/lambda/userMemories/index.ts +0 -13
- package/src/server/routers/lambda/userMemories/reembed.ts +0 -440
- package/src/server/routers/lambda/userMemories/search.ts +0 -117
- package/src/server/routers/lambda/userMemories/shared.ts +0 -63
- package/src/server/routers/lambda/userMemories/tools.ts +0 -410
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,64 @@
|
|
|
2
2
|
|
|
3
3
|
# Changelog
|
|
4
4
|
|
|
5
|
+
## [Version 2.0.0-next.342](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.341...v2.0.0-next.342)
|
|
6
|
+
|
|
7
|
+
<sup>Released on **2026-01-22**</sup>
|
|
8
|
+
|
|
9
|
+
#### âģ Code Refactoring
|
|
10
|
+
|
|
11
|
+
- **userMemories**: Removed un-used code.
|
|
12
|
+
|
|
13
|
+
#### đ Bug Fixes
|
|
14
|
+
|
|
15
|
+
- **copilot**: Pass correct scope when creating new session in PageEditor.
|
|
16
|
+
|
|
17
|
+
<br/>
|
|
18
|
+
|
|
19
|
+
<details>
|
|
20
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
21
|
+
|
|
22
|
+
#### Code refactoring
|
|
23
|
+
|
|
24
|
+
- **userMemories**: Removed un-used code, closes [#11713](https://github.com/lobehub/lobe-chat/issues/11713) ([89750fc](https://github.com/lobehub/lobe-chat/commit/89750fc))
|
|
25
|
+
|
|
26
|
+
#### What's fixed
|
|
27
|
+
|
|
28
|
+
- **copilot**: Pass correct scope when creating new session in PageEditor, closes [#11714](https://github.com/lobehub/lobe-chat/issues/11714) ([0259270](https://github.com/lobehub/lobe-chat/commit/0259270))
|
|
29
|
+
|
|
30
|
+
</details>
|
|
31
|
+
|
|
32
|
+
<div align="right">
|
|
33
|
+
|
|
34
|
+
[](#readme-top)
|
|
35
|
+
|
|
36
|
+
</div>
|
|
37
|
+
|
|
38
|
+
## [Version 2.0.0-next.341](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.340...v2.0.0-next.341)
|
|
39
|
+
|
|
40
|
+
<sup>Released on **2026-01-22**</sup>
|
|
41
|
+
|
|
42
|
+
#### ⨠Features
|
|
43
|
+
|
|
44
|
+
- **misc**: Add server version check for desktop app.
|
|
45
|
+
|
|
46
|
+
<br/>
|
|
47
|
+
|
|
48
|
+
<details>
|
|
49
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
50
|
+
|
|
51
|
+
#### What's improved
|
|
52
|
+
|
|
53
|
+
- **misc**: Add server version check for desktop app, closes [#11710](https://github.com/lobehub/lobe-chat/issues/11710) ([0cf2723](https://github.com/lobehub/lobe-chat/commit/0cf2723))
|
|
54
|
+
|
|
55
|
+
</details>
|
|
56
|
+
|
|
57
|
+
<div align="right">
|
|
58
|
+
|
|
59
|
+
[](#readme-top)
|
|
60
|
+
|
|
61
|
+
</div>
|
|
62
|
+
|
|
5
63
|
## [Version 2.0.0-next.340](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.339...v2.0.0-next.340)
|
|
6
64
|
|
|
7
65
|
<sup>Released on **2026-01-22**</sup>
|
|
@@ -50,7 +50,8 @@ export default class RemoteServerConfigCtr extends ControllerModule {
|
|
|
50
50
|
* Local mode has been removed; fall back to cloud.
|
|
51
51
|
*/
|
|
52
52
|
private normalizeConfig = (config: DataSyncConfig): DataSyncConfig => {
|
|
53
|
-
|
|
53
|
+
// Use type assertion to handle legacy 'local' value from stored data
|
|
54
|
+
if ((config.storageMode as string) !== 'local') return config;
|
|
54
55
|
|
|
55
56
|
const nextConfig: DataSyncConfig = {
|
|
56
57
|
...config,
|
|
@@ -60,7 +60,7 @@ describe('RemoteServerConfigCtr', () => {
|
|
|
60
60
|
ipcMainHandleMock.mockClear();
|
|
61
61
|
mockStoreManager.get.mockReturnValue({
|
|
62
62
|
active: false,
|
|
63
|
-
storageMode: '
|
|
63
|
+
storageMode: 'cloud',
|
|
64
64
|
});
|
|
65
65
|
controller = new RemoteServerConfigCtr(mockApp);
|
|
66
66
|
});
|
|
@@ -85,7 +85,7 @@ describe('RemoteServerConfigCtr', () => {
|
|
|
85
85
|
it('should update configuration', async () => {
|
|
86
86
|
const prevConfig: DataSyncConfig = {
|
|
87
87
|
active: false,
|
|
88
|
-
storageMode: '
|
|
88
|
+
storageMode: 'cloud',
|
|
89
89
|
};
|
|
90
90
|
mockStoreManager.get.mockReturnValue(prevConfig);
|
|
91
91
|
|
|
@@ -195,7 +195,7 @@ describe('RemoteServerConfigCtr', () => {
|
|
|
195
195
|
refreshToken: Buffer.from('stored-refresh-token').toString('base64'),
|
|
196
196
|
};
|
|
197
197
|
}
|
|
198
|
-
return { active: false, storageMode: '
|
|
198
|
+
return { active: false, storageMode: 'cloud' };
|
|
199
199
|
});
|
|
200
200
|
|
|
201
201
|
// Create new controller to test loading from store
|
|
@@ -210,7 +210,7 @@ describe('RemoteServerConfigCtr', () => {
|
|
|
210
210
|
if (key === 'encryptedTokens') {
|
|
211
211
|
return null;
|
|
212
212
|
}
|
|
213
|
-
return { active: false, storageMode: '
|
|
213
|
+
return { active: false, storageMode: 'cloud' };
|
|
214
214
|
});
|
|
215
215
|
|
|
216
216
|
const newController = new RemoteServerConfigCtr(mockApp);
|
|
@@ -243,7 +243,7 @@ describe('RemoteServerConfigCtr', () => {
|
|
|
243
243
|
refreshToken: 'invalid-encrypted-token',
|
|
244
244
|
};
|
|
245
245
|
}
|
|
246
|
-
return { active: false, storageMode: '
|
|
246
|
+
return { active: false, storageMode: 'cloud' };
|
|
247
247
|
});
|
|
248
248
|
|
|
249
249
|
const newController = new RemoteServerConfigCtr(mockApp);
|
|
@@ -273,7 +273,7 @@ describe('RemoteServerConfigCtr', () => {
|
|
|
273
273
|
if (key === 'encryptedTokens') {
|
|
274
274
|
return null;
|
|
275
275
|
}
|
|
276
|
-
return { active: false, storageMode: '
|
|
276
|
+
return { active: false, storageMode: 'cloud' };
|
|
277
277
|
});
|
|
278
278
|
|
|
279
279
|
const newController = new RemoteServerConfigCtr(mockApp);
|
|
@@ -417,7 +417,7 @@ describe('RemoteServerConfigCtr', () => {
|
|
|
417
417
|
it('should return error when remote server is not active', async () => {
|
|
418
418
|
mockStoreManager.get.mockImplementation((key) => {
|
|
419
419
|
if (key === 'dataSyncConfig') {
|
|
420
|
-
return { active: false, storageMode: '
|
|
420
|
+
return { active: false, storageMode: 'cloud' };
|
|
421
421
|
}
|
|
422
422
|
return null;
|
|
423
423
|
});
|
|
@@ -648,7 +648,7 @@ describe('RemoteServerConfigCtr', () => {
|
|
|
648
648
|
refreshToken: 'stored-refresh',
|
|
649
649
|
};
|
|
650
650
|
}
|
|
651
|
-
return { active: false, storageMode: '
|
|
651
|
+
return { active: false, storageMode: 'cloud' };
|
|
652
652
|
});
|
|
653
653
|
|
|
654
654
|
const newController = new RemoteServerConfigCtr(mockApp);
|
package/changelog/v1.json
CHANGED
|
@@ -1,4 +1,18 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"children": {},
|
|
4
|
+
"date": "2026-01-22",
|
|
5
|
+
"version": "2.0.0-next.342"
|
|
6
|
+
},
|
|
7
|
+
{
|
|
8
|
+
"children": {
|
|
9
|
+
"features": [
|
|
10
|
+
"Add server version check for desktop app."
|
|
11
|
+
]
|
|
12
|
+
},
|
|
13
|
+
"date": "2026-01-22",
|
|
14
|
+
"version": "2.0.0-next.341"
|
|
15
|
+
},
|
|
2
16
|
{
|
|
3
17
|
"children": {
|
|
4
18
|
"improvements": [
|
|
@@ -332,6 +332,11 @@
|
|
|
332
332
|
"run": "Run",
|
|
333
333
|
"save": "Save",
|
|
334
334
|
"send": "Send",
|
|
335
|
+
"serverVersionOutdated.desc": "Your client version (v{{version}}) requires a newer server version.",
|
|
336
|
+
"serverVersionOutdated.dismiss": "Continue Anyway",
|
|
337
|
+
"serverVersionOutdated.title": "Server Version Outdated",
|
|
338
|
+
"serverVersionOutdated.upgrade": "Upgrade Guide",
|
|
339
|
+
"serverVersionOutdated.warning": "Some features may not work properly or behave unexpectedly. Please update your server for the best experience.",
|
|
335
340
|
"setting": "Settings",
|
|
336
341
|
"share": "Share",
|
|
337
342
|
"stop": "Stop",
|
|
@@ -380,6 +385,7 @@
|
|
|
380
385
|
"upgradeVersion.action": "Upgrade",
|
|
381
386
|
"upgradeVersion.hasNew": "Update available",
|
|
382
387
|
"upgradeVersion.newVersion": "Update available: {{version}}",
|
|
388
|
+
"upgradeVersion.serverVersion": "Server: {{version}}",
|
|
383
389
|
"userPanel.anonymousNickName": "Anonymous User",
|
|
384
390
|
"userPanel.billing": "Billing Management",
|
|
385
391
|
"userPanel.cloud": "Launch {{name}}",
|
|
@@ -332,6 +332,11 @@
|
|
|
332
332
|
"run": "čŋčĄ",
|
|
333
333
|
"save": "äŋå",
|
|
334
334
|
"send": "åé",
|
|
335
|
+
"serverVersionOutdated.desc": "åŊååŽĸæˇį̝įæŦīŧv{{version}}īŧéčĻæ´æ°įæåĄį̝įæŦã",
|
|
336
|
+
"serverVersionOutdated.dismiss": "įģ§įģäŊŋį¨",
|
|
337
|
+
"serverVersionOutdated.title": "æåĄį̝įæŦčŋæ§",
|
|
338
|
+
"serverVersionOutdated.upgrade": "åįē§æå",
|
|
339
|
+
"serverVersionOutdated.warning": "é¨ååčŊå¯čŊæ æŗæŖå¸¸äŊŋ፿åēį°ééĸæčĄä¸ēãåģēčŽŽæ´æ°æåĄį̝äģĨčˇåžæäŊŗäŊéĒã",
|
|
335
340
|
"setting": "莞įŊŽ",
|
|
336
341
|
"share": "åäēĢ",
|
|
337
342
|
"stop": "åæĸ",
|
|
@@ -380,6 +385,7 @@
|
|
|
380
385
|
"upgradeVersion.action": "åįē§",
|
|
381
386
|
"upgradeVersion.hasNew": "æå¯į¨æ´æ°",
|
|
382
387
|
"upgradeVersion.newVersion": "å¯į¨æ´æ°įæŦīŧ{{version}}",
|
|
388
|
+
"upgradeVersion.serverVersion": "æåĄį̝īŧ{{version}}",
|
|
383
389
|
"userPanel.anonymousNickName": "åŋå፿ˇ",
|
|
384
390
|
"userPanel.billing": "č´ĻåįŽĄį",
|
|
385
391
|
"userPanel.cloud": "äŊéĒ {{name}}",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lobehub/lobehub",
|
|
3
|
-
"version": "2.0.0-next.
|
|
3
|
+
"version": "2.0.0-next.342",
|
|
4
4
|
"description": "LobeHub - an open-source,comprehensive AI Agent 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",
|
|
@@ -6,7 +6,7 @@ import { useTranslation } from 'react-i18next';
|
|
|
6
6
|
|
|
7
7
|
import { ProductLogo } from '@/components/Branding';
|
|
8
8
|
import { CHANGELOG_URL, MANUAL_UPGRADE_URL, OFFICIAL_SITE } from '@/const/url';
|
|
9
|
-
import { CURRENT_VERSION } from '@/const/version';
|
|
9
|
+
import { CURRENT_VERSION, isDesktop } from '@/const/version';
|
|
10
10
|
import { useNewVersion } from '@/features/User/UserPanel/useNewVersion';
|
|
11
11
|
import { useGlobalStore } from '@/store/global';
|
|
12
12
|
|
|
@@ -18,9 +18,17 @@ const styles = createStaticStyles(({ css, cssVar }) => ({
|
|
|
18
18
|
|
|
19
19
|
const Version = memo<{ mobile?: boolean }>(({ mobile }) => {
|
|
20
20
|
const hasNewVersion = useNewVersion();
|
|
21
|
-
const [latestVersion] = useGlobalStore((s) => [
|
|
21
|
+
const [latestVersion, serverVersion, useCheckServerVersion] = useGlobalStore((s) => [
|
|
22
|
+
s.latestVersion,
|
|
23
|
+
s.serverVersion,
|
|
24
|
+
s.useCheckServerVersion,
|
|
25
|
+
]);
|
|
22
26
|
const { t } = useTranslation('common');
|
|
23
27
|
|
|
28
|
+
useCheckServerVersion(isDesktop);
|
|
29
|
+
|
|
30
|
+
const showServerVersion = serverVersion && serverVersion !== CURRENT_VERSION;
|
|
31
|
+
|
|
24
32
|
return (
|
|
25
33
|
<Flexbox
|
|
26
34
|
align={mobile ? 'stretch' : 'center'}
|
|
@@ -46,6 +54,9 @@ const Version = memo<{ mobile?: boolean }>(({ mobile }) => {
|
|
|
46
54
|
<div style={{ fontSize: 18, fontWeight: 'bolder' }}>{BRANDING_NAME}</div>
|
|
47
55
|
<Flexbox gap={6} horizontal={!mobile}>
|
|
48
56
|
<Tag>v{CURRENT_VERSION}</Tag>
|
|
57
|
+
{showServerVersion && (
|
|
58
|
+
<Tag>{t('upgradeVersion.serverVersion', { version: `v${serverVersion}` })}</Tag>
|
|
59
|
+
)}
|
|
49
60
|
{hasNewVersion && (
|
|
50
61
|
<Tag color={'info'}>
|
|
51
62
|
{t('upgradeVersion.newVersion', { version: `v${latestVersion}` })}
|
|
@@ -189,7 +189,7 @@ const CopilotToolbar = memo<CopilotToolbarProps>(({ agentId, isHovered }) => {
|
|
|
189
189
|
<div className={cx(styles.fadeContainer, isHovered ? styles.fadeIn : styles.fadeOut)}>
|
|
190
190
|
<ActionIcon
|
|
191
191
|
icon={PlusIcon}
|
|
192
|
-
onClick={() => switchTopic()}
|
|
192
|
+
onClick={() => switchTopic(null, { scope: 'page' })}
|
|
193
193
|
size={DESKTOP_HEADER_ICON_SIZE}
|
|
194
194
|
title={t('actions.addNewTopic')}
|
|
195
195
|
/>
|
|
@@ -62,14 +62,14 @@ describe('useUserAvatar', () => {
|
|
|
62
62
|
expect(result.current).toBe(mockAvatar);
|
|
63
63
|
});
|
|
64
64
|
|
|
65
|
-
it('should return original avatar when no remote server URL in desktop environment', () => {
|
|
65
|
+
it('should return original avatar when no remote server URL in desktop environment (selfHost mode)', () => {
|
|
66
66
|
mockIsDesktop = true;
|
|
67
67
|
const mockAvatar = '/api/avatar.png';
|
|
68
68
|
|
|
69
69
|
act(() => {
|
|
70
70
|
useUserStore.setState({ user: { avatar: mockAvatar } as any });
|
|
71
71
|
useElectronStore.setState({
|
|
72
|
-
dataSyncConfig: { remoteServerUrl: undefined, storageMode: '
|
|
72
|
+
dataSyncConfig: { remoteServerUrl: undefined, storageMode: 'selfHost' },
|
|
73
73
|
});
|
|
74
74
|
});
|
|
75
75
|
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Button, Flexbox, Icon } from '@lobehub/ui';
|
|
4
|
+
import { createStyles } from 'antd-style';
|
|
5
|
+
import { TriangleAlert, X } from 'lucide-react';
|
|
6
|
+
import { useState } from 'react';
|
|
7
|
+
import { useTranslation } from 'react-i18next';
|
|
8
|
+
|
|
9
|
+
import { MANUAL_UPGRADE_URL } from '@/const/url';
|
|
10
|
+
import { CURRENT_VERSION } from '@/const/version';
|
|
11
|
+
import { useElectronStore } from '@/store/electron';
|
|
12
|
+
import { electronSyncSelectors } from '@/store/electron/selectors';
|
|
13
|
+
import { useGlobalStore } from '@/store/global';
|
|
14
|
+
|
|
15
|
+
const useStyles = createStyles(({ css, token }) => ({
|
|
16
|
+
closeButton: css`
|
|
17
|
+
cursor: pointer;
|
|
18
|
+
|
|
19
|
+
position: absolute;
|
|
20
|
+
inset-block-start: 20px;
|
|
21
|
+
inset-inline-end: 20px;
|
|
22
|
+
|
|
23
|
+
display: flex;
|
|
24
|
+
align-items: center;
|
|
25
|
+
justify-content: center;
|
|
26
|
+
|
|
27
|
+
width: 28px;
|
|
28
|
+
height: 28px;
|
|
29
|
+
border-radius: ${token.borderRadius}px;
|
|
30
|
+
|
|
31
|
+
color: ${token.colorTextSecondary};
|
|
32
|
+
|
|
33
|
+
transition: all 0.2s;
|
|
34
|
+
|
|
35
|
+
&:hover {
|
|
36
|
+
color: ${token.colorText};
|
|
37
|
+
background: ${token.colorFillSecondary};
|
|
38
|
+
}
|
|
39
|
+
`,
|
|
40
|
+
container: css`
|
|
41
|
+
position: fixed;
|
|
42
|
+
z-index: 9999;
|
|
43
|
+
inset: 0;
|
|
44
|
+
|
|
45
|
+
display: flex;
|
|
46
|
+
align-items: center;
|
|
47
|
+
justify-content: center;
|
|
48
|
+
|
|
49
|
+
background: ${token.colorBgMask};
|
|
50
|
+
`,
|
|
51
|
+
content: css`
|
|
52
|
+
position: relative;
|
|
53
|
+
|
|
54
|
+
overflow: hidden;
|
|
55
|
+
|
|
56
|
+
max-width: 480px;
|
|
57
|
+
padding: 24px;
|
|
58
|
+
border: 1px solid ${token.yellowBorder};
|
|
59
|
+
border-radius: ${token.borderRadiusLG}px;
|
|
60
|
+
|
|
61
|
+
background: ${token.colorBgContainer};
|
|
62
|
+
box-shadow: ${token.boxShadowSecondary};
|
|
63
|
+
`,
|
|
64
|
+
desc: css`
|
|
65
|
+
line-height: 1.6;
|
|
66
|
+
color: ${token.colorTextSecondary};
|
|
67
|
+
`,
|
|
68
|
+
title: css`
|
|
69
|
+
font-size: 16px;
|
|
70
|
+
font-weight: bold;
|
|
71
|
+
color: ${token.colorWarningText};
|
|
72
|
+
`,
|
|
73
|
+
titleIcon: css`
|
|
74
|
+
flex-shrink: 0;
|
|
75
|
+
color: ${token.colorWarning};
|
|
76
|
+
`,
|
|
77
|
+
warning: css`
|
|
78
|
+
padding: 12px;
|
|
79
|
+
border-radius: ${token.borderRadius}px;
|
|
80
|
+
color: ${token.colorWarningText};
|
|
81
|
+
background: ${token.yellowBg};
|
|
82
|
+
`,
|
|
83
|
+
}));
|
|
84
|
+
|
|
85
|
+
const ServerVersionOutdatedAlert = () => {
|
|
86
|
+
const { styles } = useStyles();
|
|
87
|
+
const { t } = useTranslation('common');
|
|
88
|
+
const [dismissed, setDismissed] = useState(false);
|
|
89
|
+
const isServerVersionOutdated = useGlobalStore((s) => s.isServerVersionOutdated);
|
|
90
|
+
const storageMode = useElectronStore(electronSyncSelectors.storageMode);
|
|
91
|
+
|
|
92
|
+
// Only show alert when using self-hosted server, not cloud
|
|
93
|
+
if (storageMode !== 'selfHost') return null;
|
|
94
|
+
if (!isServerVersionOutdated || dismissed) return null;
|
|
95
|
+
|
|
96
|
+
return (
|
|
97
|
+
<div className={styles.container}>
|
|
98
|
+
<div className={styles.content}>
|
|
99
|
+
<div className={styles.closeButton} onClick={() => setDismissed(true)}>
|
|
100
|
+
<Icon icon={X} />
|
|
101
|
+
</div>
|
|
102
|
+
|
|
103
|
+
<Flexbox gap={16}>
|
|
104
|
+
<Flexbox align="center" gap={8} horizontal>
|
|
105
|
+
<Icon className={styles.titleIcon} icon={TriangleAlert} />
|
|
106
|
+
<div className={styles.title}>{t('serverVersionOutdated.title')}</div>
|
|
107
|
+
</Flexbox>
|
|
108
|
+
|
|
109
|
+
<div className={styles.desc}>
|
|
110
|
+
{t('serverVersionOutdated.desc', { version: CURRENT_VERSION })}
|
|
111
|
+
</div>
|
|
112
|
+
|
|
113
|
+
<div className={styles.warning}>{t('serverVersionOutdated.warning')}</div>
|
|
114
|
+
|
|
115
|
+
<Flexbox gap={8} horizontal justify="flex-end" style={{ marginTop: 8 }}>
|
|
116
|
+
<a href={MANUAL_UPGRADE_URL} rel="noreferrer" target="_blank">
|
|
117
|
+
<Button size="small" type="primary">
|
|
118
|
+
{t('serverVersionOutdated.upgrade')}
|
|
119
|
+
</Button>
|
|
120
|
+
</a>
|
|
121
|
+
<Button onClick={() => setDismissed(true)} size="small">
|
|
122
|
+
{t('serverVersionOutdated.dismiss')}
|
|
123
|
+
</Button>
|
|
124
|
+
</Flexbox>
|
|
125
|
+
</Flexbox>
|
|
126
|
+
</div>
|
|
127
|
+
</div>
|
|
128
|
+
);
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
export default ServerVersionOutdatedAlert;
|
|
@@ -5,6 +5,7 @@ import { memo } from 'react';
|
|
|
5
5
|
import { useTranslation } from 'react-i18next';
|
|
6
6
|
import { createStoreUpdater } from 'zustand-utils';
|
|
7
7
|
|
|
8
|
+
import { isDesktop } from '@/const/version';
|
|
8
9
|
import { enableNextAuth } from '@/envs/auth';
|
|
9
10
|
import { useIsMobile } from '@/hooks/useIsMobile';
|
|
10
11
|
import { useAgentStore } from '@/store/agent';
|
|
@@ -32,7 +33,10 @@ const StoreInitialization = memo(() => {
|
|
|
32
33
|
|
|
33
34
|
const { serverConfig } = useServerConfigStore();
|
|
34
35
|
|
|
35
|
-
const useInitSystemStatus = useGlobalStore((s) =>
|
|
36
|
+
const [useInitSystemStatus, useCheckServerVersion] = useGlobalStore((s) => [
|
|
37
|
+
s.useInitSystemStatus,
|
|
38
|
+
s.useCheckServerVersion,
|
|
39
|
+
]);
|
|
36
40
|
|
|
37
41
|
const useInitBuiltinAgent = useAgentStore((s) => s.useInitBuiltinAgent);
|
|
38
42
|
const useInitAiProviderKeyVaults = useAiInfraStore((s) => s.useFetchAiProviderRuntimeState);
|
|
@@ -41,6 +45,9 @@ const StoreInitialization = memo(() => {
|
|
|
41
45
|
// init the system preference
|
|
42
46
|
useInitSystemStatus();
|
|
43
47
|
|
|
48
|
+
// check server version in desktop app
|
|
49
|
+
useCheckServerVersion(isDesktop);
|
|
50
|
+
|
|
44
51
|
// fetch server config
|
|
45
52
|
const useFetchServerConfig = useServerConfigStore((s) => s.useInitServerConfig);
|
|
46
53
|
useFetchServerConfig();
|
|
@@ -7,6 +7,7 @@ import { ReferralProvider } from '@/business/client/ReferralProvider';
|
|
|
7
7
|
import { LobeAnalyticsProviderWrapper } from '@/components/Analytics/LobeAnalyticsProviderWrapper';
|
|
8
8
|
import { DragUploadProvider } from '@/components/DragUploadZone/DragUploadProvider';
|
|
9
9
|
import { getServerFeatureFlagsValue } from '@/config/featureFlags';
|
|
10
|
+
import { isDesktop } from '@/const/version';
|
|
10
11
|
import { appEnv } from '@/envs/app';
|
|
11
12
|
import DevPanel from '@/features/DevPanel';
|
|
12
13
|
import { getServerGlobalConfig } from '@/server/globalConfig';
|
|
@@ -20,6 +21,7 @@ import ImportSettings from './ImportSettings';
|
|
|
20
21
|
import Locale from './Locale';
|
|
21
22
|
import NextThemeProvider from './NextThemeProvider';
|
|
22
23
|
import QueryProvider from './Query';
|
|
24
|
+
import ServerVersionOutdatedAlert from './ServerVersionOutdatedAlert';
|
|
23
25
|
import StoreInitialization from './StoreInitialization';
|
|
24
26
|
import StyleRegistry from './StyleRegistry';
|
|
25
27
|
|
|
@@ -66,6 +68,8 @@ const GlobalLayout = async ({
|
|
|
66
68
|
>
|
|
67
69
|
<QueryProvider>
|
|
68
70
|
<StoreInitialization />
|
|
71
|
+
|
|
72
|
+
{isDesktop && <ServerVersionOutdatedAlert />}
|
|
69
73
|
<FaviconProvider>
|
|
70
74
|
<GroupWizardProvider>
|
|
71
75
|
<DragUploadProvider>
|
|
@@ -350,6 +350,13 @@ export default {
|
|
|
350
350
|
'run': 'Run',
|
|
351
351
|
'save': 'Save',
|
|
352
352
|
'send': 'Send',
|
|
353
|
+
'serverVersionOutdated.desc':
|
|
354
|
+
'Your client version (v{{version}}) requires a newer server version.',
|
|
355
|
+
'serverVersionOutdated.dismiss': 'Continue Anyway',
|
|
356
|
+
'serverVersionOutdated.title': 'Server Version Outdated',
|
|
357
|
+
'serverVersionOutdated.upgrade': 'Upgrade Guide',
|
|
358
|
+
'serverVersionOutdated.warning':
|
|
359
|
+
'Some features may not work properly or behave unexpectedly. Please update your server for the best experience.',
|
|
353
360
|
'setting': 'Settings',
|
|
354
361
|
'share': 'Share',
|
|
355
362
|
'stop': 'Stop',
|
|
@@ -401,6 +408,7 @@ export default {
|
|
|
401
408
|
'upgradeVersion.action': 'Upgrade',
|
|
402
409
|
'upgradeVersion.hasNew': 'Update available',
|
|
403
410
|
'upgradeVersion.newVersion': 'Update available: {{version}}',
|
|
411
|
+
'upgradeVersion.serverVersion': 'Server: {{version}}',
|
|
404
412
|
'userPanel.anonymousNickName': 'Anonymous User',
|
|
405
413
|
'userPanel.billing': 'Billing Management',
|
|
406
414
|
'userPanel.cloud': 'Launch {{name}}',
|
package/src/services/global.ts
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import type { PartialDeep } from 'type-fest';
|
|
2
2
|
|
|
3
|
+
import type { VersionResponseData } from '@/app/(backend)/api/version/route';
|
|
3
4
|
import { BusinessGlobalService } from '@/business/client/services/BusinessGlobalService';
|
|
4
5
|
import { lambdaClient } from '@/libs/trpc/client';
|
|
5
6
|
import { type LobeAgentConfig } from '@/types/agent';
|
|
6
7
|
import { type GlobalRuntimeConfig } from '@/types/serverConfig';
|
|
7
8
|
|
|
8
9
|
const VERSION_URL = 'https://registry.npmmirror.com/@lobehub/chat/latest';
|
|
10
|
+
const SERVER_VERSION_URL = '/api/version';
|
|
9
11
|
|
|
10
12
|
class GlobalService extends BusinessGlobalService {
|
|
11
13
|
/**
|
|
@@ -18,6 +20,29 @@ class GlobalService extends BusinessGlobalService {
|
|
|
18
20
|
return data['version'];
|
|
19
21
|
};
|
|
20
22
|
|
|
23
|
+
/**
|
|
24
|
+
* get server version from /api/version
|
|
25
|
+
* @returns version string if available, null only if server returns 404 (API doesn't exist on old server)
|
|
26
|
+
* @throws Error for other failures (network errors, 500s, etc.) to allow SWR retry
|
|
27
|
+
*/
|
|
28
|
+
getServerVersion = async (): Promise<string | null> => {
|
|
29
|
+
const res = await fetch(SERVER_VERSION_URL);
|
|
30
|
+
|
|
31
|
+
// Only treat 404 as "server doesn't support version API"
|
|
32
|
+
// Other errors (500, network issues) should throw to allow retry
|
|
33
|
+
if (res.status === 404) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (!res.ok) {
|
|
38
|
+
throw new Error(`Failed to fetch server version: ${res.status}`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const data: VersionResponseData = await res.json();
|
|
42
|
+
|
|
43
|
+
return data.version;
|
|
44
|
+
};
|
|
45
|
+
|
|
21
46
|
getGlobalConfig = async (): Promise<GlobalRuntimeConfig> => {
|
|
22
47
|
return lambdaClient.config.getGlobalConfig.query();
|
|
23
48
|
};
|
|
@@ -23,6 +23,7 @@ export interface GlobalGeneralAction {
|
|
|
23
23
|
updateResourceManagerColumnWidth: (column: 'name' | 'date' | 'size', width: number) => void;
|
|
24
24
|
updateSystemStatus: (status: Partial<SystemStatus>, action?: any) => void;
|
|
25
25
|
useCheckLatestVersion: (enabledCheck?: boolean) => SWRResponse<string>;
|
|
26
|
+
useCheckServerVersion: (enabledCheck?: boolean) => SWRResponse<string | null>;
|
|
26
27
|
useInitSystemStatus: () => SWRResponse;
|
|
27
28
|
}
|
|
28
29
|
|
|
@@ -160,6 +161,51 @@ export const generalActionSlice: StateCreator<
|
|
|
160
161
|
},
|
|
161
162
|
),
|
|
162
163
|
|
|
164
|
+
useCheckServerVersion: (enabledCheck = true) =>
|
|
165
|
+
useOnlyFetchOnceSWR(
|
|
166
|
+
enabledCheck ? 'checkServerVersion' : null,
|
|
167
|
+
async () => globalService.getServerVersion(),
|
|
168
|
+
{
|
|
169
|
+
onSuccess: (data: string | null) => {
|
|
170
|
+
if (data === null) {
|
|
171
|
+
set({ isServerVersionOutdated: true }, false);
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
set({ serverVersion: data }, false);
|
|
176
|
+
|
|
177
|
+
if (!valid(CURRENT_VERSION) || !valid(data)) return;
|
|
178
|
+
|
|
179
|
+
const clientVersion = parse(CURRENT_VERSION);
|
|
180
|
+
const serverVersion = parse(data);
|
|
181
|
+
|
|
182
|
+
if (!clientVersion || !serverVersion) return;
|
|
183
|
+
|
|
184
|
+
const DIFF_THRESHOLD = 5;
|
|
185
|
+
// įæŦåˇŽåŧ莥įŽč§å
|
|
186
|
+
// âââââââââââââââââââŦâââââââââŦââââââââââ
|
|
187
|
+
// â åŽĸæˇį̝ â æåĄį̝ â åˇŽåŧåŧ â įģæ â
|
|
188
|
+
// âââââââââââââââââââŧâââââââââŧââââââââââ¤
|
|
189
|
+
// â 1.0.5 â 1.0.0 â 5 â â ī¸ čŋæ§ â
|
|
190
|
+
// âââââââââââââââââââŧâââââââââŧââââââââââ¤
|
|
191
|
+
// â 1.1.0 â 1.0.5 â 5 â â ī¸ čŋæ§ â
|
|
192
|
+
// âââââââââââââââââââŧâââââââââŧââââââââââ¤
|
|
193
|
+
// â 2.0.0 â 1.9.9 â 91 â â ī¸ čŋæ§ â
|
|
194
|
+
// âââââââââââââââââââŧâââââââââŧââââââââââ¤
|
|
195
|
+
// â 1.0.4 â 1.0.0 â 4 â â
æŖå¸¸ â
|
|
196
|
+
// âââââââââââââââââââ´âââââââââ´ââââââââââ
|
|
197
|
+
const versionDiff =
|
|
198
|
+
(clientVersion.major - serverVersion.major) * 100 +
|
|
199
|
+
(clientVersion.minor - serverVersion.minor) * 10 +
|
|
200
|
+
(clientVersion.patch - serverVersion.patch);
|
|
201
|
+
|
|
202
|
+
if (versionDiff >= DIFF_THRESHOLD) {
|
|
203
|
+
set({ isServerVersionOutdated: true }, false);
|
|
204
|
+
}
|
|
205
|
+
},
|
|
206
|
+
},
|
|
207
|
+
),
|
|
208
|
+
|
|
163
209
|
useInitSystemStatus: () =>
|
|
164
210
|
useOnlyFetchOnceSWR<SystemStatus>(
|
|
165
211
|
'initSystemStatus',
|
|
@@ -180,9 +180,18 @@ export interface GlobalState {
|
|
|
180
180
|
*/
|
|
181
181
|
initClientDBStage: DatabaseLoadingState;
|
|
182
182
|
isMobile?: boolean;
|
|
183
|
+
/**
|
|
184
|
+
* æåĄį̝įæŦčŋæ§īŧ䏿¯æ /api/version æĨåŖ
|
|
185
|
+
* éčĻæį¤ē፿ˇæ´æ°æåĄį̝
|
|
186
|
+
*/
|
|
187
|
+
isServerVersionOutdated?: boolean;
|
|
183
188
|
isStatusInit?: boolean;
|
|
184
189
|
latestVersion?: string;
|
|
185
190
|
navigate?: NavigateFunction;
|
|
191
|
+
/**
|
|
192
|
+
* æåĄį̝įæŦåˇīŧį¨äēæŖæĩåŽĸæˇį̝䏿åĄį̝įæŦæ¯åĻä¸č´
|
|
193
|
+
*/
|
|
194
|
+
serverVersion?: string;
|
|
186
195
|
sidebarKey: SidebarTabKey;
|
|
187
196
|
status: SystemStatus;
|
|
188
197
|
statusStorage: AsyncLocalStorage<SystemStatus>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isDesktop } from '@lobechat/const';
|
|
1
2
|
import { type Theme, css } from 'antd-style';
|
|
2
3
|
import { rgba } from 'polished';
|
|
3
4
|
|
|
@@ -16,4 +17,12 @@ export default ({ token }: { prefixCls: string; token: Theme }) => css`
|
|
|
16
17
|
background: ${rgba(token.colorBgLayout, 0.5)} !important;
|
|
17
18
|
backdrop-filter: blur(2px);
|
|
18
19
|
}
|
|
20
|
+
|
|
21
|
+
${isDesktop &&
|
|
22
|
+
css`
|
|
23
|
+
.${token.prefixCls}-modal-mask.${token.prefixCls}-modal-mask-blur {
|
|
24
|
+
background: ${rgba(token.colorBgLayout, 0.8)} !important;
|
|
25
|
+
backdrop-filter: none !important;
|
|
26
|
+
}
|
|
27
|
+
`}
|
|
19
28
|
`;
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { router } from './shared';
|
|
2
|
-
import { reembedRouter } from './reembed';
|
|
3
|
-
import { searchRouter } from './search';
|
|
4
|
-
import { toolsRouter } from './tools';
|
|
5
|
-
|
|
6
|
-
// Re-export searchUserMemories for potential external use
|
|
7
|
-
export { searchUserMemories } from './search';
|
|
8
|
-
|
|
9
|
-
export const userMemoriesRouter = router({
|
|
10
|
-
...reembedRouter._def.procedures,
|
|
11
|
-
...searchRouter._def.procedures,
|
|
12
|
-
tools: toolsRouter,
|
|
13
|
-
});
|