@lobehub/chat 1.51.16 → 1.52.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 (46) hide show
  1. package/.env.example +1 -0
  2. package/CHANGELOG.md +50 -0
  3. package/Dockerfile.database +2 -1
  4. package/changelog/v1.json +18 -0
  5. package/next.config.ts +1 -0
  6. package/package.json +1 -1
  7. package/src/app/[variants]/(main)/(mobile)/me/(home)/__tests__/UserBanner.test.tsx +3 -2
  8. package/src/app/[variants]/(main)/(mobile)/me/(home)/__tests__/useCategory.test.tsx +6 -6
  9. package/src/app/[variants]/(main)/(mobile)/me/(home)/features/UserBanner.tsx +3 -6
  10. package/src/app/[variants]/(main)/(mobile)/me/(home)/features/useCategory.tsx +2 -2
  11. package/src/app/[variants]/(main)/(mobile)/me/(home)/page.tsx +0 -2
  12. package/src/app/[variants]/(main)/(mobile)/me/profile/features/Category.tsx +2 -2
  13. package/src/app/[variants]/(main)/(mobile)/me/profile/page.tsx +0 -2
  14. package/src/app/[variants]/(main)/(mobile)/me/settings/page.tsx +0 -2
  15. package/src/app/[variants]/(main)/discover/search/page.tsx +0 -2
  16. package/src/app/[variants]/(main)/layout.tsx +0 -2
  17. package/src/app/[variants]/(main)/profile/(home)/Client.tsx +2 -2
  18. package/src/app/[variants]/(main)/profile/hooks/useCategory.tsx +2 -4
  19. package/src/app/[variants]/(main)/settings/_layout/Mobile/Header.tsx +1 -3
  20. package/src/app/[variants]/(main)/settings/layout.ts +0 -2
  21. package/src/app/[variants]/page.tsx +0 -1
  22. package/src/config/auth.ts +1 -2
  23. package/src/const/auth.ts +1 -2
  24. package/src/features/User/UserPanel/PanelContent.tsx +3 -7
  25. package/src/features/User/UserPanel/useMenu.tsx +2 -2
  26. package/src/features/User/__tests__/PanelContent.test.tsx +6 -7
  27. package/src/features/User/__tests__/useMenu.test.tsx +1 -1
  28. package/src/layout/GlobalProvider/StoreInitialization.tsx +5 -2
  29. package/src/layout/GlobalProvider/index.tsx +2 -2
  30. package/src/middleware.ts +7 -10
  31. package/src/server/routers/edge/config/index.test.ts +7 -7
  32. package/src/server/routers/edge/config/index.ts +7 -2
  33. package/src/services/__tests__/global.test.ts +6 -3
  34. package/src/services/chat.ts +2 -1
  35. package/src/services/global.ts +2 -2
  36. package/src/store/serverConfig/action.ts +36 -0
  37. package/src/store/serverConfig/index.ts +0 -1
  38. package/src/store/serverConfig/store.test.ts +1 -1
  39. package/src/store/serverConfig/store.ts +16 -9
  40. package/src/store/user/slices/auth/action.test.ts +4 -2
  41. package/src/store/user/slices/auth/action.ts +2 -4
  42. package/src/store/user/slices/auth/initialState.ts +0 -1
  43. package/src/store/user/slices/auth/selectors.ts +5 -7
  44. package/src/store/user/slices/common/action.ts +0 -1
  45. package/src/types/serverConfig.ts +6 -0
  46. package/src/libs/agent-runtime/togetherai/__snapshots__/index.test.ts.snap +0 -2190
@@ -1,7 +1,7 @@
1
1
  import { Mock, beforeEach, describe, expect, it, vi } from 'vitest';
2
2
 
3
3
  import { edgeClient } from '@/libs/trpc/client';
4
- import { GlobalServerConfig } from '@/types/serverConfig';
4
+ import { GlobalRuntimeConfig } from '@/types/serverConfig';
5
5
 
6
6
  import { globalService } from '../global';
7
7
 
