@lobehub/lobehub 2.0.0-next.17 → 2.0.0-next.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.desktop +1 -2
- package/.env.example +0 -3
- package/.env.example.development +0 -2
- package/.github/workflows/e2e.yml +14 -0
- package/.github/workflows/release.yml +0 -1
- package/.github/workflows/test.yml +0 -1
- package/CHANGELOG.md +58 -0
- package/Dockerfile +1 -3
- package/changelog/v1.json +18 -0
- 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 +2 -2
- package/packages/database/src/core/web-server.ts +2 -1
- package/packages/memory-extract/package.json +1 -1
- 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/scripts/migrateServerDB/errorHint.js +1 -7
- package/src/app/[variants]/(main)/_layout/Desktop/SideBar/TopActions.tsx +1 -4
- 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/config/db.ts +0 -5
- package/src/libs/next-auth/auth.config.ts +2 -5
- 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
package/.env.desktop
CHANGED
|
@@ -4,6 +4,5 @@ FEATURE_FLAGS=-check_updates,+pin_list
|
|
|
4
4
|
KEY_VAULTS_SECRET=oLXWIiR/AKF+rWaqy9lHkrYgzpATbW3CtJp3UfkVgpE=
|
|
5
5
|
DATABASE_URL=postgresql://postgres@localhost:5432/postgres
|
|
6
6
|
SEARCH_PROVIDERS=search1api
|
|
7
|
-
NEXT_PUBLIC_SERVICE_MODE='server'
|
|
8
7
|
NEXT_PUBLIC_IS_DESKTOP_APP=1
|
|
9
|
-
NEXT_PUBLIC_ENABLE_NEXT_AUTH=0
|
|
8
|
+
NEXT_PUBLIC_ENABLE_NEXT_AUTH=0
|
package/.env.example
CHANGED
|
@@ -273,9 +273,6 @@ OPENAI_API_KEY=sk-xxxxxxxxx
|
|
|
273
273
|
########## Server Database #############
|
|
274
274
|
########################################
|
|
275
275
|
|
|
276
|
-
# Specify the service mode as server if you want to use the server database
|
|
277
|
-
# NEXT_PUBLIC_SERVICE_MODE=server
|
|
278
|
-
|
|
279
276
|
# Postgres database URL
|
|
280
277
|
# DATABASE_URL=postgres://username:password@host:port/database
|
|
281
278
|
|
package/.env.example.development
CHANGED
|
@@ -8,8 +8,6 @@ UNSAFE_SECRET="ww+0igxjGRAAR/eTNFQ55VmhQB5KE5trFZseuntThJs="
|
|
|
8
8
|
UNSAFE_PASSWORD="CHANGE_THIS_PASSWORD_IN_PRODUCTION"
|
|
9
9
|
|
|
10
10
|
# Core Server Configuration
|
|
11
|
-
# Service mode - set to 'server' for server-side deployment
|
|
12
|
-
NEXT_PUBLIC_SERVICE_MODE=server
|
|
13
11
|
|
|
14
12
|
# Service Ports Configuration
|
|
15
13
|
LOBE_PORT=3010
|
|
@@ -14,6 +14,17 @@ jobs:
|
|
|
14
14
|
e2e:
|
|
15
15
|
name: Test Web App
|
|
16
16
|
runs-on: ubuntu-latest
|
|
17
|
+
services:
|
|
18
|
+
postgres:
|
|
19
|
+
image: paradedb/paradedb:latest
|
|
20
|
+
env:
|
|
21
|
+
POSTGRES_PASSWORD: postgres
|
|
22
|
+
options: >-
|
|
23
|
+
--health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
|
|
24
|
+
|
|
25
|
+
ports:
|
|
26
|
+
- 5432:5432
|
|
27
|
+
|
|
17
28
|
timeout-minutes: 25
|
|
18
29
|
steps:
|
|
19
30
|
- name: Checkout
|
|
@@ -33,6 +44,9 @@ jobs:
|
|
|
33
44
|
- name: Run E2E tests
|
|
34
45
|
env:
|
|
35
46
|
PORT: 3010
|
|
47
|
+
DATABASE_URL: postgresql://postgres:postgres@localhost:5432/postgres
|
|
48
|
+
DATABASE_DRIVER: node
|
|
49
|
+
KEY_VAULTS_SECRET: LA7n9k3JdEcbSgml2sxfw+4TV1AzaaFU5+R176aQz4s=
|
|
36
50
|
run: bun run e2e
|
|
37
51
|
|
|
38
52
|
- name: Upload Cucumber HTML report (on failure)
|
|
@@ -54,7 +54,6 @@ jobs:
|
|
|
54
54
|
env:
|
|
55
55
|
DATABASE_TEST_URL: postgresql://postgres:postgres@localhost:5432/postgres
|
|
56
56
|
DATABASE_DRIVER: node
|
|
57
|
-
NEXT_PUBLIC_SERVICE_MODE: server
|
|
58
57
|
KEY_VAULTS_SECRET: LA7n9k3JdEcbSgml2sxfw+4TV1AzaaFU5+R176aQz4s=
|
|
59
58
|
S3_PUBLIC_DOMAIN: https://example.com
|
|
60
59
|
APP_URL: https://home.com
|
|
@@ -165,7 +165,6 @@ jobs:
|
|
|
165
165
|
env:
|
|
166
166
|
DATABASE_TEST_URL: postgresql://postgres:postgres@localhost:5432/postgres
|
|
167
167
|
DATABASE_DRIVER: node
|
|
168
|
-
NEXT_PUBLIC_SERVICE_MODE: server
|
|
169
168
|
KEY_VAULTS_SECRET: LA7n9k3JdEcbSgml2sxfw+4TV1AzaaFU5+R176aQz4s=
|
|
170
169
|
S3_PUBLIC_DOMAIN: https://example.com
|
|
171
170
|
APP_URL: https://home.com
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,64 @@
|
|
|
2
2
|
|
|
3
3
|
# Changelog
|
|
4
4
|
|
|
5
|
+
## [Version 2.0.0-next.19](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.18...v2.0.0-next.19)
|
|
6
|
+
|
|
7
|
+
<sup>Released on **2025-11-03**</sup>
|
|
8
|
+
|
|
9
|
+
#### ♻ Code Refactoring
|
|
10
|
+
|
|
11
|
+
- **misc**: Remove `NEXT_PUBLIC_SERVICE_MODE` env and use server by default.
|
|
12
|
+
|
|
13
|
+
<br/>
|
|
14
|
+
|
|
15
|
+
<details>
|
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
17
|
+
|
|
18
|
+
#### Code refactoring
|
|
19
|
+
|
|
20
|
+
- **misc**: Remove `NEXT_PUBLIC_SERVICE_MODE` env and use server by default, closes [#10017](https://github.com/lobehub/lobe-chat/issues/10017) ([f2ab2fc](https://github.com/lobehub/lobe-chat/commit/f2ab2fc))
|
|
21
|
+
|
|
22
|
+
</details>
|
|
23
|
+
|
|
24
|
+
<div align="right">
|
|
25
|
+
|
|
26
|
+
[](#readme-top)
|
|
27
|
+
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
## [Version 2.0.0-next.18](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.17...v2.0.0-next.18)
|
|
31
|
+
|
|
32
|
+
<sup>Released on **2025-11-03**</sup>
|
|
33
|
+
|
|
34
|
+
#### ♻ Code Refactoring
|
|
35
|
+
|
|
36
|
+
- **misc**: Refactor trpc request to use zod schema.
|
|
37
|
+
|
|
38
|
+
#### 💄 Styles
|
|
39
|
+
|
|
40
|
+
- **misc**: Improve built-in client OIDC user flow.
|
|
41
|
+
|
|
42
|
+
<br/>
|
|
43
|
+
|
|
44
|
+
<details>
|
|
45
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
46
|
+
|
|
47
|
+
#### Code refactoring
|
|
48
|
+
|
|
49
|
+
- **misc**: Refactor trpc request to use zod schema, closes [#10016](https://github.com/lobehub/lobe-chat/issues/10016) ([1a84f2c](https://github.com/lobehub/lobe-chat/commit/1a84f2c))
|
|
50
|
+
|
|
51
|
+
#### Styles
|
|
52
|
+
|
|
53
|
+
- **misc**: Improve built-in client OIDC user flow, closes [#10020](https://github.com/lobehub/lobe-chat/issues/10020) ([80202ed](https://github.com/lobehub/lobe-chat/commit/80202ed))
|
|
54
|
+
|
|
55
|
+
</details>
|
|
56
|
+
|
|
57
|
+
<div align="right">
|
|
58
|
+
|
|
59
|
+
[](#readme-top)
|
|
60
|
+
|
|
61
|
+
</div>
|
|
62
|
+
|
|
5
63
|
## [Version 2.0.0-next.17](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.16...v2.0.0-next.17)
|
|
6
64
|
|
|
7
65
|
<sup>Released on **2025-11-03**</sup>
|
package/Dockerfile
CHANGED
|
@@ -37,7 +37,6 @@ FROM base AS builder
|
|
|
37
37
|
|
|
38
38
|
ARG USE_CN_MIRROR
|
|
39
39
|
ARG NEXT_PUBLIC_BASE_PATH
|
|
40
|
-
ARG NEXT_PUBLIC_SERVICE_MODE
|
|
41
40
|
ARG NEXT_PUBLIC_ENABLE_NEXT_AUTH
|
|
42
41
|
ARG NEXT_PUBLIC_ENABLE_CLERK_AUTH
|
|
43
42
|
ARG NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY
|
|
@@ -53,8 +52,7 @@ ARG FEATURE_FLAGS
|
|
|
53
52
|
ENV NEXT_PUBLIC_BASE_PATH="${NEXT_PUBLIC_BASE_PATH}" \
|
|
54
53
|
FEATURE_FLAGS="${FEATURE_FLAGS}"
|
|
55
54
|
|
|
56
|
-
ENV
|
|
57
|
-
NEXT_PUBLIC_ENABLE_NEXT_AUTH="${NEXT_PUBLIC_ENABLE_NEXT_AUTH:-1}" \
|
|
55
|
+
ENV NEXT_PUBLIC_ENABLE_NEXT_AUTH="${NEXT_PUBLIC_ENABLE_NEXT_AUTH:-1}" \
|
|
58
56
|
NEXT_PUBLIC_ENABLE_CLERK_AUTH="${NEXT_PUBLIC_ENABLE_CLERK_AUTH:-0}" \
|
|
59
57
|
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY="${NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY}" \
|
|
60
58
|
CLERK_WEBHOOK_SECRET="whsec_xxx" \
|
package/changelog/v1.json
CHANGED
|
@@ -1,4 +1,22 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"children": {
|
|
4
|
+
"improvements": [
|
|
5
|
+
"Remove NEXT_PUBLIC_SERVICE_MODE env and use server by default."
|
|
6
|
+
]
|
|
7
|
+
},
|
|
8
|
+
"date": "2025-11-03",
|
|
9
|
+
"version": "2.0.0-next.19"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"children": {
|
|
13
|
+
"improvements": [
|
|
14
|
+
"Improve built-in client OIDC user flow."
|
|
15
|
+
]
|
|
16
|
+
},
|
|
17
|
+
"date": "2025-11-03",
|
|
18
|
+
"version": "2.0.0-next.18"
|
|
19
|
+
},
|
|
2
20
|
{
|
|
3
21
|
"children": {
|
|
4
22
|
"fixes": [
|
package/locales/ar/oauth.json
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
},
|
|
19
19
|
"permissionsTitle": "طلب الأذونات التالية:",
|
|
20
20
|
"redirectUri": "سيتم إعادة التوجيه إلى بعد نجاح التفويض",
|
|
21
|
+
"redirecting": "تم التفويض بنجاح، جارٍ إعادة التوجيه...",
|
|
21
22
|
"scope": {
|
|
22
23
|
"email": "الوصول إلى عنوان بريدك الإلكتروني",
|
|
23
24
|
"offline_access": "السماح للتطبيق بالوصول إلى بياناتك",
|
package/locales/bg-BG/oauth.json
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
},
|
|
19
19
|
"permissionsTitle": "Искаме следните разрешения:",
|
|
20
20
|
"redirectUri": "Ще бъдете пренасочени след успешното разрешение",
|
|
21
|
+
"redirecting": "Успешно упълномощено, пренасочване...",
|
|
21
22
|
"scope": {
|
|
22
23
|
"email": "Достъп до вашия имейл адрес",
|
|
23
24
|
"offline_access": "Позволете на клиента да получи достъп до вашите данни",
|
package/locales/de-DE/oauth.json
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
},
|
|
19
19
|
"permissionsTitle": "Anforderung der folgenden Berechtigungen:",
|
|
20
20
|
"redirectUri": "Nach erfolgreicher Autorisierung werden Sie umgeleitet zu",
|
|
21
|
+
"redirecting": "Autorisierung erfolgreich, Weiterleitung...",
|
|
21
22
|
"scope": {
|
|
22
23
|
"email": "Zugriff auf Ihre E-Mail-Adresse",
|
|
23
24
|
"offline_access": "Erlauben Sie dem Client, auf Ihre Daten zuzugreifen",
|
package/locales/en-US/oauth.json
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
},
|
|
19
19
|
"permissionsTitle": "Requesting the following permissions:",
|
|
20
20
|
"redirectUri": "You will be redirected to after successful authorization",
|
|
21
|
+
"redirecting": "Authorization successful, redirecting...",
|
|
21
22
|
"scope": {
|
|
22
23
|
"email": "Access your email address",
|
|
23
24
|
"offline_access": "Allow the client to access your data",
|
package/locales/es-ES/oauth.json
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
},
|
|
19
19
|
"permissionsTitle": "Solicitar los siguientes permisos:",
|
|
20
20
|
"redirectUri": "Se redirigirá a después de la autorización exitosa",
|
|
21
|
+
"redirecting": "Autorización exitosa, redirigiendo...",
|
|
21
22
|
"scope": {
|
|
22
23
|
"email": "Acceder a su dirección de correo electrónico",
|
|
23
24
|
"offline_access": "Permitir que el cliente acceda a sus datos",
|
package/locales/fa-IR/oauth.json
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
},
|
|
19
19
|
"permissionsTitle": "درخواست مجوزهای زیر:",
|
|
20
20
|
"redirectUri": "پس از موفقیت در مجوز، به آدرس زیر هدایت میشوید",
|
|
21
|
+
"redirecting": "دسترسی با موفقیت انجام شد، در حال انتقال...",
|
|
21
22
|
"scope": {
|
|
22
23
|
"email": "دسترسی به آدرس ایمیل شما",
|
|
23
24
|
"offline_access": "اجازه دسترسی به دادههای شما توسط کلاینت",
|
package/locales/fr-FR/oauth.json
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
},
|
|
19
19
|
"permissionsTitle": "Demander les autorisations suivantes :",
|
|
20
20
|
"redirectUri": "Vous serez redirigé vers après une autorisation réussie",
|
|
21
|
+
"redirecting": "Autorisation réussie, redirection en cours...",
|
|
21
22
|
"scope": {
|
|
22
23
|
"email": "Accéder à votre adresse e-mail",
|
|
23
24
|
"offline_access": "Autoriser le client à accéder à vos données",
|
package/locales/it-IT/oauth.json
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
},
|
|
19
19
|
"permissionsTitle": "Richiesta delle seguenti autorizzazioni:",
|
|
20
20
|
"redirectUri": "Verrai reindirizzato a dopo un'autorizzazione riuscita",
|
|
21
|
+
"redirecting": "Autorizzazione completata, reindirizzamento in corso...",
|
|
21
22
|
"scope": {
|
|
22
23
|
"email": "Accesso al tuo indirizzo email",
|
|
23
24
|
"offline_access": "Consenti all'accesso del client ai tuoi dati",
|
package/locales/ja-JP/oauth.json
CHANGED
package/locales/ko-KR/oauth.json
CHANGED
package/locales/nl-NL/oauth.json
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
},
|
|
19
19
|
"permissionsTitle": "Vraag om de volgende toestemmingen:",
|
|
20
20
|
"redirectUri": "U wordt omgeleid naar na succesvolle autorisatie",
|
|
21
|
+
"redirecting": "Autorisatie geslaagd, doorverwijzen...",
|
|
21
22
|
"scope": {
|
|
22
23
|
"email": "Toegang tot uw e-mailadres",
|
|
23
24
|
"offline_access": "Toestaan dat de client toegang heeft tot uw gegevens",
|
package/locales/pl-PL/oauth.json
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
},
|
|
19
19
|
"permissionsTitle": "Proszę o następujące uprawnienia:",
|
|
20
20
|
"redirectUri": "Zostaniesz przekierowany po pomyślnym autoryzowaniu",
|
|
21
|
+
"redirecting": "Autoryzacja zakończona pomyślnie, przekierowywanie...",
|
|
21
22
|
"scope": {
|
|
22
23
|
"email": "Dostęp do Twojego adresu e-mail",
|
|
23
24
|
"offline_access": "Zezwól klientowi na dostęp do Twoich danych",
|
package/locales/pt-BR/oauth.json
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
},
|
|
19
19
|
"permissionsTitle": "Solicitar as seguintes permissões:",
|
|
20
20
|
"redirectUri": "Você será redirecionado após a autorização bem-sucedida",
|
|
21
|
+
"redirecting": "Autorização bem-sucedida, redirecionando...",
|
|
21
22
|
"scope": {
|
|
22
23
|
"email": "Acessar seu endereço de e-mail",
|
|
23
24
|
"offline_access": "Permitir que o cliente acesse seus dados",
|
package/locales/ru-RU/oauth.json
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
},
|
|
19
19
|
"permissionsTitle": "Запросить следующие разрешения:",
|
|
20
20
|
"redirectUri": "После успешного авторизации будет выполнен редирект на",
|
|
21
|
+
"redirecting": "Авторизация прошла успешно, перенаправление...",
|
|
21
22
|
"scope": {
|
|
22
23
|
"email": "Доступ к вашему адресу электронной почты",
|
|
23
24
|
"offline_access": "Разрешить клиенту доступ к вашим данным",
|
package/locales/tr-TR/oauth.json
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
},
|
|
19
19
|
"permissionsTitle": "Aşağıdaki izinleri talep et:",
|
|
20
20
|
"redirectUri": "İzin verildikten sonra şu adrese yönlendirileceksiniz",
|
|
21
|
+
"redirecting": "Yetkilendirme başarılı, yönlendiriliyor...",
|
|
21
22
|
"scope": {
|
|
22
23
|
"email": "E-posta adresinize erişim",
|
|
23
24
|
"offline_access": "Müşterinin verilerinize erişmesine izin ver",
|
package/locales/vi-VN/oauth.json
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
},
|
|
19
19
|
"permissionsTitle": "Yêu cầu các quyền sau:",
|
|
20
20
|
"redirectUri": "Sẽ được chuyển hướng đến sau khi ủy quyền thành công",
|
|
21
|
+
"redirecting": "Ủy quyền thành công, đang chuyển hướng...",
|
|
21
22
|
"scope": {
|
|
22
23
|
"email": "Truy cập địa chỉ email của bạn",
|
|
23
24
|
"offline_access": "Cho phép ứng dụng truy cập dữ liệu của bạn",
|
package/locales/zh-CN/oauth.json
CHANGED
package/locales/zh-TW/oauth.json
CHANGED
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.19",
|
|
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",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"build:analyze": "NODE_OPTIONS=--max-old-space-size=6144 ANALYZE=true next build --webpack",
|
|
39
39
|
"build:docker": "npm run prebuild && NODE_OPTIONS=--max-old-space-size=6144 DOCKER=true next build --webpack && npm run build-sitemap",
|
|
40
40
|
"prebuild:electron": "cross-env NEXT_PUBLIC_IS_DESKTOP_APP=1 tsx scripts/prebuild.mts",
|
|
41
|
-
"build:electron": "cross-env NODE_OPTIONS=--max-old-space-size=6144 NEXT_PUBLIC_IS_DESKTOP_APP=1
|
|
41
|
+
"build:electron": "cross-env NODE_OPTIONS=--max-old-space-size=6144 NEXT_PUBLIC_IS_DESKTOP_APP=1 next build --webpack",
|
|
42
42
|
"clean:node_modules": "bash -lc 'set -e; echo \"Removing all node_modules...\"; rm -rf node_modules; pnpm -r exec rm -rf node_modules; rm -rf apps/desktop/node_modules; echo \"All node_modules removed.\"'",
|
|
43
43
|
"db:generate": "drizzle-kit generate && npm run db:generate-client && npm run workflow:dbml",
|
|
44
44
|
"db:generate-client": "tsx ./scripts/migrateClientDB/compile-migrations.ts",
|
|
@@ -10,7 +10,8 @@ import * as schema from '../schemas';
|
|
|
10
10
|
import { LobeChatDatabase } from '../type';
|
|
11
11
|
|
|
12
12
|
export const getDBInstance = (): LobeChatDatabase => {
|
|
13
|
-
|
|
13
|
+
// In test environment, return a mock instance to avoid initialization errors
|
|
14
|
+
if (process.env.NODE_ENV === 'test') return {} as LobeChatDatabase;
|
|
14
15
|
|
|
15
16
|
if (!serverDBEnv.KEY_VAULTS_SECRET) {
|
|
16
17
|
throw new Error(
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
/* eslint-disable sort-keys-fix/sort-keys-fix , typescript-sort-keys/interface */
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
|
|
2
4
|
import { UploadFileItem } from '../../files';
|
|
3
5
|
import { MessageSemanticSearchChunk } from '../../rag';
|
|
4
|
-
import { ChatMessageError } from '../common/base';
|
|
6
|
+
import { ChatMessageError, ChatMessageErrorSchema } from '../common/base';
|
|
5
7
|
import { ChatPluginPayload } from '../common/tools';
|
|
6
|
-
import { UIChatMessage
|
|
8
|
+
import { UIChatMessage } from './chat';
|
|
9
|
+
import { SemanticSearchChunkSchema } from './rag';
|
|
10
|
+
|
|
11
|
+
export type CreateMessageRoleType = 'user' | 'assistant' | 'tool' | 'supervisor';
|
|
7
12
|
|
|
8
13
|
export interface CreateMessageParams
|
|
9
14
|
extends Partial<Omit<UIChatMessage, 'content' | 'role' | 'topicId' | 'chunksList'>> {
|
|
@@ -14,7 +19,7 @@ export interface CreateMessageParams
|
|
|
14
19
|
fromModel?: string;
|
|
15
20
|
fromProvider?: string;
|
|
16
21
|
groupId?: string;
|
|
17
|
-
role:
|
|
22
|
+
role: CreateMessageRoleType;
|
|
18
23
|
sessionId: string;
|
|
19
24
|
targetId?: string | null;
|
|
20
25
|
threadId?: string | null;
|
|
@@ -28,7 +33,7 @@ export interface CreateMessageParams
|
|
|
28
33
|
*/
|
|
29
34
|
export interface CreateNewMessageParams {
|
|
30
35
|
// ========== Required fields ==========
|
|
31
|
-
role:
|
|
36
|
+
role: CreateMessageRoleType;
|
|
32
37
|
content: string;
|
|
33
38
|
sessionId: string;
|
|
34
39
|
|
|
@@ -103,3 +108,92 @@ export interface SendGroupMessageParams {
|
|
|
103
108
|
*/
|
|
104
109
|
targetMemberId?: string | null;
|
|
105
110
|
}
|
|
111
|
+
|
|
112
|
+
// ========== Zod Schemas ========== //
|
|
113
|
+
|
|
114
|
+
const UIMessageRoleTypeSchema = z.enum(['user', 'assistant', 'tool', 'supervisor']);
|
|
115
|
+
|
|
116
|
+
const ChatPluginPayloadSchema = z.object({
|
|
117
|
+
apiName: z.string(),
|
|
118
|
+
arguments: z.string(),
|
|
119
|
+
identifier: z.string(),
|
|
120
|
+
type: z.string(),
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
export const CreateMessageParamsSchema = z
|
|
124
|
+
.object({
|
|
125
|
+
content: z.string(),
|
|
126
|
+
role: UIMessageRoleTypeSchema,
|
|
127
|
+
sessionId: z.string().nullable().optional(),
|
|
128
|
+
error: ChatMessageErrorSchema.nullable().optional(),
|
|
129
|
+
fileChunks: z.array(SemanticSearchChunkSchema).optional(),
|
|
130
|
+
files: z.array(z.string()).optional(),
|
|
131
|
+
fromModel: z.string().optional(),
|
|
132
|
+
fromProvider: z.string().optional(),
|
|
133
|
+
groupId: z.string().optional(),
|
|
134
|
+
targetId: z.string().nullable().optional(),
|
|
135
|
+
threadId: z.string().nullable().optional(),
|
|
136
|
+
topicId: z.string().optional(),
|
|
137
|
+
traceId: z.string().optional(),
|
|
138
|
+
// Allow additional fields from UIChatMessage (many can be null)
|
|
139
|
+
agentId: z.string().optional(),
|
|
140
|
+
children: z.any().optional(),
|
|
141
|
+
chunksList: z.any().optional(),
|
|
142
|
+
createdAt: z.number().optional(),
|
|
143
|
+
extra: z.any().optional(),
|
|
144
|
+
favorite: z.boolean().optional(),
|
|
145
|
+
fileList: z.any().optional(),
|
|
146
|
+
id: z.string().optional(),
|
|
147
|
+
imageList: z.any().optional(),
|
|
148
|
+
meta: z.any().optional(),
|
|
149
|
+
metadata: z.any().nullable().optional(),
|
|
150
|
+
model: z.string().nullable().optional(),
|
|
151
|
+
observationId: z.string().optional(),
|
|
152
|
+
parentId: z.string().optional(),
|
|
153
|
+
performance: z.any().optional(),
|
|
154
|
+
plugin: z.any().optional(),
|
|
155
|
+
pluginError: z.any().optional(),
|
|
156
|
+
pluginState: z.any().optional(),
|
|
157
|
+
provider: z.string().nullable().optional(),
|
|
158
|
+
quotaId: z.string().optional(),
|
|
159
|
+
ragQuery: z.string().nullable().optional(),
|
|
160
|
+
ragQueryId: z.string().nullable().optional(),
|
|
161
|
+
reasoning: z.any().optional(),
|
|
162
|
+
search: z.any().optional(),
|
|
163
|
+
tool_call_id: z.string().optional(),
|
|
164
|
+
toolCalls: z.any().optional(),
|
|
165
|
+
tools: z.any().optional(),
|
|
166
|
+
translate: z.any().optional(),
|
|
167
|
+
tts: z.any().optional(),
|
|
168
|
+
updatedAt: z.number().optional(),
|
|
169
|
+
})
|
|
170
|
+
.passthrough();
|
|
171
|
+
|
|
172
|
+
export const CreateNewMessageParamsSchema = z
|
|
173
|
+
.object({
|
|
174
|
+
// Required fields
|
|
175
|
+
role: UIMessageRoleTypeSchema,
|
|
176
|
+
content: z.string(),
|
|
177
|
+
sessionId: z.string().nullable().optional(),
|
|
178
|
+
// Tool related
|
|
179
|
+
tool_call_id: z.string().optional(),
|
|
180
|
+
plugin: ChatPluginPayloadSchema.optional(),
|
|
181
|
+
// Grouping
|
|
182
|
+
parentId: z.string().optional(),
|
|
183
|
+
groupId: z.string().optional(),
|
|
184
|
+
// Context
|
|
185
|
+
topicId: z.string().optional(),
|
|
186
|
+
threadId: z.string().nullable().optional(),
|
|
187
|
+
targetId: z.string().nullable().optional(),
|
|
188
|
+
// Model info
|
|
189
|
+
model: z.string().nullable().optional(),
|
|
190
|
+
provider: z.string().nullable().optional(),
|
|
191
|
+
// Content
|
|
192
|
+
files: z.array(z.string()).optional(),
|
|
193
|
+
// Error handling
|
|
194
|
+
error: ChatMessageErrorSchema.nullable().optional(),
|
|
195
|
+
// Metadata
|
|
196
|
+
traceId: z.string().optional(),
|
|
197
|
+
fileChunks: z.array(SemanticSearchChunkSchema).optional(),
|
|
198
|
+
})
|
|
199
|
+
.passthrough();
|
|
@@ -3,7 +3,7 @@ import { z } from 'zod';
|
|
|
3
3
|
|
|
4
4
|
import { Plans } from '../subscription';
|
|
5
5
|
import { TopicDisplayMode } from '../topic';
|
|
6
|
-
import { UserSettings } from '
|
|
6
|
+
import { UserSettings } from './settings';
|
|
7
7
|
|
|
8
8
|
export interface LobeUser {
|
|
9
9
|
avatar?: string;
|
|
@@ -74,3 +74,15 @@ export const NextAuthAccountSchame = z.object({
|
|
|
74
74
|
provider: z.string(),
|
|
75
75
|
providerAccountId: z.string(),
|
|
76
76
|
});
|
|
77
|
+
|
|
78
|
+
export const UserPreferenceSchema = z
|
|
79
|
+
.object({
|
|
80
|
+
disableInputMarkdownRender: z.boolean().optional(),
|
|
81
|
+
enableGroupChat: z.boolean().optional(),
|
|
82
|
+
guide: UserGuideSchema.optional(),
|
|
83
|
+
hideSyncAlert: z.boolean().optional(),
|
|
84
|
+
telemetry: z.boolean().nullable(),
|
|
85
|
+
topicDisplayMode: z.nativeEnum(TopicDisplayMode).optional(),
|
|
86
|
+
useCmdEnterToSend: z.boolean().optional(),
|
|
87
|
+
})
|
|
88
|
+
.partial();
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
1
3
|
import type { LobeAgentSettings } from '../../session';
|
|
2
4
|
import { UserGeneralConfig } from './general';
|
|
3
5
|
import { UserHotkeyConfig } from './hotkey';
|
|
@@ -18,6 +20,7 @@ export * from './keyVaults';
|
|
|
18
20
|
export * from './modelProvider';
|
|
19
21
|
export * from './sync';
|
|
20
22
|
export * from './systemAgent';
|
|
23
|
+
export * from './tool';
|
|
21
24
|
export * from './tts';
|
|
22
25
|
|
|
23
26
|
/**
|
|
@@ -34,3 +37,22 @@ export interface UserSettings {
|
|
|
34
37
|
tool: UserToolConfig;
|
|
35
38
|
tts: UserTTSConfig;
|
|
36
39
|
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Zod schema for partial UserSettings updates
|
|
43
|
+
* Uses passthrough to allow any nested settings fields
|
|
44
|
+
*/
|
|
45
|
+
export const UserSettingsSchema = z
|
|
46
|
+
.object({
|
|
47
|
+
defaultAgent: z.any().optional(),
|
|
48
|
+
general: z.any().optional(),
|
|
49
|
+
hotkey: z.any().optional(),
|
|
50
|
+
image: z.any().optional(),
|
|
51
|
+
keyVaults: z.any().optional(),
|
|
52
|
+
languageModel: z.any().optional(),
|
|
53
|
+
systemAgent: z.any().optional(),
|
|
54
|
+
tool: z.any().optional(),
|
|
55
|
+
tts: z.any().optional(),
|
|
56
|
+
})
|
|
57
|
+
.passthrough()
|
|
58
|
+
.partial();
|
|
@@ -15,13 +15,7 @@ if you have any other question, please open issue here: https://github.com/lobeh
|
|
|
15
15
|
const DB_FAIL_INIT_HINT = `------------------------------------------------------------------------------------------
|
|
16
16
|
⚠️ Database migrate failed due to not find the db instance.
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
\`\`\`
|
|
21
|
-
NEXT_PUBLIC_SERVICE_MODE=server
|
|
22
|
-
\`\`\`
|
|
23
|
-
|
|
24
|
-
2) if you are using docker postgres image, you may need to set DATABASE_DRIVER to node
|
|
18
|
+
if you are using docker postgres image, you may need to set DATABASE_DRIVER to node
|
|
25
19
|
|
|
26
20
|
\`\`\`
|
|
27
21
|
DATABASE_DRIVER=node
|
|
@@ -32,9 +32,6 @@ const TopActions = memo<TopActionProps>(({ tab, isPinned }) => {
|
|
|
32
32
|
useServerConfigStore(featureFlagsSelectors);
|
|
33
33
|
const hotkey = useUserStore(settingsSelectors.getHotkeyById(HotkeyEnum.NavigateToChat));
|
|
34
34
|
|
|
35
|
-
// Check if server mode is enabled
|
|
36
|
-
const isServerMode = process.env.NEXT_PUBLIC_SERVICE_MODE === 'server';
|
|
37
|
-
|
|
38
35
|
const isChatActive = tab === SidebarTabKey.Chat && !isPinned;
|
|
39
36
|
const isFilesActive = tab === SidebarTabKey.Files;
|
|
40
37
|
const isDiscoverActive = tab === SidebarTabKey.Discover;
|
|
@@ -69,7 +66,7 @@ const TopActions = memo<TopActionProps>(({ tab, isPinned }) => {
|
|
|
69
66
|
tooltipProps={{ placement: 'right' }}
|
|
70
67
|
/>
|
|
71
68
|
</Link>
|
|
72
|
-
{enableKnowledgeBase &&
|
|
69
|
+
{enableKnowledgeBase && (
|
|
73
70
|
<Link aria-label={t('tab.knowledgeBase')} href={'/knowledge'}>
|
|
74
71
|
<ActionIcon
|
|
75
72
|
active={isFilesActive}
|
|
@@ -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"
|
package/src/config/db.ts
CHANGED
|
@@ -3,9 +3,6 @@ import { z } from 'zod';
|
|
|
3
3
|
|
|
4
4
|
export const getServerDBConfig = () => {
|
|
5
5
|
return createEnv({
|
|
6
|
-
client: {
|
|
7
|
-
NEXT_PUBLIC_ENABLED_SERVER_SERVICE: z.boolean(),
|
|
8
|
-
},
|
|
9
6
|
runtimeEnv: {
|
|
10
7
|
DATABASE_DRIVER: process.env.DATABASE_DRIVER || 'neon',
|
|
11
8
|
DATABASE_TEST_URL: process.env.DATABASE_TEST_URL,
|
|
@@ -13,8 +10,6 @@ export const getServerDBConfig = () => {
|
|
|
13
10
|
|
|
14
11
|
KEY_VAULTS_SECRET: process.env.KEY_VAULTS_SECRET,
|
|
15
12
|
|
|
16
|
-
NEXT_PUBLIC_ENABLED_SERVER_SERVICE: process.env.NEXT_PUBLIC_SERVICE_MODE === 'server',
|
|
17
|
-
|
|
18
13
|
REMOVE_GLOBAL_FILE: process.env.DISABLE_REMOVE_GLOBAL_FILE !== '0',
|
|
19
14
|
},
|
|
20
15
|
server: {
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { NextAuthConfig } from 'next-auth';
|
|
2
2
|
|
|
3
|
-
import { getServerDBConfig } from '@/config/db';
|
|
4
3
|
import { getAuthConfig } from '@/envs/auth';
|
|
5
4
|
|
|
6
5
|
import { LobeNextAuthDbAdapter } from './adapter';
|
|
@@ -14,8 +13,6 @@ const {
|
|
|
14
13
|
NEXT_PUBLIC_ENABLE_NEXT_AUTH,
|
|
15
14
|
} = getAuthConfig();
|
|
16
15
|
|
|
17
|
-
const { NEXT_PUBLIC_ENABLED_SERVER_SERVICE } = getServerDBConfig();
|
|
18
|
-
|
|
19
16
|
export const initSSOProviders = () => {
|
|
20
17
|
return NEXT_PUBLIC_ENABLE_NEXT_AUTH
|
|
21
18
|
? NEXT_AUTH_SSO_PROVIDERS.split(/[,,]/).map((provider) => {
|
|
@@ -30,7 +27,7 @@ export const initSSOProviders = () => {
|
|
|
30
27
|
|
|
31
28
|
// Notice this is only an object, not a full Auth.js instance
|
|
32
29
|
export default {
|
|
33
|
-
adapter:
|
|
30
|
+
adapter: NEXT_PUBLIC_ENABLE_NEXT_AUTH ? LobeNextAuthDbAdapter() : undefined,
|
|
34
31
|
callbacks: {
|
|
35
32
|
// Note: Data processing order of callback: authorize --> jwt --> session
|
|
36
33
|
async jwt({ token, user }) {
|
|
@@ -61,7 +58,7 @@ export default {
|
|
|
61
58
|
secret: NEXT_AUTH_SECRET ?? process.env.AUTH_SECRET,
|
|
62
59
|
session: {
|
|
63
60
|
// Force use JWT if server service is disabled
|
|
64
|
-
strategy:
|
|
61
|
+
strategy: NEXT_AUTH_SSO_SESSION_STRATEGY,
|
|
65
62
|
},
|
|
66
63
|
trustHost: process.env?.AUTH_TRUST_HOST ? process.env.AUTH_TRUST_HOST === 'true' : true,
|
|
67
64
|
} satisfies NextAuthConfig;
|
|
@@ -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[]>;
|