@lobehub/chat 1.12.4 → 1.12.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/.env.example +1 -1
  2. package/CHANGELOG.md +50 -0
  3. package/Dockerfile.database +1 -0
  4. package/docs/self-hosting/advanced/auth.mdx +8 -10
  5. package/docs/self-hosting/advanced/s3/cloudflare-r2.mdx +2 -2
  6. package/docs/self-hosting/advanced/s3/cloudflare-r2.zh-CN.mdx +2 -2
  7. package/docs/self-hosting/advanced/s3.mdx +39 -0
  8. package/docs/self-hosting/advanced/s3.zh-CN.mdx +1 -1
  9. package/docs/self-hosting/server-database/docker.mdx +32 -22
  10. package/docs/self-hosting/server-database/docker.zh-CN.mdx +2 -2
  11. package/docs/self-hosting/server-database/vercel.mdx +4 -4
  12. package/docs/self-hosting/server-database/vercel.zh-CN.mdx +6 -6
  13. package/docs/self-hosting/server-database.mdx +10 -37
  14. package/locales/ar/error.json +4 -1
  15. package/locales/bg-BG/error.json +4 -1
  16. package/locales/de-DE/error.json +4 -1
  17. package/locales/en-US/error.json +4 -1
  18. package/locales/es-ES/error.json +4 -1
  19. package/locales/fr-FR/error.json +4 -1
  20. package/locales/it-IT/error.json +4 -1
  21. package/locales/ja-JP/error.json +4 -1
  22. package/locales/ko-KR/error.json +4 -1
  23. package/locales/nl-NL/error.json +4 -1
  24. package/locales/pl-PL/error.json +4 -1
  25. package/locales/pt-BR/error.json +4 -1
  26. package/locales/ru-RU/error.json +4 -1
  27. package/locales/tr-TR/error.json +4 -1
  28. package/locales/vi-VN/error.json +4 -1
  29. package/locales/zh-CN/error.json +4 -1
  30. package/locales/zh-TW/error.json +4 -1
  31. package/package.json +2 -2
  32. package/src/components/DragUpload/useDragUpload.test.tsx +130 -0
  33. package/src/components/DragUpload/useDragUpload.tsx +9 -0
  34. package/src/config/file.ts +16 -1
  35. package/src/locales/default/error.ts +3 -0
  36. package/src/server/modules/S3/index.ts +1 -0
  37. package/src/server/routers/lambda/file.ts +1 -3
  38. package/src/server/utils/files.ts +1 -1
  39. package/src/services/file/server.ts +4 -0
  40. package/src/services/upload.ts +6 -2
  41. package/src/store/file/slices/chat/action.ts +27 -4
package/.env.example CHANGED
@@ -150,7 +150,7 @@ OPENAI_API_KEY=sk-xxxxxxxxx
150
150
  #S3_ENDPOINT=https://0b33a03b5c993fd2f453379dc36558e5.r2.cloudflarestorage.com
151
151
 
152
152
  # Public access domain for the bucket
153
- #NEXT_PUBLIC_S3_DOMAIN=https://s3-for-lobechat.your-domain.com
153
+ #S3_PUBLIC_DOMAIN=https://s3-for-lobechat.your-domain.com
154
154
 
155
155
  # Bucket region, such as us-west-1, generally not needed to add
156
156
  # but some service providers may require configuration
