@lobehub/lobehub 2.0.0-next.16 → 2.0.0-next.18
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/CHANGELOG.md +58 -0
- package/README.md +2 -45
- package/README.zh-CN.md +2 -45
- package/changelog/v1.json +18 -0
- package/e2e/src/features/discover/smoke.feature +34 -1
- package/e2e/src/steps/discover/smoke.steps.ts +116 -4
- package/locales/ar/oauth.json +1 -0
- package/locales/bg-BG/oauth.json +1 -0
- package/locales/de-DE/oauth.json +1 -0
- package/locales/en-US/oauth.json +1 -0
- package/locales/es-ES/oauth.json +1 -0
- package/locales/fa-IR/oauth.json +1 -0
- package/locales/fr-FR/oauth.json +1 -0
- package/locales/it-IT/oauth.json +1 -0
- package/locales/ja-JP/oauth.json +1 -0
- package/locales/ko-KR/oauth.json +1 -0
- package/locales/nl-NL/oauth.json +1 -0
- package/locales/pl-PL/oauth.json +1 -0
- package/locales/pt-BR/oauth.json +1 -0
- package/locales/ru-RU/oauth.json +1 -0
- package/locales/tr-TR/oauth.json +1 -0
- package/locales/vi-VN/oauth.json +1 -0
- package/locales/zh-CN/oauth.json +1 -0
- package/locales/zh-TW/oauth.json +1 -0
- package/package.json +1 -1
- package/packages/model-runtime/src/utils/googleErrorParser.test.ts +125 -0
- package/packages/model-runtime/src/utils/googleErrorParser.ts +103 -77
- package/packages/types/src/message/ui/params.ts +98 -4
- package/packages/types/src/user/index.ts +13 -1
- package/packages/types/src/user/settings/index.ts +22 -0
- package/src/app/[variants]/(main)/discover/(list)/features/Pagination.tsx +1 -0
- package/src/app/[variants]/(main)/discover/(list)/features/SortButton/index.tsx +1 -1
- package/src/app/[variants]/(main)/discover/(list)/mcp/features/List/Item.tsx +1 -0
- package/src/app/[variants]/(main)/discover/(list)/model/features/List/Item.tsx +1 -0
- package/src/app/[variants]/(main)/discover/(list)/provider/features/List/Item.tsx +1 -0
- package/src/app/[variants]/(main)/discover/components/CategoryMenu.tsx +9 -1
- package/src/app/[variants]/oauth/consent/[uid]/Consent/BuiltinConsent.tsx +57 -0
- package/src/app/[variants]/oauth/consent/[uid]/{Consent.tsx → Consent/index.tsx} +9 -1
- package/src/app/[variants]/oauth/consent/[uid]/Login.tsx +9 -1
- package/src/locales/default/oauth.ts +1 -0
- package/src/server/routers/lambda/__tests__/integration/message.integration.test.ts +0 -41
- package/src/server/routers/lambda/message.ts +4 -11
- package/src/server/routers/lambda/user.ts +24 -25
- package/src/services/message/server.ts +0 -4
- package/src/services/message/type.ts +0 -2
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Icon } from '@lobehub/ui';
|
|
4
|
+
import { Card, Result } from 'antd';
|
|
5
|
+
import { useTheme } from 'antd-style';
|
|
6
|
+
import { LoaderCircle } from 'lucide-react';
|
|
7
|
+
import { memo, useEffect, useRef } from 'react';
|
|
8
|
+
import { useTranslation } from 'react-i18next';
|
|
9
|
+
import { Center } from 'react-layout-kit';
|
|
10
|
+
|
|
11
|
+
interface BuiltinConsentProps {
|
|
12
|
+
uid: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const BuiltinConsent = memo<BuiltinConsentProps>(({ uid }) => {
|
|
16
|
+
const { t } = useTranslation('oauth');
|
|
17
|
+
const formRef = useRef<HTMLFormElement>(null);
|
|
18
|
+
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
// Auto-submit on mount
|
|
21
|
+
formRef.current?.submit();
|
|
22
|
+
}, []);
|
|
23
|
+
|
|
24
|
+
const theme = useTheme();
|
|
25
|
+
return (
|
|
26
|
+
<>
|
|
27
|
+
<Center height="100vh">
|
|
28
|
+
<Card
|
|
29
|
+
style={{
|
|
30
|
+
alignItems: 'center',
|
|
31
|
+
display: 'flex',
|
|
32
|
+
justifyContent: 'center',
|
|
33
|
+
minHeight: 280,
|
|
34
|
+
minWidth: 500,
|
|
35
|
+
width: '100%',
|
|
36
|
+
}}
|
|
37
|
+
>
|
|
38
|
+
<Result
|
|
39
|
+
icon={<Icon icon={LoaderCircle} spin style={{ color: theme.colorText }} />}
|
|
40
|
+
status="success"
|
|
41
|
+
style={{ padding: 0 }}
|
|
42
|
+
subTitle={t('consent.redirecting')}
|
|
43
|
+
title=""
|
|
44
|
+
/>
|
|
45
|
+
</Card>
|
|
46
|
+
</Center>
|
|
47
|
+
<form action="/oidc/consent" method="post" ref={formRef} style={{ display: 'none' }}>
|
|
48
|
+
<input name="uid" type="hidden" value={uid} />
|
|
49
|
+
<input name="consent" type="hidden" value="accept" />
|
|
50
|
+
</form>
|
|
51
|
+
</>
|
|
52
|
+
);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
BuiltinConsent.displayName = 'BuiltinConsent';
|
|
56
|
+
|
|
57
|
+
export default BuiltinConsent;
|
|
@@ -7,7 +7,8 @@ import { memo, useState } from 'react';
|
|
|
7
7
|
import { useTranslation } from 'react-i18next';
|
|
8
8
|
import { Center, Flexbox } from 'react-layout-kit';
|
|
9
9
|
|
|
10
|
-
import OAuthApplicationLogo from '
|
|
10
|
+
import OAuthApplicationLogo from '../components/OAuthApplicationLogo';
|
|
11
|
+
import BuiltinConsent from './BuiltinConsent';
|
|
11
12
|
|
|
12
13
|
interface ClientProps {
|
|
13
14
|
clientId: string;
|
|
@@ -117,6 +118,8 @@ function getScopeDescription(scope: string, t: any): string {
|
|
|
117
118
|
return t(`consent.scope.${scope.replace(':', '-')}`, scope);
|
|
118
119
|
}
|
|
119
120
|
|
|
121
|
+
const BUILTIN_CLIENTS = new Set(['lobehub-desktop', 'lobehub-mobile', 'lobehub-market']);
|
|
122
|
+
|
|
120
123
|
const ConsentClient = memo<ClientProps>(({ uid, clientId, scopes, clientMetadata }) => {
|
|
121
124
|
const { styles } = useStyles();
|
|
122
125
|
const { t } = useTranslation('oauth');
|
|
@@ -124,6 +127,11 @@ const ConsentClient = memo<ClientProps>(({ uid, clientId, scopes, clientMetadata
|
|
|
124
127
|
const [isLoading, setIsLoading] = useState(false);
|
|
125
128
|
|
|
126
129
|
const clientDisplayName = clientMetadata?.clientName || clientId;
|
|
130
|
+
|
|
131
|
+
if (BUILTIN_CLIENTS.has(clientId)) {
|
|
132
|
+
return <BuiltinConsent uid={clientId} />;
|
|
133
|
+
}
|
|
134
|
+
|
|
127
135
|
return (
|
|
128
136
|
<Center className={styles.container} gap={16}>
|
|
129
137
|
<Flexbox gap={40}>
|
|
@@ -59,6 +59,8 @@ const LoginConfirmClient = memo<LoginConfirmProps>(({ uid, clientMetadata }) =>
|
|
|
59
59
|
const avatar = useUserStore(userProfileSelectors.userAvatar);
|
|
60
60
|
const nickName = useUserStore(userProfileSelectors.nickName);
|
|
61
61
|
|
|
62
|
+
const [isLoading, setIsLoading] = React.useState(false);
|
|
63
|
+
|
|
62
64
|
const titleText = t('login.title', { clientName: clientDisplayName });
|
|
63
65
|
const descriptionText = t('login.description', { clientName: clientDisplayName });
|
|
64
66
|
const buttonText = t('login.button'); // Or "Continue"
|
|
@@ -99,7 +101,12 @@ const LoginConfirmClient = memo<LoginConfirmProps>(({ uid, clientMetadata }) =>
|
|
|
99
101
|
|
|
100
102
|
<Flexbox gap={16}>
|
|
101
103
|
{/* Form points to the endpoint handling login confirmation */}
|
|
102
|
-
<form
|
|
104
|
+
<form
|
|
105
|
+
action="/oidc/consent"
|
|
106
|
+
method="post"
|
|
107
|
+
onSubmit={() => setIsLoading(true)}
|
|
108
|
+
style={{ width: '100%' }}
|
|
109
|
+
>
|
|
103
110
|
{/* Adjust action URL */}
|
|
104
111
|
<input name="uid" type="hidden" value={uid} />
|
|
105
112
|
<input name="choice" type="hidden" value={'accept'} />
|
|
@@ -108,6 +115,7 @@ const LoginConfirmClient = memo<LoginConfirmProps>(({ uid, clientMetadata }) =>
|
|
|
108
115
|
className={styles.authButton}
|
|
109
116
|
disabled={!isUserStateInit}
|
|
110
117
|
htmlType="submit"
|
|
118
|
+
loading={isLoading}
|
|
111
119
|
name="consent"
|
|
112
120
|
size="large"
|
|
113
121
|
type="primary"
|
|
@@ -318,47 +318,6 @@ describe('Message Router Integration Tests', () => {
|
|
|
318
318
|
});
|
|
319
319
|
});
|
|
320
320
|
|
|
321
|
-
describe('batchCreateMessages', () => {
|
|
322
|
-
it('should create multiple messages in batch', async () => {
|
|
323
|
-
const caller = messageRouter.createCaller(createTestContext(userId));
|
|
324
|
-
|
|
325
|
-
const messagesToCreate = [
|
|
326
|
-
{
|
|
327
|
-
content: 'Batch message 1',
|
|
328
|
-
role: 'user' as const,
|
|
329
|
-
sessionId: testSessionId,
|
|
330
|
-
},
|
|
331
|
-
{
|
|
332
|
-
content: 'Batch message 2',
|
|
333
|
-
role: 'assistant' as const,
|
|
334
|
-
sessionId: testSessionId,
|
|
335
|
-
},
|
|
336
|
-
{
|
|
337
|
-
content: 'Batch message 3',
|
|
338
|
-
role: 'user' as const,
|
|
339
|
-
sessionId: testSessionId,
|
|
340
|
-
topicId: testTopicId,
|
|
341
|
-
},
|
|
342
|
-
];
|
|
343
|
-
|
|
344
|
-
const result = await caller.batchCreateMessages(messagesToCreate);
|
|
345
|
-
|
|
346
|
-
expect(result.success).toBe(true);
|
|
347
|
-
// Note: rowCount might be undefined in PGlite, so we skip this check
|
|
348
|
-
// expect(result.added).toBe(3);
|
|
349
|
-
|
|
350
|
-
// 验证数据库中的消息
|
|
351
|
-
const dbMessages = await serverDB
|
|
352
|
-
.select()
|
|
353
|
-
.from(messages)
|
|
354
|
-
.where(eq(messages.sessionId, testSessionId));
|
|
355
|
-
|
|
356
|
-
expect(dbMessages.length).toBeGreaterThanOrEqual(3);
|
|
357
|
-
const topicMessage = dbMessages.find((m) => m.content === 'Batch message 3');
|
|
358
|
-
expect(topicMessage?.topicId).toBe(testTopicId);
|
|
359
|
-
});
|
|
360
|
-
});
|
|
361
|
-
|
|
362
321
|
describe('removeMessages', () => {
|
|
363
322
|
it('should remove multiple messages', async () => {
|
|
364
323
|
const caller = messageRouter.createCaller(createTestContext(userId));
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
2
|
+
CreateMessageParamsSchema,
|
|
3
|
+
CreateNewMessageParamsSchema,
|
|
3
4
|
UIChatMessage,
|
|
4
5
|
UpdateMessageParamsSchema,
|
|
5
6
|
UpdateMessageRAGParamsSchema,
|
|
@@ -27,14 +28,6 @@ const messageProcedure = authedProcedure.use(serverDatabase).use(async (opts) =>
|
|
|
27
28
|
});
|
|
28
29
|
|
|
29
30
|
export const messageRouter = router({
|
|
30
|
-
batchCreateMessages: messageProcedure
|
|
31
|
-
.input(z.array(z.any()))
|
|
32
|
-
.mutation(async ({ input, ctx }): Promise<BatchTaskResult> => {
|
|
33
|
-
const data = await ctx.messageModel.batchCreate(input);
|
|
34
|
-
|
|
35
|
-
return { added: data.rowCount as number, ids: [], skips: [], success: true };
|
|
36
|
-
}),
|
|
37
|
-
|
|
38
31
|
count: messageProcedure
|
|
39
32
|
.input(
|
|
40
33
|
z
|
|
@@ -64,7 +57,7 @@ export const messageRouter = router({
|
|
|
64
57
|
}),
|
|
65
58
|
|
|
66
59
|
createMessage: messageProcedure
|
|
67
|
-
.input(
|
|
60
|
+
.input(CreateMessageParamsSchema)
|
|
68
61
|
.mutation(async ({ input, ctx }) => {
|
|
69
62
|
const data = await ctx.messageModel.create(input as any);
|
|
70
63
|
|
|
@@ -72,7 +65,7 @@ export const messageRouter = router({
|
|
|
72
65
|
}),
|
|
73
66
|
|
|
74
67
|
createNewMessage: messageProcedure
|
|
75
|
-
.input(
|
|
68
|
+
.input(CreateNewMessageParamsSchema)
|
|
76
69
|
.mutation(async ({ input, ctx }) => {
|
|
77
70
|
return ctx.messageModel.createNewMessage(input as any, {
|
|
78
71
|
postProcessUrl: (path) => ctx.fileService.getFullFileUrl(path),
|
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
import { UserJSON } from '@clerk/backend';
|
|
2
|
+
import { enableClerk, isDesktop } from '@lobechat/const';
|
|
3
|
+
import {
|
|
4
|
+
NextAuthAccountSchame,
|
|
5
|
+
UserGuideSchema,
|
|
6
|
+
UserInitializationState,
|
|
7
|
+
UserPreference,
|
|
8
|
+
UserPreferenceSchema,
|
|
9
|
+
UserSettings,
|
|
10
|
+
UserSettingsSchema,
|
|
11
|
+
} from '@lobechat/types';
|
|
2
12
|
import { v4 as uuidv4 } from 'uuid';
|
|
3
13
|
import { z } from 'zod';
|
|
4
14
|
|
|
5
|
-
import { enableClerk } from '@/const/auth';
|
|
6
|
-
import { isDesktop } from '@/const/version';
|
|
7
15
|
import { MessageModel } from '@/database/models/message';
|
|
8
16
|
import { SessionModel } from '@/database/models/session';
|
|
9
17
|
import { UserModel, UserNotFoundError } from '@/database/models/user';
|
|
@@ -16,13 +24,6 @@ import { S3 } from '@/server/modules/S3';
|
|
|
16
24
|
import { FileService } from '@/server/services/file';
|
|
17
25
|
import { NextAuthUserService } from '@/server/services/nextAuthUser';
|
|
18
26
|
import { UserService } from '@/server/services/user';
|
|
19
|
-
import {
|
|
20
|
-
NextAuthAccountSchame,
|
|
21
|
-
UserGuideSchema,
|
|
22
|
-
UserInitializationState,
|
|
23
|
-
UserPreference,
|
|
24
|
-
} from '@/types/user';
|
|
25
|
-
import { UserSettings } from '@/types/user/settings';
|
|
26
27
|
|
|
27
28
|
const userProcedure = authedProcedure.use(serverDatabase).use(async ({ ctx, next }) => {
|
|
28
29
|
return next({
|
|
@@ -199,30 +200,28 @@ export const userRouter = router({
|
|
|
199
200
|
return ctx.userModel.updateGuide(input);
|
|
200
201
|
}),
|
|
201
202
|
|
|
202
|
-
updatePreference: userProcedure.input(
|
|
203
|
+
updatePreference: userProcedure.input(UserPreferenceSchema).mutation(async ({ ctx, input }) => {
|
|
203
204
|
return ctx.userModel.updatePreference(input);
|
|
204
205
|
}),
|
|
205
206
|
|
|
206
|
-
updateSettings: userProcedure
|
|
207
|
-
|
|
208
|
-
.mutation(async ({ ctx, input }) => {
|
|
209
|
-
const { keyVaults, ...res } = input as Partial<UserSettings>;
|
|
207
|
+
updateSettings: userProcedure.input(UserSettingsSchema).mutation(async ({ ctx, input }) => {
|
|
208
|
+
const { keyVaults, ...res } = input as Partial<UserSettings>;
|
|
210
209
|
|
|
211
|
-
|
|
212
|
-
|
|
210
|
+
// Encrypt keyVaults
|
|
211
|
+
let encryptedKeyVaults: string | null = null;
|
|
213
212
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
213
|
+
if (keyVaults) {
|
|
214
|
+
// TODO: better to add a validation
|
|
215
|
+
const data = JSON.stringify(keyVaults);
|
|
216
|
+
const gateKeeper = await KeyVaultsGateKeeper.initWithEnvKey();
|
|
218
217
|
|
|
219
|
-
|
|
220
|
-
|
|
218
|
+
encryptedKeyVaults = await gateKeeper.encrypt(data);
|
|
219
|
+
}
|
|
221
220
|
|
|
222
|
-
|
|
221
|
+
const nextValue = { ...res, keyVaults: encryptedKeyVaults };
|
|
223
222
|
|
|
224
|
-
|
|
225
|
-
|
|
223
|
+
return ctx.userModel.updateSetting(nextValue);
|
|
224
|
+
}),
|
|
226
225
|
});
|
|
227
226
|
|
|
228
227
|
export type UserRouter = typeof userRouter;
|
|
@@ -21,10 +21,6 @@ export class ServerService implements IMessageService {
|
|
|
21
21
|
});
|
|
22
22
|
};
|
|
23
23
|
|
|
24
|
-
batchCreateMessages: IMessageService['batchCreateMessages'] = async (messages) => {
|
|
25
|
-
return lambdaClient.message.batchCreateMessages.mutate(messages);
|
|
26
|
-
};
|
|
27
|
-
|
|
28
24
|
getMessages: IMessageService['getMessages'] = async (sessionId, topicId, groupId) => {
|
|
29
25
|
const data = await lambdaClient.message.getMessages.query({
|
|
30
26
|
groupId,
|
|
@@ -5,7 +5,6 @@ import {
|
|
|
5
5
|
ChatTranslate,
|
|
6
6
|
CreateMessageParams,
|
|
7
7
|
CreateMessageResult,
|
|
8
|
-
DBMessageItem,
|
|
9
8
|
ModelRankItem,
|
|
10
9
|
UIChatMessage,
|
|
11
10
|
UpdateMessageParams,
|
|
@@ -19,7 +18,6 @@ import type { HeatmapsProps } from '@lobehub/charts';
|
|
|
19
18
|
export interface IMessageService {
|
|
20
19
|
createMessage(data: CreateMessageParams): Promise<string>;
|
|
21
20
|
createNewMessage(data: CreateMessageParams): Promise<CreateMessageResult>;
|
|
22
|
-
batchCreateMessages(messages: DBMessageItem[]): Promise<any>;
|
|
23
21
|
|
|
24
22
|
getMessages(sessionId: string, topicId?: string, groupId?: string): Promise<UIChatMessage[]>;
|
|
25
23
|
getGroupMessages(groupId: string, topicId?: string): Promise<UIChatMessage[]>;
|