@lobehub/chat 0.161.11 → 0.161.13
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/.github/ISSUE_TEMPLATE/1_bug_report.yml +25 -16
- package/.github/ISSUE_TEMPLATE/1_bug_report_cn.yml +25 -17
- package/CHANGELOG.md +42 -0
- package/docs/self-hosting/environment-variables/model-provider.mdx +14 -0
- package/docs/self-hosting/environment-variables/model-provider.zh-CN.mdx +14 -0
- package/package.json +2 -1
- package/src/app/(loading)/Redirect.tsx +29 -17
- package/src/layout/AuthProvider/NoAuth/index.tsx +16 -0
- package/src/layout/AuthProvider/index.tsx +2 -1
- package/src/layout/GlobalProvider/index.tsx +32 -8
- package/src/middleware.ts +1 -10
- package/src/services/user/client.test.ts +2 -0
- package/src/services/user/client.ts +4 -0
- package/src/services/user/type.ts +0 -1
- package/src/store/user/slices/auth/selectors.ts +1 -0
- package/src/store/user/slices/common/action.test.ts +2 -1
- package/src/store/user/slices/common/action.ts +8 -1
- package/src/store/user/slices/common/initialState.ts +6 -0
- package/src/types/user/index.ts +2 -0
|
@@ -3,10 +3,29 @@ description: 'Report an bug'
|
|
|
3
3
|
title: '[Bug] '
|
|
4
4
|
labels: ['🐛 Bug']
|
|
5
5
|
body:
|
|
6
|
+
- type: dropdown
|
|
7
|
+
attributes:
|
|
8
|
+
label: '📦 Environment'
|
|
9
|
+
options:
|
|
10
|
+
- Unselected
|
|
11
|
+
- Official
|
|
12
|
+
- Official Preview
|
|
13
|
+
- Vercel / Zeabur / Sealos
|
|
14
|
+
- Docker
|
|
15
|
+
- Other
|
|
16
|
+
validations:
|
|
17
|
+
required: true
|
|
18
|
+
- type: input
|
|
19
|
+
attributes:
|
|
20
|
+
label: '📌 Version'
|
|
21
|
+
validations:
|
|
22
|
+
required: true
|
|
23
|
+
|
|
6
24
|
- type: dropdown
|
|
7
25
|
attributes:
|
|
8
26
|
label: '💻 Operating System'
|
|
9
27
|
options:
|
|
28
|
+
- Unselected
|
|
10
29
|
- Windows
|
|
11
30
|
- macOS
|
|
12
31
|
- Ubuntu
|
|
@@ -16,21 +35,11 @@ body:
|
|
|
16
35
|
- Other
|
|
17
36
|
validations:
|
|
18
37
|
required: true
|
|
19
|
-
- type: dropdown
|
|
20
|
-
attributes:
|
|
21
|
-
label: '📦 Environment'
|
|
22
|
-
options:
|
|
23
|
-
- Official Preview
|
|
24
|
-
- Vercel / Zeabur / Sealos
|
|
25
|
-
- Docker
|
|
26
|
-
- Other
|
|
27
|
-
validations:
|
|
28
|
-
required: true
|
|
29
|
-
|
|
30
38
|
- type: dropdown
|
|
31
39
|
attributes:
|
|
32
40
|
label: '🌐 Browser'
|
|
33
41
|
options:
|
|
42
|
+
- Unselected
|
|
34
43
|
- Chrome
|
|
35
44
|
- Edge
|
|
36
45
|
- Safari
|
|
@@ -41,17 +50,17 @@ body:
|
|
|
41
50
|
- type: textarea
|
|
42
51
|
attributes:
|
|
43
52
|
label: '🐛 Bug Description'
|
|
44
|
-
description: A clear and concise description of the bug.
|
|
53
|
+
description: A clear and concise description of the bug, if the above option is `Other`, please also explain in detail.
|
|
45
54
|
validations:
|
|
46
55
|
required: true
|
|
47
|
-
- type: textarea
|
|
48
|
-
attributes:
|
|
49
|
-
label: '🚦 Expected Behavior'
|
|
50
|
-
description: A clear and concise description of what you expected to happen.
|
|
51
56
|
- type: textarea
|
|
52
57
|
attributes:
|
|
53
58
|
label: '📷 Recurrence Steps'
|
|
54
59
|
description: A clear and concise description of how to recurrence.
|
|
60
|
+
- type: textarea
|
|
61
|
+
attributes:
|
|
62
|
+
label: '🚦 Expected Behavior'
|
|
63
|
+
description: A clear and concise description of what you expected to happen.
|
|
55
64
|
- type: textarea
|
|
56
65
|
attributes:
|
|
57
66
|
label: '📝 Additional Information'
|
|
@@ -5,33 +5,41 @@ labels: ['🐛 Bug']
|
|
|
5
5
|
body:
|
|
6
6
|
- type: dropdown
|
|
7
7
|
attributes:
|
|
8
|
-
label: '
|
|
8
|
+
label: '📦 部署环境'
|
|
9
9
|
options:
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
- Android
|
|
10
|
+
- 待选择
|
|
11
|
+
- Official
|
|
12
|
+
- Official Preview
|
|
13
|
+
- Vercel / Zeabur / Sealos
|
|
14
|
+
- Docker
|
|
16
15
|
- Other
|
|
17
16
|
validations:
|
|
18
17
|
required: true
|
|
18
|
+
- type: input
|
|
19
|
+
attributes:
|
|
20
|
+
label: '📌 软件版本'
|
|
21
|
+
validations:
|
|
22
|
+
required: true
|
|
19
23
|
|
|
20
24
|
- type: dropdown
|
|
21
25
|
attributes:
|
|
22
|
-
label: '
|
|
26
|
+
label: '💻 系统环境'
|
|
23
27
|
options:
|
|
24
|
-
-
|
|
25
|
-
-
|
|
26
|
-
-
|
|
28
|
+
- 待选择
|
|
29
|
+
- Windows
|
|
30
|
+
- macOS
|
|
31
|
+
- Ubuntu
|
|
32
|
+
- Other Linux
|
|
33
|
+
- iOS
|
|
34
|
+
- Android
|
|
27
35
|
- Other
|
|
28
36
|
validations:
|
|
29
37
|
required: true
|
|
30
|
-
|
|
31
38
|
- type: dropdown
|
|
32
39
|
attributes:
|
|
33
40
|
label: '🌐 浏览器'
|
|
34
41
|
options:
|
|
42
|
+
- 待选择
|
|
35
43
|
- Chrome
|
|
36
44
|
- Edge
|
|
37
45
|
- Safari
|
|
@@ -42,17 +50,17 @@ body:
|
|
|
42
50
|
- type: textarea
|
|
43
51
|
attributes:
|
|
44
52
|
label: '🐛 问题描述'
|
|
45
|
-
description:
|
|
53
|
+
description: 请提供一个清晰且简洁的问题描述,若上述选项为`Other`,也请详细说明。
|
|
46
54
|
validations:
|
|
47
55
|
required: true
|
|
48
|
-
- type: textarea
|
|
49
|
-
attributes:
|
|
50
|
-
label: '🚦 期望结果'
|
|
51
|
-
description: 请提供一个清晰且简洁的描述,说明您期望发生什么。
|
|
52
56
|
- type: textarea
|
|
53
57
|
attributes:
|
|
54
58
|
label: '📷 复现步骤'
|
|
55
59
|
description: 请提供一个清晰且简洁的描述,说明如何复现问题。
|
|
60
|
+
- type: textarea
|
|
61
|
+
attributes:
|
|
62
|
+
label: '🚦 期望结果'
|
|
63
|
+
description: 请提供一个清晰且简洁的描述,说明您期望发生什么。
|
|
56
64
|
- type: textarea
|
|
57
65
|
attributes:
|
|
58
66
|
label: '📝 补充信息'
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,48 @@
|
|
|
2
2
|
|
|
3
3
|
# Changelog
|
|
4
4
|
|
|
5
|
+
### [Version 0.161.13](https://github.com/lobehub/lobe-chat/compare/v0.161.12...v0.161.13)
|
|
6
|
+
|
|
7
|
+
<sup>Released on **2024-05-24**</sup>
|
|
8
|
+
|
|
9
|
+
<br/>
|
|
10
|
+
|
|
11
|
+
<details>
|
|
12
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
13
|
+
|
|
14
|
+
</details>
|
|
15
|
+
|
|
16
|
+
<div align="right">
|
|
17
|
+
|
|
18
|
+
[](#readme-top)
|
|
19
|
+
|
|
20
|
+
</div>
|
|
21
|
+
|
|
22
|
+
### [Version 0.161.12](https://github.com/lobehub/lobe-chat/compare/v0.161.11...v0.161.12)
|
|
23
|
+
|
|
24
|
+
<sup>Released on **2024-05-23**</sup>
|
|
25
|
+
|
|
26
|
+
#### ♻ Code Refactoring
|
|
27
|
+
|
|
28
|
+
- **misc**: Refactor the home redirect implement.
|
|
29
|
+
|
|
30
|
+
<br/>
|
|
31
|
+
|
|
32
|
+
<details>
|
|
33
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
34
|
+
|
|
35
|
+
#### Code refactoring
|
|
36
|
+
|
|
37
|
+
- **misc**: Refactor the home redirect implement, closes [#2626](https://github.com/lobehub/lobe-chat/issues/2626) ([ab4216e](https://github.com/lobehub/lobe-chat/commit/ab4216e))
|
|
38
|
+
|
|
39
|
+
</details>
|
|
40
|
+
|
|
41
|
+
<div align="right">
|
|
42
|
+
|
|
43
|
+
[](#readme-top)
|
|
44
|
+
|
|
45
|
+
</div>
|
|
46
|
+
|
|
5
47
|
### [Version 0.161.11](https://github.com/lobehub/lobe-chat/compare/v0.161.10...v0.161.11)
|
|
6
48
|
|
|
7
49
|
<sup>Released on **2024-05-23**</sup>
|
|
@@ -15,6 +15,13 @@ When deploying LobeChat, a rich set of environment variables related to model se
|
|
|
15
15
|
|
|
16
16
|
## OpenAI
|
|
17
17
|
|
|
18
|
+
### `ENABLED_OPENAI`
|
|
19
|
+
|
|
20
|
+
- Type:Optional
|
|
21
|
+
- Description:Enables OpenAI as a model provider by default, turns off the OpenAI service when set to `0`
|
|
22
|
+
- Default:`1`
|
|
23
|
+
- Example:`0`
|
|
24
|
+
|
|
18
25
|
### `OPENAI_API_KEY`
|
|
19
26
|
|
|
20
27
|
- Type: Required
|
|
@@ -189,6 +196,13 @@ If you need to use Azure OpenAI to provide model services, you can refer to the
|
|
|
189
196
|
|
|
190
197
|
## Ollama
|
|
191
198
|
|
|
199
|
+
### `ENABLED_OLLAMA`
|
|
200
|
+
|
|
201
|
+
- Type:Optional
|
|
202
|
+
- Description:Enables Ollama as a model provider by default, turns off the Ollama service when set to `0`
|
|
203
|
+
- Default:`1`
|
|
204
|
+
- Example:`0`
|
|
205
|
+
|
|
192
206
|
### `OLLAMA_PROXY_URL`
|
|
193
207
|
|
|
194
208
|
- Type: Required
|
|
@@ -15,6 +15,13 @@ LobeChat 在部署时提供了丰富的模型服务商相关的环境变量,
|
|
|
15
15
|
|
|
16
16
|
## OpenAI
|
|
17
17
|
|
|
18
|
+
### `ENABLED_OPENAI`
|
|
19
|
+
|
|
20
|
+
- 类型:可选
|
|
21
|
+
- 描述:默认启用 OpenAI 作为模型供应商,当设为 0 时关闭 OpenAI 服务
|
|
22
|
+
- 默认值:`1`
|
|
23
|
+
- 示例:`0`
|
|
24
|
+
|
|
18
25
|
### `OPENAI_API_KEY`
|
|
19
26
|
|
|
20
27
|
- 类型:必选
|
|
@@ -187,6 +194,13 @@ LobeChat 在部署时提供了丰富的模型服务商相关的环境变量,
|
|
|
187
194
|
|
|
188
195
|
## Ollama
|
|
189
196
|
|
|
197
|
+
### `ENABLED_OLLAMA`
|
|
198
|
+
|
|
199
|
+
- 类型:可选
|
|
200
|
+
- 描述:默认启用 Ollama 作为模型供应商,当设为 0 时关闭 Ollama 服务
|
|
201
|
+
- 默认值:`1`
|
|
202
|
+
- 示例:`0`
|
|
203
|
+
|
|
190
204
|
### `OLLAMA_PROXY_URL`
|
|
191
205
|
|
|
192
206
|
- 类型:必选
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lobehub/chat",
|
|
3
|
-
"version": "0.161.
|
|
3
|
+
"version": "0.161.13",
|
|
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",
|
|
@@ -158,6 +158,7 @@
|
|
|
158
158
|
"remark": "^14.0.3",
|
|
159
159
|
"remark-gfm": "^3.0.1",
|
|
160
160
|
"remark-html": "^15.0.2",
|
|
161
|
+
"resolve-accept-language": "^3.1.4",
|
|
161
162
|
"rtl-detect": "^1.1.2",
|
|
162
163
|
"semver": "^7.6.2",
|
|
163
164
|
"sharp": "^0.33.4",
|
|
@@ -3,35 +3,47 @@
|
|
|
3
3
|
import { useRouter } from 'next/navigation';
|
|
4
4
|
import { memo, useEffect } from 'react';
|
|
5
5
|
|
|
6
|
-
import { messageService } from '@/services/message';
|
|
7
|
-
import { sessionService } from '@/services/session';
|
|
8
6
|
import { useUserStore } from '@/store/user';
|
|
9
7
|
import { authSelectors } from '@/store/user/selectors';
|
|
10
8
|
|
|
11
|
-
const checkHasConversation = async () => {
|
|
12
|
-
const hasMessages = await messageService.hasMessages();
|
|
13
|
-
const hasAgents = await sessionService.hasSessions();
|
|
14
|
-
return hasMessages || hasAgents;
|
|
15
|
-
};
|
|
16
|
-
|
|
17
9
|
const Redirect = memo(() => {
|
|
18
10
|
const router = useRouter();
|
|
19
|
-
const isLogin = useUserStore(
|
|
11
|
+
const [isLogin, isLoaded, isUserStateInit, isUserHasConversation, isOnboard] = useUserStore(
|
|
12
|
+
(s) => [
|
|
13
|
+
authSelectors.isLogin(s),
|
|
14
|
+
authSelectors.isLoaded(s),
|
|
15
|
+
s.isUserStateInit,
|
|
16
|
+
s.isUserHasConversation,
|
|
17
|
+
s.isOnboard,
|
|
18
|
+
],
|
|
19
|
+
);
|
|
20
20
|
|
|
21
21
|
useEffect(() => {
|
|
22
|
+
// if user auth state is not ready, wait for loading
|
|
23
|
+
if (!isLoaded) return;
|
|
24
|
+
|
|
25
|
+
// this mean user is definitely not login
|
|
22
26
|
if (!isLogin) {
|
|
23
27
|
router.replace('/welcome');
|
|
24
28
|
return;
|
|
25
29
|
}
|
|
26
30
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
// if user state not init, wait for loading
|
|
32
|
+
if (!isUserStateInit) return;
|
|
33
|
+
|
|
34
|
+
// user need to onboard
|
|
35
|
+
if (!isOnboard) {
|
|
36
|
+
router.replace('/onboard');
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// finally check the conversation status
|
|
41
|
+
if (isUserHasConversation) {
|
|
42
|
+
router.replace('/chat');
|
|
43
|
+
} else {
|
|
44
|
+
router.replace('/welcome');
|
|
45
|
+
}
|
|
46
|
+
}, [isUserStateInit, isLoaded, isUserHasConversation, isOnboard, isLogin]);
|
|
35
47
|
|
|
36
48
|
return null;
|
|
37
49
|
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { PropsWithChildren, memo } from 'react';
|
|
4
|
+
import { createStoreUpdater } from 'zustand-utils';
|
|
5
|
+
|
|
6
|
+
import { useUserStore } from '@/store/user';
|
|
7
|
+
|
|
8
|
+
const NoAuthProvider = memo<PropsWithChildren>(({ children }) => {
|
|
9
|
+
const useStoreUpdater = createStoreUpdater(useUserStore);
|
|
10
|
+
|
|
11
|
+
useStoreUpdater('isLoaded', true);
|
|
12
|
+
|
|
13
|
+
return children;
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
export default NoAuthProvider;
|
|
@@ -4,13 +4,14 @@ import { authEnv } from '@/config/auth';
|
|
|
4
4
|
|
|
5
5
|
import Clerk from './Clerk';
|
|
6
6
|
import NextAuth from './NextAuth';
|
|
7
|
+
import NoAuth from './NoAuth';
|
|
7
8
|
|
|
8
9
|
const AuthProvider = ({ children }: PropsWithChildren) => {
|
|
9
10
|
if (authEnv.NEXT_PUBLIC_ENABLE_CLERK_AUTH) return <Clerk>{children}</Clerk>;
|
|
10
11
|
|
|
11
12
|
if (authEnv.NEXT_PUBLIC_ENABLE_NEXT_AUTH) return <NextAuth>{children}</NextAuth>;
|
|
12
13
|
|
|
13
|
-
return children
|
|
14
|
+
return <NoAuth>{children}</NoAuth>;
|
|
14
15
|
};
|
|
15
16
|
|
|
16
17
|
export default AuthProvider;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import dynamic from 'next/dynamic';
|
|
2
|
-
import { cookies } from 'next/headers';
|
|
3
|
-
import { FC,
|
|
2
|
+
import { cookies, headers } from 'next/headers';
|
|
3
|
+
import { FC, PropsWithChildren } from 'react';
|
|
4
|
+
import { resolveAcceptLanguage } from 'resolve-accept-language';
|
|
4
5
|
|
|
5
6
|
import { getDebugConfig } from '@/config/debug';
|
|
6
7
|
import { getServerFeatureFlagsValue } from '@/config/featureFlags';
|
|
@@ -10,6 +11,7 @@ import {
|
|
|
10
11
|
LOBE_THEME_NEUTRAL_COLOR,
|
|
11
12
|
LOBE_THEME_PRIMARY_COLOR,
|
|
12
13
|
} from '@/const/theme';
|
|
14
|
+
import { locales } from '@/locales/resources';
|
|
13
15
|
import { getServerGlobalConfig } from '@/server/globalConfig';
|
|
14
16
|
import { ServerConfigStoreProvider } from '@/store/serverConfig';
|
|
15
17
|
import { getAntdLocale } from '@/utils/locale';
|
|
@@ -31,11 +33,27 @@ if (process.env.NODE_ENV === 'development') {
|
|
|
31
33
|
}
|
|
32
34
|
}
|
|
33
35
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
36
|
+
const parserFallbackLang = () => {
|
|
37
|
+
/**
|
|
38
|
+
* The arguments are as follows:
|
|
39
|
+
*
|
|
40
|
+
* 1) The HTTP accept-language header.
|
|
41
|
+
* 2) The available locales (they must contain the default locale).
|
|
42
|
+
* 3) The default locale.
|
|
43
|
+
*/
|
|
44
|
+
let fallbackLang: string = resolveAcceptLanguage(
|
|
45
|
+
headers().get('accept-language') || '',
|
|
46
|
+
// Invalid locale identifier 'ar'. A valid locale should follow the BCP 47 'language-country' format.
|
|
47
|
+
locales.map((locale) => (locale === 'ar' ? 'ar-EG' : locale)),
|
|
48
|
+
'en-US',
|
|
49
|
+
);
|
|
50
|
+
// if match the ar-EG then fallback to ar
|
|
51
|
+
if (fallbackLang === 'ar-EG') fallbackLang = 'ar';
|
|
52
|
+
|
|
53
|
+
return fallbackLang;
|
|
54
|
+
};
|
|
37
55
|
|
|
38
|
-
const GlobalLayout = async ({ children }:
|
|
56
|
+
const GlobalLayout = async ({ children }: PropsWithChildren) => {
|
|
39
57
|
// get default theme config to use with ssr
|
|
40
58
|
const cookieStore = cookies();
|
|
41
59
|
const appearance = cookieStore.get(LOBE_THEME_APPEARANCE);
|
|
@@ -44,7 +62,13 @@ const GlobalLayout = async ({ children }: GlobalLayoutProps) => {
|
|
|
44
62
|
|
|
45
63
|
// get default locale config to use with ssr
|
|
46
64
|
const defaultLang = cookieStore.get(LOBE_LOCALE_COOKIE);
|
|
47
|
-
const
|
|
65
|
+
const fallbackLang = parserFallbackLang();
|
|
66
|
+
|
|
67
|
+
// if it's a new user, there's no cookie
|
|
68
|
+
// So we need to use the fallback language parsed by accept-language
|
|
69
|
+
const userLocale = defaultLang?.value || fallbackLang;
|
|
70
|
+
|
|
71
|
+
const antdLocale = await getAntdLocale(userLocale);
|
|
48
72
|
|
|
49
73
|
// get default feature flags to use with ssr
|
|
50
74
|
const serverFeatureFlags = getServerFeatureFlagsValue();
|
|
@@ -52,7 +76,7 @@ const GlobalLayout = async ({ children }: GlobalLayoutProps) => {
|
|
|
52
76
|
const isMobile = isMobileDevice();
|
|
53
77
|
return (
|
|
54
78
|
<StyleRegistry>
|
|
55
|
-
<Locale antdLocale={antdLocale} defaultLang={
|
|
79
|
+
<Locale antdLocale={antdLocale} defaultLang={userLocale}>
|
|
56
80
|
<AppTheme
|
|
57
81
|
defaultAppearance={appearance?.value}
|
|
58
82
|
defaultNeutralColor={neutralColor?.value as any}
|
package/src/middleware.ts
CHANGED
|
@@ -41,16 +41,7 @@ const nextAuthMiddleware = auth((req) => {
|
|
|
41
41
|
});
|
|
42
42
|
|
|
43
43
|
export default authEnv.NEXT_PUBLIC_ENABLE_CLERK_AUTH
|
|
44
|
-
?
|
|
45
|
-
clerkMiddleware((auth, request) => {
|
|
46
|
-
// if user is logged in and on the home page, redirect to chat
|
|
47
|
-
if (auth().userId && request.nextUrl.pathname === '/') {
|
|
48
|
-
request.nextUrl.pathname = '/chat';
|
|
49
|
-
return NextResponse.redirect(request.nextUrl);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
return NextResponse.next();
|
|
53
|
-
})
|
|
44
|
+
? clerkMiddleware()
|
|
54
45
|
: authEnv.NEXT_PUBLIC_ENABLE_NEXT_AUTH
|
|
55
46
|
? nextAuthMiddleware
|
|
56
47
|
: defaultMiddleware;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { DeepPartial } from 'utility-types';
|
|
2
2
|
|
|
3
3
|
import { MessageModel } from '@/database/client/models/message';
|
|
4
|
+
import { SessionModel } from '@/database/client/models/session';
|
|
4
5
|
import { UserModel } from '@/database/client/models/user';
|
|
5
6
|
import { GlobalSettings } from '@/types/settings';
|
|
6
7
|
import { UserInitializationState, UserPreference } from '@/types/user';
|
|
@@ -18,10 +19,13 @@ export class ClientService implements IUserService {
|
|
|
18
19
|
async getUserState(): Promise<UserInitializationState> {
|
|
19
20
|
const user = await UserModel.getUser();
|
|
20
21
|
const messageCount = await MessageModel.count();
|
|
22
|
+
const sessionCount = await SessionModel.count();
|
|
21
23
|
|
|
22
24
|
return {
|
|
23
25
|
avatar: user.avatar,
|
|
26
|
+
canEnablePWAGuide: messageCount >= 2,
|
|
24
27
|
canEnableTrace: messageCount >= 4,
|
|
28
|
+
hasConversation: messageCount > 0 || sessionCount > 0,
|
|
25
29
|
isOnboard: true,
|
|
26
30
|
preference: await this.preferenceStorage.getFromLocalStorage(),
|
|
27
31
|
settings: user.settings as GlobalSettings,
|
|
@@ -6,7 +6,6 @@ import { UserInitializationState, UserPreference } from '@/types/user';
|
|
|
6
6
|
export interface IUserService {
|
|
7
7
|
getUserState: () => Promise<UserInitializationState>;
|
|
8
8
|
resetUserSettings: () => Promise<any>;
|
|
9
|
-
updateAvatar: (avatar: string) => Promise<any>;
|
|
10
9
|
updatePreference: (preference: UserPreference) => Promise<any>;
|
|
11
10
|
updateUserSettings: (patch: DeepPartial<GlobalSettings>) => Promise<any>;
|
|
12
11
|
}
|
|
@@ -41,6 +41,7 @@ const isLogin = (s: UserStore) => {
|
|
|
41
41
|
};
|
|
42
42
|
|
|
43
43
|
export const authSelectors = {
|
|
44
|
+
isLoaded: (s: UserStore) => s.isLoaded,
|
|
44
45
|
isLogin,
|
|
45
46
|
isLoginWithAuth: (s: UserStore) => s.isSignedIn,
|
|
46
47
|
isLoginWithClerk: (s: UserStore): boolean => (s.isSignedIn && enableClerk) || false,
|
|
@@ -5,6 +5,7 @@ import { withSWR } from '~test-utils';
|
|
|
5
5
|
|
|
6
6
|
import { DEFAULT_PREFERENCE } from '@/const/user';
|
|
7
7
|
import { userService } from '@/services/user';
|
|
8
|
+
import { ClientService } from '@/services/user/client';
|
|
8
9
|
import { useUserStore } from '@/store/user';
|
|
9
10
|
import { preferenceSelectors } from '@/store/user/selectors';
|
|
10
11
|
import { GlobalServerConfig } from '@/types/serverConfig';
|
|
@@ -36,7 +37,7 @@ describe('createCommonSlice', () => {
|
|
|
36
37
|
const avatar = 'new-avatar';
|
|
37
38
|
|
|
38
39
|
const spyOn = vi.spyOn(result.current, 'refreshUserState');
|
|
39
|
-
const updateAvatarSpy = vi.spyOn(
|
|
40
|
+
const updateAvatarSpy = vi.spyOn(ClientService.prototype, 'updateAvatar');
|
|
40
41
|
|
|
41
42
|
await act(async () => {
|
|
42
43
|
await result.current.updateAvatar(avatar);
|
|
@@ -4,6 +4,7 @@ import type { StateCreator } from 'zustand/vanilla';
|
|
|
4
4
|
|
|
5
5
|
import { DEFAULT_PREFERENCE } from '@/const/user';
|
|
6
6
|
import { userService } from '@/services/user';
|
|
7
|
+
import { ClientService } from '@/services/user/client';
|
|
7
8
|
import type { UserStore } from '@/store/user';
|
|
8
9
|
import type { GlobalServerConfig } from '@/types/serverConfig';
|
|
9
10
|
import type { GlobalSettings } from '@/types/settings';
|
|
@@ -45,7 +46,9 @@ export const createCommonSlice: StateCreator<
|
|
|
45
46
|
await mutate(GET_USER_STATE_KEY);
|
|
46
47
|
},
|
|
47
48
|
updateAvatar: async (avatar) => {
|
|
48
|
-
|
|
49
|
+
const clientService = new ClientService();
|
|
50
|
+
|
|
51
|
+
await clientService.updateAvatar(avatar);
|
|
49
52
|
await get().refreshUserState();
|
|
50
53
|
},
|
|
51
54
|
|
|
@@ -89,8 +92,12 @@ export const createCommonSlice: StateCreator<
|
|
|
89
92
|
{
|
|
90
93
|
defaultSettings,
|
|
91
94
|
enabledNextAuth: serverConfig.enabledOAuthSSO,
|
|
95
|
+
isOnboard: data.isOnboard,
|
|
96
|
+
isShowPWAGuide: data.canEnablePWAGuide,
|
|
92
97
|
isUserCanEnableTrace: data.canEnableTrace,
|
|
98
|
+
isUserHasConversation: data.hasConversation,
|
|
93
99
|
isUserStateInit: true,
|
|
100
|
+
|
|
94
101
|
preference,
|
|
95
102
|
serverLanguageModel: serverConfig.languageModel,
|
|
96
103
|
settings: data.settings || {},
|
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
export interface CommonState {
|
|
2
|
+
isOnboard: boolean;
|
|
3
|
+
isShowPWAGuide: boolean;
|
|
2
4
|
isUserCanEnableTrace: boolean;
|
|
5
|
+
isUserHasConversation: boolean;
|
|
3
6
|
isUserStateInit: boolean;
|
|
4
7
|
}
|
|
5
8
|
|
|
6
9
|
export const initialCommonState: CommonState = {
|
|
10
|
+
isOnboard: false,
|
|
11
|
+
isShowPWAGuide: false,
|
|
7
12
|
isUserCanEnableTrace: false,
|
|
13
|
+
isUserHasConversation: false,
|
|
8
14
|
isUserStateInit: false,
|
|
9
15
|
};
|
package/src/types/user/index.ts
CHANGED
|
@@ -34,7 +34,9 @@ export interface UserPreference {
|
|
|
34
34
|
|
|
35
35
|
export interface UserInitializationState {
|
|
36
36
|
avatar?: string;
|
|
37
|
+
canEnablePWAGuide?: boolean;
|
|
37
38
|
canEnableTrace?: boolean;
|
|
39
|
+
hasConversation?: boolean;
|
|
38
40
|
isOnboard?: boolean;
|
|
39
41
|
preference: UserPreference;
|
|
40
42
|
settings: DeepPartial<GlobalSettings>;
|