@lobehub/lobehub 2.0.0-next.354 → 2.0.0-next.356
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.desktop +0 -1
- package/.env.example +16 -20
- package/.env.example.development +1 -4
- package/.github/workflows/e2e.yml +10 -11
- package/CHANGELOG.md +60 -0
- package/Dockerfile +28 -4
- package/changelog/v1.json +18 -0
- package/docker-compose/local/docker-compose.yml +2 -2
- package/docker-compose/local/grafana/docker-compose.yml +2 -2
- package/docker-compose/local/logto/docker-compose.yml +2 -2
- package/docker-compose/local/zitadel/.env.example +2 -2
- package/docker-compose/local/zitadel/.env.zh-CN.example +2 -2
- package/docker-compose/production/grafana/docker-compose.yml +2 -2
- package/docker-compose/production/logto/.env.example +2 -2
- package/docker-compose/production/logto/.env.zh-CN.example +2 -2
- package/docker-compose/production/zitadel/.env.example +2 -2
- package/docker-compose/production/zitadel/.env.zh-CN.example +2 -2
- package/docs/development/basic/add-new-authentication-providers.mdx +144 -136
- package/docs/development/basic/add-new-authentication-providers.zh-CN.mdx +146 -136
- package/docs/self-hosting/advanced/auth/legacy.mdx +4 -0
- package/docs/self-hosting/advanced/auth/legacy.zh-CN.mdx +4 -0
- package/docs/self-hosting/advanced/auth/nextauth-to-betterauth.mdx +326 -0
- package/docs/self-hosting/advanced/auth/nextauth-to-betterauth.zh-CN.mdx +323 -0
- package/docs/self-hosting/advanced/auth.mdx +43 -16
- package/docs/self-hosting/advanced/auth.zh-CN.mdx +44 -16
- package/docs/self-hosting/advanced/redis/upstash.mdx +69 -0
- package/docs/self-hosting/advanced/redis/upstash.zh-CN.mdx +69 -0
- package/docs/self-hosting/advanced/redis.mdx +128 -0
- package/docs/self-hosting/advanced/redis.zh-CN.mdx +126 -0
- package/docs/self-hosting/environment-variables/auth.mdx +15 -1
- package/docs/self-hosting/environment-variables/auth.zh-CN.mdx +15 -1
- package/docs/self-hosting/environment-variables/basic.mdx +13 -0
- package/docs/self-hosting/environment-variables/basic.zh-CN.mdx +13 -0
- package/docs/self-hosting/environment-variables/redis.mdx +68 -0
- package/docs/self-hosting/environment-variables/redis.zh-CN.mdx +67 -0
- package/docs/self-hosting/migration/v2/breaking-changes.mdx +23 -23
- package/docs/self-hosting/migration/v2/breaking-changes.zh-CN.mdx +23 -23
- package/docs/self-hosting/server-database/docker-compose.mdx +4 -4
- package/docs/self-hosting/server-database/docker-compose.zh-CN.mdx +4 -4
- package/e2e/CLAUDE.md +5 -6
- package/e2e/docs/local-setup.md +9 -12
- package/e2e/scripts/setup.ts +9 -15
- package/e2e/src/support/webServer.ts +6 -5
- package/locales/en-US/plugin.json +3 -0
- package/locales/zh-CN/plugin.json +3 -0
- package/package.json +4 -6
- package/packages/builtin-tool-memory/src/client/Render/SearchUserMemory/index.tsx +3 -11
- package/packages/context-engine/src/engine/messages/MessagesEngine.ts +0 -13
- package/packages/context-engine/src/engine/messages/__tests__/MessagesEngine.test.ts +0 -25
- package/packages/database/src/models/__tests__/topics/topic.create.test.ts +3 -3
- package/packages/database/src/schemas/nextauth.ts +7 -2
- package/packages/utils/src/server/__tests__/auth.test.ts +1 -63
- package/packages/utils/src/server/auth.ts +8 -24
- package/scripts/_shared/checkDeprecatedAuth.js +99 -0
- package/scripts/clerk-to-betterauth/index.ts +8 -3
- package/scripts/nextauth-to-betterauth/_internal/config.ts +41 -0
- package/scripts/nextauth-to-betterauth/_internal/db.ts +32 -0
- package/scripts/nextauth-to-betterauth/_internal/env.ts +6 -0
- package/scripts/nextauth-to-betterauth/index.ts +226 -0
- package/scripts/nextauth-to-betterauth/verify.ts +188 -0
- package/scripts/prebuild.mts +66 -13
- package/scripts/serverLauncher/startServer.js +5 -5
- package/src/app/(backend)/api/auth/[...all]/route.ts +5 -23
- package/src/app/(backend)/api/webhooks/casdoor/route.ts +5 -5
- package/src/app/(backend)/api/webhooks/logto/route.ts +8 -8
- package/src/app/(backend)/middleware/auth/index.test.ts +8 -1
- package/src/app/(backend)/middleware/auth/index.ts +6 -15
- package/src/app/(backend)/middleware/auth/utils.test.ts +0 -32
- package/src/app/(backend)/middleware/auth/utils.ts +3 -8
- package/src/app/(backend)/webapi/chat/[provider]/route.test.ts +8 -1
- package/src/app/(backend)/webapi/create-image/comfyui/route.ts +0 -1
- package/src/app/(backend)/webapi/models/[provider]/route.test.ts +8 -1
- package/src/app/[variants]/(auth)/signin/SignInEmailStep.tsx +1 -1
- package/src/app/[variants]/(auth)/signup/[[...signup]]/page.tsx +4 -17
- package/src/app/[variants]/(main)/agent/_layout/Sidebar/Topic/List/Item/index.tsx +1 -0
- package/src/app/[variants]/(main)/agent/cron/[cronId]/features/CronJobContentEditor.tsx +34 -21
- package/src/app/[variants]/(main)/agent/features/Conversation/ConversationArea.tsx +4 -0
- package/src/app/[variants]/(main)/group/_layout/Sidebar/Topic/List/Item/index.tsx +1 -0
- package/src/app/[variants]/(main)/home/_layout/Body/Agent/List/AgentItem/index.tsx +1 -1
- package/src/app/[variants]/(main)/home/_layout/Body/Agent/List/InboxItem.tsx +19 -29
- package/src/app/[variants]/(main)/home/_layout/Body/Agent/List/List.tsx +1 -1
- package/src/app/[variants]/(main)/home/_layout/Body/Agent/ModalProvider.tsx +1 -1
- package/src/app/[variants]/(main)/settings/profile/features/SSOProvidersList/index.tsx +12 -19
- package/src/app/[variants]/(main)/settings/profile/index.tsx +8 -14
- package/src/components/{NextAuth/AuthIcons.tsx → AuthIcons.tsx} +8 -10
- package/src/envs/auth.ts +12 -51
- package/src/envs/email.ts +3 -0
- package/src/envs/redis.ts +12 -54
- package/src/features/ChatInput/ChatInputProvider.tsx +22 -2
- package/src/features/ChatInput/InputEditor/index.tsx +14 -3
- package/src/features/ChatInput/store/initialState.ts +2 -0
- package/src/features/User/__tests__/PanelContent.test.tsx +0 -11
- package/src/features/User/__tests__/UserAvatar.test.tsx +1 -16
- package/src/layout/AuthProvider/index.tsx +1 -6
- package/src/layout/GlobalProvider/StoreInitialization.tsx +2 -4
- package/src/libs/better-auth/define-config.ts +2 -0
- package/src/libs/better-auth/plugins/email-whitelist.test.ts +120 -0
- package/src/libs/better-auth/plugins/email-whitelist.ts +62 -0
- package/src/libs/next/config/define-config.ts +13 -1
- package/src/libs/next/proxy/define-config.ts +2 -75
- package/src/libs/oidc-provider/provider.test.ts +0 -4
- package/src/libs/redis/index.ts +0 -1
- package/src/libs/redis/manager.test.ts +9 -45
- package/src/libs/redis/manager.ts +2 -16
- package/src/libs/redis/redis.test.ts +2 -4
- package/src/libs/redis/redis.ts +2 -4
- package/src/libs/redis/types.ts +2 -24
- package/src/libs/redis/utils.test.ts +0 -10
- package/src/libs/redis/utils.ts +0 -19
- package/src/libs/trpc/lambda/context.test.ts +0 -13
- package/src/libs/trpc/lambda/context.ts +21 -59
- package/src/libs/trpc/middleware/userAuth.ts +1 -7
- package/src/libs/trusted-client/getSessionUser.ts +15 -35
- package/src/locales/default/plugin.ts +3 -0
- package/src/server/globalConfig/index.ts +1 -3
- package/src/server/modules/Mecha/ContextEngineering/__tests__/serverMessagesEngine.test.ts +0 -25
- package/src/server/routers/lambda/__tests__/user.test.ts +0 -48
- package/src/server/routers/lambda/user.ts +1 -12
- package/src/server/services/email/impls/nodemailer/index.ts +2 -2
- package/src/server/services/webhookUser/index.ts +88 -0
- package/src/services/chat/chat.test.ts +19 -19
- package/src/services/chat/index.ts +8 -3
- package/src/services/chat/mecha/agentConfigResolver.test.ts +72 -55
- package/src/services/chat/mecha/agentConfigResolver.ts +28 -4
- package/src/services/chat/mecha/contextEngineering.test.ts +21 -14
- package/src/services/chat/mecha/contextEngineering.ts +12 -0
- package/src/services/chat/types.ts +7 -1
- package/src/services/user/index.test.ts +0 -14
- package/src/services/user/index.ts +0 -4
- package/src/store/chat/agents/createAgentExecutors.ts +15 -4
- package/src/store/chat/slices/aiChat/actions/conversationLifecycle.ts +1 -0
- package/src/store/chat/slices/aiChat/actions/streamingExecutor.ts +6 -2
- package/src/store/user/slices/auth/action.test.ts +22 -126
- package/src/store/user/slices/auth/action.ts +32 -65
- package/src/store/user/slices/auth/initialState.ts +0 -3
- package/src/store/user/slices/auth/selectors.ts +0 -3
- package/tests/setup.ts +10 -0
- package/scripts/_shared/checkDeprecatedClerkEnv.js +0 -42
- package/src/app/(backend)/api/auth/adapter/route.ts +0 -137
- package/src/app/[variants]/(auth)/next-auth/error/AuthErrorPage.tsx +0 -40
- package/src/app/[variants]/(auth)/next-auth/error/page.tsx +0 -11
- package/src/app/[variants]/(auth)/next-auth/signin/AuthSignInBox.tsx +0 -167
- package/src/app/[variants]/(auth)/next-auth/signin/page.tsx +0 -11
- package/src/app/[variants]/(auth)/reset-password/layout.tsx +0 -12
- package/src/app/[variants]/(auth)/signin/layout.tsx +0 -12
- package/src/app/[variants]/(auth)/verify-email/layout.tsx +0 -12
- package/src/envs/auth.test.ts +0 -47
- package/src/layout/AuthProvider/NextAuth/UserUpdater.tsx +0 -44
- package/src/layout/AuthProvider/NextAuth/index.tsx +0 -17
- package/src/libs/next-auth/adapter/index.ts +0 -177
- package/src/libs/next-auth/auth.config.ts +0 -64
- package/src/libs/next-auth/index.ts +0 -20
- package/src/libs/next-auth/sso-providers/auth0.ts +0 -24
- package/src/libs/next-auth/sso-providers/authelia.ts +0 -39
- package/src/libs/next-auth/sso-providers/authentik.ts +0 -25
- package/src/libs/next-auth/sso-providers/casdoor.ts +0 -50
- package/src/libs/next-auth/sso-providers/cloudflare-zero-trust.ts +0 -34
- package/src/libs/next-auth/sso-providers/cognito.ts +0 -8
- package/src/libs/next-auth/sso-providers/feishu.ts +0 -83
- package/src/libs/next-auth/sso-providers/generic-oidc.ts +0 -38
- package/src/libs/next-auth/sso-providers/github.ts +0 -23
- package/src/libs/next-auth/sso-providers/google.ts +0 -18
- package/src/libs/next-auth/sso-providers/index.ts +0 -35
- package/src/libs/next-auth/sso-providers/keycloak.ts +0 -22
- package/src/libs/next-auth/sso-providers/logto.ts +0 -48
- package/src/libs/next-auth/sso-providers/microsoft-entra-id-helper.ts +0 -29
- package/src/libs/next-auth/sso-providers/microsoft-entra-id.ts +0 -19
- package/src/libs/next-auth/sso-providers/okta.ts +0 -22
- package/src/libs/next-auth/sso-providers/sso.config.ts +0 -8
- package/src/libs/next-auth/sso-providers/wechat.ts +0 -36
- package/src/libs/next-auth/sso-providers/zitadel.ts +0 -21
- package/src/libs/redis/upstash.test.ts +0 -158
- package/src/libs/redis/upstash.ts +0 -136
- package/src/server/services/nextAuthUser/index.ts +0 -318
- package/src/server/services/nextAuthUser/utils.ts +0 -62
- package/src/types/next-auth.d.ts +0 -26
|
@@ -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
|
|
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 {
|
|
3
|
+
import { RedisConfig } from './types';
|
|
4
4
|
|
|
5
|
-
const buildRedisConfig = ():
|
|
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
|
});
|
package/src/libs/redis/redis.ts
CHANGED
|
@@ -3,10 +3,9 @@ import type { Redis } from 'ioredis';
|
|
|
3
3
|
|
|
4
4
|
import {
|
|
5
5
|
type BaseRedisProvider,
|
|
6
|
-
type
|
|
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:
|
|
20
|
+
constructor(private config: RedisConfig) {}
|
|
23
21
|
|
|
24
22
|
async initialize() {
|
|
25
23
|
const IORedis = await import('ioredis');
|
package/src/libs/redis/types.ts
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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
|
});
|
package/src/libs/redis/utils.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
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
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
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
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { TRPCError } from '@trpc/server';
|
|
2
2
|
|
|
3
|
-
import { enableBetterAuth, enableNextAuth } from '@/envs/auth';
|
|
4
|
-
|
|
5
3
|
import { trpc } from '../lambda/init';
|
|
6
4
|
|
|
7
5
|
export const userAuth = trpc.middleware(async (opts) => {
|
|
@@ -9,11 +7,7 @@ export const userAuth = trpc.middleware(async (opts) => {
|
|
|
9
7
|
|
|
10
8
|
// `ctx.user` is nullable
|
|
11
9
|
if (!ctx.userId) {
|
|
12
|
-
|
|
13
|
-
console.log('better auth: no session found in context');
|
|
14
|
-
} else if (enableNextAuth) {
|
|
15
|
-
console.log('next auth:', ctx.nextAuth);
|
|
16
|
-
}
|
|
10
|
+
console.log('better auth: no session found in context');
|
|
17
11
|
throw new TRPCError({ code: 'UNAUTHORIZED' });
|
|
18
12
|
}
|
|
19
13
|
|
|
@@ -1,50 +1,30 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { headers } from 'next/headers';
|
|
2
2
|
|
|
3
3
|
import type { TrustedClientUserInfo } from './index';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Get user info from the current session for trusted client authentication
|
|
7
|
-
* This works with different authentication providers (BetterAuth, NextAuth)
|
|
8
7
|
*
|
|
9
8
|
* @returns User info or undefined if not authenticated
|
|
10
9
|
*/
|
|
11
10
|
export const getSessionUser = async (): Promise<TrustedClientUserInfo | undefined> => {
|
|
12
11
|
try {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
return undefined;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
return {
|
|
26
|
-
email: session.user.email,
|
|
27
|
-
name: session.user.name || undefined,
|
|
28
|
-
userId: session.user.id,
|
|
29
|
-
};
|
|
12
|
+
// Dynamic import to avoid validator ESM/CJS issue during sitemap generation
|
|
13
|
+
const { auth } = await import('@/auth');
|
|
14
|
+
const headersList = await headers();
|
|
15
|
+
const session = await auth.api.getSession({
|
|
16
|
+
headers: headersList,
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
if (!session?.user?.id || !session?.user?.email) {
|
|
20
|
+
return undefined;
|
|
30
21
|
}
|
|
31
22
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
return undefined;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
return {
|
|
41
|
-
email: session.user.email,
|
|
42
|
-
name: session.user.name || undefined,
|
|
43
|
-
userId: session.user.id,
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return undefined;
|
|
23
|
+
return {
|
|
24
|
+
email: session.user.email,
|
|
25
|
+
name: session.user.name || undefined,
|
|
26
|
+
userId: session.user.id,
|
|
27
|
+
};
|
|
48
28
|
} catch {
|
|
49
29
|
return undefined;
|
|
50
30
|
}
|
|
@@ -178,6 +178,9 @@ export default {
|
|
|
178
178
|
'builtins.lobe-user-memory.apiName.searchUserMemory': 'Search memory',
|
|
179
179
|
'builtins.lobe-user-memory.apiName.updateIdentityMemory': 'Update identity memory',
|
|
180
180
|
'builtins.lobe-user-memory.inspector.noResults': 'No results',
|
|
181
|
+
'builtins.lobe-user-memory.render.contexts': 'Contexts',
|
|
182
|
+
'builtins.lobe-user-memory.render.experiences': 'Experiences',
|
|
183
|
+
'builtins.lobe-user-memory.render.preferences': 'Preferences',
|
|
181
184
|
'builtins.lobe-user-memory.title': 'Memory',
|
|
182
185
|
'builtins.lobe-web-browsing.apiName.crawlMultiPages': 'Read multiple pages',
|
|
183
186
|
'builtins.lobe-web-browsing.apiName.crawlSinglePage': 'Read page content',
|
|
@@ -90,9 +90,7 @@ export const getServerGlobalConfig = async () => {
|
|
|
90
90
|
memory: {
|
|
91
91
|
userMemory: cleanObject(getPublicMemoryExtractionConfig()),
|
|
92
92
|
},
|
|
93
|
-
oAuthSSOProviders:
|
|
94
|
-
? getBetterAuthSSOProviders()
|
|
95
|
-
: authEnv.NEXT_AUTH_SSO_PROVIDERS.trim().split(/[,,]/),
|
|
93
|
+
oAuthSSOProviders: getBetterAuthSSOProviders(),
|
|
96
94
|
systemAgent: parseSystemAgent(appEnv.SYSTEM_AGENT),
|
|
97
95
|
telemetry: {
|
|
98
96
|
langfuse: langfuseEnv.ENABLE_LANGFUSE,
|
|
@@ -68,31 +68,6 @@ describe('serverMessagesEngine', () => {
|
|
|
68
68
|
});
|
|
69
69
|
});
|
|
70
70
|
|
|
71
|
-
describe('history truncation', () => {
|
|
72
|
-
it('should truncate history when enabled', async () => {
|
|
73
|
-
const messages: UIChatMessage[] = [];
|
|
74
|
-
for (let i = 0; i < 20; i++) {
|
|
75
|
-
messages.push({
|
|
76
|
-
content: `Message ${i}`,
|
|
77
|
-
createdAt: Date.now(),
|
|
78
|
-
id: `msg-${i}`,
|
|
79
|
-
role: i % 2 === 0 ? 'user' : 'assistant',
|
|
80
|
-
updatedAt: Date.now(),
|
|
81
|
-
} as UIChatMessage);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
const result = await serverMessagesEngine({
|
|
85
|
-
enableHistoryCount: true,
|
|
86
|
-
historyCount: 5,
|
|
87
|
-
messages,
|
|
88
|
-
model: 'gpt-4',
|
|
89
|
-
provider: 'openai',
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
expect(result.length).toBeLessThanOrEqual(5);
|
|
93
|
-
});
|
|
94
|
-
});
|
|
95
|
-
|
|
96
71
|
describe('knowledge injection', () => {
|
|
97
72
|
it('should inject file contents', async () => {
|
|
98
73
|
const messages = createBasicMessages();
|
|
@@ -6,7 +6,6 @@ import { SessionModel } from '@/database/models/session';
|
|
|
6
6
|
import { UserModel, UserNotFoundError } from '@/database/models/user';
|
|
7
7
|
import { serverDB } from '@/database/server';
|
|
8
8
|
import { KeyVaultsGateKeeper } from '@/server/modules/KeyVaultsEncrypt';
|
|
9
|
-
import { NextAuthUserService } from '@/server/services/nextAuthUser';
|
|
10
9
|
import { UserService } from '@/server/services/user';
|
|
11
10
|
|
|
12
11
|
import { userRouter } from '../user';
|
|
@@ -22,11 +21,6 @@ vi.mock('@/database/models/user');
|
|
|
22
21
|
vi.mock('@/server/modules/KeyVaultsEncrypt');
|
|
23
22
|
vi.mock('@/server/modules/S3');
|
|
24
23
|
vi.mock('@/server/services/user');
|
|
25
|
-
vi.mock('@/server/services/nextAuthUser');
|
|
26
|
-
vi.mock('@/envs/auth', () => ({
|
|
27
|
-
enableBetterAuth: false,
|
|
28
|
-
enableNextAuth: false,
|
|
29
|
-
}));
|
|
30
24
|
|
|
31
25
|
describe('userRouter', () => {
|
|
32
26
|
const mockUserId = 'test-user-id';
|
|
@@ -122,7 +116,6 @@ describe('userRouter', () => {
|
|
|
122
116
|
userId: mockUserId,
|
|
123
117
|
});
|
|
124
118
|
});
|
|
125
|
-
|
|
126
119
|
});
|
|
127
120
|
|
|
128
121
|
describe('makeUserOnboarded', () => {
|
|
@@ -140,47 +133,6 @@ describe('userRouter', () => {
|
|
|
140
133
|
});
|
|
141
134
|
});
|
|
142
135
|
|
|
143
|
-
describe('unlinkSSOProvider', () => {
|
|
144
|
-
it('should unlink SSO provider successfully', async () => {
|
|
145
|
-
const mockInput = {
|
|
146
|
-
provider: 'google',
|
|
147
|
-
providerAccountId: '123',
|
|
148
|
-
};
|
|
149
|
-
|
|
150
|
-
const mockAccount = {
|
|
151
|
-
userId: mockUserId,
|
|
152
|
-
provider: 'google',
|
|
153
|
-
providerAccountId: '123',
|
|
154
|
-
type: 'oauth',
|
|
155
|
-
};
|
|
156
|
-
|
|
157
|
-
vi.mocked(NextAuthUserService).mockReturnValue({
|
|
158
|
-
getAccount: vi.fn().mockResolvedValue(mockAccount),
|
|
159
|
-
unlinkAccount: vi.fn().mockResolvedValue(undefined),
|
|
160
|
-
} as any);
|
|
161
|
-
|
|
162
|
-
await expect(
|
|
163
|
-
userRouter.createCaller({ ...mockCtx }).unlinkSSOProvider(mockInput),
|
|
164
|
-
).resolves.not.toThrow();
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
it('should throw error if account does not exist', async () => {
|
|
168
|
-
const mockInput = {
|
|
169
|
-
provider: 'google',
|
|
170
|
-
providerAccountId: '123',
|
|
171
|
-
};
|
|
172
|
-
|
|
173
|
-
vi.mocked(NextAuthUserService).mockReturnValue({
|
|
174
|
-
getAccount: vi.fn().mockResolvedValue(null),
|
|
175
|
-
unlinkAccount: vi.fn(),
|
|
176
|
-
} as any);
|
|
177
|
-
|
|
178
|
-
await expect(
|
|
179
|
-
userRouter.createCaller({ ...mockCtx }).unlinkSSOProvider(mockInput),
|
|
180
|
-
).rejects.toThrow('The account does not exist');
|
|
181
|
-
});
|
|
182
|
-
});
|
|
183
|
-
|
|
184
136
|
describe('updateSettings', () => {
|
|
185
137
|
it('should update settings with encrypted key vaults', async () => {
|
|
186
138
|
const mockSettings = {
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { isDesktop } from '@lobechat/const';
|
|
2
2
|
import {
|
|
3
|
-
NextAuthAccountSchame,
|
|
4
3
|
Plans,
|
|
5
4
|
UserGuideSchema,
|
|
6
5
|
type UserInitializationState,
|
|
@@ -16,8 +15,8 @@ import { v4 as uuidv4 } from 'uuid';
|
|
|
16
15
|
import { z } from 'zod';
|
|
17
16
|
|
|
18
17
|
import {
|
|
19
|
-
getIsInviteCodeRequired,
|
|
20
18
|
getIsInWaitList,
|
|
19
|
+
getIsInviteCodeRequired,
|
|
21
20
|
getReferralStatus,
|
|
22
21
|
getSubscriptionPlan,
|
|
23
22
|
} from '@/business/server/user';
|
|
@@ -29,7 +28,6 @@ import { serverDatabase } from '@/libs/trpc/lambda/middleware';
|
|
|
29
28
|
import { KeyVaultsGateKeeper } from '@/server/modules/KeyVaultsEncrypt';
|
|
30
29
|
import { FileS3 } from '@/server/modules/S3';
|
|
31
30
|
import { FileService } from '@/server/services/file';
|
|
32
|
-
import { NextAuthUserService } from '@/server/services/nextAuthUser';
|
|
33
31
|
|
|
34
32
|
const usernameSchema = z
|
|
35
33
|
.string()
|
|
@@ -42,7 +40,6 @@ const userProcedure = authedProcedure.use(serverDatabase).use(async ({ ctx, next
|
|
|
42
40
|
ctx: {
|
|
43
41
|
fileService: new FileService(ctx.serverDB, ctx.userId),
|
|
44
42
|
messageModel: new MessageModel(ctx.serverDB, ctx.userId),
|
|
45
|
-
nextAuthUserService: new NextAuthUserService(ctx.serverDB),
|
|
46
43
|
sessionModel: new SessionModel(ctx.serverDB, ctx.userId),
|
|
47
44
|
userModel: new UserModel(ctx.serverDB, ctx.userId),
|
|
48
45
|
},
|
|
@@ -138,14 +135,6 @@ export const userRouter = router({
|
|
|
138
135
|
return ctx.userModel.deleteSetting();
|
|
139
136
|
}),
|
|
140
137
|
|
|
141
|
-
unlinkSSOProvider: userProcedure.input(NextAuthAccountSchame).mutation(async ({ ctx, input }) => {
|
|
142
|
-
const { provider, providerAccountId } = input;
|
|
143
|
-
const account = await ctx.nextAuthUserService.getAccount(providerAccountId, provider);
|
|
144
|
-
// The userId can either get from ctx.nextAuth?.id or ctx.userId
|
|
145
|
-
if (!account || account.userId !== ctx.userId) throw new Error('The account does not exist');
|
|
146
|
-
await ctx.nextAuthUserService.unlinkAccount({ provider, providerAccountId });
|
|
147
|
-
}),
|
|
148
|
-
|
|
149
138
|
updateAvatar: userProcedure.input(z.string()).mutation(async ({ ctx, input }) => {
|
|
150
139
|
// If it's Base64 data, need to upload to S3
|
|
151
140
|
if (input.startsWith('data:image')) {
|
|
@@ -49,8 +49,8 @@ export class NodemailerImpl implements EmailServiceImpl {
|
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
async sendMail(payload: EmailPayload): Promise<EmailResponse> {
|
|
52
|
-
// Use
|
|
53
|
-
const from = payload.from ?? emailEnv.SMTP_USER!;
|
|
52
|
+
// Use SMTP_FROM as default sender, fallback to SMTP_USER for backward compatibility
|
|
53
|
+
const from = payload.from ?? emailEnv.SMTP_FROM ?? emailEnv.SMTP_USER!;
|
|
54
54
|
|
|
55
55
|
log('Sending email with payload: %o', {
|
|
56
56
|
from,
|