@lobehub/lobehub 2.0.0-next.355 → 2.0.0-next.357

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 (156) hide show
  1. package/.env.desktop +0 -1
  2. package/.env.example +16 -20
  3. package/.env.example.development +1 -4
  4. package/.github/workflows/e2e.yml +10 -11
  5. package/CHANGELOG.md +60 -0
  6. package/Dockerfile +28 -4
  7. package/changelog/v1.json +18 -0
  8. package/docker-compose/local/docker-compose.yml +2 -2
  9. package/docker-compose/local/grafana/docker-compose.yml +2 -2
  10. package/docker-compose/local/logto/docker-compose.yml +2 -2
  11. package/docker-compose/local/zitadel/.env.example +2 -2
  12. package/docker-compose/local/zitadel/.env.zh-CN.example +2 -2
  13. package/docker-compose/production/grafana/docker-compose.yml +2 -2
  14. package/docker-compose/production/logto/.env.example +2 -2
  15. package/docker-compose/production/logto/.env.zh-CN.example +2 -2
  16. package/docker-compose/production/zitadel/.env.example +2 -2
  17. package/docker-compose/production/zitadel/.env.zh-CN.example +2 -2
  18. package/docs/development/basic/add-new-authentication-providers.mdx +144 -136
  19. package/docs/development/basic/add-new-authentication-providers.zh-CN.mdx +146 -136
  20. package/docs/self-hosting/advanced/auth/legacy.mdx +4 -0
  21. package/docs/self-hosting/advanced/auth/legacy.zh-CN.mdx +4 -0
  22. package/docs/self-hosting/advanced/auth/nextauth-to-betterauth.mdx +326 -0
  23. package/docs/self-hosting/advanced/auth/nextauth-to-betterauth.zh-CN.mdx +323 -0
  24. package/docs/self-hosting/advanced/auth.mdx +43 -16
  25. package/docs/self-hosting/advanced/auth.zh-CN.mdx +44 -16
  26. package/docs/self-hosting/advanced/redis/upstash.mdx +69 -0
  27. package/docs/self-hosting/advanced/redis/upstash.zh-CN.mdx +69 -0
  28. package/docs/self-hosting/advanced/redis.mdx +128 -0
  29. package/docs/self-hosting/advanced/redis.zh-CN.mdx +126 -0
  30. package/docs/self-hosting/environment-variables/auth.mdx +15 -1
  31. package/docs/self-hosting/environment-variables/auth.zh-CN.mdx +15 -1
  32. package/docs/self-hosting/environment-variables/basic.mdx +13 -0
  33. package/docs/self-hosting/environment-variables/basic.zh-CN.mdx +13 -0
  34. package/docs/self-hosting/environment-variables/redis.mdx +68 -0
  35. package/docs/self-hosting/environment-variables/redis.zh-CN.mdx +67 -0
  36. package/docs/self-hosting/migration/v2/breaking-changes.mdx +23 -23
  37. package/docs/self-hosting/migration/v2/breaking-changes.zh-CN.mdx +23 -23
  38. package/docs/self-hosting/server-database/docker-compose.mdx +4 -4
  39. package/docs/self-hosting/server-database/docker-compose.zh-CN.mdx +4 -4
  40. package/e2e/CLAUDE.md +5 -6
  41. package/e2e/docs/local-setup.md +9 -12
  42. package/e2e/scripts/setup.ts +9 -15
  43. package/e2e/src/support/webServer.ts +6 -5
  44. package/package.json +4 -6
  45. package/packages/database/src/schemas/nextauth.ts +7 -2
  46. package/packages/model-runtime/src/core/contextBuilders/anthropic.test.ts +370 -0
  47. package/packages/model-runtime/src/core/contextBuilders/anthropic.ts +18 -5
  48. package/packages/utils/src/server/__tests__/auth.test.ts +1 -63
  49. package/packages/utils/src/server/auth.ts +8 -24
  50. package/scripts/_shared/checkDeprecatedAuth.js +99 -0
  51. package/scripts/clerk-to-betterauth/index.ts +8 -3
  52. package/scripts/nextauth-to-betterauth/_internal/config.ts +41 -0
  53. package/scripts/nextauth-to-betterauth/_internal/db.ts +32 -0
  54. package/scripts/nextauth-to-betterauth/_internal/env.ts +6 -0
  55. package/scripts/nextauth-to-betterauth/index.ts +226 -0
  56. package/scripts/nextauth-to-betterauth/verify.ts +188 -0
  57. package/scripts/prebuild.mts +66 -13
  58. package/scripts/serverLauncher/startServer.js +5 -5
  59. package/src/app/(backend)/api/auth/[...all]/route.ts +5 -23
  60. package/src/app/(backend)/api/webhooks/casdoor/route.ts +5 -5
  61. package/src/app/(backend)/api/webhooks/logto/route.ts +8 -8
  62. package/src/app/(backend)/middleware/auth/index.test.ts +8 -1
  63. package/src/app/(backend)/middleware/auth/index.ts +6 -15
  64. package/src/app/(backend)/middleware/auth/utils.test.ts +0 -32
  65. package/src/app/(backend)/middleware/auth/utils.ts +3 -8
  66. package/src/app/(backend)/webapi/chat/[provider]/route.test.ts +8 -1
  67. package/src/app/(backend)/webapi/create-image/comfyui/route.ts +0 -1
  68. package/src/app/(backend)/webapi/models/[provider]/route.test.ts +8 -1
  69. package/src/app/[variants]/(auth)/signin/SignInEmailStep.tsx +1 -1
  70. package/src/app/[variants]/(auth)/signup/[[...signup]]/page.tsx +4 -17
  71. package/src/app/[variants]/(main)/agent/cron/[cronId]/features/CronJobContentEditor.tsx +34 -21
  72. package/src/app/[variants]/(main)/settings/profile/features/SSOProvidersList/index.tsx +12 -19
  73. package/src/app/[variants]/(main)/settings/profile/index.tsx +8 -14
  74. package/src/components/{NextAuth/AuthIcons.tsx → AuthIcons.tsx} +8 -10
  75. package/src/envs/auth.ts +12 -51
  76. package/src/envs/email.ts +3 -0
  77. package/src/envs/redis.ts +12 -54
  78. package/src/features/ChatInput/ChatInputProvider.tsx +22 -2
  79. package/src/features/ChatInput/InputEditor/index.tsx +14 -3
  80. package/src/features/ChatInput/store/initialState.ts +2 -0
  81. package/src/features/EditorCanvas/DiffAllToolbar.tsx +4 -5
  82. package/src/features/EditorCanvas/DocumentIdMode.tsx +21 -1
  83. package/src/features/User/__tests__/PanelContent.test.tsx +0 -11
  84. package/src/features/User/__tests__/UserAvatar.test.tsx +1 -16
  85. package/src/layout/AuthProvider/index.tsx +1 -6
  86. package/src/layout/GlobalProvider/StoreInitialization.tsx +2 -4
  87. package/src/libs/better-auth/define-config.ts +2 -0
  88. package/src/libs/better-auth/plugins/email-whitelist.test.ts +120 -0
  89. package/src/libs/better-auth/plugins/email-whitelist.ts +62 -0
  90. package/src/libs/next/config/define-config.ts +13 -1
  91. package/src/libs/next/proxy/define-config.ts +2 -75
  92. package/src/libs/oidc-provider/provider.test.ts +0 -4
  93. package/src/libs/redis/index.ts +0 -1
  94. package/src/libs/redis/manager.test.ts +9 -45
  95. package/src/libs/redis/manager.ts +2 -16
  96. package/src/libs/redis/redis.test.ts +2 -4
  97. package/src/libs/redis/redis.ts +2 -4
  98. package/src/libs/redis/types.ts +2 -24
  99. package/src/libs/redis/utils.test.ts +0 -10
  100. package/src/libs/redis/utils.ts +0 -19
  101. package/src/libs/trpc/lambda/context.test.ts +0 -13
  102. package/src/libs/trpc/lambda/context.ts +21 -59
  103. package/src/libs/trpc/middleware/userAuth.ts +1 -7
  104. package/src/libs/trusted-client/getSessionUser.ts +15 -35
  105. package/src/server/globalConfig/index.ts +1 -3
  106. package/src/server/routers/lambda/__tests__/user.test.ts +0 -48
  107. package/src/server/routers/lambda/user.ts +1 -12
  108. package/src/server/services/email/impls/nodemailer/index.ts +2 -2
  109. package/src/server/services/webhookUser/index.ts +88 -0
  110. package/src/services/user/index.test.ts +0 -14
  111. package/src/services/user/index.ts +0 -4
  112. package/src/store/document/slices/document/action.ts +1 -0
  113. package/src/store/user/slices/auth/action.test.ts +22 -126
  114. package/src/store/user/slices/auth/action.ts +32 -65
  115. package/src/store/user/slices/auth/initialState.ts +0 -3
  116. package/src/store/user/slices/auth/selectors.ts +0 -3
  117. package/tests/setup.ts +10 -0
  118. package/scripts/_shared/checkDeprecatedClerkEnv.js +0 -42
  119. package/src/app/(backend)/api/auth/adapter/route.ts +0 -137
  120. package/src/app/[variants]/(auth)/next-auth/error/AuthErrorPage.tsx +0 -40
  121. package/src/app/[variants]/(auth)/next-auth/error/page.tsx +0 -11
  122. package/src/app/[variants]/(auth)/next-auth/signin/AuthSignInBox.tsx +0 -167
  123. package/src/app/[variants]/(auth)/next-auth/signin/page.tsx +0 -11
  124. package/src/app/[variants]/(auth)/reset-password/layout.tsx +0 -12
  125. package/src/app/[variants]/(auth)/signin/layout.tsx +0 -12
  126. package/src/app/[variants]/(auth)/verify-email/layout.tsx +0 -12
  127. package/src/envs/auth.test.ts +0 -47
  128. package/src/layout/AuthProvider/NextAuth/UserUpdater.tsx +0 -44
  129. package/src/layout/AuthProvider/NextAuth/index.tsx +0 -17
  130. package/src/libs/next-auth/adapter/index.ts +0 -177
  131. package/src/libs/next-auth/auth.config.ts +0 -64
  132. package/src/libs/next-auth/index.ts +0 -20
  133. package/src/libs/next-auth/sso-providers/auth0.ts +0 -24
  134. package/src/libs/next-auth/sso-providers/authelia.ts +0 -39
  135. package/src/libs/next-auth/sso-providers/authentik.ts +0 -25
  136. package/src/libs/next-auth/sso-providers/casdoor.ts +0 -50
  137. package/src/libs/next-auth/sso-providers/cloudflare-zero-trust.ts +0 -34
  138. package/src/libs/next-auth/sso-providers/cognito.ts +0 -8
  139. package/src/libs/next-auth/sso-providers/feishu.ts +0 -83
  140. package/src/libs/next-auth/sso-providers/generic-oidc.ts +0 -38
  141. package/src/libs/next-auth/sso-providers/github.ts +0 -23
  142. package/src/libs/next-auth/sso-providers/google.ts +0 -18
  143. package/src/libs/next-auth/sso-providers/index.ts +0 -35
  144. package/src/libs/next-auth/sso-providers/keycloak.ts +0 -22
  145. package/src/libs/next-auth/sso-providers/logto.ts +0 -48
  146. package/src/libs/next-auth/sso-providers/microsoft-entra-id-helper.ts +0 -29
  147. package/src/libs/next-auth/sso-providers/microsoft-entra-id.ts +0 -19
  148. package/src/libs/next-auth/sso-providers/okta.ts +0 -22
  149. package/src/libs/next-auth/sso-providers/sso.config.ts +0 -8
  150. package/src/libs/next-auth/sso-providers/wechat.ts +0 -36
  151. package/src/libs/next-auth/sso-providers/zitadel.ts +0 -21
  152. package/src/libs/redis/upstash.test.ts +0 -158
  153. package/src/libs/redis/upstash.ts +0 -136
  154. package/src/server/services/nextAuthUser/index.ts +0 -318
  155. package/src/server/services/nextAuthUser/utils.ts +0 -62
  156. package/src/types/next-auth.d.ts +0 -26