package/CHANGELOG.md CHANGED
@@ -2,6 +2,56 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ### [Version 1.12.6](https://github.com/lobehub/lobe-chat/compare/v1.12.5...v1.12.6)
6
+
7
+ <sup>Released on **2024-08-22**</sup>
8
+
9
+ #### ♻ Code Refactoring
10
+
11
+ - **misc**: Refactor s3 env and support path-style for minio.
12
+
13
+ <br/>
14
+
15
+ <details>
16
+ <summary><kbd>Improvements and Fixes</kbd></summary>
17
+
18
+ #### Code refactoring
19
+
20
+ - **misc**: Refactor s3 env and support path-style for minio, closes [#3559](https://github.com/lobehub/lobe-chat/issues/3559) ([1658403](https://github.com/lobehub/lobe-chat/commit/1658403))
21
+
22
+ </details>
23
+
24
+ <div align="right">
25
+
26
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
27
+
28
+ </div>
29
+
30
+ ### [Version 1.12.5](https://github.com/lobehub/lobe-chat/compare/v1.12.4...v1.12.5)
31
+
32
+ <sup>Released on **2024-08-22**</sup>
33
+
34
+ #### 🐛 Bug Fixes
35
+
36
+ - **misc**: Fix clipboard copy issue and improve upload cors feedback.
37
+
38
+ <br/>
39
+
40
+ <details>
41
+ <summary><kbd>Improvements and Fixes</kbd></summary>
42
+
43
+ #### What's fixed
44
+
45
+ - **misc**: Fix clipboard copy issue and improve upload cors feedback, closes [#3557](https://github.com/lobehub/lobe-chat/issues/3557) ([86c5a99](https://github.com/lobehub/lobe-chat/commit/86c5a99))
46
+
47
+ </details>
48
+
49
+ <div align="right">
50
+
51
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
52
+
53
+ </div>
54
+
5
55
  ### [Version 1.12.4](https://github.com/lobehub/lobe-chat/compare/v1.12.3...v1.12.4)
6
56
 
7
57
  <sup>Released on **2024-08-22**</sup>
@@ -127,6 +127,7 @@ ENV NEXT_AUTH_SECRET="" \
127
127
 
128
128
  # S3
129
129
  ENV NEXT_PUBLIC_S3_DOMAIN="" \
130
+ S3_PUBLIC_DOMAIN="" \
130
131
  S3_ACCESS_KEY_ID="" \
131
132
  S3_BUCKET="" \
132
133
  S3_ENDPOINT="" \
@@ -1,19 +1,16 @@
1
1
  ---
2
- title: LobeChat Authorization Service
3
- description: >-
4
- Learn about LobeChat's support for configuring external identity verification
5
- services for centralized user authorization within enterprises/organizations.
6
- Explore supported services like Auth0, Microsoft Entra ID, Authentik, Github,
7
- and ZITADEL.
2
+ title: LobeChat Authentication Service Configuration
3
+ description: Learn how to configure external authentication services using Clerk or Next Auth for centralized user authorization management. Supported authentication services include Auth0, Azure ID, etc.
8
4
  tags:
9
- - SSO Providers
5
+ - Authentication Service
10
6
  - Next Auth
7
+ - SSO
11
8
  - Clerk
12
9
  ---
13
10
 
14
- # LobeChat Authorization
11
+ # Authentication Service
15
12
 
16
- LobeChat supports the configuration of external identity verification services for internal use within enterprises/organizations to centrally manage user authorization.
13
+ LobeChat supports the configuration of external authentication services using Clerk or Next Auth for internal use within enterprises/organizations to centrally manage user authorization.
17
14
 
18
15
  ## Clerk
19
16
 
@@ -21,7 +18,7 @@ Clerk is a comprehensive identity verification solution that has recently gained
21
18
 
22
19
  LobeChat has deeply integrated with Clerk to provide users with a more secure and convenient login and registration experience. It also relieves developers from the burden of managing authentication logic. Clerk's concise and modern design philosophy aligns perfectly with LobeChat's goals, making user management on the entire platform more efficient and reliable.
23
20
 
24
- By setting the environment variables NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY and CLERK_SECRET_KEY in LobeChat's environment, you can enable and use Clerk.
21
+ By setting the environment variables `NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY` and `CLERK_SECRET_KEY` in LobeChat's environment, you can enable and use Clerk.
25
22
 
26
23
  ## Next Auth
27
24
 
@@ -45,6 +42,7 @@ Currently supported identity verification services include:
45
42
  <Card href={'/docs/self-hosting/advanced/sso-providers/github'} title={'Github'} />
46
43
  <Card href={'/docs/self-hosting/advanced/sso-providers/zitadel'} title={'ZITADEL'} />
47
44
  </Cards>
45
+
48
46
  Click on the links to view the corresponding platform's configuration documentation.
49
47
 
50
48
  ## Advanced Configuration
@@ -48,7 +48,7 @@ S3_BUCKET=lobechat
48
48
  # Request endpoint of the bucket (note that the path in this link includes the bucket name, which must be removed, or use the link provided on the page for applying S3 API token)
49
49
  S3_ENDPOINT=https://0b33a03b5c993fd2f453379dc36558e5.r2.cloudflarestorage.com
50
50
  # Access domain of the bucket
51
- NEXT_PUBLIC_S3_DOMAIN=https://s3-for-lobechat.your-domain.com
51
+ S3_PUBLIC_DOMAIN=https://s3-for-lobechat.your-domain.com
52
52
  ```
53
53
 
54
54
  <Callout type={'warning'}>`S3_ENDPOINT` must have its path removed, otherwise uploaded files cannot be accessed.</Callout>
@@ -138,7 +138,7 @@ S3_BUCKET=lobechat
138
138
  # Bucket Request Endpoint
139
139
  S3_ENDPOINT=https://0b33a03b5c993fd2f453379dc36558e5.r2.cloudflarestorage.com
140
140
  # Public Access Domain for the Bucket
141
- NEXT_PUBLIC_S3_DOMAIN=https://s3-dev.your-domain.com
141
+ S3_PUBLIC_DOMAIN=https://s3-dev.your-domain.com
142
142
 
143
143
  # Bucket Region, such as us-west-1. Generally not required, but some service providers may need it.
144
144
  # S3_REGION=us-west-1
@@ -49,7 +49,7 @@ S3_BUCKET=lobechat
49
49
  # 存储桶的请求端点(注意此处链接的路径带存储桶名称,必须删除该路径,或使用申请 S3 API token 页面所提供的链接)
50
50
  S3_ENDPOINT=https://0b33a03b5c993fd2f453379dc36558e5.r2.cloudflarestorage.com
51
51
  # 存储桶对外的访问域名
52
- NEXT_PUBLIC_S3_DOMAIN=https://s3-for-lobechat.your-domain.com
52
+ S3_PUBLIC_DOMAIN=https://s3-for-lobechat.your-domain.com
53
53
  ```
54
54
 
55
55
  <Callout type={'warning'}>`S3_ENDPOINT`必须删除其路径,否则会无法访问所上传文件</Callout>
@@ -139,7 +139,7 @@ S3_BUCKET=lobechat
139
139
  # 存储桶的请求端点
140
140
  S3_ENDPOINT=https://0b33a03b5c993fd2f453379dc36558e5.r2.cloudflarestorage.com
141
141
  # 存储桶对外的访问域名
142
- NEXT_PUBLIC_S3_DOMAIN=https://s3-dev.your-domain.com
142
+ S3_PUBLIC_DOMAIN=https://s3-dev.your-domain.com
143
143
 
144
144
  # 桶的区域,如 us-west-1,一般来说不需要添加,但某些服务商则需要配置
145
145
  # S3_REGION=us-west-1
@@ -18,6 +18,45 @@ The best practice in this area is to use a file storage service (S3) to store im
18
18
  In this documentation, S3 refers to a compatible S3 storage solution, which supports the Amazon S3 API for object storage systems. Common examples include Cloudflare R2, Alibaba Cloud OSS, and self-deployable Minio, all of which support the S3 compatible API.
19
19
  </Callout>
20
20
 
21
+ ## Core Environment Variables
22
+
23
+ <Steps>
24
+ ### `S3_ACCESS_KEY_ID` and `S3_SECRET_ACCESS_KEY`
25
+
26
+ These are the two keys required by all S3 compatible storage services to access the S3 storage service, not detailed here.
27
+
28
+ ### `S3_ENDPOINT`
29
+
30
+ The request endpoint of the storage bucket. Note that this link should not contain the name of the storage bucket.
31
+
32
+ <Callout type={'warning'}>`S3_ENDPOINT` must remove the suffix path, otherwise the uploaded files will not be accessible</Callout>
33
+
34
+ For example, for Cloudflare:
35
+
36
+ ```shell
37
+ S3_ENDPOINT=https://0b33a03b5c993fd2f453379dc36558e5.r2.cloudflarestorage.com
38
+ ```
39
+
40
+ ### `S3_BUCKET` and `S3_REGION`
41
+
42
+ The name and region of the storage bucket. `S3_BUCKET` is required to specify the name of the storage bucket. `S3_REGION` is optional and is used to specify the region of the storage bucket. Generally, it does not need to be added, but some service providers may require configuration.
43
+
44
+ ### `S3_SET_ACL`
45
+
46
+ Whether to set the ACL to `public-read` when uploading files. This option is enabled by default. If the service provider does not support setting individual ACLs for files (i.e., all files inherit the ACL of the storage bucket), enabling this option may cause request errors. Set `S3_SET_ACL` to `0` to disable it.
47
+
48
+ ### `S3_PUBLIC_DOMAIN`
49
+
50
+ The public access domain of the storage bucket, used to access files in the storage bucket. This address needs to be **publicly readable**. The reason is that when OpenAI's gpt-4o and other vision models recognize images, OpenAI will try to download this image link on their servers. Therefore, this link must be publicly accessible. If it is a private link, OpenAI will not be able to access the image and thus will not be able to recognize the image content properly.
51
+
52
+ <Callout type={'warning'}>
53
+ Additionally, since this access domain is often a separate URL, it needs to be configured to allow cross-origin access to the site. Otherwise, cross-origin issues will occur in the browser.
54
+ </Callout>
55
+
56
+ </Steps>
57
+
58
+ ## S3 Configuration Guide
59
+
21
60
  Currently, the S3 configuration tutorials included in the documentation are:
22
61
 
23
62
  <Cards>
@@ -46,7 +46,7 @@ S3_ENDPOINT=https://0b33a03b5c993fd2f453379dc36558e5.r2.cloudflarestorage.com
46
46
 
47
47
  是否在上传文件时设置 ACL 为 `public-read`。该选项默认启用。如果服务商不支持为文件设置单独的 ACL(即所有文件继承存储桶的 ACL),启用此选项可能会导致请求错误,将 `S3_SET_ACL` 设置为 `0` 即可关闭。
48
48
 
49
- ### `NEXT_PUBLIC_S3_DOMAIN`
49
+ ### `S3_PUBLIC_DOMAIN`
50
50
 
51
51
  存储桶对外的访问域名,用于访问存储桶中的文件,这个地址需要**允许互联网可读**。 原因是 OpenAI 的 gpt-4o 等视觉模型识别图片时,OpenAI 会尝试在他们的服务器中下载这个图片链接,因此这个链接必须是公开可访问的,如果是私有的链接,OpenAI 将无法访问到这个图片,进而无法正常识别到图片内容。
52
52
 
@@ -32,26 +32,35 @@ Here is the process for deploying the LobeChat server database version on a Linu
32
32
 
33
33
  ### Create a Postgres Database Instance
34
34
 
35
- Create a Postgres database instance according to your needs, for example:
35
+ Please create a Postgres database instance with the PGVector plugin according to your needs, for example:
36
36
 
37
37
  ```sh
38
- docker run --name my-postgres --network pg -e POSTGRES_PASSWORD=mysecretpassword -p 5432:5432 -d postgres
38
+ docker network create pg
39
+
40
+ docker run --name my-postgres --network pg -e POSTGRES_PASSWORD=mysecretpassword -p 5432:5432 -d pgvector/pgvector:pg16
39
41
  ```
40
42
 
41
- <Callout type="warning">
42
- The above command is only for testing/demonstration purposes because this pg instance does not include a persistent part.
43
+ The above command will create a PG instance named `my-postgres` on the network `pg`, where `pgvector/pgvector:pg16` is a Postgres 16 image with the pgvector plugin installed by default.
44
+
45
+ <Callout type="info">
46
+ The pgvector plugin provides vector search capabilities for Postgres, which is an important component for LobeChat to implement RAG.
43
47
  </Callout>
44
48
 
45
- The above command will create a PG instance named `my-postgres` on the network `pg`.
49
+ <Callout type="warning">
50
+ The above command does not specify a persistent storage location for the pg instance, so it is only for testing/demonstration purposes. Please configure persistent storage for production environments.
51
+ </Callout>
46
52
 
47
53
  ### Create a file named `lobe-chat.env` to store environment variables:
48
54
 
49
55
  ```shell
50
- # DB required
56
+ # Website domain
57
+ APP_URL=https://your-prod-domain.com
58
+
59
+ # DB required environment variables
51
60
  KEY_VAULTS_SECRET=jgwsK28dspyVQoIf8/M3IIHl1h6LYYceSYNXeLpy6uk=
52
61
  DATABASE_URL=postgres://postgres:mysecretpassword@my-postgres:5432/postgres
53
62
 
54
- # NEXT_AUTH related
63
+ # NEXT_AUTH related, can use auth0, Azure AD, GitHub, Authentik, zitadel, etc. If you have other access requirements, feel free to submit a PR
55
64
  NEXT_AUTH_SECRET=3904039cd41ea1bdf6c93db0db96e250
56
65
  NEXT_AUTH_SSO_PROVIDERS=auth0
57
66
  NEXTAUTH_URL=https://your-prod-domain.com/api/auth
@@ -64,7 +73,7 @@ S3_ACCESS_KEY_ID=xxxxxxxxxx
64
73
  S3_SECRET_ACCESS_KEY=xxxxxxxxxx
65
74
  S3_ENDPOINT=https://xxxxxxxxxx.r2.cloudflarestorage.com
66
75
  S3_BUCKET=lobechat
67
- NEXT_PUBLIC_S3_DOMAIN=https://s3-for-lobechat.your-domain.com
76
+ S3_PUBLIC_DOMAIN=https://s3-for-lobechat.your-domain.com
68
77
  ```
69
78
 
70
79
  ### Start the lobe-chat-database Docker image
@@ -103,20 +112,21 @@ The script command you need to execute is:
103
112
 
104
113
  ```shell
105
114
  $ docker run -it -d --name lobe-chat-database -p 3210:3210 \
106
- -e DATABASE_URL=postgres://postgres:mysecretpassword@host.docker.internal:5432/postgres \
107
- -e KEY_VAULTS_SECRET=jgwsK28dspyVQoIf8/M3IIHl1h6LYYceSYNXeLpy6uk= \
108
- -e NEXT_AUTH_SECRET=3904039cd41ea1bdf6c93db0db96e250 \
109
- -e NEXT_AUTH_SSO_PROVIDERS=auth0 \
110
- -e AUTH0_CLIENT_ID=xxxxxx \
111
- -e AUTH0_CLIENT_SECRET=cSX_xxxxx \
112
- -e AUTH0_ISSUER=https://lobe-chat-demo.us.auth0.com \
113
- -e NEXTAUTH_URL=http://localhost:3210/api/auth \
114
- -e S3_ACCESS_KEY_ID=xxxxxxxxxx \
115
- -e S3_SECRET_ACCESS_KEY=xxxxxxxxxx \
116
- -e S3_ENDPOINT=https://xxxxxxxxxx.r2.cloudflarestorage.com \
117
- -e S3_BUCKET=lobechat \
118
- -e NEXT_PUBLIC_S3_DOMAIN=https://s3-for-lobechat.your-domain.com \
119
- lobehub/lobe-chat-database
115
+ -e DATABASE_URL=postgres://postgres:mysecretpassword@host.docker.internal:5432/postgres \
116
+ -e KEY_VAULTS_SECRET=jgwsK28dspyVQoIf8/M3IIHl1h6LYYceSYNXeLpy6uk= \
117
+ -e NEXT_AUTH_SECRET=3904039cd41ea1bdf6c93db0db96e250 \
118
+ -e NEXT_AUTH_SSO_PROVIDERS=auth0 \
119
+ -e AUTH0_CLIENT_ID=xxxxxx \
120
+ -e AUTH0_CLIENT_SECRET=cSX_xxxxx \
121
+ -e AUTH0_ISSUER=https://lobe-chat-demo.us.auth0.com \
122
+ -e APP_URL=http://localhost:3210 \
123
+ -e NEXTAUTH_URL=http://localhost:3210/api/auth \
124
+ -e S3_ACCESS_KEY_ID=xxxxxxxxxx \
125
+ -e S3_SECRET_ACCESS_KEY=xxxxxxxxxx \
126
+ -e S3_ENDPOINT=https://xxxxxxxxxx.r2.cloudflarestorage.com \
127
+ -e S3_BUCKET=lobechat \
128
+ -e S3_PUBLIC_DOMAIN=https://s3-for-lobechat.your-domain.com \
129
+ lobehub/lobe-chat-database
120
130
  ```
121
131
 
122
132
  <Callout type="tip">
@@ -75,7 +75,7 @@ S3_ACCESS_KEY_ID=xxxxxxxxxx
75
75
  S3_SECRET_ACCESS_KEY=xxxxxxxxxx
76
76
  S3_ENDPOINT=https://xxxxxxxxxx.r2.cloudflarestorage.com
77
77
  S3_BUCKET=lobechat
78
- NEXT_PUBLIC_S3_DOMAIN=https://s3-for-lobechat.your-domain.com
78
+ S3_PUBLIC_DOMAIN=https://s3-for-lobechat.your-domain.com
79
79
  ```
80
80
 
81
81
  ### 启动 lobe-chat-database docker 镜像
@@ -130,7 +130,7 @@ $ docker run -it -d --name lobe-chat-database -p 3210:3210 \
130
130
  -e S3_SECRET_ACCESS_KEY=xxxxxxxxxx \
131
131
  -e S3_ENDPOINT=https://xxxxxxxxxx.r2.cloudflarestorage.com \
132
132
  -e S3_BUCKET=lobechat \
133
- -e NEXT_PUBLIC_S3_DOMAIN=https://s3-for-lobechat.your-domain.com \
133
+ -e S3_PUBLIC_DOMAIN=https://s3-for-lobechat.your-domain.com \
134
134
  lobehub/lobe-chat-database
135
135
  ```
136
136
 
@@ -250,7 +250,7 @@ S3_BUCKET=lobechat
250
250
  # Storage bucket request endpoint (note that the path in this link includes the bucket name, which must be removed, or use the link provided on the S3 API token application page)
251
251
  S3_ENDPOINT=https://0b33a03b5c993fd2f453379dc36558e5.r2.cloudflarestorage.com
252
252
  # Public access domain for the storage bucket
253
- NEXT_PUBLIC_S3_DOMAIN=https://s3-for-lobechat.your-domain.com
253
+ S3_PUBLIC_DOMAIN=https://s3-for-lobechat.your-domain.com
254
254
  ```
255
255
 
256
256
  <Callout type={'warning'}>
@@ -312,7 +312,7 @@ S3_BUCKET=lobechat
312
312
  # Bucket request endpoint
313
313
  S3_ENDPOINT=https://0b33a03b5c993fd2f453379dc36558e5.r2.cloudflarestorage.com
314
314
  # Public domain for bucket access
315
- NEXT_PUBLIC_S3_DOMAIN=https://s3-dev.your-domain.com
315
+ S3_PUBLIC_DOMAIN=https://s3-dev.your-domain.com
316
316
 
317
317
  # Bucket region, such as us-west-1, generally not required, but some providers may need to configure
318
318
  # S3_REGION=us-west-1
@@ -384,7 +384,7 @@ src={'https://github.com/lobehub/lobe-chat/assets/28616219/da84edc3-46f7-4e2b-a0
384
384
  <Image
385
385
  alt={'Login successful state'}
386
386
  src={'https://github.com/lobehub/lobe-chat/assets/28616219/9cb5150d-6e1e-4c59-9a18-4e418dce1a5d'}/>
387
-
387
+
388
388
  </Steps>
389
389
 
390
390
  ## Appendix
@@ -416,7 +416,7 @@ S3_BUCKET=lobechat
416
416
  # Bucket request endpoint
417
417
  S3_ENDPOINT=https://0b33a03b5c993fd2f453379dc36558e5.r2.cloudflarestorage.com
418
418
  # Public access domain for the bucket
419
- NEXT_PUBLIC_S3_DOMAIN=https://s3-for-lobechat.your-domain.com
419
+ S3_PUBLIC_DOMAIN=https://s3-for-lobechat.your-domain.com
420
420
  # Bucket region, such as us-west-1, generally not needed to add, but some service providers may require configuration
421
421
  # S3_REGION=us-west-1
422
422
  ```
@@ -18,8 +18,8 @@ tags:
18
18
 
19
19
  <Callout type={'warning'}>
20
20
  进行后续操作前,请务必确认以下事项:
21
- - 导出所有数据,部署服务端数据库后,原有用户数据无法自动迁移,只能提前备份后进行手动导入!
22
- - 环境变量中的`ACCESS_CODE`未设置或已清除!
21
+ - 导出所有数据,部署服务端数据库后,原有用户数据无法自动迁移,只能提前备份后进行手动导入!
22
+ - 环境变量中的`ACCESS_CODE`未设置或已清除!
23
23
  - 配置服务端数据库所需要的环境变量时,需全部填入后再进行部署,否则可能遭遇数据库迁移问题!
24
24
  </Callout>
25
25
 
@@ -46,7 +46,7 @@ tags:
46
46
 
47
47
  <Callout type={'warning'}>
48
48
  请确认您的供应商所提供的 `Postgres` 类型,若为 `Node Postgres`,请切换到 `Node Postgres` Tab 。
49
-
49
+
50
50
  </Callout>
51
51
 
52
52
  Serverless Postgres 需要填写的变量如下:
@@ -233,7 +233,7 @@ S3_BUCKET=lobechat
233
233
  # 存储桶的请求端点(注意此处链接的路径带存储桶名称,必须删除该路径,或使用申请 S3 API token 页面所提供的链接)
234
234
  S3_ENDPOINT=https://0b33a03b5c993fd2f453379dc36558e5.r2.cloudflarestorage.com
235
235
  # 存储桶对外的访问域名
236
- NEXT_PUBLIC_S3_DOMAIN=https://s3-for-lobechat.your-domain.com
236
+ S3_PUBLIC_DOMAIN=https://s3-for-lobechat.your-domain.com
237
237
  ```
238
238
 
239
239
  <Callout type={'warning'}>`S3_ENDPOINT`必须删除其路径,否则会无法访问所上传文件</Callout>
@@ -291,7 +291,7 @@ S3_BUCKET=lobechat
291
291
  # 存储桶的请求端点
292
292
  S3_ENDPOINT=https://0b33a03b5c993fd2f453379dc36558e5.r2.cloudflarestorage.com
293
293
  # 存储桶对外的访问域名
294
- NEXT_PUBLIC_S3_DOMAIN=https://s3-dev.your-domain.com
294
+ S3_PUBLIC_DOMAIN=https://s3-dev.your-domain.com
295
295
 
296
296
  # 桶的区域,如 us-west-1,一般来说不需要添加,但某些服务商则需要配置
297
297
  # S3_REGION=us-west-1
@@ -397,7 +397,7 @@ S3_BUCKET=lobechat
397
397
  # 存储桶的请求端点
398
398
  S3_ENDPOINT=https://0b33a03b5c993fd2f453379dc36558e5.r2.cloudflarestorage.com
399
399
  # 存储桶对外的访问域名
400
- NEXT_PUBLIC_S3_DOMAIN=https://s3-for-lobechat.your-domain.com
400
+ S3_PUBLIC_DOMAIN=https://s3-for-lobechat.your-domain.com
401
401
  # 桶的区域,如 us-west-1,一般来说不需要添加,但某些服务商则需要配置
402
402
  # S3_REGION=us-west-1
403
403
  ```
@@ -36,7 +36,9 @@ Before deployment, make sure you have a Postgres database instance ready. You ca
36
36
  - `A.` Use Serverless Postgres instances like Vercel/Neon;
37
37
  - `B.` Use self-deployed Postgres instances like Docker/Railway/Zeabur, collectively referred to as Node Postgres instances;
38
38
 
39
- There is a slight difference in the way they are configured in terms of environment variables.
39
+ <Callout>There is a slight difference in the way they are configured in terms of environment variables.</Callout>
40
+
41
+ Since we support file-based conversations/knowledge base conversations, we need to install the `pgvector` plugin for Postgres. This plugin provides vector search capabilities and is a key component for LobeChat to implement RAG.
40
42
 
41
43
  <Steps>
42
44
  ### `NEXT_PUBLIC_SERVICE_MODE`
@@ -49,6 +51,12 @@ For server-side database deployment scenarios, you need to set `NEXT_PUBLIC_SERV
49
51
  In the official `lobe-chat-database` Docker image, this environment variable is already set to `server` by default. Therefore, if you deploy using the Docker image, you do not need to configure this environment variable again.
50
52
  </Callout>
51
53
 
54
+ <Callout type={'tip'}>
55
+ Since environment variables starting with `NEXT_PUBLIC` take effect in the front-end code, they cannot be modified through container runtime injection. (Refer to the `next.js` documentation [Configuring: Environment Variables | Next.js (nextjs.org)](https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables)). This is why we chose to create a separate DB version image.
56
+
57
+ If you need to modify variables with the `NEXT_PUBLIC` prefix in a Docker deployment, you must build the image yourself and inject your own `NEXT_PUBLIC` prefixed environment variables during the build.
58
+ </Callout>
59
+
52
60
  ### `DATABASE_URL`
53
61
 
54
62
  The core of configuring the database is to add the `DATABASE_URL` environment variable and fill in the Postgres database connection URL you have prepared. The typical format of the database connection URL is `postgres://username:password@host:port/database`.
@@ -87,7 +95,7 @@ In the server-side database mode, we need an authentication service to distingui
87
95
 
88
96
  ### Clerk
89
97
 
90
- [Clerk](https://clerk.com?utm_source=lobehub\&utm_medium=docs) is an authentication SaaS service that provides out-of-the-box authentication capabilities with high productization, low integration costs, and a great user experience. For those who offer SaaS products, Clerk is a good choice. Our official [LobeChat Cloud](https://lobechat.com) uses Clerk as the authentication service.
98
+ [Clerk](https://clerk.com?utm_source=lobehub&utm_medium=docs) is an authentication SaaS service that provides out-of-the-box authentication capabilities with high productization, low integration costs, and a great user experience. For those who offer SaaS products, Clerk is a good choice. Our official [LobeChat Cloud](https://lobechat.com) uses Clerk as the authentication service.
91
99
 
92
100
  The integration of Clerk is relatively simple, requiring only the configuration of the `NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY`, `CLERK_SECRET_KEY`, and `CLERK_WEBHOOK_SECRET` environment variables, which can be obtained from the Clerk console.
93
101
 
@@ -123,40 +131,6 @@ The best practice in this area is to use a file storage service (S3) to store im
123
131
  In this documentation, S3 refers to a compatible S3 storage solution, which supports the Amazon S3 API-compatible object storage system. Common examples include Cloudflare R2, Alibaba Cloud OSS, and self-deployable Minio, all of which support the S3-compatible API.
124
132
  </Callout>
125
133
 
126
- <Steps>
127
- ### `S3_ACCESS_KEY_ID` and `S3_SECRET_ACCESS_KEY`
128
-
129
- These are the two keys required by all S3-compatible storage services to access the S3 storage service, without going into detail.
130
-
131
- ### `S3_ENDPOINT`
132
-
133
- The request endpoint of the bucket, note that the link here should not include the bucket's name.
134
-
135
- <Callout type={'warning'}>`S3_ENDPOINT` must remove the suffix path, otherwise the uploaded files will not be accessible.</Callout>
136
-
137
- For example, for Cloudflare:
138
-
139
- ```shell
140
- S3_ENDPOINT=https://0b33a03b5c993fd2f453379dc36558e5.r2.cloudflarestorage.com
141
- ```
142
-
143
- ### `S3_BUCKET` and `S3_REGION`
144
-
145
- The name and region of the bucket. `S3_BUCKET` is mandatory for specifying the bucket's name. `S3_REGION` is optional for specifying the bucket's region, generally not required to add, but some service providers may need to configure it.
146
-
147
- ### `S3_SET_ACL`
148
- Whether to set the ACL to `public-read` when uploading files. This option is enabled by default. If the service provider does not support setting individual ACLs for files (i.e., all files inherit the bucket's ACL), enabling this option may result in a request error. You can disable it by setting `S3_SET_ACL` to `0`.
149
-
150
- ### `NEXT_PUBLIC_S3_DOMAIN`
151
-
152
- The public access domain of the bucket, used to access files in the bucket. This address needs to be **internet-readable**. The reason is that when OpenAI's GPT-4o and other visual models recognize images, OpenAI will try to download the image link on their servers. Therefore, this link must be publicly accessible. If it is a private link, OpenAI will not be able to access the image and will not be able to recognize the image content properly.
153
-
154
- <Callout type={'warning'}>
155
- In addition, since this access domain is often an independent URL, it needs to be configured to allow cross-origin access to the site, otherwise cross-origin issues will occur in the browser.
156
- </Callout>
157
-
158
- </Steps>
159
-
160
134
  For detailed configuration guidelines on S3, please refer to [S3 Object Storage](/en/docs/self-hosting/advanced/s3) for more information.
161
135
 
162
136
  ## Getting Started with Deployment
@@ -164,4 +138,3 @@ For detailed configuration guidelines on S3, please refer to [S3 Object Storage]
164
138
  The above is a detailed explanation of configuring LobeChat with a server-side database. You can configure it according to your actual situation and then choose a deployment platform that suits you to start deployment:
165
139
 
166
140
  <PlatformCards urlPrefix={'server-database'} />
167
-
@@ -116,6 +116,9 @@
116
116
  "upload": {
117
117
  "desc": "التفاصيل: {{detail}}",
118
118
  "fileOnlySupportInServerMode": "وضع النشر الحالي لا يدعم تحميل ملفات غير الصور. إذا كنت بحاجة إلى تحميل تنسيق {{ext}}، يرجى التبديل إلى نشر قاعدة بيانات الخادم أو استخدام خدمة LobeChat Cloud.",
119
- "title": "فشل تحميل الملف، يرجى التحقق من الاتصال بالشبكة أو المحاولة لاحقًا"
119
+ "networkError": "يرجى التأكد من أن اتصال الشبكة لديك يعمل بشكل صحيح، والتحقق من إعدادات تكوين خدمة تخزين الملفات عبر النطاق.",
120
+ "title": "فشل تحميل الملف، يرجى التحقق من الاتصال بالشبكة أو المحاولة لاحقًا",
121
+ "unknownError": "سبب الخطأ: {{reason}}",
122
+ "uploadFailed": "فشل تحميل الملف"
120
123
  }
121
124
  }
@@ -116,6 +116,9 @@
116
116
  "upload": {
117
117
  "desc": "Подробности: {{detail}}",
118
118
  "fileOnlySupportInServerMode": "Текущият режим на разполагане не поддържа качване на файлове, различни от изображения. За да качите формат {{ext}}, моля, превключете на разполагане с база данни на сървъра или използвайте услугата LobeChat Cloud.",
119
- "title": "Неуспешно качване на файл, моля проверете интернет връзката или опитайте по-късно"
119
+ "networkError": "Моля, уверете се, че вашата мрежа работи нормално и проверете дали конфигурацията за крос-домейн на услугата за съхранение на файлове е правилна.",
120
+ "title": "Неуспешно качване на файл, моля проверете интернет връзката или опитайте по-късно",
121
+ "unknownError": "Причина за грешка: {{reason}}",
122
+ "uploadFailed": "Неуспешно качване на файла."
120
123
  }
121
124
  }
@@ -116,6 +116,9 @@
116
116
  "upload": {
117
117
  "desc": "Details: {{detail}}",
118
118
  "fileOnlySupportInServerMode": "Der aktuelle Bereitstellungsmodus unterstützt das Hochladen von Nicht-Bilddateien nicht. Um Dateien im {{ext}}-Format hochzuladen, wechseln Sie bitte zum Serverdatenbank-Bereitstellungsmodus oder verwenden Sie den LobeChat Cloud-Dienst.",
119
- "title": "Dateiupload fehlgeschlagen. Bitte überprüfen Sie Ihre Netzwerkverbindung und versuchen Sie es später erneut."
119
+ "networkError": "Bitte überprüfen Sie, ob Ihre Internetverbindung stabil ist, und prüfen Sie die Cross-Origin-Konfiguration des Dateispeicherdienstes.",
120
+ "title": "Dateiupload fehlgeschlagen. Bitte überprüfen Sie Ihre Netzwerkverbindung und versuchen Sie es später erneut.",
121
+ "unknownError": "Fehlerursache: {{reason}}",
122
+ "uploadFailed": "Der Datei-Upload ist fehlgeschlagen."
120
123
  }
121
124
  }
@@ -116,6 +116,9 @@
116
116
  "upload": {
117
117
  "desc": "Details: {{detail}}",
118
118
  "fileOnlySupportInServerMode": "The current deployment mode does not support uploading non-image files. To upload files in {{ext}} format, please switch to server database deployment or use LobeChat Cloud service.",
119
- "title": "File upload failed. Please check your network connection or try again later"
119
+ "networkError": "Please check your network connection and ensure that the file storage service's cross-origin configuration is correct.",
120
+ "title": "File upload failed. Please check your network connection or try again later",
121
+ "unknownError": "Error reason: {{reason}}",
122
+ "uploadFailed": "File upload failed."
120
123
  }
121
124
  }
@@ -116,6 +116,9 @@
116
116
  "upload": {
117
117
  "desc": "Detalles: {{detail}}",
118
118
  "fileOnlySupportInServerMode": "El modo de implementación actual no admite la carga de archivos que no sean imágenes. Si desea cargar un archivo en formato {{ext}}, cambie a la implementación de base de datos en servidor o utilice el servicio LobeChat Cloud.",
119
- "title": "Error al subir el archivo, por favor verifica la conexión a internet o inténtalo de nuevo más tarde"
119
+ "networkError": "Por favor, verifica que tu red esté funcionando correctamente y comprueba si la configuración de CORS del servicio de almacenamiento de archivos es correcta.",
120
+ "title": "Error al subir el archivo, por favor verifica la conexión a internet o inténtalo de nuevo más tarde",
121
+ "unknownError": "Razón del error: {{reason}}",
122
+ "uploadFailed": "La carga del archivo ha fallado."
120
123
  }
121
124
  }
