@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/src/middleware.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server';
|
|
|
2
2
|
import { NextResponse } from 'next/server';
|
|
3
3
|
|
|
4
4
|
import { authEnv } from '@/config/auth';
|
|
5
|
-
import
|
|
5
|
+
import NextAuthEdge from '@/libs/next-auth/edge';
|
|
6
6
|
|
|
7
7
|
import { OAUTH_AUTHORIZED } from './const/auth';
|
|
8
8
|
|
|
@@ -20,7 +20,8 @@ export const config = {
|
|
|
20
20
|
|
|
21
21
|
const defaultMiddleware = () => NextResponse.next();
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
// Initialize an Edge compatible NextAuth middleware
|
|
24
|
+
const nextAuthMiddleware = NextAuthEdge.auth((req) => {
|
|
24
25
|
// skip the '/' route
|
|
25
26
|
if (req.nextUrl.pathname === '/') return NextResponse.next();
|
|
26
27
|
|
package/src/server/context.ts
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
2
2
|
import { getAuth } from '@clerk/nextjs/server';
|
|
3
|
+
import { User } from 'next-auth';
|
|
3
4
|
import { NextRequest } from 'next/server';
|
|
4
5
|
|
|
5
|
-
import { JWTPayload, LOBE_CHAT_AUTH_HEADER, enableClerk } from '@/const/auth';
|
|
6
|
+
import { JWTPayload, LOBE_CHAT_AUTH_HEADER, enableClerk, enableNextAuth } from '@/const/auth';
|
|
7
|
+
import NextAuthEdge from '@/libs/next-auth/edge';
|
|
6
8
|
|
|
7
9
|
type ClerkAuth = ReturnType<typeof getAuth>;
|
|
8
10
|
|
|
9
11
|
export interface AuthContext {
|
|
10
|
-
auth?: ClerkAuth;
|
|
11
12
|
authorizationHeader?: string | null;
|
|
13
|
+
clerkAuth?: ClerkAuth;
|
|
12
14
|
jwtPayload?: JWTPayload | null;
|
|
15
|
+
nextAuth?: User;
|
|
13
16
|
userId?: string | null;
|
|
14
17
|
}
|
|
15
18
|
|
|
@@ -18,12 +21,14 @@ export interface AuthContext {
|
|
|
18
21
|
* This is useful for testing when we don't want to mock Next.js' request/response
|
|
19
22
|
*/
|
|
20
23
|
export const createContextInner = async (params?: {
|
|
21
|
-
auth?: ClerkAuth;
|
|
22
24
|
authorizationHeader?: string | null;
|
|
25
|
+
clerkAuth?: ClerkAuth;
|
|
26
|
+
nextAuth?: User;
|
|
23
27
|
userId?: string | null;
|
|
24
28
|
}): Promise<AuthContext> => ({
|
|
25
|
-
auth: params?.auth,
|
|
26
29
|
authorizationHeader: params?.authorizationHeader,
|
|
30
|
+
clerkAuth: params?.clerkAuth,
|
|
31
|
+
nextAuth: params?.nextAuth,
|
|
27
32
|
userId: params?.userId,
|
|
28
33
|
});
|
|
29
34
|
|
|
@@ -45,7 +50,19 @@ export const createContext = async (request: NextRequest): Promise<Context> => {
|
|
|
45
50
|
auth = getAuth(request);
|
|
46
51
|
|
|
47
52
|
userId = auth.userId;
|
|
53
|
+
return createContextInner({ authorizationHeader: authorization, clerkAuth: auth, userId });
|
|
48
54
|
}
|
|
49
55
|
|
|
50
|
-
|
|
56
|
+
if (enableNextAuth) {
|
|
57
|
+
try {
|
|
58
|
+
const session = await NextAuthEdge.auth();
|
|
59
|
+
if (session && session?.user?.id) {
|
|
60
|
+
auth = session.user;
|
|
61
|
+
userId = session.user.id;
|
|
62
|
+
}
|
|
63
|
+
return createContextInner({ authorizationHeader: authorization, nextAuth: auth, userId });
|
|
64
|
+
} catch {}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return createContextInner({ authorizationHeader: authorization, userId });
|
|
51
68
|
};
|
|
@@ -14,6 +14,12 @@ const createCaller = createCallerFactory(configRouter);
|
|
|
14
14
|
let ctx: AuthContext;
|
|
15
15
|
let router: ReturnType<typeof createCaller>;
|
|
16
16
|
|
|
17
|
+
vi.mock('@/libs/next-auth/edge', () => {
|
|
18
|
+
return {
|
|
19
|
+
auth: vi.fn().mockResolvedValue(undefined),
|
|
20
|
+
};
|
|
21
|
+
});
|
|
22
|
+
|
|
17
23
|
beforeEach(async () => {
|
|
18
24
|
vi.resetAllMocks();
|
|
19
25
|
ctx = await createContextInner();
|
|
@@ -207,7 +207,7 @@ describe('AgentSlice', () => {
|
|
|
207
207
|
model: 'gemini-pro',
|
|
208
208
|
} as any);
|
|
209
209
|
|
|
210
|
-
renderHook(() => result.current.useInitAgentStore());
|
|
210
|
+
renderHook(() => result.current.useInitAgentStore(true));
|
|
211
211
|
|
|
212
212
|
await waitFor(async () => {
|
|
213
213
|
expect(result.current.agentMap[INBOX_SESSION_ID]).toEqual({ model: 'gemini-pro' });
|
|
@@ -215,12 +215,26 @@ describe('AgentSlice', () => {
|
|
|
215
215
|
});
|
|
216
216
|
});
|
|
217
217
|
|
|
218
|
+
it('should not modify state if user not logged in', async () => {
|
|
219
|
+
const { result } = renderHook(() => useAgentStore());
|
|
220
|
+
vi.spyOn(sessionService, 'getSessionConfig').mockResolvedValue({
|
|
221
|
+
model: 'gemini-pro',
|
|
222
|
+
} as any);
|
|
223
|
+
|
|
224
|
+
renderHook(() => result.current.useInitAgentStore(false));
|
|
225
|
+
|
|
226
|
+
await waitFor(async () => {
|
|
227
|
+
expect(result.current.agentMap[INBOX_SESSION_ID]).toBeUndefined();
|
|
228
|
+
expect(result.current.isInboxAgentConfigInit).toBe(false);
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
|
|
218
232
|
it('should not modify state on failure', async () => {
|
|
219
233
|
const { result } = renderHook(() => useAgentStore());
|
|
220
234
|
|
|
221
235
|
vi.spyOn(globalService, 'getDefaultAgentConfig').mockRejectedValueOnce(new Error());
|
|
222
236
|
|
|
223
|
-
renderHook(() => result.current.useInitAgentStore());
|
|
237
|
+
renderHook(() => result.current.useInitAgentStore(true));
|
|
224
238
|
|
|
225
239
|
await waitFor(async () => {
|
|
226
240
|
expect(result.current.agentMap[INBOX_SESSION_ID]).toBeUndefined();
|
|
@@ -27,6 +27,7 @@ export interface AgentChatAction {
|
|
|
27
27
|
|
|
28
28
|
useFetchAgentConfig: (id: string) => SWRResponse<LobeAgentConfig>;
|
|
29
29
|
useInitAgentStore: (
|
|
30
|
+
isLogin: boolean | undefined,
|
|
30
31
|
defaultAgentConfig?: DeepPartial<LobeAgentConfig>,
|
|
31
32
|
) => SWRResponse<DeepPartial<LobeAgentConfig>>;
|
|
32
33
|
|
|
@@ -111,9 +112,9 @@ export const createChatSlice: StateCreator<
|
|
|
111
112
|
suspense: true,
|
|
112
113
|
},
|
|
113
114
|
),
|
|
114
|
-
useInitAgentStore: (defaultAgentConfig) =>
|
|
115
|
+
useInitAgentStore: (isLogin, defaultAgentConfig) =>
|
|
115
116
|
useOnlyFetchOnceSWR<DeepPartial<LobeAgentConfig>>(
|
|
116
|
-
'fetchInboxAgentConfig',
|
|
117
|
+
!!isLogin ? 'fetchInboxAgentConfig' : null,
|
|
117
118
|
() => sessionService.getSessionConfig(INBOX_SESSION_ID),
|
|
118
119
|
{
|
|
119
120
|
onSuccess: (data) => {
|
|
@@ -41,6 +41,8 @@ const isLogin = (s: UserStore) => {
|
|
|
41
41
|
};
|
|
42
42
|
|
|
43
43
|
export const authSelectors = {
|
|
44
|
+
enabledAuth: (s: UserStore): boolean => s.enableAuth(),
|
|
45
|
+
enabledNextAuth: (s: UserStore): boolean => !!s.enabledNextAuth,
|
|
44
46
|
isLoaded: (s: UserStore) => s.isLoaded,
|
|
45
47
|
isLogin,
|
|
46
48
|
isLoginWithAuth: (s: UserStore) => s.isSignedIn,
|
package/src/types/next-auth.d.ts
CHANGED