@lobehub/lobehub 2.0.0-next.344 → 2.0.0-next.346

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 (185) hide show
  1. package/.cursor/rules/i18n.mdc +1 -1
  2. package/.cursor/rules/modal-imperative.mdc +162 -0
  3. package/.cursor/rules/rules-index.mdc +1 -0
  4. package/.env.example +0 -14
  5. package/.eslintrc.js +8 -1
  6. package/CHANGELOG.md +66 -0
  7. package/CLAUDE.md +4 -2
  8. package/Dockerfile +3 -13
  9. package/README.md +3 -5
  10. package/README.zh-CN.md +3 -5
  11. package/changelog/v1.json +20 -0
  12. package/docs/self-hosting/advanced/auth/clerk-to-betterauth.mdx +11 -42
  13. package/docs/self-hosting/advanced/auth/clerk-to-betterauth.zh-CN.mdx +10 -41
  14. package/e2e/src/support/webServer.ts +2 -0
  15. package/locales/ar/error.json +0 -4
  16. package/locales/bg-BG/error.json +0 -4
  17. package/locales/de-DE/error.json +0 -4
  18. package/locales/en-US/error.json +0 -4
  19. package/locales/es-ES/error.json +0 -4
  20. package/locales/fa-IR/error.json +0 -4
  21. package/locales/fr-FR/error.json +0 -4
  22. package/locales/it-IT/error.json +0 -4
  23. package/locales/ja-JP/error.json +0 -4
  24. package/locales/ko-KR/error.json +0 -4
  25. package/locales/nl-NL/error.json +0 -4
  26. package/locales/pl-PL/error.json +0 -4
  27. package/locales/pt-BR/error.json +0 -4
  28. package/locales/ru-RU/error.json +0 -4
  29. package/locales/tr-TR/error.json +0 -4
  30. package/locales/vi-VN/error.json +0 -4
  31. package/locales/zh-CN/error.json +0 -4
  32. package/locales/zh-TW/error.json +0 -4
  33. package/package.json +12 -12
  34. package/packages/builtin-agents/package.json +2 -0
  35. package/packages/builtin-agents/src/agents/agent-builder/index.ts +4 -2
  36. package/packages/builtin-agents/src/agents/group-agent-builder/index.ts +4 -2
  37. package/packages/builtin-agents/src/agents/page-agent/index.ts +5 -2
  38. package/packages/context-engine/src/engine/messages/MessagesEngine.ts +9 -9
  39. package/packages/context-engine/src/providers/GroupContextInjector.ts +19 -33
  40. package/packages/context-engine/src/providers/__tests__/GroupContextInjector.test.ts +79 -43
  41. package/packages/context-engine/src/providers/__tests__/__snapshots__/GroupContextInjector.test.ts.snap +5 -15
  42. package/packages/database/src/repositories/userMemory/__tests__/UserMemoryTopicRepository.test.ts +24 -3
  43. package/packages/file-loaders/package.json +1 -1
  44. package/packages/file-loaders/src/loadFile.ts +10 -15
  45. package/packages/file-loaders/src/loaders/index.ts +68 -19
  46. package/packages/file-loaders/src/loaders/pdf/__snapshots__/index.test.ts.snap +1 -1
  47. package/packages/file-loaders/test/__snapshots__/loaders.test.ts.snap +1 -1
  48. package/packages/model-bank/src/modelProviders/comfyui.ts +0 -1
  49. package/packages/model-bank/src/modelProviders/fal.ts +0 -1
  50. package/packages/types/src/fetch.ts +1 -2
  51. package/packages/utils/src/server/__tests__/auth.test.ts +0 -47
  52. package/packages/utils/src/server/auth.ts +1 -9
  53. package/pnpm-workspace.yaml +1 -0
  54. package/scripts/_shared/checkDeprecatedClerkEnv.js +42 -0
  55. package/scripts/changelogWorkflow/buildStaticChangelog.ts +2 -1
  56. package/scripts/clerk-to-betterauth/_internal/types.ts +53 -20
  57. package/scripts/clerk-to-betterauth/export-clerk-users-with-api.ts +43 -36
  58. package/scripts/countEnWord.ts +1 -1
  59. package/scripts/electronWorkflow/modifiers/appCode.mts +2 -131
  60. package/scripts/i18nWorkflow/protectedPatterns.ts +1 -2
  61. package/scripts/prebuild.mts +10 -8
  62. package/scripts/serverLauncher/startServer.js +23 -5
  63. package/src/app/(backend)/middleware/auth/index.test.ts +8 -4
  64. package/src/app/(backend)/middleware/auth/index.ts +0 -15
  65. package/src/app/(backend)/middleware/auth/utils.test.ts +0 -28
  66. package/src/app/(backend)/middleware/auth/utils.ts +2 -17
  67. package/src/app/(backend)/webapi/chat/[provider]/route.test.ts +3 -51
  68. package/src/app/(backend)/webapi/models/[provider]/route.test.ts +8 -4
  69. package/src/app/[variants]/(auth)/next-auth/signin/AuthSignInBox.tsx +7 -6
  70. package/src/app/[variants]/(auth)/signup/[[...signup]]/page.tsx +1 -16
  71. package/src/app/[variants]/(main)/home/_layout/Body/Agent/List/index.tsx +1 -1
  72. package/src/app/[variants]/(main)/home/features/InputArea/SkillInstallBanner.tsx +13 -13
  73. package/src/app/[variants]/(main)/home/features/RecentPage/Item.tsx +2 -2
  74. package/src/app/[variants]/(main)/resource/features/store/action.ts +2 -2
  75. package/src/app/[variants]/(main)/resource/features/store/initialState.ts +2 -2
  76. package/src/app/[variants]/(main)/resource/store/action.ts +2 -2
  77. package/src/app/[variants]/(main)/resource/store/initialState.ts +2 -2
  78. package/src/app/[variants]/(main)/settings/hooks/useCategory.tsx +3 -21
  79. package/src/app/[variants]/(main)/settings/profile/features/AvatarRow.tsx +1 -2
  80. package/src/app/[variants]/(main)/settings/security/index.tsx +1 -22
  81. package/src/app/[variants]/(main)/settings/skill/features/KlavisSkillItem.tsx +12 -14
  82. package/src/app/[variants]/(main)/settings/skill/features/LobehubSkillItem.tsx +8 -14
  83. package/src/app/[variants]/(main)/settings/skill/index.tsx +7 -5
  84. package/src/app/[variants]/(mobile)/me/(home)/__tests__/UserBanner.test.tsx +2 -35
  85. package/src/app/[variants]/(mobile)/me/(home)/__tests__/useCategory.test.tsx +0 -20
  86. package/src/app/[variants]/(mobile)/me/(home)/features/UserBanner.tsx +1 -2
  87. package/src/app/[variants]/(mobile)/me/profile/features/Category.tsx +3 -13
  88. package/src/app/[variants]/(mobile)/settings/_layout/Header.tsx +2 -3
  89. package/src/app/[variants]/share/t/[id]/_layout/index.tsx +1 -1
  90. package/src/app/[variants]/share/t/[id]/index.tsx +1 -1
  91. package/src/app/robots.tsx +1 -1
  92. package/src/envs/auth.ts +2 -27
  93. package/src/envs/llm.ts +2 -2
  94. package/src/features/AgentSetting/AgentPlugin/index.tsx +9 -12
  95. package/src/features/ChatInput/ActionBar/Tools/index.tsx +7 -5
  96. package/src/features/ChatMiniMap/utils.ts +1 -1
  97. package/src/features/CommandMenu/SearchResults.tsx +1 -1
  98. package/src/features/Conversation/ChatList/components/AutoScroll/DebugInspector.tsx +166 -0
  99. package/src/features/Conversation/ChatList/components/AutoScroll/index.tsx +86 -0
  100. package/src/features/Conversation/ChatList/components/VirtualizedList.tsx +11 -17
  101. package/src/features/Conversation/Messages/AgentCouncil/components/AutoScrollShadow.tsx +25 -14
  102. package/src/features/Conversation/Messages/AgentCouncil/components/CouncilMember.tsx +1 -1
  103. package/src/features/FileViewer/Renderer/PDF/index.tsx +5 -8
  104. package/src/features/IntegrationDetailModal/IntegrationDetailContent.tsx +305 -0
  105. package/src/features/IntegrationDetailModal/index.tsx +21 -283
  106. package/src/features/MCPPluginDetail/Deployment/index.tsx +1 -1
  107. package/src/features/MCPPluginDetail/Schema/Prompts.tsx +1 -1
  108. package/src/features/MCPPluginDetail/Schema/Tools.tsx +1 -1
  109. package/src/features/ProfileEditor/AgentTool.tsx +14 -20
  110. package/src/features/ResourceManager/components/Explorer/ListView/ListItem/index.tsx +0 -8
  111. package/src/features/ResourceManager/components/Explorer/MasonryView/MasonryFileItem/NoteFileItem.tsx +1 -1
  112. package/src/features/ResourceManager/index.tsx +1 -1
  113. package/src/features/ShareModal/SharePdf/PdfPreview.tsx +4 -4
  114. package/src/features/SkillStore/LobeHubList/index.tsx +50 -87
  115. package/src/features/SkillStore/Search/index.tsx +1 -1
  116. package/src/features/SkillStore/{Content.tsx → SkillStoreContent.tsx} +3 -8
  117. package/src/features/SkillStore/index.tsx +15 -33
  118. package/src/features/User/UserPanel/PanelContent.tsx +0 -8
  119. package/src/features/User/__tests__/PanelContent.test.tsx +1 -35
  120. package/src/features/User/__tests__/UserAvatar.test.tsx +30 -57
  121. package/src/features/User/__tests__/useMenu.test.tsx +2 -43
  122. package/src/layout/AuthProvider/index.tsx +0 -5
  123. package/src/libs/next/config/define-config.ts +20 -15
  124. package/src/libs/next/proxy/createRouteMatcher.test.ts +121 -0
  125. package/src/libs/next/proxy/createRouteMatcher.ts +18 -0
  126. package/src/libs/next/proxy/define-config.ts +4 -53
  127. package/src/libs/next-auth/adapter/index.ts +1 -2
  128. package/src/libs/oidc-provider/provider.test.ts +5 -316
  129. package/src/libs/pdfjs/pdf.worker.ts +1 -0
  130. package/src/libs/pdfjs/worker.ts +12 -0
  131. package/src/libs/trpc/lambda/context.test.ts +0 -13
  132. package/src/libs/trpc/lambda/context.ts +3 -22
  133. package/src/libs/trpc/middleware/userAuth.ts +2 -4
  134. package/src/libs/trusted-client/getSessionUser.ts +2 -17
  135. package/src/locales/default/error.ts +0 -6
  136. package/src/locales/default/index.ts +0 -2
  137. package/src/proxy.ts +0 -1
  138. package/src/server/routers/lambda/__tests__/user.test.ts +0 -71
  139. package/src/server/routers/lambda/user.ts +6 -63
  140. package/src/server/services/changelog/index.test.ts +3 -2
  141. package/src/server/services/changelog/index.ts +1 -1
  142. package/src/server/services/user/index.ts +0 -83
  143. package/src/services/chat/index.ts +1 -2
  144. package/src/services/chat/mecha/agentConfigResolver.test.ts +43 -0
  145. package/src/services/chat/mecha/agentConfigResolver.ts +3 -1
  146. package/src/store/chat/slices/aiChat/actions/__tests__/streamingExecutor.test.ts +58 -14
  147. package/src/store/chat/slices/aiChat/actions/streamingExecutor.ts +10 -2
  148. package/src/store/user/slices/auth/action.test.ts +1 -81
  149. package/src/store/user/slices/auth/action.ts +3 -28
  150. package/src/store/user/slices/auth/initialState.ts +1 -18
  151. package/src/store/user/slices/auth/selectors.test.ts +2 -127
  152. package/src/store/user/slices/auth/selectors.ts +1 -21
  153. package/src/utils/errorResponse.ts +1 -4
  154. package/src/utils/markdownToTxt.ts +20 -0
  155. package/locales/ar/clerk.json +0 -545
  156. package/locales/bg-BG/clerk.json +0 -545
  157. package/locales/de-DE/clerk.json +0 -545
  158. package/locales/en-US/clerk.json +0 -545
  159. package/locales/es-ES/clerk.json +0 -545
  160. package/locales/fa-IR/clerk.json +0 -545
  161. package/locales/fr-FR/clerk.json +0 -545
  162. package/locales/it-IT/clerk.json +0 -545
  163. package/locales/ja-JP/clerk.json +0 -545
  164. package/locales/ko-KR/clerk.json +0 -545
  165. package/locales/nl-NL/clerk.json +0 -545
  166. package/locales/pl-PL/clerk.json +0 -545
  167. package/locales/pt-BR/clerk.json +0 -545
  168. package/locales/ru-RU/clerk.json +0 -545
  169. package/locales/tr-TR/clerk.json +0 -545
  170. package/locales/vi-VN/clerk.json +0 -545
  171. package/locales/zh-CN/clerk.json +0 -545
  172. package/locales/zh-TW/clerk.json +0 -545
  173. package/src/app/(backend)/api/webhooks/clerk/__tests__/fixtures/createUser.json +0 -73
  174. package/src/app/(backend)/api/webhooks/clerk/route.ts +0 -95
  175. package/src/app/(backend)/api/webhooks/clerk/validateRequest.ts +0 -22
  176. package/src/app/[variants]/(auth)/login/[[...login]]/page.tsx +0 -27
  177. package/src/app/[variants]/(main)/settings/security/features/ClerkProfile.tsx +0 -67
  178. package/src/features/Conversation/ChatList/components/AutoScroll.tsx +0 -25
  179. package/src/layout/AuthProvider/Clerk/UserUpdater.tsx +0 -40
  180. package/src/layout/AuthProvider/Clerk/index.tsx +0 -54
  181. package/src/layout/AuthProvider/Clerk/useAppearance.ts +0 -133
  182. package/src/libs/clerk-auth/index.test.ts +0 -216
  183. package/src/libs/clerk-auth/index.ts +0 -80
  184. package/src/locales/default/clerk.ts +0 -677
  185. package/src/server/services/user/index.test.ts +0 -220
