@lobehub/chat 0.160.4 → 0.160.6

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 (73) hide show
  1. package/CHANGELOG.md +65 -0
  2. package/docs/self-hosting/environment-variables/analytics.mdx +12 -35
  3. package/docs/self-hosting/environment-variables/analytics.zh-CN.mdx +13 -34
  4. package/locales/ar/error.json +2 -0
  5. package/locales/bg-BG/error.json +2 -0
  6. package/locales/de-DE/error.json +2 -0
  7. package/locales/en-US/error.json +2 -0
  8. package/locales/es-ES/error.json +2 -0
  9. package/locales/fr-FR/error.json +2 -0
  10. package/locales/it-IT/error.json +2 -0
  11. package/locales/ja-JP/error.json +2 -0
  12. package/locales/ko-KR/error.json +2 -0
  13. package/locales/nl-NL/error.json +2 -0
  14. package/locales/pl-PL/error.json +2 -0
  15. package/locales/pt-BR/error.json +2 -0
  16. package/locales/ru-RU/error.json +2 -0
  17. package/locales/tr-TR/error.json +2 -0
  18. package/locales/vi-VN/error.json +2 -0
  19. package/locales/zh-CN/error.json +2 -0
  20. package/locales/zh-TW/error.json +2 -0
  21. package/package.json +3 -3
  22. package/src/app/(loading)/Redirect.tsx +8 -0
  23. package/src/app/(main)/chat/(workspace)/features/TelemetryNotification.tsx +42 -75
  24. package/src/app/(main)/market/_layout/Desktop/index.tsx +24 -20
  25. package/src/app/(main)/settings/llm/Azure/index.tsx +1 -0
  26. package/src/app/(main)/settings/llm/OpenAI/index.tsx +0 -1
  27. package/src/app/layout.tsx +3 -1
  28. package/src/app/trpc/edge/[trpc]/route.ts +1 -1
  29. package/src/components/Analytics/Clarity.tsx +28 -0
  30. package/src/components/Analytics/Google.tsx +2 -9
  31. package/src/components/Analytics/Plausible.tsx +7 -12
  32. package/src/components/Analytics/Posthog.tsx +9 -10
  33. package/src/components/Analytics/Umami.tsx +7 -6
  34. package/src/components/Analytics/Vercel.tsx +2 -4
  35. package/src/components/Analytics/index.tsx +29 -14
  36. package/src/components/FetchErrorNotification/Description.tsx +48 -0
  37. package/src/components/FetchErrorNotification/index.tsx +15 -0
  38. package/src/components/ModelProviderIcon/index.tsx +6 -1
  39. package/src/components/Notification/index.tsx +95 -0
  40. package/src/config/__tests__/analytics.test.ts +70 -0
  41. package/src/config/__tests__/client.test.ts +0 -6
  42. package/src/config/analytics.ts +148 -0
  43. package/src/config/client.ts +0 -35
  44. package/src/config/server/index.ts +1 -3
  45. package/src/config/server/provider.ts +1 -0
  46. package/src/database/client/models/__tests__/message.test.ts +2 -2
  47. package/src/database/client/models/message.ts +2 -2
  48. package/src/features/User/UserAvatar.tsx +4 -3
  49. package/src/locales/default/error.ts +3 -1
  50. package/src/middleware.ts +10 -1
  51. package/src/server/globalConfig/index.ts +2 -0
  52. package/src/server/routers/edge/config/__snapshots__/index.test.ts.snap +1 -0
  53. package/src/services/file/type.ts +2 -3
  54. package/src/services/message/client.test.ts +151 -23
  55. package/src/services/message/client.ts +9 -5
  56. package/src/services/message/type.ts +10 -3
  57. package/src/services/upload.ts +4 -4
  58. package/src/services/user/client.ts +17 -1
  59. package/src/services/user/type.ts +14 -0
  60. package/src/store/chat/slices/enchance/action.test.ts +4 -3
  61. package/src/store/chat/slices/enchance/action.ts +3 -2
  62. package/src/store/chat/slices/plugin/action.test.ts +3 -5
  63. package/src/store/chat/slices/plugin/action.ts +1 -1
  64. package/src/store/market/action.ts +8 -0
  65. package/src/store/user/slices/auth/selectors.ts +1 -1
  66. package/src/store/user/slices/preference/action.test.ts +4 -9
  67. package/src/store/user/slices/preference/action.ts +17 -20
  68. package/src/store/user/slices/settings/selectors/modelConfig.test.ts +29 -1
  69. package/src/store/user/slices/settings/selectors/modelConfig.ts +1 -1
  70. package/src/tools/dalle/Render/Item/Error.tsx +1 -1
  71. package/src/types/files.ts +33 -0
  72. package/src/config/server/analytics.ts +0 -32
  73. /package/src/types/{user.ts → user/index.ts} +0 -0