@@ -116,6 +116,9 @@
116
116
  "upload": {
117
117
  "desc": "Détails : {{detail}}",
118
118
  "fileOnlySupportInServerMode": "Le mode de déploiement actuel ne prend pas en charge le téléchargement de fichiers non image. Pour télécharger un format {{ext}}, veuillez passer à un déploiement de base de données côté serveur ou utiliser le service LobeChat Cloud.",
119
- "title": "Échec de l'envoi du fichier, veuillez vérifier votre connexion réseau ou réessayer plus tard"
119
+ "networkError": "Veuillez vérifier si votre réseau fonctionne correctement et assurez-vous que la configuration CORS du service de stockage de fichiers est correcte.",
120
+ "title": "Échec de l'envoi du fichier, veuillez vérifier votre connexion réseau ou réessayer plus tard",
121
+ "unknownError": "Raison de l'erreur : {{reason}}",
122
+ "uploadFailed": "Échec du téléchargement du fichier."
120
123
  }
121
124
  }
@@ -116,6 +116,9 @@
116
116
  "upload": {
117
117
  "desc": "Dettagli: {{detail}}",
118
118
  "fileOnlySupportInServerMode": "L'attuale modalità di distribuzione non supporta il caricamento di file non immagine. Se desideri caricare un formato {{ext}}, ti preghiamo di passare alla distribuzione del database sul server o di utilizzare il servizio LobeChat Cloud.",
119
- "title": "Caricamento del file fallito, controlla la connessione di rete o riprova più tardi"
119
+ "networkError": "Si prega di verificare che la connessione di rete sia stabile e controllare se la configurazione CORS del servizio di archiviazione file è corretta.",
120
+ "title": "Caricamento del file fallito, controlla la connessione di rete o riprova più tardi",
121
+ "unknownError": "Motivo dell'errore: {{reason}}",
122
+ "uploadFailed": "Caricamento del file non riuscito."
120
123
  }