@@ -0,0 +1,121 @@
1
+ import { type NextRequest } from 'next/server';
2
+ import { describe, expect, it } from 'vitest';
3
+
4
+ import { createRouteMatcher } from './createRouteMatcher';
5
+
6
+ // Helper to create a mock NextRequest with a given pathname
7
+ const createMockRequest = (pathname: string): NextRequest =>
8
+ ({
9
+ nextUrl: { pathname },
10
+ }) as NextRequest;
11
+
12
+ describe('createRouteMatcher', () => {
13
+ describe('exact path matching', () => {
14
+ it('should match exact paths', () => {
15
+ const matcher = createRouteMatcher(['/signin', '/signup']);
16
+
17
+ expect(matcher(createMockRequest('/signin'))).toBe(true);
18
+ expect(matcher(createMockRequest('/signup'))).toBe(true);
19
+ expect(matcher(createMockRequest('/login'))).toBe(false);
20
+ });
21
+
22
+ it('should not match partial paths without wildcard', () => {
23
+ const matcher = createRouteMatcher(['/api']);
24
+
25
+ expect(matcher(createMockRequest('/api'))).toBe(true);
26
+ expect(matcher(createMockRequest('/api/users'))).toBe(false);
27
+ expect(matcher(createMockRequest('/api/'))).toBe(false);
28
+ });
29
+ });
30
+
31
+ describe('wildcard pattern matching', () => {
32
+ it('should match paths with (.*) wildcard', () => {
33
+ const matcher = createRouteMatcher(['/api/auth(.*)']);
34
+
35
+ expect(matcher(createMockRequest('/api/auth'))).toBe(true);
36
+ expect(matcher(createMockRequest('/api/auth/'))).toBe(true);
37
+ expect(matcher(createMockRequest('/api/auth/callback'))).toBe(true);
38
+ expect(matcher(createMockRequest('/api/auth/callback/google'))).toBe(true);
39
+ expect(matcher(createMockRequest('/api/other'))).toBe(false);
40
+ });
41
+
42
+ it('should match /share(.*) pattern for public share pages', () => {
43
+ const matcher = createRouteMatcher(['/share(.*)']);
44
+
45
+ expect(matcher(createMockRequest('/share'))).toBe(true);
46
+ expect(matcher(createMockRequest('/share/'))).toBe(true);
47
+ expect(matcher(createMockRequest('/share/abc123'))).toBe(true);
48
+ expect(matcher(createMockRequest('/share/t/abc123'))).toBe(true);
49
+ // Note: /shared also matches because (.*) matches 'd' - use /share/(.*) for strict matching
50
+ expect(matcher(createMockRequest('/shared'))).toBe(true);
51
+ });
52
+
53
+ it('should match /trpc(.*) pattern', () => {
54
+ const matcher = createRouteMatcher(['/trpc(.*)']);
55
+
56
+ expect(matcher(createMockRequest('/trpc'))).toBe(true);
57
+ expect(matcher(createMockRequest('/trpc/user.get'))).toBe(true);
58
+ expect(matcher(createMockRequest('/trpc/chat.create'))).toBe(true);
59
+ });
60
+
61
+ it('should match /next-auth/(.*) pattern', () => {
62
+ const matcher = createRouteMatcher(['/next-auth/(.*)']);
63
+
64
+ expect(matcher(createMockRequest('/next-auth/'))).toBe(true);
65
+ expect(matcher(createMockRequest('/next-auth/signin'))).toBe(true);
66
+ expect(matcher(createMockRequest('/next-auth/callback/github'))).toBe(true);
67
+ expect(matcher(createMockRequest('/next-auth'))).toBe(false); // no trailing slash or path
68
+ });
69
+ });
70
+
71
+ describe('multiple patterns', () => {
72
+ it('should match any of multiple patterns', () => {
73
+ const matcher = createRouteMatcher(['/api/auth(.*)', '/signin', '/share(.*)']);
74
+
75
+ expect(matcher(createMockRequest('/api/auth/callback'))).toBe(true);
76
+ expect(matcher(createMockRequest('/signin'))).toBe(true);
77
+ expect(matcher(createMockRequest('/share/abc'))).toBe(true);
78
+ expect(matcher(createMockRequest('/other'))).toBe(false);
79
+ });
80
+ });
81
+
82
+ describe('special regex characters', () => {
83
+ it('should escape special regex characters in patterns', () => {
84
+ const matcher = createRouteMatcher(['/api/v1.0/users']);
85
+
86
+ expect(matcher(createMockRequest('/api/v1.0/users'))).toBe(true);
87
+ expect(matcher(createMockRequest('/api/v1X0/users'))).toBe(false); // . should not match any char
88
+ });
89
+
90
+ it('should handle patterns with multiple special characters', () => {
91
+ const matcher = createRouteMatcher(['/oauth/consent/(.*)']);
92
+
93
+ expect(matcher(createMockRequest('/oauth/consent/'))).toBe(true);
94
+ expect(matcher(createMockRequest('/oauth/consent/abc123'))).toBe(true);
95
+ expect(matcher(createMockRequest('/oauth/consent'))).toBe(false);
96
+ });
97
+ });
98
+
99
+ describe('edge cases', () => {
100
+ it('should handle root path', () => {
101
+ const matcher = createRouteMatcher(['/']);
102
+
103
+ expect(matcher(createMockRequest('/'))).toBe(true);
104
+ expect(matcher(createMockRequest('/anything'))).toBe(false);
105
+ });
106
+
107
+ it('should handle empty patterns array', () => {
108
+ const matcher = createRouteMatcher([]);
109
+
110
+ expect(matcher(createMockRequest('/'))).toBe(false);
111
+ expect(matcher(createMockRequest('/any/path'))).toBe(false);
112
+ });
113
+
114
+ it('should be case sensitive', () => {
115
+ const matcher = createRouteMatcher(['/API/auth(.*)']);
116
+
117
+ expect(matcher(createMockRequest('/API/auth/callback'))).toBe(true);
118
+ expect(matcher(createMockRequest('/api/auth/callback'))).toBe(false);
119
+ });
120
+ });
121
+ });
@@ -0,0 +1,18 @@
1
+ import { type NextRequest } from 'next/server';
2
+
3
+ /**
4
+ * Creates a route matcher function that checks if a request path matches any of the given patterns
5
+ * @param patterns Array of route patterns - supports `(.*)` as wildcard
6
+ * @returns Function that returns true if the request matches any pattern
7
+ */
8
+ export function createRouteMatcher(patterns: string[]) {
9
+ const regexPatterns = patterns.map((pattern) => {
10
+ // Escape all special regex chars (including parentheses), then restore (.*) to wildcard
11
+ const regexStr = pattern
12
+ .replaceAll(/[$()*+.?[\\\]^{|}]/g, '\\$&')
13
+ .replaceAll('\\(\\.\\*\\)', '.*');
14
+ return new RegExp(`^${regexStr}$`);
15
+ });
16
+
17
+ return (req: NextRequest) => regexPatterns.some((regex) => regex.test(req.nextUrl.pathname));
18
+ }
@@ -1,4 +1,3 @@
1
- import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server';
2
1
  import debug from 'debug';