@@ -10,29 +10,33 @@ import Hero from './Hero';
10
10
 
11
11
  const Layout = ({ children, detail }: LayoutProps) => {
12
12
  return (
13
- <Flexbox
14
- height={'100%'}
15
- id={'lobe-market-container'}
16
- style={{ position: 'relative' }}
17
- width={'100%'}
18
- >
19
- <Header />
20
- <Flexbox height={'100%'} horizontal style={{ position: 'relative' }} width={'100%'}>
21
- <Flexbox
22
- align={'center'}
23
- flex={1}
24
- padding={16}
25
- style={{ overflowX: 'hidden', overflowY: 'scroll', position: 'relative' }}
26
- >
27
- <SafeSpacing />
28
- <Flexbox gap={16} style={{ maxWidth: MAX_WIDTH, position: 'relative', width: '100%' }}>
29
- <Hero />
30
- {children}
13
+ <>
14
+ <Flexbox
15
+ height={'100%'}
16
+ id={'lobe-market-container'}
17
+ style={{ position: 'relative' }}
18
+ width={'100%'}
19
+ >
20
+ <Header />
21
+ <Flexbox height={'100%'} horizontal style={{ position: 'relative' }} width={'100%'}>
22
+ <Flexbox
23
+ align={'center'}
24
+ flex={1}
25
+ padding={16}
26
+ style={{ overflowX: 'hidden', overflowY: 'scroll', position: 'relative' }}
27
+ >
28
+ <SafeSpacing />
29
+ <Flexbox gap={16} style={{ maxWidth: MAX_WIDTH, position: 'relative', width: '100%' }}>
30
+ <Hero />
31
+ {children}
32
+ </Flexbox>
31
33
  </Flexbox>
34
+ <DetailSidebar>{detail}</DetailSidebar>
32
35
  </Flexbox>
33
- <DetailSidebar>{detail}</DetailSidebar>
34
36
  </Flexbox>
35
- </Flexbox>
37
+ {/* ↓ cloud slot ↓ */}
38
+ {/* ↑ cloud slot ↑ */}
39
+ </>
36
40
  );
37
41
  };
38
42
 
@@ -95,6 +95,7 @@ const AzureOpenAIProvider = memo(() => {
95
95
  placeholder: t('azure.modelListPlaceholder'),
96
96
  }}
97
97
  provider={providerKey}
98
+ showBrowserRequest
98
99
  title={
99
100
  <Flexbox align={'center'} gap={8} horizontal>
100
101
  <Azure.Combine size={22} type={'color'}></Azure.Combine>
@@ -20,7 +20,6 @@ const OpenAIProvider = memo(() => {
20
20
  }
21
21
  }
22
22
  showApiKey={showOpenAIApiKey}
23
- showBrowserRequest
24
23
  title={<OpenAI.Combine size={24} />}
25
24
  />
26
25
  );
@@ -10,6 +10,8 @@ import AuthProvider from '@/layout/AuthProvider';
10
10
  import GlobalProvider from '@/layout/GlobalProvider';
11
11
  import { isMobileDevice } from '@/utils/responsive';
12
12
 
