@lobehub/chat 1.79.7 → 1.79.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.
- package/.eslintrc.js +1 -0
- package/CHANGELOG.md +58 -0
- package/changelog/v1.json +18 -0
- package/docs/development/database-schema.dbml +119 -0
- package/locales/ar/models.json +12 -0
- package/locales/ar/oauth.json +40 -0
- package/locales/bg-BG/models.json +12 -0
- package/locales/bg-BG/oauth.json +40 -0
- package/locales/de-DE/models.json +12 -0
- package/locales/de-DE/oauth.json +40 -0
- package/locales/en-US/models.json +12 -0
- package/locales/en-US/oauth.json +40 -0
- package/locales/es-ES/models.json +12 -0
- package/locales/es-ES/oauth.json +40 -0
- package/locales/fa-IR/models.json +12 -0
- package/locales/fa-IR/oauth.json +40 -0
- package/locales/fr-FR/models.json +12 -0
- package/locales/fr-FR/oauth.json +40 -0
- package/locales/it-IT/models.json +12 -0
- package/locales/it-IT/oauth.json +40 -0
- package/locales/ja-JP/models.json +12 -0
- package/locales/ja-JP/oauth.json +40 -0
- package/locales/ko-KR/models.json +12 -0
- package/locales/ko-KR/oauth.json +40 -0
- package/locales/nl-NL/models.json +12 -0
- package/locales/nl-NL/oauth.json +40 -0
- package/locales/pl-PL/models.json +12 -0
- package/locales/pl-PL/oauth.json +40 -0
- package/locales/pt-BR/models.json +12 -0
- package/locales/pt-BR/oauth.json +40 -0
- package/locales/ru-RU/models.json +12 -0
- package/locales/ru-RU/oauth.json +40 -0
- package/locales/tr-TR/models.json +12 -0
- package/locales/tr-TR/oauth.json +40 -0
- package/locales/vi-VN/models.json +12 -0
- package/locales/vi-VN/oauth.json +40 -0
- package/locales/zh-CN/models.json +12 -0
- package/locales/zh-CN/oauth.json +40 -0
- package/locales/zh-TW/models.json +12 -0
- package/locales/zh-TW/oauth.json +40 -0
- package/package.json +4 -1
- package/scripts/generate-oidc-jwk.mjs +59 -0
- package/scripts/migrateServerDB/index.ts +3 -1
- package/src/app/(backend)/oidc/[...oidc]/route.ts +96 -0
- package/src/app/(backend)/oidc/consent/route.ts +131 -0
- package/src/app/(backend)/trpc/async/[trpc]/route.ts +1 -1
- package/src/app/(backend)/trpc/edge/[trpc]/route.ts +2 -2
- package/src/app/(backend)/trpc/lambda/[trpc]/route.ts +2 -2
- package/src/app/(backend)/trpc/tools/[trpc]/route.ts +2 -2
- package/src/app/[variants]/(main)/files/[id]/page.tsx +1 -1
- package/src/app/[variants]/oauth/consent/[uid]/Client.tsx +224 -0
- package/src/app/[variants]/oauth/consent/[uid]/ClientError.tsx +46 -0
- package/src/app/[variants]/oauth/consent/[uid]/failed/page.tsx +36 -0
- package/src/app/[variants]/oauth/consent/[uid]/page.tsx +69 -0
- package/src/app/[variants]/oauth/consent/[uid]/success/page.tsx +30 -0
- package/src/components/Branding/ProductLogo/index.tsx +6 -1
- package/src/config/aiModels/openai.ts +63 -41
- 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 +499 -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 +541 -0
- package/src/libs/oidc-provider/config.ts +52 -0
- package/src/libs/oidc-provider/http-adapter.ts +311 -0
- package/src/libs/oidc-provider/interaction-policy.ts +37 -0
- package/src/libs/oidc-provider/provider.ts +288 -0
- package/src/libs/trpc/async/init.ts +1 -1
- package/src/{server → libs/trpc/edge}/context.ts +2 -2
- package/src/libs/trpc/{index.ts → edge/index.ts} +8 -8
- package/src/libs/trpc/{init.ts → edge/init.ts} +2 -2
- package/src/libs/trpc/{middleware → edge/middleware}/jwtPayload.test.ts +3 -3
- package/src/libs/trpc/{middleware → edge/middleware}/jwtPayload.ts +3 -2
- package/src/libs/trpc/lambda/context.ts +70 -0
- package/src/libs/trpc/lambda/index.ts +39 -1
- package/src/libs/trpc/lambda/init.ts +26 -0
- package/src/libs/trpc/lambda/middleware/index.ts +2 -0
- package/src/libs/trpc/{middleware → lambda/middleware}/keyVaults.ts +2 -1
- package/src/libs/trpc/lambda/{serverDatabase.ts → middleware/serverDatabase.ts} +2 -1
- package/src/libs/trpc/middleware/userAuth.test.ts +3 -3
- package/src/libs/trpc/middleware/userAuth.ts +1 -1
- package/src/libs/trpc/mock.ts +7 -0
- package/src/locales/default/index.ts +2 -0
- package/src/locales/default/oauth.ts +43 -0
- package/src/middleware.ts +94 -6
- package/src/server/routers/edge/appStatus.ts +1 -1
- package/src/server/routers/edge/config/index.test.ts +2 -3
- package/src/server/routers/edge/config/index.ts +1 -1
- package/src/server/routers/edge/index.ts +1 -1
- package/src/server/routers/edge/upload.ts +1 -1
- package/src/server/routers/lambda/_template.ts +2 -2
- package/src/server/routers/lambda/agent.ts +2 -2
- package/src/server/routers/lambda/aiModel.ts +2 -2
- package/src/server/routers/lambda/aiProvider.ts +2 -2
- package/src/server/routers/lambda/chunk.ts +2 -3
- package/src/server/routers/lambda/exporter.ts +2 -2
- package/src/server/routers/lambda/file.ts +2 -2
- package/src/server/routers/lambda/importer.ts +2 -2
- package/src/server/routers/lambda/index.ts +1 -1
- package/src/server/routers/lambda/knowledgeBase.ts +2 -2
- package/src/server/routers/lambda/message.ts +2 -2
- package/src/server/routers/lambda/plugin.ts +2 -2
- package/src/server/routers/lambda/ragEval.ts +2 -3
- package/src/server/routers/lambda/session.ts +2 -2
- package/src/server/routers/lambda/sessionGroup.ts +2 -2
- package/src/server/routers/lambda/thread.ts +2 -2
- package/src/server/routers/lambda/topic.ts +2 -2
- package/src/server/routers/lambda/user.ts +2 -2
- package/src/server/routers/tools/__tests__/search.test.ts +2 -2
- package/src/server/routers/tools/index.ts +1 -1
- package/src/server/routers/tools/search.ts +2 -1
- package/src/server/services/oidc/index.ts +64 -0
- package/src/server/services/oidc/oidcProvider.ts +25 -0
- package/src/server/mock.ts +0 -8
- /package/src/{server/asyncContext.ts → libs/trpc/async/context.ts} +0 -0
@@ -10,40 +10,40 @@
|
|
10
10
|
import { DESKTOP_USER_ID } from '@/const/desktop';
|
11
11
|
import { isDesktop } from '@/const/version';
|
12
12
|
|
13
|
-
import {
|
13
|
+
import { userAuth } from '../middleware/userAuth';
|
14
|
+
import { edgeTrpc } from './init';
|
14
15
|
import { jwtPayloadChecker } from './middleware/jwtPayload';
|
15
|
-
import { userAuth } from './middleware/userAuth';
|
16
16
|
|
17
17
|
/**
|
18
18
|
* Create a router
|
19
19
|
* @link https://trpc.io/docs/v11/router
|
20
20
|
*/
|
21
|
-
export const router =
|
21
|
+
export const router = edgeTrpc.router;
|
22
22
|
|
23
23
|
/**
|
24
24
|
* Create an unprotected procedure
|
25
25
|
* @link https://trpc.io/docs/v11/procedures
|
26
26
|
**/
|
27
|
-
export const publicProcedure =
|
27
|
+
export const publicProcedure = edgeTrpc.procedure.use(({ next, ctx }) => {
|
28
28
|
return next({
|
29
29
|
ctx: { userId: isDesktop ? DESKTOP_USER_ID : ctx.userId },
|
30
30
|
});
|
31
31
|
});
|
32
32
|
|
33
33
|
// procedure that asserts that the user is logged in
|
34
|
-
export const authedProcedure =
|
34
|
+
export const authedProcedure = edgeTrpc.procedure.use(userAuth);
|
35
35
|
|
36
36
|
// procedure that asserts that the user add the password
|
37
|
-
export const passwordProcedure =
|
37
|
+
export const passwordProcedure = edgeTrpc.procedure.use(jwtPayloadChecker);
|
38
38
|
|
39
39
|
/**
|
40
40
|
* Merge multiple routers together
|
41
41
|
* @link https://trpc.io/docs/v11/merging-routers
|
42
42
|
*/
|
43
|
-
export const mergeRouters =
|
43
|
+
export const mergeRouters = edgeTrpc.mergeRouters;
|
44
44
|
|
45
45
|
/**
|
46
46
|
* Create a server-side caller
|
47
47
|
* @link https://trpc.io/docs/v11/server/server-side-calls
|
48
48
|
*/
|
49
|
-
export const createCallerFactory =
|
49
|
+
export const createCallerFactory = edgeTrpc.createCallerFactory;
|
@@ -10,9 +10,9 @@
|
|
10
10
|
import { initTRPC } from '@trpc/server';
|
11
11
|
import superjson from 'superjson';
|
12
12
|
|
13
|
-
import type {
|
13
|
+
import type { EdgeContext } from './context';
|
14
14
|
|
15
|
-
export const
|
15
|
+
export const edgeTrpc = initTRPC.context<EdgeContext>().create({
|
16
16
|
/**
|
17
17
|
* @link https://trpc.io/docs/v11/error-formatting
|
18
18
|
*/
|
@@ -2,9 +2,9 @@
|
|
2
2
|
import { TRPCError } from '@trpc/server';
|
3
3
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
4
4
|
|
5
|
-
import { createCallerFactory } from '@/libs/trpc';
|
6
|
-
import {
|
7
|
-
import {
|
5
|
+
import { createCallerFactory } from '@/libs/trpc/edge';
|
6
|
+
import { AuthContext, createContextInner } from '@/libs/trpc/edge/context';
|
7
|
+
import { edgeTrpc as trpc } from '@/libs/trpc/edge/init';
|
8
8
|
import * as utils from '@/utils/server/jwt';
|
9
9
|
|
10
10
|
import { jwtPayloadChecker } from './jwtPayload';
|
@@ -1,9 +1,10 @@
|
|
1
1
|
import { TRPCError } from '@trpc/server';
|
2
2
|
|
3
|
-
import { trpc } from '@/libs/trpc/init';
|
4
3
|
import { getJWTPayload } from '@/utils/server/jwt';
|
5
4
|
|
6
|
-
|
5
|
+
import { edgeTrpc } from '../init';
|
6
|
+
|
7
|
+
export const jwtPayloadChecker = edgeTrpc.middleware(async (opts) => {
|
7
8
|
const { ctx } = opts;
|
8
9
|
|
9
10
|
if (!ctx.authorizationHeader) throw new TRPCError({ code: 'UNAUTHORIZED' });
|
@@ -0,0 +1,70 @@
|
|
1
|
+
import { User } from 'next-auth';
|
2
|
+
import { NextRequest } from 'next/server';
|
3
|
+
|
4
|
+
import { JWTPayload, LOBE_CHAT_AUTH_HEADER, enableClerk, enableNextAuth } from '@/const/auth';
|
5
|
+
import { ClerkAuth, IClerkAuth } from '@/libs/clerk-auth';
|
6
|
+
|
7
|
+
export interface AuthContext {
|
8
|
+
authorizationHeader?: string | null;
|
9
|
+
clerkAuth?: IClerkAuth;
|
10
|
+
jwtPayload?: JWTPayload | null;
|
11
|
+
nextAuth?: User;
|
12
|
+
userId?: string | null;
|
13
|
+
}
|
14
|
+
|
15
|
+
/**
|
16
|
+
* Inner function for `createContext` where we create the context.
|
17
|
+
* This is useful for testing when we don't want to mock Next.js' request/response
|
18
|
+
*/
|
19
|
+
export const createContextInner = async (params?: {
|
20
|
+
authorizationHeader?: string | null;
|
21
|
+
clerkAuth?: IClerkAuth;
|
22
|
+
nextAuth?: User;
|
23
|
+
userId?: string | null;
|
24
|
+
}): Promise<AuthContext> => ({
|
25
|
+
authorizationHeader: params?.authorizationHeader,
|
26
|
+
clerkAuth: params?.clerkAuth,
|
27
|
+
nextAuth: params?.nextAuth,
|
28
|
+
userId: params?.userId,
|
29
|
+
});
|
30
|
+
|
31
|
+
export type LambdaContext = Awaited<ReturnType<typeof createContextInner>>;
|
32
|
+
|
33
|
+
/**
|
34
|
+
* Creates context for an incoming request
|
35
|
+
* @link https://trpc.io/docs/v11/context
|
36
|
+
*/
|
37
|
+
export const createLambdaContext = async (request: NextRequest): Promise<LambdaContext> => {
|
38
|
+
// for API-response caching see https://trpc.io/docs/v11/caching
|
39
|
+
|
40
|
+
const authorization = request.headers.get(LOBE_CHAT_AUTH_HEADER);
|
41
|
+
|
42
|
+
let userId;
|
43
|
+
let auth;
|
44
|
+
|
45
|
+
if (enableClerk) {
|
46
|
+
const clerkAuth = new ClerkAuth();
|
47
|
+
const result = clerkAuth.getAuthFromRequest(request);
|
48
|
+
auth = result.clerkAuth;
|
49
|
+
userId = result.userId;
|
50
|
+
|
51
|
+
return createContextInner({ authorizationHeader: authorization, clerkAuth: auth, userId });
|
52
|
+
}
|
53
|
+
|
54
|
+
if (enableNextAuth) {
|
55
|
+
try {
|
56
|
+
const { default: NextAuthEdge } = await import('@/libs/next-auth/edge');
|
57
|
+
|
58
|
+
const session = await NextAuthEdge.auth();
|
59
|
+
if (session && session?.user?.id) {
|
60
|
+
auth = session.user;
|
61
|
+
userId = session.user.id;
|
62
|
+
}
|
63
|
+
return createContextInner({ authorizationHeader: authorization, nextAuth: auth, userId });
|
64
|
+
} catch (e) {
|
65
|
+
console.error('next auth err', e);
|
66
|
+
}
|
67
|
+
}
|
68
|
+
|
69
|
+
return createContextInner({ authorizationHeader: authorization, userId });
|
70
|
+
};
|
@@ -1 +1,39 @@
|
|
1
|
-
|
1
|
+
/**
|
2
|
+
* This is your entry point to setup the root configuration for tRPC on the server.
|
3
|
+
* - `initTRPC` should only be used once per app.
|
4
|
+
* - We export only the functionality that we use so we can enforce which base procedures should be used
|
5
|
+
*
|
6
|
+
* Learn how to create protected base procedures and other things below:
|
7
|
+
* @link https://trpc.io/docs/v11/router
|
8
|
+
* @link https://trpc.io/docs/v11/procedures
|
9
|
+
*/
|
10
|
+
import { DESKTOP_USER_ID } from '@/const/desktop';
|
11
|
+
import { isDesktop } from '@/const/version';
|
12
|
+
|
13
|
+
import { userAuth } from '../middleware/userAuth';
|
14
|
+
import { trpc } from './init';
|
15
|
+
|
16
|
+
/**
|
17
|
+
* Create a router
|
18
|
+
* @link https://trpc.io/docs/v11/router
|
19
|
+
*/
|
20
|
+
export const router = trpc.router;
|
21
|
+
|
22
|
+
/**
|
23
|
+
* Create an unprotected procedure
|
24
|
+
* @link https://trpc.io/docs/v11/procedures
|
25
|
+
**/
|
26
|
+
export const publicProcedure = trpc.procedure.use(({ next, ctx }) => {
|
27
|
+
return next({
|
28
|
+
ctx: { userId: isDesktop ? DESKTOP_USER_ID : ctx.userId },
|
29
|
+
});
|
30
|
+
});
|
31
|
+
|
32
|
+
// procedure that asserts that the user is logged in
|
33
|
+
export const authedProcedure = trpc.procedure.use(userAuth);
|
34
|
+
|
35
|
+
/**
|
36
|
+
* Create a server-side caller
|
37
|
+
* @link https://trpc.io/docs/v11/server/server-side-calls
|
38
|
+
*/
|
39
|
+
export const createCallerFactory = trpc.createCallerFactory;
|
@@ -0,0 +1,26 @@
|
|
1
|
+
/**
|
2
|
+
* This is your entry point to setup the root configuration for tRPC on the server.
|
3
|
+
* - `initTRPC` should only be used once per app.
|
4
|
+
* - We export only the functionality that we use so we can enforce which base procedures should be used
|
5
|
+
*
|
6
|
+
* Learn how to create protected base procedures and other things below:
|
7
|
+
* @link https://trpc.io/docs/v11/router
|
8
|
+
* @link https://trpc.io/docs/v11/procedures
|
9
|
+
*/
|
10
|
+
import { initTRPC } from '@trpc/server';
|
11
|
+
import superjson from 'superjson';
|
12
|
+
|
13
|
+
import type { LambdaContext } from './context';
|
14
|
+
|
15
|
+
export const trpc = initTRPC.context<LambdaContext>().create({
|
16
|
+
/**
|
17
|
+
* @link https://trpc.io/docs/v11/error-formatting
|
18
|
+
*/
|
19
|
+
errorFormatter({ shape }) {
|
20
|
+
return shape;
|
21
|
+
},
|
22
|
+
/**
|
23
|
+
* @link https://trpc.io/docs/v11/data-transformers
|
24
|
+
*/
|
25
|
+
transformer: superjson,
|
26
|
+
});
|
@@ -1,8 +1,9 @@
|
|
1
1
|
import { TRPCError } from '@trpc/server';
|
2
2
|
|
3
|
-
import { trpc } from '@/libs/trpc/init';
|
4
3
|
import { getJWTPayload } from '@/utils/server/jwt';
|
5
4
|
|
5
|
+
import { trpc } from '../init';
|
6
|
+
|
6
7
|
export const keyVaults = trpc.middleware(async (opts) => {
|
7
8
|
const { ctx } = opts;
|
8
9
|
|
@@ -1,10 +1,10 @@
|
|
1
1
|
import { TRPCError } from '@trpc/server';
|
2
2
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
3
3
|
|
4
|
-
import { createCallerFactory } from '@/libs/trpc';
|
5
|
-
import { AuthContext, createContextInner } from '@/
|
4
|
+
import { createCallerFactory } from '@/libs/trpc/lambda';
|
5
|
+
import { AuthContext, createContextInner } from '@/libs/trpc/lambda/context';
|
6
6
|
|
7
|
-
import { trpc } from '../init';
|
7
|
+
import { trpc } from '../lambda/init';
|
8
8
|
import { userAuth } from './userAuth';
|
9
9
|
|
10
10
|
const appRouter = trpc.router({
|
@@ -4,7 +4,7 @@ import { enableClerk } from '@/const/auth';
|
|
4
4
|
import { DESKTOP_USER_ID } from '@/const/desktop';
|
5
5
|
import { isDesktop } from '@/const/version';
|
6
6
|
|
7
|
-
import { trpc } from '../init';
|
7
|
+
import { trpc } from '../lambda/init';
|
8
8
|
|
9
9
|
export const userAuth = trpc.middleware(async (opts) => {
|
10
10
|
const { ctx } = opts;
|
@@ -13,6 +13,7 @@ import metadata from './metadata';
|
|
13
13
|
import migration from './migration';
|
14
14
|
import modelProvider from './modelProvider';
|
15
15
|
import models from './models';
|
16
|
+
import oauth from './oauth';
|
16
17
|
import plugin from './plugin';
|
17
18
|
import portal from './portal';
|
18
19
|
import providers from './providers';
|
@@ -39,6 +40,7 @@ const resources = {
|
|
39
40
|
migration,
|
40
41
|
modelProvider,
|
41
42
|
models,
|
43
|
+
oauth,
|
42
44
|
plugin,
|
43
45
|
portal,
|
44
46
|
providers,
|
@@ -0,0 +1,43 @@
|
|
1
|
+
const oauth = {
|
2
|
+
consent: {
|
3
|
+
buttons: {
|
4
|
+
accept: '授权',
|
5
|
+
deny: '拒绝',
|
6
|
+
},
|
7
|
+
description: '应用 {{clientName}} 申请您的账户授权',
|
8
|
+
|
9
|
+
error: {
|
10
|
+
sessionInvalid: {
|
11
|
+
message: '授权会话已过期或无效,请重新发起授权流程。',
|
12
|
+
title: '授权会话无效',
|
13
|
+
},
|
14
|
+
title: '发生错误',
|
15
|
+
unsupportedInteraction: {
|
16
|
+
message: '不支持的交互类型: {promptName}',
|
17
|
+
title: '不支持的交互类型',
|
18
|
+
},
|
19
|
+
},
|
20
|
+
permissionsTitle: '请求以下权限:',
|
21
|
+
redirectUri: '授权成功后将重定向到',
|
22
|
+
scope: {
|
23
|
+
'email': '访问您的电子邮件地址',
|
24
|
+
'offline_access': '允许客户端访问您的数据',
|
25
|
+
'openid': '使用您的 LobeChat 账户进行身份验证',
|
26
|
+
'profile': '访问您的基本资料信息(名称、头像等)',
|
27
|
+
'sync-read': '读取您的同步数据',
|
28
|
+
'sync-write': '写入并更新您的同步数据',
|
29
|
+
},
|
30
|
+
title: '授权 {{clientName}}',
|
31
|
+
},
|
32
|
+
failed: {
|
33
|
+
backToHome: '返回首页',
|
34
|
+
subTitle: '您已拒绝授权应用访问您的 LobeChat 账户',
|
35
|
+
title: '授权被拒绝',
|
36
|
+
},
|
37
|
+
success: {
|
38
|
+
subTitle: '您已成功授权应用访问您的 LobeChat 账户,可以关闭该页面了',
|
39
|
+
title: '授权成功',
|
40
|
+
},
|
41
|
+
};
|
42
|
+
|
43
|
+
export default oauth;
|
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
|
@@ -4,9 +4,8 @@ import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
4
4
|
/**
|
5
5
|
* This file contains the root router of your tRPC-backend
|
6
6
|
*/
|
7
|
-
import { createCallerFactory } from '@/libs/trpc';
|
8
|
-
import { AuthContext, createContextInner } from '@/
|
9
|
-
import { GlobalServerConfig } from '@/types/serverConfig';
|
7
|
+
import { createCallerFactory } from '@/libs/trpc/edge';
|
8
|
+
import { AuthContext, createContextInner } from '@/libs/trpc/edge/context';
|
10
9
|
|
11
10
|
import { configRouter } from './index';
|
12
11
|
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import { getServerFeatureFlagsValue } from '@/config/featureFlags';
|
2
|
-
import { publicProcedure, router } from '@/libs/trpc';
|
2
|
+
import { publicProcedure, router } from '@/libs/trpc/edge';
|
3
3
|
import { getServerDefaultAgentConfig, getServerGlobalConfig } from '@/server/globalConfig';
|
4
4
|
import { GlobalRuntimeConfig } from '@/types/serverConfig';
|
5
5
|
|
@@ -1,7 +1,7 @@
|
|
1
1
|
/**
|
2
2
|
* This file contains the edge router of Lobe Chat tRPC-backend
|
3
3
|
*/
|
4
|
-
import { publicProcedure, router } from '@/libs/trpc';
|
4
|
+
import { publicProcedure, router } from '@/libs/trpc/edge';
|
5
5
|
|
6
6
|
import { appStatusRouter } from './appStatus';
|
7
7
|
import { configRouter } from './config';
|
@@ -2,8 +2,8 @@ import { z } from 'zod';
|
|
2
2
|
|
3
3
|
import { SessionGroupModel } from '@/database/models/sessionGroup';
|
4
4
|
import { insertSessionGroupSchema } from '@/database/schemas';
|
5
|
-
import { authedProcedure, router } from '@/libs/trpc';
|
6
|
-
import { serverDatabase } from '@/libs/trpc/lambda';
|
5
|
+
import { authedProcedure, router } from '@/libs/trpc/lambda';
|
6
|
+
import { serverDatabase } from '@/libs/trpc/lambda/middleware';
|
7
7
|
import { SessionGroupItem } from '@/types/session';
|
8
8
|
|
9
9
|
const sessionProcedure = authedProcedure.use(serverDatabase).use(async (opts) => {
|
@@ -8,8 +8,8 @@ import { KnowledgeBaseModel } from '@/database/models/knowledgeBase';
|
|
8
8
|
import { SessionModel } from '@/database/models/session';
|
9
9
|
import { UserModel } from '@/database/models/user';
|
10
10
|
import { pino } from '@/libs/logger';
|
11
|
-
import { authedProcedure, router } from '@/libs/trpc';
|
12
|
-
import { serverDatabase } from '@/libs/trpc/lambda';
|
11
|
+
import { authedProcedure, router } from '@/libs/trpc/lambda';
|
12
|
+
import { serverDatabase } from '@/libs/trpc/lambda/middleware';
|
13
13
|
import { AgentService } from '@/server/services/agent';
|
14
14
|
import { KnowledgeItem, KnowledgeType } from '@/types/knowledgeBase';
|
15
15
|
|
@@ -3,8 +3,8 @@ import { z } from 'zod';
|
|
3
3
|
import { AiModelModel } from '@/database/models/aiModel';
|
4
4
|
import { UserModel } from '@/database/models/user';
|
5
5
|
import { AiInfraRepos } from '@/database/repositories/aiInfra';
|
6
|
-
import { authedProcedure, router } from '@/libs/trpc';
|
7
|
-
import { serverDatabase } from '@/libs/trpc/lambda';
|
6
|
+
import { authedProcedure, router } from '@/libs/trpc/lambda';
|
7
|
+
import { serverDatabase } from '@/libs/trpc/lambda/middleware';
|
8
8
|
import { getServerGlobalConfig } from '@/server/globalConfig';
|
9
9
|
import { KeyVaultsGateKeeper } from '@/server/modules/KeyVaultsEncrypt';
|
10
10
|
import {
|
@@ -3,8 +3,8 @@ import { z } from 'zod';
|
|
3
3
|
import { AiProviderModel } from '@/database/models/aiProvider';
|
4
4
|
import { UserModel } from '@/database/models/user';
|
5
5
|
import { AiInfraRepos } from '@/database/repositories/aiInfra';
|
6
|
-
import { authedProcedure, router } from '@/libs/trpc';
|
7
|
-
import { serverDatabase } from '@/libs/trpc/lambda';
|
6
|
+
import { authedProcedure, router } from '@/libs/trpc/lambda';
|
7
|
+
import { serverDatabase } from '@/libs/trpc/lambda/middleware';
|
8
8
|
import { getServerGlobalConfig } from '@/server/globalConfig';
|
9
9
|
import { KeyVaultsGateKeeper } from '@/server/modules/KeyVaultsEncrypt';
|
10
10
|
import {
|
@@ -9,9 +9,8 @@ import { EmbeddingModel } from '@/database/models/embedding';
|
|
9
9
|
import { FileModel } from '@/database/models/file';
|
10
10
|
import { MessageModel } from '@/database/models/message';
|
11
11
|
import { knowledgeBaseFiles } from '@/database/schemas';
|
12
|
-
import { authedProcedure, router } from '@/libs/trpc';
|
13
|
-
import { serverDatabase } from '@/libs/trpc/lambda';
|
14
|
-
import { keyVaults } from '@/libs/trpc/middleware/keyVaults';
|
12
|
+
import { authedProcedure, router } from '@/libs/trpc/lambda';
|
13
|
+
import { keyVaults, serverDatabase } from '@/libs/trpc/lambda/middleware';
|
15
14
|
import { getServerDefaultFilesConfig } from '@/server/globalConfig';
|
16
15
|
import { initAgentRuntimeWithUserPayload } from '@/server/modules/AgentRuntime';
|
17
16
|
import { ChunkService } from '@/server/services/chunk';
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import { DrizzleMigrationModel } from '@/database/models/drizzleMigration';
|
2
2
|
import { DataExporterRepos } from '@/database/repositories/dataExporter';
|
3
|
-
import { authedProcedure, router } from '@/libs/trpc';
|
4
|
-
import { serverDatabase } from '@/libs/trpc/lambda';
|
3
|
+
import { authedProcedure, router } from '@/libs/trpc/lambda';
|
4
|
+
import { serverDatabase } from '@/libs/trpc/lambda/middleware';
|
5
5
|
import { ExportDatabaseData } from '@/types/export';
|
6
6
|
|
7
7
|
const exportProcedure = authedProcedure.use(serverDatabase).use(async (opts) => {
|