@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
package/.env.example CHANGED
@@ -190,6 +190,7 @@ OPENAI_API_KEY=sk-xxxxxxxxx
190
190
 
191
191
 
192
192
  # NextAuth related configurations
193
+ # NEXT_PUBLIC_ENABLE_NEXT_AUTH=1
193
194
  # NEXT_AUTH_SECRET=
194
195
 
195
196
  # Auth0 configurations
package/CHANGELOG.md CHANGED
@@ -2,6 +2,56 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ### [Version 1.52.1](https://github.com/lobehub/lobe-chat/compare/v1.52.0...v1.52.1)
6
+
7
+ <sup>Released on **2025-02-08**</sup>
8
+
9
+ #### 🐛 Bug Fixes
10
+
11
+ - **misc**: Fix static relative issues.
12
+
13
+ <br/>
14
+
15
+ <details>
16
+ <summary><kbd>Improvements and Fixes</kbd></summary>
17
+
18
+ #### What's fixed
19
+
20
+ - **misc**: Fix static relative issues, closes [#5874](https://github.com/lobehub/lobe-chat/issues/5874) ([419977b](https://github.com/lobehub/lobe-chat/commit/419977b))
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
+
30
+ ## [Version 1.52.0](https://github.com/lobehub/lobe-chat/compare/v1.51.16...v1.52.0)
31
+
32
+ <sup>Released on **2025-02-08**</sup>
33
+
34
+ #### ✨ Features
35
+
36
+ - **misc**: Refactor the auth condition in Next Auth.
37
+
38
+ <br/>
39
+
40
+ <details>
41
+ <summary><kbd>Improvements and Fixes</kbd></summary>
42
+
43
+ #### What's improved
44
+
45
+ - **misc**: Refactor the auth condition in Next Auth, closes [#5866](https://github.com/lobehub/lobe-chat/issues/5866) ([e529108](https://github.com/lobehub/lobe-chat/commit/e529108))
46
+
47
+ </details>
48
+
49
+ <div align="right">
50
+
51
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
52
+
53
+ </div>
54
+
5
55
  ### [Version 1.51.16](https://github.com/lobehub/lobe-chat/compare/v1.51.15...v1.51.16)
6
56
 
7
57
  <sup>Released on **2025-02-07**</sup>
@@ -38,6 +38,7 @@ FROM base AS builder
38
38
  ARG USE_CN_MIRROR
39
39
  ARG NEXT_PUBLIC_BASE_PATH
40
40
  ARG NEXT_PUBLIC_SERVICE_MODE
41
+ ARG NEXT_PUBLIC_ENABLE_NEXT_AUTH
41
42
  ARG NEXT_PUBLIC_SENTRY_DSN
42
43
  ARG NEXT_PUBLIC_ANALYTICS_POSTHOG
43
44
  ARG NEXT_PUBLIC_POSTHOG_HOST
@@ -49,7 +50,7 @@ ARG NEXT_PUBLIC_UMAMI_WEBSITE_ID
49
50
  ENV NEXT_PUBLIC_BASE_PATH="${NEXT_PUBLIC_BASE_PATH}"
50
51
 
51
52
  ENV NEXT_PUBLIC_SERVICE_MODE="${NEXT_PUBLIC_SERVICE_MODE:-server}" \
52
- NEXT_PUBLIC_ENABLE_NEXT_AUTH="1" \
53
+ NEXT_PUBLIC_ENABLE_NEXT_AUTH="${NEXT_PUBLIC_ENABLE_NEXT_AUTH:-1}" \
53
54
  APP_URL="http://app.com" \
54
55
  DATABASE_DRIVER="node" \
55
56
  DATABASE_URL="postgres://postgres:password@localhost:5432/postgres" \
package/changelog/v1.json CHANGED
@@ -1,4 +1,22 @@
1
1
  [
2
+ {
3
+ "children": {
4
+ "fixes": [
5
+ "Fix static relative issues."
6
+ ]
7
+ },
8
+ "date": "2025-02-08",
9
+ "version": "1.52.1"
10
+ },
11
+ {
12
+ "children": {
13
+ "features": [
14
+ "Refactor the auth condition in Next Auth."
15
+ ]
16
+ },
17
+ "date": "2025-02-08",
18
+ "version": "1.52.0"
19
+ },
2
20
  {
3
21
  "children": {},
4
22
  "date": "2025-02-07",
package/next.config.ts CHANGED
@@ -179,6 +179,7 @@ const nextConfig: NextConfig = {
179
179
  ],
180
180
  // when external packages in dev mode with turbopack, this config will lead to bundle error
181
181
  serverExternalPackages: isProd ? ['@electric-sql/pglite'] : undefined,
182
+
182
183
  transpilePackages: ['pdfjs-dist', 'mermaid'],
183
184
 
184
185
  webpack(config) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/chat",
3
- "version": "1.51.16",
3
+ "version": "1.52.1",
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",
@@ -49,8 +49,9 @@ afterEach(() => {
49
49
  describe('UserBanner', () => {
50
50
  it('should render UserInfo and DataStatistics when auth is disabled', () => {
51
51
  act(() => {
52
- useUserStore.setState({ isSignedIn: false, enableAuth: () => false });
52
+ useUserStore.setState({ isSignedIn: false });
53
53
  });
54
+ enableAuth = false;
54
55
 
55
56
  render(<UserBanner />);
56
57
 
@@ -75,7 +76,7 @@ describe('UserBanner', () => {
75
76
 
76
77
  it('should render UserLoginOrSignup when user is not logged in with auth enabled', () => {
77
78
  act(() => {
78
- useUserStore.setState({ isSignedIn: false, enableAuth: () => true });
79
+ useUserStore.setState({ isSignedIn: false });
79
80
  });
80
81
  enableClerk = true;
81
82
 
@@ -1,7 +1,7 @@
1
1
  import { act, renderHook } from '@testing-library/react';
2
2
  import { describe, expect, it, vi } from 'vitest';
3
3
 
4
- import { ServerConfigStoreProvider } from '@/store/serverConfig';
4
+ import { ServerConfigStoreProvider } from '@/store/serverConfig/Provider';
5
5
  import { useUserStore } from '@/store/user';
6
6
 
7
7
  import { useCategory } from '../features/useCategory';
@@ -45,12 +45,10 @@ afterEach(() => {
45
45
  enableClerk = true;
46
46
  });
47
47
 
48
- // 目前对 enableAuth 的判定是在 useUserStore 中,所以需要 mock useUserStore
49
- // 类型定义: enableAuth: () => boolean
50
48
  describe('useCategory', () => {
51
49
  it('should return correct items when the user is logged in with authentication', () => {
52
50
  act(() => {
53
- useUserStore.setState({ isSignedIn: true, enableAuth: () => true });
51
+ useUserStore.setState({ isSignedIn: true });
54
52
  });
55
53
  enableAuth = true;
56
54
  enableClerk = false;
@@ -70,8 +68,9 @@ describe('useCategory', () => {
70
68
 
71
69
  it('should return correct items when the user is not logged in', () => {
72
70
  act(() => {
73
- useUserStore.setState({ isSignedIn: false, enableAuth: () => true });
71
+ useUserStore.setState({ isSignedIn: false });
74
72
  });
73
+ enableAuth = true;
75
74
 
76
75
  const { result } = renderHook(() => useCategory(), { wrapper });
77
76
 
@@ -88,9 +87,10 @@ describe('useCategory', () => {
88
87
 
89
88
  it('should handle settings for non-authenticated users', () => {
90
89
  act(() => {
91
- useUserStore.setState({ isSignedIn: false, enableAuth: () => false });
90
+ useUserStore.setState({ isSignedIn: false });
92
91
  });
93
92
  enableClerk = false;
93
+ enableAuth = false;
94
94
 
95
95
  const { result } = renderHook(() => useCategory(), { wrapper });
96
96
 
@@ -5,6 +5,7 @@ import { useRouter } from 'next/navigation';
5
5
  import { memo } from 'react';
6
6
  import { Flexbox } from 'react-layout-kit';
7
7
 
8
+ import { enableAuth, enableNextAuth } from '@/const/auth';
8
9
  import { isDeprecatedEdition } from '@/const/version';
9
10
  import DataStatistics from '@/features/User/DataStatistics';
10
11
  import UserInfo from '@/features/User/UserInfo';
@@ -15,11 +16,7 @@ import { authSelectors } from '@/store/user/selectors';
15
16
  const UserBanner = memo(() => {
16
17
  const router = useRouter();
17
18
  const isLoginWithAuth = useUserStore(authSelectors.isLoginWithAuth);
18
- const [enableAuth, signIn, enabledNextAuth] = useUserStore((s) => [
19
- authSelectors.enabledAuth(s),
20
- s.openLogin,
21
- authSelectors.enabledNextAuth(s),
22
- ]);
19
+ const [signIn] = useUserStore((s) => [s.openLogin]);
23
20
 
24
21
  return (
25
22
  <Flexbox gap={12} paddingBlock={8}>
@@ -38,7 +35,7 @@ const UserBanner = memo(() => {
38
35
  <UserLoginOrSignup
39
36
  onClick={() => {
40
37
  // If use NextAuth, call openLogin method directly
41
- if (enabledNextAuth) {
38
+ if (enableNextAuth) {
42
39
  signIn();
43
40
  return;
44
41
  }
@@ -12,6 +12,7 @@ import { useRouter } from 'next/navigation';
12
12
  import { useTranslation } from 'react-i18next';
13
13
 
14
14
  import { CellProps } from '@/components/Cell';
15
+ import { enableAuth } from '@/const/auth';
15
16
  import { LOBE_CHAT_CLOUD } from '@/const/branding';
16
17
  import { DOCUMENTS, FEEDBACK, OFFICIAL_URL, UTM_SOURCE } from '@/const/url';
17
18
  import { isServerMode } from '@/const/version';
@@ -27,10 +28,9 @@ export const useCategory = () => {
27
28
  const { canInstall, install } = usePWAInstall();
28
29
  const { t } = useTranslation(['common', 'setting', 'auth']);
29
30
  const { showCloudPromotion, hideDocs } = useServerConfigStore(featureFlagsSelectors);
30
- const [isLogin, isLoginWithAuth, enableAuth] = useUserStore((s) => [
31
+ const [isLogin, isLoginWithAuth] = useUserStore((s) => [
31
32
  authSelectors.isLogin(s),
32
33
  authSelectors.isLoginWithAuth(s),
33
- authSelectors.enabledAuth(s),
34
34
  ]);
35
35
 
36
36
  const profile: CellProps[] = [
@@ -37,5 +37,3 @@ const Page = async (props: DynamicLayoutProps) => {
37
37
  Page.displayName = 'Me';
38
38
 
39
39
  export default Page;
40
-
41
- export const dynamic = 'force-static';
@@ -6,15 +6,15 @@ import { memo } from 'react';
6
6
  import { useTranslation } from 'react-i18next';
7
7
 
8
8
  import Cell, { CellProps } from '@/components/Cell';
9
+ import { enableAuth } from '@/const/auth';
9
10
  import { isDeprecatedEdition } from '@/const/version';
10
11
  import { ProfileTabs } from '@/store/global/initialState';
11
12
  import { useUserStore } from '@/store/user';
12
13
  import { authSelectors } from '@/store/user/selectors';
13
14
 
14
15
  const Category = memo(() => {
15
- const [isLogin, enableAuth, isLoginWithClerk, signOut] = useUserStore((s) => [
16
+ const [isLogin, isLoginWithClerk, signOut] = useUserStore((s) => [
16
17
  authSelectors.isLogin(s),
17
- authSelectors.enabledAuth(s),
18
18
  authSelectors.isLoginWithClerk(s),
19
19
  s.logout,
20
20
  ]);
@@ -27,5 +27,3 @@ const Page = async (props: DynamicLayoutProps) => {
27
27
  Page.displayName = 'MeProfile';
28
28
 
29
29
  export default Page;
30
-
31
- export const dynamic = 'force-static';
@@ -27,5 +27,3 @@ const Page = async (props: DynamicLayoutProps) => {
27
27
  Page.displayName = 'MeSettings';
28
28
 
29
29
  export default Page;
30
-
31
- export const dynamic = 'force-static';
@@ -71,5 +71,3 @@ const Page = async (props: Props) => {
71
71
  Page.DisplayName = 'DiscoverSearch';
72
72
 
73
73
  export default Page;
74
-
75
- export const dynamic = 'force-static';
@@ -8,5 +8,3 @@ const MainLayout = ServerLayout({ Desktop, Mobile });
8
8
  MainLayout.displayName = 'MainLayout';
9
9
 
10
10
  export default MainLayout;
11
-
12
- export const dynamic = 'force-dynamic';
@@ -4,6 +4,7 @@ import { Form, type ItemGroup } from '@lobehub/ui';
4
4
  import { memo } from 'react';
5
5
  import { useTranslation } from 'react-i18next';
6
6
 
7
+ import { enableAuth } from '@/const/auth';
7
8
  import { FORM_STYLE } from '@/const/layoutTokens';
8
9
  import AvatarWithUpload from '@/features/AvatarWithUpload';
9
10
  import UserAvatar from '@/features/User/UserAvatar';
@@ -14,8 +15,7 @@ type SettingItemGroup = ItemGroup;
14
15
 
15
16
  const Client = memo<{ mobile?: boolean }>(() => {
16
17
  const [isLoginWithNextAuth] = useUserStore((s) => [authSelectors.isLoginWithNextAuth(s)]);
17
- const [enableAuth, nickname, username, userProfile] = useUserStore((s) => [
18
- s.enableAuth(),
18
+ const [nickname, username, userProfile] = useUserStore((s) => [
19
19
  userProfileSelectors.nickName(s),
20
20
  userProfileSelectors.username(s),
21
21
  userProfileSelectors.userProfile(s),
@@ -4,6 +4,7 @@ import Link from 'next/link';
4
4
  import { useTranslation } from 'react-i18next';
5
5
 
6
6
  import type { MenuProps } from '@/components/Menu';
7
+ import { enableAuth } from '@/const/auth';
7
8
  import { isDeprecatedEdition } from '@/const/version';
8
9
  import { ProfileTabs } from '@/store/global/initialState';
9
10
  import { useUserStore } from '@/store/user';
@@ -11,10 +12,7 @@ import { authSelectors } from '@/store/user/slices/auth/selectors';
11
12
 
12
13
  export const useCategory = () => {
13
14
  const { t } = useTranslation('auth');
14
- const [enableAuth, isLoginWithClerk] = useUserStore((s) => [
15
- authSelectors.enabledAuth(s),
16
- authSelectors.isLoginWithClerk(s),
17
- ]);
15
+ const [isLoginWithClerk] = useUserStore((s) => [authSelectors.isLoginWithClerk(s)]);
18
16
 
19
17
  const cateItems: MenuProps['items'] = [
20
18
  {
@@ -6,13 +6,12 @@ import { memo } from 'react';
6
6
  import { useTranslation } from 'react-i18next';
7
7
  import { Flexbox } from 'react-layout-kit';
8
8
 
9
+ import { enableAuth } from '@/const/auth';
9
10
  import { useActiveSettingsKey } from '@/hooks/useActiveTabKey';
10
11
  import { useQueryRoute } from '@/hooks/useQueryRoute';
11
12
  import { useShowMobileWorkspace } from '@/hooks/useShowMobileWorkspace';
12
13
  import { SettingsTabs } from '@/store/global/initialState';
13
14
  import { useSessionStore } from '@/store/session';
14
- import { useUserStore } from '@/store/user';
15
- import { authSelectors } from '@/store/user/selectors';
16
15
  import { mobileHeaderSticky } from '@/styles/mobileHeader';
17
16
 
18
17
  const Header = memo(() => {
@@ -22,7 +21,6 @@ const Header = memo(() => {
22
21
  const showMobileWorkspace = useShowMobileWorkspace();
23
22
  const activeSettingsKey = useActiveSettingsKey();
24
23
  const isSessionActive = useSessionStore((s) => !!s.activeId);
25
- const enableAuth = useUserStore(authSelectors.enabledAuth);
26
24
 
27
25
  const handleBackClick = () => {
28
26
  if (isSessionActive && showMobileWorkspace) {
@@ -9,5 +9,3 @@ const SettingsLayout = ServerLayout<LayoutProps>({ Desktop, Mobile });
9
9
  SettingsLayout.displayName = 'SettingsLayout';
10
10
 
11
11
  export default SettingsLayout;
12
-
13
- export const dynamic = 'force-static';
@@ -7,4 +7,3 @@ export const metadata: Metadata = {
7
7
  };
8
8
 
9
9
  export { default } from './loading';
10
-
@@ -217,8 +217,7 @@ export const getAuthConfig = () => {
217
217
  CLERK_WEBHOOK_SECRET: process.env.CLERK_WEBHOOK_SECRET,
218
218
 
219
219
  // Next Auth
220
- NEXT_PUBLIC_ENABLE_NEXT_AUTH:
221
- !!process.env.NEXT_AUTH_SECRET || process.env.NEXT_PUBLIC_ENABLE_NEXT_AUTH === '1',
220
+ NEXT_PUBLIC_ENABLE_NEXT_AUTH: process.env.NEXT_PUBLIC_ENABLE_NEXT_AUTH === '1',
222
221
  NEXT_AUTH_SSO_PROVIDERS: process.env.NEXT_AUTH_SSO_PROVIDERS,
223
222
  NEXT_AUTH_SECRET: process.env.NEXT_AUTH_SECRET,
224
223
  NEXT_AUTH_DEBUG: !!process.env.NEXT_AUTH_DEBUG,
package/src/const/auth.ts CHANGED
@@ -2,8 +2,7 @@ import { authEnv } from '@/config/auth';
2
2
 
3
3
  export const enableClerk = authEnv.NEXT_PUBLIC_ENABLE_CLERK_AUTH;
4
4
  export const enableNextAuth = authEnv.NEXT_PUBLIC_ENABLE_NEXT_AUTH;
5
- export const enableAuth =
6
- authEnv.NEXT_PUBLIC_ENABLE_CLERK_AUTH || authEnv.NEXT_PUBLIC_ENABLE_NEXT_AUTH;
5
+ export const enableAuth = enableClerk || enableNextAuth || false;
7
6
 
8
7
  export const LOBE_CHAT_AUTH_HEADER = 'X-lobe-chat-auth';
9
8
 
@@ -5,6 +5,7 @@ import { Flexbox } from 'react-layout-kit';
5
5
 
6
6
  import BrandWatermark from '@/components/BrandWatermark';
7
7
  import Menu from '@/components/Menu';
8
+ import { enableAuth, enableNextAuth } from '@/const/auth';
8
9
  import { isDeprecatedEdition } from '@/const/version';
9
10
  import { useUserStore } from '@/store/user';
10
11
  import { authSelectors } from '@/store/user/selectors';
@@ -19,12 +20,7 @@ import { useMenu } from './useMenu';
19
20
  const PanelContent = memo<{ closePopover: () => void }>(({ closePopover }) => {
20
21
  const router = useRouter();
21
22
  const isLoginWithAuth = useUserStore(authSelectors.isLoginWithAuth);
22
- const [openSignIn, signOut, enableAuth, enabledNextAuth] = useUserStore((s) => [
23
- s.openLogin,
24
- s.logout,
25
- s.enableAuth(),
26
- s.enabledNextAuth,
27
- ]);
23
+ const [openSignIn, signOut] = useUserStore((s) => [s.openLogin, s.logout]);
28
24
  const { mainItems, logoutItems } = useMenu();
29
25
 
30
26
  const handleSignIn = () => {
@@ -36,7 +32,7 @@ const PanelContent = memo<{ closePopover: () => void }>(({ closePopover }) => {
36
32
  signOut();
37
33
  closePopover();
38
34
  // NextAuth doesn't need to redirect to login page
39
- if (enabledNextAuth) return;
35
+ if (enableNextAuth) return;
40
36
  router.push('/login');
41
37
  };
42
38
 
@@ -21,6 +21,7 @@ import { useTranslation } from 'react-i18next';
21
21
  import { Flexbox } from 'react-layout-kit';
22
22
 
23
23
  import type { MenuProps } from '@/components/Menu';
24
+ import { enableAuth } from '@/const/auth';
24
25
  import { LOBE_CHAT_CLOUD } from '@/const/branding';
25
26
  import {
26
27
  DISCORD,
@@ -68,8 +69,7 @@ export const useMenu = () => {
68
69
  const hasNewVersion = useNewVersion();
69
70
  const { t } = useTranslation(['common', 'setting', 'auth']);
70
71
  const { showCloudPromotion, hideDocs } = useServerConfigStore(featureFlagsSelectors);
71
- const [enableAuth, isLogin, isLoginWithAuth] = useUserStore((s) => [
72
- authSelectors.enabledAuth(s),
72
+ const [isLogin, isLoginWithAuth] = useUserStore((s) => [
73
73
  authSelectors.isLogin(s),
74
74
  authSelectors.isLoginWithAuth(s),
75
75
  ]);
@@ -68,13 +68,12 @@ vi.mock('@/const/version', () => ({
68
68
  // 定义一个变量来存储 enableAuth 的值
69
69
  let enableAuth = true;
70
70
 
71
- beforeEach(() => {
72
- useUserStore.setState({ enableAuth: () => true });
73
- });
74
-
75
- afterEach(() => {
76
- enableAuth = true;
77
- });
71
+ // 模拟 @/const/auth 模块
72
+ vi.mock('@/const/auth', () => ({
73
+ get enableAuth() {
74
+ return enableAuth;
75
+ },
76
+ }));
78
77
 
79
78
  describe('PanelContent', () => {
80
79
  const closePopover = vi.fn();
@@ -1,7 +1,7 @@
1
1
  import { act, renderHook } from '@testing-library/react';
2
2
  import { describe, expect, it, vi } from 'vitest';
3
3
 
4
- import { ServerConfigStoreProvider } from '@/store/serverConfig';
4
+ import { ServerConfigStoreProvider } from '@/store/serverConfig/Provider';
5
5
  import { useUserStore } from '@/store/user';
6
6
 
7
7
  import { useMenu } from '../UserPanel/useMenu';
@@ -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 { enableNextAuth } from '@/const/auth';
8
9
  import { useIsMobile } from '@/hooks/useIsMobile';
9
10
  import { useEnabledDataSync } from '@/hooks/useSyncData';
10
11
  import { useAgentStore } from '@/store/agent';
@@ -37,10 +38,12 @@ const StoreInitialization = memo(() => {
37
38
  // init the system preference
38
39
  useInitSystemStatus();
39
40
 
41
+ // fetch server config
42
+ const useFetchServerConfig = useServerConfigStore((s) => s.useInitServerConfig);
43
+ useFetchServerConfig();
44
+
40
45
  // Update NextAuth status
41
46
  const useUserStoreUpdater = createStoreUpdater(useUserStore);
42
- const enableNextAuth = useServerConfigStore(serverConfigSelectors.enabledOAuthSSO);
43
- useUserStoreUpdater('enabledNextAuth', enableNextAuth);
44
47
  const oAuthSSOProviders = useServerConfigStore(serverConfigSelectors.oAuthSSOProviders);
45
48
  useUserStoreUpdater('oAuthSSOProviders', oAuthSSOProviders);
46
49
 
@@ -4,7 +4,7 @@ import { appEnv } from '@/config/app';
4
4
  import { getServerFeatureFlagsValue } from '@/config/featureFlags';
5
5
  import DevPanel from '@/features/DevPanel';
6
6
  import { getServerGlobalConfig } from '@/server/globalConfig';
7
- import { ServerConfigStoreProvider } from '@/store/serverConfig';
7
+ import { ServerConfigStoreProvider } from '@/store/serverConfig/Provider';
8
8
  import { getAntdLocale } from '@/utils/locale';
9
9
 
10
10
  import AntdV5MonkeyPatch from './AntdV5MonkeyPatch';
@@ -37,7 +37,7 @@ const GlobalLayout = async ({
37
37
 
38
38
  // get default feature flags to use with ssr
39
39
  const serverFeatureFlags = getServerFeatureFlagsValue();
40
- const serverConfig = getServerGlobalConfig();
40
+ const serverConfig = await getServerGlobalConfig();
41
41
  return (
42
42
  <StyleRegistry>
43
43
  <Locale antdLocale={antdLocale} defaultLang={userLocale}>
package/src/middleware.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server';
2
2
  import { NextRequest, NextResponse } from 'next/server';
3
3
  import { UAParser } from 'ua-parser-js';
4
- import urlJoin from 'url-join';
5
4
 
6
5
  import { authEnv } from '@/config/auth';
7
6
  import { LOBE_THEME_APPEARANCE } from '@/const/theme';
@@ -84,23 +83,21 @@ const defaultMiddleware = (request: NextRequest) => {
84
83
  if (['/api', '/trpc', '/webapi'].some((path) => url.pathname.startsWith(path)))
85
84
  return NextResponse.next();
86
85
 
87
- // 处理 URL 重写
88
- // 构建新路径: /${route}${originalPathname}
89
- // 只对 GET 请求进行 URL 重写,确保其他类型的请求(包括 OPTIONS)不受影响
90
- const nextPathname = `/${urlJoin(route, url.pathname)}`;
86
+ // refs: https://github.com/lobehub/lobe-chat/pull/5866
87
+ // new handle segment rewrite: /${route}${originalPathname}
88
+ // / -> /zh-CN__0__dark
89
+ // /discover -> /zh-CN__0__dark/discover
90
+ const nextPathname = `/${route}` + (url.pathname === '/' ? '' : url.pathname);
91
91
  console.log(`[rewrite] ${url.pathname} -> ${nextPathname}`);
92
+
92
93
  url.pathname = nextPathname;
93
94
 
94
- return NextResponse.rewrite(url);
95
+ return NextResponse.rewrite(url, { status: 200 });
95
96
  };
96
97
 
97
- const publicRoute = ['/', '/discover'];
98
-
99
98
  // Initialize an Edge compatible NextAuth middleware
100
99
  const nextAuthMiddleware = NextAuthEdge.auth((req) => {
101
100
  const response = defaultMiddleware(req);
102
- // skip the '/' route
103
- if (publicRoute.some((url) => req.nextUrl.pathname.startsWith(url))) return response;
104
101
 
105
102
  // Just check if session exists
106
103
  const session = req.auth;
@@ -37,7 +37,7 @@ describe('configRouter', () => {
37
37
  const response = await router.getGlobalConfig();
38
38
 
39
39
  // Assert
40
- const result = response.languageModel?.openai;
40
+ const result = response.serverConfig.languageModel?.openai;
41
41
 
42
42
  expect(result).toMatchSnapshot();
43
43
  process.env.OPENAI_MODEL_LIST = '';
@@ -49,7 +49,7 @@ describe('configRouter', () => {
49
49
 
50
50
  const response = await router.getGlobalConfig();
51
51
 
52
- const result = response.languageModel?.openai?.serverModelCards;
52
+ const result = response.serverConfig.languageModel?.openai?.serverModelCards;
53
53
 
54
54
  expect(result).toMatchSnapshot();
55
55
 
@@ -62,7 +62,7 @@ describe('configRouter', () => {
62
62
 
63
63
  const response = await router.getGlobalConfig();
64
64
 
65
- const result = response.languageModel?.openai?.serverModelCards;
65
+ const result = response.serverConfig.languageModel?.openai?.serverModelCards;
66
66
 
67
67
  expect(result?.find((s) => s.id === 'gpt-4-0125-preview')?.displayName).toEqual(
68
68
  'gpt-4-32k',
@@ -76,7 +76,7 @@ describe('configRouter', () => {
76
76
 
77
77
  const response = await router.getGlobalConfig();
78
78
 
79
- const result = response.languageModel?.openai?.serverModelCards;
79
+ const result = response.serverConfig.languageModel?.openai?.serverModelCards;
80
80
 
81
81
  expect(result?.find((r) => r.id === 'gpt-4')).toBeUndefined();
82
82
 
@@ -88,7 +88,7 @@ describe('configRouter', () => {
88
88
 
89
89
  const response = await router.getGlobalConfig();
90
90
 
91
- const result = response.languageModel?.openai?.serverModelCards;
91
+ const result = response.serverConfig.languageModel?.openai?.serverModelCards;
92
92
 
93
93
  const model = result?.find((o) => o.id === 'gpt-4-1106-preview');
94
94
 
@@ -102,7 +102,7 @@ describe('configRouter', () => {
102
102
 
103
103
  const response = await router.getGlobalConfig();
104
104
 
105
- const result = response.languageModel?.openai?.serverModelCards;
105
+ const result = response.serverConfig.languageModel?.openai?.serverModelCards;
106
106
 
107
107
  expect(result).toContainEqual({
108
108
  displayName: 'model1',
@@ -137,7 +137,7 @@ describe('configRouter', () => {
137
137
  const response = await router.getGlobalConfig();
138
138
 
139
139
  // Assert
140
- const result = response.languageModel?.openrouter;
140
+ const result = response.serverConfig.languageModel?.openrouter;
141
141
 
142
142
  expect(result).toMatchSnapshot();
143
143
 
@@ -1,12 +1,17 @@
1
+ import { getServerFeatureFlagsValue } from '@/config/featureFlags';
1
2
  import { publicProcedure, router } from '@/libs/trpc';
2
3
  import { getServerDefaultAgentConfig, getServerGlobalConfig } from '@/server/globalConfig';
4
+ import { GlobalRuntimeConfig } from '@/types/serverConfig';
3
5
 
4
6
  export const configRouter = router({
5
7
  getDefaultAgentConfig: publicProcedure.query(async () => {
6
8
  return getServerDefaultAgentConfig();
7
9
  }),
8
10
 
9
- getGlobalConfig: publicProcedure.query(async () => {
10
- return getServerGlobalConfig();
11
+ getGlobalConfig: publicProcedure.query(async (): Promise<GlobalRuntimeConfig> => {
12
+ const serverConfig = await getServerGlobalConfig();
13
+ const serverFeatureFlags = getServerFeatureFlagsValue();
14
+
15
+ return { serverConfig, serverFeatureFlags };
11
16
  }),
12
17
  });