@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.
Files changed (121) hide show
  1. package/.eslintrc.js +1 -0
  2. package/CHANGELOG.md +58 -0
  3. package/changelog/v1.json +18 -0
  4. package/docs/development/database-schema.dbml +119 -0
  5. package/locales/ar/models.json +12 -0
  6. package/locales/ar/oauth.json +40 -0
  7. package/locales/bg-BG/models.json +12 -0
  8. package/locales/bg-BG/oauth.json +40 -0
  9. package/locales/de-DE/models.json +12 -0
  10. package/locales/de-DE/oauth.json +40 -0
  11. package/locales/en-US/models.json +12 -0
  12. package/locales/en-US/oauth.json +40 -0
  13. package/locales/es-ES/models.json +12 -0
  14. package/locales/es-ES/oauth.json +40 -0
  15. package/locales/fa-IR/models.json +12 -0
  16. package/locales/fa-IR/oauth.json +40 -0
  17. package/locales/fr-FR/models.json +12 -0
  18. package/locales/fr-FR/oauth.json +40 -0
  19. package/locales/it-IT/models.json +12 -0
  20. package/locales/it-IT/oauth.json +40 -0
  21. package/locales/ja-JP/models.json +12 -0
  22. package/locales/ja-JP/oauth.json +40 -0
  23. package/locales/ko-KR/models.json +12 -0
  24. package/locales/ko-KR/oauth.json +40 -0
  25. package/locales/nl-NL/models.json +12 -0
  26. package/locales/nl-NL/oauth.json +40 -0
  27. package/locales/pl-PL/models.json +12 -0
  28. package/locales/pl-PL/oauth.json +40 -0
  29. package/locales/pt-BR/models.json +12 -0
  30. package/locales/pt-BR/oauth.json +40 -0
  31. package/locales/ru-RU/models.json +12 -0
  32. package/locales/ru-RU/oauth.json +40 -0
  33. package/locales/tr-TR/models.json +12 -0
  34. package/locales/tr-TR/oauth.json +40 -0
  35. package/locales/vi-VN/models.json +12 -0
  36. package/locales/vi-VN/oauth.json +40 -0
  37. package/locales/zh-CN/models.json +12 -0
  38. package/locales/zh-CN/oauth.json +40 -0
  39. package/locales/zh-TW/models.json +12 -0
  40. package/locales/zh-TW/oauth.json +40 -0
  41. package/package.json +4 -1
  42. package/scripts/generate-oidc-jwk.mjs +59 -0
  43. package/scripts/migrateServerDB/index.ts +3 -1
  44. package/src/app/(backend)/oidc/[...oidc]/route.ts +96 -0
  45. package/src/app/(backend)/oidc/consent/route.ts +131 -0
  46. package/src/app/(backend)/trpc/async/[trpc]/route.ts +1 -1
  47. package/src/app/(backend)/trpc/edge/[trpc]/route.ts +2 -2
  48. package/src/app/(backend)/trpc/lambda/[trpc]/route.ts +2 -2
  49. package/src/app/(backend)/trpc/tools/[trpc]/route.ts +2 -2
  50. package/src/app/[variants]/(main)/files/[id]/page.tsx +1 -1
  51. package/src/app/[variants]/oauth/consent/[uid]/Client.tsx +224 -0
  52. package/src/app/[variants]/oauth/consent/[uid]/ClientError.tsx +46 -0
  53. package/src/app/[variants]/oauth/consent/[uid]/failed/page.tsx +36 -0
  54. package/src/app/[variants]/oauth/consent/[uid]/page.tsx +69 -0
  55. package/src/app/[variants]/oauth/consent/[uid]/success/page.tsx +30 -0
  56. package/src/components/Branding/ProductLogo/index.tsx +6 -1
  57. package/src/config/aiModels/openai.ts +63 -41
  58. package/src/database/client/migrations.json +27 -8
  59. package/src/database/migrations/0020_add_oidc.sql +124 -0
  60. package/src/database/migrations/meta/0020_snapshot.json +4975 -0
  61. package/src/database/migrations/meta/_journal.json +7 -0
  62. package/src/database/repositories/tableViewer/index.test.ts +1 -1
  63. package/src/database/schemas/index.ts +1 -0
  64. package/src/database/schemas/oidc.ts +158 -0
  65. package/src/database/server/models/__tests__/adapter.test.ts +499 -0
  66. package/src/envs/oidc.ts +18 -0
  67. package/src/libs/agent-runtime/azureOpenai/index.ts +4 -1
  68. package/src/libs/agent-runtime/utils/streams/protocol.ts +2 -4
  69. package/src/libs/oidc-provider/adapter.ts +541 -0
  70. package/src/libs/oidc-provider/config.ts +52 -0
  71. package/src/libs/oidc-provider/http-adapter.ts +311 -0
  72. package/src/libs/oidc-provider/interaction-policy.ts +37 -0
  73. package/src/libs/oidc-provider/provider.ts +288 -0
  74. package/src/libs/trpc/async/init.ts +1 -1
  75. package/src/{server → libs/trpc/edge}/context.ts +2 -2
  76. package/src/libs/trpc/{index.ts → edge/index.ts} +8 -8
  77. package/src/libs/trpc/{init.ts → edge/init.ts} +2 -2
  78. package/src/libs/trpc/{middleware → edge/middleware}/jwtPayload.test.ts +3 -3
  79. package/src/libs/trpc/{middleware → edge/middleware}/jwtPayload.ts +3 -2
  80. package/src/libs/trpc/lambda/context.ts +70 -0
  81. package/src/libs/trpc/lambda/index.ts +39 -1
  82. package/src/libs/trpc/lambda/init.ts +26 -0
  83. package/src/libs/trpc/lambda/middleware/index.ts +2 -0
  84. package/src/libs/trpc/{middleware → lambda/middleware}/keyVaults.ts +2 -1
  85. package/src/libs/trpc/lambda/{serverDatabase.ts → middleware/serverDatabase.ts} +2 -1
  86. package/src/libs/trpc/middleware/userAuth.test.ts +3 -3
  87. package/src/libs/trpc/middleware/userAuth.ts +1 -1
  88. package/src/libs/trpc/mock.ts +7 -0
  89. package/src/locales/default/index.ts +2 -0
  90. package/src/locales/default/oauth.ts +43 -0
  91. package/src/middleware.ts +94 -6
  92. package/src/server/routers/edge/appStatus.ts +1 -1
  93. package/src/server/routers/edge/config/index.test.ts +2 -3
  94. package/src/server/routers/edge/config/index.ts +1 -1
  95. package/src/server/routers/edge/index.ts +1 -1
  96. package/src/server/routers/edge/upload.ts +1 -1
  97. package/src/server/routers/lambda/_template.ts +2 -2
  98. package/src/server/routers/lambda/agent.ts +2 -2
  99. package/src/server/routers/lambda/aiModel.ts +2 -2
  100. package/src/server/routers/lambda/aiProvider.ts +2 -2
  101. package/src/server/routers/lambda/chunk.ts +2 -3
  102. package/src/server/routers/lambda/exporter.ts +2 -2
  103. package/src/server/routers/lambda/file.ts +2 -2
  104. package/src/server/routers/lambda/importer.ts +2 -2
  105. package/src/server/routers/lambda/index.ts +1 -1
  106. package/src/server/routers/lambda/knowledgeBase.ts +2 -2
  107. package/src/server/routers/lambda/message.ts +2 -2
  108. package/src/server/routers/lambda/plugin.ts +2 -2
  109. package/src/server/routers/lambda/ragEval.ts +2 -3
  110. package/src/server/routers/lambda/session.ts +2 -2
  111. package/src/server/routers/lambda/sessionGroup.ts +2 -2
  112. package/src/server/routers/lambda/thread.ts +2 -2
  113. package/src/server/routers/lambda/topic.ts +2 -2
  114. package/src/server/routers/lambda/user.ts +2 -2
  115. package/src/server/routers/tools/__tests__/search.test.ts +2 -2
  116. package/src/server/routers/tools/index.ts +1 -1
  117. package/src/server/routers/tools/search.ts +2 -1
  118. package/src/server/services/oidc/index.ts +64 -0
  119. package/src/server/services/oidc/oidcProvider.ts +25 -0
  120. package/src/server/mock.ts +0 -8
  121. /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 { trpc } from './init';
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 = trpc.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 = trpc.procedure.use(({ next, ctx }) => {
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 = trpc.procedure.use(userAuth);
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 = trpc.procedure.use(jwtPayloadChecker);
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 = trpc.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 = trpc.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 { Context } from '@/server/context';
13
+ import type { EdgeContext } from './context';
14
14
 
15
- export const trpc = initTRPC.context<Context>().create({
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 { trpc } from '@/libs/trpc/init';
7
- import { AuthContext, createContextInner } from '@/server/context';
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
- export const jwtPayloadChecker = trpc.middleware(async (opts) => {
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
- export * from './serverDatabase';
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
+ });
@@ -0,0 +1,2 @@
1
+ export * from './keyVaults';
2
+ export * from './serverDatabase';
@@ -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,5 +1,6 @@
1
1
  import { getServerDB } from '@/database/core/db-adaptor';
2
- import { trpc } from '@/libs/trpc/init';
2
+
3
+ import { trpc } from '../init';
3
4
 
4
5
  export const serverDatabase = trpc.middleware(async (opts) => {
5
6
  const serverDB = await getServerDB();
@@ -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 '@/server/context';
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;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * This file contains the root router of your tRPC-backend
3
+ */
4
+ import { createCallerFactory } from '@/libs/trpc/lambda';
5
+ import { lambdaRouter } from '@/server/routers/lambda';
6
+
7
+ export const createCaller = createCallerFactory(lambdaRouter);
@@ -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 (['/api', '/trpc', '/webapi'].some((path) => url.pathname.startsWith(path))) {
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. cookie 中读取用户偏好
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
- // 2. 创建规范化的偏好值
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
- console.log(`[rewrite] ${url.pathname} -> ${nextURL}`);
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
- if (isProtectedRoute(req)) await auth.protect();
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
- return defaultMiddleware(req);
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
@@ -1,3 +1,3 @@
1
- import { router } from '@/libs/trpc';
1
+ import { router } from '@/libs/trpc/edge';
2
2
 
3
3
  export const appStatusRouter = router({});
@@ -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 '@/server/context';
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';
@@ -1,6 +1,6 @@
1
1
  import { z } from 'zod';
2
2
 
3
- import { passwordProcedure, router } from '@/libs/trpc';
3
+ import { passwordProcedure, router } from '@/libs/trpc/edge';
4
4
  import { S3 } from '@/server/modules/S3';
5
5
 
6
6
  export const uploadRouter = router({
@@ -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) => {