@lobehub/chat 1.82.7 → 1.82.9

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 (52) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/changelog/v1.json +21 -0
  3. package/locales/ar/models.json +18 -3
  4. package/locales/ar/oauth.json +16 -0
  5. package/locales/bg-BG/models.json +18 -3
  6. package/locales/bg-BG/oauth.json +16 -0
  7. package/locales/de-DE/models.json +18 -3
  8. package/locales/de-DE/oauth.json +16 -0
  9. package/locales/en-US/models.json +18 -3
  10. package/locales/en-US/oauth.json +16 -0
  11. package/locales/es-ES/models.json +18 -3
  12. package/locales/es-ES/oauth.json +16 -0
  13. package/locales/fa-IR/models.json +18 -3
  14. package/locales/fa-IR/oauth.json +16 -0
  15. package/locales/fr-FR/models.json +18 -3
  16. package/locales/fr-FR/oauth.json +16 -0
  17. package/locales/it-IT/models.json +18 -3
  18. package/locales/it-IT/oauth.json +16 -0
  19. package/locales/ja-JP/models.json +18 -3
  20. package/locales/ja-JP/oauth.json +16 -0
  21. package/locales/ko-KR/models.json +18 -3
  22. package/locales/ko-KR/oauth.json +16 -0
  23. package/locales/nl-NL/models.json +18 -3
  24. package/locales/nl-NL/oauth.json +16 -0
  25. package/locales/pl-PL/models.json +18 -3
  26. package/locales/pl-PL/oauth.json +16 -0
  27. package/locales/pt-BR/models.json +18 -3
  28. package/locales/pt-BR/oauth.json +16 -0
  29. package/locales/ru-RU/models.json +18 -3
  30. package/locales/ru-RU/oauth.json +16 -0
  31. package/locales/tr-TR/models.json +18 -3
  32. package/locales/tr-TR/oauth.json +16 -0
  33. package/locales/vi-VN/models.json +18 -3
  34. package/locales/vi-VN/oauth.json +16 -0
  35. package/locales/zh-CN/models.json +19 -4
  36. package/locales/zh-CN/oauth.json +16 -0
  37. package/locales/zh-TW/models.json +18 -3
  38. package/locales/zh-TW/oauth.json +16 -0
  39. package/package.json +1 -1
  40. package/src/app/(backend)/oidc/consent/route.ts +12 -0
  41. package/src/app/[variants]/oauth/consent/[uid]/{Client.tsx → Consent.tsx} +6 -40
  42. package/src/app/[variants]/oauth/consent/[uid]/Login.tsx +130 -0
  43. package/src/app/[variants]/oauth/consent/[uid]/components/OAuthApplicationLogo.tsx +82 -0
  44. package/src/app/[variants]/oauth/consent/[uid]/page.tsx +12 -7
  45. package/src/app/[variants]/oauth/handoff/Client.tsx +98 -0
  46. package/src/app/[variants]/oauth/handoff/page.tsx +13 -0
  47. package/src/libs/oidc-provider/config.ts +11 -3
  48. package/src/libs/oidc-provider/http-adapter.ts +9 -3
  49. package/src/locales/default/oauth.ts +16 -0
  50. package/src/middleware.ts +1 -0
  51. package/src/tools/web-browsing/index.ts +0 -5
  52. package/src/tools/web-browsing/systemRole.ts +11 -42
@@ -1673,9 +1673,6 @@
1673
1673
  "o3-mini": {
1674
1674
  "description": "o3-mini 是我們最新的小型推理模型,在與 o1-mini 相同的成本和延遲目標下提供高智能。"
1675
1675
  },
1676
- "o3-mini-high": {
1677
- "description": "o3-mini 高推理等級版,在與 o1-mini 相同的成本和延遲目標下提供高智能。"
1678
- },
1679
1676
  "o4-mini": {
1680
1677
  "description": "o4-mini 是我們最新的小型 o 系列模型。它專為快速有效的推理而優化,在編碼和視覺任務中表現出極高的效率和性能。"
1681
1678
  },
@@ -1694,6 +1691,15 @@
1694
1691
  "open-mixtral-8x7b": {
1695
1692
  "description": "Mixtral 8x7B 是一個稀疏專家模型,利用多個參數提高推理速度,適合處理多語言和代碼生成任務。"
1696
1693
  },