121
124
  }
@@ -116,6 +116,9 @@
116
116
  "upload": {
117
117
  "desc": "詳細: {{detail}}",
118
118
  "fileOnlySupportInServerMode": "現在のデプロイモードでは、画像以外のファイルのアップロードはサポートされていません。{{ext}} 形式のファイルをアップロードするには、サーバーデータベースデプロイに切り替えるか、LobeChat Cloud サービスを使用してください。",
119
- "title": "ファイルのアップロードに失敗しました。ネットワーク接続を確認するか、後でもう一度お試しください"
119
+ "networkError": "ネットワークが正常であることを確認し、ファイルストレージサービスのクロスオリジン設定が正しいかどうかを確認してください。",
120
+ "title": "ファイルのアップロードに失敗しました。ネットワーク接続を確認するか、後でもう一度お試しください",
121
+ "unknownError": "エラーの原因: {{reason}}",
122
+ "uploadFailed": "ファイルのアップロードに失敗しました。"
120
123
  }
121
124
  }
@@ -116,6 +116,9 @@
116
116
  "upload": {
117
117
  "desc": "상세 내용: {{detail}}",
118
118
  "fileOnlySupportInServerMode": "현재 배포 모드에서는 이미지 파일이 아닌 파일 업로드를 지원하지 않습니다. {{ext}} 형식을 업로드하려면 서버 데이터베이스 배포로 전환하거나 LobeChat Cloud 서비스를 사용하세요.",
119
- "title": "파일 업로드 실패, 네트워크 연결을 확인하거나 나중에 다시 시도해주세요"
119
+ "networkError": "네트워크가 정상인지 확인하고 파일 저장 서비스의 교차 출처 구성도 올바른지 확인하세요.",
120
+ "title": "파일 업로드 실패, 네트워크 연결을 확인하거나 나중에 다시 시도해주세요",
121
+ "unknownError": "오류 원인: {{reason}}",
122
+ "uploadFailed": "파일 업로드에 실패했습니다."
120
123
  }
