feat: custom domain (#6067)
Some checks are pending
Document deploy / sync-images (push) Waiting to run
Document deploy / generate-timestamp (push) Blocked by required conditions
Document deploy / build-images (map[domain:https://fastgpt.cn suffix:cn]) (push) Blocked by required conditions
Document deploy / build-images (map[domain:https://fastgpt.io suffix:io]) (push) Blocked by required conditions
Document deploy / update-images (map[deployment:fastgpt-docs domain:https://fastgpt.cn kube_config:KUBE_CONFIG_CN suffix:cn]) (push) Blocked by required conditions
Document deploy / update-images (map[deployment:fastgpt-docs domain:https://fastgpt.io kube_config:KUBE_CONFIG_IO suffix:io]) (push) Blocked by required conditions
Build FastGPT images in Personal warehouse / get-vars (push) Waiting to run
Build FastGPT images in Personal warehouse / build-fastgpt-images (map[arch:amd64 runs-on:ubuntu-24.04]) (push) Blocked by required conditions
Build FastGPT images in Personal warehouse / build-fastgpt-images (map[arch:arm64 runs-on:ubuntu-24.04-arm]) (push) Blocked by required conditions
Build FastGPT images in Personal warehouse / release-fastgpt-images (push) Blocked by required conditions
* perf: faq * index * delete dataset * delete dataset * perf: delete dataset * init * fix: faq * doc * fix: share link auth (#6063) * standard plan add custom domain config (#6061) * standard plan add custom domain config * bill detail modal * perf: vector count api * feat: custom domain & wecom bot SaaS integration (#6047) * feat: custom Domain type define * feat: custom domain * feat: wecom custom domain * chore: i18n * chore: i18n; team auth * feat: wecom multi-model message support * chore: wecom edit modal * chore(doc): custom domain && wecom bot * fix: type * fix: type * fix: file detect * feat: fe * fix: img name * fix: test * compress img * rename * editor initial status * fix: chat url * perf: s3 upload by buffer * img * refresh * fix: custom domain selector (#6069) * empty tip * perf: s3 init * sort provider * fix: extend * perf: extract filename --------- Co-authored-by: Roy <whoeverimf5@gmail.com> Co-authored-by: heheer <heheer@sealos.io> Co-authored-by: Finley Ge <32237950+FinleyGe@users.noreply.github.com>
|
|
@ -28,7 +28,7 @@ description: FastGPT 分享链接身份鉴权
|
|||
|
||||
`FastGPT` 将会判断`success`是否为`true`决定是允许用户继续操作。`message`与`msg`是等同的,你可以选择返回其中一个,当`success`不为`true`时,将会提示这个错误。
|
||||
|
||||
`uid` 是用户的唯一凭证,必须返回该 ID 且 ID 的格式为不包含 "|"、"/“、"\" 字符的、长度小于等于 255 的字符串,否则会返回 `Invalid UID` 的错误。`uid` 将会用于拉取对话记录以及保存对话记录,可参考下方实践案例。
|
||||
`uid` 是用户的唯一凭证,必须返回该 ID 且 ID 的格式为不包含 "|"、"/“、"\\" 字符的、小于等于 255 **字节长度**的字符串,否则会返回 `Invalid UID` 的错误。`uid` 将会用于拉取对话记录以及保存对话记录,可参考下方实践案例。
|
||||
|
||||
### 触发流程
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,42 @@
|
|||
---
|
||||
title: 配置自定义域名
|
||||
description: 如何在 FastGPT 中配置自定义域名
|
||||
---
|
||||
|
||||
FastGPT 云服务版自 v4.14.4 后支持配置自定义域名。
|
||||
|
||||
## 如何配置自定义域名
|
||||
|
||||
### 1. 打开“自定义域名”页面
|
||||
|
||||
在侧边栏选择“账号” -> “自定义域名”,打开自定义域名配置页。
|
||||
|
||||
如果您的套餐等级不支持配置,请根据页面的指引升级套餐。
|
||||
|
||||

|
||||
|
||||
### 2. 添加自定义域名
|
||||
|
||||
1. 准备好您的域名。您的域名必须先经过备案,目前支持“阿里云”、“腾讯云”、“火山引擎”三家服务商的备案域名。
|
||||
2. 点击“编辑”按钮,进入编辑状态。
|
||||
3. 填入您的域名,例如 www.example.com
|
||||
4. 在域名服务商的域名解析处,添加界面中提示的 DNS 纪录,注意纪录类型为 CNAME。
|
||||
5. 添加解析纪录后,点击“保存”按钮。系统将自动检查 DNS 解析情况,一般情况下,在一分钟内就可以获取到解析纪录。如果长时间没有获取到纪录,可以重试一次。
|
||||
6. 待状态提示显示为“已生效”后,点击“确认”按钮即可。
|
||||
|
||||

|
||||
|
||||
现在您可以通过您自己的域名访问 fastgpt 服务、调用 fastgpt 的 API 了。
|
||||
|
||||
## 域名解析失效
|
||||
|
||||
系统会每天对 DNS 解析进行检查,如果发现 DNS 解析纪录失效,则会停用该自定义域名,可以在“自定义域名”管理界面中点击“编辑”进行重新解析。
|
||||
|
||||

|
||||
|
||||
如果您需要修改自定义域名、或修改服务商,则需要删除自定义域名配置后进行重新配置。
|
||||
|
||||
|
||||
## 使用案例
|
||||
|
||||
- [接入企业微信智能机器人](/docs/use-cases/external-integration/wecom)
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"title": "团队与权限",
|
||||
"description": "团队管理、成员组与权限设置,确保团队协作中的数据安全和权限分配合理。",
|
||||
"pages": ["team_roles_permissions","invitation_link"]
|
||||
}
|
||||
"pages": ["team_roles_permissions","invitation_link", "customDomain"]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -92,6 +92,7 @@ description: FastGPT 文档目录
|
|||
- [/docs/introduction/guide/plugins/google_search_plugin_guide](/docs/introduction/guide/plugins/google_search_plugin_guide)
|
||||
- [/docs/introduction/guide/plugins/searxng_plugin_guide](/docs/introduction/guide/plugins/searxng_plugin_guide)
|
||||
- [/docs/introduction/guide/plugins/upload_system_tool](/docs/introduction/guide/plugins/upload_system_tool)
|
||||
- [/docs/introduction/guide/team_permissions/customDomain](/docs/introduction/guide/team_permissions/customDomain)
|
||||
- [/docs/introduction/guide/team_permissions/invitation_link](/docs/introduction/guide/team_permissions/invitation_link)
|
||||
- [/docs/introduction/guide/team_permissions/team_roles_permissions](/docs/introduction/guide/team_permissions/team_roles_permissions)
|
||||
- [/docs/introduction/index](/docs/introduction/index)
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ curl --location --request POST 'https://{{host}}/api/admin/initv4144' \
|
|||
5. 新版订阅套餐逻辑。
|
||||
6. 支持配置对话文件白名单。
|
||||
7. S3 支持 pathStyle 配置。
|
||||
8. 支持通过 Sealos 来进行多租户自定义域名配置。
|
||||
|
||||
## ⚙️ 优化
|
||||
|
||||
|
|
@ -56,3 +57,4 @@ curl --location --request POST 'https://{{host}}/api/admin/initv4144' \
|
|||
|
||||
## 插件
|
||||
|
||||
1. 新增 GLM4.6 与 DS3.2 系列模型预设。
|
||||
|
|
|
|||
|
|
@ -3,119 +3,70 @@ title: 接入企微机器人教程
|
|||
description: FastGPT 接入企微机器人教程
|
||||
---
|
||||
|
||||
从 4.12.4 版本起,FastGPT 商业版支持直接接入企微机器人,无需额外的 API。
|
||||
- 从 4.12.4 版本起,FastGPT 商业版支持直接接入企微机器人,无需额外的 API。
|
||||
- 从 4.14.4 版本起,FastGPT 云服务版支持通过配置自定义域名的方式接入企微智能机器人。
|
||||
|
||||
## 1. 配置可信域名和可信IP
|
||||
## 1. (云服务版必须)配置自定义域名
|
||||
|
||||
点击企微头像,打开管理企业
|
||||
企微要求智能机器人消息推送地址必须使用企业主体域名,因此云服务版本用户必须先配置自定义域名才能使用企微机器人。
|
||||
|
||||

|
||||
- [配置自定义域名](/docs/introduction/guide/team_permissions/customDomain)
|
||||
|
||||
在应用管理中找到"自建"-"创建应用"
|
||||
若您是商业版用户,请继续使用您企业的域名。
|
||||
|
||||

|
||||
## 2. 创建智能机器人
|
||||
|
||||
创建好应用后, 下拉, 依次配置"网页授权及JS-SDK"和"企业可信IP"
|
||||
### 2.1 超级管理员登录
|
||||
|
||||

|
||||
[点击打开企业微信管理后台](https://work.weixin.qq.com/)
|
||||
|
||||
其中, 网页授权及JS-SDK要求按照企微指引,完成域名归属认证
|
||||
### 2.2 找到智能机器人入口
|
||||
|
||||

|
||||
在"安全与管理" - "管理工具"页面点击"智能机器人" ( 注意: 只有企业创建者或超级管理员才有权限看到这个入口 )
|
||||
|
||||
企业可信IP要求为企业服务器IP, 后续企微的回调URL将请求到此IP
|
||||

|
||||
|
||||

|
||||
### 2.3 选择 “API模式创建” 智能机器人
|
||||
|
||||
## 2. 创建企业自建应用
|
||||
在创建机器人页面, 下拉, 点击 "API模式创建"
|
||||
|
||||
前往 FastGPT ,选择想要接入的应用,在 发布渠道 页面,新建一个接入企微智能机器人的发布渠道,填写好基础信息。
|
||||

|
||||
|
||||

|
||||
### 2.4 获取关键密钥
|
||||
|
||||
现在回到企业微信平台,找到 Corp ID, Secret, Agent ID, Token, AES Key 信息并填写回 FastGPT 平台
|
||||
随机生成或者手动输入 Token 和 Encoding-AESKey,并且纪录下来
|
||||
|
||||

|
||||

|
||||
|
||||
在"我的企业"里找到企业 ID, 填写到 FastGPT 的 Corp ID 中
|
||||
### 2.5 创建企微机器人发布渠道
|
||||
|
||||

|
||||
在 FastGPT 中,选择要使用 Agent,在发布渠道页面,选择“企业微信机器人”,点击“创建”
|
||||
|
||||
在应用中找到 Agent Id 和 Secret, 并填写回 FastGPT
|
||||

|
||||
|
||||

|
||||
### 2.6 配置发布渠道信息
|
||||
|
||||
点击"消息接收"-"设置API接收"
|
||||
配置该发布渠道的信息,需要填入 Token 和 AESKey,也就是第四步中纪录下来的 Token 和 Encoding-AESKey
|
||||
|
||||

|
||||

|
||||
|
||||
随机生成或者手动输入 Token 和 Encoding-Key, 分别填写到 FastGPT 的 Token 和 AES Key 中
|
||||
### 2.7 复制回调地址
|
||||
|
||||

|
||||
点击“确认”后,选择您配置的自定义域名,复制回调地址,填回企微智能机器人配置页中。
|
||||
|
||||
填写完成后确认创建
|
||||

|
||||
|
||||
然后点击请求地址, 复制页面中的链接
|
||||
|
||||

|
||||
|
||||
回到刚才的配置详情, 将刚才复制的链接填入 URL 框中, 并点击下方的保存 ,即可完成自建应用的创建
|
||||
|
||||
注意: 若复制的链接是以 "http://localhost" 开头, 需要将本地地址改为企业主体域名
|
||||
|
||||
因为企微会给填写的 URL 发送验证密文, 若 URL 为本地地址, 则本地接收不到企微的密文
|
||||
|
||||
若 URL 不是企业主体域名, 则验证会失败
|
||||
|
||||
## 3. 创建智能机器人
|
||||
|
||||
第二步创建企业自建应用是为了验证域名和IP的合规性, 并获取 secret 参数, 下面创建智能机器人才是正式的配置流程
|
||||
|
||||
在"安全与管理" - "管理工具"页面找到"智能机器人" ( 注意: 只有企业创建者或超级管理员才有权限看到这个入口 )
|
||||
|
||||

|
||||
|
||||
创建机器人页面,下拉,找到,点击"API模式创建"
|
||||
|
||||

|
||||
|
||||
与刚才配置自建应用同理, 在 FastGPT 平台再新增一个发布渠道, 并回到企业微信配置参数
|
||||
|
||||

|
||||
|
||||
随机生成或者手动输入 Token 和 Encoding-AESKey, 分别填写到 FastGPT 的 Token 和 AES Key 中
|
||||
|
||||

|
||||
|
||||
Corp ID 和 Secret 这两个参数和刚才的自建应用保持一致
|
||||
|
||||
Agent ID 和自建应用的不同, 需要先填写一个自定义值, 后续会根据企业微信提供的数据重新更改
|
||||
|
||||
在 FastGPT 将Corp ID, Secret, Agent ID, Token, AES Key 等参数都填写完毕后, 点击确认
|
||||
|
||||
然后点击请求地址, 复制页面中的链接
|
||||
|
||||
回到企业微信, 将链接粘贴到智能机器人的 URL 配置栏, 点击创建
|
||||
|
||||
创建完成后, 找到智能机器人的配置详情
|
||||
|
||||

|
||||
|
||||
复制 Bot ID, 填写到 FastGPT 的 Agent ID 中, 即可完成智能机器人配置
|
||||
|
||||

|
||||
|
||||
## 4. 使用智能机器人
|
||||
## 3. 使用智能机器人
|
||||
|
||||
在企业微信平台的"通讯录",即可找到创建的机器人,就可以发送消息了
|
||||
|
||||

|
||||

|
||||
|
||||
## FAQ
|
||||
|
||||
### 发送了消息,没响应
|
||||
|
||||
1. 检查可信域名和可信IP是否配置正确。
|
||||
2. 检查自建应用的 Secret 参数是否与智能机器人一致。
|
||||
3. 查看 FastGPT 对话日志,是否有对应的提问记录
|
||||
1. 检查可信域名是否配置正确。
|
||||
2. 检查 Token 和 Encoding-AESKey 是否正确。
|
||||
3. 查看 FastGPT 对话日志,是否有对应的提问记录。
|
||||
4. 如果没记录,则可能是应用运行报错了,可以先试试最简单的机器人.
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@
|
|||
"document/content/docs/introduction/development/openapi/chat.mdx": "2025-11-14T13:21:17+08:00",
|
||||
"document/content/docs/introduction/development/openapi/dataset.mdx": "2025-09-29T11:34:11+08:00",
|
||||
"document/content/docs/introduction/development/openapi/intro.mdx": "2025-09-29T11:34:11+08:00",
|
||||
"document/content/docs/introduction/development/openapi/share.mdx": "2025-12-08T16:10:51+08:00",
|
||||
"document/content/docs/introduction/development/openapi/share.mdx": "2025-12-09T12:18:15+08:00",
|
||||
"document/content/docs/introduction/development/proxy/cloudflare.mdx": "2025-07-23T21:35:03+08:00",
|
||||
"document/content/docs/introduction/development/proxy/http_proxy.mdx": "2025-07-23T21:35:03+08:00",
|
||||
"document/content/docs/introduction/development/proxy/nginx.mdx": "2025-07-23T21:35:03+08:00",
|
||||
|
|
@ -89,6 +89,7 @@
|
|||
"document/content/docs/introduction/guide/plugins/google_search_plugin_guide.mdx": "2025-07-23T21:35:03+08:00",
|
||||
"document/content/docs/introduction/guide/plugins/searxng_plugin_guide.mdx": "2025-07-23T21:35:03+08:00",
|
||||
"document/content/docs/introduction/guide/plugins/upload_system_tool.mdx": "2025-11-04T16:58:12+08:00",
|
||||
"document/content/docs/introduction/guide/team_permissions/customDomain.mdx": "2025-12-09T18:14:22+08:00",
|
||||
"document/content/docs/introduction/guide/team_permissions/invitation_link.mdx": "2025-07-23T21:35:03+08:00",
|
||||
"document/content/docs/introduction/guide/team_permissions/team_roles_permissions.mdx": "2025-07-23T21:35:03+08:00",
|
||||
"document/content/docs/introduction/index.en.mdx": "2025-07-23T21:35:03+08:00",
|
||||
|
|
@ -101,7 +102,7 @@
|
|||
"document/content/docs/protocol/terms.en.mdx": "2025-08-03T22:37:45+08:00",
|
||||
"document/content/docs/protocol/terms.mdx": "2025-08-03T22:37:45+08:00",
|
||||
"document/content/docs/toc.en.mdx": "2025-08-04T13:42:36+08:00",
|
||||
"document/content/docs/toc.mdx": "2025-11-29T09:24:47+08:00",
|
||||
"document/content/docs/toc.mdx": "2025-12-09T18:14:22+08:00",
|
||||
"document/content/docs/upgrading/4-10/4100.mdx": "2025-08-02T19:38:37+08:00",
|
||||
"document/content/docs/upgrading/4-10/4101.mdx": "2025-09-08T20:07:20+08:00",
|
||||
"document/content/docs/upgrading/4-11/4110.mdx": "2025-08-05T23:20:39+08:00",
|
||||
|
|
@ -118,7 +119,7 @@
|
|||
"document/content/docs/upgrading/4-14/4141.mdx": "2025-11-19T10:15:27+08:00",
|
||||
"document/content/docs/upgrading/4-14/4142.mdx": "2025-11-18T19:27:14+08:00",
|
||||
"document/content/docs/upgrading/4-14/4143.mdx": "2025-11-26T20:52:05+08:00",
|
||||
"document/content/docs/upgrading/4-14/4144.mdx": "2025-12-08T17:57:59+08:00",
|
||||
"document/content/docs/upgrading/4-14/4144.mdx": "2025-12-08T21:45:21+08:00",
|
||||
"document/content/docs/upgrading/4-8/40.mdx": "2025-08-02T19:38:37+08:00",
|
||||
"document/content/docs/upgrading/4-8/41.mdx": "2025-08-02T19:38:37+08:00",
|
||||
"document/content/docs/upgrading/4-8/42.mdx": "2025-08-02T19:38:37+08:00",
|
||||
|
|
@ -198,6 +199,6 @@
|
|||
"document/content/docs/use-cases/external-integration/feishu.mdx": "2025-07-24T14:23:04+08:00",
|
||||
"document/content/docs/use-cases/external-integration/official_account.mdx": "2025-08-05T23:20:39+08:00",
|
||||
"document/content/docs/use-cases/external-integration/openapi.mdx": "2025-09-29T11:34:11+08:00",
|
||||
"document/content/docs/use-cases/external-integration/wecom.mdx": "2025-09-16T14:22:28+08:00",
|
||||
"document/content/docs/use-cases/external-integration/wecom.mdx": "2025-12-09T20:03:29+08:00",
|
||||
"document/content/docs/use-cases/index.mdx": "2025-07-24T14:23:04+08:00"
|
||||
}
|
||||
|
|
@ -2543,7 +2543,6 @@
|
|||
"integrity": "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"csstype": "^3.0.2"
|
||||
}
|
||||
|
|
@ -2554,7 +2553,6 @@
|
|||
"integrity": "sha512-4hOiT/dwO8Ko0gV1m/TJZYk3y0KBnY9vzDh7W+DH17b2HFSOGgdj33dhihPeuy3l0q23+4e+hoXHV6hCC4dCXw==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"peerDependencies": {
|
||||
"@types/react": "^19.0.0"
|
||||
}
|
||||
|
|
@ -2576,7 +2574,6 @@
|
|||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
|
|
@ -3244,7 +3241,6 @@
|
|||
"resolved": "https://registry.npmjs.org/fumadocs-core/-/fumadocs-core-15.6.3.tgz",
|
||||
"integrity": "sha512-71IPC6Y0ZLPHlavYormnF1r2uX/lNrTFTYCEh6Akll8hWxRNbKG9Hk4xpFJDTkU83c8eLtHk2iow/ccQkcV6Hw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@formatjs/intl-localematcher": "^0.6.1",
|
||||
"@orama/orama": "^3.1.9",
|
||||
|
|
@ -5177,7 +5173,6 @@
|
|||
"resolved": "https://registry.npmjs.org/next/-/next-15.5.7.tgz",
|
||||
"integrity": "sha512-+t2/0jIJ48kUpGKkdlhgkv+zPTEOoXyr60qXe68eB/pl3CMJaLeIGjzp5D6Oqt25hCBiBTt8wEeeAzfJvUKnPQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@next/env": "15.5.7",
|
||||
"@swc/helpers": "0.5.15",
|
||||
|
|
@ -5353,7 +5348,6 @@
|
|||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
|
|
@ -5469,7 +5463,6 @@
|
|||
"resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz",
|
||||
"integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
|
|
@ -5479,7 +5472,6 @@
|
|||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz",
|
||||
"integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"scheduler": "^0.26.0"
|
||||
},
|
||||
|
|
@ -6101,8 +6093,7 @@
|
|||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.11.tgz",
|
||||
"integrity": "sha512-2E9TBm6MDD/xKYe+dvJZAmg3yxIEDNRc0jwlNyDg/4Fil2QcSLjFKGVff0lAf1jjeaArlG/M75Ey/EYr/OJtBA==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tapable": {
|
||||
"version": "2.2.2",
|
||||
|
|
@ -6198,7 +6189,6 @@
|
|||
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
|
|
|
|||
|
After Width: | Height: | Size: 92 KiB |
|
After Width: | Height: | Size: 176 KiB |
|
After Width: | Height: | Size: 62 KiB |
|
After Width: | Height: | Size: 55 KiB |
|
After Width: | Height: | Size: 48 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 74 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 135 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 111 KiB |
|
Before Width: | Height: | Size: 114 KiB |
|
Before Width: | Height: | Size: 162 KiB |
|
Before Width: | Height: | Size: 160 KiB |
|
Before Width: | Height: | Size: 166 KiB |
|
Before Width: | Height: | Size: 144 KiB |
|
Before Width: | Height: | Size: 124 KiB |
|
Before Width: | Height: | Size: 132 KiB |
|
Before Width: | Height: | Size: 89 KiB |
|
Before Width: | Height: | Size: 83 KiB |
|
Before Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 192 KiB |
|
Before Width: | Height: | Size: 141 KiB |
|
Before Width: | Height: | Size: 114 KiB |
|
Before Width: | Height: | Size: 97 KiB |
|
Before Width: | Height: | Size: 99 KiB |
|
Before Width: | Height: | Size: 98 KiB |
|
Before Width: | Height: | Size: 90 KiB |
|
Before Width: | Height: | Size: 108 KiB |
|
|
@ -1,5 +1,5 @@
|
|||
import { detect } from 'jschardet';
|
||||
import { documentFileType } from './constants';
|
||||
import { imageFileType } from './constants';
|
||||
import { ChatFileTypeEnum } from '../../core/chat/constants';
|
||||
import { type UserChatItemFileItemType } from '../../core/chat/type';
|
||||
import * as fs from 'fs';
|
||||
|
|
@ -66,18 +66,17 @@ export const parseUrlToFileType = (url: string): UserChatItemFileItemType | unde
|
|||
})();
|
||||
const extension = filename?.split('.').pop()?.toLowerCase() || '';
|
||||
|
||||
// If it's a document type, return as file, otherwise treat as image
|
||||
if (extension && documentFileType.includes(extension)) {
|
||||
if (extension && imageFileType.includes(extension)) {
|
||||
// Default to file type for non-extension files
|
||||
return {
|
||||
type: ChatFileTypeEnum.file,
|
||||
type: ChatFileTypeEnum.image,
|
||||
name: filename || 'null',
|
||||
url
|
||||
};
|
||||
}
|
||||
|
||||
// Default to image type for non-document files
|
||||
// If it's a document type, return as file, otherwise treat as image
|
||||
return {
|
||||
type: ChatFileTypeEnum.image,
|
||||
type: ChatFileTypeEnum.file,
|
||||
name: filename || 'null',
|
||||
url
|
||||
};
|
||||
|
|
|
|||
|
|
@ -4,3 +4,25 @@ export const isCSVFile = (filename: string) => {
|
|||
const extension = path.extname(filename).toLowerCase();
|
||||
return extension === '.csv';
|
||||
};
|
||||
|
||||
export function detectImageContentType(buffer: Buffer) {
|
||||
if (!buffer || buffer.length < 12) return 'text/plain';
|
||||
|
||||
// JPEG
|
||||
if (buffer[0] === 0xff && buffer[1] === 0xd8 && buffer[2] === 0xff) return 'image/jpeg';
|
||||
|
||||
// PNG
|
||||
const pngSig = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a];
|
||||
if (pngSig.every((v, i) => buffer.readUInt8(i) === v)) return 'image/png';
|
||||
|
||||
// GIF
|
||||
const gifSig = buffer.subarray(0, 6).toString('ascii');
|
||||
if (gifSig === 'GIF87a' || gifSig === 'GIF89a') return 'image/gif';
|
||||
|
||||
// WEBP
|
||||
const riff = buffer.subarray(0, 4).toString('ascii');
|
||||
const webp = buffer.subarray(8, 12).toString('ascii');
|
||||
if (riff === 'RIFF' && webp === 'WEBP') return 'image/webp';
|
||||
|
||||
return 'text/plain';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -128,6 +128,17 @@ export type FastGPTFeConfigsType = {
|
|||
alipay?: boolean;
|
||||
bank?: boolean;
|
||||
};
|
||||
fileUrlWhitelist?: string[];
|
||||
customDomain?: {
|
||||
enable?: boolean;
|
||||
domain?: {
|
||||
aliyun?: string;
|
||||
tencent?: string;
|
||||
volcengine?: string;
|
||||
};
|
||||
};
|
||||
|
||||
ip_whitelist?: string;
|
||||
};
|
||||
|
||||
export type SystemEnvType = {
|
||||
|
|
@ -147,6 +158,30 @@ export type SystemEnvType = {
|
|||
|
||||
customPdfParse?: customPdfParseType;
|
||||
fileUrlWhitelist?: string[];
|
||||
customDomain?: customDomainType;
|
||||
};
|
||||
|
||||
export type customDomainType = {
|
||||
kc?: {
|
||||
aliyun?: string;
|
||||
tencent?: string;
|
||||
volcengine?: string;
|
||||
};
|
||||
domain?: {
|
||||
aliyun?: string;
|
||||
tencent?: string;
|
||||
volcengine?: string;
|
||||
};
|
||||
issuerServiceName?: {
|
||||
aliyun?: string;
|
||||
tencent?: string;
|
||||
volcengine?: string;
|
||||
};
|
||||
nginxServiceName?: {
|
||||
aliyun?: string;
|
||||
tencent?: string;
|
||||
volcengine?: string;
|
||||
};
|
||||
};
|
||||
|
||||
export type customPdfParseType = {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
import { createDocument } from 'zod-openapi';
|
||||
import { DashboardPath } from './admin/core/dashboard';
|
||||
import { TagsMap } from './tag';
|
||||
|
||||
export const adminOpenAPIDocument = createDocument({
|
||||
openapi: '3.1.0',
|
||||
info: {
|
||||
title: 'FastGPT Admin API',
|
||||
version: '0.1.0',
|
||||
description: 'FastGPT Admin API 文档'
|
||||
},
|
||||
paths: {
|
||||
...DashboardPath
|
||||
},
|
||||
servers: [{ url: '/api' }],
|
||||
'x-tagGroups': [
|
||||
{
|
||||
name: '仪表盘',
|
||||
tags: [TagsMap.adminDashboard]
|
||||
}
|
||||
]
|
||||
});
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
import { z } from 'zod';
|
||||
import { UsageSourceEnum } from '../../../../support/wallet/usage/constants';
|
||||
|
||||
// Common query schema
|
||||
export const GetDataChartsQuerySchema = z.object({
|
||||
startTime: z.string().meta({ description: '查询起始时间(ISO 8601 格式)' }),
|
||||
sources: z.array(z.enum(UsageSourceEnum)).optional().meta({ description: '使用来源筛选' })
|
||||
});
|
||||
export type GetDataChartsQueryType = z.infer<typeof GetDataChartsQuerySchema>;
|
||||
|
||||
// Get user form data response
|
||||
export const RegisteredUserCountSchema = z.object({
|
||||
date: z.string().meta({ description: '注册日期' }),
|
||||
count: z.number().meta({ description: '该日期注册的用户数' })
|
||||
});
|
||||
export const GetUserFormDataResponseSchema = z.object({
|
||||
startUserCount: z.number().meta({ description: '起始时间之前的用户总数' }),
|
||||
registeredUserCount: z.array(RegisteredUserCountSchema).meta({ description: '用户注册时间序列' })
|
||||
});
|
||||
export type GetUserFormDataResponseType = z.infer<typeof GetUserFormDataResponseSchema>;
|
||||
|
||||
// Get Pays Form Data Response
|
||||
export const OrderAmountSchema = z.object({
|
||||
date: z.string().meta({ description: '数据点日期' }),
|
||||
totalCount: z.number().meta({ description: '订单总数' }),
|
||||
successCount: z.number().meta({ description: '成功订单数' })
|
||||
});
|
||||
export const PayAmountSchema = z.object({
|
||||
date: z.string().meta({ description: '数据点日期' }),
|
||||
totalCount: z.number().meta({ description: '支付总金额' })
|
||||
});
|
||||
export const PayTeamSchema = z.object({
|
||||
date: z.string().meta({ description: '数据点日期' }),
|
||||
totalCount: z.number().meta({ description: '支付团队数' })
|
||||
});
|
||||
export const GetPaysFormDataResponseSchema = z.object({
|
||||
orderAmounts: z.array(OrderAmountSchema).meta({ description: '订单数量时间序列' }),
|
||||
payAmounts: z.array(PayAmountSchema).meta({ description: '支付金额时间序列' }),
|
||||
payTeams: z.array(PayTeamSchema).meta({ description: '支付团队时间序列' })
|
||||
});
|
||||
export type GetPaysFormDataResponseType = z.infer<typeof GetPaysFormDataResponseSchema>;
|
||||
|
||||
// Get chat form data response
|
||||
export const ChatAmountSchema = z.object({
|
||||
date: z.string().meta({ description: '数据点日期' }),
|
||||
totalCount: z.number().meta({ description: '对话总数' })
|
||||
});
|
||||
export const ChatItemAmountSchema = z.object({
|
||||
date: z.string().meta({ description: '数据点日期' }),
|
||||
totalCount: z.number().meta({ description: '对话消息总数' }),
|
||||
averageCount: z.number().meta({ description: '每个对话的平均消息数' })
|
||||
});
|
||||
export const GetChatFormDataResponseSchema = z.object({
|
||||
chatAmounts: z.array(ChatAmountSchema).meta({ description: '对话数量时间序列' }),
|
||||
chatItemAmounts: z.array(ChatItemAmountSchema).meta({ description: '对话消息数量时间序列' })
|
||||
});
|
||||
export type GetChatFormDataResponseType = z.infer<typeof GetChatFormDataResponseSchema>;
|
||||
|
||||
// Get QPM range distribution response
|
||||
export const QpmRangeSchema = z.object({
|
||||
range: z.string().meta({ description: 'QPM 范围标签' }),
|
||||
count: z.number().meta({ description: '范围内的团队数量' })
|
||||
});
|
||||
export const GetQpmRangeResponseSchema = z.object({
|
||||
ranges: z.array(QpmRangeSchema).meta({ description: 'QPM 范围统计列表' })
|
||||
});
|
||||
export type GetQpmRangeResponseType = z.infer<typeof GetQpmRangeResponseSchema>;
|
||||
|
||||
// Get cost form data response
|
||||
export const PointUsageSchema = z.object({
|
||||
date: z.string().meta({ description: '数据点日期' }),
|
||||
totalCount: z.number().meta({ description: '积分使用总数' })
|
||||
});
|
||||
export const GetCostFormDataResponseSchema = z.object({
|
||||
pointUsages: z.array(PointUsageSchema).meta({ description: '积分使用时间序列' })
|
||||
});
|
||||
export type GetCostFormDataResponseType = z.infer<typeof GetCostFormDataResponseSchema>;
|
||||
|
||||
// Get user stats response
|
||||
export const GetUserStatsResponseSchema = z.object({
|
||||
usersCount: z.number().meta({ description: '用户总数' }),
|
||||
rechargeCount: z.number().meta({ description: '充值总数' })
|
||||
});
|
||||
export type GetUserStatsResponseType = z.infer<typeof GetUserStatsResponseSchema>;
|
||||
|
||||
// Get app stats response
|
||||
export const GetAppStatsResponseSchema = z.object({
|
||||
workflowCount: z.number().meta({ description: '工作流总数' }),
|
||||
simpleAppCount: z.number().meta({ description: '简易应用总数' }),
|
||||
workflowToolCount: z.number().meta({ description: '工作流工具总数' }),
|
||||
httpToolCount: z.number().meta({ description: 'HTTP 工具总数' }),
|
||||
mcpToolCount: z.number().meta({ description: 'MCP 工具总数' })
|
||||
});
|
||||
export type GetAppStatsResponseType = z.infer<typeof GetAppStatsResponseSchema>;
|
||||
|
||||
// Get dataset stats response
|
||||
export const GetDatasetStatsResponseSchema = z.object({
|
||||
commonDatasetCount: z.number().meta({ description: '通用知识库总数' }),
|
||||
websiteDatasetCount: z.number().meta({ description: 'Web 站点同步总数' }),
|
||||
apiDatasetCount: z.number().meta({ description: 'API 知识库总数' }),
|
||||
yuqueDatasetCount: z.number().meta({ description: '语雀知识库总数' }),
|
||||
feishuDatasetCount: z.number().meta({ description: '飞书知识库总数' }),
|
||||
totalIndexCount: z.number().meta({ description: '索引总量' })
|
||||
});
|
||||
export type GetDatasetStatsResponseType = z.infer<typeof GetDatasetStatsResponseSchema>;
|
||||
|
|
@ -0,0 +1,190 @@
|
|||
import { z } from 'zod';
|
||||
import type { OpenAPIPath } from '../../../type';
|
||||
import {
|
||||
GetDataChartsQuerySchema,
|
||||
GetChatFormDataResponseSchema,
|
||||
GetCostFormDataResponseSchema,
|
||||
GetPaysFormDataResponseSchema,
|
||||
GetUserFormDataResponseSchema,
|
||||
GetQpmRangeResponseSchema,
|
||||
GetUserStatsResponseSchema,
|
||||
GetAppStatsResponseSchema,
|
||||
GetDatasetStatsResponseSchema
|
||||
} from './api';
|
||||
import { TagsMap } from '../../../tag';
|
||||
|
||||
export * from './api';
|
||||
|
||||
export const DashboardPath: OpenAPIPath = {
|
||||
'/admin/core/dashboard/getUserStats': {
|
||||
get: {
|
||||
summary: '获取用户全局统计',
|
||||
description: '获取用户总数和充值总数',
|
||||
tags: [TagsMap.adminDashboard],
|
||||
responses: {
|
||||
200: {
|
||||
description: '成功获取用户统计',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: GetUserStatsResponseSchema
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
'/admin/core/dashboard/getAppStats': {
|
||||
get: {
|
||||
summary: '获取应用全局统计',
|
||||
description: '获取工作流、简易应用、工作流工具、HTTP 工具和 MCP 工具的总数',
|
||||
tags: [TagsMap.adminDashboard],
|
||||
responses: {
|
||||
200: {
|
||||
description: '成功获取应用统计',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: GetAppStatsResponseSchema
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
'/admin/core/dashboard/getDatasetStats': {
|
||||
get: {
|
||||
summary: '获取知识库全局统计',
|
||||
description: '获取通用知识库、Web 站点同步、API、语雀、飞书知识库的总数以及索引总量',
|
||||
tags: [TagsMap.adminDashboard],
|
||||
responses: {
|
||||
200: {
|
||||
description: '成功获取知识库统计',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: GetDatasetStatsResponseSchema
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
'/admin/core/dashboard/getChatFormData': {
|
||||
get: {
|
||||
summary: '获取对话统计数据',
|
||||
description: '获取对话数量和对话消息数量的时间序列统计数据',
|
||||
tags: [TagsMap.adminDashboard],
|
||||
requestParams: {
|
||||
query: z.object({
|
||||
startTime: z.string().meta({
|
||||
description: '查询起始时间(ISO 8601 格式)'
|
||||
})
|
||||
})
|
||||
},
|
||||
responses: {
|
||||
200: {
|
||||
description: '成功获取对话统计数据',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: GetChatFormDataResponseSchema
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
'/admin/core/dashboard/getWorkflowQpmRange': {
|
||||
get: {
|
||||
summary: '获取工作流 QPM 范围分布',
|
||||
description: '按团队最大 QPM 统计各范围的团队数量',
|
||||
tags: [TagsMap.adminDashboard],
|
||||
requestParams: {
|
||||
query: z.object({
|
||||
startTime: z.string().meta({
|
||||
description: '查询起始时间(ISO 8601 格式)'
|
||||
})
|
||||
})
|
||||
},
|
||||
responses: {
|
||||
200: {
|
||||
description: '成功获取 QPM 范围分布',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: GetQpmRangeResponseSchema
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
'/admin/core/dashboard/getCostFormData': {
|
||||
post: {
|
||||
summary: '获取消费统计数据',
|
||||
description: '获取积分消耗的时间序列统计数据',
|
||||
tags: [TagsMap.adminDashboard],
|
||||
requestBody: {
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: GetDataChartsQuerySchema
|
||||
}
|
||||
}
|
||||
},
|
||||
responses: {
|
||||
200: {
|
||||
description: '成功获取消费统计数据',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: GetCostFormDataResponseSchema
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
'/admin/core/dashboard/getPaysFormData': {
|
||||
get: {
|
||||
summary: '获取支付统计数据',
|
||||
description: '获取订单和支付金额的时间序列统计数据',
|
||||
tags: [TagsMap.adminDashboard],
|
||||
requestParams: {
|
||||
query: z.object({
|
||||
startTime: z.string().meta({
|
||||
description: '查询起始时间(ISO 8601 格式)'
|
||||
})
|
||||
})
|
||||
},
|
||||
responses: {
|
||||
200: {
|
||||
description: '成功获取支付统计数据',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: GetPaysFormDataResponseSchema
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
'/admin/core/dashboard/getUserFormData': {
|
||||
get: {
|
||||
summary: '获取用户注册统计数据',
|
||||
description: '获取用户注册数量的时间序列统计数据',
|
||||
tags: [TagsMap.adminDashboard],
|
||||
requestParams: {
|
||||
query: z.object({
|
||||
startTime: z.string().meta({
|
||||
description: '查询起始时间(ISO 8601 格式)'
|
||||
})
|
||||
})
|
||||
},
|
||||
responses: {
|
||||
200: {
|
||||
description: '成功获取用户统计数据',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: GetUserFormDataResponseSchema
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -8,5 +8,7 @@ export const TagsMap = {
|
|||
pluginTeam: '团队插件管理',
|
||||
apiKey: 'APIKey',
|
||||
walletBill: '订单',
|
||||
walletDiscountCoupon: '优惠券'
|
||||
walletDiscountCoupon: '优惠券',
|
||||
|
||||
adminDashboard: '管理员仪表盘'
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
import z from 'zod';
|
||||
|
||||
export const CustomDomainStatusEnum = z.enum([
|
||||
'active', // confirm to active
|
||||
'inactive' // scheduled task find DNS Resolve is down
|
||||
]);
|
||||
|
||||
export const ProviderEnum = z.enum(['aliyun', 'tencent', 'volcengine']);
|
||||
export const VerifyFileType = z.object({
|
||||
path: z.string(),
|
||||
content: z.string()
|
||||
});
|
||||
export const CustomDomainType = z.object({
|
||||
teamId: z.string(),
|
||||
domain: z.string(),
|
||||
cnameDomain: z.string(),
|
||||
status: CustomDomainStatusEnum,
|
||||
verifyFile: VerifyFileType.optional(),
|
||||
provider: ProviderEnum
|
||||
});
|
||||
|
||||
export type VerifyFileType = z.infer<typeof VerifyFileType>;
|
||||
export type CustomDomainType = z.infer<typeof CustomDomainType>;
|
||||
|
||||
export type CreateCustomDomainBody = {
|
||||
domain: string;
|
||||
provider: ProviderEnum;
|
||||
cnameDomain: string;
|
||||
};
|
||||
export type ProviderEnum = z.infer<typeof ProviderEnum>;
|
||||
export type CustomDomainStatusEnum = z.infer<typeof CustomDomainStatusEnum>;
|
||||
|
||||
export type UpdateDomainVerifyFileBody = {
|
||||
domain: string;
|
||||
path: string;
|
||||
content: string;
|
||||
};
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
import { customAlphabet } from 'nanoid';
|
||||
|
||||
/**
|
||||
* @param domain : should be like sealosbja.site (secondary domain)
|
||||
* @returns CNAME domain: fastgpt-<random_string>.<domain>
|
||||
*/
|
||||
export const generateCNAMEDomain = (domain: string): string => {
|
||||
const str = customAlphabet('abcdefghijklmnopqrstuvwxyz', 8);
|
||||
return `fastgpt-${str()}.${domain}`;
|
||||
};
|
||||
|
|
@ -20,11 +20,15 @@ export interface DingtalkAppType {
|
|||
}
|
||||
|
||||
export interface WecomAppType {
|
||||
AgentId: string;
|
||||
CorpId: string;
|
||||
SuiteSecret: string;
|
||||
CallbackToken: string;
|
||||
CallbackEncodingAesKey: string;
|
||||
|
||||
/** @deprecated */
|
||||
// AgentId: string;
|
||||
/** @deprecated */
|
||||
// CorpId: string;
|
||||
/** @deprecated */
|
||||
// SuiteSecret: string;
|
||||
}
|
||||
|
||||
// TODO: unused
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ export type CustomSubConfig = {
|
|||
appRegistrationCount: number;
|
||||
auditLogStoreDuration: number;
|
||||
ticketResponseTime: number;
|
||||
customDomain: number;
|
||||
};
|
||||
|
||||
export type TeamCouponSub = {
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ export type TeamStandardSubPlanItemType = {
|
|||
websiteSyncPerDataset?: number;
|
||||
auditLogStoreDuration?: number;
|
||||
ticketResponseTime?: number;
|
||||
customDomain?: number;
|
||||
|
||||
// Custom plan specific fields
|
||||
priceDescription?: string;
|
||||
|
|
@ -73,6 +74,7 @@ export type TeamSubSchema = {
|
|||
appRegistrationCount?: number;
|
||||
auditLogStoreDuration?: number;
|
||||
ticketResponseTime?: number;
|
||||
customDomain?: number;
|
||||
|
||||
totalPoints: number;
|
||||
surplusPoints: number;
|
||||
|
|
|
|||
|
|
@ -11,19 +11,24 @@ const TrackSchema = new Schema({
|
|||
data: Object
|
||||
});
|
||||
|
||||
try {
|
||||
TrackSchema.index({ event: 1 });
|
||||
|
||||
TrackSchema.index(
|
||||
{ event: 1, teamId: 1, 'data.datasetId': 1, createTime: -1 },
|
||||
{
|
||||
partialFilterExpression: {
|
||||
'data.datasetId': { $exists: true }
|
||||
}
|
||||
TrackSchema.index({ event: 1 });
|
||||
// Dataset search index
|
||||
TrackSchema.index(
|
||||
{ event: 1, teamId: 1, 'data.datasetId': 1, createTime: -1 },
|
||||
{
|
||||
partialFilterExpression: {
|
||||
event: TrackEnum.datasetSearch
|
||||
}
|
||||
);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
);
|
||||
// QPM index
|
||||
TrackSchema.index(
|
||||
{ event: 1, createTime: -1, 'data.requestCount': 1 },
|
||||
{
|
||||
partialFilterExpression: {
|
||||
event: TrackEnum.teamChatQPM
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
export const TrackModel = getMongoModel<TrackSchemaType>('track', TrackSchema);
|
||||
export const TrackModel = getMongoModel<TrackSchemaType>('tracks', TrackSchema);
|
||||
|
|
|
|||
|
|
@ -10,11 +10,11 @@ import {
|
|||
import { defaultS3Options, getSystemMaxFileSize, Mimes } from '../constants';
|
||||
import path from 'node:path';
|
||||
import { MongoS3TTL } from '../schema';
|
||||
import { getNanoid } from '@fastgpt/global/common/string/tools';
|
||||
import { addHours, addMinutes } from 'date-fns';
|
||||
import { addLog } from '../../system/log';
|
||||
import { addS3DelJob } from '../mq';
|
||||
import { type Readable } from 'node:stream';
|
||||
import { type UploadFileByBufferParams, UploadFileByBufferSchema } from '../type';
|
||||
|
||||
export class S3BaseBucket {
|
||||
private _client: Client;
|
||||
|
|
@ -62,7 +62,9 @@ export class S3BaseBucket {
|
|||
await this.options.afterInit?.();
|
||||
console.log(`S3 init success: ${this.name}`);
|
||||
};
|
||||
init();
|
||||
if (this.options.init) {
|
||||
init();
|
||||
}
|
||||
}
|
||||
|
||||
get name(): string {
|
||||
|
|
@ -251,4 +253,25 @@ export class S3BaseBucket {
|
|||
|
||||
return await this.client.presignedGetObject(this.name, key, expires);
|
||||
}
|
||||
|
||||
async uploadFileByBuffer(params: UploadFileByBufferParams) {
|
||||
const { key, buffer, contentType } = UploadFileByBufferSchema.parse(params);
|
||||
|
||||
await MongoS3TTL.create({
|
||||
minioKey: key,
|
||||
bucketName: this.name,
|
||||
expiredTime: addHours(new Date(), 1)
|
||||
});
|
||||
await this.putObject(key, buffer, undefined, {
|
||||
'Content-Type': contentType || 'application/octet-stream'
|
||||
});
|
||||
|
||||
return {
|
||||
key,
|
||||
accessUrl: await this.createExternalUrl({
|
||||
key,
|
||||
expiredHours: 2
|
||||
})
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ export const Mimes = {
|
|||
export const defaultS3Options: {
|
||||
externalBaseURL?: string;
|
||||
afterInit?: () => Promise<void> | void;
|
||||
init?: boolean;
|
||||
} & ClientOptions = {
|
||||
useSSL: process.env.S3_USE_SSL === 'true',
|
||||
endPoint: process.env.S3_ENDPOINT || 'localhost',
|
||||
|
|
@ -53,8 +54,4 @@ export const getSystemMaxFileSize = () => {
|
|||
return config; // bytes
|
||||
};
|
||||
|
||||
export const S3_KEY_PATH_INVALID_CHARS_MAP: Record<string, boolean> = {
|
||||
'/': true,
|
||||
'\\': true,
|
||||
'|': true
|
||||
};
|
||||
export const S3_KEY_PATH_INVALID_CHARS = /[|\\/]/;
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ import { addLog } from '../system/log';
|
|||
import { startS3DelWorker } from './mq';
|
||||
|
||||
export function initS3Buckets() {
|
||||
const publicBucket = new S3PublicBucket();
|
||||
const privateBucket = new S3PrivateBucket();
|
||||
const publicBucket = new S3PublicBucket({ init: true });
|
||||
const privateBucket = new S3PrivateBucket({ init: true });
|
||||
|
||||
global.s3BucketMap = {
|
||||
[publicBucket.name]: publicBucket,
|
||||
|
|
|
|||
|
|
@ -7,16 +7,11 @@ import { getFileS3Key } from '../utils';
|
|||
|
||||
class S3AvatarSource {
|
||||
private bucket: S3PublicBucket;
|
||||
private static instance: S3AvatarSource;
|
||||
|
||||
constructor() {
|
||||
this.bucket = new S3PublicBucket();
|
||||
}
|
||||
|
||||
static getInstance() {
|
||||
return (this.instance ??= new S3AvatarSource());
|
||||
}
|
||||
|
||||
get prefix(): string {
|
||||
return imageBaseUrl;
|
||||
}
|
||||
|
|
@ -89,5 +84,13 @@ class S3AvatarSource {
|
|||
}
|
||||
|
||||
export function getS3AvatarSource() {
|
||||
return S3AvatarSource.getInstance();
|
||||
if (global.avatarBucket) {
|
||||
return global.avatarBucket;
|
||||
}
|
||||
global.avatarBucket = new S3AvatarSource();
|
||||
return global.avatarBucket;
|
||||
}
|
||||
|
||||
declare global {
|
||||
var avatarBucket: S3AvatarSource;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,9 @@ import {
|
|||
type CheckChatFileKeys,
|
||||
type DelChatFileByPrefixParams,
|
||||
ChatFileUploadSchema,
|
||||
DelChatFileByPrefixSchema
|
||||
DelChatFileByPrefixSchema,
|
||||
UploadChatFileSchema,
|
||||
type UploadFileParams
|
||||
} from './type';
|
||||
import { differenceInHours } from 'date-fns';
|
||||
import { S3Buckets } from '../../constants';
|
||||
|
|
@ -14,16 +16,11 @@ import { getFileS3Key } from '../../utils';
|
|||
|
||||
export class S3ChatSource {
|
||||
private bucket: S3PrivateBucket;
|
||||
private static instance: S3ChatSource;
|
||||
|
||||
constructor() {
|
||||
this.bucket = new S3PrivateBucket();
|
||||
}
|
||||
|
||||
static getInstance() {
|
||||
return (this.instance ??= new S3ChatSource());
|
||||
}
|
||||
|
||||
static parseChatUrl(url: string | URL) {
|
||||
try {
|
||||
const parseUrl = new URL(url);
|
||||
|
|
@ -95,7 +92,7 @@ export class S3ChatSource {
|
|||
const { fileKey } = getFileS3Key.chat({ appId, chatId, uId, filename });
|
||||
return await this.bucket.createPostPresignedUrl(
|
||||
{ rawKey: fileKey, filename },
|
||||
{ expiredHours: expiredTime ? differenceInHours(new Date(), expiredTime) : 24 }
|
||||
{ expiredHours: expiredTime ? differenceInHours(expiredTime, new Date()) : 24 }
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -109,8 +106,33 @@ export class S3ChatSource {
|
|||
deleteChatFileByKey(key: string) {
|
||||
return this.bucket.addDeleteJob({ key });
|
||||
}
|
||||
|
||||
async uploadChatFileByBuffer(params: UploadFileParams) {
|
||||
const { appId, chatId, uId, filename, buffer, contentType } =
|
||||
UploadChatFileSchema.parse(params);
|
||||
const { fileKey } = getFileS3Key.chat({
|
||||
appId,
|
||||
chatId,
|
||||
uId,
|
||||
filename
|
||||
});
|
||||
|
||||
return this.bucket.uploadFileByBuffer({
|
||||
key: fileKey,
|
||||
buffer,
|
||||
contentType
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function getS3ChatSource() {
|
||||
return S3ChatSource.getInstance();
|
||||
if (global.chatBucket) {
|
||||
return global.chatBucket;
|
||||
}
|
||||
global.chatBucket = new S3ChatSource();
|
||||
return global.chatBucket;
|
||||
}
|
||||
|
||||
declare global {
|
||||
var chatBucket: S3ChatSource;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,3 +16,14 @@ export const DelChatFileByPrefixSchema = z.object({
|
|||
uId: z.string().nonempty().optional()
|
||||
});
|
||||
export type DelChatFileByPrefixParams = z.infer<typeof DelChatFileByPrefixSchema>;
|
||||
|
||||
export const UploadChatFileSchema = z.object({
|
||||
appId: ObjectIdSchema,
|
||||
chatId: z.string().nonempty(),
|
||||
uId: z.string().nonempty(),
|
||||
filename: z.string().nonempty(),
|
||||
buffer: z.instanceof(Buffer),
|
||||
contentType: z.string().optional()
|
||||
});
|
||||
|
||||
export type UploadFileParams = z.infer<typeof UploadChatFileSchema>;
|
||||
|
|
|
|||
|
|
@ -29,16 +29,11 @@ import { S3Error } from 'minio';
|
|||
|
||||
export class S3DatasetSource {
|
||||
public bucket: S3PrivateBucket;
|
||||
private static instance: S3DatasetSource;
|
||||
|
||||
constructor() {
|
||||
this.bucket = new S3PrivateBucket();
|
||||
}
|
||||
|
||||
static getInstance() {
|
||||
return (this.instance ??= new S3DatasetSource());
|
||||
}
|
||||
|
||||
// 下载链接
|
||||
async createGetDatasetFileURL(params: CreateGetDatasetFileURLParams) {
|
||||
const { key, expiredHours, external } = CreateGetDatasetFileURLParamsSchema.parse(params);
|
||||
|
|
@ -260,5 +255,13 @@ export class S3DatasetSource {
|
|||
}
|
||||
|
||||
export function getS3DatasetSource() {
|
||||
return S3DatasetSource.getInstance();
|
||||
if (global.datasetBucket) {
|
||||
return global.datasetBucket;
|
||||
}
|
||||
global.datasetBucket = new S3DatasetSource();
|
||||
return global.datasetBucket;
|
||||
}
|
||||
|
||||
declare global {
|
||||
var datasetBucket: S3DatasetSource;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,6 +56,13 @@ export const UploadImage2S3BucketParamsSchema = z.object({
|
|||
});
|
||||
export type UploadImage2S3BucketParams = z.infer<typeof UploadImage2S3BucketParamsSchema>;
|
||||
|
||||
export const UploadFileByBufferSchema = z.object({
|
||||
buffer: z.instanceof(Buffer),
|
||||
contentType: z.string().optional(),
|
||||
key: z.string().nonempty()
|
||||
});
|
||||
export type UploadFileByBufferParams = z.infer<typeof UploadFileByBufferSchema>;
|
||||
|
||||
declare global {
|
||||
var s3BucketMap: {
|
||||
[key: string]: S3BaseBucket;
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ import { getNanoid } from '@fastgpt/global/common/string/tools';
|
|||
import path from 'node:path';
|
||||
import type { ParsedFileContentS3KeyParams } from './sources/dataset/type';
|
||||
import { EndpointUrl } from '@fastgpt/global/common/file/constants';
|
||||
import type { NextApiRequest } from 'next';
|
||||
|
||||
// S3文件名最大长度配置
|
||||
export const S3_FILENAME_MAX_LENGTH = 50;
|
||||
|
|
@ -246,130 +245,3 @@ export function isS3ObjectKey<T extends keyof typeof S3Sources>(
|
|||
): key is `${T}/${string}` {
|
||||
return typeof key === 'string' && key.startsWith(`${S3Sources[source]}/`);
|
||||
}
|
||||
|
||||
// export const multer = {
|
||||
// _storage: multer.diskStorage({
|
||||
// filename: (_, file, cb) => {
|
||||
// if (!file?.originalname) {
|
||||
// cb(new Error('File not found'), '');
|
||||
// } else {
|
||||
// const ext = path.extname(decodeURIComponent(file.originalname));
|
||||
// cb(null, `${getNanoid()}${ext}`);
|
||||
// }
|
||||
// }
|
||||
// }),
|
||||
|
||||
// singleStore(maxFileSize: number = 500) {
|
||||
// const fileSize = maxFileSize * 1024 * 1024;
|
||||
|
||||
// return multer({
|
||||
// limits: {
|
||||
// fileSize
|
||||
// },
|
||||
// preservePath: true,
|
||||
// storage: this._storage
|
||||
// }).single('file');
|
||||
// },
|
||||
|
||||
// multipleStore(maxFileSize: number = 500) {
|
||||
// const fileSize = maxFileSize * 1024 * 1024;
|
||||
|
||||
// return multer({
|
||||
// limits: {
|
||||
// fileSize
|
||||
// },
|
||||
// preservePath: true,
|
||||
// storage: this._storage
|
||||
// }).array('file', global.feConfigs?.uploadFileMaxSize);
|
||||
// },
|
||||
|
||||
// resolveFormData({ request, maxFileSize }: { request: NextApiRequest; maxFileSize?: number }) {
|
||||
// return new Promise<{
|
||||
// data: Record<string, any>;
|
||||
// fileMetadata: Express.Multer.File;
|
||||
// getBuffer: () => Buffer;
|
||||
// getReadStream: () => fs.ReadStream;
|
||||
// }>((resolve, reject) => {
|
||||
// const handler = this.singleStore(maxFileSize);
|
||||
|
||||
// // @ts-expect-error it can accept a NextApiRequest
|
||||
// handler(request, null, (error) => {
|
||||
// if (error) {
|
||||
// return reject(error);
|
||||
// }
|
||||
|
||||
// // @ts-expect-error `file` will be injected by multer
|
||||
// const file = request.file as Express.Multer.File;
|
||||
|
||||
// if (!file) {
|
||||
// return reject(new Error('File not found'));
|
||||
// }
|
||||
|
||||
// const data = (() => {
|
||||
// if (!request.body?.data) return {};
|
||||
// try {
|
||||
// return JSON.parse(request.body.data);
|
||||
// } catch {
|
||||
// return {};
|
||||
// }
|
||||
// })();
|
||||
|
||||
// resolve({
|
||||
// data,
|
||||
// fileMetadata: file,
|
||||
// getBuffer: () => fs.readFileSync(file.path),
|
||||
// getReadStream: () => fs.createReadStream(file.path)
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
// },
|
||||
|
||||
// resolveMultipleFormData({
|
||||
// request,
|
||||
// maxFileSize
|
||||
// }: {
|
||||
// request: NextApiRequest;
|
||||
// maxFileSize?: number;
|
||||
// }) {
|
||||
// return new Promise<{
|
||||
// data: Record<string, any>;
|
||||
// fileMetadata: Array<Express.Multer.File>;
|
||||
// }>((resolve, reject) => {
|
||||
// const handler = this.multipleStore(maxFileSize);
|
||||
|
||||
// // @ts-expect-error it can accept a NextApiRequest
|
||||
// handler(request, null, (error) => {
|
||||
// if (error) {
|
||||
// return reject(error);
|
||||
// }
|
||||
|
||||
// // @ts-expect-error `files` will be injected by multer
|
||||
// const files = request.files as Array<Express.Multer.File>;
|
||||
|
||||
// if (!files || files.length === 0) {
|
||||
// return reject(new Error('File not found'));
|
||||
// }
|
||||
|
||||
// const data = (() => {
|
||||
// if (!request.body?.data) return {};
|
||||
// try {
|
||||
// return JSON.parse(request.body.data);
|
||||
// } catch {
|
||||
// return {};
|
||||
// }
|
||||
// })();
|
||||
|
||||
// resolve({
|
||||
// data,
|
||||
// fileMetadata: files
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
// },
|
||||
|
||||
// clearDiskTempFiles(filepaths: string[]) {
|
||||
// for (const filepath of filepaths) {
|
||||
// fs.unlink(filepath, (_) => {});
|
||||
// }
|
||||
// }
|
||||
// };
|
||||
|
|
|
|||
|
|
@ -64,13 +64,14 @@ export const recallFromVectorStore = (props: EmbeddingRecallCtrlProps) =>
|
|||
retryFn(() => Vector.embRecall(props));
|
||||
export const getVectorDataByTime = Vector.getVectorDataByTime;
|
||||
|
||||
// Count vector
|
||||
export const getVectorCountByTeamId = async (teamId: string) => {
|
||||
const cacheCount = await teamVectorCache.get(teamId);
|
||||
if (cacheCount !== undefined) {
|
||||
return cacheCount;
|
||||
}
|
||||
|
||||
const count = await Vector.getVectorCountByTeamId(teamId);
|
||||
const count = await Vector.getVectorCount({ teamId });
|
||||
|
||||
teamVectorCache.set({
|
||||
teamId,
|
||||
|
|
@ -79,9 +80,7 @@ export const getVectorCountByTeamId = async (teamId: string) => {
|
|||
|
||||
return count;
|
||||
};
|
||||
|
||||
export const getVectorCountByDatasetId = Vector.getVectorCountByDatasetId;
|
||||
export const getVectorCountByCollectionId = Vector.getVectorCountByCollectionId;
|
||||
export const getVectorCount = Vector.getVectorCount;
|
||||
|
||||
export const insertDatasetDataVector = async ({
|
||||
model,
|
||||
|
|
|
|||
|
|
@ -257,43 +257,36 @@ export class MilvusCtrl {
|
|||
};
|
||||
};
|
||||
|
||||
getVectorCountByTeamId = async (teamId: string) => {
|
||||
getVectorCount = async (props: {
|
||||
teamId?: string;
|
||||
datasetId?: string;
|
||||
collectionId?: string;
|
||||
}) => {
|
||||
const { teamId, datasetId, collectionId } = props;
|
||||
const client = await this.getClient();
|
||||
|
||||
// Build filter conditions dynamically (each condition wrapped in parentheses)
|
||||
const filterConditions: string[] = [];
|
||||
|
||||
if (teamId) {
|
||||
filterConditions.push(`(teamId == "${String(teamId)}")`);
|
||||
}
|
||||
|
||||
if (datasetId) {
|
||||
filterConditions.push(`(datasetId == "${String(datasetId)}")`);
|
||||
}
|
||||
|
||||
if (collectionId) {
|
||||
filterConditions.push(`(collectionId == "${String(collectionId)}")`);
|
||||
}
|
||||
|
||||
// If no conditions provided, count all (empty filter)
|
||||
const filter = filterConditions.length > 0 ? filterConditions.join(' and ') : '';
|
||||
|
||||
const result = await client.query({
|
||||
collection_name: DatasetVectorTableName,
|
||||
output_fields: ['count(*)'],
|
||||
filter: `teamId == "${String(teamId)}"`
|
||||
});
|
||||
|
||||
const total = result.data?.[0]?.['count(*)'] as number;
|
||||
|
||||
return total;
|
||||
};
|
||||
getVectorCountByDatasetId = async (teamId: string, datasetId: string) => {
|
||||
const client = await this.getClient();
|
||||
|
||||
const result = await client.query({
|
||||
collection_name: DatasetVectorTableName,
|
||||
output_fields: ['count(*)'],
|
||||
filter: `(teamId == "${String(teamId)}") and (dataset == "${String(datasetId)}")`
|
||||
});
|
||||
|
||||
const total = result.data?.[0]?.['count(*)'] as number;
|
||||
|
||||
return total;
|
||||
};
|
||||
getVectorCountByCollectionId = async (
|
||||
teamId: string,
|
||||
datasetId: string,
|
||||
collectionId: string
|
||||
) => {
|
||||
const client = await this.getClient();
|
||||
|
||||
const result = await client.query({
|
||||
collection_name: DatasetVectorTableName,
|
||||
output_fields: ['count(*)'],
|
||||
filter: `(teamId == "${String(teamId)}") and (datasetId == "${String(datasetId)}") and (collectionId == "${String(collectionId)}")`
|
||||
filter: filter || undefined
|
||||
});
|
||||
|
||||
const total = result.data?.[0]?.['count(*)'] as number;
|
||||
|
|
|
|||
|
|
@ -180,33 +180,34 @@ export class ObVectorCtrl {
|
|||
datasetId: item.dataset_id
|
||||
}));
|
||||
};
|
||||
getVectorCountByTeamId = async (teamId: string) => {
|
||||
const total = await ObClient.count(DatasetVectorTableName, {
|
||||
where: [['team_id', String(teamId)]]
|
||||
});
|
||||
|
||||
return total;
|
||||
};
|
||||
getVectorCountByDatasetId = async (teamId: string, datasetId: string) => {
|
||||
const total = await ObClient.count(DatasetVectorTableName, {
|
||||
where: [['team_id', String(teamId)], 'and', ['dataset_id', String(datasetId)]]
|
||||
});
|
||||
getVectorCount = async (props: {
|
||||
teamId?: string;
|
||||
datasetId?: string;
|
||||
collectionId?: string;
|
||||
}) => {
|
||||
const { teamId, datasetId, collectionId } = props;
|
||||
|
||||
return total;
|
||||
};
|
||||
getVectorCountByCollectionId = async (
|
||||
teamId: string,
|
||||
datasetId: string,
|
||||
collectionId: string
|
||||
) => {
|
||||
// Build where conditions dynamically
|
||||
const whereConditions: any[] = [];
|
||||
|
||||
if (teamId) {
|
||||
whereConditions.push(['team_id', String(teamId)]);
|
||||
}
|
||||
|
||||
if (datasetId) {
|
||||
if (whereConditions.length > 0) whereConditions.push('and');
|
||||
whereConditions.push(['dataset_id', String(datasetId)]);
|
||||
}
|
||||
|
||||
if (collectionId) {
|
||||
if (whereConditions.length > 0) whereConditions.push('and');
|
||||
whereConditions.push(['collection_id', String(collectionId)]);
|
||||
}
|
||||
|
||||
// If no conditions provided, count all
|
||||
const total = await ObClient.count(DatasetVectorTableName, {
|
||||
where: [
|
||||
['team_id', String(teamId)],
|
||||
'and',
|
||||
['dataset_id', String(datasetId)],
|
||||
'and',
|
||||
['collection_id', String(collectionId)]
|
||||
]
|
||||
where: whereConditions.length > 0 ? whereConditions : undefined
|
||||
});
|
||||
|
||||
return total;
|
||||
|
|
|
|||
|
|
@ -204,33 +204,34 @@ export class PgVectorCtrl {
|
|||
datasetId: item.dataset_id
|
||||
}));
|
||||
};
|
||||
getVectorCountByTeamId = async (teamId: string) => {
|
||||
const total = await PgClient.count(DatasetVectorTableName, {
|
||||
where: [['team_id', String(teamId)]]
|
||||
});
|
||||
|
||||
return total;
|
||||
};
|
||||
getVectorCountByDatasetId = async (teamId: string, datasetId: string) => {
|
||||
const total = await PgClient.count(DatasetVectorTableName, {
|
||||
where: [['team_id', String(teamId)], 'and', ['dataset_id', String(datasetId)]]
|
||||
});
|
||||
getVectorCount = async (props: {
|
||||
teamId?: string;
|
||||
datasetId?: string;
|
||||
collectionId?: string;
|
||||
}) => {
|
||||
const { teamId, datasetId, collectionId } = props;
|
||||
|
||||
return total;
|
||||
};
|
||||
getVectorCountByCollectionId = async (
|
||||
teamId: string,
|
||||
datasetId: string,
|
||||
collectionId: string
|
||||
) => {
|
||||
// Build where conditions dynamically
|
||||
const whereConditions: any[] = [];
|
||||
|
||||
if (teamId) {
|
||||
whereConditions.push(['team_id', String(teamId)]);
|
||||
}
|
||||
|
||||
if (datasetId) {
|
||||
if (whereConditions.length > 0) whereConditions.push('and');
|
||||
whereConditions.push(['dataset_id', String(datasetId)]);
|
||||
}
|
||||
|
||||
if (collectionId) {
|
||||
if (whereConditions.length > 0) whereConditions.push('and');
|
||||
whereConditions.push(['collection_id', String(collectionId)]);
|
||||
}
|
||||
|
||||
// If no conditions provided, count all
|
||||
const total = await PgClient.count(DatasetVectorTableName, {
|
||||
where: [
|
||||
['team_id', String(teamId)],
|
||||
'and',
|
||||
['dataset_id', String(datasetId)],
|
||||
'and',
|
||||
['collection_id', String(collectionId)]
|
||||
]
|
||||
where: whereConditions.length > 0 ? whereConditions : undefined
|
||||
});
|
||||
|
||||
return total;
|
||||
|
|
|
|||
|
|
@ -126,7 +126,6 @@ const AppSchema = new Schema(
|
|||
}
|
||||
);
|
||||
|
||||
AppSchema.index({ type: 1 });
|
||||
AppSchema.index({ teamId: 1, updateTime: -1 });
|
||||
AppSchema.index({ teamId: 1, type: 1 });
|
||||
AppSchema.index(
|
||||
|
|
@ -137,5 +136,7 @@ AppSchema.index(
|
|||
}
|
||||
}
|
||||
);
|
||||
// Admin count
|
||||
AppSchema.index({ type: 1 });
|
||||
|
||||
export const MongoApp = getMongoModel<AppType>(AppCollectionName, AppSchema);
|
||||
|
|
|
|||
|
|
@ -96,40 +96,20 @@ export async function delDatasetRelevantData({
|
|||
datasetId: { $in: datasetIds }
|
||||
});
|
||||
|
||||
// Delete dataset_data_texts in batches by datasetId
|
||||
for (const datasetId of datasetIds) {
|
||||
// Delete dataset_data_texts in batches by datasetId
|
||||
await MongoDatasetDataText.deleteMany({
|
||||
teamId,
|
||||
datasetId
|
||||
}).maxTimeMS(300000); // Reduce timeout for single batch
|
||||
}
|
||||
// Delete dataset_datas in batches by datasetId
|
||||
for (const datasetId of datasetIds) {
|
||||
await MongoDatasetData.deleteMany({
|
||||
teamId,
|
||||
datasetId
|
||||
}).maxTimeMS(300000);
|
||||
}
|
||||
|
||||
await delCollectionRelatedSource({ collections });
|
||||
// Delete vector data
|
||||
await deleteDatasetDataVector({ teamId, datasetIds });
|
||||
|
||||
// Delete dataset_data_texts in batches by datasetId
|
||||
for (const datasetId of datasetIds) {
|
||||
await MongoDatasetDataText.deleteMany({
|
||||
teamId,
|
||||
datasetId
|
||||
}).maxTimeMS(300000); // Reduce timeout for single batch
|
||||
}
|
||||
// Delete dataset_datas in batches by datasetId
|
||||
for (const datasetId of datasetIds) {
|
||||
// Delete dataset_datas in batches by datasetId
|
||||
await MongoDatasetData.deleteMany({
|
||||
teamId,
|
||||
datasetId
|
||||
}).maxTimeMS(300000);
|
||||
}
|
||||
|
||||
// Delete source: 兼容旧版的图片
|
||||
await delCollectionRelatedSource({ collections });
|
||||
// Delete vector data
|
||||
await deleteDatasetDataVector({ teamId, datasetIds });
|
||||
|
|
|
|||
|
|
@ -148,7 +148,7 @@ const DatasetSchema = new Schema({
|
|||
|
||||
try {
|
||||
DatasetSchema.index({ teamId: 1 });
|
||||
DatasetSchema.index({ type: 1 });
|
||||
DatasetSchema.index({ type: 1 }); // Admin count
|
||||
DatasetSchema.index({ deleteTime: 1 }); // 添加软删除字段索引
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
|
|
|
|||
|
|
@ -179,13 +179,13 @@ export const getFileContentFromLinks = async ({
|
|||
sourceId: url,
|
||||
customPdfParse
|
||||
});
|
||||
if (rawTextBuffer) {
|
||||
return formatResponseObject({
|
||||
filename: rawTextBuffer.filename || url,
|
||||
url,
|
||||
content: rawTextBuffer.text
|
||||
});
|
||||
}
|
||||
// if (rawTextBuffer) {
|
||||
// return formatResponseObject({
|
||||
// filename: rawTextBuffer.filename || url,
|
||||
// url,
|
||||
// content: rawTextBuffer.text
|
||||
// });
|
||||
// }
|
||||
|
||||
try {
|
||||
if (isInternalAddress(url)) {
|
||||
|
|
@ -207,23 +207,39 @@ export const getFileContentFromLinks = async ({
|
|||
|
||||
// Get file name
|
||||
const { filename, extension, imageParsePrefix } = (() => {
|
||||
const contentDisposition = response.headers['content-disposition'];
|
||||
if (contentDisposition) {
|
||||
const filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
|
||||
const matches = filenameRegex.exec(contentDisposition);
|
||||
if (matches != null && matches[1]) {
|
||||
const filename = decodeURIComponent(matches[1].replace(/['"]/g, ''));
|
||||
return {
|
||||
filename,
|
||||
extension: path.extname(filename).replace('.', ''),
|
||||
imageParsePrefix: `` // TODO: 需要根据是否是聊天对话里面的外部链接来决定
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (isChatExternalUrl) {
|
||||
const filename = urlObj.pathname.split('/').pop() || 'file';
|
||||
const contentDisposition = response.headers['content-disposition'] || '';
|
||||
|
||||
// Priority: filename* (RFC 5987, UTF-8 encoded) > filename (traditional)
|
||||
const extractFilename = (contentDisposition: string): string => {
|
||||
// Try RFC 5987 filename* first (e.g., filename*=UTF-8''encoded-name)
|
||||
const filenameStarRegex = /filename\*=([^']*)'([^']*)'([^;\n]*)/i;
|
||||
const starMatches = filenameStarRegex.exec(contentDisposition);
|
||||
if (starMatches && starMatches[3]) {
|
||||
const charset = starMatches[1].toLowerCase();
|
||||
const encodedFilename = starMatches[3];
|
||||
// Decode percent-encoded UTF-8 filename
|
||||
try {
|
||||
return decodeURIComponent(encodedFilename);
|
||||
} catch (error) {
|
||||
addLog.warn('Failed to decode filename*', { encodedFilename, error });
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to traditional filename parameter
|
||||
const filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/i;
|
||||
const matches = filenameRegex.exec(contentDisposition);
|
||||
if (matches && matches[1]) {
|
||||
return matches[1].replace(/['"]/g, '');
|
||||
}
|
||||
|
||||
return '';
|
||||
};
|
||||
|
||||
const matchFilename = extractFilename(contentDisposition);
|
||||
const filename = matchFilename || urlObj.pathname.split('/').pop() || 'file';
|
||||
const extension = path.extname(filename).replace('.', '');
|
||||
|
||||
return {
|
||||
filename,
|
||||
extension,
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ const SubSchema = new Schema({
|
|||
appRegistrationCount: Number,
|
||||
auditLogStoreDuration: Number,
|
||||
ticketResponseTime: Number,
|
||||
customDomain: Number,
|
||||
|
||||
// stand sub and extra points sub. Plan total points
|
||||
totalPoints: {
|
||||
|
|
|
|||
|
|
@ -74,7 +74,8 @@ export const getTeamStandPlan = async ({ teamId }: { teamId: string }) => {
|
|||
standard?.appRegistrationCount ?? standardConstants.appRegistrationCount,
|
||||
auditLogStoreDuration:
|
||||
standard?.auditLogStoreDuration ?? standardConstants.auditLogStoreDuration,
|
||||
ticketResponseTime: standard?.ticketResponseTime ?? standardConstants.ticketResponseTime
|
||||
ticketResponseTime: standard?.ticketResponseTime ?? standardConstants.ticketResponseTime,
|
||||
customDomain: standard?.customDomain ?? standardConstants.customDomain
|
||||
}
|
||||
: undefined
|
||||
};
|
||||
|
|
@ -210,7 +211,8 @@ export const getTeamPlanStatus = async ({
|
|||
auditLogStoreDuration:
|
||||
standardPlan?.auditLogStoreDuration ?? standardConstants.auditLogStoreDuration,
|
||||
ticketResponseTime:
|
||||
standardPlan?.ticketResponseTime ?? standardConstants.ticketResponseTime
|
||||
standardPlan?.ticketResponseTime ?? standardConstants.ticketResponseTime,
|
||||
customDomain: standardPlan?.customDomain ?? standardConstants.customDomain
|
||||
}
|
||||
: undefined,
|
||||
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@ export const iconPaths = {
|
|||
'common/gitFill': () => import('./icons/common/gitFill.svg'),
|
||||
'common/gitInlight': () => import('./icons/common/gitInlight.svg'),
|
||||
'common/gitLight': () => import('./icons/common/gitLight.svg'),
|
||||
'common/globalLine': () => import('./icons/common/globalLine.svg'),
|
||||
'common/googleFill': () => import('./icons/common/googleFill.svg'),
|
||||
'common/help': () => import('./icons/common/help.svg'),
|
||||
'common/importLight': () => import('./icons/common/importLight.svg'),
|
||||
|
|
@ -472,6 +473,12 @@ export const iconPaths = {
|
|||
star: () => import('./icons/star.svg'),
|
||||
stop: () => import('./icons/stop.svg'),
|
||||
'support/account/coupon': () => import('./icons/support/account/coupon.svg'),
|
||||
'support/account/customDomain/provider/aliyun': () =>
|
||||
import('./icons/support/account/customDomain/provider/aliyun.svg'),
|
||||
'support/account/customDomain/provider/tencent': () =>
|
||||
import('./icons/support/account/customDomain/provider/tencent.svg'),
|
||||
'support/account/customDomain/provider/volcengine': () =>
|
||||
import('./icons/support/account/customDomain/provider/volcengine.svg'),
|
||||
'support/account/laf': () => import('./icons/support/account/laf.svg'),
|
||||
'support/account/loginoutLight': () => import('./icons/support/account/loginoutLight.svg'),
|
||||
'support/account/plans': () => import('./icons/support/account/plans.svg'),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_30360_927)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10 0.833336C15.0626 0.833336 19.1667 4.93739 19.1667 10C19.1667 15.0626 15.0626 19.1667 10 19.1667C4.93739 19.1667 0.833336 15.0626 0.833336 10C0.833336 4.93739 4.93739 0.833336 10 0.833336ZM10 17.5C10.0951 17.5 10.7259 17.3391 11.429 15.7921C11.742 15.1036 12.0067 14.2572 12.1946 13.2943H7.80539C7.99334 14.2572 8.25805 15.1036 8.571 15.7921C9.27415 17.3391 9.90487 17.5 10 17.5ZM7.5715 11.6277C7.52492 11.1056 7.5 10.5614 7.5 10C7.5 9.42872 7.5258 8.87537 7.57397 8.34492H12.426C12.4742 8.87537 12.5 9.42872 12.5 10C12.5 10.5614 12.4751 11.1056 12.4285 11.6277H7.5715ZM13.8895 13.2943C13.6355 14.7449 13.2187 16.0175 12.6883 17.0038C14.4614 16.3228 15.9103 14.9879 16.7396 13.2943H13.8895ZM17.3229 11.6277H14.1012C14.1442 11.0994 14.1667 10.5555 14.1667 10C14.1667 9.43491 14.1434 8.88176 14.0989 8.34492H17.3168C17.4367 8.87739 17.5 9.4313 17.5 10C17.5 10.5589 17.4389 11.1036 17.3229 11.6277ZM5.89882 11.6277H2.67711C2.56115 11.1036 2.5 10.5589 2.5 10C2.5 9.4313 2.5633 8.87739 2.68323 8.34492H5.90108C5.85658 8.88176 5.83334 9.43491 5.83334 10C5.83334 10.5555 5.85579 11.0994 5.89882 11.6277ZM3.26036 13.2943H6.1105C6.36451 14.7449 6.78134 16.0175 7.31173 17.0038C5.53862 16.3228 4.08972 14.9879 3.26036 13.2943ZM7.81077 6.67825H12.1892C12.0016 5.72674 11.739 4.88982 11.429 4.20786C10.7259 2.66093 10.0951 2.5 10 2.5C9.90487 2.5 9.27415 2.66093 8.571 4.20786C8.26102 4.88982 7.99838 5.72674 7.81077 6.67825ZM13.8847 6.67825H16.7261C15.8946 4.99771 14.4518 3.6735 12.6883 2.99618C13.2153 3.97628 13.6302 5.23909 13.8847 6.67825ZM7.31173 2.99618C6.78469 3.97628 6.36977 5.23909 6.11533 6.67825H3.27386C4.10539 4.99771 5.5482 3.6735 7.31173 2.99618Z" fill="#485264"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_30360_927">
|
||||
<rect width="20" height="20" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
|
|
@ -0,0 +1,5 @@
|
|||
<svg viewBox="0 0 91 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M65.1605 15.1515H71.6969V13.1313H65.1605V11.1111H71.4927V0.60606H56.7856V11.1111H63.1178V13.1313H56.5813V15.1515H63.1178V17.3737H56.0707V19.3939H72.3097V17.3737H65.2626V15.1515H65.1605ZM65.3647 2.62626H69.6543V4.84848H65.3647V2.62626ZM65.3647 6.86869H69.6543V8.9899H65.3647V6.86869ZM63.3221 8.9899H59.0325V6.86869H63.3221V8.9899ZM63.3221 4.84848H59.0325V2.62626H63.3221V4.84848ZM10.7239 8.68687H21.4478V11.1111H10.7239V8.68687Z" fill="#FF6A00"/>
|
||||
<path d="M26.7587 0H19.7116L21.4478 2.42424L26.5544 4.0404C27.4736 4.34343 28.0864 5.25253 28.0864 6.16162V13.8384C28.0864 14.7475 27.4736 15.6566 26.5544 15.9596L21.4478 17.5758L19.7116 20H26.7587C29.7205 20 32.0696 17.6768 32.0696 14.7475V5.35354C32.0696 2.42424 29.7205 0 26.7587 0ZM5.51515 15.9596C4.59596 15.6566 3.98316 14.7475 3.98316 13.8384V6.16162C3.98316 5.25253 4.59596 4.34343 5.51515 4.0404L10.6218 2.42424L12.358 0H5.31089C2.34905 0 0 2.42424 0 5.35354V14.6465C0 17.5758 2.34905 19.899 5.31089 19.899H12.358L10.6218 17.4747L5.51515 15.9596ZM49.7385 4.84848H44.5297V15.4545H49.7385V4.84848ZM47.5937 13.3333H46.4703V6.86869H47.5937V13.3333ZM36.8698 19.3939H38.9125V2.62626H41.0572L39.8316 8.18182V10.202H41.0572V14.6465C41.0572 14.9495 40.853 15.1515 40.5466 15.1515H40.0359V17.1717H41.0572C42.1807 17.1717 43.0999 16.2626 43.0999 15.1515V8.08081H41.8743L43.0999 2.52525V0.50505H36.8698V19.3939Z" fill="#FF6A00"/>
|
||||
<path d="M44.019 2.62626H51.0662V15.6566C51.0662 16.5657 50.3513 17.3737 49.3299 17.3737H47.7979V19.3939H49.9427C51.679 19.3939 53.211 17.9798 53.211 16.1616V2.62626H53.9259V0.60606H44.019V2.62626ZM75.0673 0.70707H89.7744V2.72727H75.0673V0.70707ZM90.5914 10.303V8.28283H74.3524V10.303H78.4377L75.1694 17.3737V19.3939H88.9573C89.5701 19.3939 89.9786 18.8889 89.9786 18.3838C89.9786 18.1818 89.9786 18.0808 89.8765 17.9798L88.1403 14.1414H85.8933L87.4253 17.3737H77.5185L80.7867 10.303H90.5914Z" fill="#FF6A00"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
|
|
@ -0,0 +1,7 @@
|
|||
<svg viewBox="0 0 77 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M61.9037 7.97203H54.3043V9.01632H56.6884V13.1935H53.8572V14.387H56.6884V21.1002H57.8804V14.387H60.8606V20.9511H63.2447V19.7576H62.2017V7.97203H61.9037ZM57.8804 13.0443V9.01632H60.8606V13.1935H57.8804V13.0443ZM40.5955 17.9674H46.7048V19.1608H40.5955V17.9674ZM36.2743 15.8788C36.2743 17.9674 36.1252 19.4592 35.6782 20.8019C35.6782 20.9511 35.6782 20.9511 35.8272 20.9511H37.0193C37.4663 19.0117 37.4663 17.5198 37.4663 16.3263H39.1054V19.7576H38.0624C37.9133 19.7576 37.9133 19.7576 37.9133 19.9068L38.2114 20.9511H40.2975V7.97203H36.4233L36.2743 15.8788ZM37.6153 15.4312V12.7459H39.2544V15.5804H37.6153V15.4312ZM39.1054 9.01632V11.7016H37.4663V9.01632H39.1054Z" fill="#56606F"/>
|
||||
<path d="M47.152 12.5967H48.6421V11.5524H44.3208C44.3208 11.2541 44.3208 10.8065 44.6188 10.5082H48.4931V9.46387H47.45C47.599 8.86713 47.897 8.12121 47.897 8.12121C47.897 7.97203 47.897 7.97203 47.748 7.97203H46.854L46.4069 9.46387H45.0659C45.2149 8.86713 45.2149 8.2704 45.3639 7.52447C45.3639 7.37529 45.3639 7.37529 45.2149 7.37529H44.3208C44.1718 8.12121 44.1718 8.86713 43.8738 9.46387H43.1288L42.6817 7.97203H41.7877C41.6387 7.97203 41.6387 7.97203 41.6387 8.12121C41.6387 8.27039 41.7877 8.86713 42.0857 9.46387H41.0426V10.5082H43.5758C43.4268 10.8065 43.4268 11.2541 43.1288 11.5524H40.7446V12.5967H42.5327C41.7877 13.641 41.0426 14.0886 40.7446 14.2378V15.4312C40.7446 15.5804 40.8936 15.5804 40.8936 15.4312C41.3407 15.5804 41.6387 15.1329 41.9367 14.8345H46.2579V16.4755H42.8307L42.9798 15.5804C42.9798 15.4312 42.9798 15.4312 42.8307 15.4312H41.7877L41.6387 17.3706C41.6387 17.5198 41.6387 17.5198 41.7877 17.5198H47.599V20.3543H45.0659C44.9169 20.3543 44.9169 20.3543 44.9169 20.5035L45.2149 21.5478H48.7911V16.6247H47.45V14.0886C48.046 14.5361 48.4931 14.9837 49.0891 15.1329C49.2381 15.282 49.2381 15.282 49.2381 15.1329V14.0886C48.6421 13.9394 47.897 13.4918 47.152 12.5967ZM45.8109 12.5967C45.9599 13.0443 46.4069 13.4918 46.705 13.7902H42.9798C43.2778 13.4918 43.5758 13.0443 43.8738 12.5967H45.8109ZM55.0494 17.2214L52.9633 18.8625V11.5524H50.1321V12.7459H51.7712V19.7576C51.3242 20.0559 51.0262 20.2051 51.0262 20.2051L51.7712 21.1002L55.1984 18.5641L55.0494 17.2214ZM51.9202 10.359H53.1123C53.2613 10.359 53.2613 10.2098 53.2613 10.2098L51.4732 7.52447H50.2812L50.1321 7.67366C50.2812 7.82284 51.9202 10.359 51.9202 10.359ZM65.629 8.12121H75.3145V9.31468H65.629V8.12121ZM69.2052 14.0886H76.8046V12.8951H64.4369V14.0886H67.8641C67.1191 16.028 65.331 20.6527 65.331 20.8019C65.331 20.951 65.331 20.951 65.48 20.951H76.2086C76.3576 20.951 76.3576 20.8019 76.3576 20.8019L74.7185 16.6247H73.5264C73.3774 16.6247 73.3774 16.7739 73.3774 16.7739C73.3774 16.9231 74.5695 19.7576 74.5695 19.7576H67.4171C67.2681 19.6084 69.2052 14.0886 69.2052 14.0886Z" fill="#56606F"/>
|
||||
<path d="M28.2277 19.4592C27.7807 19.9068 26.7376 20.8019 24.8005 20.8019H13.7739C17.2011 17.5198 19.8832 14.6853 20.1813 14.5361C20.4793 14.2378 21.0753 13.641 21.6713 13.1935C22.8634 11.8508 23.9065 11.7016 24.9495 11.7016C26.2906 11.7016 27.3336 12.1492 28.2277 13.0443C30.0158 14.6853 30.0158 17.8182 28.2277 19.4592ZM30.4628 10.9557C29.1217 9.61305 27.1846 8.56876 25.2475 8.56876C23.3104 8.56876 21.8203 9.31468 20.4793 10.2098C20.0322 10.6573 19.1382 11.2541 18.5422 12.1492C17.7971 12.5967 6.77051 23.7855 6.77051 23.7855H23.0124C24.0555 23.7855 24.9495 23.7855 25.5455 23.6364C27.1846 23.4872 28.9727 22.7413 30.3138 21.5478C33.145 18.5641 33.145 13.7902 30.4628 10.9557Z" fill="#00A3FF"/>
|
||||
<path d="M12.135 10.2098C10.6449 9.1655 9.3038 8.71795 7.5157 8.71795C5.42958 8.71795 3.49248 9.61305 2.30041 11.1049C-0.530744 14.0886 -0.530744 18.8625 2.44942 21.8462C3.64149 23.0396 4.98256 23.6364 6.47264 23.6364L9.45281 20.8019H7.81372C6.17463 20.6527 5.13157 20.0559 4.38653 19.4592C2.59843 17.669 2.59843 14.8345 4.38653 12.8951C5.28058 12 6.17462 11.5524 7.66471 11.5524C8.55876 11.5524 9.45281 11.7016 10.6449 12.8951C11.0919 13.4918 12.433 14.3869 12.88 14.9837H13.029L14.8171 12.8951C14.2211 12.1492 12.731 10.9557 12.135 10.2098Z" fill="#00C8DC"/>
|
||||
<path d="M25.6948 6.77855C24.3537 2.89977 20.4795 0.214451 16.3073 0.214451C11.241 0.214451 7.21778 3.94405 6.32373 8.71795C6.62175 8.71795 7.06877 8.56876 7.5158 8.56876C7.96282 8.56876 8.70786 8.71795 9.15489 8.71795C9.89993 5.4359 12.7311 3.19813 16.0093 3.19813C18.8404 3.19813 21.3736 4.83916 22.5656 7.37529C22.5656 7.37529 22.7146 7.52447 22.7146 7.37529C23.7577 7.22611 24.8007 6.77855 25.6948 6.77855C25.6948 7.07692 25.6948 7.07692 25.6948 6.77855Z" fill="#006EFF"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.5 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
|
@ -9,7 +9,26 @@
|
|||
"confirm_logout": "Confirm to log out?",
|
||||
"create_channel": "Add new channel",
|
||||
"create_model": "Add new model",
|
||||
"custom_domain": "Custom Domain",
|
||||
"custom_model": "custom model",
|
||||
"custom_domain.domain": "Domain",
|
||||
"custom_domain.provider": "Domain Provider",
|
||||
"custom_domain.registration_hint": "Please prepare your own domain and complete the ICP filing through <bold>{{provider}}</bold>, then fill in the domain in the input box below",
|
||||
"custom_domain.provider.aliyun": "Alibaba Cloud",
|
||||
"custom_domain.provider.tencent": "Tencent Cloud",
|
||||
"custom_domain.provider.volcengine": "Volcano Engine",
|
||||
"custom_domain.DNS_record": "DNS Record",
|
||||
"custom_domain.DNS_record.type": "Type",
|
||||
"custom_domain.DNS_resolve_hint": "Please go to your domain service provider and add a CNAME resolution for this domain to <bold>{{domain}}</bold>. After the resolution takes effect, you can bind the custom domain.",
|
||||
"custom_domain.dns_resolved": "Verified",
|
||||
"custom_domain.dns_resolving": "Verifying",
|
||||
"custom_domain.delete_confirm": "Confirm to delete this custom domain?",
|
||||
"custom_domain.status.active": "Active",
|
||||
"custom_domain.status.inactive": "Inactive",
|
||||
"custom_domain.domain_verify": "Domain Verification",
|
||||
"custom_domain.domain_verify.path": "File Path",
|
||||
"custom_domain.domain_verify.content": "File Content",
|
||||
"custom_domain.domain_verify.desc": "After saving, visiting {{domain}}/{{path}} will return {{content}}",
|
||||
"default_model": "Default model",
|
||||
"default_model_config": "Default model configuration",
|
||||
"language": "Language and time zone",
|
||||
|
|
@ -113,5 +132,7 @@
|
|||
"app_registration_count": "App registration count",
|
||||
"audit_log_store_duration": "Audit log storage duration",
|
||||
"ticket_response_time": "Ticket response time",
|
||||
"custom_config_details": "Custom configuration details"
|
||||
"custom_config_details": "Custom configuration details",
|
||||
"upgrade_to_use_custom_domain": "Current plan does not support custom domains, please upgrade first",
|
||||
"upgrade_plan": "Upgrade plan"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -776,11 +776,11 @@
|
|||
"create_success": "Created Successfully",
|
||||
"create_time": "Creation Time",
|
||||
"cron_job_run_app": "Scheduled Task",
|
||||
"custom_plan_price": "Custom Billing",
|
||||
"custom_plan_feature_1": "Priority Deep Technical Support",
|
||||
"custom_plan_feature_2": "Dedicated Account Manager",
|
||||
"custom_plan_feature_3": "Flexible Resource Configuration",
|
||||
"custom_plan_feature_4": "Secure and Controllable",
|
||||
"custom_plan_price": "Custom Billing",
|
||||
"custom_title": "Custom Title",
|
||||
"data_index_custom": "Custom index",
|
||||
"data_index_default": "Default index",
|
||||
|
|
@ -1216,6 +1216,7 @@
|
|||
"support.wallet.subscription.Upgrade plan": "Upgrade Package",
|
||||
"support.wallet.subscription.ai_model": "AI Language Model",
|
||||
"support.wallet.subscription.function.Audit log store duration": "{{amount}} days team operation log records",
|
||||
"support.wallet.subscription.function.Custom domain": "{{amount}} Custom domains",
|
||||
"support.wallet.subscription.function.History store": "{{amount}} Days of Chat History Retention",
|
||||
"support.wallet.subscription.function.Max app": "{{amount}} Agent limit",
|
||||
"support.wallet.subscription.function.Max dataset": "{{amount}} Dataset limit",
|
||||
|
|
@ -1224,6 +1225,7 @@
|
|||
"support.wallet.subscription.function.Points": "{{amount}} points",
|
||||
"support.wallet.subscription.function.Requests per minute": "{{amount}} QPM",
|
||||
"support.wallet.subscription.function.Website sync per dataset": "Single knowledge base {{amount}} web pages synchronized",
|
||||
"support.wallet.subscription.function.custom domain tip": "The number of custom domain names that the team can configure, which can currently be used to access Wecom intelligent robots",
|
||||
"support.wallet.subscription.mode.Month": "Month",
|
||||
"support.wallet.subscription.mode.Period": "Subscription Period",
|
||||
"support.wallet.subscription.mode.Year": "Year",
|
||||
|
|
|
|||
|
|
@ -35,11 +35,16 @@
|
|||
"wecom.bot_desc": "Connect to WeCom Bot directly via API",
|
||||
"wecom.create_modal_title": "Create WeCom Bot",
|
||||
"wecom.edit_modal_title": "Edit WeCom Bot",
|
||||
"wecom.create_modal.step.1": "Configure Parameters",
|
||||
"wecom.create_modal.step.2": "Fill in Callback URL",
|
||||
"wecom.title": "Publish to WeCom Bot",
|
||||
"dingtalk.bot": "DingTalk Bot",
|
||||
"dingtalk.bot_desc": "Connect to DingTalk Bot directly via API",
|
||||
"dingtalk.create_modal_title": "Create DingTalk Bot",
|
||||
"dingtalk.edit_modal_title": "Edit DingTalk Bot",
|
||||
"dingtalk.title": "Publish to DingTalk Bot",
|
||||
"dingtalk.api": "DingTalk API"
|
||||
"dingtalk.api": "DingTalk API",
|
||||
"use_default_domain": "Use Default Domain",
|
||||
"ip_whitelist": "IP Whitelist",
|
||||
"custom_domain_management": "Custom Domain Management"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,26 @@
|
|||
"create_channel": "新增渠道",
|
||||
"create_model": "新增模型",
|
||||
"custom_config_details": "定制配置详情",
|
||||
"custom_domain": "自定义域名",
|
||||
"custom_model": "自定义模型",
|
||||
"custom_domain.domain": "域名",
|
||||
"custom_domain.provider": "域名备案商",
|
||||
"custom_domain.registration_hint": "请自备域名并通过 <bold>{{provider}}</bold> 完成备案后,将域名填入下方输入框中",
|
||||
"custom_domain.provider.aliyun": "阿里云",
|
||||
"custom_domain.provider.tencent": "腾讯云",
|
||||
"custom_domain.provider.volcengine": "火山引擎",
|
||||
"custom_domain.DNS_record": "DNS 记录",
|
||||
"custom_domain.DNS_record.type": "类型",
|
||||
"custom_domain.DNS_resolve_hint": "请到您的域名服务商处,添加该域名的 CNAME 解析到 <bold>{{domain}}</bold>,解析生效后即可绑定自定义域名。",
|
||||
"custom_domain.dns_resolved": "已验证",
|
||||
"custom_domain.dns_resolving": "验证中",
|
||||
"custom_domain.delete_confirm": "确认删除该自定义域名?",
|
||||
"custom_domain.status.active": "已生效",
|
||||
"custom_domain.status.inactive": "已失效",
|
||||
"custom_domain.domain_verify": "域名校验",
|
||||
"custom_domain.domain_verify.path": "文件路径",
|
||||
"custom_domain.domain_verify.content": "文件内容",
|
||||
"custom_domain.domain_verify.desc": "保存后,访问 {{domain}}/{{path}} 将返回 {{content}}",
|
||||
"day": "天",
|
||||
"default_model": "预设模型",
|
||||
"default_model_config": "默认模型配置",
|
||||
|
|
@ -103,9 +122,6 @@
|
|||
"requests_per_minute": "QPM",
|
||||
"reset_default": "恢复默认配置",
|
||||
"status": "状态",
|
||||
"subscription_mode_month": "时长",
|
||||
"subscription_package": "订阅套餐",
|
||||
"subscription_period": "订阅周期",
|
||||
"support_wallet_amount": "金额",
|
||||
"team": "团队管理",
|
||||
"third_party": "第三方账号",
|
||||
|
|
@ -113,5 +129,10 @@
|
|||
"usage_records": "使用记录",
|
||||
"website_sync_per_dataset": "站点同步最大页数",
|
||||
"yes": "是",
|
||||
"yuan": "{{amount}}元"
|
||||
"yuan": "{{amount}}元",
|
||||
"subscription_period": "订阅周期",
|
||||
"subscription_package": "订阅套餐",
|
||||
"subscription_mode_month": "时长",
|
||||
"upgrade_to_use_custom_domain": "当前套餐不支持自定义域名,请先升级",
|
||||
"upgrade_plan": "升级套餐"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -301,6 +301,7 @@
|
|||
"pro_modal_title": "商业版专享!",
|
||||
"pro_modal_unlock_button": "去解锁",
|
||||
"publish_channel": "发布渠道",
|
||||
"publish_channel.wecom.empty": "发布到企业微信机器人,请先 <a>绑定自定义域名</a>,并且通过域名校验。",
|
||||
"publish_success": "发布成功",
|
||||
"question_guide_tip": "对话结束后,会为你生成 3 个引导性问题。",
|
||||
"reasoning_response": "输出思考",
|
||||
|
|
|
|||
|
|
@ -781,11 +781,11 @@
|
|||
"create_success": "创建成功",
|
||||
"create_time": "创建时间",
|
||||
"cron_job_run_app": "定时任务",
|
||||
"custom_plan_price": "定制化计费",
|
||||
"custom_plan_feature_1": "优先深度技术支持",
|
||||
"custom_plan_feature_2": "专属客户经理",
|
||||
"custom_plan_feature_3": "弹性资源配置",
|
||||
"custom_plan_feature_4": "安全可控",
|
||||
"custom_plan_price": "定制化计费",
|
||||
"custom_title": "自定义标题",
|
||||
"data_index_custom": "自定义索引",
|
||||
"data_index_default": "默认索引",
|
||||
|
|
@ -1052,6 +1052,7 @@
|
|||
"read_quote": "查看引用",
|
||||
"redo_tip": "恢复 ctrl shift z",
|
||||
"redo_tip_mac": "恢复 ⌘ shift z",
|
||||
"refresh": "刷新",
|
||||
"request_end": "已加载全部",
|
||||
"request_error": "请求异常",
|
||||
"request_more": "点击加载更多",
|
||||
|
|
@ -1224,6 +1225,7 @@
|
|||
"support.wallet.subscription.eval_items_count": "单次评测数据条数: {{count}} 条",
|
||||
"support.wallet.subscription.function.App registration count": "{{amount}} 个应用备案",
|
||||
"support.wallet.subscription.function.Audit log store duration": "{{amount}} 天团队操作日志记录",
|
||||
"support.wallet.subscription.function.Custom domain": "{{amount}} 个自定义域名",
|
||||
"support.wallet.subscription.function.History store": "{{amount}} 天对话记录保留",
|
||||
"support.wallet.subscription.function.Max app": "{{amount}} 个 Agent",
|
||||
"support.wallet.subscription.function.Max dataset": "{{amount}} 个知识库",
|
||||
|
|
@ -1233,6 +1235,7 @@
|
|||
"support.wallet.subscription.function.Requests per minute": "{{amount}} QPM",
|
||||
"support.wallet.subscription.function.Ticket response time": "{{amount}} 小时工单支持响应",
|
||||
"support.wallet.subscription.function.Website sync per dataset": "站点同步最大 {{amount}} 页",
|
||||
"support.wallet.subscription.function.custom domain tip": "团队可以配置的自定义域名数量,目前可用于接入企微智能机器人",
|
||||
"support.wallet.subscription.function.qpm tip": "主要指团队每分钟请求 Agent 的最大次数,与单个 Agent 复杂度无关。其他 OpenAPI 接口也受此影响,每个接口单独计算",
|
||||
"support.wallet.subscription.mode.Month": "按月",
|
||||
"support.wallet.subscription.mode.Period": "订阅周期",
|
||||
|
|
|
|||
|
|
@ -35,11 +35,16 @@
|
|||
"wecom.bot_desc": "通过 API 直接接入企业微信机器人",
|
||||
"wecom.create_modal_title": "创建企微机器人",
|
||||
"wecom.edit_modal_title": "编辑企微机器人",
|
||||
"wecom.create_modal.step.1": "配置参数",
|
||||
"wecom.create_modal.step.2": "填写回调地址",
|
||||
"wecom.title": "发布到企业微信机器人",
|
||||
"dingtalk.bot": "钉钉机器人",
|
||||
"dingtalk.bot_desc": "通过 API 直接接入钉钉机器人",
|
||||
"dingtalk.create_modal_title": "创建钉钉机器人",
|
||||
"dingtalk.edit_modal_title": "编辑钉钉机器人",
|
||||
"dingtalk.title": "发布到钉钉机器人",
|
||||
"dingtalk.api": "钉钉 API"
|
||||
"dingtalk.api": "钉钉 API",
|
||||
"use_default_domain": "使用默认域名",
|
||||
"ip_whitelist": "IP 白名单",
|
||||
"custom_domain_management": "自定义域名管理"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,25 @@
|
|||
"create_model": "新增模型",
|
||||
"custom_config_details": "定制配置詳情",
|
||||
"custom_model": "自訂模型",
|
||||
"custom_domain": "自訂域名",
|
||||
"custom_domain.domain": "域名",
|
||||
"custom_domain.provider": "域名備案商",
|
||||
"custom_domain.registration_hint": "請自備域名並透過 <bold>{{provider}}</bold> 完成備案後,將域名填入下方輸入框中",
|
||||
"custom_domain.provider.aliyun": "阿里雲",
|
||||
"custom_domain.provider.tencent": "騰訊雲",
|
||||
"custom_domain.provider.volcengine": "火山引擎",
|
||||
"custom_domain.DNS_record": "DNS 記錄",
|
||||
"custom_domain.DNS_record.type": "類型",
|
||||
"custom_domain.DNS_resolve_hint": "請到您的域名服務商處,新增該域名的 CNAME 解析到 <bold>{{domain}}</bold>,解析生效後即可繫結自訂域名。",
|
||||
"custom_domain.dns_resolved": "已驗證",
|
||||
"custom_domain.dns_resolving": "驗證中",
|
||||
"custom_domain.delete_confirm": "確認刪除該自訂域名?",
|
||||
"custom_domain.status.active": "已生效",
|
||||
"custom_domain.status.inactive": "已失效",
|
||||
"custom_domain.domain_verify": "域名校驗",
|
||||
"custom_domain.domain_verify.path": "檔案路徑",
|
||||
"custom_domain.domain_verify.content": "檔案內容",
|
||||
"custom_domain.domain_verify.desc": "儲存後,訪問 {{domain}}/{{path}} 將傳回 {{content}}",
|
||||
"day": "天",
|
||||
"default_model": "預設模型",
|
||||
"default_model_config": "預設模型設定",
|
||||
|
|
@ -113,5 +132,10 @@
|
|||
"usage_records": "使用記錄",
|
||||
"website_sync_per_dataset": "站點同步最大頁數",
|
||||
"yes": "是",
|
||||
"yuan": "{{amount}}元"
|
||||
}
|
||||
"yuan": "{{amount}}元",
|
||||
"month": "月",
|
||||
"extra_dataset_size": "額外知識庫容量",
|
||||
"extra_ai_points": "AI 積分運算標準",
|
||||
"upgrade_to_use_custom_domain": "目前套餐不支援自訂域名,請先升級",
|
||||
"upgrade_plan": "升級套餐"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -775,11 +775,11 @@
|
|||
"create_success": "建立成功",
|
||||
"create_time": "建立時間",
|
||||
"cron_job_run_app": "排程任務",
|
||||
"custom_plan_price": "定制化計費",
|
||||
"custom_plan_feature_1": "優先深度技術支援",
|
||||
"custom_plan_feature_2": "專屬客戶經理",
|
||||
"custom_plan_feature_3": "彈性資源配置",
|
||||
"custom_plan_feature_4": "安全可控",
|
||||
"custom_plan_price": "定制化計費",
|
||||
"custom_title": "自訂標題",
|
||||
"data_index_custom": "自定義索引",
|
||||
"data_index_default": "預設索引",
|
||||
|
|
@ -1213,6 +1213,7 @@
|
|||
"support.wallet.subscription.Upgrade plan": "升級方案",
|
||||
"support.wallet.subscription.ai_model": "AI 語言模型",
|
||||
"support.wallet.subscription.function.Audit log store duration": "{{amount}} 天團隊操作日誌記錄",
|
||||
"support.wallet.subscription.function.Custom domain": "{{amount}} 個自定義域名",
|
||||
"support.wallet.subscription.function.History store": "{{amount}} 天對話紀錄保留",
|
||||
"support.wallet.subscription.function.Max app": "{{amount}} 個 Agent",
|
||||
"support.wallet.subscription.function.Max dataset": "{{amount}} 個知識庫",
|
||||
|
|
@ -1221,6 +1222,7 @@
|
|||
"support.wallet.subscription.function.Points": "{{amount}} 積分",
|
||||
"support.wallet.subscription.function.Requests per minute": "{{amount}} QPM",
|
||||
"support.wallet.subscription.function.Website sync per dataset": "單知識庫 {{amount}} 個網頁同步",
|
||||
"support.wallet.subscription.function.custom domain tip": "團隊可以配置的自定義域名數量,目前可用於接入企微智能機器人",
|
||||
"support.wallet.subscription.mode.Month": "按月",
|
||||
"support.wallet.subscription.mode.Period": "訂閱週期",
|
||||
"support.wallet.subscription.mode.Year": "按年",
|
||||
|
|
|
|||
|
|
@ -35,11 +35,16 @@
|
|||
"wecom.bot_desc": "透過 API 直接連結企業微信聊天機器人",
|
||||
"wecom.create_modal_title": "建立企業微信聊天機器人",
|
||||
"wecom.edit_modal_title": "編輯企業微信聊天機器人",
|
||||
"wecom.create_modal.step.1": "配置參數",
|
||||
"wecom.create_modal.step.2": "填寫回調地址",
|
||||
"wecom.title": "發布至企業微信聊天機器人",
|
||||
"dingtalk.bot": "釘釘聊天機器人",
|
||||
"dingtalk.bot_desc": "透過 API 直接連結釘釘聊天機器人",
|
||||
"dingtalk.create_modal_title": "建立釘釘聊天機器人",
|
||||
"dingtalk.edit_modal_title": "編輯釘釘聊天機器人",
|
||||
"dingtalk.title": "發布至釘釘聊天機器人",
|
||||
"dingtalk.api": "釘釘 API"
|
||||
"dingtalk.api": "釘釘 API",
|
||||
"use_default_domain": "使用預設域名",
|
||||
"ip_whitelist": "IP 白名單",
|
||||
"custom_domain_management": "自訂域名管理"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,6 +76,8 @@ COPY --from=maindeps /app/node_modules/@zilliz/milvus2-sdk-node ./node_modules/@
|
|||
COPY --from=builder /app/projects/app/package.json ./package.json
|
||||
# copy config
|
||||
COPY ./projects/app/data/config.json /app/data/config.json
|
||||
# copy test.mp3
|
||||
COPY ./projects/app/data/test.mp3 /app/data/test.mp3
|
||||
# copy GeoLite2-City.mmdb
|
||||
COPY ./projects/app/data/GeoLite2-City.mmdb /app/data/GeoLite2-City.mmdb
|
||||
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 201 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 199 KiB |
|
Before Width: | Height: | Size: 78 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 82 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 79 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 8.2 KiB |
|
Before Width: | Height: | Size: 428 KiB |
|
Before Width: | Height: | Size: 596 KiB |
|
Before Width: | Height: | Size: 223 KiB |
|
|
@ -1 +0,0 @@
|
|||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1693533362715" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4021" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M166.32 808.98l-99.28 99.28c-15.31 15.32-39.71 15.32-55.03 0-15.32-15.31-15.32-39.71 0-55.03l98.71-99.28c15.32-15.31 39.71-15.31 55.03 0 15.32 15.32 15.32 39.72 0.57 55.03z m48.22 48.79l-99.28 99.27c-15.31 15.32-15.31 39.71 0 55.03 15.32 15.32 39.71 15.32 55.03 0l99.28-99.28c15.32-15.32 15.32-39.71 0-55.03-15.32-15.31-39.72-15.31-55.03 0.01z m625.14-545.16c0 71.47-57.86 129.34-129.34 129.34S581 384.08 581 312.61c0-71.48 57.86-129.34 129.34-129.34 72.05 0 129.91 57.86 129.34 129.34z m-77.71 0.57c0-28.36-23.26-51.63-51.62-51.63-28.36 0-51.62 23.26-51.62 51.63 0 28.36 23.26 51.62 51.62 51.62 28.93 0 52.19-23.26 51.62-51.62z m236.56 68.07c-27.8 104.39-83.39 203.65-164.51 285.35h-3.97l-5.11 5.67c10.21 38.57 11.35 78.28 2.84 116.86-9.07 46.52-32.33 90.2-66.94 124.8l-2.27 2.27c-38.57 36.87-85.09 59.57-134.45 67.51-51.62 7.94-105.51 0-152.6-23.26-17.58-10.21-25.53-32.9-16.45-51.63 1.7-2.84 3.41-5.67 5.67-8.5 14.18-15.32 24.96-32.9 32.9-51.63l3.4-7.94c-10.21 1.7-20.42 4.53-30.63 5.1-29.5 4.54-60.14 6.24-90.2 5.67-10.21 0-19.86-3.97-26.1-11.35L181.63 671.14c-8.51-7.95-11.35-18.72-10.78-29.5 0-28.37 2.27-58.43 5.67-86.79 1.7-10.78 2.84-20.99 5.1-31.2l-8.51 3.4c-17.58 7.95-35.17 18.72-51.06 33.47-14.75 14.18-39.14 12.48-53.32-2.27-2.27-2.84-3.97-7.37-6.24-10.21-23.26-47.09-30.63-99.28-22.69-150.9 8.22-51.8 32.65-99.66 69.78-136.71 34.61-34.61 78.85-57.86 123.67-66.94 39.14-8.51 79.42-7.94 116.86 2.27l7.95-7.95C439.75 106.69 538.45 51.66 642.83 23.3c108.35-28.36 222.38-28.36 329.59 0 13.62 4.54 23.82 15.32 27.23 27.23l0.57 0.57v0.56c28.37 107.78 28.37 221.81-1.69 329.59zM936.12 92.51c-89.06-19.86-182.67-18.16-270.03 5.1-91.34 24.96-178.7 73.18-250.74 145.22l-10.78 10.78-10.21 11.35c-10.21 11.35-26.66 16.45-42.55 10.78-31.2-12.48-65.81-14.75-98.71-7.38-31.2 6.81-61.27 22.13-86.23 46.52-26.66 27.8-43.12 60.7-48.79 95.3-3.4 20.99-3.4 42.55 0.57 62.97 9.64-5.67 18.72-10.78 28.93-14.18 26.66-11.34 55.6-18.15 84.53-18.72 3.97-0.57 7.94 0 11.91 0.56 19.86 5.67 31.77 26.66 26.1 46.52-7.95 26.1-13.05 52.2-16.45 78.29-1.7 22.12-3.4 43.12-3.97 64.67l146.36 146.93c22.12-1.14 43.11-2.27 64.67-5.11 27.23-3.97 52.76-8.51 78.29-15.89 3.97-0.56 7.94-1.13 11.91-1.13 20.42 0.57 36.31 18.72 36.31 39.14-1.7 29.5-7.38 58.43-19.29 85.66-3.97 9.08-9.07 19.29-14.18 28.36 20.42 3.97 41.98 3.97 62.4 1.13 33.47-5.1 66.94-20.99 93.03-47.09l1.13-1.13c24.39-24.96 39.71-55.6 47.09-86.79 6.81-32.34 3.97-66.94-7.94-99.28-4.54-14.18-1.7-30.64 10.21-41.41l11.91-11.35 8.51-8.51 2.27-1.13c72.04-72.61 119.7-159.41 145.79-250.17 23.27-89.05 25.54-180.95 7.95-270.01z" p-id="4022" fill="#515151"></path></svg>
|
||||
|
Before Width: | Height: | Size: 2.9 KiB |
|
|
@ -1 +0,0 @@
|
|||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1691391466028" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4715" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M511.957333 21.333333C241.024 21.333333 21.333333 240.981333 21.333333 512c0 216.832 140.544 400.725333 335.573334 465.664 24.490667 4.394667 32.256-10.069333 32.256-23.082667 0-11.690667 0.256-44.245333 0-85.205333-136.448 29.610667-164.736-64.64-164.736-64.64-22.314667-56.704-54.4-71.765333-54.4-71.765333-44.586667-30.464 3.285333-29.824 3.285333-29.824 49.194667 3.413333 75.178667 50.517333 75.178667 50.517333 43.776 75.008 114.816 53.333333 142.762666 40.789333 4.522667-31.658667 17.152-53.376 31.189334-65.536-108.970667-12.458667-223.488-54.485333-223.488-242.602666 0-53.546667 19.114667-97.322667 50.517333-131.669334-5.034667-12.330667-21.930667-62.293333 4.778667-129.834666 0 0 41.258667-13.184 134.912 50.346666a469.802667 469.802667 0 0 1 122.88-16.554666c41.642667 0.213333 83.626667 5.632 122.88 16.554666 93.653333-63.488 134.784-50.346667 134.784-50.346666 26.752 67.541333 9.898667 117.504 4.864 129.834666 31.402667 34.346667 50.474667 78.122667 50.474666 131.669334 0 188.586667-114.730667 230.016-224.042666 242.090666 17.578667 15.232 33.578667 44.672 33.578666 90.453334v135.850666c0 13.141333 7.936 27.605333 32.853334 22.869334C862.250667 912.597333 1002.666667 728.746667 1002.666667 512 1002.666667 240.981333 783.018667 21.333333 511.957333 21.333333z" p-id="4716"></path></svg>
|
||||
|
Before Width: | Height: | Size: 1.6 KiB |
|
|
@ -1 +0,0 @@
|
|||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1688889092975" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2652" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M932.15857778 820.62601482V639.06702222c-26.82121482 23.42305185-57.04059259 42.71976297-89.4445037 57.28331853-104.97896297 46.11792592-218.45333333 69.41961482-333.14133333 68.32734814-148.06281482 8.61677037-294.66927408-32.768-416.39632593-117.35798519v182.89398519C101.4290963 910.07051852 277.52675555 997.45185185 512.36408889 997.45185185c234.83733333 0 409.6-88.10951111 419.79448889-168.57315555v-8.25268148z" p-id="2653" fill="#4e83fd"></path><path d="M932.15857778 505.93185185v-3.39816296 8.25268148c-10.19448889 80.58500741-184.95715555 168.57315555-419.79448889 168.57315555-234.83733333 0-410.93499259-87.38133333-419.18767407-167.23816295V329.10601482C214.90346667 413.81736297 361.50992592 455.08077037 509.57274075 446.464c114.688 1.09226667 228.16237037-22.08805925 333.14133333-68.32734815 32.40391111-14.68491852 62.62328889-33.98162963 89.4445037-57.28331852V505.93185185z" p-id="2654" fill="#4e83fd"></path><path d="M816.13558518 85.40918518c69.66234075 30.70482963 111.28983703 74.3954963 111.28983704 113.35300741 0 38.22933333-42.96248889 80.58500741-111.28983704 113.35300741-97.09037037 41.26340741-201.70524445 61.53102222-307.16965926 59.34648889-238.96367408 0-417.1245037-90.7794963-417.1245037-171.97131852s178.88900741-172.6994963 418.4594963-172.69949629c104.97896297-2.3058963 209.10838518 17.71899259 305.83466666 58.6183111z" p-id="2655" fill="#4e83fd"></path></svg>
|
||||
|
Before Width: | Height: | Size: 1.7 KiB |
|
|
@ -1 +0,0 @@
|
|||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1691459708057" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5077" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M934.423357 388.738675H636.152113v99.05297h-64.657602L413.227927 382.546649v-64.511269h32.679284c69.808926 0 126.410038-56.601112 126.410038-126.410038 0-69.807902-56.602136-126.409015-126.410038-126.409015H330.962296c-69.808926 0-126.410038 56.602136-126.410038 126.409015 0 69.809949 56.602136 126.410038 126.410038 126.410038h32.702819v64.536852L166.551782 513.623987l197.114356 131.054825v61.440327h-32.702819c-69.808926 0-126.410038 56.601112-126.410038 126.410038 0 69.807902 56.602136 126.410038 126.410038 126.410038h114.944915c60.205197 0 110.372783-42.185798 123.096573-98.54541h240.532215V636.430964h124.887358V388.738675zM759.973186 810.856575h-189.833533c-10.328229-59.431577-61.84965-104.737436-124.232442-104.737436h-32.679284v-61.414745l161.435763-107.349936h61.488423v99.075483h123.821073v174.426634z" p-id="5078" fill="#FC9663"></path></svg>
|
||||
|
Before Width: | Height: | Size: 1.2 KiB |
|
|
@ -1 +0,0 @@
|
|||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1691460113549" class="icon" viewBox="0 0 1443 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2328" xmlns:xlink="http://www.w3.org/1999/xlink" width="90.1875" height="64"><path d="M1200.165345 1023.218368H349.208974c-192.401638 0-349.208974-215.369584-349.208974-406.207959a348.006463 348.006463 0 0 1 349.208974-346.022321 322.33287 322.33287 0 0 1 38.780955 2.344895C470.963136 141.056856 617.248506 0.002405 774.777348 0.002405a454.368494 454.368494 0 0 1 454.669122 423.403856c120.251024 14.309872 213.625944 168.952689 213.625944 359.189808a242.005186 242.005186 0 0 1-242.907069 240.622299zM349.148848 330.57247A288.061328 288.061328 0 0 0 60.125512 617.010409c0 158.009846 129.690729 346.623577 289.083462 346.623577h850.956371A182.120176 182.120176 0 0 0 1382.886776 782.596069c0-156.867461-81.951073-301.349066-182.661305-301.349066a30.062756 30.062756 0 0 1-30.062756-30.062756A394.062606 394.062606 0 0 0 774.717222 59.28616c-142.67784 0-274.893841 136.965916-344.940062 260.70422a29.581752 29.581752 0 0 1-31.265266 14.73075 297.080155 297.080155 0 0 0-49.30292-4.14866z m881.079253 120.912405zM300.62756 783.497952H240.502048v-240.502048h60.125512v60.125512h60.125512v-60.125512h60.125512v240.502048H360.753072v-120.251024H300.62756v120.251024z m180.376536-180.376536v-60.125512h180.376536v60.125512h-60.125512v180.376536H541.129608v-180.376536H481.004096z m240.502048 0v-60.125512h180.376536v60.125512h-60.125512v180.376536h-60.125512v-180.376536h-60.125512z m300.62756 180.376536h-60.125512v-240.502048h60.125512c71.368983-0.962008 127.225583-24.050205 126.263575 30.062756-1.863891 50.264928-7.936568 147.548006-66.138063 150.31378h-60.125512v60.125512z m0-180.376536v60.125512a35.293676 35.293676 0 0 0 12.025103 6.012551q56.39773 5.471422 54.954717-36.075307 0-37.818947-54.954717-36.075307a35.293676 35.293676 0 0 0-12.025103 6.012551z" fill="#7839ee" p-id="2329"></path></svg>
|
||||
|
Before Width: | Height: | Size: 2.0 KiB |