1694
+ "openai/gpt-4.1": {
1695
+ "description": "GPT-4.1 是我們用於複雜任務的旗艦模型。它非常適合跨領域解決問題。"
1696
+ },
1697
+ "openai/gpt-4.1-mini": {
1698
+ "description": "GPT-4.1 mini 提供了智能、速度和成本之間的平衡,使其成為許多用例中有吸引力的模型。"
1699
+ },
1700
+ "openai/gpt-4.1-nano": {
1701
+ "description": "GPT-4.1 nano 是最快、最具成本效益的 GPT-4.1 模型。"
1702
+ },
1697
1703
  "openai/gpt-4o": {
1698
1704
  "description": "ChatGPT-4o 是一款動態模型,實時更新以保持當前最新版本。它結合了強大的語言理解與生成能力,適合於大規模應用場景,包括客戶服務、教育和技術支持。"
1699
1705
  },
@@ -1706,6 +1712,15 @@
1706
1712
  "openai/o1-preview": {
1707
1713
  "description": "o1是OpenAI新的推理模型,適用於需要廣泛通用知識的複雜任務。該模型具有128K上下文和2023年10月的知識截止日期。"
1708
1714
  },
1715
+ "openai/o3": {
1716
+ "description": "o3 是一款全能強大的模型,在多個領域表現出色。它為數學、科學、程式設計和視覺推理任務樹立了新標杆。它也擅長技術寫作和指令遵循。用戶可利用它分析文本、程式碼和圖像,解決多步驟的複雜問題。"
1717
+ },
1718
+ "openai/o3-mini": {
1719
+ "description": "o3-mini 在與 o1-mini 相同的成本和延遲目標下提供高智能。"
1720
+ },
1721
+ "openai/o3-mini-high": {
1722
+ "description": "o3-mini 高推理等級版,在與 o1-mini 相同的成本和延遲目標下提供高智能。"
1723
+ },
1709
1724
  "openai/o4-mini": {
1710
1725
  "description": "o4-mini 專為快速有效的推理而優化,在編碼和視覺任務中表現出極高的效率和性能。"
1711
1726
  },
@@ -33,6 +33,22 @@
33
33
  "subTitle": "您已拒絕授權應用訪問您的 LobeChat 帳戶",
34
34
  "title": "授權被拒絕"
35
35
  },