121
124
  }
@@ -116,6 +116,9 @@
116
116
  "upload": {
117
117
  "desc": "Details: {{detail}}",
118
118
  "fileOnlySupportInServerMode": "De huidige implementatiemodus ondersteunt het uploaden van niet-afbeeldingsbestanden niet. Als u bestanden in {{ext}}-formaat wilt uploaden, schakelt u over naar de serverdatabase-implementatie of gebruikt u de LobeChat Cloud-service.",
119
- "title": "Bestand uploaden mislukt, controleer uw internetverbinding of probeer het later opnieuw"
119
+ "networkError": "Controleer of je netwerk goed werkt en of de cross-origin configuratie van de bestandsopslagservice correct is.",
120
+ "title": "Bestand uploaden mislukt, controleer uw internetverbinding of probeer het later opnieuw",
121
+ "unknownError": "Foutreden: {{reason}}",
122
+ "uploadFailed": "Bestand uploaden is mislukt."
120
123
  }
121
124
  }
@@ -116,6 +116,9 @@
116
116
  "upload": {
117
117
  "desc": "Szczegóły: {{detail}}",
118
118
  "fileOnlySupportInServerMode": "Aktualny tryb wdrożenia nie obsługuje przesyłania plików, które nie są obrazami. Aby przesłać pliki w formacie {{ext}}, przełącz się na wdrożenie bazy danych na serwerze lub skorzystaj z usługi LobeChat Cloud.",
119
- "title": "Nie udało się przesłać pliku. Sprawdź połączenie sieciowe lub spróbuj ponownie później"
119
+ "networkError": "Proszę upewnić się, że Twoja sieć działa poprawnie oraz sprawdzić, czy konfiguracja CORS dla usługi przechowywania plików jest prawidłowa.",
120
+ "title": "Nie udało się przesłać pliku. Sprawdź połączenie sieciowe lub spróbuj ponownie później",
121
+ "unknownError": "Przyczyna błędu: {{reason}}",
122
+ "uploadFailed": "Wysyłanie pliku nie powiodło się."
120
123
  }