13
+ const inVercel = process.env.VERCEL === '1';
14
+
13
15
  type RootLayoutProps = {
14
16
  children: ReactNode;
15
17
  modal: ReactNode;
@@ -31,7 +33,7 @@ const RootLayout = async ({ children, modal }: RootLayoutProps) => {
31
33
  </AuthProvider>
32
34
  </GlobalProvider>
33
35
  <Analytics />
34
- <SpeedInsights />
36
+ {inVercel && <SpeedInsights />}
35
37
  </body>
36
38
  </html>
37
39
  );
@@ -18,7 +18,7 @@ const handler = (req: NextRequest) =>
18
18
 
19
19
  onError: ({ error, path }) => {
20
20
  pino.info(`Error in tRPC handler (edge) on path: ${path}`);
21
- pino.error(error);
21
+ console.error(error);
22
22
  },
23
23
 
24
24
  req,
@@ -0,0 +1,28 @@
1
+ 'use client';
2
+
3
+ import Script from 'next/script';
4
+ import { memo } from 'react';
5
+
6
+ interface ClarityProps {
7
+ projectId?: string;
8
+ }
9
+
10
+ const Clarity = memo<ClarityProps>(
11
+ ({ projectId }) =>
12
+ projectId && (
13
+ <Script
14
+ dangerouslySetInnerHTML={{
15
+ __html: `
16
+ (function(c,l,a,r,i,t,y){
17
+ c[a]=c[a]||function(){(c[a].q=c[a].q||[]).push(arguments)};
18
+ t=l.createElement(r);t.async=1;t.src="https://www.clarity.ms/tag/"+i;
19
+ y=l.getElementsByTagName(r)[0];y.parentNode.insertBefore(t,y);
20
+ })(window, document, "clarity", "script", "${projectId}");
21
+ `,
22
+ }}
23
+ id="clarity-script"
24
+ />
25
+ ),
26
+ );
27
+
28
+ export default Clarity;
@@ -1,14 +1,7 @@
1
1
  import { GoogleAnalytics as GA } from '@next/third-parties/google';
2
2
 
3
- import { getServerConfig } from '@/config/server';
3
+ import { analyticsEnv } from '@/config/analytics';
4
4
 
5
- const { GOOGLE_ANALYTICS_MEASUREMENT_ID, ENABLE_GOOGLE_ANALYTICS } = getServerConfig();
6
-
7
- if (ENABLE_GOOGLE_ANALYTICS && !GOOGLE_ANALYTICS_MEASUREMENT_ID)
8
- throw new Error(
9
- 'You have enable the google analytics but not provided the google analytics id. Please provide the google analytics id in your env',
10
- );
11
-
12
- const GoogleAnalytics = () => <GA gaId={GOOGLE_ANALYTICS_MEASUREMENT_ID!} />;
5
+ const GoogleAnalytics = () => <GA gaId={analyticsEnv.GOOGLE_ANALYTICS_MEASUREMENT_ID!} />;
13
6
 
14
7
  export default GoogleAnalytics;
@@ -3,19 +3,14 @@
3
3
  import Script from 'next/script';
4
4
  import { memo } from 'react';
5
5
 
6
- import { getClientConfig } from '@/config/client';
6
+ interface PlausibleAnalyticsProps {
7
+ domain?: string;
8
+ scriptBaseUrl: string;
9
+ }
7
10
 
8
- const { PLAUSIBLE_DOMAIN, PLAUSIBLE_SCRIPT_BASE_URL } = getClientConfig();
9
-
10
- const PlausibleAnalytics = memo(
11
- () =>
12
- PLAUSIBLE_DOMAIN && (
13
- <Script
14
- data-domain={PLAUSIBLE_DOMAIN}
15
- defer
16
- src={`${PLAUSIBLE_SCRIPT_BASE_URL}/js/script.js`}
17
- />
18
- ),
11
+ const PlausibleAnalytics = memo<PlausibleAnalyticsProps>(
12
+ ({ domain, scriptBaseUrl }) =>
13
+ domain && <Script data-domain={domain} defer src={`${scriptBaseUrl}/js/script.js`} />,
19
14
  );
20
15
 
21
16
  export default PlausibleAnalytics;
@@ -1,20 +1,19 @@
1
1
  'use client';
2
2
 
3
3
  import posthog from 'posthog-js';
4
- import { FC, memo, useEffect } from 'react';
4
+ import { memo, useEffect } from 'react';
5
5
 
6
- import { getClientConfig } from '@/config/client';
6
+ interface PostHogProps {
7
+ debug: boolean;
8
+ host: string;
9
+ token?: string;
10
+ }
7
11
 
8
- const { POSTHOG_HOST, POSTHOG_KEY, POSTHOG_DEBUG } = getClientConfig();
9
-
10
- const PostHog: FC = memo(() => {
12
+ const PostHog = memo<PostHogProps>(({ token, host, debug }) => {
11
13
  useEffect(() => {
12
- if (!POSTHOG_KEY) return;
14
+ if (!token) return;
13
15
 
14
- posthog.init(POSTHOG_KEY, {
15
- api_host: POSTHOG_HOST ?? 'https://app.posthog.com',
16
- debug: POSTHOG_DEBUG,
17
- });
16
+ posthog.init(token, { api_host: host, debug });
18
17
  }, []);
19
18
 
20
19
  return null;
@@ -3,13 +3,14 @@
3
3
  import Script from 'next/script';
4
4
  import { memo } from 'react';
5
5
 
6
- import { getClientConfig } from '@/config/client';
6
+ interface UmamiAnalyticsProps {
7
+ scriptUrl: string;
8
+ websiteId?: string;
9
+ }
7
10
 
8
- const { UMAMI_SCRIPT_URL, UMAMI_WEBSITE_ID } = getClientConfig();
9
-
10
- const UmamiAnalytics = memo(
11
- () =>
12
- UMAMI_WEBSITE_ID && <Script data-website-id={UMAMI_WEBSITE_ID} defer src={UMAMI_SCRIPT_URL} />,
11
+ const UmamiAnalytics = memo<UmamiAnalyticsProps>(
12
+ ({ scriptUrl, websiteId }) =>
13
+ websiteId && <Script data-website-id={websiteId} defer src={scriptUrl} />,
13
14
  );
14
15
 
15
16
  export default UmamiAnalytics;
@@ -1,10 +1,8 @@
1
1
  import { Analytics } from '@vercel/analytics/react';
2
2
  import { memo } from 'react';
3
3
 
4
- import { getServerConfig } from '@/config/server';
4
+ import { analyticsEnv } from '@/config/analytics';
5
5
 
6
- const { VERCEL_DEBUG } = getServerConfig();
7
-
8
- const VercelAnalytics = memo(() => <Analytics debug={VERCEL_DEBUG} />);
6
+ const VercelAnalytics = memo(() => <Analytics debug={analyticsEnv.DEBUG_VERCEL_ANALYTICS} />);
9
7
 
10
8
  export default VercelAnalytics;
@@ -1,27 +1,42 @@
1
1
  import dynamic from 'next/dynamic';
2
2
 
3
- import { getClientConfig } from '@/config/client';
4
- import { getServerConfig } from '@/config/server';
3
+ import { analyticsEnv } from '@/config/analytics';
5
4
 
6
5
  import Google from './Google';
7
6
  import Vercel from './Vercel';
8
7
 
9
- const Plausible = dynamic(() => import('./Plausible'), { ssr: false });
10
- const Posthog = dynamic(() => import('./Posthog'), { ssr: false });
11
- const Umami = dynamic(() => import('./Umami'), { ssr: false });
12
-
13
- const { ANALYTICS_POSTHOG, ANALYTICS_PLAUSIBLE, ANALYTICS_UMAMI } = getClientConfig();
14
-
15
- const { ENABLE_VERCEL_ANALYTICS, ENABLE_GOOGLE_ANALYTICS } = getServerConfig();
8
+ const Plausible = dynamic(() => import('./Plausible'));
9
+ const Posthog = dynamic(() => import('./Posthog'));
10
+ const Umami = dynamic(() => import('./Umami'));
11
+ const Clarity = dynamic(() => import('./Clarity'));
16
12
 
17
13
  const Analytics = () => {
18
14
  return (
19
15
  <>
20
- {ENABLE_VERCEL_ANALYTICS && <Vercel />}
21
- {ENABLE_GOOGLE_ANALYTICS && <Google />}
22
- {ANALYTICS_PLAUSIBLE && <Plausible />}
23
- {ANALYTICS_POSTHOG && <Posthog />}
24
- {ANALYTICS_UMAMI && <Umami />}
16
+ {analyticsEnv.ENABLE_VERCEL_ANALYTICS && <Vercel />}
17
+ {analyticsEnv.ENABLE_GOOGLE_ANALYTICS && <Google />}
18
+ {analyticsEnv.ENABLED_PLAUSIBLE_ANALYTICS && (
19
+ <Plausible
20
+ domain={analyticsEnv.PLAUSIBLE_DOMAIN}
21
+ scriptBaseUrl={analyticsEnv.PLAUSIBLE_SCRIPT_BASE_URL}
22
+ />
23
+ )}
24
+ {analyticsEnv.ENABLED_POSTHOG_ANALYTICS && (
25
+ <Posthog
26
+ debug={analyticsEnv.DEBUG_POSTHOG_ANALYTICS}
27
+ host={analyticsEnv.POSTHOG_HOST!}
28
+ token={analyticsEnv.POSTHOG_KEY}
29
+ />
30
+ )}
31
+ {analyticsEnv.ENABLED_UMAMI_ANALYTICS && (
32
+ <Umami
33
+ scriptUrl={analyticsEnv.UMAMI_SCRIPT_URL}
34
+ websiteId={analyticsEnv.UMAMI_WEBSITE_ID}
35
+ />
36
+ )}
37
+ {analyticsEnv.ENABLED_CLARITY_ANALYTICS && (
38
+ <Clarity projectId={analyticsEnv.CLARITY_PROJECT_ID} />
39
+ )}
25
40
  </>
26
41
  );
27
42
  };
@@ -0,0 +1,48 @@
1
+ import { Icon } from '@lobehub/ui';
2
+ import { Skeleton } from 'antd';
3
+ import { css, cx } from 'antd-style';
4
+ import { ChevronDown, ChevronUp } from 'lucide-react';
5
+ import dynamic from 'next/dynamic';
6
+ import { memo, useState } from 'react';
7
+ import { useTranslation } from 'react-i18next';
8
+ import { Flexbox } from 'react-layout-kit';
9
+
10
+ const container = css`
11
+ pre.shiki {
12
+ padding: 8px !important;
13
+ }
14
+ `;
15
+
16
+ const Highlighter = dynamic(() => import('@lobehub/ui/es/Highlighter'), {
17
+ loading: () => <Skeleton avatar={false} title={false} />,
18
+ ssr: false,
19
+ });
20
+
21
+ const Description = memo<{ message: string; status: number }>(({ message, status }) => {
22
+ const { t } = useTranslation('error');
23
+ const [show, setShow] = useState(false);
24
+ return (
25
+ <Flexbox gap={8}>
26
+ {t(`response.${status}` as any)}
27
+ <Flexbox
28
+ gap={4}
29
+ horizontal
30
+ onClick={() => {
31
+ setShow(!show);
32
+ }}
33
+ style={{ cursor: 'pointer', fontSize: 12 }}
34
+ >
35
+ {t('fetchErrorDetail')} <Icon icon={show ? ChevronUp : ChevronDown} />
36
+ </Flexbox>
37
+ <Highlighter
38
+ className={cx(container)}
39
+ language={'text'}
40
+ style={{ display: show ? undefined : 'none', maxHeight: 80 }}
41
+ >
42
+ {message}
43
+ </Highlighter>
44
+ </Flexbox>
45
+ );
46
+ });
47
+
48
+ export default Description;
@@ -0,0 +1,15 @@
1
+ import { t } from 'i18next';
2
+
3
+ import { notification } from '@/components/AntdStaticMethods';
4
+
5
+ import Description from './Description';
6
+
7
+ export const fetchErrorNotification = {
8
+ error: ({ status, errorMessage }: { errorMessage: string; status: number }) => {
9
+ notification.error({
10
+ description: <Description message={errorMessage} status={status} />,
11
+ message: t('fetchError', { ns: 'error' }),
12
+ type: 'error',
13
+ });
14
+ },
15
+ };
@@ -16,6 +16,7 @@ import {
16
16
  ZeroOne,
17
17
  Zhipu,
18
18
  } from '@lobehub/icons';
19
+ import { Logo } from '@lobehub/ui';
19
20
  import { memo } from 'react';
20
21
  import { Center } from 'react-layout-kit';
21
22
 
@@ -27,6 +28,10 @@ interface ModelProviderIconProps {
27
28
 
28
29
  const ModelProviderIcon = memo<ModelProviderIconProps>(({ provider }) => {
29
30
  switch (provider) {
31
+ case 'lobehub': {
32
+ return <Logo size={20} />;
33
+ }
34
+
30
35
  case ModelProvider.ZhiPu: {
31
36
  return <Zhipu size={20} />;
32
37
  }
@@ -38,7 +43,7 @@ const ModelProviderIcon = memo<ModelProviderIconProps>(({ provider }) => {
38
43
  case ModelProvider.DeepSeek: {
39
44
  return <DeepSeek size={20} />;
40
45
  }
41
-
46
+
42
47
  case ModelProvider.Google: {
43
48
  return (
44
49
  <Center height={20} width={20}>
@@ -0,0 +1,95 @@
1
+ 'use client';
2
+
3
+ import { ActionIcon } from '@lobehub/ui';
4
+ import { createStyles } from 'antd-style';
5
+ import { XIcon } from 'lucide-react';
6
+ import { rgba } from 'polished';
7
+ import { memo } from 'react';
8
+ import { Flexbox, FlexboxProps } from 'react-layout-kit';
9
+
10
+ const useStyles = createStyles(({ css, token, isDarkMode }) => ({
11
+ cancelIcon: css`
12
+ position: absolute;
13
+ z-index: 100;
14
+ top: 8px;
15
+ right: 8px;
16
+ `,
17
+ container: css`
18
+ position: absolute;
19
+ z-index: 1100;
20
+ bottom: 16px;
21
+ inset-inline-end: 20px;
22
+
23
+ overflow: hidden;
24
+
25
+ background: ${token.colorBgContainer};
26
+ border: 1px solid ${token.colorSplit};
27
+ border-radius: 8px;
28
+ box-shadow: ${token.boxShadowSecondary};
29
+ `,
30
+ mobileContainer: css`
31
+ bottom: 8px;
32
+ inset-inline-start: 8px;
33
+ `,
34
+ wrapper: css`
35
+ background: linear-gradient(
36
+ 180deg,
37
+ ${rgba(token.colorBgContainer, 0)},
38
+ ${token.colorBgContainer} ${isDarkMode ? '80' : '140'}px
39
+ ),
40
+ url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24'%3E%3Cg fill='${token.colorFillTertiary}' %3E %3Cpolygon fill-rule='evenodd' points='8 4 12 6 8 8 6 12 4 8 0 6 4 4 6 0 8 4'/%3E%3C/g%3E%3C/svg%3E");
41
+ `,
42
+ }));
43
+
44
+ interface NotificationProps extends FlexboxProps {
45
+ height?: number | string;
46
+ mobile?: boolean;
47
+ onCancel?: (show?: boolean) => void;
48
+ show: boolean;
49
+ showCloseIcon?: boolean;
50
+ width?: number | string;
51
+ wrapper?: FlexboxProps;
52
+ }
53
+
54
+ const Notification = memo<NotificationProps>(
55
+ ({
56
+ mobile,
57
+ children,
58
+ show,
59
+ onCancel,
60
+ showCloseIcon = true,
61
+ width = 422,
62
+ height = 'auto',
63
+ wrapper = {},
64
+ className,
65
+ ...rest
66
+ }) => {
67
+ const { styles, cx } = useStyles();
68
+ const { className: wrapperClassName, ...restWrapper } = wrapper;
69
+ return (
70
+ show && (
71
+ <Flexbox
72
+ className={cx(styles.container, mobile && styles.mobileContainer, className)}
73
+ height={height}
74
+ width={mobile ? 'calc(100% - 16px)' : width}
75
+ {...rest}
76
+ >
77
+ {showCloseIcon && (
78
+ <ActionIcon className={styles.cancelIcon} icon={XIcon} onClick={() => onCancel?.()} />
79
+ )}
80
+ <Flexbox
81
+ className={cx(styles.wrapper, wrapperClassName)}
82
+ gap={16}
83
+ horizontal
84
+ padding={'20px 20px 16px'}
85
+ {...restWrapper}
86
+ >
87
+ {children}
88
+ </Flexbox>
89
+ </Flexbox>
90
+ )
91
+ );
92
+ },
93
+ );
94
+
95
+ export default Notification;
@@ -0,0 +1,70 @@
1
+ // @vitest-environment node
2
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
3
+
4
+ import { analyticsEnv, getAnalyticsConfig } from '../analytics';
5
+
6
+ beforeEach(() => {
7
+ // 在每个测试用例之前,清除所有的 console.warn mock
8
+ console.warn = vi.fn();
9
+ });
10
+
11
+ afterEach(() => {
12
+ // 在每个测试用例之后,恢复所有的环境变量
13
+ vi.resetModules();
14
+ });
15
+
16
+ describe('getAnalyticsConfig', () => {
17
+ it('should return the correct analytics config', () => {
18
+ // 设置环境变量
19
+ process.env.PLAUSIBLE_DOMAIN = 'example.com';
20
+ process.env.POSTHOG_KEY = 'posthog_key';
21
+ process.env.UMAMI_WEBSITE_ID = 'umami_id';
22
+ process.env.CLARITY_PROJECT_ID = 'clarity_id';
23
+ process.env.ENABLE_VERCEL_ANALYTICS = '1';
24
+ process.env.GOOGLE_ANALYTICS_MEASUREMENT_ID = 'ga_id';
25
+
26
+ const config = getAnalyticsConfig();
27
+
28
+ expect(config).toEqual({
29
+ ENABLED_PLAUSIBLE_ANALYTICS: true,
30
+ PLAUSIBLE_DOMAIN: 'example.com',
31
+ PLAUSIBLE_SCRIPT_BASE_URL: 'https://plausible.io',
32
+ ENABLED_POSTHOG_ANALYTICS: true,
33
+ POSTHOG_KEY: 'posthog_key',
34
+ POSTHOG_HOST: 'https://app.posthog.com',
35
+ DEBUG_POSTHOG_ANALYTICS: false,
36
+ ENABLED_UMAMI_ANALYTICS: true,
37
+ UMAMI_SCRIPT_URL: 'https://analytics.umami.is/script.js',
38
+ UMAMI_WEBSITE_ID: 'umami_id',
39
+ ENABLED_CLARITY_ANALYTICS: true,
40
+ CLARITY_PROJECT_ID: 'clarity_id',
41
+ ENABLE_VERCEL_ANALYTICS: true,
42
+ DEBUG_VERCEL_ANALYTICS: false,
43
+ ENABLE_GOOGLE_ANALYTICS: true,
44
+ GOOGLE_ANALYTICS_MEASUREMENT_ID: 'ga_id',
45
+ });
46
+ });
47
+
48
+ it('should use deprecated env vars and log warnings', () => {
49
+ process.env.NEXT_PUBLIC_PLAUSIBLE_DOMAIN = 'deprecated.com';
50
+ process.env.NEXT_PUBLIC_POSTHOG_KEY = 'deprecated_key';
51
+ process.env.NEXT_PUBLIC_UMAMI_WEBSITE_ID = 'deprecated_id';
52
+
53
+ const config = getAnalyticsConfig();
54
+
55
+ expect(config.ENABLED_PLAUSIBLE_ANALYTICS).toBeTruthy();
56
+ expect(config.ENABLED_POSTHOG_ANALYTICS).toBeTruthy();
57
+ expect(config.ENABLED_UMAMI_ANALYTICS).toBeTruthy();
58
+
59
+ expect(console.warn).toHaveBeenCalledTimes(3);
60
+ expect(console.warn).toHaveBeenCalledWith(
61
+ 'NEXT_PUBLIC_PLAUSIBLE_DOMAIN is deprecated. Please use PLAUSIBLE_DOMAIN instead. We will remove it in LobeChat 1.0',
62
+ );
63
+ expect(console.warn).toHaveBeenCalledWith(
64
+ 'NEXT_PUBLIC_POSTHOG_KEY is deprecated. Please use POSTHOG_KEY instead. We will remove it in LobeChat 1.0',
65
+ );
66
+ expect(console.warn).toHaveBeenCalledWith(
67
+ 'NEXT_PUBLIC_UMAMI_WEBSITE_ID is deprecated. Please use UMAMI_WEBSITE_ID instead. We will remove it in LobeChat 1.0',
68
+ );
69
+ });
70
+ });
@@ -18,9 +18,6 @@ describe('getClientConfig', () => {
18
18
  process.env.NEXT_PUBLIC_I18N_DEBUG_SERVER = '1';
19
19
 
20
20
  const config = getClientConfig();
21
- expect(config.ANALYTICS_PLAUSIBLE).toBe(true);
22
- expect(config.ANALYTICS_POSTHOG).toBe(true);
23
- expect(config.POSTHOG_DEBUG).toBe(true);
24
21
  expect(config.I18N_DEBUG).toBe(true);
25
22
  expect(config.I18N_DEBUG_BROWSER).toBe(true);
26
23
  expect(config.I18N_DEBUG_SERVER).toBe(true);
@@ -36,9 +33,6 @@ describe('getClientConfig', () => {
36
33
 
37
34
  const config = getClientConfig();
38
35
 
39
- expect(config.ANALYTICS_PLAUSIBLE).toBe(false);
40
- expect(config.ANALYTICS_POSTHOG).toBe(false);
41
- expect(config.POSTHOG_DEBUG).toBe(false);
42
36
  expect(config.I18N_DEBUG).toBe(false);
43
37
  expect(config.I18N_DEBUG_BROWSER).toBe(false);
44
38
  expect(config.I18N_DEBUG_SERVER).toBe(false);