3
2
  import { type NextRequest, NextResponse } from 'next/server';
4
3
  import { UAParser } from 'ua-parser-js';
@@ -14,10 +13,11 @@ import { type Locales } from '@/locales/resources';
14
13
  import { parseBrowserLanguage } from '@/utils/locale';
15
14
  import { RouteVariants } from '@/utils/server/routeVariants';
16
15
 
16
+ import { createRouteMatcher } from './createRouteMatcher';
17
+
17
18
  // Create debug logger instances
18
19
  const logDefault = debug('middleware:default');
19
20
  const logNextAuth = debug('middleware:next-auth');
20
- const logClerk = debug('middleware:clerk');
21
21
  const logBetterAuth = debug('middleware:better-auth');
22
22
 
23
23
  // OIDC session pre-sync constant
@@ -171,11 +171,9 @@ export function defineConfig() {
171
171
  '/trpc(.*)',
172
172
  // next auth
173
173
  '/next-auth/(.*)',
174
- // clerk
175
- '/login',
176
- '/signup',
177
174
  // better auth
178
175
  '/signin',
176
+ '/signup',
179
177
  '/verify-email',
180
178
  '/reset-password',
181
179
  // oauth
@@ -253,48 +251,6 @@ export function defineConfig() {
253
251
  return response;
254
252
  });