121
124
  }
@@ -116,6 +116,9 @@
116
116
  "upload": {
117
117
  "desc": "Detalhes: {{detail}}",
118
118
  "fileOnlySupportInServerMode": "O modo de implantação atual não suporta o upload de arquivos que não sejam imagens. Para fazer o upload de arquivos no formato {{ext}}, por favor, mude para a implantação do banco de dados no servidor ou utilize o serviço LobeChat Cloud.",
119
- "title": "Falha ao enviar o arquivo, verifique a conexão de rede ou tente novamente mais tarde"
119
+ "networkError": "Por favor, verifique se sua rede está funcionando corretamente e se a configuração de CORS do serviço de armazenamento de arquivos está correta.",
120
+ "title": "Falha ao enviar o arquivo, verifique a conexão de rede ou tente novamente mais tarde",
121
+ "unknownError": "Erro: {{reason}}",
122
+ "uploadFailed": "Falha ao fazer o upload do arquivo."
120
123
  }
121
124
  }
@@ -116,6 +116,9 @@
116
116
  "upload": {
117
117
  "desc": "Подробности: {{detail}}",
118
118
  "fileOnlySupportInServerMode": "Текущий режим развертывания не поддерживает загрузку файлов, отличных от изображений. Если вы хотите загрузить файл формата {{ext}}, пожалуйста, переключитесь на развертывание с серверной базой данных или используйте LobeChat Cloud.",
119
- "title": "Ошибка загрузки файла. Проверьте подключение к сети или попробуйте позже"
119
+ "networkError": "Пожалуйста, убедитесь, что ваше соединение с сетью работает нормально, и проверьте правильность конфигурации кросс-доменных запросов для службы хранения файлов.",
120
+ "title": "Ошибка загрузки файла. Проверьте подключение к сети или попробуйте позже",
121
+ "unknownError": "Причина ошибки: {{reason}}",
122
+ "uploadFailed": "Ошибка загрузки файла"
120
123
  }
121
124
  }
@@ -116,6 +116,9 @@
116
116
  "upload": {
117
117
  "desc": "Detay: {{detail}}",
118
118
  "fileOnlySupportInServerMode": "Mevcut dağıtım modu, yalnızca resim dosyalarının yüklenmesini desteklemektedir. {{ext}} formatında bir dosya yüklemek istiyorsanız, lütfen sunucu veritabanı dağıtımına geçin veya LobeChat Cloud hizmetini kullanın.",
119
- "title": "Dosya yükleme başarısız, lütfen bağlantınızı kontrol edin veya daha sonra tekrar deneyin"
119
+ "networkError": "Lütfen bağlantınızın düzgün çalıştığından emin olun ve dosya depolama hizmetinin çapraz alan yapılandırmasının doğru olup olmadığını kontrol edin.",
120
+ "title": "Dosya yükleme başarısız, lütfen ağ bağlantınızı kontrol edin veya daha sonra tekrar deneyin",
121
+ "unknownError": "Hata nedeni: {{reason}}",
122
+ "uploadFailed": "Dosya yüklemesi başarısız oldu."
120
123
  }
121
124
  }
@@ -116,6 +116,9 @@
116
116
  "upload": {
117
117
  "desc": "Chi tiết: {{detail}}",
118
118
  "fileOnlySupportInServerMode": "Chế độ triển khai hiện tại không hỗ trợ tải lên các tệp không phải hình ảnh. Nếu bạn cần tải lên định dạng {{ext}}, vui lòng chuyển sang triển khai cơ sở dữ liệu trên máy chủ hoặc sử dụng dịch vụ LobeChat Cloud.",
119
- "title": "Tải lên tệp thất bại, vui lòng kiểm tra kết nối mạng hoặc thử lại sau"
119
+ "networkError": "Vui lòng kiểm tra xem mạng của bạn có hoạt động bình thường không và kiểm tra cấu hình chia sẻ tệp giữa các miền có đúng không",
120
+ "title": "Tải lên tệp thất bại, vui lòng kiểm tra kết nối mạng hoặc thử lại sau",
121
+ "unknownError": "Lỗi: {{reason}}",
122
+ "uploadFailed": "Tải tệp lên không thành công"
120
123
  }
