@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,137 +0,0 @@
|
|
|
1
|
-
import debug from 'debug';
|
|
2
|
-
import { type NextRequest, NextResponse } from 'next/server';
|
|
3
|
-
|
|
4
|
-
import { serverDBEnv } from '@/config/db';
|
|
5
|
-
import { serverDB } from '@/database/server';
|
|
6
|
-
import { dateKeys } from '@/libs/next-auth/adapter';
|
|
7
|
-
import { NextAuthUserService } from '@/server/services/nextAuthUser';
|
|
8
|
-
|
|
9
|
-
const log = debug('lobe-next-auth:api:auth:adapter');
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* @description Process the db query for the NextAuth adapter.
|
|
13
|
-
* Returns the db query result directly and let NextAuth handle the raw results.
|
|
14
|
-
* @returns {
|
|
15
|
-
* success: boolean; // Only return false if the database query fails or the action is invalid.
|
|
16
|
-
* data?: any;
|
|
17
|
-
* error?: string;
|
|
18
|
-
* }
|
|
19
|
-
*/
|
|
20
|
-
export async function POST(req: NextRequest) {
|
|
21
|
-
try {
|
|
22
|
-
// try validate the request
|
|
23
|
-
if (
|
|
24
|
-
!req.headers.get('Authorization') ||
|
|
25
|
-
req.headers.get('Authorization')?.trim() !== `Bearer ${serverDBEnv.KEY_VAULTS_SECRET}`
|
|
26
|
-
) {
|
|
27
|
-
log('Unauthorized request, missing or invalid Authorization header');
|
|
28
|
-
return NextResponse.json({ error: 'Unauthorized', success: false }, { status: 401 });
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// Parse the request body
|
|
32
|
-
const data = await req.json();
|
|
33
|
-
log('Received request data:', data);
|
|
34
|
-
// Preprocess
|
|
35
|
-
if (data?.data) {
|
|
36
|
-
for (const key of dateKeys) {
|
|
37
|
-
if (data?.data && data.data[key]) {
|
|
38
|
-
data.data[key] = new Date(data.data[key]);
|
|
39
|
-
continue;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
const service = new NextAuthUserService(serverDB);
|
|
44
|
-
let result;
|
|
45
|
-
switch (data.action) {
|
|
46
|
-
case 'createAuthenticator': {
|
|
47
|
-
result = await service.createAuthenticator(data.data);
|
|
48
|
-
break;
|
|
49
|
-
}
|
|
50
|
-
case 'createSession': {
|
|
51
|
-
result = await service.createSession(data.data);
|
|
52
|
-
break;
|
|
53
|
-
}
|
|
54
|
-
case 'createUser': {
|
|
55
|
-
result = await service.createUser(data.data);
|
|
56
|
-
break;
|
|
57
|
-
}
|
|
58
|
-
case 'createVerificationToken': {
|
|
59
|
-
result = await service.createVerificationToken(data.data);
|
|
60
|
-
break;
|
|
61
|
-
}
|
|
62
|
-
case 'deleteSession': {
|
|
63
|
-
result = await service.deleteSession(data.data);
|
|
64
|
-
break;
|
|
65
|
-
}
|
|
66
|
-
case 'deleteUser': {
|
|
67
|
-
result = await service.deleteUser(data.data);
|
|
68
|
-
break;
|
|
69
|
-
}
|
|
70
|
-
case 'getAccount': {
|
|
71
|
-
result = await service.getAccount(data.data.providerAccountId, data.data.provider);
|
|
72
|
-
break;
|
|
73
|
-
}
|
|
74
|
-
case 'getAuthenticator': {
|
|
75
|
-
result = await service.getAuthenticator(data.data);
|
|
76
|
-
break;
|
|
77
|
-
}
|
|
78
|
-
case 'getSessionAndUser': {
|
|
79
|
-
result = await service.getSessionAndUser(data.data);
|
|
80
|
-
break;
|
|
81
|
-
}
|
|
82
|
-
case 'getUser': {
|
|
83
|
-
result = await service.getUser(data.data);
|
|
84
|
-
break;
|
|
85
|
-
}
|
|
86
|
-
case 'getUserByAccount': {
|
|
87
|
-
result = await service.getUserByAccount(data.data);
|
|
88
|
-
break;
|
|
89
|
-
}
|
|
90
|
-
case 'getUserByEmail': {
|
|
91
|
-
result = await service.getUserByEmail(data.data);
|
|
92
|
-
break;
|
|
93
|
-
}
|
|
94
|
-
case 'linkAccount': {
|
|
95
|
-
result = await service.linkAccount(data.data);
|
|
96
|
-
break;
|
|
97
|
-
}
|
|
98
|
-
case 'listAuthenticatorsByUserId': {
|
|
99
|
-
result = await service.listAuthenticatorsByUserId(data.data);
|
|
100
|
-
break;
|
|
101
|
-
}
|
|
102
|
-
case 'unlinkAccount': {
|
|
103
|
-
result = await service.unlinkAccount(data.data);
|
|
104
|
-
break;
|
|
105
|
-
}
|
|
106
|
-
case 'updateAuthenticatorCounter': {
|
|
107
|
-
result = await service.updateAuthenticatorCounter(
|
|
108
|
-
data.data.credentialID,
|
|
109
|
-
data.data.counter,
|
|
110
|
-
);
|
|
111
|
-
break;
|
|
112
|
-
}
|
|
113
|
-
case 'updateSession': {
|
|
114
|
-
result = await service.updateSession(data.data);
|
|
115
|
-
break;
|
|
116
|
-
}
|
|
117
|
-
case 'updateUser': {
|
|
118
|
-
result = await service.updateUser(data.data);
|
|
119
|
-
break;
|
|
120
|
-
}
|
|
121
|
-
case 'useVerificationToken': {
|
|
122
|
-
result = await service.useVerificationToken(data.data);
|
|
123
|
-
break;
|
|
124
|
-
}
|
|
125
|
-
default: {
|
|
126
|
-
return NextResponse.json({ error: 'Invalid action', success: false }, { status: 400 });
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
return NextResponse.json({ data: result, success: true });
|
|
130
|
-
} catch (error) {
|
|
131
|
-
log('Error processing request:');
|
|
132
|
-
log(error);
|
|
133
|
-
return NextResponse.json({ error, success: false }, { status: 400 });
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
export const runtime = 'nodejs';
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { signIn } from 'next-auth/react';
|
|
4
|
-
import { useSearchParams } from '@/libs/next/navigation';
|
|
5
|
-
import { memo } from 'react';
|
|
6
|
-
|
|
7
|
-
import ErrorCapture from '@/components/Error';
|
|
8
|
-
|
|
9
|
-
enum ErrorEnum {
|
|
10
|
-
AccessDenied = 'AccessDenied',
|
|
11
|
-
Configuration = 'Configuration',
|
|
12
|
-
Default = 'Default',
|
|
13
|
-
Verification = 'Verification',
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const errorMap = {
|
|
17
|
-
[ErrorEnum.Configuration]:
|
|
18
|
-
'Wrong configuration, make sure you have the correct environment variables set. Visit https://lobehub.com/docs/self-hosting/advanced/authentication for more details.',
|
|
19
|
-
[ErrorEnum.AccessDenied]:
|
|
20
|
-
'Access was denied. Visit https://authjs.dev/reference/core/errors#accessdenied for more details. ',
|
|
21
|
-
[ErrorEnum.Verification]:
|
|
22
|
-
'Verification error, visit https://authjs.dev/reference/core/errors#verification for more details.',
|
|
23
|
-
[ErrorEnum.Default]:
|
|
24
|
-
'There was a problem when trying to authenticate. Visit https://authjs.dev/reference/core/errors for more details.',
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
export default memo(() => {
|
|
28
|
-
const search = useSearchParams();
|
|
29
|
-
const error = search.get('error') as ErrorEnum;
|
|
30
|
-
const props = {
|
|
31
|
-
error: {
|
|
32
|
-
cause: error,
|
|
33
|
-
message: errorMap[error] || 'Unknown error type.',
|
|
34
|
-
name: 'NextAuth Error',
|
|
35
|
-
},
|
|
36
|
-
reset: () => signIn(undefined, { callbackUrl: '/' }),
|
|
37
|
-
};
|
|
38
|
-
console.log('[NextAuth] Error:', props.error);
|
|
39
|
-
return <ErrorCapture {...props} />;
|
|
40
|
-
});
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { Suspense } from 'react';
|
|
2
|
-
|
|
3
|
-
import Loading from '@/components/Loading/BrandTextLoading';
|
|
4
|
-
|
|
5
|
-
import AuthErrorPage from './AuthErrorPage';
|
|
6
|
-
|
|
7
|
-
export default () => (
|
|
8
|
-
<Suspense fallback={<Loading debugId="Auth > Error" />}>
|
|
9
|
-
<AuthErrorPage />
|
|
10
|
-
</Suspense>
|
|
11
|
-
);
|
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { BRANDING_NAME } from '@lobechat/business-const';
|
|
4
|
-
import { DOCUMENTS_REFER_URL, PRIVACY_URL, TERMS_URL } from '@lobechat/const';
|
|
5
|
-
import { Button, Skeleton, Text } from '@lobehub/ui';
|
|
6
|
-
import { LobeHub } from '@lobehub/ui/brand';
|
|
7
|
-
import { Col, Flex, Row } from 'antd';
|
|
8
|
-
import { createStaticStyles } from 'antd-style';
|
|
9
|
-
import { AuthError } from 'next-auth';
|
|
10
|
-
import { signIn } from 'next-auth/react';
|
|
11
|
-
import { useRouter, useSearchParams } from '@/libs/next/navigation';
|
|
12
|
-
import { memo, useState } from 'react';
|
|
13
|
-
import { useTranslation } from 'react-i18next';
|
|
14
|
-
|
|
15
|
-
import BrandWatermark from '@/components/BrandWatermark';
|
|
16
|
-
import AuthIcons from '@/components/NextAuth/AuthIcons';
|
|
17
|
-
import { useUserStore } from '@/store/user';
|
|
18
|
-
|
|
19
|
-
const styles = createStaticStyles(({ css, cssVar }) => ({
|
|
20
|
-
button: css`
|
|
21
|
-
text-transform: capitalize;
|
|
22
|
-
`,
|
|
23
|
-
container: css`
|
|
24
|
-
min-width: 360px;
|
|
25
|
-
border: 1px solid ${cssVar.colorBorder};
|
|
26
|
-
border-radius: ${cssVar.borderRadiusLG}px;
|
|
27
|
-
background: ${cssVar.colorBgContainer};
|
|
28
|
-
`,
|
|
29
|
-
contentCard: css`
|
|
30
|
-
padding-block: 2.5rem;
|
|
31
|
-
padding-inline: 2rem;
|
|
32
|
-
`,
|
|
33
|
-
description: css`
|
|
34
|
-
margin: 0;
|
|
35
|
-
color: ${cssVar.colorTextSecondary};
|
|
36
|
-
`,
|
|
37
|
-
footer: css`
|
|
38
|
-
padding: 1rem;
|
|
39
|
-
border-block-start: 1px solid ${cssVar.colorBorder};
|
|
40
|
-
border-radius: 0 0 8px 8px;
|
|
41
|
-
|
|
42
|
-
color: ${cssVar.colorTextDescription};
|
|
43
|
-
|
|
44
|
-
background: ${cssVar.colorBgElevated};
|
|
45
|
-
`,
|
|
46
|
-
text: css`
|
|
47
|
-
text-align: center;
|
|
48
|
-
`,
|
|
49
|
-
title: css`
|
|
50
|
-
margin: 0;
|
|
51
|
-
color: ${cssVar.colorTextHeading};
|
|
52
|
-
`,
|
|
53
|
-
}));
|
|
54
|
-
|
|
55
|
-
const BtnListLoading = memo(() => {
|
|
56
|
-
return (
|
|
57
|
-
<Flex gap={'small'} vertical>
|
|
58
|
-
<Skeleton.Button active style={{ minWidth: 300 }} />
|
|
59
|
-
<Skeleton.Button active style={{ minWidth: 300 }} />
|
|
60
|
-
<Skeleton.Button active style={{ minWidth: 300 }} />
|
|
61
|
-
</Flex>
|
|
62
|
-
);
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Follow the implementation from AuthJS official documentation,
|
|
67
|
-
* but using client components.
|
|
68
|
-
* ref: https://authjs.dev/guides/pages/signin
|
|
69
|
-
*/
|
|
70
|
-
export default memo(() => {
|
|
71
|
-
const { t } = useTranslation('auth');
|
|
72
|
-
const { t: tCommon } = useTranslation('common');
|
|
73
|
-
const router = useRouter();
|
|
74
|
-
const [loadingProvider, setLoadingProvider] = useState<string | null>(null);
|
|
75
|
-
|
|
76
|
-
const oAuthSSOProviders = useUserStore((s) => s.oAuthSSOProviders);
|
|
77
|
-
|
|
78
|
-
const searchParams = useSearchParams();
|
|
79
|
-
|
|
80
|
-
// Redirect back to the page url, fallback to '/' if failed
|
|
81
|
-
const callbackUrl = searchParams.get('callbackUrl') ?? '/';
|
|
82
|
-
|
|
83
|
-
const handleSignIn = async (provider: string) => {
|
|
84
|
-
setLoadingProvider(provider);
|
|
85
|
-
try {
|
|
86
|
-
await signIn(provider, { redirectTo: callbackUrl });
|
|
87
|
-
} catch (error) {
|
|
88
|
-
setLoadingProvider(null);
|
|
89
|
-
// Signin can fail for a number of reasons, such as the user
|
|
90
|
-
// not existing, or the user not having the correct role.
|
|
91
|
-
// In some cases, you may want to redirect to a custom error
|
|
92
|
-
if (error instanceof AuthError) {
|
|
93
|
-
return router.push(`/next-auth/?error=${error.type}`);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// Otherwise if a redirects happens Next.js can handle it
|
|
97
|
-
// so you can just re-thrown the error and let Next.js handle it.
|
|
98
|
-
// Docs: https://nextjs.org/docs/app/api-reference/functions/redirect#server-component
|
|
99
|
-
throw error;
|
|
100
|
-
}
|
|
101
|
-
};
|
|
102
|
-
|
|
103
|
-
const footerBtns = [
|
|
104
|
-
{ href: DOCUMENTS_REFER_URL, id: 0, label: tCommon('document') },
|
|
105
|
-
{ href: PRIVACY_URL, id: 1, label: t('footer.privacy') },
|
|
106
|
-
{ href: TERMS_URL, id: 2, label: t('footer.terms') },
|
|
107
|
-
];
|
|
108
|
-
|
|
109
|
-
return (
|
|
110
|
-
<div className={styles.container}>
|
|
111
|
-
<div className={styles.contentCard}>
|
|
112
|
-
{/* Card Body */}
|
|
113
|
-
<Flex gap="large" vertical>
|
|
114
|
-
{/* Header */}
|
|
115
|
-
<div className={styles.text}>
|
|
116
|
-
<Text as={'h4'} className={styles.title}>
|
|
117
|
-
<div>
|
|
118
|
-
<LobeHub size={48} />
|
|
119
|
-
</div>
|
|
120
|
-
{t('signin.title')}
|
|
121
|
-
</Text>
|
|
122
|
-
<Text as={'p'} className={styles.description}>
|
|
123
|
-
{t('signin.subtitle', { appName: BRANDING_NAME })}
|
|
124
|
-
</Text>
|
|
125
|
-
</div>
|
|
126
|
-
{/* Content */}
|
|
127
|
-
<Flex gap="small" vertical>
|
|
128
|
-
{oAuthSSOProviders ? (
|
|
129
|
-
oAuthSSOProviders.map((provider) => (
|
|
130
|
-
<Button
|
|
131
|
-
className={styles.button}
|
|
132
|
-
icon={AuthIcons(provider, 16)}
|
|
133
|
-
key={provider}
|
|
134
|
-
loading={loadingProvider === provider}
|
|
135
|
-
onClick={() => handleSignIn(provider)}
|
|
136
|
-
>
|
|
137
|
-
{provider}
|
|
138
|
-
</Button>
|
|
139
|
-
))
|
|
140
|
-
) : (
|
|
141
|
-
<BtnListLoading />
|
|
142
|
-
)}
|
|
143
|
-
</Flex>
|
|
144
|
-
</Flex>
|
|
145
|
-
</div>
|
|
146
|
-
<div className={styles.footer}>
|
|
147
|
-
{/* Footer */}
|
|
148
|
-
<Row>
|
|
149
|
-
<Col span={12}>
|
|
150
|
-
<Flex justify="left" style={{ height: '100%' }}>
|
|
151
|
-
<BrandWatermark />
|
|
152
|
-
</Flex>
|
|
153
|
-
</Col>
|
|
154
|
-
<Col offset={4} span={8}>
|
|
155
|
-
<Flex justify="right">
|
|
156
|
-
{footerBtns.map((btn) => (
|
|
157
|
-
<Button key={btn.id} onClick={() => router.push(btn.href)} size="small" type="text">
|
|
158
|
-
{btn.label}
|
|
159
|
-
</Button>
|
|
160
|
-
))}
|
|
161
|
-
</Flex>
|
|
162
|
-
</Col>
|
|
163
|
-
</Row>
|
|
164
|
-
</div>
|
|
165
|
-
</div>
|
|
166
|
-
);
|
|
167
|
-
});
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { Suspense } from 'react';
|
|
2
|
-
|
|
3
|
-
import Loading from '@/components/Loading/BrandTextLoading';
|
|
4
|
-
|
|
5
|
-
import AuthSignInBox from './AuthSignInBox';
|
|
6
|
-
|
|
7
|
-
export default () => (
|
|
8
|
-
<Suspense fallback={<Loading debugId="Auth > SignIn" />}>
|
|
9
|
-
<AuthSignInBox />
|
|
10
|
-
</Suspense>
|
|
11
|
-
);
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { notFound } from '@/libs/next/navigation';
|
|
2
|
-
import { type PropsWithChildren } from 'react';
|
|
3
|
-
|
|
4
|
-
import { enableBetterAuth } from '@/envs/auth';
|
|
5
|
-
|
|
6
|
-
const Layout = ({ children }: PropsWithChildren) => {
|
|
7
|
-
if (!enableBetterAuth) return notFound();
|
|
8
|
-
|
|
9
|
-
return children;
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
export default Layout;
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { notFound } from '@/libs/next/navigation';
|
|
2
|
-
import { type PropsWithChildren } from 'react';
|
|
3
|
-
|
|
4
|
-
import { enableBetterAuth } from '@/envs/auth';
|
|
5
|
-
|
|
6
|
-
const Layout = ({ children }: PropsWithChildren) => {
|
|
7
|
-
if (!enableBetterAuth) return notFound();
|
|
8
|
-
|
|
9
|
-
return children;
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
export default Layout;
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { notFound } from '@/libs/next/navigation';
|
|
2
|
-
import { type PropsWithChildren } from 'react';
|
|
3
|
-
|
|
4
|
-
import { enableBetterAuth } from '@/envs/auth';
|
|
5
|
-
|
|
6
|
-
const Layout = ({ children }: PropsWithChildren) => {
|
|
7
|
-
if (!enableBetterAuth) return notFound();
|
|
8
|
-
|
|
9
|
-
return children;
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
export default Layout;
|
package/src/envs/auth.test.ts
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
2
|
-
|
|
3
|
-
import { getAuthConfig } from './auth';
|
|
4
|
-
|
|
5
|
-
const ORIGINAL_ENV = { ...process.env };
|
|
6
|
-
const ORIGINAL_WINDOW = globalThis.window;
|
|
7
|
-
|
|
8
|
-
describe('getAuthConfig fallbacks', () => {
|
|
9
|
-
beforeEach(() => {
|
|
10
|
-
// reset env to a clean clone before each test
|
|
11
|
-
process.env = { ...ORIGINAL_ENV };
|
|
12
|
-
globalThis.window = ORIGINAL_WINDOW;
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
afterEach(() => {
|
|
16
|
-
process.env = { ...ORIGINAL_ENV };
|
|
17
|
-
globalThis.window = ORIGINAL_WINDOW;
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
it('should fall back to NEXT_AUTH_SSO_PROVIDERS when AUTH_SSO_PROVIDERS is empty string', () => {
|
|
21
|
-
process.env.AUTH_SSO_PROVIDERS = '';
|
|
22
|
-
process.env.NEXT_AUTH_SSO_PROVIDERS = 'logto,github';
|
|
23
|
-
|
|
24
|
-
// Simulate server runtime so @t3-oss/env treats this as server-side access
|
|
25
|
-
// (happy-dom sets window by default in Vitest)
|
|
26
|
-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
27
|
-
// @ts-expect-error - allow overriding for test
|
|
28
|
-
globalThis.window = undefined;
|
|
29
|
-
|
|
30
|
-
const config = getAuthConfig();
|
|
31
|
-
|
|
32
|
-
expect(config.AUTH_SSO_PROVIDERS).toBe('logto,github');
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it('should fall back to NEXT_AUTH_SECRET when AUTH_SECRET is empty string', () => {
|
|
36
|
-
process.env.AUTH_SECRET = '';
|
|
37
|
-
process.env.NEXT_AUTH_SECRET = 'nextauth-secret';
|
|
38
|
-
|
|
39
|
-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
40
|
-
// @ts-expect-error - allow overriding for test
|
|
41
|
-
globalThis.window = undefined;
|
|
42
|
-
|
|
43
|
-
const config = getAuthConfig();
|
|
44
|
-
|
|
45
|
-
expect(config.AUTH_SECRET).toBe('nextauth-secret');
|
|
46
|
-
});
|
|
47
|
-
});
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { useSession } from 'next-auth/react';
|
|
4
|
-
import { memo, useEffect } from 'react';
|
|
5
|
-
import { createStoreUpdater } from 'zustand-utils';
|
|
6
|
-
|
|
7
|
-
import { useUserStore } from '@/store/user';
|
|
8
|
-
import { type LobeUser } from '@/types/user';
|
|
9
|
-
|
|
10
|
-
// update the user data into the context
|
|
11
|
-
const UserUpdater = memo(() => {
|
|
12
|
-
const { data: session, status } = useSession();
|
|
13
|
-
const isLoaded = status !== 'loading';
|
|
14
|
-
|
|
15
|
-
const isSignedIn = (status === 'authenticated' && session && !!session.user) || false;
|
|
16
|
-
|
|
17
|
-
const nextUser = session?.user;
|
|
18
|
-
const useStoreUpdater = createStoreUpdater(useUserStore);
|
|
19
|
-
|
|
20
|
-
useStoreUpdater('isLoaded', isLoaded);
|
|
21
|
-
useStoreUpdater('isSignedIn', isSignedIn);
|
|
22
|
-
useStoreUpdater('nextSession', session!);
|
|
23
|
-
|
|
24
|
-
// 使用 useEffect 处理需要保持同步的用户数据
|
|
25
|
-
useEffect(() => {
|
|
26
|
-
if (nextUser) {
|
|
27
|
-
const userAvatar = useUserStore.getState().user?.avatar;
|
|
28
|
-
|
|
29
|
-
const lobeUser = {
|
|
30
|
-
// 头像使用设置的,而不是从 next-auth 中获取
|
|
31
|
-
avatar: userAvatar || '',
|
|
32
|
-
email: nextUser.email,
|
|
33
|
-
fullName: nextUser.name,
|
|
34
|
-
id: nextUser.id,
|
|
35
|
-
} as LobeUser;
|
|
36
|
-
|
|
37
|
-
// 更新用户相关数据
|
|
38
|
-
useUserStore.setState({ nextUser: nextUser, user: lobeUser });
|
|
39
|
-
}
|
|
40
|
-
}, [nextUser]);
|
|
41
|
-
return null;
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
export default UserUpdater;
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { SessionProvider } from 'next-auth/react';
|
|
2
|
-
import { type PropsWithChildren } from 'react';
|
|
3
|
-
|
|
4
|
-
import { API_ENDPOINTS } from '@/services/_url';
|
|
5
|
-
|
|
6
|
-
import UserUpdater from './UserUpdater';
|
|
7
|
-
|
|
8
|
-
const NextAuth = ({ children }: PropsWithChildren) => {
|
|
9
|
-
return (
|
|
10
|
-
<SessionProvider basePath={API_ENDPOINTS.oauth}>
|
|
11
|
-
{children}
|
|
12
|
-
<UserUpdater />
|
|
13
|
-
</SessionProvider>
|
|
14
|
-
);
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
export default NextAuth;
|