255
253
 
256
- const clerkAuthMiddleware = clerkMiddleware(
257
- async (auth, req) => {
258
- logClerk('Clerk middleware processing request: %s %s', req.method, req.url);
259
-
260
- // when enable auth protection, only public route is not protected, others are all protected
261
- const isProtected = appEnv.ENABLE_AUTH_PROTECTION
262
- ? !isPublicRoute(req)
263
- : isProtectedRoute(req);
264
-
265
- logClerk('Route protection status: %s, %s', req.url, isProtected ? 'protected' : 'public');
266
-
267
- if (isProtected) {
268
- logClerk('Protecting route: %s', req.url);
269
- await auth.protect();
270
- }
271
-
272
- const response = defaultMiddleware(req);
273
-
274
- const data = await auth();
275
- logClerk('Clerk auth status: %O', {
276
- isSignedIn: !!data.userId,
277
- userId: data.userId,
278
- });
279
-
280
- // If OIDC is enabled and Clerk user is logged in, add OIDC session pre-sync header
281
- if (authEnv.ENABLE_OIDC && data.userId) {
282
- logClerk('OIDC session pre-sync: Setting %s = %s', OIDC_SESSION_HEADER, data.userId);
283
- response.headers.set(OIDC_SESSION_HEADER, data.userId);
284
- } else if (authEnv.ENABLE_OIDC) {
285
- logClerk('No Clerk user detected, not setting OIDC session sync header');
286
- }
287
-
288
- return response;
289
- },
290
- {
291
- // https://github.com/lobehub/lobe-chat/pull/3084
292
- clockSkewInMs: 60 * 60 * 1000,
293
- signInUrl: '/login',
294
- signUpUrl: '/signup',
295
- },
296
- );
297
-
298
254
  const betterAuthMiddleware = async (req: NextRequest) => {
299
255
  logBetterAuth('BetterAuth middleware processing request: %s %s', req.method, req.url);
300
256
 
@@ -343,16 +299,11 @@ export function defineConfig() {
343
299
  logDefault('Middleware configuration: %O', {
344
300
  enableAuthProtection: appEnv.ENABLE_AUTH_PROTECTION,
345
301
  enableBetterAuth: authEnv.NEXT_PUBLIC_ENABLE_BETTER_AUTH,
346
- enableClerk: authEnv.NEXT_PUBLIC_ENABLE_CLERK_AUTH,
347
302
  enableNextAuth: authEnv.NEXT_PUBLIC_ENABLE_NEXT_AUTH,
348
303
  enableOIDC: authEnv.ENABLE_OIDC,
349
304
  });
350
305
 
351
306
  return {
352
- middleware: authEnv.NEXT_PUBLIC_ENABLE_CLERK_AUTH
353
- ? clerkAuthMiddleware
354
- : authEnv.NEXT_PUBLIC_ENABLE_NEXT_AUTH
355
- ? nextAuthMiddleware
356
- : betterAuthMiddleware,
307
+ middleware: authEnv.NEXT_PUBLIC_ENABLE_NEXT_AUTH ? nextAuthMiddleware : betterAuthMiddleware,
357
308
  };
358
309
  }
@@ -23,8 +23,7 @@ interface BackendAdapterResponse {
23
23
  export const dateKeys = ['expires', 'emailVerified'];
24
24
 
25
25
  /**
26
- * @description LobeNextAuthDbAdapter is implemented to handle the database operations
27
- * for NextAuth, this function do the same things as `src/app/api/webhooks/clerk/route.ts`
26
+ * @description LobeNextAuthDbAdapter is implemented to handle the database operations for NextAuth
28
27
  * @returns {Adapter}
29
28
  */
30
29
  export function LobeNextAuthDbAdapter(): Adapter {
@@ -6,7 +6,6 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
6
6
  // Mock dependencies
7
7
  vi.mock('@/envs/auth', () => ({
8
8
  enableBetterAuth: false,
9
- enableClerk: false,
10
9
  enableNextAuth: false,
11
10
  }));
12
11
 
@@ -38,280 +37,8 @@ describe('OIDC Provider - Market Client Integration', () => {
38
37
  vi.resetModules();
39
38
  });
40
39
 
41
- describe('resolveClerkAccount', () => {
42
- it('should return undefined when Clerk is disabled', async () => {
43
- // Import with Clerk disabled
44
- vi.doMock('@/const/auth', () => ({
45
- enableClerk: false,
46
- }));
47
-
48
- // Note: resolveClerkAccount is not exported, but we can test its behavior
49
- // through the findAccount method with market client
50
- // For now, we'll test the constants and basic setup
51
- const module = await import('./provider');
52
- expect(module.API_AUDIENCE).toBe('urn:lobehub:chat');
53
-
54
- vi.doUnmock('@/const/auth');
55
- });
56
-
57
- it('should handle market client ID constant', () => {
58
- // The MARKET_CLIENT_ID should match the client in config
59
- expect(MARKET_CLIENT_ID).toBe('lobehub-market');
60
- });
61
- });
62
-
63
- describe('resolveClerkAccount - with Clerk enabled', () => {
64
- it('should resolve Clerk user with full profile', async () => {
65
- const mockClerkUser = {
66
- id: 'user_123',
67
- fullName: 'John Doe',
68
- firstName: 'John',
69
- lastName: 'Doe',
70
- username: 'johndoe',
71
- imageUrl: 'https://example.com/avatar.jpg',
72
- primaryEmailAddressId: 'email_1',
73
- emailAddresses: [
74
- {
75
- id: 'email_1',
76
- emailAddress: 'john@example.com',
77
- verification: { status: 'verified' },
78
- },
79
- ],
80
- };
81
-
82
- const mockClerkClient = {
83
- users: {
84
- getUser: vi.fn().mockResolvedValue(mockClerkUser),
85
- },
86
- };
87
-
88
- vi.doMock('@/const/auth', () => ({
89
- enableClerk: true,
90
- }));
91
-
92
- vi.doMock('@clerk/nextjs/server', () => ({
93
- clerkClient: vi.fn().mockResolvedValue(mockClerkClient),
94
- }));
95
-
96
- // Import the provider module to access resolveClerkAccount behavior
97
- const module = await import('./provider');
98
-
99
- // Verify the module loads correctly
100
- expect(module.API_AUDIENCE).toBe('urn:lobehub:chat');
101
-
102
- vi.doUnmock('@/const/auth');
103
- vi.doUnmock('@clerk/nextjs/server');
104
- });
105
-
106
- it('should handle Clerk user with only username', async () => {
107
- const mockClerkUser = {
108
- id: 'user_123',
109
- fullName: null,
110
- firstName: null,
111
- lastName: null,
112
- username: 'johndoe',
113
- imageUrl: null,
114
- primaryEmailAddressId: 'email_1',
115
- emailAddresses: [
116
- {
117
- id: 'email_1',
118
- emailAddress: 'john@example.com',
119
- verification: { status: 'verified' },
120
- },
121
- ],
122
- };
123
-
124
- const mockClerkClient = {
125
- users: {
126
- getUser: vi.fn().mockResolvedValue(mockClerkUser),
127
- },
128
- };
129
-
130
- vi.doMock('@/const/auth', () => ({
131
- enableClerk: true,
132
- }));
133
-
134
- vi.doMock('@clerk/nextjs/server', () => ({
135
- clerkClient: vi.fn().mockResolvedValue(mockClerkClient),
136
- }));
137
-
138
- const module = await import('./provider');
139
- expect(module.API_AUDIENCE).toBe('urn:lobehub:chat');
140
-
141
- vi.doUnmock('@/const/auth');
142
- vi.doUnmock('@clerk/nextjs/server');
143
- });
144
-
145
- it('should handle Clerk user with firstName and lastName', async () => {
146
- const mockClerkUser = {
147
- id: 'user_123',
148
- fullName: null,
149
- firstName: 'John',
150
- lastName: 'Doe',
151
- username: null,
152
- imageUrl: null,
153
- primaryEmailAddressId: 'email_1',
154
- emailAddresses: [
155
- {
156
- id: 'email_1',
157
- emailAddress: 'john@example.com',
158
- verification: { status: 'verified' },
159
- },
160
- ],
161
- };
162
-
163
- const mockClerkClient = {
164
- users: {
165
- getUser: vi.fn().mockResolvedValue(mockClerkUser),
166
- },
167
- };
168
-
169
- vi.doMock('@/const/auth', () => ({
170
- enableClerk: true,
171
- }));
172
-
173
- vi.doMock('@clerk/nextjs/server', () => ({
174
- clerkClient: vi.fn().mockResolvedValue(mockClerkClient),
175
- }));
176
-
177
- const module = await import('./provider');
178
- expect(module.API_AUDIENCE).toBe('urn:lobehub:chat');
179
-
180
- vi.doUnmock('@/const/auth');
181
- vi.doUnmock('@clerk/nextjs/server');
182
- });
183
-
184
- it('should handle Clerk user not found', async () => {
185
- const mockClerkClient = {
186
- users: {
187
- getUser: vi.fn().mockResolvedValue(null),
188
- },
189
- };
190
-
191
- vi.doMock('@/const/auth', () => ({
192
- enableClerk: true,
193
- }));
194
-
195
- vi.doMock('@clerk/nextjs/server', () => ({
196
- clerkClient: vi.fn().mockResolvedValue(mockClerkClient),
197
- }));
198
-
199
- const module = await import('./provider');
200
- expect(module.API_AUDIENCE).toBe('urn:lobehub:chat');
201
-
202
- vi.doUnmock('@/const/auth');
203
- vi.doUnmock('@clerk/nextjs/server');
204
- });
205
-
206
- it('should handle Clerk API error', async () => {
207
- const mockClerkClient = {
208
- users: {
209
- getUser: vi.fn().mockRejectedValue(new Error('Clerk API error')),
210
- },
211
- };
212
-
213
- vi.doMock('@/const/auth', () => ({
214
- enableClerk: true,
215
- }));
216
-
217
- vi.doMock('@clerk/nextjs/server', () => ({
218
- clerkClient: vi.fn().mockResolvedValue(mockClerkClient),
219
- }));
220
-
221
- const module = await import('./provider');
222
- expect(module.API_AUDIENCE).toBe('urn:lobehub:chat');
223
-
224
- vi.doUnmock('@/const/auth');
225
- vi.doUnmock('@clerk/nextjs/server');
226
- });
227
-
228
- it('should handle email without verification', async () => {
229
- const mockClerkUser = {
230
- id: 'user_123',
231
- fullName: 'John Doe',
232
- firstName: 'John',
233
- lastName: 'Doe',
234
- username: 'johndoe',
235
- imageUrl: 'https://example.com/avatar.jpg',
236
- primaryEmailAddressId: 'email_1',
237
- emailAddresses: [
238
- {
239
- id: 'email_1',
240
- emailAddress: 'john@example.com',
241
- verification: null,
242
- },
243
- ],
244
- };
245
-
246
- const mockClerkClient = {
247
- users: {
248
- getUser: vi.fn().mockResolvedValue(mockClerkUser),
249
- },
250
- };
251
-
252
- vi.doMock('@/const/auth', () => ({
253
- enableClerk: true,
254
- }));
255
-
256
- vi.doMock('@clerk/nextjs/server', () => ({
257
- clerkClient: vi.fn().mockResolvedValue(mockClerkClient),
258
- }));
259
-
260
- const module = await import('./provider');
261
- expect(module.API_AUDIENCE).toBe('urn:lobehub:chat');
262
-
263
- vi.doUnmock('@/const/auth');
264
- vi.doUnmock('@clerk/nextjs/server');
265
- });
266
-
267
- it('should use first email when no primary email is set', async () => {
268
- const mockClerkUser = {
269
- id: 'user_123',
270
- fullName: 'John Doe',
271
- firstName: 'John',
272
- lastName: 'Doe',
273
- username: 'johndoe',
274
- imageUrl: 'https://example.com/avatar.jpg',
275
- primaryEmailAddressId: null,
276
- emailAddresses: [
277
- {
278
- id: 'email_2',
279
- emailAddress: 'john.first@example.com',
280
- verification: { status: 'verified' },
281
- },
282
- {
283
- id: 'email_3',
284
- emailAddress: 'john.second@example.com',
285
- verification: { status: 'verified' },
286
- },
287
- ],
288
- };
289
-
290
- const mockClerkClient = {
291
- users: {
292
- getUser: vi.fn().mockResolvedValue(mockClerkUser),
293
- },
294
- };
295
-
296
- vi.doMock('@/const/auth', () => ({
297
- enableClerk: true,
298
- }));
299
-
300
- vi.doMock('@clerk/nextjs/server', () => ({
301
- clerkClient: vi.fn().mockResolvedValue(mockClerkClient),
302
- }));
303
-
304
- const module = await import('./provider');
305
- expect(module.API_AUDIENCE).toBe('urn:lobehub:chat');
306
-
307
- vi.doUnmock('@/const/auth');
308
- vi.doUnmock('@clerk/nextjs/server');
309
- });
310
- });
311
-
312
40
  describe('Market Client Logic', () => {
313
41
  it('should identify market client correctly', () => {
314
- // The market client should route to Clerk resolution
315
42
  expect(MARKET_CLIENT_ID).toBe('lobehub-market');
316
43
  });
317
44
 
@@ -445,32 +172,7 @@ describe('OIDC Provider - Market Client Integration', () => {
445
172
  });
446
173
 
447
174
  describe('Business Logic Scenarios', () => {
448
- describe('Scenario 1: Market Client + Clerk Authentication', () => {
449
- it('should route market client to Clerk when enableClerk is true', () => {
450
- // Business: When user accesses from marketplace, use Clerk for SSO
451
- const scenario = {
452
- client: 'lobehub-market',
453
- authProvider: 'Clerk',
454
- useCase: 'Marketplace SSO - users login via Clerk on marketplace',
455
- };
456
-
457
- expect(scenario.client).toBe(MARKET_CLIENT_ID);
458
- expect(scenario.authProvider).toBe('Clerk');
459
- });
460
-
461
- it('should return undefined for market client when Clerk is disabled', () => {
462
- // Business: Market requires Clerk, if disabled, auth fails
463
- const scenario = {
464
- client: 'lobehub-market',
465
- clerkEnabled: false,
466
- expectedResult: 'undefined (auth fails)',
467
- };
468
-
469
- expect(scenario.expectedResult).toBe('undefined (auth fails)');
470
- });
471
- });
472
-
473
- describe('Scenario 2: Desktop Client + Local Database', () => {
175
+ describe('Scenario 1: Desktop Client + Local Database', () => {
474
176
  it('should use local UserModel for desktop client', () => {
475
177
  // Business: Desktop app uses local database for user management
476
178
  const scenario = {
@@ -484,7 +186,7 @@ describe('OIDC Provider - Market Client Integration', () => {
484
186
  });
485
187
  });
486
188
 
487
- describe('Scenario 3: Mobile Client + Local Database', () => {
189
+ describe('Scenario 2: Mobile Client + Local Database', () => {
488
190
  it('should use local UserModel for mobile client', () => {
489
191
  // Business: Mobile app uses local database for user management
490
192
  const scenario = {
@@ -498,22 +200,9 @@ describe('OIDC Provider - Market Client Integration', () => {
498
200
  });
499
201
  });
500
202
 
501
- describe('Scenario 4: Claims Generation by Client Type', () => {
502
- it('should generate Clerk-based claims for market client', () => {
503
- // Business: Market users get profile/email from Clerk
504
- const marketClaims = {
505
- source: 'Clerk API',
506
- fields: ['sub', 'name', 'picture', 'email', 'email_verified'],
507
- nameResolution: 'fullName || firstName+lastName || username || id',
508
- };
509
-
510
- expect(marketClaims.source).toBe('Clerk API');
511
- expect(marketClaims.fields).toContain('name');
512
- expect(marketClaims.fields).toContain('email');
513
- });
514
-
515
- it('should generate database-based claims for non-market clients', () => {
516
- // Business: Desktop/Mobile users get profile/email from local DB
203
+ describe('Scenario 3: Claims Generation', () => {
204
+ it('should generate database-based claims for clients', () => {
205
+ // Business: Users get profile/email from local DB
517
206
  const localClaims = {
518
207
  source: 'UserModel (PostgreSQL/PGLite)',
519
208
  fields: ['sub', 'name', 'picture', 'email', 'email_verified'],
@@ -0,0 +1 @@
1
+ import 'pdfjs-dist/build/pdf.worker.min.mjs';
@@ -0,0 +1,12 @@
1
+ 'use client';
2
+
3
+ import { pdfjs } from 'react-pdf';
4
+
5
+ pdfjs.GlobalWorkerOptions.workerSrc = `https://registry.npmmirror.com/pdfjs-dist/${pdfjs.version}/files/build/pdf.worker.min.mjs`;
6
+
7
+ // TODO: Re-enable module worker when fully on Turbopack.
8
+ // if (typeof Worker !== 'undefined' && !pdfjs.GlobalWorkerOptions.workerPort) {
9
+ // pdfjs.GlobalWorkerOptions.workerPort = new Worker(new URL('./pdf.worker.ts', import.meta.url), {
10
+ // type: 'module',
11
+ // });
12
+ // }
@@ -8,7 +8,6 @@ describe('createContextInner', () => {
8
8
 
9
9
  expect(context).toMatchObject({
10
10
  authorizationHeader: undefined,
11
- clerkAuth: undefined,
12
11
  marketAccessToken: undefined,
13
12
  nextAuth: undefined,
14
13
  oidcAuth: undefined,
@@ -59,18 +58,6 @@ describe('createContextInner', () => {
59
58
  expect(context.oidcAuth).toEqual(oidcAuth);
60
59
  });
61
60
 
62
- it('should create context with Clerk auth data', async () => {
63
- const clerkAuth = {
64
- userId: 'clerk-user-id',
65
- sessionId: 'session-id',
66
- getToken: async () => 'clerk-token',
67
- } as any;
68
-
69
- const context = await createContextInner({ clerkAuth });
70
-
71
- expect(context.clerkAuth).toBe(clerkAuth);
72
- });
73
-
74
61
  it('should create context with NextAuth user data', async () => {
75
62
  const nextAuth = {
76
63
  id: 'next-auth-user-id',