@@ -0,0 +1,62 @@
1
+ import { APIError } from 'better-auth/api';
2
+ import { type BetterAuthPlugin } from 'better-auth/types';
3
+
4
+ import { authEnv } from '@/envs/auth';
5
+
6
+ /**
7
+ * Parse comma-separated email whitelist string into array.
8
+ */
9
+ function parseAllowedEmails(value: string | undefined): string[] {
10
+ if (!value) return [];
11
+ return value
12
+ .split(',')
13
+ .map((s) => s.trim())
14
+ .filter(Boolean);
15
+ }
16
+
17
+ /**
18
+ * Check if email is allowed based on whitelist.
19
+ * Supports full email (user@example.com) or domain (example.com).
20
+ */
21
+ export function isEmailAllowed(email: string): boolean {
22
+ const allowedList = parseAllowedEmails(authEnv.AUTH_ALLOWED_EMAILS);
23
+ if (allowedList.length === 0) return true;
24
+
25
+ const domain = email.split('@')[1];
26
+
27
+ return allowedList.some((item) => {
28
+ // Full email match
29
+ if (item.includes('@')) return item === email;
30
+ // Domain match
31
+ return item === domain;
32
+ });
33
+ }
34
+
35
+ /**
36
+ * Better Auth plugin to restrict registration to whitelisted emails/domains.
37
+ * Intercepts user creation (both email signup and SSO) via databaseHooks.
38
+ */
39
+ export const emailWhitelist = (): BetterAuthPlugin => ({
40
+ id: 'email-whitelist',
41
+ init() {
42
+ return {
43
+ options: {
44
+ databaseHooks: {
45
+ user: {
46
+ create: {
47
+ before: async (user) => {
48
+ if (!user.email) return { data: user };
49
+
50
+ if (!isEmailAllowed(user.email)) {
51
+ throw new APIError('FORBIDDEN', { message: 'Email not allowed for registration' });
52
+ }
53
+
54
+ return { data: user };
55
+ },
56
+ },
57
+ },
58
+ },
59
+ },
60
+ };
61
+ },
62
+ });
@@ -31,10 +31,22 @@ export function defineConfig(config: CustomNextConfig) {
31
31
  outputFileTracingIncludes: { '*': ['public/**/*', '.next/static/**/*'] },
32
32
  };