@@ -77,14 +77,17 @@ describe('GlobalService', () => {
77
77
  describe('ServerConfig', () => {
78
78
  it('should return the serverConfig when fetch is successful', async () => {
79
79
  // Arrange
80
- const mockConfig = { enabledOAuthSSO: true } as GlobalServerConfig;
80
+ const mockConfig = {
81
+ serverConfig: { enabledOAuthSSO: true },
82
+ serverFeatureFlags: {},
83
+ } as GlobalRuntimeConfig;
81
84
  vi.spyOn(edgeClient.config.getGlobalConfig, 'query').mockResolvedValue(mockConfig);
82
85
 
83
86
  // Act
84
87
  const config = await globalService.getGlobalConfig();
85
88
 
86
89
  // Assert
87
- expect(config).toEqual({ enabledOAuthSSO: true });
90
+ expect(config).toEqual(mockConfig);
88
91
  });
89
92
 
90
93
  it('should return the defaultAgentConfig when fetch is successful', async () => {
@@ -3,6 +3,7 @@ import { produce } from 'immer';
3
3
  import { merge } from 'lodash-es';
4
4
 
5
5
  import { DEFAULT_MODEL_PROVIDER_LIST } from '@/config/modelProviders';
6
+ import { enableAuth } from '@/const/auth';
6
7
  import { INBOX_GUIDE_SYSTEMROLE } from '@/const/guide';
7
8
  import { INBOX_SESSION_ID } from '@/const/session';
8
9
  import { DEFAULT_AGENT_CONFIG } from '@/const/settings';
@@ -515,7 +516,7 @@ class ChatService {
515
516
  * if enable login and not signed in, return unauthorized error
516
517
  */
517
518
  const userStore = useUserStore.getState();
518
- if (userStore.enableAuth() && !userStore.isSignedIn) {
519
+ if (enableAuth && !userStore.isSignedIn) {
519
520
  throw AgentRuntimeError.createError(ChatErrorType.InvalidAccessCode);
520
521
  }
521
522
 
@@ -2,7 +2,7 @@ import { DeepPartial } from 'utility-types';
2
2
 
3
3
  import { edgeClient } from '@/libs/trpc/client';
4
4
  import { LobeAgentConfig } from '@/types/agent';
5
- import { GlobalServerConfig } from '@/types/serverConfig';
5
+ import { GlobalRuntimeConfig } from '@/types/serverConfig';
6
6
 
7
7
  const VERSION_URL = 'https://registry.npmmirror.com/@lobehub/chat/latest';
8
8
 
@@ -17,7 +17,7 @@ class GlobalService {
17
17
  return data['version'];
18
18
  };
19
19
 
20
- getGlobalConfig = async (): Promise<GlobalServerConfig> => {
20
+ getGlobalConfig = async (): Promise<GlobalRuntimeConfig> => {
21
21
  return edgeClient.config.getGlobalConfig.query();
22
22
  };
23
23
 
@@ -0,0 +1,36 @@
1
+ import { SWRResponse } from 'swr';
2
+ import { StateCreator } from 'zustand/vanilla';
3
+
4
+ import { useOnlyFetchOnceSWR } from '@/libs/swr';
5
+ import { globalService } from '@/services/global';
6
+ import { GlobalRuntimeConfig } from '@/types/serverConfig';
7
+
8
+ import type { ServerConfigStore } from './store';
9
+
10
+ const FETCH_SERVER_CONFIG_KEY = 'FETCH_SERVER_CONFIG';
11
+ export interface ServerConfigAction {
12
+ useInitServerConfig: () => SWRResponse<GlobalRuntimeConfig>;
13
+ }
14
+
15
+ export const createServerConfigSlice: StateCreator<
16
+ ServerConfigStore,
17
+ [['zustand/devtools', never]],
18
+ [],
19
+ ServerConfigAction
20
+ > = (set) => ({
21
+ useInitServerConfig: () => {
22
+ return useOnlyFetchOnceSWR<GlobalRuntimeConfig>(
23
+ FETCH_SERVER_CONFIG_KEY,
24
+ () => globalService.getGlobalConfig(),
25
+ {
26
+ onSuccess: (data) => {
27
+ set(
28
+ { featureFlags: data.serverFeatureFlags, serverConfig: data.serverConfig },
29
+ false,
30
+ 'initServerConfig',
31
+ );
32
+ },
33
+ },
34
+ );
35
+ },
36
+ });
@@ -1,3 +1,2 @@
1
- export { ServerConfigStoreProvider } from './Provider';
2
1
  export { featureFlagsSelectors } from './selectors';
3
2
  export { useServerConfigStore } from './store';
@@ -21,7 +21,7 @@ describe('createServerConfigStore', () => {
21
21
  it('should initialize store with default state', () => {
22
22
  const store = createServerConfigStore();
23
23
 
24
- expect(store.getState()).toEqual({
24
+ expect(store.getState()).toMatchObject({
25
25
  featureFlags: DEFAULT_FEATURE_FLAGS,
26
26
  serverConfig: { telemetry: {}, aiProvider: {} },
27
27
  });
@@ -10,26 +10,33 @@ import { GlobalServerConfig } from '@/types/serverConfig';
10
10
  import { merge } from '@/utils/merge';
11
11
  import { StoreApiWithSelector } from '@/utils/zustand';
12
12
 
13
- const initialState: ServerConfigStore = {
13
+ import { ServerConfigAction, createServerConfigSlice } from './action';
14
+
15
+ interface ServerConfigState {
16
+ featureFlags: IFeatureFlags;
17
+ isMobile?: boolean;
18
+ serverConfig: GlobalServerConfig;
19
+ }
20
+
21
+ const initialState: ServerConfigState = {
14
22
  featureFlags: DEFAULT_FEATURE_FLAGS,
15
23
  serverConfig: { aiProvider: {}, telemetry: {} },
16
24
  };
17
25
 
18
26
  // =============== 聚合 createStoreFn ============ //
19
27
 
20
- export interface ServerConfigStore {
21
- featureFlags: IFeatureFlags;
22
- isMobile?: boolean;
23
- serverConfig: GlobalServerConfig;
24
- }
28
+ export interface ServerConfigStore extends ServerConfigState, ServerConfigAction {}
25
29
 
26
30
  type CreateStore = (
27
31
  initState: Partial<ServerConfigStore>,
28
32
  ) => StateCreator<ServerConfigStore, [['zustand/devtools', never]]>;
29
33
 
30
- const createStore: CreateStore = (runtimeState) => () => ({
31
- ...merge(initialState, runtimeState),
32
- });
34
+ const createStore: CreateStore =
35
+ (runtimeState) =>
36
+ (...params) => ({
37
+ ...merge(initialState, runtimeState),
38
+ ...createServerConfigSlice(...params),
39
+ });
33
40
 
34
41
  // =============== 实装 useStore ============ //
35
42
 
@@ -89,7 +89,7 @@ describe('createAuthSlice', () => {
89
89
  });
90
90
 
91
91
  it('should call next-auth signOut when NextAuth is enabled', async () => {
92
- useUserStore.setState({ enabledNextAuth: true });
92
+ enableNextAuth = true;
93
93
 
94
94
  const { result } = renderHook(() => useUserStore());
95
95
 
@@ -100,6 +100,7 @@ describe('createAuthSlice', () => {
100
100
  const { signOut } = await import('next-auth/react');
101
101
 
102
102
  expect(signOut).toHaveBeenCalled();
103
+ enableNextAuth = false;
103
104
  });
104
105
 
105
106
  it('should not call next-auth signOut when NextAuth is disabled', async () => {
@@ -143,7 +144,7 @@ describe('createAuthSlice', () => {
143
144
  });
144
145
 
145
146
  it('should call next-auth signIn when NextAuth is enabled', async () => {
146
- useUserStore.setState({ enabledNextAuth: true });
147
+ enableNextAuth = true;
147
148
 
148
149
  const { result } = renderHook(() => useUserStore());
149
150
 
@@ -154,6 +155,7 @@ describe('createAuthSlice', () => {
154
155
  const { signIn } = await import('next-auth/react');
155
156
 
156
157
  expect(signIn).toHaveBeenCalled();
158
+ enableNextAuth = false;
157
159
  });
158
160
  it('should not call next-auth signIn when NextAuth is disabled', async () => {
159
161
  const { result } = renderHook(() => useUserStore());
@@ -1,6 +1,6 @@
1
1
  import { StateCreator } from 'zustand/vanilla';
2
2
 
3
- import { enableClerk } from '@/const/auth';
3
+ import { enableAuth, enableClerk, enableNextAuth } from '@/const/auth';
4
4
 
5
5
  import { UserStore } from '../../store';
6
6
 
@@ -23,7 +23,7 @@ export const createAuthSlice: StateCreator<
23
23
  UserAuthAction
24
24
  > = (set, get) => ({
25
25
  enableAuth: () => {
26
- return enableClerk || get()?.enabledNextAuth || false;
26
+ return enableAuth;
27
27
  },
28
28
  logout: async () => {
29
29
  if (enableClerk) {
@@ -32,7 +32,6 @@ export const createAuthSlice: StateCreator<
32
32
  return;
33
33
  }
34
34
 
35
- const enableNextAuth = get().enabledNextAuth;
36
35
  if (enableNextAuth) {
37
36
  const { signOut } = await import('next-auth/react');
38
37
  signOut();
@@ -50,7 +49,6 @@ export const createAuthSlice: StateCreator<
50
49
  return;
51
50
  }
52
51
 
53
- const enableNextAuth = get().enabledNextAuth;
54
52
  if (enableNextAuth) {
55
53
  const { signIn } = await import('next-auth/react');
56
54
  // Check if only one provider is available
@@ -16,7 +16,6 @@ export interface UserAuthState {
16
16
  clerkSignIn?: (props?: SignInProps) => void;
17
17
  clerkSignOut?: SignOut;
18
18
  clerkUser?: UserResource;
19
- enabledNextAuth?: boolean;
20
19
  isLoaded?: boolean;
21
20
 
22
21
  isSignedIn?: boolean;
@@ -1,6 +1,6 @@
1
1
  import { t } from 'i18next';
2
2
 
3
- import { enableClerk } from '@/const/auth';
3
+ import { enableAuth, enableClerk, enableNextAuth } from '@/const/auth';
4
4
  import { BRANDING_NAME } from '@/const/branding';
5
5
  import { UserStore } from '@/store/user';
6
6
  import { LobeUser } from '@/types/user';
@@ -8,7 +8,7 @@ import { LobeUser } from '@/types/user';
8
8
  const DEFAULT_USERNAME = BRANDING_NAME;
9
9
 
10
10
  const nickName = (s: UserStore) => {
11
- if (!s.enableAuth()) return t('userPanel.defaultNickname', { ns: 'common' });
11
+ if (!enableAuth) return t('userPanel.defaultNickname', { ns: 'common' });
12
12
 
13
13
  if (s.isSignedIn) return s.user?.fullName || s.user?.username;
14
14
 
@@ -16,7 +16,7 @@ const nickName = (s: UserStore) => {
16
16
  };
17
17
 
18
18
  const username = (s: UserStore) => {
19
- if (!s.enableAuth()) return DEFAULT_USERNAME;
19
+ if (!enableAuth) return DEFAULT_USERNAME;
20
20
 
21
21
  if (s.isSignedIn) return s.user?.username;
22
22
 
@@ -36,17 +36,15 @@ export const userProfileSelectors = {
36
36
  */
37
37
  const isLogin = (s: UserStore) => {
38
38
  // 如果没有开启鉴权,说明不需要登录,默认是登录态
39
- if (!s.enableAuth()) return true;
39
+ if (!enableAuth) return true;
40
40
 
41
41
  return s.isSignedIn;
42
42
  };
43
43
 
44
44
  export const authSelectors = {
45
- enabledAuth: (s: UserStore): boolean => s.enableAuth(),
46
- enabledNextAuth: (s: UserStore): boolean => !!s.enabledNextAuth,
47
45
  isLoaded: (s: UserStore) => s.isLoaded,
48
46
  isLogin,
49
47
  isLoginWithAuth: (s: UserStore) => s.isSignedIn,
50
48
  isLoginWithClerk: (s: UserStore): boolean => (s.isSignedIn && enableClerk) || false,
51
- isLoginWithNextAuth: (s: UserStore): boolean => (s.isSignedIn && !!s.enabledNextAuth) || false,
49
+ isLoginWithNextAuth: (s: UserStore): boolean => (s.isSignedIn && !!enableNextAuth) || false,
52
50
  };
@@ -99,7 +99,6 @@ export const createCommonSlice: StateCreator<
99
99
  set(
100
100
  {
101
101
  defaultSettings,
102
- enabledNextAuth: serverConfig.enabledOAuthSSO,
103
102
  isOnboard: data.isOnboard,
104
103
  isShowPWAGuide: data.canEnablePWAGuide,
105
104
  isUserCanEnableTrace: data.canEnableTrace,
@@ -1,5 +1,6 @@
1
1
  import { DeepPartial } from 'utility-types';
2
2
 
3
+ import { IFeatureFlags } from '@/config/featureFlags';
3
4
  import { ChatModelCard } from '@/types/llm';
4
5
  import {
5
6
  GlobalLLMProviderKey,
@@ -38,3 +39,8 @@ export interface GlobalServerConfig {
38
39
  langfuse?: boolean;
39
40
  };
40
41
  }
42
+
43
+ export interface GlobalRuntimeConfig {
44
+ serverConfig: GlobalServerConfig;
45
+ serverFeatureFlags: IFeatureFlags;
46
+ }