@lobehub/lobehub 2.0.0-next.142 → 2.0.0-next.144
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 +50 -0
- package/apps/desktop/package.json +1 -0
- package/apps/desktop/src/main/core/ui/__tests__/MenuManager.test.ts +320 -0
- package/apps/desktop/src/main/core/ui/__tests__/Tray.test.ts +518 -0
- package/apps/desktop/src/main/core/ui/__tests__/TrayManager.test.ts +360 -0
- package/apps/desktop/src/main/menus/impls/BaseMenuPlatform.test.ts +49 -0
- package/apps/desktop/src/main/menus/impls/linux.test.ts +552 -0
- package/apps/desktop/src/main/menus/impls/macOS.test.ts +464 -0
- package/apps/desktop/src/main/menus/impls/windows.test.ts +429 -0
- package/apps/desktop/src/main/modules/fileSearch/__tests__/macOS.integration.test.ts +2 -2
- package/apps/desktop/src/main/services/__tests__/fileSearchSrv.test.ts +402 -0
- package/apps/desktop/src/main/utils/__tests__/file-system.test.ts +91 -0
- package/apps/desktop/src/main/utils/__tests__/logger.test.ts +229 -0
- package/apps/desktop/src/preload/electronApi.test.ts +142 -0
- package/apps/desktop/src/preload/invoke.test.ts +145 -0
- package/apps/desktop/src/preload/routeInterceptor.test.ts +374 -0
- package/apps/desktop/src/preload/streamer.test.ts +365 -0
- package/apps/desktop/vitest.config.mts +1 -0
- package/changelog/v1.json +18 -0
- package/locales/ar/marketAuth.json +13 -0
- package/locales/bg-BG/marketAuth.json +13 -0
- package/locales/de-DE/marketAuth.json +13 -0
- package/locales/en-US/marketAuth.json +13 -0
- package/locales/es-ES/marketAuth.json +13 -0
- package/locales/fa-IR/marketAuth.json +13 -0
- package/locales/fr-FR/marketAuth.json +13 -0
- package/locales/it-IT/marketAuth.json +13 -0
- package/locales/ja-JP/marketAuth.json +13 -0
- package/locales/ko-KR/marketAuth.json +13 -0
- package/locales/nl-NL/marketAuth.json +13 -0
- package/locales/pl-PL/marketAuth.json +13 -0
- package/locales/pt-BR/marketAuth.json +13 -0
- package/locales/ru-RU/marketAuth.json +13 -0
- package/locales/tr-TR/marketAuth.json +13 -0
- package/locales/vi-VN/marketAuth.json +13 -0
- package/locales/zh-CN/marketAuth.json +13 -0
- package/locales/zh-TW/marketAuth.json +13 -0
- package/package.json +1 -1
- package/packages/database/migrations/0054_better_auth_two_factor.sql +2 -0
- package/packages/database/src/core/migrations.json +1 -1
- package/packages/database/src/models/user.ts +27 -5
- package/packages/types/src/discover/mcp.ts +2 -1
- package/packages/types/src/tool/plugin.ts +2 -1
- package/scripts/migrateServerDB/errorHint.js +26 -0
- package/scripts/migrateServerDB/index.ts +5 -1
- package/src/app/[variants]/(main)/chat/settings/features/SmartAgentActionButton/MarketPublishButton.tsx +0 -2
- package/src/app/[variants]/(main)/discover/(detail)/mcp/features/Sidebar/ActionButton/index.tsx +33 -7
- package/src/features/PluginStore/McpList/List/Action.tsx +20 -1
- package/src/layout/AuthProvider/MarketAuth/MarketAuthConfirmModal.tsx +158 -0
- package/src/layout/AuthProvider/MarketAuth/MarketAuthProvider.tsx +130 -14
- package/src/libs/mcp/types.ts +8 -0
- package/src/locales/default/marketAuth.ts +13 -0
- package/src/server/routers/lambda/market/index.ts +85 -2
- package/src/server/services/discover/index.ts +45 -4
- package/src/services/discover.ts +1 -1
- package/src/services/mcp.ts +18 -3
- package/src/store/tool/slices/mcpStore/action.test.ts +141 -0
- package/src/store/tool/slices/mcpStore/action.ts +153 -11
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
{
|
|
2
|
+
"authorize": {
|
|
3
|
+
"cancel": "Annuleren",
|
|
4
|
+
"confirm": "Toestemming geven",
|
|
5
|
+
"description": {
|
|
6
|
+
"and": "en",
|
|
7
|
+
"prefix": "Door op 'Toestemming geven' te klikken, ga je akkoord met onze",
|
|
8
|
+
"privacy": "Privacybeleid",
|
|
9
|
+
"terms": "Servicevoorwaarden"
|
|
10
|
+
},
|
|
11
|
+
"title": "Autorisatie bevestigen"
|
|
12
|
+
},
|
|
2
13
|
"callback": {
|
|
3
14
|
"buttons": {
|
|
4
15
|
"close": "Venster sluiten"
|
|
@@ -33,8 +44,10 @@
|
|
|
33
44
|
"stateMissing": "Autorisatiestatus niet gevonden, probeer het opnieuw."
|
|
34
45
|
},
|
|
35
46
|
"messages": {
|
|
47
|
+
"authorized": "LobeHub-service succesvol geautoriseerd",
|
|
36
48
|
"loading": "Autorisatieproces wordt gestart...",
|
|
37
49
|
"success": {
|
|
50
|
+
"cloudMcpInstall": "Toestemming succesvol! Je kunt nu de Cloud MCP-plugin installeren.",
|
|
38
51
|
"submit": "Autorisatie geslaagd! Je kunt nu een assistent publiceren.",
|
|
39
52
|
"upload": "Autorisatie geslaagd! Je kunt nu een nieuwe versie publiceren."
|
|
40
53
|
}
|
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
{
|
|
2
|
+
"authorize": {
|
|
3
|
+
"cancel": "Anuluj",
|
|
4
|
+
"confirm": "Autoryzuj użycie",
|
|
5
|
+
"description": {
|
|
6
|
+
"and": "oraz",
|
|
7
|
+
"prefix": "Klikając „Autoryzuj użycie”, wyrażasz zgodę na",
|
|
8
|
+
"privacy": "Politykę prywatności",
|
|
9
|
+
"terms": "Warunki korzystania z usługi"
|
|
10
|
+
},
|
|
11
|
+
"title": "Potwierdź autoryzację"
|
|
12
|
+
},
|
|
2
13
|
"callback": {
|
|
3
14
|
"buttons": {
|
|
4
15
|
"close": "Zamknij okno"
|
|
@@ -33,8 +44,10 @@
|
|
|
33
44
|
"stateMissing": "Nie znaleziono stanu autoryzacji, spróbuj ponownie."
|
|
34
45
|
},
|
|
35
46
|
"messages": {
|
|
47
|
+
"authorized": "Autoryzacja usługi LobeHub zakończona pomyślnie",
|
|
36
48
|
"loading": "Uruchamianie procesu autoryzacji...",
|
|
37
49
|
"success": {
|
|
50
|
+
"cloudMcpInstall": "Autoryzacja zakończona sukcesem! Możesz teraz zainstalować wtyczkę Cloud MCP.",
|
|
38
51
|
"submit": "Autoryzacja zakończona sukcesem! Możesz teraz opublikować asystenta.",
|
|
39
52
|
"upload": "Autoryzacja zakończona sukcesem! Możesz teraz opublikować nową wersję."
|
|
40
53
|
}
|
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
{
|
|
2
|
+
"authorize": {
|
|
3
|
+
"cancel": "Cancelar",
|
|
4
|
+
"confirm": "Autorizar uso",
|
|
5
|
+
"description": {
|
|
6
|
+
"and": "e",
|
|
7
|
+
"prefix": "Ao clicar em autorizar uso, você concorda com",
|
|
8
|
+
"privacy": "Política de Privacidade",
|
|
9
|
+
"terms": "Termos de Serviço"
|
|
10
|
+
},
|
|
11
|
+
"title": "Confirmar Autorização"
|
|
12
|
+
},
|
|
2
13
|
"callback": {
|
|
3
14
|
"buttons": {
|
|
4
15
|
"close": "Fechar janela"
|
|
@@ -33,8 +44,10 @@
|
|
|
33
44
|
"stateMissing": "Estado de autorização não encontrado, tente novamente."
|
|
34
45
|
},
|
|
35
46
|
"messages": {
|
|
47
|
+
"authorized": "Autorização do serviço LobeHub realizada com sucesso",
|
|
36
48
|
"loading": "Iniciando o processo de autorização...",
|
|
37
49
|
"success": {
|
|
50
|
+
"cloudMcpInstall": "Autorização bem-sucedida! Agora você pode instalar o plugin Cloud MCP.",
|
|
38
51
|
"submit": "Autorização bem-sucedida! Agora você pode publicar o assistente.",
|
|
39
52
|
"upload": "Autorização bem-sucedida! Agora você pode publicar uma nova versão."
|
|
40
53
|
}
|
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
{
|
|
2
|
+
"authorize": {
|
|
3
|
+
"cancel": "Отмена",
|
|
4
|
+
"confirm": "Подтвердить доступ",
|
|
5
|
+
"description": {
|
|
6
|
+
"and": "и",
|
|
7
|
+
"prefix": "Нажимая «Подтвердить доступ», вы соглашаетесь с",
|
|
8
|
+
"privacy": "Политикой конфиденциальности",
|
|
9
|
+
"terms": "Условиями обслуживания"
|
|
10
|
+
},
|
|
11
|
+
"title": "Подтвердить авторизацию"
|
|
12
|
+
},
|
|
2
13
|
"callback": {
|
|
3
14
|
"buttons": {
|
|
4
15
|
"close": "Закрыть окно"
|
|
@@ -33,8 +44,10 @@
|
|
|
33
44
|
"stateMissing": "Состояние авторизации не найдено, пожалуйста, попробуйте снова."
|
|
34
45
|
},
|
|
35
46
|
"messages": {
|
|
47
|
+
"authorized": "Авторизация сервиса LobeHub прошла успешно",
|
|
36
48
|
"loading": "Запуск процесса авторизации...",
|
|
37
49
|
"success": {
|
|
50
|
+
"cloudMcpInstall": "Доступ успешно предоставлен! Теперь вы можете установить плагин Cloud MCP.",
|
|
38
51
|
"submit": "Авторизация прошла успешно! Теперь вы можете опубликовать помощника.",
|
|
39
52
|
"upload": "Авторизация прошла успешно! Теперь вы можете опубликовать новую версию."
|
|
40
53
|
}
|
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
{
|
|
2
|
+
"authorize": {
|
|
3
|
+
"cancel": "İptal",
|
|
4
|
+
"confirm": "Kullanıma Yetki Ver",
|
|
5
|
+
"description": {
|
|
6
|
+
"and": "ve",
|
|
7
|
+
"prefix": "Kullanıma yetki ver'e tıklayarak şu şartları kabul etmiş olursunuz:",
|
|
8
|
+
"privacy": "Gizlilik Politikası",
|
|
9
|
+
"terms": "Hizmet Şartları"
|
|
10
|
+
},
|
|
11
|
+
"title": "Yetkilendirmeyi Onayla"
|
|
12
|
+
},
|
|
2
13
|
"callback": {
|
|
3
14
|
"buttons": {
|
|
4
15
|
"close": "Pencereyi Kapat"
|
|
@@ -33,8 +44,10 @@
|
|
|
33
44
|
"stateMissing": "Yetkilendirme durumu bulunamadı, lütfen tekrar deneyin."
|
|
34
45
|
},
|
|
35
46
|
"messages": {
|
|
47
|
+
"authorized": "LobeHub hizmet yetkilendirmesi başarıyla tamamlandı",
|
|
36
48
|
"loading": "Yetkilendirme süreci başlatılıyor...",
|
|
37
49
|
"success": {
|
|
50
|
+
"cloudMcpInstall": "Yetkilendirme başarılı! Artık Cloud MCP eklentisini yükleyebilirsiniz.",
|
|
38
51
|
"submit": "Yetkilendirme başarılı! Artık asistan yayınlayabilirsiniz.",
|
|
39
52
|
"upload": "Yetkilendirme başarılı! Artık yeni bir sürüm yayınlayabilirsiniz."
|
|
40
53
|
}
|
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
{
|
|
2
|
+
"authorize": {
|
|
3
|
+
"cancel": "Hủy",
|
|
4
|
+
"confirm": "Ủy quyền sử dụng",
|
|
5
|
+
"description": {
|
|
6
|
+
"and": "và",
|
|
7
|
+
"prefix": "Bằng cách nhấp vào ủy quyền sử dụng, bạn đồng ý với",
|
|
8
|
+
"privacy": "Chính sách quyền riêng tư",
|
|
9
|
+
"terms": "Điều khoản dịch vụ"
|
|
10
|
+
},
|
|
11
|
+
"title": "Xác nhận ủy quyền"
|
|
12
|
+
},
|
|
2
13
|
"callback": {
|
|
3
14
|
"buttons": {
|
|
4
15
|
"close": "Đóng cửa sổ"
|
|
@@ -33,8 +44,10 @@
|
|
|
33
44
|
"stateMissing": "Không tìm thấy trạng thái xác thực, vui lòng thử lại."
|
|
34
45
|
},
|
|
35
46
|
"messages": {
|
|
47
|
+
"authorized": "Dịch vụ LobeHub đã được cấp quyền thành công",
|
|
36
48
|
"loading": "Đang khởi động quy trình xác thực...",
|
|
37
49
|
"success": {
|
|
50
|
+
"cloudMcpInstall": "Ủy quyền thành công! Bây giờ bạn có thể cài đặt plugin Cloud MCP.",
|
|
38
51
|
"submit": "Xác thực thành công! Bây giờ bạn có thể xuất bản trợ lý.",
|
|
39
52
|
"upload": "Xác thực thành công! Bây giờ bạn có thể xuất bản phiên bản mới."
|
|
40
53
|
}
|
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
{
|
|
2
|
+
"authorize": {
|
|
3
|
+
"cancel": "取消",
|
|
4
|
+
"confirm": "授权使用",
|
|
5
|
+
"description": {
|
|
6
|
+
"and": "和",
|
|
7
|
+
"prefix": "点击授权使用即视为同意",
|
|
8
|
+
"privacy": "隐私协议",
|
|
9
|
+
"terms": "服务条款"
|
|
10
|
+
},
|
|
11
|
+
"title": "确认授权"
|
|
12
|
+
},
|
|
2
13
|
"callback": {
|
|
3
14
|
"buttons": {
|
|
4
15
|
"close": "关闭窗口"
|
|
@@ -33,8 +44,10 @@
|
|
|
33
44
|
"stateMissing": "未找到授权状态,请重试。"
|
|
34
45
|
},
|
|
35
46
|
"messages": {
|
|
47
|
+
"authorized": "LobeHub 服务授权成功",
|
|
36
48
|
"loading": "正在启动授权流程...",
|
|
37
49
|
"success": {
|
|
50
|
+
"cloudMcpInstall": "授权成功!现在可以安装 Cloud MCP 插件了。",
|
|
38
51
|
"submit": "授权成功!现在可以发布助手了。",
|
|
39
52
|
"upload": "授权成功!现在可以发布新版本了。"
|
|
40
53
|
}
|
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
{
|
|
2
|
+
"authorize": {
|
|
3
|
+
"cancel": "取消",
|
|
4
|
+
"confirm": "授權使用",
|
|
5
|
+
"description": {
|
|
6
|
+
"and": "和",
|
|
7
|
+
"prefix": "點擊授權使用即視為同意",
|
|
8
|
+
"privacy": "隱私協議",
|
|
9
|
+
"terms": "服務條款"
|
|
10
|
+
},
|
|
11
|
+
"title": "確認授權"
|
|
12
|
+
},
|
|
2
13
|
"callback": {
|
|
3
14
|
"buttons": {
|
|
4
15
|
"close": "關閉視窗"
|
|
@@ -33,8 +44,10 @@
|
|
|
33
44
|
"stateMissing": "找不到授權狀態,請重試。"
|
|
34
45
|
},
|
|
35
46
|
"messages": {
|
|
47
|
+
"authorized": "LobeHub 服務授權成功",
|
|
36
48
|
"loading": "正在啟動授權流程⋯⋯",
|
|
37
49
|
"success": {
|
|
50
|
+
"cloudMcpInstall": "授權成功!現在可以安裝 Cloud MCP 外掛了。",
|
|
38
51
|
"submit": "授權成功!現在可以發佈助手了。",
|
|
39
52
|
"upload": "授權成功!現在可以發佈新版本了。"
|
|
40
53
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lobehub/lobehub",
|
|
3
|
-
"version": "2.0.0-next.
|
|
3
|
+
"version": "2.0.0-next.144",
|
|
4
4
|
"description": "LobeHub - an open-source,comprehensive AI Agent 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",
|
|
@@ -35,6 +35,8 @@ CREATE INDEX IF NOT EXISTS "verification_identifier_idx" ON "verifications" USIN
|
|
|
35
35
|
DO $$
|
|
36
36
|
BEGIN
|
|
37
37
|
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'users_email_unique') THEN
|
|
38
|
+
-- Normalize empty emails so the unique constraint can be created safely
|
|
39
|
+
UPDATE "users" SET "email" = NULL WHERE "email" = '';
|
|
38
40
|
ALTER TABLE "users" ADD CONSTRAINT "users_email_unique" UNIQUE ("email");
|
|
39
41
|
END IF;
|
|
40
42
|
END $$;
|
|
@@ -884,7 +884,7 @@
|
|
|
884
884
|
"\nCREATE INDEX IF NOT EXISTS \"account_userId_idx\" ON \"accounts\" USING btree (\"user_id\");\n",
|
|
885
885
|
"\nCREATE INDEX IF NOT EXISTS \"auth_session_userId_idx\" ON \"auth_sessions\" USING btree (\"user_id\");\n",
|
|
886
886
|
"\nCREATE INDEX IF NOT EXISTS \"verification_identifier_idx\" ON \"verifications\" USING btree (\"identifier\");\n",
|
|
887
|
-
"\nDO $$\nBEGIN\n IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'users_email_unique') THEN\n ALTER TABLE \"users\" ADD CONSTRAINT \"users_email_unique\" UNIQUE (\"email\");\n END IF;\nEND $$;\n",
|
|
887
|
+
"\nDO $$\nBEGIN\n IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'users_email_unique') THEN\n UPDATE \"users\" SET \"email\" = NULL WHERE \"email\" = '';\n ALTER TABLE \"users\" ADD CONSTRAINT \"users_email_unique\" UNIQUE (\"email\");\n END IF;\nEND $$;\n",
|
|
888
888
|
"\nDO $$\nBEGIN\n IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'users_phone_number_unique') THEN\n ALTER TABLE \"users\" ADD CONSTRAINT \"users_phone_number_unique\" UNIQUE (\"phone_number\");\n END IF;\nEND $$;\n"
|
|
889
889
|
],
|
|
890
890
|
"bps": true,
|
|
@@ -80,6 +80,7 @@ export class UserModel {
|
|
|
80
80
|
settingsImage: userSettings.image,
|
|
81
81
|
settingsKeyVaults: userSettings.keyVaults,
|
|
82
82
|
settingsLanguageModel: userSettings.languageModel,
|
|
83
|
+
settingsMarket: userSettings.market,
|
|
83
84
|
settingsSystemAgent: userSettings.systemAgent,
|
|
84
85
|
settingsTTS: userSettings.tts,
|
|
85
86
|
settingsTool: userSettings.tool,
|
|
@@ -112,6 +113,7 @@ export class UserModel {
|
|
|
112
113
|
image: state.settingsImage || {},
|
|
113
114
|
keyVaults: decryptKeyVaults,
|
|
114
115
|
languageModel: state.settingsLanguageModel || {},
|
|
116
|
+
market: state.settingsMarket || undefined,
|
|
115
117
|
systemAgent: state.settingsSystemAgent || {},
|
|
116
118
|
tool: state.settingsTool || {},
|
|
117
119
|
tts: state.settingsTTS || {},
|
|
@@ -147,9 +149,11 @@ export class UserModel {
|
|
|
147
149
|
};
|
|
148
150
|
|
|
149
151
|
updateUser = async (value: Partial<UserItem>) => {
|
|
152
|
+
const nextValue = UserModel.normalizeUniqueUserFields(value);
|
|
153
|
+
|
|
150
154
|
return this.db
|
|
151
155
|
.update(users)
|
|
152
|
-
.set({ ...
|
|
156
|
+
.set({ ...nextValue, updatedAt: new Date() })
|
|
153
157
|
.where(eq(users.id, this.userId));
|
|
154
158
|
};
|
|
155
159
|
|
|
@@ -191,6 +195,26 @@ export class UserModel {
|
|
|
191
195
|
.where(eq(users.id, this.userId));
|
|
192
196
|
};
|
|
193
197
|
|
|
198
|
+
/**
|
|
199
|
+
* Normalize unique user fields so empty strings become null, keeping unique constraints safe.
|
|
200
|
+
*/
|
|
201
|
+
private static normalizeUniqueUserFields = <
|
|
202
|
+
T extends { email?: string | null; phone?: string | null },
|
|
203
|
+
>(
|
|
204
|
+
value: T,
|
|
205
|
+
) => {
|
|
206
|
+
const normalizedEmail =
|
|
207
|
+
typeof value.email === 'string' && value.email.trim() === '' ? null : value.email;
|
|
208
|
+
const normalizedPhone =
|
|
209
|
+
typeof value.phone === 'string' && value.phone.trim() === '' ? null : value.phone;
|
|
210
|
+
|
|
211
|
+
return {
|
|
212
|
+
...value,
|
|
213
|
+
...(value.email !== undefined ? { email: normalizedEmail } : {}),
|
|
214
|
+
...(value.phone !== undefined ? { phone: normalizedPhone } : {}),
|
|
215
|
+
};
|
|
216
|
+
};
|
|
217
|
+
|
|
194
218
|
// Static method
|
|
195
219
|
static makeSureUserExist = async (db: LobeChatDatabase, userId: string) => {
|
|
196
220
|
await db.insert(users).values({ id: userId }).onConflictDoNothing();
|
|
@@ -203,10 +227,8 @@ export class UserModel {
|
|
|
203
227
|
if (!!user) return { duplicate: true };
|
|
204
228
|
}
|
|
205
229
|
|
|
206
|
-
const
|
|
207
|
-
|
|
208
|
-
.values({ ...params })
|
|
209
|
-
.returning();
|
|
230
|
+
const normalizedParams = this.normalizeUniqueUserFields(params);
|
|
231
|
+
const [user] = await db.insert(users).values(normalizedParams).returning();
|
|
210
232
|
|
|
211
233
|
return { duplicate: false, user };
|
|
212
234
|
};
|
|
@@ -41,7 +41,7 @@ export enum McpNavKey {
|
|
|
41
41
|
|
|
42
42
|
export enum McpConnectionType {
|
|
43
43
|
http = 'http',
|
|
44
|
-
stdio = 'stdio'
|
|
44
|
+
stdio = 'stdio',
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
export type DiscoverMcpItem = PluginItem;
|
|
@@ -60,6 +60,7 @@ export interface McpQueryParams {
|
|
|
60
60
|
export type McpListResponse = PluginListResponse;
|
|
61
61
|
|
|
62
62
|
export interface DiscoverMcpDetail extends PluginItemDetail {
|
|
63
|
+
haveCloudEndpoint?: boolean;
|
|
63
64
|
isClaimed?: boolean;
|
|
64
65
|
related: DiscoverMcpItem[];
|
|
65
66
|
}
|
|
@@ -25,8 +25,9 @@ export interface CustomPluginParams {
|
|
|
25
25
|
args?: string[];
|
|
26
26
|
env?: Record<string, string>;
|
|
27
27
|
command?: string;
|
|
28
|
-
type: 'http' | 'stdio';
|
|
28
|
+
type: 'http' | 'stdio' | 'cloud';
|
|
29
29
|
url?: string;
|
|
30
|
+
cloudEndPoint?: string; // Cloud gateway endpoint for cloud type
|
|
30
31
|
// Added authentication configuration support
|
|
31
32
|
auth?: {
|
|
32
33
|
type: 'none' | 'bearer' | 'oauth2';
|
|
@@ -24,7 +24,33 @@ DATABASE_DRIVER=node
|
|
|
24
24
|
if you have any other question, please open issue here: https://github.com/lobehub/lobe-chat/issues
|
|
25
25
|
`;
|
|
26
26
|
|
|
27
|
+
const DUPLICATE_EMAIL_HINT = `------------------------------------------------------------------------------------------
|
|
28
|
+
⚠️ Database migration failed due to duplicate email addresses in the users table.
|
|
29
|
+
|
|
30
|
+
The database schema requires each email to be unique, but multiple users currently share the same email value.
|
|
31
|
+
|
|
32
|
+
Recommended solutions (choose one and rerun the migration):
|
|
33
|
+
|
|
34
|
+
1) Update duplicate emails to make them unique: change the conflicting email addresses to another unique email address or just change them email to NULL
|
|
35
|
+
2) Remove duplicate user records (dangerously, only if safe to delete)
|
|
36
|
+
|
|
37
|
+
⚠️ IMPORTANT: Always backup your database before making any changes!
|
|
38
|
+
|
|
39
|
+
To find duplicate emails, run this query:
|
|
40
|
+
|
|
41
|
+
\`\`\`sql
|
|
42
|
+
SELECT email, COUNT(*) as count
|
|
43
|
+
FROM users
|
|
44
|
+
WHERE email IS NOT NULL
|
|
45
|
+
GROUP BY email
|
|
46
|
+
HAVING COUNT(*) > 1;
|
|
47
|
+
\`\`\`
|
|
48
|
+
|
|
49
|
+
If you need further assistance, please open an issue: https://github.com/lobehub/lobe-chat/issues
|
|
50
|
+
`;
|
|
51
|
+
|
|
27
52
|
module.exports = {
|
|
28
53
|
DB_FAIL_INIT_HINT,
|
|
54
|
+
DUPLICATE_EMAIL_HINT,
|
|
29
55
|
PGVECTOR_HINT,
|
|
30
56
|
};
|
|
@@ -4,7 +4,7 @@ import { migrate as nodeMigrate } from 'drizzle-orm/node-postgres/migrator';
|
|
|
4
4
|
import { join } from 'node:path';
|
|
5
5
|
|
|
6
6
|
// @ts-ignore tsgo handle esm import cjs and compatibility issues
|
|
7
|
-
import { DB_FAIL_INIT_HINT, PGVECTOR_HINT } from './errorHint';
|
|
7
|
+
import { DB_FAIL_INIT_HINT, DUPLICATE_EMAIL_HINT, PGVECTOR_HINT } from './errorHint';
|
|
8
8
|
|
|
9
9
|
// Read the `.env` file if it exists, or a file specified by the
|
|
10
10
|
// dotenv_config_path parameter that's passed to Node.js
|
|
@@ -39,8 +39,12 @@ if (!isDesktop && connectionString) {
|
|
|
39
39
|
|
|
40
40
|
const errMsg = err.message as string;
|
|
41
41
|
|
|
42
|
+
const constraint = (err as { constraint?: string })?.constraint;
|
|
43
|
+
|
|
42
44
|
if (errMsg.includes('extension "vector" is not available')) {
|
|
43
45
|
console.info(PGVECTOR_HINT);
|
|
46
|
+
} else if (constraint === 'users_email_unique' || errMsg.includes('users_email_unique')) {
|
|
47
|
+
console.info(DUPLICATE_EMAIL_HINT);
|
|
44
48
|
} else if (errMsg.includes(`Cannot read properties of undefined (reading 'migrate')`)) {
|
|
45
49
|
console.info(DB_FAIL_INIT_HINT);
|
|
46
50
|
}
|
|
@@ -87,9 +87,7 @@ const MarketPublishButton = memo<MarketPublishButtonProps>(
|
|
|
87
87
|
`[MarketPublishButton][${action}] User not authenticated, starting authorization`,
|
|
88
88
|
);
|
|
89
89
|
try {
|
|
90
|
-
message.loading({ content: tMarketAuth('messages.loading'), key: 'market-auth' });
|
|
91
90
|
const accountId = await signIn();
|
|
92
|
-
message.success({ content: buttonCopy.successMessage, key: 'market-auth' });
|
|
93
91
|
|
|
94
92
|
let targetAction: MarketPublishAction = action;
|
|
95
93
|
|
package/src/app/[variants]/(main)/discover/(detail)/mcp/features/Sidebar/ActionButton/index.tsx
CHANGED
|
@@ -10,6 +10,7 @@ import { Flexbox } from 'react-layout-kit';
|
|
|
10
10
|
|
|
11
11
|
import MCPInstallProgress from '@/features/MCP/MCPInstallProgress';
|
|
12
12
|
import { useDetailContext } from '@/features/MCPPluginDetail/DetailProvider';
|
|
13
|
+
import { useMarketAuth } from '@/layout/AuthProvider/MarketAuth';
|
|
13
14
|
import { useToolStore } from '@/store/tool';
|
|
14
15
|
import { pluginSelectors } from '@/store/tool/slices/plugin/selectors';
|
|
15
16
|
|
|
@@ -23,9 +24,11 @@ const useStyles = createStyles(({ css }) => ({
|
|
|
23
24
|
|
|
24
25
|
const ActionButton = memo(() => {
|
|
25
26
|
const { t } = useTranslation(['discover', 'plugin']);
|
|
26
|
-
const
|
|
27
|
+
const detailContext = useDetailContext();
|
|
28
|
+
const { identifier, haveCloudEndpoint } = detailContext;
|
|
27
29
|
const { styles } = useStyles();
|
|
28
30
|
const [isLoading, setIsLoading] = useState(false);
|
|
31
|
+
const { isAuthenticated, isLoading: isAuthLoading, signIn } = useMarketAuth();
|
|
29
32
|
|
|
30
33
|
const [installed, installMCPPlugin, uninstallMCPPlugin] = useToolStore((s) => [
|
|
31
34
|
pluginSelectors.isPluginInstalled(identifier!)(s),
|
|
@@ -33,24 +36,47 @@ const ActionButton = memo(() => {
|
|
|
33
36
|
s.uninstallMCPPlugin,
|
|
34
37
|
]);
|
|
35
38
|
|
|
39
|
+
// Check if this is a cloud MCP plugin
|
|
40
|
+
const isCloudMcp = haveCloudEndpoint;
|
|
41
|
+
|
|
36
42
|
const installPlugin = async () => {
|
|
37
43
|
if (!identifier) return;
|
|
38
|
-
setIsLoading(true);
|
|
39
44
|
|
|
40
|
-
|
|
45
|
+
// If this is a cloud MCP and user is not authenticated, request authorization first
|
|
46
|
+
if (isCloudMcp && !isAuthenticated) {
|
|
47
|
+
try {
|
|
48
|
+
await signIn();
|
|
49
|
+
} catch {
|
|
50
|
+
return; // Don't proceed with installation if auth fails
|
|
51
|
+
}
|
|
52
|
+
}
|
|
41
53
|
|
|
42
|
-
|
|
54
|
+
// Proceed with installation
|
|
55
|
+
setIsLoading(true);
|
|
56
|
+
try {
|
|
57
|
+
await installMCPPlugin(identifier);
|
|
58
|
+
} finally {
|
|
59
|
+
setIsLoading(false);
|
|
60
|
+
}
|
|
43
61
|
};
|
|
44
62
|
|
|
63
|
+
const buttonLoading = isLoading || isAuthLoading;
|
|
64
|
+
|
|
45
65
|
return installed ? (
|
|
46
66
|
<Flexbox gap={8} horizontal>
|
|
47
|
-
<Button
|
|
67
|
+
<Button
|
|
68
|
+
block
|
|
69
|
+
className={styles.button}
|
|
70
|
+
disabled={buttonLoading}
|
|
71
|
+
size={'large'}
|
|
72
|
+
type={'default'}
|
|
73
|
+
>
|
|
48
74
|
{t('plugins.installed')}
|
|
49
75
|
</Button>
|
|
50
76
|
|
|
51
77
|
<Button
|
|
52
78
|
icon={<Icon icon={Trash2Icon} size={20} />}
|
|
53
|
-
loading={
|
|
79
|
+
loading={buttonLoading}
|
|
54
80
|
onClick={async () => {
|
|
55
81
|
setIsLoading(true);
|
|
56
82
|
await uninstallMCPPlugin(identifier!);
|
|
@@ -68,7 +94,7 @@ const ActionButton = memo(() => {
|
|
|
68
94
|
<Button
|
|
69
95
|
block
|
|
70
96
|
className={styles.button}
|
|
71
|
-
loading={
|
|
97
|
+
loading={buttonLoading}
|
|
72
98
|
onClick={installPlugin}
|
|
73
99
|
size={'large'}
|
|
74
100
|
type={'primary'}
|
|
@@ -5,6 +5,7 @@ import { memo } from 'react';
|
|
|
5
5
|
import { useTranslation } from 'react-i18next';
|
|
6
6
|
import { Flexbox } from 'react-layout-kit';
|
|
7
7
|
|
|
8
|
+
import { useMarketAuth } from '@/layout/AuthProvider/MarketAuth';
|
|
8
9
|
import { useAgentStore } from '@/store/agent';
|
|
9
10
|
import { agentSelectors } from '@/store/agent/selectors';
|
|
10
11
|
import { useToolStore } from '@/store/tool';
|
|
@@ -15,13 +16,14 @@ interface ActionsProps {
|
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
const Actions = memo<ActionsProps>(({ identifier }) => {
|
|
18
|
-
const [installed, installing, unInstallPlugin, installMCPPlugin, cancelInstallMCPPlugin] =
|
|
19
|
+
const [installed, installing, unInstallPlugin, installMCPPlugin, cancelInstallMCPPlugin, plugin] =
|
|
19
20
|
useToolStore((s) => [
|
|
20
21
|
pluginSelectors.isPluginInstalled(identifier)(s),
|
|
21
22
|
mcpStoreSelectors.isMCPInstalling(identifier)(s),
|
|
22
23
|
s.uninstallPlugin,
|
|
23
24
|
s.installMCPPlugin,
|
|
24
25
|
s.cancelInstallMCPPlugin,
|
|
26
|
+
mcpStoreSelectors.getPluginById(identifier)(s),
|
|
25
27
|
]);
|
|
26
28
|
|
|
27
29
|
const { t } = useTranslation('plugin');
|
|
@@ -30,6 +32,10 @@ const Actions = memo<ActionsProps>(({ identifier }) => {
|
|
|
30
32
|
agentSelectors.currentAgentPlugins(s).includes(identifier),
|
|
31
33
|
]);
|
|
32
34
|
const { modal } = App.useApp();
|
|
35
|
+
const { isAuthenticated, signIn } = useMarketAuth();
|
|
36
|
+
|
|
37
|
+
// Check if this is a cloud MCP plugin
|
|
38
|
+
const isCloudMcp = !!((plugin as any)?.cloudEndPoint || (plugin as any)?.haveCloudEndpoint);
|
|
33
39
|
|
|
34
40
|
return (
|
|
35
41
|
<Flexbox align={'center'} horizontal>
|
|
@@ -86,6 +92,19 @@ const Actions = memo<ActionsProps>(({ identifier }) => {
|
|
|
86
92
|
onClick={async (e) => {
|
|
87
93
|
e.stopPropagation();
|
|
88
94
|
|
|
95
|
+
// If this is a cloud MCP and user is not authenticated, request authorization first
|
|
96
|
+
if (isCloudMcp && !isAuthenticated) {
|
|
97
|
+
console.log(
|
|
98
|
+
'[MCPListAction] Cloud MCP detected, user not authenticated, starting authorization',
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
try {
|
|
102
|
+
await signIn();
|
|
103
|
+
} catch {
|
|
104
|
+
return; // Don't proceed with installation if auth fails
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
89
108
|
const isSuccess = await installMCPPlugin(identifier);
|
|
90
109
|
|
|
91
110
|
if (isSuccess) {
|