@lobehub/chat 1.79.6 → 1.79.8
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 +50 -0
- package/changelog/v1.json +18 -0
- package/docs/development/database-schema.dbml +119 -0
- package/docs/self-hosting/advanced/online-search.mdx +63 -0
- package/locales/ar/models.json +12 -0
- package/locales/ar/oauth.json +39 -0
- package/locales/bg-BG/models.json +12 -0
- package/locales/bg-BG/oauth.json +39 -0
- package/locales/de-DE/models.json +12 -0
- package/locales/de-DE/oauth.json +39 -0
- package/locales/en-US/models.json +12 -0
- package/locales/en-US/oauth.json +39 -0
- package/locales/es-ES/models.json +12 -0
- package/locales/es-ES/oauth.json +39 -0
- package/locales/fa-IR/models.json +12 -0
- package/locales/fa-IR/oauth.json +39 -0
- package/locales/fr-FR/models.json +12 -0
- package/locales/fr-FR/oauth.json +39 -0
- package/locales/it-IT/models.json +12 -0
- package/locales/it-IT/oauth.json +39 -0
- package/locales/ja-JP/models.json +12 -0
- package/locales/ja-JP/oauth.json +39 -0
- package/locales/ko-KR/models.json +12 -0
- package/locales/ko-KR/oauth.json +39 -0
- package/locales/nl-NL/models.json +12 -0
- package/locales/nl-NL/oauth.json +39 -0
- package/locales/pl-PL/models.json +12 -0
- package/locales/pl-PL/oauth.json +39 -0
- package/locales/pt-BR/models.json +12 -0
- package/locales/pt-BR/oauth.json +39 -0
- package/locales/ru-RU/models.json +12 -0
- package/locales/ru-RU/oauth.json +39 -0
- package/locales/tr-TR/models.json +12 -0
- package/locales/tr-TR/oauth.json +39 -0
- package/locales/vi-VN/models.json +12 -0
- package/locales/vi-VN/oauth.json +39 -0
- package/locales/zh-CN/models.json +12 -0
- package/locales/zh-CN/oauth.json +39 -0
- package/locales/zh-TW/models.json +12 -0
- package/locales/zh-TW/oauth.json +39 -0
- package/package.json +5 -2
- package/scripts/generate-oidc-jwk.mjs +59 -0
- package/scripts/migrateServerDB/index.ts +3 -1
- package/src/app/(backend)/oidc/[...oidc]/route.ts +270 -0
- package/src/app/(backend)/oidc/consent/route.ts +97 -0
- package/src/app/[variants]/oauth/consent/[uid]/Client.tsx +97 -0
- package/src/app/[variants]/oauth/consent/[uid]/failed/page.tsx +36 -0
- package/src/app/[variants]/oauth/consent/[uid]/page.tsx +71 -0
- package/src/app/[variants]/oauth/consent/[uid]/success/page.tsx +30 -0
- package/src/const/hotkeys.ts +2 -2
- package/src/const/trace.ts +1 -0
- package/src/database/client/migrations.json +27 -8
- package/src/database/migrations/0020_add_oidc.sql +124 -0
- package/src/database/migrations/meta/0020_snapshot.json +4975 -0
- package/src/database/migrations/meta/_journal.json +7 -0
- package/src/database/repositories/tableViewer/index.test.ts +1 -1
- package/src/database/schemas/index.ts +1 -0
- package/src/database/schemas/oidc.ts +158 -0
- package/src/database/server/models/__tests__/adapter.test.ts +503 -0
- package/src/envs/oidc.ts +18 -0
- package/src/libs/agent-runtime/azureOpenai/index.ts +4 -1
- package/src/libs/agent-runtime/utils/streams/protocol.ts +2 -4
- package/src/libs/oidc-provider/adapter.ts +494 -0
- package/src/libs/oidc-provider/config.ts +53 -0
- package/src/libs/oidc-provider/http-adapter.ts +279 -0
- package/src/libs/oidc-provider/interaction-policy.ts +37 -0
- package/src/libs/oidc-provider/provider.ts +260 -0
- package/src/locales/default/index.ts +2 -0
- package/src/locales/default/oauth.ts +41 -0
- package/src/middleware.ts +94 -6
- package/src/server/services/oidc/index.ts +29 -0
- package/src/server/services/oidc/oidcProvider.ts +27 -0
- package/src/store/chat/slices/aiChat/actions/memory.ts +6 -1
- package/src/types/hotkey.ts +54 -3
package/src/middleware.ts
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server';
|
2
|
+
import debug from 'debug';
|
2
3
|
import { NextRequest, NextResponse } from 'next/server';
|
3
4
|
import { UAParser } from 'ua-parser-js';
|
4
5
|
import urlJoin from 'url-join';
|
@@ -14,6 +15,15 @@ import { parseDefaultThemeFromCountry } from '@/utils/server/geo';
|
|
14
15
|
import { RouteVariants } from '@/utils/server/routeVariants';
|
15
16
|
|
16
17
|
import { OAUTH_AUTHORIZED } from './const/auth';
|
18
|
+
import { oidcEnv } from './envs/oidc';
|
19
|
+
|
20
|
+
// Create debug logger instances
|
21
|
+
const logDefault = debug('lobe-middleware:default');
|
22
|
+
const logNextAuth = debug('lobe-middleware:next-auth');
|
23
|
+
const logClerk = debug('lobe-middleware:clerk');
|
24
|
+
|
25
|
+
// OIDC session pre-sync constant
|
26
|
+
const OIDC_SESSION_HEADER = 'x-oidc-session-sync';
|
17
27
|
|
18
28
|
export const config = {
|
19
29
|
matcher: [
|
@@ -37,19 +47,25 @@ export const config = {
|
|
37
47
|
'/login(.*)',
|
38
48
|
'/signup(.*)',
|
39
49
|
'/next-auth/(.*)',
|
50
|
+
'/oauth(.*)',
|
51
|
+
'/oidc(.*)',
|
40
52
|
// ↓ cloud ↓
|
41
53
|
],
|
42
54
|
};
|
43
55
|
|
56
|
+
const backendApiEndpoints = ['/api', '/trpc', '/webapi', '/oidc'];
|
57
|
+
|
44
58
|
const defaultMiddleware = (request: NextRequest) => {
|
45
59
|
const url = new URL(request.url);
|
60
|
+
logDefault('Processing request: %s %s', request.method, request.url);
|
46
61
|
|
47
62
|
// skip all api requests
|
48
|
-
if (
|
63
|
+
if (backendApiEndpoints.some((path) => url.pathname.startsWith(path))) {
|
64
|
+
logDefault('Skipping API request: %s', url.pathname);
|
49
65
|
return NextResponse.next();
|
50
66
|
}
|
51
67
|
|
52
|
-
// 1.
|
68
|
+
// 1. Read user preferences from cookies
|
53
69
|
const theme =
|
54
70
|
request.cookies.get(LOBE_THEME_APPEARANCE)?.value || parseDefaultThemeFromCountry(request);
|
55
71
|
|
@@ -62,16 +78,36 @@ const defaultMiddleware = (request: NextRequest) => {
|
|
62
78
|
|
63
79
|
const device = new UAParser(ua || '').getDevice();
|
64
80
|
|
65
|
-
|
81
|
+
logDefault('User preferences: %O', {
|
82
|
+
browserLanguage,
|
83
|
+
deviceType: device.type,
|
84
|
+
hasCookies: {
|
85
|
+
locale: !!request.cookies.get(LOBE_LOCALE_COOKIE)?.value,
|
86
|
+
theme: !!request.cookies.get(LOBE_THEME_APPEARANCE)?.value,
|
87
|
+
},
|
88
|
+
locale,
|
89
|
+
theme,
|
90
|
+
});
|
91
|
+
|
92
|
+
// 2. Create normalized preference values
|
66
93
|
const route = RouteVariants.serializeVariants({
|
67
94
|
isMobile: device.type === 'mobile',
|
68
95
|
locale,
|
69
96
|
theme,
|
70
97
|
});
|
71
98
|
|
99
|
+
logDefault('Serialized route variant: %s', route);
|
100
|
+
|
72
101
|
// if app is in docker, rewrite to self container
|
73
102
|
// https://github.com/lobehub/lobe-chat/issues/5876
|
74
103
|
if (appEnv.MIDDLEWARE_REWRITE_THROUGH_LOCAL) {
|
104
|
+
logDefault('Local container rewrite enabled: %O', {
|
105
|
+
host: '127.0.0.1',
|
106
|
+
original: url.toString(),
|
107
|
+
port: process.env.PORT || '3210',
|
108
|
+
protocol: 'http',
|
109
|
+
});
|
110
|
+
|
75
111
|
url.protocol = 'http';
|
76
112
|
url.host = '127.0.0.1';
|
77
113
|
url.port = process.env.PORT || '3210';
|
@@ -86,7 +122,12 @@ const defaultMiddleware = (request: NextRequest) => {
|
|
86
122
|
? urlJoin(url.origin, nextPathname)
|
87
123
|
: nextPathname;
|
88
124
|
|
89
|
-
|
125
|
+
logDefault('URL rewrite: %O', {
|
126
|
+
isLocalRewrite: appEnv.MIDDLEWARE_REWRITE_THROUGH_LOCAL,
|
127
|
+
nextPathname: nextPathname,
|
128
|
+
nextURL: nextURL,
|
129
|
+
originalPathname: url.pathname,
|
130
|
+
});
|
90
131
|
|
91
132
|
url.pathname = nextPathname;
|
92
133
|
|
@@ -95,6 +136,8 @@ const defaultMiddleware = (request: NextRequest) => {
|
|
95
136
|
|
96
137
|
// Initialize an Edge compatible NextAuth middleware
|
97
138
|
const nextAuthMiddleware = NextAuthEdge.auth((req) => {
|
139
|
+
logNextAuth('NextAuth middleware processing request: %s %s', req.method, req.url);
|
140
|
+
|
98
141
|
const response = defaultMiddleware(req);
|
99
142
|
|
100
143
|
// Just check if session exists
|
@@ -104,10 +147,25 @@ const nextAuthMiddleware = NextAuthEdge.auth((req) => {
|
|
104
147
|
// refs: https://github.com/lobehub/lobe-chat/pull/1323
|
105
148
|
const isLoggedIn = !!session?.expires;
|
106
149
|
|
150
|
+
logNextAuth('NextAuth session status: %O', {
|
151
|
+
expires: session?.expires,
|
152
|
+
isLoggedIn,
|
153
|
+
userId: session?.user?.id,
|
154
|
+
});
|
155
|
+
|
107
156
|
// Remove & amend OAuth authorized header
|
108
157
|
response.headers.delete(OAUTH_AUTHORIZED);
|
109
158
|
if (isLoggedIn) {
|
159
|
+
logNextAuth('Setting auth header: %s = %s', OAUTH_AUTHORIZED, 'true');
|
110
160
|
response.headers.set(OAUTH_AUTHORIZED, 'true');
|
161
|
+
|
162
|
+
// If OIDC is enabled and user is logged in, add OIDC session pre-sync header
|
163
|
+
if (oidcEnv.ENABLE_OIDC && session?.user?.id) {
|
164
|
+
logNextAuth('OIDC session pre-sync: Setting %s = %s', OIDC_SESSION_HEADER, session.user.id);
|
165
|
+
response.headers.set(OIDC_SESSION_HEADER, session.user.id);
|
166
|
+
}
|
167
|
+
} else {
|
168
|
+
logNextAuth('Not logged in, no auth header set');
|
111
169
|
}
|
112
170
|
|
113
171
|
return response;
|
@@ -122,9 +180,33 @@ const isProtectedRoute = createRouteMatcher([
|
|
122
180
|
|
123
181
|
const clerkAuthMiddleware = clerkMiddleware(
|
124
182
|
async (auth, req) => {
|
125
|
-
|
183
|
+
logClerk('Clerk middleware processing request: %s %s', req.method, req.url);
|
184
|
+
|
185
|
+
const isProtected = isProtectedRoute(req);
|
186
|
+
logClerk('Route protection status: %s, %s', req.url, isProtected ? 'protected' : 'public');
|
187
|
+
|
188
|
+
if (isProtected) {
|
189
|
+
logClerk('Protecting route: %s', req.url);
|
190
|
+
await auth.protect();
|
191
|
+
}
|
126
192
|
|
127
|
-
|
193
|
+
const response = defaultMiddleware(req);
|
194
|
+
|
195
|
+
const data = await auth();
|
196
|
+
logClerk('Clerk auth status: %O', {
|
197
|
+
isSignedIn: !!data.userId,
|
198
|
+
userId: data.userId,
|
199
|
+
});
|
200
|
+
|
201
|
+
// If OIDC is enabled and Clerk user is logged in, add OIDC session pre-sync header
|
202
|
+
if (oidcEnv.ENABLE_OIDC && data.userId) {
|
203
|
+
logClerk('OIDC session pre-sync: Setting %s = %s', OIDC_SESSION_HEADER, data.userId);
|
204
|
+
response.headers.set(OIDC_SESSION_HEADER, data.userId);
|
205
|
+
} else if (oidcEnv.ENABLE_OIDC) {
|
206
|
+
logClerk('No Clerk user detected, not setting OIDC session sync header');
|
207
|
+
}
|
208
|
+
|
209
|
+
return response;
|
128
210
|
},
|
129
211
|
{
|
130
212
|
// https://github.com/lobehub/lobe-chat/pull/3084
|
@@ -134,6 +216,12 @@ const clerkAuthMiddleware = clerkMiddleware(
|
|
134
216
|
},
|
135
217
|
);
|
136
218
|
|
219
|
+
logDefault('Middleware configuration: %O', {
|
220
|
+
enableClerk: authEnv.NEXT_PUBLIC_ENABLE_CLERK_AUTH,
|
221
|
+
enableNextAuth: authEnv.NEXT_PUBLIC_ENABLE_NEXT_AUTH,
|
222
|
+
enableOIDC: oidcEnv.ENABLE_OIDC,
|
223
|
+
});
|
224
|
+
|
137
225
|
export default authEnv.NEXT_PUBLIC_ENABLE_CLERK_AUTH
|
138
226
|
? clerkAuthMiddleware
|
139
227
|
: authEnv.NEXT_PUBLIC_ENABLE_NEXT_AUTH
|
@@ -0,0 +1,29 @@
|
|
1
|
+
import { createContextForInteractionDetails } from '@/libs/oidc-provider/http-adapter';
|
2
|
+
import { OIDCProvider } from '@/libs/oidc-provider/provider';
|
3
|
+
|
4
|
+
import { getOIDCProvider } from './oidcProvider';
|
5
|
+
|
6
|
+
export class OIDCService {
|
7
|
+
private provider: OIDCProvider;
|
8
|
+
|
9
|
+
constructor(provider: OIDCProvider) {
|
10
|
+
this.provider = provider;
|
11
|
+
}
|
12
|
+
static async initialize() {
|
13
|
+
const provider = await getOIDCProvider();
|
14
|
+
|
15
|
+
return new OIDCService(provider);
|
16
|
+
}
|
17
|
+
|
18
|
+
async getInteractionDetails(uid: string) {
|
19
|
+
const { req, res } = await createContextForInteractionDetails(uid);
|
20
|
+
return this.provider.interactionDetails(req, res);
|
21
|
+
}
|
22
|
+
|
23
|
+
async getInteractionResult(uid: string, result: any) {
|
24
|
+
const { req, res } = await createContextForInteractionDetails(uid);
|
25
|
+
return this.provider.interactionResult(req, res, result, { mergeWithLastSubmission: true });
|
26
|
+
}
|
27
|
+
}
|
28
|
+
|
29
|
+
export { getOIDCProvider } from './oidcProvider';
|
@@ -0,0 +1,27 @@
|
|
1
|
+
import { appEnv } from '@/config/app';
|
2
|
+
import { getDBInstance } from '@/database/core/web-server';
|
3
|
+
import { oidcEnv } from '@/envs/oidc';
|
4
|
+
import { OIDCProvider, createOIDCProvider } from '@/libs/oidc-provider/provider';
|
5
|
+
|
6
|
+
/**
|
7
|
+
* OIDC Provider 实例
|
8
|
+
*/
|
9
|
+
let provider: OIDCProvider;
|
10
|
+
|
11
|
+
/**
|
12
|
+
* 获取 OIDC Provider 实例
|
13
|
+
* @returns OIDC Provider 实例
|
14
|
+
*/
|
15
|
+
export const getOIDCProvider = async (): Promise<OIDCProvider> => {
|
16
|
+
if (!provider) {
|
17
|
+
if (!oidcEnv.ENABLE_OIDC) {
|
18
|
+
throw new Error('OIDC is not enabled. Set ENABLE_OIDC=1 to enable it.');
|
19
|
+
}
|
20
|
+
|
21
|
+
const baseUrl = appEnv.APP_URL!;
|
22
|
+
const db = getDBInstance();
|
23
|
+
provider = await createOIDCProvider(db, baseUrl);
|
24
|
+
}
|
25
|
+
|
26
|
+
return provider;
|
27
|
+
};
|
@@ -1,6 +1,7 @@
|
|
1
1
|
import { StateCreator } from 'zustand/vanilla';
|
2
2
|
|
3
3
|
import { chainSummaryHistory } from '@/chains/summaryHistory';
|
4
|
+
import { TraceNameMap } from '@/const/trace';
|
4
5
|
import { chatService } from '@/services/chat';
|
5
6
|
import { topicService } from '@/services/topic';
|
6
7
|
import { ChatStore } from '@/store/chat';
|
@@ -29,8 +30,12 @@ export const chatMemory: StateCreator<
|
|
29
30
|
onFinish: async (text) => {
|
30
31
|
historySummary = text;
|
31
32
|
},
|
32
|
-
|
33
33
|
params: { ...chainSummaryHistory(messages), model, provider, stream: false },
|
34
|
+
trace: {
|
35
|
+
sessionId: get().activeId,
|
36
|
+
topicId: get().activeTopicId,
|
37
|
+
traceName: TraceNameMap.SummaryHistoryMessages,
|
38
|
+
},
|
34
39
|
});
|
35
40
|
|
36
41
|
await topicService.updateTopic(topicId, {
|
package/src/types/hotkey.ts
CHANGED
@@ -1,8 +1,59 @@
|
|
1
|
-
import { KeyMapEnum } from '@lobehub/ui/es/Hotkey';
|
2
|
-
|
3
1
|
export const KeyEnum = {
|
4
|
-
|
2
|
+
Alt: 'alt',
|
3
|
+
Backquote: 'backquote',
|
4
|
+
// `
|
5
|
+
Backslash: 'backslash',
|
6
|
+
// \
|
7
|
+
Backspace: 'backspace',
|
8
|
+
BracketLeft: 'bracketleft',
|
9
|
+
// [
|
10
|
+
BracketRight: 'bracketright',
|
11
|
+
// ]
|
12
|
+
Comma: 'comma',
|
13
|
+
// ,
|
14
|
+
Ctrl: 'ctrl',
|
15
|
+
Down: 'down',
|
16
|
+
Enter: 'enter',
|
17
|
+
Equal: 'equal',
|
18
|
+
// =
|
19
|
+
Esc: 'esc',
|
20
|
+
Left: 'left',
|
21
|
+
LeftClick: 'left-click',
|
22
|
+
LeftDoubleClick: 'left-double-click',
|
23
|
+
Meta: 'meta',
|
24
|
+
// Command on Mac, Win on Win
|
25
|
+
MiddleClick: 'middle-click',
|
26
|
+
Minus: 'minus',
|
27
|
+
// -
|
28
|
+
Mod: 'mod',
|
29
|
+
|
5
30
|
Number: '1-9',
|
31
|
+
|
32
|
+
// Command on Mac, Ctrl on Win
|
33
|
+
Period: 'period',
|
34
|
+
|
35
|
+
// .
|
36
|
+
Plus: 'equal',
|
37
|
+
|
38
|
+
// +
|
39
|
+
QuestionMark: 'slash',
|
40
|
+
|
41
|
+
// ?
|
42
|
+
Quote: 'quote',
|
43
|
+
// '
|
44
|
+
Right: 'right',
|
45
|
+
RightClick: 'right-click',
|
46
|
+
RightDoubleClick: 'right-double-click',
|
47
|
+
|
48
|
+
Semicolon: 'semicolon',
|
49
|
+
// ;
|
50
|
+
Shift: 'shift',
|
51
|
+
|
52
|
+
Slash: 'slash',
|
53
|
+
// /
|
54
|
+
Space: 'space',
|
55
|
+
Tab: 'tab',
|
56
|
+
Up: 'up',
|
6
57
|
} as const;
|
7
58
|
|
8
59
|
export const HotkeyEnum = {
|