33
33
 
34
+ // Vercel serverless optimization: exclude musl binaries
35
+ // Vercel uses Amazon Linux (glibc), not Alpine Linux (musl)
36
+ // This saves ~45MB (29MB canvas-musl + 16MB sharp-musl)
37
+ const vercelConfig: NextConfig = {
38
+ outputFileTracingExcludes: {
39
+ '*': [
40
+ 'node_modules/.pnpm/@napi-rs+canvas-*-musl*',
41
+ 'node_modules/.pnpm/@img+sharp-libvips-*musl*',
42
+ ],
43
+ },
44
+ };
45
+
34
46
  const assetPrefix = process.env.NEXT_PUBLIC_ASSET_PREFIX;
35
47
 
36
48
  const nextConfig: NextConfig = {
37
- ...(isStandaloneMode ? standaloneConfig : {}),
49
+ ...(isStandaloneMode ? standaloneConfig : vercelConfig),
38
50
  assetPrefix,
39
51
 
40
52
  compiler: {
@@ -7,8 +7,7 @@ import { auth } from '@/auth';
7
7
  import { LOBE_LOCALE_COOKIE } from '@/const/locale';
8
8
  import { isDesktop } from '@/const/version';
9
9
  import { appEnv } from '@/envs/app';
10
- import { OAUTH_AUTHORIZED, authEnv } from '@/envs/auth';
11
- import NextAuth from '@/libs/next-auth';
10
+ import { authEnv } from '@/envs/auth';
12
11
  import { type Locales } from '@/locales/resources';
13
12
  import { parseBrowserLanguage } from '@/utils/locale';
14
13
  import { RouteVariants } from '@/utils/server/routeVariants';
@@ -17,12 +16,8 @@ import { createRouteMatcher } from './createRouteMatcher';
17
16
 
18
17
  // Create debug logger instances
19
18
  const logDefault = debug('middleware:default');
20
- const logNextAuth = debug('middleware:next-auth');
21
19
  const logBetterAuth = debug('middleware:better-auth');
22
20
 
23
- // OIDC session pre-sync constant
24
- const OIDC_SESSION_HEADER = 'x-oidc-session-sync';
25
-
26
21
  export function defineConfig() {
27
22
  const backendApiEndpoints = ['/api', '/trpc', '/webapi', '/oidc'];
28
23
 
@@ -169,8 +164,6 @@ export function defineConfig() {
169
164
  '/api/agent(.*)',
170
165
  '/webapi(.*)',
171
166
  '/trpc(.*)',
172
- // next auth
173
- '/next-auth/(.*)',
174
167
  // better auth
175
168
  '/signin',
176
169
  '/signup',
@@ -187,70 +180,6 @@ export function defineConfig() {
187
180
  '/share(.*)',
188
181
  ]);
189
182
 
190
- const isProtectedRoute = createRouteMatcher([
191
- '/settings(.*)',
192
- '/knowledge(.*)',
193
- '/onboard(.*)',
194
- '/oauth(.*)',
195
- // ↓ cloud ↓
196
- ]);
197
-
198
- // Initialize an Edge compatible NextAuth middleware
199
- const nextAuthMiddleware = NextAuth.auth((req) => {
200
- logNextAuth('NextAuth middleware processing request: %s %s', req.method, req.url);
201
-
202
- const response = defaultMiddleware(req);
203
-
204
- // when enable auth protection, only public route is not protected, others are all protected
205
- const isProtected = appEnv.ENABLE_AUTH_PROTECTION ? !isPublicRoute(req) : isProtectedRoute(req);
206
-
207
- logNextAuth('Route protection status: %s, %s', req.url, isProtected ? 'protected' : 'public');
208
-
209
- // Just check if session exists
210
- const session = req.auth;
211
-
212
- // Check if next-auth throws errors
213
- // refs: https://github.com/lobehub/lobe-chat/pull/1323
214
- const isLoggedIn = !!session?.expires;
215
-
216
- logNextAuth('NextAuth session status: %O', {
217
- expires: session?.expires,
218
- isLoggedIn,
219
- userId: session?.user?.id,
220
- });
221
-
222
- // Remove & amend OAuth authorized header
223
- response.headers.delete(OAUTH_AUTHORIZED);
224
- if (isLoggedIn) {
225
- logNextAuth('Setting auth header: %s = %s', OAUTH_AUTHORIZED, 'true');
226
- response.headers.set(OAUTH_AUTHORIZED, 'true');
227
-
228
- // If OIDC is enabled and user is logged in, add OIDC session pre-sync header
229
- if (authEnv.ENABLE_OIDC && session?.user?.id) {
230
- logNextAuth('OIDC session pre-sync: Setting %s = %s', OIDC_SESSION_HEADER, session.user.id);
231
- response.headers.set(OIDC_SESSION_HEADER, session.user.id);
232
- }
233
- } else {
234
- // If request a protected route, redirect to sign-in page
235
- // ref: https://authjs.dev/getting-started/session-management/protecting
236
- if (isProtected) {
237
- logNextAuth('Request a protected route, redirecting to sign-in page');
238
- const callbackUrl = `${appEnv.APP_URL}${req.nextUrl.pathname}${req.nextUrl.search}`;
239
- const nextLoginUrl = new URL('/next-auth/signin', appEnv.APP_URL);
240
- nextLoginUrl.searchParams.set('callbackUrl', callbackUrl);
241
- const hl = req.nextUrl.searchParams.get('hl');
242
- if (hl) {
243
- nextLoginUrl.searchParams.set('hl', hl);
244
- logNextAuth('Preserving locale to sign-in: hl=%s', hl);
245
- }
246
- return Response.redirect(nextLoginUrl);
247
- }
248
- logNextAuth('Request a free route but not login, allow visit without auth header');
249
- }
250
-
251
- return response;
252
- });
253
-
254
183
  const betterAuthMiddleware = async (req: NextRequest) => {
255
184
  logBetterAuth('BetterAuth middleware processing request: %s %s', req.method, req.url);
256
185
 
@@ -298,12 +227,10 @@ export function defineConfig() {
298
227
 
299
228
  logDefault('Middleware configuration: %O', {
300
229
  enableAuthProtection: appEnv.ENABLE_AUTH_PROTECTION,
301
- enableBetterAuth: authEnv.NEXT_PUBLIC_ENABLE_BETTER_AUTH,
302
- enableNextAuth: authEnv.NEXT_PUBLIC_ENABLE_NEXT_AUTH,
303
230
  enableOIDC: authEnv.ENABLE_OIDC,
304
231
  });
305
232
 
306
233
  return {
307
- middleware: authEnv.NEXT_PUBLIC_ENABLE_NEXT_AUTH ? nextAuthMiddleware : betterAuthMiddleware,
234
+ middleware: betterAuthMiddleware,
308
235
  };
309
236
  }
@@ -4,10 +4,6 @@
4
4
  import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
5
5
 
6
6
  // Mock dependencies
7
- vi.mock('@/envs/auth', () => ({
8
- enableBetterAuth: false,
9
- enableNextAuth: false,
10
- }));
11
7
 
12
8
  vi.mock('@/envs/app', () => ({
13
9
  appEnv: {
@@ -2,5 +2,4 @@ export * from './keys';
2
2
  export * from './manager';
3
3
  export * from './redis';
4
4
  export * from './types';
5
- export * from './upstash';
6
5
  export * from './utils';
@@ -1,23 +1,15 @@
1
1
  import { afterEach, describe, expect, it, vi } from 'vitest';
2
2
 
3
3
  import { RedisManager, initializeRedis, resetRedisClient } from './manager';
4
- import { DisabledRedisConfig } from './types';
4
+ import { RedisConfig } from './types';
5
5
 
6
- const {
7
- mockIoRedisInitialize,
8
- mockIoRedisDisconnect,
9
- mockUpstashInitialize,
10
- mockUpstashDisconnect,
11
- } = vi.hoisted(() => ({
6
+ const { mockIoRedisInitialize, mockIoRedisDisconnect } = vi.hoisted(() => ({
12
7
  mockIoRedisInitialize: vi.fn().mockResolvedValue(undefined),
13
8
  mockIoRedisDisconnect: vi.fn().mockResolvedValue(undefined),
14
- mockUpstashInitialize: vi.fn().mockResolvedValue(undefined),
15
- mockUpstashDisconnect: vi.fn().mockResolvedValue(undefined),
16
9
  }));
17
10
 
18
11
  vi.mock('./redis', () => {
19
12
  const IoRedisRedisProvider = vi.fn().mockImplementation((config) => ({
20
- provider: 'redis' as const,
21
13
  config,
22
14
  initialize: mockIoRedisInitialize,
23
15
  disconnect: mockIoRedisDisconnect,
@@ -26,20 +18,9 @@ vi.mock('./redis', () => {
26
18
  return { IoRedisRedisProvider };
27
19
  });
28
20
 
29
- vi.mock('./upstash', () => {
30
- const UpstashRedisProvider = vi.fn().mockImplementation((config) => ({
31
- provider: 'upstash' as const,
32
- config,
33
- initialize: mockUpstashInitialize,
34
- disconnect: mockUpstashDisconnect,
35
- }));
36
-
37
- return { UpstashRedisProvider };
38
- });
39
-
40
21
  afterEach(async () => {
41
- vi.clearAllMocks();
42
22
  await RedisManager.reset();
23
+ vi.clearAllMocks();
43
24
  });
44
25
 
45
26
  describe('RedisManager', () => {
@@ -47,14 +28,14 @@ describe('RedisManager', () => {
47
28
  const config = {
48
29
  enabled: false,
49
30
  prefix: 'test',
50
- provider: false,
51
- } satisfies DisabledRedisConfig;
31
+ tls: false,
32
+ url: '',
33
+ } satisfies RedisConfig;
52
34
 
53
35
  const instance = await initializeRedis(config);
54
36
 
55
37
  expect(instance).toBeNull();
56
38
  expect(mockIoRedisInitialize).not.toHaveBeenCalled();
57
- expect(mockUpstashInitialize).not.toHaveBeenCalled();
58
39
  });
59
40
 
60
41
  it('initializes ioredis provider once and memoizes the instance', async () => {
@@ -63,41 +44,24 @@ describe('RedisManager', () => {
63
44
  enabled: true,
64
45
  password: 'pwd',
65
46
  prefix: 'test',
66
- provider: 'redis' as const,
67
47
  tls: false,
68
48
  url: 'redis://localhost:6379',
69
49
  username: 'user',
70
- };
50
+ } satisfies RedisConfig;
51
+
71
52
  const [first, second] = await Promise.all([initializeRedis(config), initializeRedis(config)]);
72
53
 
73
54
  expect(first).toBe(second);
74
55
  expect(mockIoRedisInitialize).toHaveBeenCalledTimes(1);
75
- expect(mockUpstashInitialize).not.toHaveBeenCalled();
76
- });
77
-
78
- it('initializes upstash provider when configured', async () => {
79
- const config = {
80
- enabled: true,
81
- prefix: 'test',
82
- provider: 'upstash' as const,
83
- token: 'token',
84
- url: 'https://example.upstash.io',
85
- };
86
- const instance = await initializeRedis(config);
87
-
88
- expect(instance?.provider).toBe('upstash');
89
- expect(mockUpstashInitialize).toHaveBeenCalledTimes(1);
90
- expect(mockIoRedisInitialize).not.toHaveBeenCalled();
91
56
  });
92
57
 
93
58
  it('disconnects existing provider on reset', async () => {
94
59
  const config = {
95
60
  enabled: true,
96
61
  prefix: 'test',
97
- provider: 'redis' as const,
98
62
  tls: false,
99
63
  url: 'redis://localhost:6379',
100
- };
64
+ } satisfies RedisConfig;
101
65
 
102
66
  await initializeRedis(config);
103
67
  await resetRedisClient();
@@ -1,32 +1,18 @@
1
1
  import { IoRedisRedisProvider } from './redis';
2
2
  import { type BaseRedisProvider, type RedisConfig } from './types';
3
- import { UpstashRedisProvider } from './upstash';
4
3
 
5
4
  /**
6
5
  * Create a Redis provider instance based on config
7
6
  *
8
7
  * @param config - Redis config
9
8
  * @param prefix - Optional custom prefix to override config.prefix
10
- * @returns Provider instance or null if disabled/unsupported
9
+ * @returns Provider instance or null if disabled
11
10
  */
12
11
  const createProvider = (config: RedisConfig, prefix?: string): BaseRedisProvider | null => {
13
12
  if (!config.enabled) return null;
14
13
 
15
14
  const actualPrefix = prefix ?? config.prefix;
16
-
17
- if (config.provider === 'redis') {
18
- return new IoRedisRedisProvider({ ...config, prefix: actualPrefix });
19
- }
20
-
21
- if (config.provider === 'upstash') {
22
- return new UpstashRedisProvider({
23
- prefix: actualPrefix,
24
- token: config.token,
25
- url: config.url,
26
- });
27
- }
28
-
29
- return null;
15
+ return new IoRedisRedisProvider({ ...config, prefix: actualPrefix });
30
16
  };
31
17
 
32
18
  class RedisManager {
@@ -1,8 +1,8 @@
1
1
  import { afterEach, describe, expect, it, vi } from 'vitest';
2
2
 
3
- import { IoRedisConfig } from './types';
3
+ import { RedisConfig } from './types';
4
4
 
5
- const buildRedisConfig = (): IoRedisConfig | null => {
5
+ const buildRedisConfig = (): RedisConfig | null => {
6
6
  const url = process.env.REDIS_URL;
7
7
 
8
8
  if (!url) return null;
@@ -14,7 +14,6 @@ const buildRedisConfig = (): IoRedisConfig | null => {
14
14
  enabled: true,
15
15
  password: process.env.REDIS_PASSWORD,
16
16
  prefix: process.env.REDIS_PREFIX ?? 'lobe-chat-test',
17
- provider: 'redis',
18
17
  tls: process.env.REDIS_TLS === 'true',
19
18
  url,
20
19
  username: process.env.REDIS_USERNAME,
@@ -79,7 +78,6 @@ const createMockedProvider = async () => {
79
78
  const provider = new IoRedisRedisProvider({
80
79
  enabled: true,
81
80
  prefix: 'mock',
82
- provider: 'redis',
83
81
  tls: false,
84
82
  url: 'redis://localhost:6379',
85
83
  });
@@ -3,10 +3,9 @@ import type { Redis } from 'ioredis';
3
3
 
4
4
  import {
5
5
  type BaseRedisProvider,
6
- type IoRedisConfig,
6
+ type RedisConfig,
7
7
  type RedisKey,
8
8
  type RedisMSetArgument,
9
- type RedisProviderName,
10
9
  type RedisSetResult,
11
10
  type RedisValue,
12
11
  type SetOptions,
@@ -16,10 +15,9 @@ import { buildIORedisSetArgs, normalizeMsetValues } from './utils';
16
15
  const log = debug('lobe:redis');
17
16
 
18
17
  export class IoRedisRedisProvider implements BaseRedisProvider {
19
- provider: RedisProviderName = 'redis';
20
18
  private client: Redis | null = null;
21
19
 
22
- constructor(private config: IoRedisConfig) {}
20
+ constructor(private config: RedisConfig) {}
23
21
 
24
22
  async initialize() {
25
23
  const IORedis = await import('ioredis');
@@ -1,35 +1,16 @@
1
1
  export type RedisKey = string | Buffer;
2
2
  export type RedisValue = string | Buffer | number;
3
- export type RedisProvider = false | 'redis' | 'upstash';
4
- export type RedisProviderName = Exclude<RedisProvider, false>;
5
3
 
6
- export type IoRedisConfig = {
4
+ export type RedisConfig = {
7
5
  database?: number;
8
6
  enabled: boolean;
9
7
  password?: string;
10
8
  prefix: string;
11
- provider: 'redis';
12
9
  tls: boolean;
13
10
  url: string;
14
11
  username?: string;
15
12
  };
16
13
 
17
- export type UpstashConfig = {
18
- enabled: boolean;
19
- prefix: string;
20
- provider: 'upstash';
21
- token: string;
22
- url: string;
23
- };
24
-
25
- export type DisabledRedisConfig = {
26
- enabled: false;
27
- prefix: string;
28
- provider: false;
29
- };
30
-
31
- export type RedisConfig = IoRedisConfig | UpstashConfig | DisabledRedisConfig;
32
-
33
14
  export interface SetOptions {
34
15
  ex?: number;
35
16
  exat?: number;
@@ -41,8 +22,7 @@ export interface SetOptions {
41
22
  xx?: boolean;
42
23
  }
43
24
 
44
- // NOTICE: number comes from upstash
45
- export type RedisSetResult = 'OK' | null | string | number;
25
+ export type RedisSetResult = 'OK' | null | string;
46
26
  export type RedisMSetArgument = Record<string, RedisValue> | Map<RedisKey, RedisValue>;
47
27
 
48
28
  export interface RedisClient {
@@ -65,7 +45,5 @@ export interface RedisClient {
65
45
 
66
46
  export interface BaseRedisProvider extends RedisClient {
67
47
  disconnect(): Promise<void>;
68
-
69
48
  initialize(): Promise<void>;
70
- provider: RedisProviderName;
71
49
  }
@@ -2,7 +2,6 @@ import { describe, expect, it } from 'vitest';
2
2
 
3
3
  import {
4
4
  buildIORedisSetArgs,
5
- buildUpstashSetOptions,
6
5
  normalizeMsetValues,
7
6
  normalizeRedisKey,
8
7
  normalizeRedisKeys,
@@ -34,13 +33,4 @@ describe('redis utils', () => {
34
33
 
35
34
  expect(args).toEqual(['EX', 1, 'NX', 'GET']);
36
35
  });
37
-
38
- it('builds upstash set options', () => {
39
- expect(buildUpstashSetOptions()).toBeUndefined();
40
- expect(buildUpstashSetOptions({ ex: 10, nx: true, get: true })).toEqual({
41
- ex: 10,
42
- nx: true,
43
- get: true,
44
- });
45
- });
46
36
  });
@@ -1,5 +1,3 @@
1
- import type { SetCommandOptions } from '@upstash/redis';
2
-
3
1
  import { type RedisKey, type RedisMSetArgument, type RedisValue, type SetOptions } from './types';
4
2
 
5
3
  export const normalizeRedisKey = (key: RedisKey) =>
@@ -34,20 +32,3 @@ export const buildIORedisSetArgs = (options?: SetOptions): Array<string | number
34
32
 
35
33
  return args;
36
34
  };
37
-
38
- export const buildUpstashSetOptions = (options?: SetOptions): SetCommandOptions | undefined => {
39
- if (!options) return undefined;
40
-
41
- const mapped: Partial<SetCommandOptions> = {};
42
-
43
- if (options.ex !== undefined) mapped.ex = options.ex;
44
- if (options.px !== undefined) mapped.px = options.px;
45
- if (options.exat !== undefined) mapped.exat = options.exat;
46
- if (options.pxat !== undefined) mapped.pxat = options.pxat;
47
- if (options.keepTtl) mapped.keepTtl = true;
48
- if (options.nx) mapped.nx = true;
49
- if (options.xx) mapped.xx = true;
50
- if (options.get) mapped.get = true;
51
-
52
- return Object.keys(mapped).length ? (mapped as SetCommandOptions) : undefined;
53
- };
@@ -9,7 +9,6 @@ describe('createContextInner', () => {
9
9
  expect(context).toMatchObject({
10
10
  authorizationHeader: undefined,
11
11
  marketAccessToken: undefined,
12
- nextAuth: undefined,
13
12
  oidcAuth: undefined,
14
13
  userAgent: undefined,
15
14
  userId: undefined,
@@ -58,18 +57,6 @@ describe('createContextInner', () => {
58
57
  expect(context.oidcAuth).toEqual(oidcAuth);
59
58
  });
60
59
 
61
- it('should create context with NextAuth user data', async () => {
62
- const nextAuth = {
63
- id: 'next-auth-user-id',
64
- name: 'Test User',
65
- email: 'test@example.com',
66
- };
67
-
68
- const context = await createContextInner({ nextAuth });
69
-
70
- expect(context.nextAuth).toEqual(nextAuth);
71
- });
72
-
73
60
  it('should create context with all parameters combined', async () => {
74
61
  const params = {
75
62
  authorizationHeader: 'Bearer token',
@@ -1,16 +1,10 @@
1
1
  import { type ClientSecretPayload } from '@lobechat/types';
2
2
  import { parse } from 'cookie';
3
3
  import debug from 'debug';
4
- import { type User } from 'next-auth';
5
4
  import { type NextRequest } from 'next/server';
6
5
 
7
- import {
8
- LOBE_CHAT_AUTH_HEADER,
9
- LOBE_CHAT_OIDC_AUTH_HEADER,
10
- authEnv,
11
- enableBetterAuth,
12
- enableNextAuth,
13
- } from '@/envs/auth';
6
+ import { auth } from '@/auth';
7
+ import { LOBE_CHAT_AUTH_HEADER, LOBE_CHAT_OIDC_AUTH_HEADER, authEnv } from '@/envs/auth';
14
8
  import { validateOIDCJWT } from '@/libs/oidc-provider/jwt';
15
9
 
16
10
  // Create context logger namespace
@@ -43,7 +37,6 @@ export interface AuthContext {
43
37
  clientIp?: string | null;
44
38
  jwtPayload?: ClientSecretPayload | null;
45
39
  marketAccessToken?: string;
46
- nextAuth?: User;
47
40
  // Add OIDC authentication information
48
41
  oidcAuth?: OIDCAuth | null;
49
42
  resHeaders?: Headers;
@@ -59,7 +52,6 @@ export const createContextInner = async (params?: {
59
52
  authorizationHeader?: string | null;
60
53
  clientIp?: string | null;
61
54
  marketAccessToken?: string;
62
- nextAuth?: User;
63
55
  oidcAuth?: OIDCAuth | null;
64
56
  userAgent?: string;
65
57
  userId?: string | null;
@@ -71,7 +63,6 @@ export const createContextInner = async (params?: {
71
63
  authorizationHeader: params?.authorizationHeader,
72
64
  clientIp: params?.clientIp,
73
65
  marketAccessToken: params?.marketAccessToken,
74
- nextAuth: params?.nextAuth,
75
66
  oidcAuth: params?.oidcAuth,
76
67
  resHeaders: responseHeaders,
77
68
  userAgent: params?.userAgent,
@@ -120,7 +111,6 @@ export const createLambdaContext = async (request: NextRequest): Promise<LambdaC
120
111
  log('LobeChat Authorization header: %s', authorization ? 'exists' : 'not found');
121
112
 
122
113
  let userId;
123
- let auth;
124
114
  let oidcAuth = null;
125
115
 
126
116
  // Prioritize checking for OIDC authentication (both standard Authorization and custom Oidc-Auth headers)
@@ -159,55 +149,27 @@ export const createLambdaContext = async (request: NextRequest): Promise<LambdaC
159
149
  }
160
150
  }
161
151
 
162
- // If OIDC is not enabled or validation fails, try other authentication methods
163
- if (enableBetterAuth) {
164
- log('Attempting Better Auth authentication');
165
- try {
166
- const { auth: betterAuth } = await import('@/auth');
167
-
168
- const session = await betterAuth.api.getSession({
169
- headers: request.headers,
170
- });
171
-
172
- if (session && session?.user?.id) {
173
- userId = session.user.id;
174
- log('Better Auth authentication successful, userId: %s', userId);
175
- } else {
176
- log('Better Auth authentication failed, no valid session');
177
- }
178
-
179
- return createContextInner({
180
- ...commonContext,
181
- userId,
182
- });
183
- } catch (e) {
184
- log('Better Auth authentication error: %O', e);
185
- console.error('better auth err', e);
152
+ // If OIDC is not enabled or validation fails, try Better Auth authentication
153
+ log('Attempting Better Auth authentication');
154
+ try {
155
+ const session = await auth.api.getSession({
156
+ headers: request.headers,
157
+ });
158
+
159
+ if (session && session?.user?.id) {
160
+ userId = session.user.id;
161
+ log('Better Auth authentication successful, userId: %s', userId);
162
+ } else {
163
+ log('Better Auth authentication failed, no valid session');
186
164
  }
187
- }
188
165
 
189
- if (enableNextAuth) {
190
- log('Attempting NextAuth authentication');
191
- try {
192
- const { default: NextAuth } = await import('@/libs/next-auth');
193
-
194
- const session = await NextAuth.auth();
195
- if (session && session?.user?.id) {
196
- auth = session.user;
197
- userId = session.user.id;
198
- log('NextAuth authentication successful, userId: %s', userId);
199
- } else {
200
- log('NextAuth authentication failed, no valid session');
201
- }
202
- return createContextInner({
203
- nextAuth: auth,
204
- ...commonContext,
205
- userId,
206
- });
207
- } catch (e) {
208
- log('NextAuth authentication error: %O', e);
209
- console.error('next auth err', e);
210
- }
166
+ return createContextInner({
167
+ ...commonContext,
168
+ userId,
169
+ });
170
+ } catch (e) {
171
+ log('Better Auth authentication error: %O', e);
172
+ console.error('better auth err', e);
211
173
  }
212
174
 
213
175
  // Final return, userId may be undefined