@lobehub/chat 1.122.6 → 1.123.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.
Files changed (37) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/changelog/v1.json +18 -0
  3. package/package.json +2 -2
  4. package/packages/model-bank/package.json +1 -0
  5. package/packages/model-bank/src/aiModels/index.ts +3 -1
  6. package/packages/model-bank/src/aiModels/newapi.ts +11 -0
  7. package/packages/model-runtime/src/RouterRuntime/createRuntime.test.ts +60 -0
  8. package/packages/model-runtime/src/RouterRuntime/createRuntime.ts +6 -3
  9. package/packages/model-runtime/src/index.ts +1 -0
  10. package/packages/model-runtime/src/newapi/index.test.ts +618 -0
  11. package/packages/model-runtime/src/newapi/index.ts +245 -0
  12. package/packages/model-runtime/src/runtimeMap.ts +2 -0
  13. package/packages/model-runtime/src/types/type.ts +1 -0
  14. package/packages/types/src/user/settings/keyVaults.ts +1 -0
  15. package/packages/utils/src/server/__tests__/auth.test.ts +1 -1
  16. package/packages/utils/src/server/auth.ts +2 -2
  17. package/src/app/(backend)/api/auth/adapter/route.ts +137 -0
  18. package/src/app/(backend)/api/webhooks/logto/route.ts +9 -0
  19. package/src/app/[variants]/(main)/settings/provider/(detail)/newapi/page.tsx +27 -0
  20. package/src/config/auth.ts +4 -0
  21. package/src/config/modelProviders/index.ts +3 -0
  22. package/src/config/modelProviders/newapi.ts +17 -0
  23. package/src/libs/next-auth/adapter/index.ts +103 -201
  24. package/src/libs/next-auth/auth.config.ts +22 -5
  25. package/src/libs/next-auth/index.ts +11 -24
  26. package/src/libs/trpc/edge/context.ts +2 -2
  27. package/src/libs/trpc/lambda/context.ts +2 -2
  28. package/src/locales/default/modelProvider.ts +26 -0
  29. package/src/middleware.ts +2 -2
  30. package/src/server/routers/lambda/user.test.ts +4 -17
  31. package/src/server/routers/lambda/user.ts +6 -15
  32. package/src/server/services/nextAuthUser/index.ts +282 -6
  33. package/packages/database/src/server/models/__tests__/nextauth.test.ts +0 -556
  34. package/src/libs/next-auth/edge.ts +0 -26
  35. package/src/server/services/nextAuthUser/index.test.ts +0 -108
  36. /package/{.env.development → .env.example.development} +0 -0
  37. /package/src/{libs/next-auth/adapter → server/services/nextAuthUser}/utils.ts +0 -0
@@ -4,273 +4,175 @@ import type {
4
4
  AdapterUser,
5
5
  VerificationToken,
6
6
  } from '@auth/core/adapters';
7
- import { and, eq } from 'drizzle-orm';
8
- import type { NeonDatabase } from 'drizzle-orm/neon-serverless';
7
+ import debug from 'debug';
9
8
  import { Adapter, AdapterAccount } from 'next-auth/adapters';
9
+ import urlJoin from 'url-join';
10
10
 
11
- import { UserModel } from '@/database/models/user';
12
- import * as schema from '@/database/schemas';
13
- import { AgentService } from '@/server/services/agent';
14
- import { merge } from '@/utils/merge';
11
+ import { serverDBEnv } from '@/config/db';
12
+ import { appEnv } from '@/envs/app';
15
13
 
16
- import {
17
- mapAdapterUserToLobeUser,
18
- mapAuthenticatorQueryResutlToAdapterAuthenticator,
19
- mapLobeUserToAdapterUser,
20
- partialMapAdapterUserToLobeUser,
21
- } from './utils';
14
+ const log = debug('lobe-next-auth:adapter');
22
15
 
23
- const {
24
- nextauthAccounts,
25
- nextauthAuthenticators,
26
- nextauthSessions,
27
- nextauthVerificationTokens,
28
- users,
29
- } = schema;
16
+ interface BackendAdapterResponse {
17
+ data?: any;
18
+ error?: string;
19
+ success: boolean;
20
+ }
21
+
22
+ // Due to use direct HTTP Post, the date string cannot parse automatically
23
+ export const dateKeys = ['expires', 'emailVerified'];
30
24
 
