@lobehub/chat 1.7.10 → 1.8.0
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.example +8 -0
- package/CHANGELOG.md +25 -0
- package/package.json +1 -1
- package/src/app/(main)/(mobile)/me/(home)/__tests__/UserBanner.test.tsx +9 -5
- package/src/app/(main)/(mobile)/me/(home)/__tests__/useCategory.test.tsx +27 -10
- package/src/app/(main)/(mobile)/me/(home)/features/UserBanner.tsx +22 -3
- package/src/app/(main)/(mobile)/me/(home)/features/useCategory.tsx +2 -2
- package/src/app/(main)/settings/_layout/Mobile/Header.tsx +3 -2
- package/src/app/(main)/settings/common/features/Common.tsx +2 -43
- package/src/app/(main)/settings/common/features/Theme/index.tsx +10 -3
- package/src/app/api/auth/[...nextauth]/route.ts +2 -2
- package/src/app/api/auth/error/AuthErrorPage.tsx +38 -0
- package/src/app/api/auth/error/page.tsx +5 -0
- package/src/database/server/migrations/0004_add_next_auth.sql +60 -0
- package/src/database/server/migrations/meta/0004_snapshot.json +2119 -0
- package/src/database/server/migrations/meta/_journal.json +7 -0
- package/src/database/server/models/__tests__/nextauth.test.ts +496 -0
- package/src/database/server/models/__tests__/user.test.ts +13 -0
- package/src/database/server/models/user.ts +4 -0
- package/src/database/server/schemas/lobechat.ts +7 -0
- package/src/database/server/schemas/nextauth.ts +90 -0
- package/src/layout/GlobalProvider/StoreInitialization.tsx +18 -3
- package/src/libs/next-auth/adapter/index.ts +264 -0
- package/src/libs/next-auth/adapter/utils.ts +62 -0
- package/src/libs/next-auth/auth.config.ts +45 -0
- package/src/libs/next-auth/edge.ts +26 -0
- package/src/libs/next-auth/index.ts +26 -39
- package/src/libs/next-auth/sso-providers/auth0.ts +11 -0
- package/src/libs/next-auth/sso-providers/authentik.ts +12 -0
- package/src/libs/next-auth/sso-providers/azure-ad.ts +12 -0
- package/src/libs/next-auth/sso-providers/github.ts +11 -0
- package/src/libs/next-auth/sso-providers/sso.config.ts +8 -0
- package/src/libs/next-auth/sso-providers/zitadel.ts +9 -0
- package/src/libs/trpc/middleware/password.test.ts +6 -0
- package/src/libs/trpc/middleware/userAuth.test.ts +6 -0
- package/src/middleware.ts +3 -2
- package/src/server/context.ts +22 -5
- package/src/server/routers/edge/config/index.test.ts +6 -0
- package/src/store/agent/slices/chat/action.test.ts +16 -2
- package/src/store/agent/slices/chat/action.ts +3 -2
- package/src/store/user/slices/auth/selectors.ts +2 -0
- package/src/types/next-auth.d.ts +3 -0
package/.env.example
CHANGED
|
@@ -168,6 +168,14 @@ OPENAI_API_KEY=sk-xxxxxxxxx
|
|
|
168
168
|
#CLERK_WEBHOOK_SECRET=whsec_xxxxxxxxxxxxxxxxxxxxxx
|
|
169
169
|
|
|
170
170
|
|
|
171
|
+
# NextAuth related configurations
|
|
172
|
+
# NEXT_AUTH_SECRET=
|
|
173
|
+
|
|
174
|
+
# Auth0 configurations
|
|
175
|
+
# AUTH0_CLIENT_ID=
|
|
176
|
+
# AUTH0_CLIENT_SECRET=
|
|
177
|
+
# AUTH0_ISSUER=https://your-domain.auth0.com
|
|
178
|
+
|
|
171
179
|
########################################
|
|
172
180
|
########## Server Database #############
|
|
173
181
|
########################################
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
# Changelog
|
|
4
4
|
|
|
5
|
+
## [Version 1.8.0](https://github.com/lobehub/lobe-chat/compare/v1.7.10...v1.8.0)
|
|
6
|
+
|
|
7
|
+
<sup>Released on **2024-08-02**</sup>
|
|
8
|
+
|
|
9
|
+
#### ✨ Features
|
|
10
|
+
|
|
11
|
+
- **misc**: Add NextAuth as authentication service in server database.
|
|
12
|
+
|
|
13
|
+
<br/>
|
|
14
|
+
|
|
15
|
+
<details>
|
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
17
|
+
|
|
18
|
+
#### What's improved
|
|
19
|
+
|
|
20
|
+
- **misc**: Add NextAuth as authentication service in server database, closes [#2935](https://github.com/lobehub/lobe-chat/issues/2935) ([5a0b972](https://github.com/lobehub/lobe-chat/commit/5a0b972))
|
|
21
|
+
|
|
22
|
+
</details>
|
|
23
|
+
|
|
24
|
+
<div align="right">
|
|
25
|
+
|
|
26
|
+
[](#readme-top)
|
|
27
|
+
|
|
28
|
+
</div>
|
|
29
|
+
|
|
5
30
|
### [Version 1.7.10](https://github.com/lobehub/lobe-chat/compare/v1.7.9...v1.7.10)
|
|
6
31
|
|
|
7
32
|
<sup>Released on **2024-08-02**</sup>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lobehub/chat",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.0",
|
|
4
4
|
"description": "Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"framework",
|
|
@@ -26,12 +26,16 @@ vi.mock('@/features/User/UserLoginOrSignup/Community', () => ({
|
|
|
26
26
|
|
|
27
27
|
// 定义一个变量来存储 enableAuth 的值
|
|
28
28
|
let enableAuth = true;
|
|
29
|
+
let enableClerk = false;
|
|
29
30
|
|
|
30
31
|
// 模拟 @/const/auth 模块
|
|
31
32
|
vi.mock('@/const/auth', () => ({
|
|
32
33
|
get enableAuth() {
|
|
33
34
|
return enableAuth;
|
|
34
35
|
},
|
|
36
|
+
get enableClerk() {
|
|
37
|
+
return enableClerk;
|
|
38
|
+
},
|
|
35
39
|
}));
|
|
36
40
|
|
|
37
41
|
afterEach(() => {
|
|
@@ -41,9 +45,8 @@ afterEach(() => {
|
|
|
41
45
|
describe('UserBanner', () => {
|
|
42
46
|
it('should render UserInfo and DataStatistics when auth is disabled', () => {
|
|
43
47
|
act(() => {
|
|
44
|
-
useUserStore.setState({ isSignedIn: false });
|
|
48
|
+
useUserStore.setState({ isSignedIn: false, enableAuth: () => false });
|
|
45
49
|
});
|
|
46
|
-
enableAuth = false;
|
|
47
50
|
|
|
48
51
|
render(<UserBanner />);
|
|
49
52
|
|
|
@@ -56,7 +59,8 @@ describe('UserBanner', () => {
|
|
|
56
59
|
act(() => {
|
|
57
60
|
useUserStore.setState({ isSignedIn: true });
|
|
58
61
|
});
|
|
59
|
-
|
|
62
|
+
|
|
63
|
+
enableClerk = true;
|
|
60
64
|
|
|
61
65
|
render(<UserBanner />);
|
|
62
66
|
|
|
@@ -67,9 +71,9 @@ describe('UserBanner', () => {
|
|
|
67
71
|
|
|
68
72
|
it('should render UserLoginOrSignup when user is not logged in with auth enabled', () => {
|
|
69
73
|
act(() => {
|
|
70
|
-
useUserStore.setState({ isSignedIn: false });
|
|
74
|
+
useUserStore.setState({ isSignedIn: false, enableAuth: () => true });
|
|
71
75
|
});
|
|
72
|
-
|
|
76
|
+
enableClerk = true;
|
|
73
77
|
|
|
74
78
|
render(<UserBanner />);
|
|
75
79
|
|
|
@@ -24,7 +24,7 @@ vi.mock('../../settings/features/useCategory', () => ({
|
|
|
24
24
|
|
|
25
25
|
// 定义一个变量来存储 enableAuth 的值
|
|
26
26
|
let enableAuth = true;
|
|
27
|
-
let enableClerk =
|
|
27
|
+
let enableClerk = false;
|
|
28
28
|
// 模拟 @/const/auth 模块
|
|
29
29
|
vi.mock('@/const/auth', () => ({
|
|
30
30
|
get enableAuth() {
|
|
@@ -37,16 +37,16 @@ vi.mock('@/const/auth', () => ({
|
|
|
37
37
|
|
|
38
38
|
afterEach(() => {
|
|
39
39
|
enableAuth = true;
|
|
40
|
-
enableClerk =
|
|
40
|
+
enableClerk = false;
|
|
41
41
|
});
|
|
42
42
|
|
|
43
|
+
// 目前对 enableAuth 的判定是在 useUserStore 中,所以需要 mock useUserStore
|
|
44
|
+
// 类型定义: enableAuth: () => boolean
|
|
43
45
|
describe('useCategory', () => {
|
|
44
46
|
it('should return correct items when the user is logged in with authentication', () => {
|
|
45
47
|
act(() => {
|
|
46
|
-
useUserStore.setState({ isSignedIn: true });
|
|
48
|
+
useUserStore.setState({ isSignedIn: true, enableAuth: () => true });
|
|
47
49
|
});
|
|
48
|
-
enableAuth = true;
|
|
49
|
-
enableClerk = false;
|
|
50
50
|
|
|
51
51
|
const { result } = renderHook(() => useCategory());
|
|
52
52
|
|
|
@@ -65,7 +65,6 @@ describe('useCategory', () => {
|
|
|
65
65
|
act(() => {
|
|
66
66
|
useUserStore.setState({ isSignedIn: true });
|
|
67
67
|
});
|
|
68
|
-
enableAuth = true;
|
|
69
68
|
enableClerk = true;
|
|
70
69
|
|
|
71
70
|
const { result } = renderHook(() => useCategory());
|
|
@@ -81,11 +80,29 @@ describe('useCategory', () => {
|
|
|
81
80
|
});
|
|
82
81
|
});
|
|
83
82
|
|
|
83
|
+
it('should return correct items when the user is logged in with NextAuth', () => {
|
|
84
|
+
act(() => {
|
|
85
|
+
useUserStore.setState({ isSignedIn: true, enableAuth: () => true, enabledNextAuth: true });
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
const { result } = renderHook(() => useCategory());
|
|
89
|
+
|
|
90
|
+
act(() => {
|
|
91
|
+
const items = result.current;
|
|
92
|
+
// Should not render profile for NextAuth, it's Clerk only
|
|
93
|
+
expect(items.some((item) => item.key === 'profile')).toBe(false);
|
|
94
|
+
expect(items.some((item) => item.key === 'setting')).toBe(true);
|
|
95
|
+
expect(items.some((item) => item.key === 'data')).toBe(true);
|
|
96
|
+
expect(items.some((item) => item.key === 'docs')).toBe(true);
|
|
97
|
+
expect(items.some((item) => item.key === 'feedback')).toBe(true);
|
|
98
|
+
expect(items.some((item) => item.key === 'discord')).toBe(true);
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
|
|
84
102
|
it('should return correct items when the user is not logged in', () => {
|
|
85
103
|
act(() => {
|
|
86
|
-
useUserStore.setState({ isSignedIn: false });
|
|
104
|
+
useUserStore.setState({ isSignedIn: false, enableAuth: () => true });
|
|
87
105
|
});
|
|
88
|
-
enableAuth = true;
|
|
89
106
|
|
|
90
107
|
const { result } = renderHook(() => useCategory());
|
|
91
108
|
|
|
@@ -102,9 +119,9 @@ describe('useCategory', () => {
|
|
|
102
119
|
|
|
103
120
|
it('should handle settings for non-authenticated users', () => {
|
|
104
121
|
act(() => {
|
|
105
|
-
useUserStore.setState({ isSignedIn: false });
|
|
122
|
+
useUserStore.setState({ isSignedIn: false, enableAuth: () => false });
|
|
106
123
|
});
|
|
107
|
-
|
|
124
|
+
enableClerk = false;
|
|
108
125
|
|
|
109
126
|
const { result } = renderHook(() => useCategory());
|
|
110
127
|
|
|
@@ -4,7 +4,6 @@ import { useRouter } from 'next/navigation';
|
|
|
4
4
|
import { memo } from 'react';
|
|
5
5
|
import { Flexbox } from 'react-layout-kit';
|
|
6
6
|
|
|
7
|
-
import { enableAuth } from '@/const/auth';
|
|
8
7
|
import DataStatistics from '@/features/User/DataStatistics';
|
|
9
8
|
import UserInfo from '@/features/User/UserInfo';
|
|
10
9
|
import UserLoginOrSignup from '@/features/User/UserLoginOrSignup/Community';
|
|
@@ -14,6 +13,11 @@ import { authSelectors } from '@/store/user/selectors';
|
|
|
14
13
|
const UserBanner = memo(() => {
|
|
15
14
|
const router = useRouter();
|
|
16
15
|
const isLoginWithAuth = useUserStore(authSelectors.isLoginWithAuth);
|
|
16
|
+
const [enableAuth, signIn, enabledNextAuth] = useUserStore((s) => [
|
|
17
|
+
authSelectors.enabledAuth(s),
|
|
18
|
+
s.openLogin,
|
|
19
|
+
authSelectors.enabledNextAuth(s),
|
|
20
|
+
]);
|
|
17
21
|
|
|
18
22
|
return (
|
|
19
23
|
<Flexbox gap={12} paddingBlock={8}>
|
|
@@ -24,11 +28,26 @@ const UserBanner = memo(() => {
|
|
|
24
28
|
</>
|
|
25
29
|
) : isLoginWithAuth ? (
|
|
26
30
|
<>
|
|
27
|
-
<UserInfo
|
|
31
|
+
<UserInfo
|
|
32
|
+
onClick={() => {
|
|
33
|
+
// Profile page only works with Clerk
|
|
34
|
+
if (enabledNextAuth) return;
|
|
35
|
+
router.push('/me/profile');
|
|
36
|
+
}}
|
|
37
|
+
/>
|
|
28
38
|
<DataStatistics paddingInline={12} />
|
|
29
39
|
</>
|
|
30
40
|
) : (
|
|
31
|
-
<UserLoginOrSignup
|
|
41
|
+
<UserLoginOrSignup
|
|
42
|
+
onClick={() => {
|
|
43
|
+
// If use NextAuth, call openLogin method directly
|
|
44
|
+
if (enabledNextAuth) {
|
|
45
|
+
signIn();
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
router.push('/login');
|
|
49
|
+
}}
|
|
50
|
+
/>
|
|
32
51
|
)}
|
|
33
52
|
</Flexbox>
|
|
34
53
|
);
|
|
@@ -4,7 +4,6 @@ import { useRouter } from 'next/navigation';
|
|
|
4
4
|
import { useTranslation } from 'react-i18next';
|
|
5
5
|
|
|
6
6
|
import { CellProps } from '@/components/Cell';
|
|
7
|
-
import { enableAuth } from '@/const/auth';
|
|
8
7
|
import { DISCORD, DOCUMENTS, FEEDBACK } from '@/const/url';
|
|
9
8
|
import { isServerMode } from '@/const/version';
|
|
10
9
|
import { usePWAInstall } from '@/hooks/usePWAInstall';
|
|
@@ -17,10 +16,11 @@ export const useCategory = () => {
|
|
|
17
16
|
const router = useRouter();
|
|
18
17
|
const { canInstall, install } = usePWAInstall();
|
|
19
18
|
const { t } = useTranslation(['common', 'setting', 'auth']);
|
|
20
|
-
const [isLogin, isLoginWithAuth, isLoginWithClerk] = useUserStore((s) => [
|
|
19
|
+
const [isLogin, isLoginWithAuth, isLoginWithClerk, enableAuth] = useUserStore((s) => [
|
|
21
20
|
authSelectors.isLogin(s),
|
|
22
21
|
authSelectors.isLoginWithAuth(s),
|
|
23
22
|
authSelectors.isLoginWithClerk(s),
|
|
23
|
+
authSelectors.enabledAuth(s),
|
|
24
24
|
]);
|
|
25
25
|
|
|
26
26
|
const profile: CellProps[] = [
|
|
@@ -7,9 +7,10 @@ import { memo } from 'react';
|
|
|
7
7
|
import { useTranslation } from 'react-i18next';
|
|
8
8
|
import { Flexbox } from 'react-layout-kit';
|
|
9
9
|
|
|
10
|
-
import { enableAuth } from '@/const/auth';
|
|
11
10
|
import { useActiveSettingsKey } from '@/hooks/useActiveSettingsKey';
|
|
12
11
|
import { SettingsTabs } from '@/store/global/initialState';
|
|
12
|
+
import { useUserStore } from '@/store/user';
|
|
13
|
+
import { authSelectors } from '@/store/user/selectors';
|
|
13
14
|
import { mobileHeaderSticky } from '@/styles/mobileHeader';
|
|
14
15
|
|
|
15
16
|
const Header = memo(() => {
|
|
@@ -19,6 +20,7 @@ const Header = memo(() => {
|
|
|
19
20
|
const searchParams = useSearchParams();
|
|
20
21
|
const activeSettingsKey = useActiveSettingsKey();
|
|
21
22
|
|
|
23
|
+
const enableAuth = useUserStore(authSelectors.enabledAuth);
|
|
22
24
|
const handleBackClick = () => {
|
|
23
25
|
if (searchParams.has('session') && searchParams.has('showMobileWorkspace')) {
|
|
24
26
|
router.push(`/chat?${searchParams.toString()}`);
|
|
@@ -26,7 +28,6 @@ const Header = memo(() => {
|
|
|
26
28
|
router.push(enableAuth ? '/me/settings' : '/me');
|
|
27
29
|
}
|
|
28
30
|
};
|
|
29
|
-
|
|
30
31
|
return (
|
|
31
32
|
<MobileNavBar
|
|
32
33
|
center={
|
|
@@ -16,7 +16,7 @@ import { serverConfigSelectors } from '@/store/serverConfig/selectors';
|
|
|
16
16
|
import { useSessionStore } from '@/store/session';
|
|
17
17
|
import { useToolStore } from '@/store/tool';
|
|
18
18
|
import { useUserStore } from '@/store/user';
|
|
19
|
-
import { settingsSelectors
|
|
19
|
+
import { settingsSelectors } from '@/store/user/selectors';
|
|
20
20
|
|
|
21
21
|
type SettingItemGroup = ItemGroup;
|
|
22
22
|
|
|
@@ -24,10 +24,7 @@ const Common = memo(() => {
|
|
|
24
24
|
const { t } = useTranslation('setting');
|
|
25
25
|
const [form] = Form.useForm();
|
|
26
26
|
|
|
27
|
-
const isSignedIn = useUserStore((s) => s.isSignedIn);
|
|
28
27
|
const showAccessCodeConfig = useServerConfigStore(serverConfigSelectors.enabledAccessCode);
|
|
29
|
-
const showOAuthLogin = useServerConfigStore(serverConfigSelectors.enabledOAuthSSO);
|
|
30
|
-
const user = useUserStore(userProfileSelectors.userProfile, isEqual);
|
|
31
28
|
|
|
32
29
|
const [clearSessions, clearSessionGroups] = useSessionStore((s) => [
|
|
33
30
|
s.clearSessions,
|
|
@@ -40,31 +37,10 @@ const Common = memo(() => {
|
|
|
40
37
|
const [removeAllFiles] = useFileStore((s) => [s.removeAllFiles]);
|
|
41
38
|
const removeAllPlugins = useToolStore((s) => s.removeAllPlugins);
|
|
42
39
|
const settings = useUserStore(settingsSelectors.currentSettings, isEqual);
|
|
43
|
-
const [setSettings, resetSettings
|
|
44
|
-
s.setSettings,
|
|
45
|
-
s.resetSettings,
|
|
46
|
-
s.openLogin,
|
|
47
|
-
s.logout,
|
|
48
|
-
]);
|
|
40
|
+
const [setSettings, resetSettings] = useUserStore((s) => [s.setSettings, s.resetSettings]);
|
|
49
41
|
|
|
50
42
|
const { message, modal } = App.useApp();
|
|
51
43
|
|
|
52
|
-
const handleSignOut = useCallback(() => {
|
|
53
|
-
modal.confirm({
|
|
54
|
-
centered: true,
|
|
55
|
-
okButtonProps: { danger: true },
|
|
56
|
-
onOk: () => {
|
|
57
|
-
signOut();
|
|
58
|
-
message.success(t('settingSystem.oauth.signout.success'));
|
|
59
|
-
},
|
|
60
|
-
title: t('settingSystem.oauth.signout.confirm'),
|
|
61
|
-
});
|
|
62
|
-
}, []);
|
|
63
|
-
|
|
64
|
-
const handleSignIn = useCallback(() => {
|
|
65
|
-
signIn();
|
|
66
|
-
}, []);
|
|
67
|
-
|
|
68
44
|
const handleReset = useCallback(() => {
|
|
69
45
|
modal.confirm({
|
|
70
46
|
centered: true,
|
|
@@ -112,23 +88,6 @@ const Common = memo(() => {
|
|
|
112
88
|
label: t('settingSystem.accessCode.title'),
|
|
113
89
|
name: ['keyVaults', 'password'],
|
|
114
90
|
},
|
|
115
|
-
{
|
|
116
|
-
children: isSignedIn ? (
|
|
117
|
-
<Button onClick={handleSignOut}>{t('settingSystem.oauth.signout.action')}</Button>
|
|
118
|
-
) : (
|
|
119
|
-
<Button onClick={handleSignIn} type="primary">
|
|
120
|
-
{t('settingSystem.oauth.signin.action')}
|
|
121
|
-
</Button>
|
|
122
|
-
),
|
|
123
|
-
desc: isSignedIn
|
|
124
|
-
? `${user?.email} ${t('settingSystem.oauth.info.desc')}`
|
|
125
|
-
: t('settingSystem.oauth.signin.desc'),
|
|
126
|
-
hidden: !showOAuthLogin,
|
|
127
|
-
label: isSignedIn
|
|
128
|
-
? t('settingSystem.oauth.info.title')
|
|
129
|
-
: t('settingSystem.oauth.signin.title'),
|
|
130
|
-
minWidth: undefined,
|
|
131
|
-
},
|
|
132
91
|
{
|
|
133
92
|
children: (
|
|
134
93
|
<Button danger onClick={handleReset} type="primary">
|
|
@@ -9,13 +9,16 @@ import { memo } from 'react';
|
|
|
9
9
|
import { useTranslation } from 'react-i18next';
|
|
10
10
|
|
|
11
11
|
import { useSyncSettings } from '@/app/(main)/settings/hooks/useSyncSettings';
|
|
12
|
-
import { enableAuth } from '@/const/auth';
|
|
13
12
|
import { FORM_STYLE } from '@/const/layoutTokens';
|
|
14
13
|
import { imageUrl } from '@/const/url';
|
|
15
14
|
import AvatarWithUpload from '@/features/AvatarWithUpload';
|
|
16
15
|
import { Locales, localeOptions } from '@/locales/resources';
|
|
17
16
|
import { useUserStore } from '@/store/user';
|
|
18
|
-
import {
|
|
17
|
+
import {
|
|
18
|
+
authSelectors,
|
|
19
|
+
settingsSelectors,
|
|
20
|
+
userGeneralSettingsSelectors,
|
|
21
|
+
} from '@/store/user/selectors';
|
|
19
22
|
import { switchLang } from '@/utils/client/switchLang';
|
|
20
23
|
|
|
21
24
|
import { ThemeSwatchesNeutral, ThemeSwatchesPrimary } from './ThemeSwatches';
|
|
@@ -28,7 +31,11 @@ const Theme = memo(() => {
|
|
|
28
31
|
const [form] = Form.useForm();
|
|
29
32
|
const settings = useUserStore(settingsSelectors.currentSettings, isEqual);
|
|
30
33
|
const themeMode = useUserStore(userGeneralSettingsSelectors.currentThemeMode);
|
|
31
|
-
const [setThemeMode, setSettings] = useUserStore((s) => [
|
|
34
|
+
const [setThemeMode, setSettings, enableAuth] = useUserStore((s) => [
|
|
35
|
+
s.switchThemeMode,
|
|
36
|
+
s.setSettings,
|
|
37
|
+
authSelectors.enabledAuth(s),
|
|
38
|
+
]);
|
|
32
39
|
|
|
33
40
|
useSyncSettings(form);
|
|
34
41
|
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
|
|
1
|
+
import NextAuthNode from '@/libs/next-auth';
|
|
2
2
|
|
|
3
|
-
export const
|
|
3
|
+
export const { GET, POST } = NextAuthNode.handlers;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { signIn } from 'next-auth/react';
|
|
2
|
+
import { useSearchParams } from 'next/navigation';
|
|
3
|
+
import { memo } from 'react';
|
|
4
|
+
|
|
5
|
+
import ErrorCapture from '@/components/Error';
|
|
6
|
+
|
|
7
|
+
enum ErrorEnum {
|
|
8
|
+
AccessDenied = 'AccessDenied',
|
|
9
|
+
Configuration = 'Configuration',
|
|
10
|
+
Default = 'Default',
|
|
11
|
+
Verification = 'Verification',
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const errorMap = {
|
|
15
|
+
[ErrorEnum.Configuration]:
|
|
16
|
+
'Wrong configuration, make sure you have the correct environment variables set. Visit https://lobehub.com/docs/self-hosting/advanced/authentication for more details.',
|
|
17
|
+
[ErrorEnum.AccessDenied]:
|
|
18
|
+
'Access was denied. Visit https://authjs.dev/reference/core/errors#accessdenied for more details. ',
|
|
19
|
+
[ErrorEnum.Verification]:
|
|
20
|
+
'Verification error, visit https://authjs.dev/reference/core/errors#verification for more details.',
|
|
21
|
+
[ErrorEnum.Default]:
|
|
22
|
+
'There was a problem when trying to authenticate. Visit https://authjs.dev/reference/core/errors for more details.',
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export default memo(() => {
|
|
26
|
+
const search = useSearchParams();
|
|
27
|
+
const error = search.get('error') as ErrorEnum;
|
|
28
|
+
const props = {
|
|
29
|
+
error: {
|
|
30
|
+
cause: error,
|
|
31
|
+
message: errorMap[error] || 'Unknown error type.',
|
|
32
|
+
name: 'NextAuth Error',
|
|
33
|
+
},
|
|
34
|
+
reset: () => signIn(undefined, { callbackUrl: '/' }),
|
|
35
|
+
};
|
|
36
|
+
console.log('[NextAuth] Error:', props.error);
|
|
37
|
+
return <ErrorCapture {...props} />;
|
|
38
|
+
});
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
CREATE TABLE IF NOT EXISTS "nextauth_accounts" (
|
|
2
|
+
"access_token" text,
|
|
3
|
+
"expires_at" integer,
|
|
4
|
+
"id_token" text,
|
|
5
|
+
"provider" text NOT NULL,
|
|
6
|
+
"providerAccountId" text NOT NULL,
|
|
7
|
+
"refresh_token" text,
|
|
8
|
+
"scope" text,
|
|
9
|
+
"session_state" text,
|
|
10
|
+
"token_type" text,
|
|
11
|
+
"type" text NOT NULL,
|
|
12
|
+
"userId" text NOT NULL,
|
|
13
|
+
CONSTRAINT "nextauth_accounts_provider_providerAccountId_pk" PRIMARY KEY("provider","providerAccountId")
|
|
14
|
+
);
|
|
15
|
+
--> statement-breakpoint
|
|
16
|
+
CREATE TABLE IF NOT EXISTS "nextauth_authenticators" (
|
|
17
|
+
"counter" integer NOT NULL,
|
|
18
|
+
"credentialBackedUp" boolean NOT NULL,
|
|
19
|
+
"credentialDeviceType" text NOT NULL,
|
|
20
|
+
"credentialID" text NOT NULL,
|
|
21
|
+
"credentialPublicKey" text NOT NULL,
|
|
22
|
+
"providerAccountId" text NOT NULL,
|
|
23
|
+
"transports" text,
|
|
24
|
+
"userId" text NOT NULL,
|
|
25
|
+
CONSTRAINT "nextauth_authenticators_userId_credentialID_pk" PRIMARY KEY("userId","credentialID"),
|
|
26
|
+
CONSTRAINT "nextauth_authenticators_credentialID_unique" UNIQUE("credentialID")
|
|
27
|
+
);
|
|
28
|
+
--> statement-breakpoint
|
|
29
|
+
CREATE TABLE IF NOT EXISTS "nextauth_sessions" (
|
|
30
|
+
"expires" timestamp NOT NULL,
|
|
31
|
+
"sessionToken" text PRIMARY KEY NOT NULL,
|
|
32
|
+
"userId" text NOT NULL
|
|
33
|
+
);
|
|
34
|
+
--> statement-breakpoint
|
|
35
|
+
CREATE TABLE IF NOT EXISTS "nextauth_verificationtokens" (
|
|
36
|
+
"expires" timestamp NOT NULL,
|
|
37
|
+
"identifier" text NOT NULL,
|
|
38
|
+
"token" text NOT NULL,
|
|
39
|
+
CONSTRAINT "nextauth_verificationtokens_identifier_token_pk" PRIMARY KEY("identifier","token")
|
|
40
|
+
);
|
|
41
|
+
--> statement-breakpoint
|
|
42
|
+
ALTER TABLE "users" ADD COLUMN "full_name" text;--> statement-breakpoint
|
|
43
|
+
ALTER TABLE "users" ADD COLUMN "email_verified_at" timestamp with time zone;--> statement-breakpoint
|
|
44
|
+
DO $$ BEGIN
|
|
45
|
+
ALTER TABLE "nextauth_accounts" ADD CONSTRAINT "nextauth_accounts_userId_users_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;
|
|
46
|
+
EXCEPTION
|
|
47
|
+
WHEN duplicate_object THEN null;
|
|
48
|
+
END $$;
|
|
49
|
+
--> statement-breakpoint
|
|
50
|
+
DO $$ BEGIN
|
|
51
|
+
ALTER TABLE "nextauth_authenticators" ADD CONSTRAINT "nextauth_authenticators_userId_users_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;
|
|
52
|
+
EXCEPTION
|
|
53
|
+
WHEN duplicate_object THEN null;
|
|
54
|
+
END $$;
|
|
55
|
+
--> statement-breakpoint
|
|
56
|
+
DO $$ BEGIN
|
|
57
|
+
ALTER TABLE "nextauth_sessions" ADD CONSTRAINT "nextauth_sessions_userId_users_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;
|
|
58
|
+
EXCEPTION
|
|
59
|
+
WHEN duplicate_object THEN null;
|
|
60
|
+
END $$;
|