121
124
  }
@@ -116,6 +116,9 @@
116
116
  "upload": {
117
117
  "desc": "详情: {{detail}}",
118
118
  "fileOnlySupportInServerMode": "当前部署模式不支持上传非图片文件,如需上传 {{ext}} 格式,请切换到服务端数据库部署或使用 LobeChat Cloud 服务",
119
- "title": "文件上传失败,请检查网络连接或稍后再试"
119
+ "networkError": "请确认你的网络是否正常,并检查文件存储服务跨域配置是否正确",
120
+ "title": "文件上传失败,请检查网络连接或稍后再试",
121
+ "unknownError": "错误原因: {{reason}}",
122
+ "uploadFailed": "文件上传失败"
120
123
  }
121
124
  }
@@ -116,6 +116,9 @@
116
116
  "upload": {
117
117
  "desc": "詳情: {{detail}}",
118
118
  "fileOnlySupportInServerMode": "當前部署模式不支持上傳非圖片文件,如需上傳 {{ext}} 格式,請切換到伺服器端資料庫部署或使用 LobeChat Cloud 服務",
119
- "title": "檔案上傳失敗,請檢查網路連線或稍後再試"
119
+ "networkError": "請確認你的網路是否正常,並檢查檔案儲存服務的跨域配置是否正確",
120
+ "title": "檔案上傳失敗,請檢查網路連線或稍後再試",
121
+ "unknownError": "錯誤原因: {{reason}}",
122
+ "uploadFailed": "檔案上傳失敗"
120
123
  }
121
124
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/chat",
3
- "version": "1.12.4",
3
+ "version": "1.12.6",
4
4
  "description": "Lobe Chat - an open-source, high-performance chatbot 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",
@@ -135,7 +135,7 @@
135
135
  "@vercel/speed-insights": "^1.0.12",
136
136
  "ahooks": "^3.8.0",
137
137
  "ai": "^3.2.16",
138
- "antd": "5.20.0",
138
+ "antd": "^5.20.2",
139
139
  "antd-style": "^3.6.2",
140
140
  "brotli-wasm": "^3.0.1",
141
141
  "chroma-js": "^2.4.2",
@@ -0,0 +1,130 @@
1
+ import { act, renderHook } from '@testing-library/react';
2
+ import { Mock, afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
3
+
4
+ import { getContainer, useDragUpload } from './useDragUpload';
5
+
6
+ describe('useDragUpload', () => {
7
+ let mockOnUploadFiles: Mock;
8
+
9
+ beforeEach(() => {
10
+ mockOnUploadFiles = vi.fn();
11
+ vi.useFakeTimers();
12
+ document.body.innerHTML = '';
13
+ });
14
+
15
+ afterEach(() => {
16
+ vi.useRealTimers();
17
+ vi.clearAllMocks();
18
+ });
19
+
20
+ it('should initialize and cleanup correctly', () => {
21
+ const { result, unmount } = renderHook(() => useDragUpload(mockOnUploadFiles));
22
+
23
+ expect(result.current).toBe(false);
24
+ expect(document.getElementById('dragging-root')).not.toBeNull();
25
+
26
+ unmount();
27
+
28
+ expect(document.getElementById('dragging-root')).toBeNull();
29
+ });
30
+
31
+ it('should handle drag events correctly', () => {
32
+ const { result } = renderHook(() => useDragUpload(mockOnUploadFiles));
33
+
34
+ act(() => {
35
+ window.dispatchEvent(new Event('dragenter'));
36
+ });
37
+
38
+ expect(result.current).toBe(false);
39
+
40
+ act(() => {
41
+ const dragEnterEvent = new Event('dragenter') as DragEvent;
42
+ Object.defineProperty(dragEnterEvent, 'dataTransfer', {
43
+ value: {
44
+ items: [{}],
45
+ types: ['Files'],
46
+ },
47
+ });
48
+ window.dispatchEvent(dragEnterEvent);
49
+ });
50
+
51
+ expect(result.current).toBe(true);
52
+
53
+ act(() => {
54
+ const dragLeaveEvent = new Event('dragleave') as DragEvent;
55
+ Object.defineProperty(dragLeaveEvent, 'dataTransfer', {
56
+ value: {
57
+ items: [{}],
58
+ types: ['Files'],
59
+ },
60
+ });
61
+ window.dispatchEvent(dragLeaveEvent);
62
+ });
63
+
64
+ expect(result.current).toBe(false);
65
+ });
66
+
67
+ it('should handle file drop', async () => {
68
+ renderHook(() => useDragUpload(mockOnUploadFiles));
69
+
70
+ const mockFile = new File([''], 'test.txt', { type: 'text/plain' });
71
+ const dropEvent = new Event('drop') as DragEvent;
72
+ Object.defineProperty(dropEvent, 'dataTransfer', {
73
+ value: {
74
+ items: [
75
+ {
76
+ kind: 'file',
77
+ getAsFile: () => mockFile,
78
+ webkitGetAsEntry: () => ({
79
+ isFile: true,
80
+ file: (cb: (file: File) => void) => cb(mockFile),
81
+ }),
82
+ },
83
+ ],
84
+ types: ['Files'],
85
+ },
86
+ });
87
+
88
+ await act(async () => {
89
+ window.dispatchEvent(dropEvent);
90
+ });
91
+
92
+ expect(mockOnUploadFiles).toHaveBeenCalledWith([mockFile]);
93
+ });
94
+
95
+ it('should handle paste event', async () => {
96
+ renderHook(() => useDragUpload(mockOnUploadFiles));
97
+
98
+ const mockFile = new File([''], 'test.txt', { type: 'text/plain' });
99
+ const pasteEvent = new Event('paste') as ClipboardEvent;
100
+ Object.defineProperty(pasteEvent, 'clipboardData', {
101
+ value: {
102
+ items: [
103
+ {
104
+ kind: 'file',
105
+ getAsFile: () => mockFile,
106
+ webkitGetAsEntry: () => null,
107
+ },
108
+ ],
109
+ },
110
+ });
111
+
112
+ await act(async () => {
113
+ window.dispatchEvent(pasteEvent);
114
+ });
115
+
116
+ expect(mockOnUploadFiles).toHaveBeenCalledWith([mockFile]);
117
+ });
118
+ });
119
+
120
+ describe('getContainer', () => {
121
+ it('should return the dragging root element', () => {
122
+ const rootElement = document.createElement('div');
123
+ rootElement.id = 'dragging-root';
124
+ document.body.appendChild(rootElement);
125
+
126
+ const container = getContainer();
127
+ expect(container).not.toBeNull();
128
+ expect(container?.id).toBe('dragging-root');
129
+ });
130
+ });
@@ -40,6 +40,15 @@ const getFileListFromDataTransferItems = async (items: DataTransferItem[]) => {
40
40
  const entry = item.webkitGetAsEntry();
41
41
  if (entry) {
42
42
  filePromises.push(processEntry(entry));
43
+ } else {
44
+ const file = item.getAsFile();
45
+
46
+ if (file)
47
+ filePromises.push(
48
+ new Promise((resolve) => {
49
+ resolve([file]);
50
+ }),
51
+ );
43
52
  }
44
53
  }
45
54
  }
@@ -4,8 +4,19 @@ import { z } from 'zod';
4
4
  const DEFAULT_S3_FILE_PATH = 'files';
5
5
 