31
25
  /**
32
26
  * @description LobeNextAuthDbAdapter is implemented to handle the database operations
33
27
  * for NextAuth, this function do the same things as `src/app/api/webhooks/clerk/route.ts`
34
28
  * @returns {Adapter}
35
29
  */
36
- export function LobeNextAuthDbAdapter(serverDB: NeonDatabase<typeof schema>): Adapter {
30
+ export function LobeNextAuthDbAdapter(): Adapter {
31
+ const baseUrl = appEnv.APP_URL;
32
+
33
+ // Ensure the baseUrl is set, otherwise throw an error
34
+ if (!baseUrl) {
35
+ throw new Error('LobeNextAuthDbAdapter: APP_URL is not set in environment variables');
36
+ }
37
+ const interactionUrl = urlJoin(baseUrl, '/api/auth/adapter');
38
+ log(`LobeNextAuthDbAdapter initialized with url: ${interactionUrl}`);
39
+
40
+ // Ensure serverDBEnv.KEY_VAULTS_SECRET is set, otherwise throw an error
41
+ if (!serverDBEnv.KEY_VAULTS_SECRET) {
42
+ throw new Error('LobeNextAuthDbAdapter: KEY_VAULTS_SECRET is not set in environment variables');
43
+ }
44
+
45
+ const fetcher = (action: string, data: any) =>
46
+ fetch(interactionUrl, {
47
+ body: JSON.stringify({ action, data }),
48
+ headers: {
49
+ 'Authorization': `Bearer ${serverDBEnv.KEY_VAULTS_SECRET}`,
50
+ 'Content-Type': 'application/json',
51
+ },
52
+ method: 'POST',
53
+ });
54
+ const postProcessor = async (res: Response) => {
55
+ const data = (await res.json()) as BackendAdapterResponse;
56
+ log('LobeNextAuthDbAdapter: postProcessor called with data:', data);
57
+ if (!data.success) {
58
+ log('LobeNextAuthDbAdapter: Error in postProcessor:');
59
+ log(data);
60
+ throw new Error(`LobeNextAuthDbAdapter: ${data.error}`);
61
+ }
62
+ if (data?.data) {
63
+ for (const key of dateKeys) {
64
+ if (data.data[key]) {
65
+ data.data[key] = new Date(data.data[key]);
66
+ continue;
67
+ }
68
+ }
69
+ }
70
+ return data.data;
71
+ };
72
+
37
73
  return {
38
74
  async createAuthenticator(authenticator): Promise<AdapterAuthenticator> {
39
- const result = await serverDB
40
- .insert(nextauthAuthenticators)
41
- .values(authenticator)
42
- .returning()
43
- .then((res) => res[0] ?? undefined);
44
- if (!result) throw new Error('LobeNextAuthDbAdapter: Failed to create authenticator');
45
- return mapAuthenticatorQueryResutlToAdapterAuthenticator(result);
75
+ const data = await fetcher('createAuthenticator', authenticator);
76
+ return await postProcessor(data);
46
77
  },
47
- async createSession(data): Promise<AdapterSession> {
48
- return serverDB
49
- .insert(nextauthSessions)
50
- .values(data)
51
- .returning()
52
- .then((res) => res[0]);
78
+ async createSession(session): Promise<AdapterSession> {
79
+ const data = await fetcher('createSession', session);
80
+ return await postProcessor(data);
53
81
  },
54
82
  async createUser(user): Promise<AdapterUser> {
55
- const { id, name, email, emailVerified, image, providerAccountId } = user;
56
- // return the user if it already exists
57
- let existingUser =
58
- email && typeof email === 'string' && email.trim()
59
- ? await UserModel.findByEmail(serverDB, email)
60
- : undefined;
61
- // If the user is not found by email, try to find by providerAccountId
62
- if (!existingUser && providerAccountId) {
63
- existingUser = await UserModel.findById(serverDB, providerAccountId);
64
- }
65
- if (existingUser) {
66
- const adapterUser = mapLobeUserToAdapterUser(existingUser);
67
- return adapterUser;
68
- }
69
-
70
- // create a new user if it does not exist
71
- // Use id from provider if it exists, otherwise use id assigned by next-auth
72
- // ref: https://github.com/lobehub/lobe-chat/pull/2935
73
- const uid = providerAccountId ?? id;
74
- await UserModel.createUser(
75
- serverDB,
76
- mapAdapterUserToLobeUser({
77
- email,
78
- emailVerified,
79
- // Use providerAccountId as userid to identify if the user exists in a SSO provider
80
- id: uid,
81
- image,
82
- name,
83
- }),
84
- );
85
-
86
- // 3. Create an inbox session for the user
87
- const agentService = new AgentService(serverDB, uid);
88
- await agentService.createInbox();
89
-
90
- return { ...user, id: uid };
83
+ const data = await fetcher('createUser', user);
84
+ return await postProcessor(data);
91
85
  },
92
86
  async createVerificationToken(data): Promise<VerificationToken | null | undefined> {
93
- return serverDB
94
- .insert(nextauthVerificationTokens)
95
- .values(data)
96
- .returning()
97
- .then((res) => res[0]);
87
+ const result = await fetcher('createVerificationToken', data);
88
+ return await postProcessor(result);
98
89
  },
99
90
  async deleteSession(sessionToken): Promise<AdapterSession | null | undefined> {
100
- await serverDB
101
- .delete(nextauthSessions)
102
- .where(eq(nextauthSessions.sessionToken, sessionToken));
91
+ const result = await fetcher('deleteSession', sessionToken);
92
+ await postProcessor(result);
103
93
  return;
104
94
  },
105
95
  async deleteUser(id): Promise<AdapterUser | null | undefined> {
106
- const user = await UserModel.findById(serverDB, id);
107
- if (!user) throw new Error('NextAuth: Delete User not found');
108
-
109
- await UserModel.deleteUser(serverDB, id);
96
+ const result = await fetcher('deleteUser', id);
97
+ await postProcessor(result);
110
98
  return;
111
99
  },
112
100
 
113
101
  async getAccount(providerAccountId, provider): Promise<AdapterAccount | null> {
114
- return serverDB
115
- .select()
116
- .from(nextauthAccounts)
117
- .where(
118
- and(
119
- eq(nextauthAccounts.provider, provider),
120
- eq(nextauthAccounts.providerAccountId, providerAccountId),
121
- ),
122
- )
123
- .then((res) => res[0] ?? null) as Promise<AdapterAccount | null>;
102
+ const data = await fetcher('getAccount', {
103
+ provider,
104
+ providerAccountId,
105
+ });
106
+ return await postProcessor(data);
124
107
  },
125
108
 
126
109
  async getAuthenticator(credentialID): Promise<AdapterAuthenticator | null> {
127
- const result = await serverDB
128
- .select()
129
- .from(nextauthAuthenticators)
130
- .where(eq(nextauthAuthenticators.credentialID, credentialID))
131
- .then((res) => res[0] ?? null);
132
- if (!result) throw new Error('LobeNextAuthDbAdapter: Failed to get authenticator');
133
- return mapAuthenticatorQueryResutlToAdapterAuthenticator(result);
110
+ const result = await fetcher('getAuthenticator', credentialID);
111
+ return await postProcessor(result);
134
112
  },
135
113
 
136
114
  async getSessionAndUser(sessionToken): Promise<{
137
115
  session: AdapterSession;
138
116
  user: AdapterUser;
139
117
  } | null> {
140
- const result = await serverDB
141
- .select({
142
- session: nextauthSessions,
143
- user: users,
144
- })
145
- .from(nextauthSessions)
146
- .where(eq(nextauthSessions.sessionToken, sessionToken))
147
- .innerJoin(users, eq(users.id, nextauthSessions.userId))
148
- .then((res) => (res.length > 0 ? res[0] : null));
149
-
150
- if (!result) return null;
151
- const adapterUser = mapLobeUserToAdapterUser(result.user);
152
- if (!adapterUser) return null;
153
- return {
154
- session: result.session,
155
- user: adapterUser,
156
- };
118
+ const result = await fetcher('getSessionAndUser', sessionToken);
119
+ return await postProcessor(result);
157
120
  },
158
121
 
159
122
  async getUser(id): Promise<AdapterUser | null> {
160
- const lobeUser = await UserModel.findById(serverDB, id);
161
- if (!lobeUser) return null;
162
- return mapLobeUserToAdapterUser(lobeUser);
123
+ log('getUser called with id:', id);
124
+ const result = await fetcher('getUser', id);
125
+ return await postProcessor(result);
163
126
  },
164
127
 
165
128
  async getUserByAccount(account): Promise<AdapterUser | null> {
166
- const result = await serverDB
167
- .select({
168
- account: nextauthAccounts,
169
- users,
170
- })
171
- .from(nextauthAccounts)
172
- .innerJoin(users, eq(nextauthAccounts.userId, users.id))
173
- .where(
174
- and(
175
- eq(nextauthAccounts.provider, account.provider),
176
- eq(nextauthAccounts.providerAccountId, account.providerAccountId),
177
- ),
178
- )
179
- .then((res) => res[0]);
180
-
181
- return result?.users ? mapLobeUserToAdapterUser(result.users) : null;
129
+ const data = await fetcher('getUserByAccount', account);
130
+ return await postProcessor(data);
182
131
  },
183
132
 
184
133
  async getUserByEmail(email): Promise<AdapterUser | null> {
185
- const lobeUser =
186
- email && typeof email === 'string' && email.trim()
187
- ? await UserModel.findByEmail(serverDB, email)
188
- : undefined;
189
- return lobeUser ? mapLobeUserToAdapterUser(lobeUser) : null;
134
+ const data = await fetcher('getUserByEmail', email);
135
+ return await postProcessor(data);
190
136
  },
191
137
 
192
138
  async linkAccount(data): Promise<AdapterAccount | null | undefined> {
193
- const [account] = await serverDB
194
- .insert(nextauthAccounts)
195
- .values(data as any)
196
- .returning();
197
- if (!account) throw new Error('NextAuthAccountModel: Failed to create account');
198
- // TODO Update type annotation
199
- return account as any;
139
+ const result = await fetcher('linkAccount', data);
140
+ return await postProcessor(result);
200
141
  },
201
142
 
202
143
  async listAuthenticatorsByUserId(userId): Promise<AdapterAuthenticator[]> {
203
- const result = await serverDB
204
- .select()
205
- .from(nextauthAuthenticators)
206
- .where(eq(nextauthAuthenticators.userId, userId))
207
- .then((res) => res);
208
- if (result.length === 0)
209
- throw new Error('LobeNextAuthDbAdapter: Failed to get authenticator list');
210
- return result.map((r) => mapAuthenticatorQueryResutlToAdapterAuthenticator(r));
144
+ const result = await fetcher('listAuthenticatorsByUserId', userId);
145
+ return await postProcessor(result);
211
146
  },
212
147
 
213
148
  // @ts-ignore: The return type is {Promise<void> | Awaitable<AdapterAccount | undefined>}
214
149
  async unlinkAccount(account): Promise<void | AdapterAccount | undefined> {
215
- await serverDB
216
- .delete(nextauthAccounts)
217
- .where(
218
- and(
219
- eq(nextauthAccounts.provider, account.provider),
220
- eq(nextauthAccounts.providerAccountId, account.providerAccountId),
221
- ),
222
- );
150
+ const result = await fetcher('unlinkAccount', account);
151
+ await postProcessor(result);
152
+ return;
223
153
  },
224
154
 
225
155
  async updateAuthenticatorCounter(credentialID, counter): Promise<AdapterAuthenticator> {
226
- const result = await serverDB
227
- .update(nextauthAuthenticators)
228
- .set({ counter })
229
- .where(eq(nextauthAuthenticators.credentialID, credentialID))
230
- .returning()
231
- .then((res) => res[0]);
232
- if (!result) throw new Error('LobeNextAuthDbAdapter: Failed to update authenticator counter');
233
- return mapAuthenticatorQueryResutlToAdapterAuthenticator(result);
156
+ const result = await fetcher('updateAuthenticatorCounter', {
157
+ counter,
158
+ credentialID,
159
+ });
160
+ return await postProcessor(result);
234
161
  },
235
162
 
236
163
  async updateSession(data): Promise<AdapterSession | null | undefined> {
237
- const res = await serverDB
238
- .update(nextauthSessions)
239
- .set(data)
240
- .where(eq(nextauthSessions.sessionToken, data.sessionToken))
241
- .returning();
242
- return res[0];
164
+ const result = await fetcher('updateSession', data);
165
+ return await postProcessor(result);
243
166
  },
244
167
 
245
168
  async updateUser(user): Promise<AdapterUser> {
246
- const lobeUser = await UserModel.findById(serverDB, user?.id);
247
- if (!lobeUser) throw new Error('NextAuth: User not found');
248
- const userModel = new UserModel(serverDB, user.id);
249
-
250
- const updatedUser = await userModel.updateUser({
251
- ...partialMapAdapterUserToLobeUser(user),
252
- });
253
- if (!updatedUser) throw new Error('NextAuth: Failed to update user');
254
-
255
- // merge new user data with old user data
256
- const newAdapterUser = mapLobeUserToAdapterUser(lobeUser);
257
- if (!newAdapterUser) {
258
- throw new Error('NextAuth: Failed to map user data to adapter user');
259
- }
260
- return merge(newAdapterUser, user);
169
+ const result = await fetcher('updateUser', user);
170
+ return await postProcessor(result);
261
171
  },
262
172
 
263
173
  async useVerificationToken(identifier_token): Promise<VerificationToken | null> {
264
- return serverDB
265
- .delete(nextauthVerificationTokens)
266
- .where(
267
- and(
268
- eq(nextauthVerificationTokens.identifier, identifier_token.identifier),
269
- eq(nextauthVerificationTokens.token, identifier_token.token),
270
- ),
271
- )
272
- .returning()
273
- .then((res) => (res.length > 0 ? res[0] : null));
174
+ const result = await fetcher('useVerificationToken', identifier_token);
175
+ return await postProcessor(result);
274
176
  },
275
177
  };
276
178
  }
@@ -1,12 +1,24 @@
1
1
  import type { NextAuthConfig } from 'next-auth';
2
2
 
3
- import { authEnv } from '@/config/auth';
3
+ import { getAuthConfig } from '@/config/auth';
4
+ import { getServerDBConfig } from '@/config/db';
4
5
 
6
+ import { LobeNextAuthDbAdapter } from './adapter';
5
7
  import { ssoProviders } from './sso-providers';
6
8
 
9
+ const {
10
+ NEXT_AUTH_DEBUG,
11
+ NEXT_AUTH_SECRET,
12
+ NEXT_AUTH_SSO_SESSION_STRATEGY,
13
+ NEXT_AUTH_SSO_PROVIDERS,
14
+ NEXT_PUBLIC_ENABLE_NEXT_AUTH,
15
+ } = getAuthConfig();
16
+
17
+ const { NEXT_PUBLIC_ENABLED_SERVER_SERVICE } = getServerDBConfig();
18
+
7
19
  export const initSSOProviders = () => {
8
- return authEnv.NEXT_PUBLIC_ENABLE_NEXT_AUTH
9
- ? authEnv.NEXT_AUTH_SSO_PROVIDERS.split(/[,,]/).map((provider) => {
20
+ return NEXT_PUBLIC_ENABLE_NEXT_AUTH
21
+ ? NEXT_AUTH_SSO_PROVIDERS.split(/[,,]/).map((provider) => {
10
22
  const validProvider = ssoProviders.find((item) => item.id === provider.trim());
11
23
 
12
24
  if (validProvider) return validProvider.provider;
@@ -18,6 +30,7 @@ export const initSSOProviders = () => {
18
30
 
19
31
  // Notice this is only an object, not a full Auth.js instance
20
32
  export default {
33
+ adapter: NEXT_PUBLIC_ENABLED_SERVER_SERVICE ? LobeNextAuthDbAdapter() : undefined,
21
34
  callbacks: {
22
35
  // Note: Data processing order of callback: authorize --> jwt --> session
23
36
  async jwt({ token, user }) {
@@ -39,12 +52,16 @@ export default {
39
52
  return session;
40
53
  },
41
54
  },
42
- debug: authEnv.NEXT_AUTH_DEBUG,
55
+ debug: NEXT_AUTH_DEBUG,
43
56
  pages: {
44
57
  error: '/next-auth/error',
45
58
  signIn: '/next-auth/signin',
46
59
  },
47
60
  providers: initSSOProviders(),
48
- secret: authEnv.NEXT_AUTH_SECRET,
61
+ secret: NEXT_AUTH_SECRET,
62
+ session: {
63
+ // Force use JWT if server service is disabled
64
+ strategy: NEXT_PUBLIC_ENABLED_SERVER_SERVICE ? NEXT_AUTH_SSO_SESSION_STRATEGY : 'jwt',
65
+ },
49
66
  trustHost: process.env?.AUTH_TRUST_HOST ? process.env.AUTH_TRUST_HOST === 'true' : true,
50
67
  } satisfies NextAuthConfig;
@@ -1,33 +1,20 @@
1
1
  import NextAuth from 'next-auth';
2
2
 
3
- import { getServerDBConfig } from '@/config/db';
4
- import { serverDB } from '@/database/server';
5
-
6
- import { LobeNextAuthDbAdapter } from './adapter';
7
- import config from './auth.config';
8
-
9
- const { NEXT_PUBLIC_ENABLED_SERVER_SERVICE } = getServerDBConfig();
3
+ import authConfig from './auth.config';
10
4
 
11
5
  /**
12
- * NextAuth initialization with Database adapter
6
+ * NextAuth initialization without Database adapter
7
+ *
8
+ * @note
9
+ * We currently use `jwt` strategy for session management.
10
+ * So you don't need to import `signIn` or `signOut` from
11
+ * this module, just import from `next-auth` directly.
13
12
  *
13
+ * Inside react component
14
14
  * @example
15
15
  * ```ts
16
- * import NextAuthNode from '@/libs/next-auth';
17
- * const { handlers } = NextAuthNode;
16
+ * import { signOut } from 'next-auth/react';
17
+ * signOut();
18
18
  * ```
19
- *
20
- * @note
21
- * If you meet the edge runtime compatible problem,
22
- * you can import from `@/libs/next-auth/edge` which is not initial with the database adapter.
23
- *
24
- * The difference and usage of the two different NextAuth modules is can be
25
- * ref to: https://github.com/lobehub/lobe-chat/pull/2935
26
19
  */
27
- export default NextAuth({
28
- ...config,
29
- adapter: NEXT_PUBLIC_ENABLED_SERVER_SERVICE ? LobeNextAuthDbAdapter(serverDB) : undefined,
30
- session: {
31
- strategy: 'jwt',
32
- },
33
- });
20
+ export default NextAuth(authConfig);
@@ -54,9 +54,9 @@ export const createEdgeContext = async (request: NextRequest): Promise<EdgeConte
54
54
 
55
55
  if (enableNextAuth) {
56
56
  try {
57
- const { default: NextAuthEdge } = await import('@/libs/next-auth/edge');
57
+ const { default: NextAuth } = await import('@/libs/next-auth');
58
58
 
59
- const session = await NextAuthEdge.auth();
59
+ const session = await NextAuth.auth();
60
60
  if (session && session?.user?.id) {
61
61
  auth = session.user;
62
62
  userId = session.user.id;
@@ -161,9 +161,9 @@ export const createLambdaContext = async (request: NextRequest): Promise<LambdaC
161
161
  if (enableNextAuth) {
162
162
  log('Attempting NextAuth authentication');
163
163
  try {
164
- const { default: NextAuthEdge } = await import('@/libs/next-auth/edge');
164
+ const { default: NextAuth } = await import('@/libs/next-auth');
165
165
 
166
- const session = await NextAuthEdge.auth();
166
+ const session = await NextAuth.auth();
167
167
  if (session && session?.user?.id) {
168
168
  auth = session.user;
169
169
  userId = session.user.id;
@@ -156,6 +156,28 @@ export default {
156
156
  searchProviders: '搜索服务商...',
157
157
  sort: '自定义排序',
158
158
  },
159
+ newapi: {
160
+ apiKey: {
161
+ desc: 'New API 平台提供的 API 密钥',
162
+ placeholder: 'New API API 密钥',
163
+ required: 'API 密钥是必需的',
164
+ title: 'API 密钥',
165
+ },
166
+ apiUrl: {
167
+ desc: 'New API 服务的 API 地址,大部分时候需要带 /v1',
168
+ title: 'API 地址',
169
+ },
170
+ enabled: {
171
+ title: '启用 New API',
172
+ },
173
+ models: {
174
+ batchSelect: '批量选择模型 ({{count}} 个)',
175
+ fetch: '获取模型列表',
176
+ selected: '已选择的模型',
177
+ title: '可用模型',
178
+ },
179
+ title: 'New API',
180
+ },
159
181
  ollama: {
160
182
  checker: {
161
183
  desc: '测试代理地址是否正确填写',
@@ -188,6 +210,10 @@ export default {
188
210
  },
189
211
  },
190
212
  providerModels: {
213
+ batchSelect: {
214
+ selected: '已选择 {{count}} 个模型',
215
+ title: '批量选择',
216
+ },
191
217
  config: {
192
218
  aesGcm: '您的秘钥与代理地址等将使用 <1>AES-GCM</1> 加密算法进行加密',
193
219
  apiKey: {
package/src/middleware.ts CHANGED
@@ -10,7 +10,7 @@ import { OAUTH_AUTHORIZED } from '@/const/auth';
10
10
  import { LOBE_LOCALE_COOKIE } from '@/const/locale';
11
11
  import { LOBE_THEME_APPEARANCE } from '@/const/theme';
12
12
  import { appEnv } from '@/envs/app';
13
- import NextAuthEdge from '@/libs/next-auth/edge';
13
+ import NextAuth from '@/libs/next-auth';
14
14
  import { Locales } from '@/locales/resources';
15
15
 
16
16
  import { oidcEnv } from './envs/oidc';
@@ -170,7 +170,7 @@ const isProtectedRoute = createRouteMatcher([
170
170
  ]);
171
171
 
172
172
  // Initialize an Edge compatible NextAuth middleware
173
- const nextAuthMiddleware = NextAuthEdge.auth(async (req) => {
173
+ const nextAuthMiddleware = NextAuth.auth((req) => {
174
174
  logNextAuth('NextAuth middleware processing request: %s %s', req.method, req.url);
175
175
 
176
176
  const response = defaultMiddleware(req);
@@ -6,8 +6,8 @@ import { MessageModel } from '@/database/models/message';
6
6
  import { SessionModel } from '@/database/models/session';
7
7
  import { UserModel, UserNotFoundError } from '@/database/models/user';
8
8
  import { serverDB } from '@/database/server';
9
- import { LobeNextAuthDbAdapter } from '@/libs/next-auth/adapter';
10
9
  import { KeyVaultsGateKeeper } from '@/server/modules/KeyVaultsEncrypt';
10
+ import { NextAuthUserService } from '@/server/services/nextAuthUser';
11
11
  import { UserService } from '@/server/services/user';
12
12
 
13
13
  import { userRouter } from './user';
@@ -24,10 +24,10 @@ vi.mock('@/database/server', () => ({
24
24
  vi.mock('@/database/models/message');
25
25
  vi.mock('@/database/models/session');
26
26
  vi.mock('@/database/models/user');
27
- vi.mock('@/libs/next-auth/adapter');
28
27
  vi.mock('@/server/modules/KeyVaultsEncrypt');
29
28
  vi.mock('@/server/modules/S3');
30
29
  vi.mock('@/server/services/user');
30
+ vi.mock('@/server/services/nextAuthUser');
31
31
  vi.mock('@/const/auth', () => ({
32
32
  enableClerk: true,
33
33
  }));
@@ -221,7 +221,7 @@ describe('userRouter', () => {
221
221
  type: 'oauth',
222
222
  };
223
223
 
224
- vi.mocked(LobeNextAuthDbAdapter).mockReturnValue({
224
+ vi.mocked(NextAuthUserService).mockReturnValue({
225
225
  getAccount: vi.fn().mockResolvedValue(mockAccount),
226
226
  unlinkAccount: vi.fn().mockResolvedValue(undefined),
227
227
  } as any);
@@ -237,7 +237,7 @@ describe('userRouter', () => {
237
237
  providerAccountId: '123',
238
238
  };
239
239
 
240
- vi.mocked(LobeNextAuthDbAdapter).mockReturnValue({
240
+ vi.mocked(NextAuthUserService).mockReturnValue({
241
241
  getAccount: vi.fn().mockResolvedValue(null),
242
242
  unlinkAccount: vi.fn(),
243
243
  } as any);
@@ -246,19 +246,6 @@ describe('userRouter', () => {
246
246
  userRouter.createCaller({ ...mockCtx }).unlinkSSOProvider(mockInput),
247
247
  ).rejects.toThrow('The account does not exist');
248
248
  });
249
-
250
- it('should throw error if adapter methods are not implemented', async () => {
251
- const mockInput = {
252
- provider: 'google',
253
- providerAccountId: '123',
254
- };
255
-
256
- vi.mocked(LobeNextAuthDbAdapter).mockReturnValue({} as any);
257
-
258
- await expect(
259
- userRouter.createCaller({ ...mockCtx }).unlinkSSOProvider(mockInput),
260
- ).rejects.toThrow('The method in LobeNextAuthDbAdapter `unlinkAccount` is not implemented');
261
- });
262
249
  });
263
250
 
264
251
  describe('updateSettings', () => {
@@ -9,12 +9,12 @@ import { SessionModel } from '@/database/models/session';
9
9
  import { UserModel, UserNotFoundError } from '@/database/models/user';
10
10
  import { ClerkAuth } from '@/libs/clerk-auth';
11
11
  import { pino } from '@/libs/logger';
12
- import { LobeNextAuthDbAdapter } from '@/libs/next-auth/adapter';
13
12
  import { authedProcedure, router } from '@/libs/trpc/lambda';
14
13
  import { serverDatabase } from '@/libs/trpc/lambda/middleware';
15
14
  import { KeyVaultsGateKeeper } from '@/server/modules/KeyVaultsEncrypt';
16
15
  import { S3 } from '@/server/modules/S3';
17
16
  import { FileService } from '@/server/services/file';
17
+ import { NextAuthUserService } from '@/server/services/nextAuthUser';
18
18
  import { UserService } from '@/server/services/user';
19
19
  import {
20
20
  NextAuthAccountSchame,
@@ -29,7 +29,7 @@ const userProcedure = authedProcedure.use(serverDatabase).use(async ({ ctx, next
29
29
  ctx: {
30
30
  clerkAuth: new ClerkAuth(),
31
31
  fileService: new FileService(ctx.serverDB, ctx.userId),
32
- nextAuthDbAdapter: LobeNextAuthDbAdapter(ctx.serverDB),
32
+ nextAuthUserService: new NextAuthUserService(ctx.serverDB),
33
33
  userModel: new UserModel(ctx.serverDB, ctx.userId),
34
34
  },
35
35
  });
@@ -134,19 +134,10 @@ export const userRouter = router({
134
134
 
135
135
  unlinkSSOProvider: userProcedure.input(NextAuthAccountSchame).mutation(async ({ ctx, input }) => {
136
136
  const { provider, providerAccountId } = input;
137
- if (
138
- ctx.nextAuthDbAdapter?.unlinkAccount &&
139
- typeof ctx.nextAuthDbAdapter.unlinkAccount === 'function' &&
140
- ctx.nextAuthDbAdapter?.getAccount &&
141
- typeof ctx.nextAuthDbAdapter.getAccount === 'function'
142
- ) {
143
- const account = await ctx.nextAuthDbAdapter.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.nextAuthDbAdapter.unlinkAccount({ provider, providerAccountId });
147
- } else {
148
- throw new Error('The method in LobeNextAuthDbAdapter `unlinkAccount` is not implemented');
149
- }
137
+ const account = await ctx.nextAuthUserService.getAccount(providerAccountId, provider);
138
+ // The userId can either get from ctx.nextAuth?.id or ctx.userId
139
+ if (!account || account.userId !== ctx.userId) throw new Error('The account does not exist');
140
+ await ctx.nextAuthUserService.unlinkAccount({ provider, providerAccountId });
150
141
  }),
151
142
 
152
143
  // 服务端上传头像