36
+ "handoff": {
37
+ "desc": {
38
+ "processing": "應用正在處理授權,即將跳轉到下一個頁面...",
39
+ "success": "已嘗試打開桌面應用。如果應用未自動打開,請手動切換。您可以稍後關閉此瀏覽器窗口。"
40
+ },
41
+ "title": {
42
+ "processing": "授權處理中...",
43
+ "success": "授權已完成"
44
+ }
45
+ },
46
+ "login": {
47
+ "button": "確認登入",
48
+ "description": "應用 {{clientName}} 申請使用您的帳戶進行登入",
49
+ "title": "登入 {{clientName}}",
50
+ "userWelcome": "歡迎回來,"
51
+ },
36
52
  "success": {
37
53
  "subTitle": "您已成功授權應用訪問您的 LobeChat 帳戶,可以關閉該頁面了",
38
54
  "title": "授權成功"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/chat",
3
- "version": "1.82.7",
3
+ "version": "1.82.9",
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",
@@ -113,6 +113,18 @@ export async function POST(request: NextRequest) {
113
113
  const internalRedirectUrlString = await oidcService.getInteractionResult(uid, result);
114
114
  log('OIDC Provider internal redirect URL string: %s', internalRedirectUrlString);
115
115
 
116
+ // // Construct the handoff URL
117
+ // const handoffUrl = new URL('/oauth/handoff', request.nextUrl.origin);
118
+ // // Set the original redirect URL as the 'target' query parameter (URL encoded)
119
+ // handoffUrl.searchParams.set('target', internalRedirectUrlString);
120
+ //
121
+ // log('Redirecting to handoff page: %s', handoffUrl.toString());
122
+ // // Redirect to the handoff page
123
+ // return NextResponse.redirect(handoffUrl.toString(), {
124
+ // headers: request.headers, // Keep original headers if necessary
125
+ // status: 303,
126
+ // });
127
+
116
128
  return NextResponse.redirect(internalRedirectUrlString, {
117
129
  headers: request.headers,
118
130
  status: 303,
@@ -1,15 +1,12 @@
1
1
  'use client';
2
2
 
3
- import { Icon } from '@lobehub/ui';
4
3
  import { Button, Card, Divider, Typography } from 'antd';
5
4
  import { createStyles } from 'antd-style';
6
- import { Link2Icon, ServerIcon } from 'lucide-react';
7
- import Image from 'next/image';
8
5
  import React, { memo } from 'react';
9
6
  import { useTranslation } from 'react-i18next';
10
7
  import { Center, Flexbox } from 'react-layout-kit';
11
8
 
12
- import { ProductLogo } from '@/components/Branding';
9
+ import OAuthApplicationLogo from './components/OAuthApplicationLogo';
13
10
 
14
11
  interface ClientProps {
15
12
  clientId: string;
@@ -129,42 +126,11 @@ const ConsentClient = memo<ClientProps>(
129
126
  return (
130
127
  <Center className={styles.container} gap={16}>
131
128
  <Flexbox gap={40}>
132
- {clientMetadata.isFirstParty ? (
133
- <Flexbox align={'center'} gap={12} horizontal justify={'center'}>
134
- <Image
135
- alt={clientDisplayName}
136
- height={64}
137
- src={clientMetadata.logo!}
138
- unoptimized
139
- width={64}
140
- />
141
- </Flexbox>
142
- ) : (
143
- <Flexbox align={'center'} gap={12} horizontal justify={'center'}>
144
- <div className={styles.icon}>
145
- {clientMetadata?.logo ? (
146
- <Image
147
- alt={clientDisplayName}
148
- height={56}
149
- src={clientMetadata?.logo}
150
- unoptimized
151
- width={56}
152
- />
153
- ) : (
154
- <Icon icon={ServerIcon} />
155
- )}
156
- </div>
157
- <div className={styles.connectorLine} />
158
- <Center className={styles.connector}>
159
- <Icon icon={Link2Icon} style={{ color: theme.colorTextSecondary, fontSize: 20 }} />
160
- </Center>
161
- <div className={styles.connectorLine} />
162
- <div className={styles.lobeIcon}>
163
- <ProductLogo height={48} style={{ objectFit: 'cover' }} width={48} />
164
- </div>
165
- </Flexbox>
166
- )}
167
-
129
+ <OAuthApplicationLogo
130
+ clientDisplayName={clientDisplayName}
131
+ isFirstParty={clientMetadata.isFirstParty}
132
+ logoUrl={clientMetadata.logo}
133
+ />
168
134
  <Title className={styles.title} level={3}>
169
135
  {t('consent.title', { clientName: clientDisplayName })}
170
136
  </Title>
@@ -0,0 +1,130 @@
1
+ 'use client';
2
+
3
+ import { Avatar } from '@lobehub/ui';
4
+ import { Button, Card, Skeleton, Typography } from 'antd';
5
+ import { createStyles } from 'antd-style';
6
+ import React, { memo } from 'react';
7
+ import { useTranslation } from 'react-i18next';
8
+ import { Center, Flexbox } from 'react-layout-kit';
9
+
10
+ import { useUserStore } from '@/store/user';
11
+ import { userProfileSelectors } from '@/store/user/selectors';
12
+
13
+ import OAuthApplicationLogo from './components/OAuthApplicationLogo';
14
+
15
+ interface LoginConfirmProps {
16
+ clientMetadata: {
17
+ clientName?: string;
18
+ isFirstParty?: boolean;
19
+ logo?: string;
20
+ };
21
+ uid: string;
22
+ }
23
+
24
+ const { Title } = Typography;
25
+
26
+ const useStyles = createStyles(({ css, token }) => ({
27
+ authButton: css`
28
+ width: 100%;
29
+ height: 40px;
30
+ border-radius: ${token.borderRadius}px;
31
+ font-weight: 500;
32
+ `,
33
+ card: css`
34
+ width: 100%;
35
+ max-width: 500px;
36
+ border-color: ${token.colorBorderSecondary};
37
+ border-radius: 12px;
38
+
39
+ background: ${token.colorBgContainer};
40
+ `,
41
+ container: css`
42
+ width: 100%;
43
+ min-height: 100vh;
44
+ color: ${token.colorTextBase};
45
+ background-color: ${token.colorBgLayout};
46
+ `,
47
+ title: css`
48
+ margin-block-end: ${token.marginLG}px;
49
+ color: ${token.colorTextBase};
50
+ text-align: center;
51
+ `,
52
+ }));
53
+
54
+ const LoginConfirmClient = memo<LoginConfirmProps>(({ uid, clientMetadata }) => {
55
+ const { styles } = useStyles();
56
+ const { t } = useTranslation('oauth'); // Assuming translations are in 'oauth'
57
+
58
+ const clientDisplayName = clientMetadata?.clientName || 'the application';
59
+
60
+ const isUserStateInit = useUserStore((s) => s.isUserStateInit);
61
+ const avatar = useUserStore(userProfileSelectors.userAvatar);
62
+ const nickName = useUserStore(userProfileSelectors.nickName);
63
+
64
+ const titleText = t('login.title', { clientName: clientDisplayName });
65
+ const descriptionText = t('login.description', { clientName: clientDisplayName });
66
+ const buttonText = t('login.button'); // Or "Continue"
67
+
68
+ return (
69
+ <Center className={styles.container} gap={16}>
70
+ <Flexbox align={'center'} gap={40}>
71
+ {/* Branding section - similar to Consent */}
72
+ <OAuthApplicationLogo
73
+ clientDisplayName={clientDisplayName}
74
+ isFirstParty={clientMetadata.isFirstParty}
75
+ logoUrl={clientMetadata.logo}
76
+ />
77
+ </Flexbox>
78
+ <Title className={styles.title} level={3}>
79
+ {titleText}
80
+ </Title>
81
+
82
+ <Card className={styles.card}>
83
+ <Flexbox gap={64}>
84
+ {/* Increased gap for better spacing */}
85
+ <Flexbox gap={24}>
86
+ <Center horizontal justify={'center'}>
87
+ {isUserStateInit ? (
88
+ <Flexbox align={'center'} gap={8} horizontal>
89
+ <Avatar alt={nickName || ''} avatar={avatar} size={40} />
90
+ <div style={{ fontSize: 20 }}>{nickName}</div>
91
+ </Flexbox>
92
+ ) : (
93
+ <Flexbox gap={8} horizontal>
94
+ <Skeleton.Avatar active />
95
+ <Skeleton.Button active />
96
+ </Flexbox>
97
+ )}
98
+ </Center>
99
+ <div style={{ textAlign: 'center' }}>{descriptionText}</div>
100
+ </Flexbox>
101
+
102
+ <Flexbox gap={16}>
103
+ {/* Form points to the endpoint handling login confirmation */}
104
+ <form action="/oidc/consent" method="post" style={{ width: '100%' }}>
105
+ {/* Adjust action URL */}
106
+ <input name="uid" type="hidden" value={uid} />
107
+ <input name="choice" type="hidden" value={'accept'} />
108
+ {/* Single confirmation button */}
109
+ <Button
110
+ className={styles.authButton}
111
+ disabled={!isUserStateInit}
112
+ htmlType="submit"
113
+ name="consent"
114
+ size="large"
115
+ type="primary"
116
+ value="accept"
117
+ >
118
+ {buttonText}
119
+ </Button>
120
+ </form>
121
+ </Flexbox>
122
+ </Flexbox>
123
+ </Card>
124
+ </Center>
125
+ );
126
+ });
127
+
128
+ LoginConfirmClient.displayName = 'LoginConfirmClient';
129
+
130
+ export default LoginConfirmClient;
@@ -0,0 +1,82 @@
1
+ import { Icon } from '@lobehub/ui';
2
+ import { createStyles } from 'antd-style';
3
+ import { Link2Icon, ServerIcon } from 'lucide-react';
4
+ import Image from 'next/image';
5
+ import React, { memo } from 'react';
6
+ import { Center, Flexbox } from 'react-layout-kit';
7
+
8
+ import { ProductLogo } from '@/components/Branding';
9
+
10
+ const useStyles = createStyles(({ css, token }) => ({
11
+ connector: css`
12
+ width: 40px;
13
+ height: 40px;
14
+ `,
15
+ connectorLine: css`
16
+ width: 32px;
17
+ height: 1px;
18
+ background-color: ${token.colorBorderSecondary};
19
+ `,
20
+ icon: css`
21
+ overflow: hidden;
22
+ display: flex;
23
+ align-items: center;
24
+ justify-content: center;
25
+
26
+ width: 64px;
27
+ height: 64px;
28
+ border: 1px solid ${token.colorBorderSecondary};
29
+ border-radius: 16px;
30
+
31
+ background-color: ${token.colorBgElevated};
32
+ `,
33
+ lobeIcon: css`
34
+ overflow: hidden;
35
+ display: flex;
36
+ align-items: center;
37
+ justify-content: center;
38
+
39
+ width: 64px;
40
+ height: 64px;
41
+ border-radius: 50%;
42
+
43
+ background-color: ${token.colorBgElevated};
44
+ `,
45
+ }));
46
+
47
+ interface OAuthApplicationLogoProps {
48
+ clientDisplayName: string;
49
+ isFirstParty?: boolean;
50
+ logoUrl?: string;
51
+ }
52
+
53
+ const OAuthApplicationLogo = memo<OAuthApplicationLogoProps>(
54
+ ({ isFirstParty, clientDisplayName, logoUrl }) => {
55
+ const { styles, theme } = useStyles();
56
+ return isFirstParty ? (
57
+ <Flexbox align={'center'} gap={12} horizontal justify={'center'}>
58
+ <Image alt={clientDisplayName} height={64} src={logoUrl!} unoptimized width={64} />
59
+ </Flexbox>
60
+ ) : (
61
+ <Flexbox align={'center'} gap={12} horizontal justify={'center'}>
62
+ <div className={styles.icon}>
63
+ {logoUrl ? (
64
+ <Image alt={clientDisplayName} height={56} src={logoUrl} unoptimized width={56} />
65
+ ) : (
66
+ <Icon icon={ServerIcon} />
67
+ )}
68
+ </div>
69
+ <div className={styles.connectorLine} />
70
+ <Center className={styles.connector}>
71
+ <Icon icon={Link2Icon} style={{ color: theme.colorTextSecondary, fontSize: 20 }} />
72
+ </Center>
73
+ <div className={styles.connectorLine} />
74
+ <div className={styles.lobeIcon}>
75
+ <ProductLogo height={48} style={{ objectFit: 'cover' }} width={48} />
76
+ </div>
77
+ </Flexbox>
78
+ );
79
+ },
80
+ );
81
+
82
+ export default OAuthApplicationLogo;
@@ -4,8 +4,9 @@ import { oidcEnv } from '@/envs/oidc';
4
4
  import { defaultClients } from '@/libs/oidc-provider/config';
5
5
  import { OIDCService } from '@/server/services/oidc';
6
6
 
7
- import ConsentClient from './Client';
8
7
  import ConsentClientError from './ClientError';
8
+ import Consent from './Consent';
9
+ import Login from './Login';
9
10
 
10
11
  const InteractionPage = async (props: { params: Promise<{ uid: string }> }) => {
11
12
  if (!oidcEnv.ENABLE_OIDC) return notFound();
@@ -37,15 +38,19 @@ const InteractionPage = async (props: { params: Promise<{ uid: string }> }) => {
37
38
 
38
39
  const clientDetail = await oidcService.getClientMetadata(clientId);
39
40
 
41
+ const clientMetadata = {
42
+ clientName: clientDetail?.client_name,
43
+ isFirstParty: defaultClients.map((c) => c.client_id).includes(clientId),
44
+ logo: clientDetail?.logo_uri,
45
+ };
40
46
  // 渲染客户端组件,无论是 login 还是 consent 类型
47
+ if (details.prompt.name === 'login')
48
+ return <Login clientMetadata={clientMetadata} uid={params.uid} />;
49
+
41
50
  return (
42
- <ConsentClient
51
+ <Consent
43
52
  clientId={clientId}
44
- clientMetadata={{
45
- clientName: clientDetail?.client_name,
46
- isFirstParty: defaultClients.map((c) => c.client_id).includes(clientId),
47
- logo: clientDetail?.logo_uri,
48
- }}
53
+ clientMetadata={clientMetadata}
49
54
  redirectUri={details.params.redirect_uri as string}
50
55
  scopes={scopes}
51
56
  uid={params.uid}
@@ -0,0 +1,98 @@
1
+ 'use client';
2
+
3
+ import { Spin, Typography } from 'antd';
4
+ import { createStyles } from 'antd-style';
5
+ import { useSearchParams } from 'next/navigation';
6
+ import React, { useEffect, useState } from 'react';
7
+ import { useTranslation } from 'react-i18next';
8
+ import { Center, Flexbox } from 'react-layout-kit';
9
+
10
+ const { Title, Paragraph } = Typography;
11
+
12
+ const useStyles = createStyles(({ css, token }) => ({
13
+ container: css`
14
+ width: 100%;
15
+ min-height: 100vh;
16
+ padding-block: 40px;
17
+ padding-inline: 24px;
18
+
19
+ color: ${token.colorTextBase};
20
+
21
+ background-color: ${token.colorBgLayout};
22
+ `,
23
+ content: css`
24
+ max-width: 600px;
25
+ text-align: center;
26
+ `,
27
+ message: css`
28
+ margin-block-end: ${token.marginXL}px;
29
+ color: ${token.colorTextSecondary};
30
+ `,
31
+ title: css`
32
+ margin-block-end: ${token.marginLG}px;
33
+ `,
34
+ }));
35
+
36
+ interface Status {
37
+ desc: string;
38
+ status: 'processing' | 'success';
39
+ title: string;
40
+ }
41
+ const AuthHandoffPage = () => {
42
+ const { styles } = useStyles();
43
+ const { t } = useTranslation('oauth'); // Assuming 'oauth' namespace exists
44
+ const searchParams = useSearchParams();
45
+
46
+ const [status, setStatus] = useState<Status>({
47
+ desc: t('handoff.desc.processing'),
48
+ status: 'processing',
49
+ title: t('handoff.title.processing'),
50
+ });
51
+
52
+ const [isError, setIsError] = useState<boolean>(false);
53
+
54
+ useEffect(() => {
55
+ const targetUrl = searchParams.get('target');
56
+
57
+ if (targetUrl) {
58
+ try {
59
+ const decodedTargetUrl = decodeURIComponent(targetUrl);
60
+ console.log(`Attempting redirect to: ${decodedTargetUrl}`);
61
+
62
+ window.location.href = decodedTargetUrl;
63
+
64
+ const url = new URL(decodedTargetUrl);
65
+ if (!url.pathname.startsWith('/oidc/auth')) {
66
+ setStatus({
67
+ desc: t('handoff.desc.success'),
68
+ status: 'success',
69
+ title: t('handoff.title.success'),
70
+ });
71
+ }
72
+ } catch (error) {
73
+ console.error('Error decoding or redirecting:', error);
74
+ // setMessage(
75
+ // t('handoff.error', '无法自动打开桌面应用。请检查链接是否有效或尝试手动打开应用。'),
76
+ // );
77
+ setIsError(true);
78
+ }
79
+ } else {
80
+ console.error('Missing target URL for handoff.');
81
+ setIsError(true);
82
+ }
83
+ }, [searchParams]);
84
+
85
+ return (
86
+ <Center className={styles.container} gap={12}>
87
+ {!isError && <Spin size="large" />}
88
+ <Flexbox align="center" className={styles.content} gap={16}>
89
+ <Title className={styles.title} level={3}>
90
+ {status.title}
91
+ </Title>
92
+ <Paragraph className={styles.message}>{status.desc}</Paragraph>
93
+ </Flexbox>
94
+ </Center>
95
+ );
96
+ };
97
+
98
+ export default AuthHandoffPage;
@@ -0,0 +1,13 @@
1
+ 'use client';
2
+
3
+ import React, { Suspense } from 'react';
4
+
5
+ import Client from './Client';
6
+
7
+ const AuthHandoffPage = () => (
8
+ <Suspense>
9
+ <Client />
10
+ </Suspense>
11
+ );
12
+
13
+ export default AuthHandoffPage;
@@ -14,9 +14,17 @@ export const defaultClients: ClientMetadata[] = [
14
14
  logo_uri: 'https://hub-apac-1.lobeobjects.space/lobehub-desktop-icon.png',
15
15
 
16
16
  // 桌面端注册的自定义协议回调(使用反向域名格式)
17
- post_logout_redirect_uris: ['com.lobehub.desktop://auth/logout/callback'],
18
-
19
- redirect_uris: ['com.lobehub.desktop://auth/callback', 'https://oauthdebugger.com/debug'],
17
+ post_logout_redirect_uris: [
18
+ 'com.lobehub.lobehub-desktop-dev://auth/logout/callback',
19
+ 'com.lobehub.lobehub-desktop-nightly://auth/logout/callback',
20
+ 'com.lobehub.lobehub-desktop://auth/logout/callback',
21
+ ],
22
+
23
+ redirect_uris: [
24
+ 'com.lobehub.lobehub-desktop-dev://auth/callback',
25
+ 'com.lobehub.lobehub-desktop-nightly://auth/callback',
26
+ 'com.lobehub.lobehub-desktop://auth/logout/callback',
27
+ ],
20
28
 
21
29
  // 支持授权码获取令牌和刷新令牌
22
30
  response_types: ['code'],
@@ -235,8 +235,10 @@ export const createContextForInteractionDetails = async (
235
235
  const baseUrl = appEnv.APP_URL!;
236
236
  log('Using base URL: %s', baseUrl);
237
237
 
238
- // 从baseUrl提取主机名用于headers
239
- const hostName = new URL(baseUrl).host;
238
+ // 从baseUrl提取主机名和协议用于headers
239
+ const parsedUrl = new URL(baseUrl);
240
+ const hostName = parsedUrl.host;
241
+ const protocol = parsedUrl.protocol.replace(':', '');
240
242
 
241
243
  // 1. 获取真实的 Cookies
242
244
  const cookieStore = await cookies();
@@ -255,7 +257,11 @@ export const createContextForInteractionDetails = async (
255
257
  }
256
258
 
257
259
  // 2. 构建包含真实 Cookie 的 Headers
258
- const headers = new Headers({ host: hostName });
260
+ const headers = new Headers({
261
+ 'host': hostName,
262
+ 'x-forwarded-host': hostName,
263
+ 'x-forwarded-proto': protocol,
264
+ });
259
265
  const cookieString = Object.entries(realCookies)
260
266
  .map(([name, value]) => `${name}=${value}`)
261
267
  .join('; ');
@@ -34,6 +34,22 @@ const oauth = {
34
34
  subTitle: '您已拒绝授权应用访问您的 LobeChat 账户',
35
35
  title: '授权被拒绝',
36
36
  },
37
+ handoff: {
38
+ desc: {
39
+ processing: '应用正在处理授权,即将跳转下一个页面...',
40
+ success: '已尝试打开桌面应用。如果应用未自动打开,请手动切换。您可以稍后关闭此浏览器窗口。',
41
+ },
42
+ title: {
43
+ processing: '授权处理中...',
44
+ success: '授权已完成',
45
+ },
46
+ },
47
+ login: {
48
+ button: '确认登录',
49
+ description: '应用 {{clientName}} 申请使用您的账户进行登录',
50
+ title: '登录 {{clientName}}',
51
+ userWelcome: '欢迎回来,',
52
+ },
37
53
  success: {
38
54
  subTitle: '您已成功授权应用访问您的 LobeChat 账户,可以关闭该页面了',
39
55
  title: '授权成功',
package/src/middleware.ts CHANGED
@@ -175,6 +175,7 @@ const isProtectedRoute = createRouteMatcher([
175
175
  '/settings(.*)',
176
176
  '/files(.*)',
177
177
  '/onboard(.*)',
178
+ '/oauth(.*)',
178
179
  // ↓ cloud ↓
179
180
  ]);
180
181
 
@@ -26,15 +26,10 @@ export const WebBrowsingManifest: BuiltinToolManifest = {
26
26
  description: 'The search categories you can set:',
27
27
  items: {
28
28
  enum: [
29
- 'files',
30
29
  'general',
31
30
  'images',
32
- 'it',
33
- 'map',
34
- 'music',
35
31
  'news',
36
32
  'science',
37
- 'social_media',
38
33
  'videos',
39
34
  ],
40
35
  type: 'string',