@lobehub/chat 1.74.1 → 1.74.3
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 +59 -0
- package/README.md +2 -2
- package/README.zh-CN.md +2 -2
- package/changelog/v1.json +18 -0
- package/docs/developer/database-schema.dbml +54 -2
- package/docs/self-hosting/environment-variables/model-provider.mdx +9 -7
- package/docs/self-hosting/environment-variables/model-provider.zh-CN.mdx +9 -7
- package/locales/ar/common.json +51 -0
- package/locales/ar/models.json +69 -3
- package/locales/ar/providers.json +6 -0
- package/locales/bg-BG/common.json +51 -0
- package/locales/bg-BG/models.json +69 -3
- package/locales/bg-BG/providers.json +6 -0
- package/locales/de-DE/common.json +51 -0
- package/locales/de-DE/models.json +69 -3
- package/locales/de-DE/providers.json +6 -0
- package/locales/en-US/common.json +51 -0
- package/locales/en-US/models.json +69 -3
- package/locales/en-US/providers.json +6 -3
- package/locales/es-ES/common.json +51 -0
- package/locales/es-ES/models.json +69 -3
- package/locales/es-ES/providers.json +6 -0
- package/locales/fa-IR/common.json +51 -0
- package/locales/fa-IR/models.json +69 -3
- package/locales/fa-IR/providers.json +6 -0
- package/locales/fr-FR/common.json +51 -0
- package/locales/fr-FR/models.json +69 -3
- package/locales/fr-FR/providers.json +6 -0
- package/locales/it-IT/common.json +51 -0
- package/locales/it-IT/models.json +69 -3
- package/locales/it-IT/providers.json +6 -0
- package/locales/ja-JP/common.json +51 -0
- package/locales/ja-JP/models.json +78 -4
- package/locales/ja-JP/providers.json +6 -0
- package/locales/ko-KR/common.json +51 -0
- package/locales/ko-KR/models.json +69 -3
- package/locales/ko-KR/providers.json +6 -0
- package/locales/nl-NL/common.json +51 -0
- package/locales/nl-NL/models.json +69 -3
- package/locales/nl-NL/providers.json +6 -0
- package/locales/pl-PL/common.json +51 -0
- package/locales/pl-PL/models.json +69 -3
- package/locales/pl-PL/providers.json +6 -0
- package/locales/pt-BR/common.json +51 -0
- package/locales/pt-BR/models.json +69 -3
- package/locales/pt-BR/providers.json +6 -0
- package/locales/ru-RU/common.json +51 -0
- package/locales/ru-RU/models.json +69 -3
- package/locales/ru-RU/providers.json +6 -0
- package/locales/tr-TR/common.json +51 -0
- package/locales/tr-TR/models.json +69 -3
- package/locales/tr-TR/providers.json +6 -0
- package/locales/vi-VN/common.json +51 -0
- package/locales/vi-VN/models.json +69 -3
- package/locales/vi-VN/providers.json +6 -0
- package/locales/zh-CN/common.json +53 -2
- package/locales/zh-CN/models.json +79 -13
- package/locales/zh-CN/providers.json +6 -4
- package/locales/zh-TW/common.json +51 -0
- package/locales/zh-TW/models.json +81 -4
- package/locales/zh-TW/providers.json +6 -0
- package/package.json +1 -1
- package/packages/web-crawler/src/utils/__tests__/withTimeout.test.ts +0 -1
- package/src/app/[variants]/(main)/settings/provider/features/ProviderConfig/Checker.tsx +9 -1
- package/src/config/aiModels/qwen.ts +4 -4
- package/src/config/aiModels/volcengine.ts +2 -2
- package/src/database/client/db.ts +102 -11
- package/src/database/client/migrations.json +38 -8
- package/src/database/migrations/0018_add_client_id_for_entities.sql +32 -0
- package/src/database/migrations/meta/0018_snapshot.json +4212 -0
- package/src/database/migrations/meta/_journal.json +7 -0
- package/src/database/models/drizzleMigration.ts +23 -0
- package/src/database/schemas/agent.ts +48 -31
- package/src/database/schemas/file.ts +32 -16
- package/src/database/schemas/message.ts +91 -54
- package/src/database/schemas/rag.ts +65 -32
- package/src/database/schemas/session.ts +6 -3
- package/src/database/schemas/topic.ts +31 -24
- package/src/features/InitClientDB/ErrorResult.tsx +53 -32
- package/src/features/InitClientDB/features/DatabaseRepair/Backup.tsx +77 -0
- package/src/features/InitClientDB/features/DatabaseRepair/Diagnosis.tsx +98 -0
- package/src/features/InitClientDB/features/DatabaseRepair/Repair.tsx +220 -0
- package/src/features/InitClientDB/features/DatabaseRepair/index.tsx +85 -0
- package/src/features/ModelSwitchPanel/index.tsx +13 -7
- package/src/locales/default/common.ts +53 -1
- package/src/store/global/actions/clientDb.ts +19 -3
- package/src/store/global/initialState.ts +6 -1
- package/src/store/global/selectors/clientDB.ts +43 -0
- package/src/store/global/selectors/index.ts +1 -0
- package/src/store/user/slices/settings/selectors/general.test.ts +90 -0
- package/src/types/clientDB.ts +13 -0
@@ -1,18 +1,21 @@
|
|
1
|
-
import { Highlighter, Icon, Modal } from '@lobehub/ui';
|
2
|
-
import { Button } from 'antd';
|
1
|
+
import { Alert, Highlighter, Icon, Modal } from '@lobehub/ui';
|
2
|
+
import { Button, Spin } from 'antd';
|
3
3
|
import { createStyles } from 'antd-style';
|
4
4
|
import isEqual from 'fast-deep-equal';
|
5
|
-
import { TriangleAlert } from 'lucide-react';
|
6
|
-
import
|
5
|
+
import { PenToolIcon, TriangleAlert } from 'lucide-react';
|
6
|
+
import dynamic from 'next/dynamic';
|
7
7
|
import { ReactNode, memo, useState } from 'react';
|
8
|
-
import {
|
8
|
+
import { useTranslation } from 'react-i18next';
|
9
9
|
import { Center, Flexbox } from 'react-layout-kit';
|
10
|
-
import Balancer from 'react-wrap-balancer';
|
11
10
|
|
12
|
-
import { GITHUB_ISSUES } from '@/const/url';
|
13
11
|
import { githubService } from '@/services/github';
|
14
12
|
import { useGlobalStore } from '@/store/global';
|
15
13
|
|
14
|
+
const DatabaseRepair = dynamic(() => import('./features/DatabaseRepair'), {
|
15
|
+
loading: () => <Spin fullscreen />,
|
16
|
+
ssr: false,
|
17
|
+
});
|
18
|
+
|
16
19
|
const useStyles = createStyles(({ css, token }) => ({
|
17
20
|
bg: css`
|
18
21
|
cursor: pointer;
|
@@ -33,7 +36,6 @@ const useStyles = createStyles(({ css, token }) => ({
|
|
33
36
|
transform: scale(1);
|
34
37
|
}
|
35
38
|
`,
|
36
|
-
|
37
39
|
text: css`
|
38
40
|
font-size: 15px;
|
39
41
|
color: ${token.colorText};
|
@@ -56,6 +58,7 @@ const ErrorResult = memo<FailedModalProps>(({ children }) => {
|
|
56
58
|
const initializeClientDB = useGlobalStore((s) => s.initializeClientDB);
|
57
59
|
const error = useGlobalStore((s) => s.initClientDBError, isEqual);
|
58
60
|
const [open, setOpen] = useState(false);
|
61
|
+
const [showRepair, setShowRepair] = useState(true);
|
59
62
|
|
60
63
|
return (
|
61
64
|
<>
|
@@ -88,35 +91,53 @@ const ErrorResult = memo<FailedModalProps>(({ children }) => {
|
|
88
91
|
open={open}
|
89
92
|
title={t('clientDB.error.title')}
|
90
93
|
>
|
91
|
-
|
92
|
-
<
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
94
|
+
{showRepair ? (
|
95
|
+
<DatabaseRepair setShowRepair={setShowRepair} />
|
96
|
+
) : (
|
97
|
+
<Center gap={24}>
|
98
|
+
<div>{t('clientDB.error.desc')}</div>
|
99
|
+
<Flexbox width={'100%'}>
|
100
|
+
<Alert
|
101
|
+
description={`[${error?.name}] ${error?.message}`}
|
102
|
+
extra={
|
103
|
+
<Flexbox gap={8} style={{ marginTop: 8, overflow: 'scroll' }} width={'100%'}>
|
104
|
+
<Highlighter copyButtonSize={'small'} language={'json'}>
|
105
|
+
{JSON.stringify(error, null, 2)}
|
106
|
+
</Highlighter>
|
107
|
+
</Flexbox>
|
108
|
+
}
|
109
|
+
message={t('clientDB.error.detailTitle')}
|
110
|
+
type={'error'}
|
111
|
+
/>
|
112
|
+
</Flexbox>
|
113
|
+
|
114
|
+
<Flexbox horizontal justify={'space-between'}>
|
115
|
+
<Button
|
98
116
|
onClick={(e) => {
|
99
117
|
e.preventDefault();
|
100
118
|
githubService.submitPgliteInitError(error);
|
101
119
|
}}
|
102
|
-
target=
|
120
|
+
target={'_blank'}
|
121
|
+
type={'text'}
|
103
122
|
>
|
104
|
-
|
105
|
-
</
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
123
|
+
{t('clientDB.error.report')}
|
124
|
+
</Button>
|
125
|
+
<Flexbox gap={8} horizontal>
|
126
|
+
<Button
|
127
|
+
icon={<Icon icon={PenToolIcon} />}
|
128
|
+
onClick={() => {
|
129
|
+
setShowRepair(true);
|
130
|
+
}}
|
131
|
+
>
|
132
|
+
{t('clientDB.error.selfSolve')}
|
133
|
+
</Button>
|
134
|
+
<Button onClick={() => initializeClientDB()} type={'primary'}>
|
135
|
+
{t('clientDB.error.retry')}
|
136
|
+
</Button>
|
137
|
+
</Flexbox>
|
138
|
+
</Flexbox>
|
139
|
+
</Center>
|
140
|
+
)}
|
120
141
|
</Modal>
|
121
142
|
</>
|
122
143
|
);
|
@@ -0,0 +1,77 @@
|
|
1
|
+
import { Alert } from '@lobehub/ui';
|
2
|
+
import { App, Button, Card, Typography } from 'antd';
|
3
|
+
import { createStyles } from 'antd-style';
|
4
|
+
import { AlertCircle } from 'lucide-react';
|
5
|
+
import { useTranslation } from 'react-i18next';
|
6
|
+
import { Flexbox } from 'react-layout-kit';
|
7
|
+
|
8
|
+
import { resetClientDatabase } from '@/database/client/db';
|
9
|
+
|
10
|
+
const { Text, Paragraph } = Typography;
|
11
|
+
|
12
|
+
const useStyles = createStyles(({ css, token }) => ({
|
13
|
+
card: css`
|
14
|
+
border-radius: ${token.borderRadiusLG}px;
|
15
|
+
`,
|
16
|
+
}));
|
17
|
+
|
18
|
+
const Backup = () => {
|
19
|
+
const { t } = useTranslation('common');
|
20
|
+
const { styles } = useStyles();
|
21
|
+
|
22
|
+
const { modal } = App.useApp();
|
23
|
+
return (
|
24
|
+
<Flexbox gap={24}>
|
25
|
+
<Card
|
26
|
+
className={styles.card}
|
27
|
+
extra={<Text type="secondary">{t('clientDB.solve.backup.desc')}</Text>}
|
28
|
+
title={t('clientDB.solve.backup.title')}
|
29
|
+
>
|
30
|
+
<Paragraph>{t('clientDB.solve.backup.exportDesc')}</Paragraph>
|
31
|
+
<Button block type={'primary'}>
|
32
|
+
{t('clientDB.solve.backup.export')}
|
33
|
+
</Button>
|
34
|
+
</Card>
|
35
|
+
|
36
|
+
<Card
|
37
|
+
className={styles.card}
|
38
|
+
extra={<Text type="secondary">{t('clientDB.solve.backup.reset.desc')}</Text>}
|
39
|
+
title={t('clientDB.solve.backup.reset.title')}
|
40
|
+
>
|
41
|
+
<Flexbox gap={24}>
|
42
|
+
<Alert
|
43
|
+
description={t('clientDB.solve.backup.reset.alertDesc')}
|
44
|
+
icon={<AlertCircle size={16} />}
|
45
|
+
message={t('clientDB.solve.backup.reset.alert')}
|
46
|
+
showIcon
|
47
|
+
type="error"
|
48
|
+
variant={'pure'}
|
49
|
+
/>
|
50
|
+
|
51
|
+
<Button
|
52
|
+
block
|
53
|
+
danger
|
54
|
+
onClick={() => {
|
55
|
+
modal.confirm({
|
56
|
+
content: t('clientDB.solve.backup.reset.confirm.desc'),
|
57
|
+
okButtonProps: {
|
58
|
+
danger: true,
|
59
|
+
},
|
60
|
+
onOk: async () => {
|
61
|
+
await resetClientDatabase();
|
62
|
+
|
63
|
+
location.reload();
|
64
|
+
},
|
65
|
+
title: t('clientDB.solve.backup.reset.confirm.title'),
|
66
|
+
});
|
67
|
+
}}
|
68
|
+
>
|
69
|
+
{t('clientDB.solve.backup.reset.button')}
|
70
|
+
</Button>
|
71
|
+
</Flexbox>
|
72
|
+
</Card>
|
73
|
+
</Flexbox>
|
74
|
+
);
|
75
|
+
};
|
76
|
+
|
77
|
+
export default Backup;
|
@@ -0,0 +1,98 @@
|
|
1
|
+
import { Highlighter } from '@lobehub/ui';
|
2
|
+
import { Card, List, Popover, Tag } from 'antd';
|
3
|
+
import { createStyles } from 'antd-style';
|
4
|
+
import dayjs from 'dayjs';
|
5
|
+
import isEqual from 'fast-deep-equal';
|
6
|
+
import { useTranslation } from 'react-i18next';
|
7
|
+
import { Flexbox } from 'react-layout-kit';
|
8
|
+
|
9
|
+
import { useGlobalStore } from '@/store/global';
|
10
|
+
import { clientDBSelectors } from '@/store/global/selectors';
|
11
|
+
|
12
|
+
const useStyles = createStyles(({ css, token }) => ({
|
13
|
+
card: css`
|
14
|
+
margin-block-end: ${token.marginMD}px;
|
15
|
+
border-radius: ${token.borderRadiusLG}px;
|
16
|
+
`,
|
17
|
+
hash: css`
|
18
|
+
font-family: ${token.fontFamilyCode};
|
19
|
+
`,
|
20
|
+
sql: css`
|
21
|
+
overflow: hidden;
|
22
|
+
display: -webkit-box;
|
23
|
+
-webkit-box-orient: vertical;
|
24
|
+
-webkit-line-clamp: 3;
|
25
|
+
|
26
|
+
font-size: 12px;
|
27
|
+
`,
|
28
|
+
time: css`
|
29
|
+
font-size: 12px;
|
30
|
+
color: ${token.colorTextTertiary};
|
31
|
+
`,
|
32
|
+
}));
|
33
|
+
|
34
|
+
const Diagnosis = () => {
|
35
|
+
const { t } = useTranslation('common');
|
36
|
+
const { styles, cx } = useStyles();
|
37
|
+
|
38
|
+
const migrations = useGlobalStore(clientDBSelectors.displayMigrationStatus, isEqual);
|
39
|
+
|
40
|
+
return (
|
41
|
+
<Card
|
42
|
+
className={styles.card}
|
43
|
+
styles={{ body: { padding: 0 } }}
|
44
|
+
title={t('clientDB.solve.diagnosis.title')}
|
45
|
+
>
|
46
|
+
<List
|
47
|
+
dataSource={migrations}
|
48
|
+
renderItem={(migration) => (
|
49
|
+
<List.Item
|
50
|
+
key={migration.id}
|
51
|
+
style={{ display: 'flex', gap: 12, justifyContent: 'space-between' }}
|
52
|
+
>
|
53
|
+
<Flexbox gap={4} horizontal>
|
54
|
+
<Flexbox style={{ minWidth: 20 }} width={20}>
|
55
|
+
{migration.index}
|
56
|
+
</Flexbox>
|
57
|
+
<Popover
|
58
|
+
content={
|
59
|
+
<Flexbox gap={8}>
|
60
|
+
<div className={styles.hash}>Hash: {migration.id}</div>
|
61
|
+
<Highlighter
|
62
|
+
language={'sql'}
|
63
|
+
style={{ maxHeight: '30vh', maxWidth: '70vw', overflow: 'scroll' }}
|
64
|
+
>
|
65
|
+
{migration.sql.join('\n')}
|
66
|
+
</Highlighter>
|
67
|
+
</Flexbox>
|
68
|
+
}
|
69
|
+
title={t('clientDB.solve.diagnosis.sql')}
|
70
|
+
>
|
71
|
+
<Flexbox gap={8}>
|
72
|
+
<Flexbox
|
73
|
+
className={cx(styles.sql, migration.status === 'success' && styles.time)}
|
74
|
+
>
|
75
|
+
{migration.desc}
|
76
|
+
</Flexbox>
|
77
|
+
<Flexbox>
|
78
|
+
<Flexbox className={styles.time}>
|
79
|
+
{t('clientDB.solve.diagnosis.createdAt')}
|
80
|
+
{dayjs(migration.createdAt).format('YYYY-MM-DD hh:mm:ss')} ·{' '}
|
81
|
+
{t('clientDB.solve.diagnosis.migratedAt')}{' '}
|
82
|
+
{dayjs(migration.migratedAt).format('YYYY-MM-DD hh:mm:ss')}
|
83
|
+
</Flexbox>
|
84
|
+
</Flexbox>
|
85
|
+
</Flexbox>
|
86
|
+
</Popover>
|
87
|
+
</Flexbox>
|
88
|
+
<Tag bordered={false} color={migration.status}>
|
89
|
+
{migration.status}
|
90
|
+
</Tag>
|
91
|
+
</List.Item>
|
92
|
+
)}
|
93
|
+
size="small"
|
94
|
+
/>
|
95
|
+
</Card>
|
96
|
+
);
|
97
|
+
};
|
98
|
+
export default Diagnosis;
|
@@ -0,0 +1,220 @@
|
|
1
|
+
'use client';
|
2
|
+
|
3
|
+
import { Alert, CodeEditor, Icon } from '@lobehub/ui';
|
4
|
+
import { Button, Card, List, Typography } from 'antd';
|
5
|
+
import { createStyles } from 'antd-style';
|
6
|
+
import dayjs from 'dayjs';
|
7
|
+
import isEqual from 'fast-deep-equal';
|
8
|
+
import { AlertCircle, CheckCircle, Play } from 'lucide-react';
|
9
|
+
import { memo, useState } from 'react';
|
10
|
+
import { useTranslation } from 'react-i18next';
|
11
|
+
import { Flexbox } from 'react-layout-kit';
|
12
|
+
|
13
|
+
import { clientDB, updateMigrationRecord } from '@/database/client/db';
|
14
|
+
import { useGlobalStore } from '@/store/global';
|
15
|
+
import { clientDBSelectors } from '@/store/global/selectors';
|
16
|
+
|
17
|
+
const { Text } = Typography;
|
18
|
+
|
19
|
+
// 使用 antd-style 创建样式
|
20
|
+
const useStyles = createStyles(({ css, token }) => ({
|
21
|
+
actionBar: css`
|
22
|
+
display: flex;
|
23
|
+
justify-content: flex-end;
|
24
|
+
margin-block-start: ${token.marginSM}px;
|
25
|
+
`,
|
26
|
+
card: css`
|
27
|
+
margin-block-end: ${token.marginMD}px;
|
28
|
+
border-radius: ${token.borderRadiusLG}px;
|
29
|
+
`,
|
30
|
+
codeBlock: css`
|
31
|
+
overflow-x: auto;
|
32
|
+
|
33
|
+
margin-block: ${token.marginSM}px;
|
34
|
+
margin-inline: 0;
|
35
|
+
padding: ${token.paddingSM}px;
|
36
|
+
border-radius: ${token.borderRadiusSM}px;
|
37
|
+
|
38
|
+
font-family: ${token.fontFamilyCode};
|
39
|
+
font-size: 12px;
|
40
|
+
|
41
|
+
background: ${token.colorBgTextHover};
|
42
|
+
`,
|
43
|
+
container: css`
|
44
|
+
overflow-y: auto;
|
45
|
+
padding: 0;
|
46
|
+
`,
|
47
|
+
header: css`
|
48
|
+
display: flex;
|
49
|
+
align-items: center;
|
50
|
+
margin-block-end: ${token.marginMD}px;
|
51
|
+
`,
|
52
|
+
migrationItem: css`
|
53
|
+
display: flex;
|
54
|
+
align-items: center;
|
55
|
+
justify-content: space-between;
|
56
|
+
|
57
|
+
padding-block: ${token.paddingSM}px;
|
58
|
+
padding-inline: 0;
|
59
|
+
border-block-end: none;
|
60
|
+
`,
|
61
|
+
spotlight: css`
|
62
|
+
margin-block-end: ${token.marginLG}px;
|
63
|
+
`,
|
64
|
+
|
65
|
+
templateCard: css`
|
66
|
+
margin-block-end: ${token.marginSM}px;
|
67
|
+
`,
|
68
|
+
}));
|
69
|
+
|
70
|
+
interface QueryResult {
|
71
|
+
message: string;
|
72
|
+
success: boolean;
|
73
|
+
}
|
74
|
+
const Repair = memo(() => {
|
75
|
+
const { t } = useTranslation('common');
|
76
|
+
|
77
|
+
const { styles } = useStyles();
|
78
|
+
const [sqlQuery, setSqlQuery] = useState('');
|
79
|
+
const [queryResult, setQueryResult] = useState<QueryResult | null>(null);
|
80
|
+
const [showSQLHash, setShowSQLHash] = useState('');
|
81
|
+
|
82
|
+
const errorMigrations = useGlobalStore(clientDBSelectors.errorMigrations, isEqual);
|
83
|
+
|
84
|
+
const handleExecuteSQL = async () => {
|
85
|
+
try {
|
86
|
+
// 移除 BEGIN 和 COMMIT
|
87
|
+
let processedSQL = sqlQuery.replaceAll(/BEGIN;|COMMIT;/g, '');
|
88
|
+
|
89
|
+
// 分割 SQL 语句(使用 statement-breakpoint 作为分隔符)
|
90
|
+
const migrationQueries = processedSQL.split('--> statement-breakpoint');
|
91
|
+
|
92
|
+
// 手动管理事务
|
93
|
+
await clientDB.execute('BEGIN;');
|
94
|
+
|
95
|
+
try {
|
96
|
+
for (const query of migrationQueries) {
|
97
|
+
if (query.trim()) {
|
98
|
+
await clientDB.execute(query);
|
99
|
+
}
|
100
|
+
}
|
101
|
+
await clientDB.execute('COMMIT;');
|
102
|
+
|
103
|
+
setQueryResult({
|
104
|
+
message: `SQL executed successfully.`,
|
105
|
+
success: true,
|
106
|
+
});
|
107
|
+
} catch (error) {
|
108
|
+
await clientDB.execute('ROLLBACK;');
|
109
|
+
|
110
|
+
throw error;
|
111
|
+
}
|
112
|
+
} catch (error) {
|
113
|
+
setQueryResult({ message: (error as Error).message, success: false });
|
114
|
+
}
|
115
|
+
};
|
116
|
+
|
117
|
+
return (
|
118
|
+
<div>
|
119
|
+
<Card
|
120
|
+
className={styles.card}
|
121
|
+
extra={<Text type="secondary">{t('clientDB.solve.repair.desc')}</Text>}
|
122
|
+
title={t('clientDB.solve.repair.title')}
|
123
|
+
variant={'borderless'}
|
124
|
+
>
|
125
|
+
<List
|
126
|
+
dataSource={errorMigrations}
|
127
|
+
renderItem={(migration) => (
|
128
|
+
<>
|
129
|
+
<List.Item
|
130
|
+
actions={[
|
131
|
+
<Button
|
132
|
+
icon={<Icon icon={Play} />}
|
133
|
+
key="retry"
|
134
|
+
onClick={() => {
|
135
|
+
setShowSQLHash(!!showSQLHash ? '' : migration.hash);
|
136
|
+
setSqlQuery(migration.sql.join('--> statement-breakpoint\n'));
|
137
|
+
}}
|
138
|
+
>
|
139
|
+
{t('clientDB.solve.repair.runSQL')}
|
140
|
+
</Button>,
|
141
|
+
]}
|
142
|
+
className={styles.migrationItem}
|
143
|
+
key={migration.hash}
|
144
|
+
>
|
145
|
+
<List.Item.Meta
|
146
|
+
description={
|
147
|
+
<Flexbox>
|
148
|
+
{t('clientDB.solve.diagnosis.createdAt')}
|
149
|
+
{dayjs(migration.folderMillis).format('YYYY-MM-DD hh:mm:ss')} ·
|
150
|
+
</Flexbox>
|
151
|
+
}
|
152
|
+
title={migration.sql[0]}
|
153
|
+
/>
|
154
|
+
</List.Item>
|
155
|
+
{showSQLHash === migration.hash && (
|
156
|
+
<Card
|
157
|
+
className={styles.card}
|
158
|
+
extra={<Text type="secondary">{t('clientDB.solve.repair.sql.desc')}</Text>}
|
159
|
+
title={t('clientDB.solve.repair.sql.title')}
|
160
|
+
>
|
161
|
+
<Flexbox gap={16}>
|
162
|
+
<CodeEditor
|
163
|
+
language={'sql'}
|
164
|
+
onValueChange={(e) => setSqlQuery(e)}
|
165
|
+
placeholder={t('clientDB.solve.repair.sql.placeholder')}
|
166
|
+
style={{ fontFamily: 'monospace', height: 300 }}
|
167
|
+
value={sqlQuery}
|
168
|
+
/>
|
169
|
+
<Flexbox horizontal style={{ justifyContent: 'space-between' }}>
|
170
|
+
<Button onClick={() => setSqlQuery('')}>
|
171
|
+
{t('clientDB.solve.repair.sql.clear')}
|
172
|
+
</Button>
|
173
|
+
<Button disabled={!sqlQuery.trim()} onClick={handleExecuteSQL} type="primary">
|
174
|
+
{t('clientDB.solve.repair.sql.run')}
|
175
|
+
</Button>
|
176
|
+
</Flexbox>
|
177
|
+
|
178
|
+
{queryResult && (
|
179
|
+
<Alert
|
180
|
+
action={
|
181
|
+
queryResult.success && (
|
182
|
+
<Button
|
183
|
+
key="complete"
|
184
|
+
onClick={() => updateMigrationRecord(migration.hash)}
|
185
|
+
size={'small'}
|
186
|
+
type="primary"
|
187
|
+
>
|
188
|
+
{t('clientDB.solve.repair.sql.markFinished')}
|
189
|
+
</Button>
|
190
|
+
)
|
191
|
+
}
|
192
|
+
description={
|
193
|
+
<pre style={{ marginTop: '8px', whiteSpace: 'pre-wrap' }}>
|
194
|
+
{queryResult.message}
|
195
|
+
</pre>
|
196
|
+
}
|
197
|
+
icon={
|
198
|
+
queryResult.success ? (
|
199
|
+
<AlertCircle size={16} />
|
200
|
+
) : (
|
201
|
+
<CheckCircle size={16} />
|
202
|
+
)
|
203
|
+
}
|
204
|
+
message={t('clientDB.solve.repair.sql.result')}
|
205
|
+
showIcon
|
206
|
+
type={queryResult.success ? 'success' : 'error'}
|
207
|
+
/>
|
208
|
+
)}
|
209
|
+
</Flexbox>
|
210
|
+
</Card>
|
211
|
+
)}
|
212
|
+
</>
|
213
|
+
)}
|
214
|
+
/>
|
215
|
+
</Card>
|
216
|
+
</div>
|
217
|
+
);
|
218
|
+
});
|
219
|
+
|
220
|
+
export default Repair;
|
@@ -0,0 +1,85 @@
|
|
1
|
+
'use client';
|
2
|
+
|
3
|
+
import { Button, Segmented } from 'antd';
|
4
|
+
import { createStyles } from 'antd-style';
|
5
|
+
import { AlertCircle, ArrowLeft, PenToolIcon as Tool } from 'lucide-react';
|
6
|
+
import { memo, useState } from 'react';
|
7
|
+
import { useTranslation } from 'react-i18next';
|
8
|
+
import { Flexbox } from 'react-layout-kit';
|
9
|
+
|
10
|
+
import Backup from './Backup';
|
11
|
+
import Diagnosis from './Diagnosis';
|
12
|
+
import Repair from './Repair';
|
13
|
+
|
14
|
+
const useStyles = createStyles(({ css, token }) => ({
|
15
|
+
backButton: css`
|
16
|
+
margin-inline-end: ${token.marginSM}px;
|
17
|
+
padding-block: 4px;
|
18
|
+
padding-inline: 8px;
|
19
|
+
`,
|
20
|
+
}));
|
21
|
+
|
22
|
+
interface DatabaseRepairProps {
|
23
|
+
setShowRepair: (value: boolean) => void;
|
24
|
+
}
|
25
|
+
|
26
|
+
const DatabaseRepair = memo<DatabaseRepairProps>(({ setShowRepair }) => {
|
27
|
+
const { t } = useTranslation('common');
|
28
|
+
const { styles } = useStyles();
|
29
|
+
const [activeTab, setActiveTab] = useState('diagnosis');
|
30
|
+
|
31
|
+
return (
|
32
|
+
<Flexbox gap={12}>
|
33
|
+
<Flexbox horizontal justify={'space-between'}>
|
34
|
+
<Button
|
35
|
+
className={styles.backButton}
|
36
|
+
icon={<ArrowLeft size={16} />}
|
37
|
+
onClick={() => setShowRepair(false)}
|
38
|
+
type="text"
|
39
|
+
>
|
40
|
+
{t('back')}
|
41
|
+
</Button>
|
42
|
+
<Segmented
|
43
|
+
onChange={setActiveTab}
|
44
|
+
options={[
|
45
|
+
{
|
46
|
+
label: (
|
47
|
+
<Flexbox align={'center'} horizontal>
|
48
|
+
<AlertCircle size={16} style={{ marginRight: 8 }} />
|
49
|
+
{t('clientDB.solve.tabs.diagnosis')}
|
50
|
+
</Flexbox>
|
51
|
+
),
|
52
|
+
value: 'diagnosis',
|
53
|
+
},
|
54
|
+
{
|
55
|
+
label: (
|
56
|
+
<Flexbox align={'center'} horizontal>
|
57
|
+
<Tool size={16} style={{ marginRight: 8 }} />
|
58
|
+
{t('clientDB.solve.tabs.repair')}
|
59
|
+
</Flexbox>
|
60
|
+
),
|
61
|
+
value: 'repair',
|
62
|
+
},
|
63
|
+
// {
|
64
|
+
// label: (
|
65
|
+
// <Flexbox align={'center'} horizontal>
|
66
|
+
// <Shield size={16} style={{ marginRight: 8 }} />
|
67
|
+
// {t('clientDB.solve.tabs.backup')}
|
68
|
+
// </Flexbox>
|
69
|
+
// ),
|
70
|
+
// value: 'backup',
|
71
|
+
// },
|
72
|
+
]}
|
73
|
+
value={activeTab}
|
74
|
+
/>
|
75
|
+
<Flexbox width={36} />
|
76
|
+
</Flexbox>
|
77
|
+
|
78
|
+
{activeTab === 'diagnosis' && <Diagnosis />}
|
79
|
+
{activeTab === 'repair' && <Repair />}
|
80
|
+
{activeTab === 'backup' && <Backup />}
|
81
|
+
</Flexbox>
|
82
|
+
);
|
83
|
+
});
|
84
|
+
|
85
|
+
export default DatabaseRepair;
|
@@ -15,6 +15,7 @@ import { useEnabledChatModels } from '@/hooks/useEnabledChatModels';
|
|
15
15
|
import { useIsMobile } from '@/hooks/useIsMobile';
|
16
16
|
import { useAgentStore } from '@/store/agent';
|
17
17
|
import { agentSelectors } from '@/store/agent/slices/chat';
|
18
|
+
import { featureFlagsSelectors, useServerConfigStore } from '@/store/serverConfig';
|
18
19
|
import { EnabledProviderWithModels } from '@/types/aiProvider';
|
19
20
|
|
20
21
|
const useStyles = createStyles(({ css, prefixCls }) => ({
|
@@ -49,6 +50,7 @@ const ModelSwitchPanel = memo<PropsWithChildren>(({ children }) => {
|
|
49
50
|
s.updateAgentConfig,
|
50
51
|
]);
|
51
52
|
|
53
|
+
const { showLLM } = useServerConfigStore(featureFlagsSelectors);
|
52
54
|
const isMobile = useIsMobile();
|
53
55
|
|
54
56
|
const router = useRouter();
|
@@ -115,13 +117,17 @@ const ModelSwitchPanel = memo<PropsWithChildren>(({ children }) => {
|
|
115
117
|
provider={provider.id}
|
116
118
|
source={provider.source}
|
117
119
|
/>
|
118
|
-
|
119
|
-
<
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
120
|
+
{showLLM && (
|
121
|
+
<Link
|
122
|
+
href={isDeprecatedEdition ? '/settings/llm' : `/settings/provider/${provider.id}`}
|
123
|
+
>
|
124
|
+
<ActionIcon
|
125
|
+
icon={LucideBolt}
|
126
|
+
size={'small'}
|
127
|
+
title={t('ModelSwitchPanel.goToSettings')}
|
128
|
+
/>
|
129
|
+
</Link>
|
130
|
+
)}
|
125
131
|
</Flexbox>
|
126
132
|
),
|
127
133
|
type: 'group',
|