@lobehub/chat 1.106.1 → 1.106.2
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/CHANGELOG.md
CHANGED
@@ -2,6 +2,31 @@
|
|
2
2
|
|
3
3
|
# Changelog
|
4
4
|
|
5
|
+
### [Version 1.106.2](https://github.com/lobehub/lobe-chat/compare/v1.106.1...v1.106.2)
|
6
|
+
|
7
|
+
<sup>Released on **2025-07-29**</sup>
|
8
|
+
|
9
|
+
#### 🐛 Bug Fixes
|
10
|
+
|
11
|
+
- **misc**: Fix desktop auth redirect url error.
|
12
|
+
|
13
|
+
<br/>
|
14
|
+
|
15
|
+
<details>
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
17
|
+
|
18
|
+
#### What's fixed
|
19
|
+
|
20
|
+
- **misc**: Fix desktop auth redirect url error, closes [#8597](https://github.com/lobehub/lobe-chat/issues/8597) ([0ed7368](https://github.com/lobehub/lobe-chat/commit/0ed7368))
|
21
|
+
|
22
|
+
</details>
|
23
|
+
|
24
|
+
<div align="right">
|
25
|
+
|
26
|
+
[](#readme-top)
|
27
|
+
|
28
|
+
</div>
|
29
|
+
|
5
30
|
### [Version 1.106.1](https://github.com/lobehub/lobe-chat/compare/v1.106.0...v1.106.1)
|
6
31
|
|
7
32
|
<sup>Released on **2025-07-29**</sup>
|
package/changelog/v1.json
CHANGED
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@lobehub/chat",
|
3
|
-
"version": "1.106.
|
3
|
+
"version": "1.106.2",
|
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",
|
@@ -257,7 +257,7 @@
|
|
257
257
|
"semver": "^7.7.2",
|
258
258
|
"sharp": "^0.34.3",
|
259
259
|
"shiki": "^3.8.1",
|
260
|
-
"stripe": "^
|
260
|
+
"stripe": "^17.7.0",
|
261
261
|
"superjson": "^2.2.2",
|
262
262
|
"svix": "^1.69.0",
|
263
263
|
"swr": "^2.3.4",
|
@@ -3,9 +3,50 @@ import { NextRequest, NextResponse, after } from 'next/server';
|
|
3
3
|
|
4
4
|
import { OAuthHandoffModel } from '@/database/models/oauthHandoff';
|
5
5
|
import { serverDB } from '@/database/server';
|
6
|
+
import { correctOIDCUrl } from '@/utils/server/correctOIDCUrl';
|
6
7
|
|
7
8
|
const log = debug('lobe-oidc:callback:desktop');
|
8
9
|
|
10
|
+
const errorPathname = '/oauth/callback/error';
|
11
|
+
|
12
|
+
/**
|
13
|
+
* 安全地构建重定向URL
|
14
|
+
*/
|
15
|
+
const buildRedirectUrl = (req: NextRequest, pathname: string): URL => {
|
16
|
+
const forwardedHost = req.headers.get('x-forwarded-host');
|
17
|
+
const requestHost = req.headers.get('host');
|
18
|
+
const forwardedProto =
|
19
|
+
req.headers.get('x-forwarded-proto') || req.headers.get('x-forwarded-protocol');
|
20
|
+
|
21
|
+
// 确定实际的主机名,提供后备值
|
22
|
+
const actualHost = forwardedHost || requestHost;
|
23
|
+
const actualProto = forwardedProto || 'https';
|
24
|
+
|
25
|
+
log(
|
26
|
+
'Building redirect URL - host: %s, proto: %s, pathname: %s',
|
27
|
+
actualHost,
|
28
|
+
actualProto,
|
29
|
+
pathname,
|
30
|
+
);
|
31
|
+
|
32
|
+
// 如果主机名仍然无效,使用req.nextUrl作为后备
|
33
|
+
if (!actualHost) {
|
34
|
+
log('Warning: Invalid host detected, using req.nextUrl as fallback');
|
35
|
+
const fallbackUrl = req.nextUrl.clone();
|
36
|
+
fallbackUrl.pathname = pathname;
|
37
|
+
return fallbackUrl;
|
38
|
+
}
|
39
|
+
|
40
|
+
try {
|
41
|
+
return new URL(`${actualProto}://${actualHost}${pathname}`);
|
42
|
+
} catch (error) {
|
43
|
+
log('Error constructing URL, using req.nextUrl as fallback: %O', error);
|
44
|
+
const fallbackUrl = req.nextUrl.clone();
|
45
|
+
fallbackUrl.pathname = pathname;
|
46
|
+
return fallbackUrl;
|
47
|
+
}
|
48
|
+
};
|
49
|
+
|
9
50
|
export const GET = async (req: NextRequest) => {
|
10
51
|
try {
|
11
52
|
const searchParams = req.nextUrl.searchParams;
|
@@ -14,9 +55,11 @@ export const GET = async (req: NextRequest) => {
|
|
14
55
|
|
15
56
|
if (!code || !state || typeof code !== 'string' || typeof state !== 'string') {
|
16
57
|
log('Missing code or state in form data');
|
17
|
-
|
18
|
-
errorUrl
|
58
|
+
|
59
|
+
const errorUrl = buildRedirectUrl(req, errorPathname);
|
19
60
|
errorUrl.searchParams.set('reason', 'invalid_request');
|
61
|
+
|
62
|
+
log('Redirecting to error URL: %s', errorUrl.toString());
|
20
63
|
return NextResponse.redirect(errorUrl);
|
21
64
|
}
|
22
65
|
|
@@ -31,9 +74,16 @@ export const GET = async (req: NextRequest) => {
|
|
31
74
|
await authHandoffModel.create({ client, id, payload });
|
32
75
|
log('Handoff record created successfully for id: %s', id);
|
33
76
|
|
34
|
-
|
35
|
-
|
36
|
-
|
77
|
+
const successUrl = buildRedirectUrl(req, '/oauth/callback/success');
|
78
|
+
|
79
|
+
// 添加调试日志
|
80
|
+
log('Request host header: %s', req.headers.get('host'));
|
81
|
+
log('Request x-forwarded-host: %s', req.headers.get('x-forwarded-host'));
|
82
|
+
log('Request x-forwarded-proto: %s', req.headers.get('x-forwarded-proto'));
|
83
|
+
log('Constructed success URL: %s', successUrl.toString());
|
84
|
+
|
85
|
+
const correctedUrl = correctOIDCUrl(req, successUrl);
|
86
|
+
log('Final redirect URL: %s', correctedUrl.toString());
|
37
87
|
|
38
88
|
// cleanup expired
|
39
89
|
after(async () => {
|
@@ -42,17 +92,18 @@ export const GET = async (req: NextRequest) => {
|
|
42
92
|
log('Cleaned up %d expired handoff records', cleanedCount);
|
43
93
|
});
|
44
94
|
|
45
|
-
return NextResponse.redirect(
|
95
|
+
return NextResponse.redirect(correctedUrl);
|
46
96
|
} catch (error) {
|
47
97
|
log('Error in OIDC callback: %O', error);
|
48
|
-
|
49
|
-
errorUrl
|
98
|
+
|
99
|
+
const errorUrl = buildRedirectUrl(req, errorPathname);
|
50
100
|
errorUrl.searchParams.set('reason', 'internal_error');
|
51
101
|
|
52
102
|
if (error instanceof Error) {
|
53
103
|
errorUrl.searchParams.set('errorMessage', error.message);
|
54
104
|
}
|
55
105
|
|
106
|
+
log('Redirecting to error URL: %s', errorUrl.toString());
|
56
107
|
return NextResponse.redirect(errorUrl);
|
57
108
|
}
|
58
109
|
};
|
@@ -3,6 +3,7 @@ import { NextRequest, NextResponse } from 'next/server';
|
|
3
3
|
|
4
4
|
import { OIDCService } from '@/server/services/oidc';
|
5
5
|
import { getUserAuth } from '@/utils/server/auth';
|
6
|
+
import { correctOIDCUrl } from '@/utils/server/correctOIDCUrl';
|
6
7
|
|
7
8
|
const log = debug('lobe-oidc:consent');
|
8
9
|
|
@@ -113,19 +114,15 @@ export async function POST(request: NextRequest) {
|
|
113
114
|
const internalRedirectUrlString = await oidcService.getInteractionResult(uid, result);
|
114
115
|
log('OIDC Provider internal redirect URL string: %s', internalRedirectUrlString);
|
115
116
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
// status: 303,
|
126
|
-
// });
|
127
|
-
|
128
|
-
return NextResponse.redirect(internalRedirectUrlString, {
|
117
|
+
let finalRedirectUrl;
|
118
|
+
try {
|
119
|
+
finalRedirectUrl = correctOIDCUrl(request, new URL(internalRedirectUrlString));
|
120
|
+
} catch {
|
121
|
+
finalRedirectUrl = new URL(internalRedirectUrlString);
|
122
|
+
log('Warning: Could not parse redirect URL, using as-is: %s', internalRedirectUrlString);
|
123
|
+
}
|
124
|
+
|
125
|
+
return NextResponse.redirect(finalRedirectUrl, {
|
129
126
|
headers: request.headers,
|
130
127
|
status: 303,
|
131
128
|
});
|
@@ -0,0 +1,61 @@
|
|
1
|
+
import debug from 'debug';
|
2
|
+
import { NextRequest } from 'next/server';
|
3
|
+
|
4
|
+
const log = debug('lobe-oidc:correctOIDCUrl');
|
5
|
+
|
6
|
+
/**
|
7
|
+
* 修复 OIDC 重定向 URL 在代理环境下的问题
|
8
|
+
* @param req - Next.js 请求对象
|
9
|
+
* @param url - 要修复的 URL 对象
|
10
|
+
* @returns 修复后的 URL 对象
|
11
|
+
*/
|
12
|
+
export const correctOIDCUrl = (req: NextRequest, url: URL): URL => {
|
13
|
+
const requestHost = req.headers.get('host');
|
14
|
+
const forwardedHost = req.headers.get('x-forwarded-host');
|
15
|
+
const forwardedProto =
|
16
|
+
req.headers.get('x-forwarded-proto') || req.headers.get('x-forwarded-protocol');
|
17
|
+
|
18
|
+
log('Input URL: %s', url.toString());
|
19
|
+
log(
|
20
|
+
'Request headers - host: %s, x-forwarded-host: %s, x-forwarded-proto: %s',
|
21
|
+
requestHost,
|
22
|
+
forwardedHost,
|
23
|
+
forwardedProto,
|
24
|
+
);
|
25
|
+
|
26
|
+
// 确定实际的主机名和协议,提供后备值
|
27
|
+
const actualHost = forwardedHost || requestHost;
|
28
|
+
const actualProto = forwardedProto || (url.protocol === 'https:' ? 'https' : 'http');
|
29
|
+
|
30
|
+
// 如果无法确定有效的主机名,直接返回原URL
|
31
|
+
if (!actualHost || actualHost === 'null') {
|
32
|
+
log('Warning: Cannot determine valid host, returning original URL');
|
33
|
+
return url;
|
34
|
+
}
|
35
|
+
|
36
|
+
// 如果 URL 指向本地地址,或者主机名与实际请求主机不匹配,则修正 URL
|
37
|
+
const needsCorrection =
|
38
|
+
url.hostname === 'localhost' ||
|
39
|
+
url.hostname === '127.0.0.1' ||
|
40
|
+
url.hostname === '0.0.0.0' ||
|
41
|
+
url.hostname !== actualHost;
|
42
|
+
|
43
|
+
if (needsCorrection) {
|
44
|
+
log('URL needs correction. Original hostname: %s, correcting to: %s', url.hostname, actualHost);
|
45
|
+
|
46
|
+
try {
|
47
|
+
const correctedUrl = new URL(url.toString());
|
48
|
+
correctedUrl.protocol = actualProto + ':';
|
49
|
+
correctedUrl.host = actualHost;
|
50
|
+
|
51
|
+
log('Corrected URL: %s', correctedUrl.toString());
|
52
|
+
return correctedUrl;
|
53
|
+
} catch (error) {
|
54
|
+
log('Error creating corrected URL, returning original: %O', error);
|
55
|
+
return url;
|
56
|
+
}
|
57
|
+
}
|
58
|
+
|
59
|
+
log('URL does not need correction, returning original: %s', url.toString());
|
60
|
+
return url;
|
61
|
+
};
|