6
6
  export const getFileConfig = () => {
7
+ if (!!process.env.NEXT_PUBLIC_S3_DOMAIN) {
8
+ console.warn(
9
+ '⚠️ `NEXT_PUBLIC_S3_DOMAIN` will be de deprecated in the next major version, please replace it with `S3_PUBLIC_DOMAIN` in your env',
10
+ );
11
+ }
12
+
13
+ const S3_PUBLIC_DOMAIN = process.env.S3_PUBLIC_DOMAIN || process.env.NEXT_PUBLIC_S3_DOMAIN;
14
+
7
15
  return createEnv({
8
16
  client: {
17
+ /**
18
+ * @deprecated
19
+ */
9
20
  NEXT_PUBLIC_S3_DOMAIN: z.string().url().optional(),
10
21
  NEXT_PUBLIC_S3_FILE_PATH: z.string().optional(),
11
22
  },
@@ -18,7 +29,9 @@ export const getFileConfig = () => {
18
29
 
19
30
  S3_ACCESS_KEY_ID: process.env.S3_ACCESS_KEY_ID,
20
31
  S3_BUCKET: process.env.S3_BUCKET,
32
+ S3_ENABLE_PATH_STYLE: process.env.S3_ENABLE_PATH_STYLE === '1',
21
33
  S3_ENDPOINT: process.env.S3_ENDPOINT,
34
+ S3_PUBLIC_DOMAIN,
22
35
  S3_REGION: process.env.S3_REGION,
23
36
  S3_SECRET_ACCESS_KEY: process.env.S3_SECRET_ACCESS_KEY,
24
37
  S3_SET_ACL: process.env.S3_SET_ACL !== '0',
@@ -30,8 +43,10 @@ export const getFileConfig = () => {
30
43
  // S3
31
44
  S3_ACCESS_KEY_ID: z.string().optional(),
32
45
  S3_BUCKET: z.string().optional(),
33
- S3_ENDPOINT: z.string().url().optional(),
46
+ S3_ENABLE_PATH_STYLE: z.boolean(),
34
47
 
48
+ S3_ENDPOINT: z.string().url().optional(),
49
+ S3_PUBLIC_DOMAIN: z.string().url().optional(),
35
50
  S3_REGION: z.string().optional(),
36
51
  S3_SECRET_ACCESS_KEY: z.string().optional(),
37
52
  S3_SET_ACL: z.boolean(),
@@ -142,6 +142,9 @@ export default {
142
142
  desc: '详情: {{detail}}',
143
143
  fileOnlySupportInServerMode:
144
144
  '当前部署模式不支持上传非图片文件,如需上传 {{ext}} 格式,请切换到服务端数据库部署或使用 LobeChat Cloud 服务',
145
+ networkError: '请确认你的网络是否正常,并检查文件存储服务跨域配置是否正确',
145
146
  title: '文件上传失败,请检查网络连接或稍后再试',
147
+ unknownError: '错误原因: {{reason}}',
148
+ uploadFailed: '文件上传失败',
146
149
  },
147
150
  };
@@ -42,6 +42,7 @@ export class S3 {
42
42
  secretAccessKey: fileEnv.S3_SECRET_ACCESS_KEY,
43
43
  },
44
44
  endpoint: fileEnv.S3_ENDPOINT,
45
+ forcePathStyle: fileEnv.S3_ENABLE_PATH_STYLE,
45
46
  region: fileEnv.S3_REGION || DEFAULT_S3_REGION,
46
47
  });
47
48
  }
@@ -1,8 +1,6 @@
1
1
  import { TRPCError } from '@trpc/server';
2
- import urlJoin from 'url-join';
3
2
  import { z } from 'zod';
4
3
 
5
- import { fileEnv } from '@/config/file';
6
4
  import { AsyncTaskModel } from '@/database/server/models/asyncTask';
7
5
  import { ChunkModel } from '@/database/server/models/chunk';
8
6
  import { FileModel } from '@/database/server/models/file';
@@ -137,7 +135,7 @@ export const fileRouter = router({
137
135
  embeddingError: embeddingTask?.error ?? null,
138
136
  embeddingStatus: embeddingTask?.status as AsyncTaskStatus,
139
137
  finishEmbedding: embeddingTask?.status === AsyncTaskStatus.Success,
140
- url: urlJoin(fileEnv.NEXT_PUBLIC_S3_DOMAIN!, item.url!),
138
+ url: getFullFileUrl(item.url!),
141
139
  };
142
140
  });
143
141
  }),
@@ -5,5 +5,5 @@ import { fileEnv } from '@/config/file';
5
5
  export const getFullFileUrl = (url?: string | null) => {
6
6
  if (!url) return '';
7
7
 
8
- return urlJoin(fileEnv.NEXT_PUBLIC_S3_DOMAIN!, url);
8
+ return urlJoin(fileEnv.S3_PUBLIC_DOMAIN!, url);
9
9
  };
@@ -21,6 +21,10 @@ export class ServerService implements IFileService {
21
21
  return lambdaClient.file.createFile.mutate({ ...params, knowledgeBaseId } as CreateFileParams);
22
22
  }
23
23
 
24
+ /**
25
+ * @deprecated
26
+ * @param id
27
+ */
24
28
  async getFile(id: string): Promise<FilePreview> {
25
29
  if (!fileEnv.NEXT_PUBLIC_S3_DOMAIN) {
26
30
  throw new Error('fileEnv.NEXT_PUBLIC_S3_DOMAIN is not set while enable server upload');
@@ -5,6 +5,8 @@ import { FileMetadata, UploadFileParams } from '@/types/files';
5
5
  import { FileUploadState, FileUploadStatus } from '@/types/files/upload';
6
6
  import { uuid } from '@/utils/uuid';
7
7
 
8
+ export const UPLOAD_NETWORK_ERROR = 'NetWorkError';
9
+
8
10
  class UploadService {
9
11
  uploadWithProgress = async (
10
12
  file: File,
@@ -13,7 +15,6 @@ class UploadService {
13
15
  const xhr = new XMLHttpRequest();
14
16
 
15
17
  const { preSignUrl, ...result } = await this.getSignedUploadUrl(file);
16
-
17
18
  let startTime = Date.now();
18
19
  xhr.upload.addEventListener('progress', (event) => {
19
20
  if (event.lengthComputable) {
@@ -49,7 +50,10 @@ class UploadService {
49
50
  reject(xhr.statusText);
50
51
  }
51
52
  });
52
- xhr.addEventListener('error', () => reject(xhr.statusText));
53
+ xhr.addEventListener('error', () => {
54
+ if (xhr.status === 0) reject(UPLOAD_NETWORK_ERROR);
55
+ else reject(xhr.statusText);
56
+ });
53
57
  xhr.send(data);
54
58
  });
55
59
 
@@ -1,11 +1,14 @@
1
+ import { t } from 'i18next';
1
2
  import { produce } from 'immer';
2
3
  import useSWR, { SWRResponse } from 'swr';
3
4
  import { StateCreator } from 'zustand/vanilla';
4
5
 
6
+ import { notification } from '@/components/AntdStaticMethods';
5
7
  import { FILE_UPLOAD_BLACKLIST } from '@/const/file';
6
8
  import { fileService } from '@/services/file';
7
9
  import { ServerService } from '@/services/file/server';
8
10
  import { ragService } from '@/services/rag';
11
+ import { UPLOAD_NETWORK_ERROR } from '@/services/upload';
9
12
  import { userService } from '@/services/user';
10
13
  import { useAgentStore } from '@/store/agent';
11
14
  import {
@@ -128,10 +131,30 @@ export const createFileSlice: StateCreator<
128
131
 
129
132
  // upload files and process it
130
133
  const pools = files.map(async (file) => {
131
- const fileResult = await get().uploadWithProgress({
132
- file,
133
- onStatusUpdate: dispatchChatUploadFileList,
134
- });
134
+ let fileResult: { id: string; url: string } | undefined;
135
+
136
+ try {
137
+ fileResult = await get().uploadWithProgress({
138
+ file,
139
+ onStatusUpdate: dispatchChatUploadFileList,
140
+ });
141
+ } catch (error) {
142
+ // skip `UNAUTHORIZED` error
143
+ if ((error as any)?.message !== 'UNAUTHORIZED')
144
+ notification.error({
145
+ description:
146
+ // it may be a network error or the cors error
147
+ error === UPLOAD_NETWORK_ERROR
148
+ ? t('upload.networkError', { ns: 'error' })
149
+ : // or the error from the server
150
+ typeof error === 'string'
151
+ ? error
152
+ : t('upload.unknownError', { ns: 'error', reason: (error as Error).message }),
153
+ message: t('upload.uploadFailed', { ns: 'error' }),
154
+ });
155
+
156
+ dispatchChatUploadFileList({ id: file.name, type: 'removeFile' });
157
+ }
135
158
 
136
159
  if (!fileResult) return;
137
160