Commit 85fbe9dc by Archer Committed by GitHub

feat: pro token (#7238)

* feat: pro token

* submodule

* test: fix admin action env
parent 73d13a91
...@@ -5,7 +5,6 @@ stringData: ...@@ -5,7 +5,6 @@ stringData:
CHAT_API_KEY: "sk-xxxx" CHAT_API_KEY: "sk-xxxx"
DB_MAX_LINK: "5" DB_MAX_LINK: "5"
SYSTEM_MAX_STRING_LENGTH_M: "{{ .Values.system.maxStringLengthM }}" SYSTEM_MAX_STRING_LENGTH_M: "{{ .Values.system.maxStringLengthM }}"
TOKEN_KEY: "any"
ROOT_KEY: "root_key" ROOT_KEY: "root_key"
FILE_TOKEN_KEY: "filetoken" FILE_TOKEN_KEY: "filetoken"
AES256_SECRET_KEY: "fastgptsecret" AES256_SECRET_KEY: "fastgptsecret"
......
...@@ -21,14 +21,14 @@ These variables are mainly validated by `packages/service/env.ts` and apply to ` ...@@ -21,14 +21,14 @@ These variables are mainly validated by `packages/service/env.ts` and apply to `
### Basics and Secrets ### Basics and Secrets
| Variable | Default | Description | | Variable | Default | Description |
| --------------------- | ------------------- | ---------------------------------------------------------------------------------------------------------- | | --------------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `DB_MAX_LINK` | `5` | Maximum connection pool size for MongoDB, PG, OceanBase, openGauss, and other databases. | | `DB_MAX_LINK` | `5` | Maximum connection pool size for MongoDB, PG, OceanBase, openGauss, and other databases. |
| `SYNC_INDEX` | `true` | Whether MongoDB indexes are synchronized at startup. | | `SYNC_INDEX` | `true` | Whether MongoDB indexes are synchronized at startup. |
| `TOKEN_KEY` | `fastgpt_token_key` | Signing secret for user sessions and business tokens. Must be at least 6 characters. |
| `FILE_TOKEN_KEY` | None, **required** | Secret for file read and file authorization flows. Must be at least 6 characters. | | `FILE_TOKEN_KEY` | None, **required** | Secret for file read and file authorization flows. Must be at least 6 characters. |
| `AES256_SECRET_KEY` | None, **required** | Secret used by AES encryption and decryption. Must be at least 6 characters. | | `AES256_SECRET_KEY` | None, **required** | Secret used by AES encryption and decryption. Must be at least 6 characters. |
| `INVOKE_TOKEN_SECRET` | None, **required** | JWT secret for Invoke reverse calls. Must be at least 32 characters. | | `INVOKE_TOKEN_SECRET` | None, **required** | JWT secret for Invoke reverse calls. Must be at least 32 characters. |
| `ROOT_KEY` | `fastgpt_root_key` | Root API key or internal highest-privilege secret. Must be at least 6 characters. | | `ROOT_KEY` | `fastgpt_root_key` | Admin API key for the current system. It can call `/api/admin/**` APIs and must be at least 6 characters. |
| `PRO_TOKEN` | Empty | Token for FastGPT app server calls to pro/admin internal APIs. It must match the pro/admin configuration and is required when App configures `PRO_URL`. |
| `PRO_URL` | Empty | Commercial service URL. When set, App can call Pro APIs, and the domain is allowed by file URL validation. | | `PRO_URL` | Empty | Commercial service URL. When set, App can call Pro APIs, and the domain is allowed by file URL validation. |
### Service URLs and Integrations ### Service URLs and Integrations
...@@ -226,22 +226,18 @@ These variables are mainly read by `projects/app`. Some are currently defined in ...@@ -226,22 +226,18 @@ These variables are mainly read by `projects/app`. Some are currently defined in
These variables are mainly read by `pro/admin`. Admin also uses the shared App/Admin variables above. These variables are mainly read by `pro/admin`. Admin also uses the shared App/Admin variables above.
| Variable | Default | Description | | Variable | Default | Description |
| ------------------------------------- | ----------- | ----------------------------------------------------------------------------------------------- | | ------------------------------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------ |
| `EVAL_CONCURRENCY` | `3` | Batch evaluation task concurrency. | | `PRO_TOKEN` | None, **required** | Service-to-service token for FastGPT app calls to pro/admin internal APIs. Must be at least 32 characters and match App. |
| `EVAL_LINE_LIMIT` | `1000` | Maximum number of rows allowed when creating one evaluation task. Also sent to frontend config. | | `EVAL_LINE_LIMIT` | `1000` | Maximum number of rows allowed when creating one evaluation task. Also sent to frontend config. |
| `UPDATE_BALANCE_DELAY` | Empty | Legacy balance update frequency variable, still kept in types. |
| `BATCH_UPDATE_TIME` | `3000` | Wallet balance batch update interval, in milliseconds. | | `BATCH_UPDATE_TIME` | `3000` | Wallet balance batch update interval, in milliseconds. |
| `INVOICE_FEISHU_WEBHOOK_URL` | Empty | Lark webhook URL for invoice application notifications. | | `INVOICE_FEISHU_WEBHOOK_URL` | Empty | Lark webhook URL for invoice application notifications. |
| `INVOICE_FEISHU_WEBHOOK_CALLBACK_URL` | Empty | Callback URL for buttons in invoice notifications. | | `INVOICE_FEISHU_WEBHOOK_CALLBACK_URL` | Empty | Callback URL for buttons in invoice notifications. |
| `SMS_PROXY` | Empty | SMS sending proxy service URL. | | `SMS_PROXY` | Empty | SMS sending proxy service URL. |
| `MAX_CRAWL_PAGE` | `2000` | Maximum number of pages to crawl during website sync. | | `MAX_CRAWL_PAGE` | `2000` | Maximum number of pages to crawl during website sync. |
| `CRAWL_DYNAMIC_WEBSITE` | `false` | Whether dynamic page crawling is enabled. |
| `PLUGIN_URL` | Empty | Plugin service URL for dynamic page crawling. |
| `CRAWL_MAX_HTML_SIZE` | `10` | Estimated maximum HTML size for one static crawled page, in MB. | | `CRAWL_MAX_HTML_SIZE` | `10` | Estimated maximum HTML size for one static crawled page, in MB. |
| `CRAWL_EXCLUDE_LIST` | Empty | Crawler exclusion rules for domains or paths. Use commas to separate values. | | `CRAWL_EXCLUDE_LIST` | Empty | Crawler exclusion rules for domains or paths. Use commas to separate values. |
| `SHOW_GIT` | Empty | Whether Git information is shown in Admin. Empty means hidden. | | `SHOW_GIT` | `false` | Whether Git information is shown in Admin. |
| `WARN_FREE_ACCOUNT` | Empty | Warning configuration before free account cleanup. | | `CLEAR_FREE_ACCOUNT` | `false` | Whether free account resource cleanup is enabled. |
| `CLEAR_FREE_ACCOUNT` | Empty | Free account cleanup configuration. |
| `SYNC_MEMBER_CRON` | Empty | Cron expression for automatic member sync. Empty disables the sync task. | | `SYNC_MEMBER_CRON` | Empty | Cron expression for automatic member sync. Empty disables the sync task. |
| `WORKORDER_BASE_URL` | Empty | Work order system URL. When set, the frontend shows work order entry points. | | `WORKORDER_BASE_URL` | Empty | Work order system URL. When set, the frontend shows work order entry points. |
| `WORKORDER_JWT_SECRET` | Empty | Secret used to sign JWTs when creating work orders. | | `WORKORDER_JWT_SECRET` | Empty | Secret used to sign JWTs when creating work orders. |
......
...@@ -21,14 +21,14 @@ description: projects/app、projects/code-sandbox 与 pro/admin 环境变量说 ...@@ -21,14 +21,14 @@ description: projects/app、projects/code-sandbox 与 pro/admin 环境变量说
### 基础与密钥 ### 基础与密钥
| 变量 | 默认值 | 说明 | | 变量 | 默认值 | 说明 |
| --------------------- | ------------------- | ------------------------------------------------------------------------------ | | --------------------- | ------------------ | ----------------------------------------------------------------------------------------------------- |
| `DB_MAX_LINK` | `5` | MongoDB、PG、OceanBase、openGauss 等数据库连接池最大连接数。 | | `DB_MAX_LINK` | `5` | MongoDB、PG、OceanBase、openGauss 等数据库连接池最大连接数。 |
| `SYNC_INDEX` | `true` | 启动时是否同步 MongoDB 索引。 | | `SYNC_INDEX` | `true` | 启动时是否同步 MongoDB 索引。 |
| `TOKEN_KEY` | `fastgpt_token_key` | 用户登录态、业务 Token 等签名密钥,长度至少 6 位。 |
| `FILE_TOKEN_KEY` | 无,**必填** | 文件读取、文件鉴权相关密钥,长度至少 6 位。 | | `FILE_TOKEN_KEY` | 无,**必填** | 文件读取、文件鉴权相关密钥,长度至少 6 位。 |
| `AES256_SECRET_KEY` | 无,**必填** | AES 加解密密钥,长度至少 6 位。 | | `AES256_SECRET_KEY` | 无,**必填** | AES 加解密密钥,长度至少 6 位。 |
| `INVOKE_TOKEN_SECRET` | 无,**必填** | Invoke 反向调用 JWT 密钥,长度至少 32 位。 | | `INVOKE_TOKEN_SECRET` | 无,**必填** | Invoke 反向调用 JWT 密钥,长度至少 32 位。 |
| `ROOT_KEY` | `fastgpt_root_key` | Root API Key/内部最高权限密钥,长度至少 6 位。 | | `ROOT_KEY` | `fastgpt_root_key` | 当前系统管理员 API 密钥,可用于调用 `/api/admin/**` 接口,长度至少 6 位。 |
| `PRO_TOKEN` | 空 | FastGPT app 服务端调用 pro/admin 内部接口的凭证,需与 pro/admin 配置一致;App 配置 `PRO_URL` 时必填。 |
| `PRO_URL` | 空 | 商业版服务地址,配置后 App 可调用 Pro API,也会作为文件 URL 安全校验允许域名。 | | `PRO_URL` | 空 | 商业版服务地址,配置后 App 可调用 Pro API,也会作为文件 URL 安全校验允许域名。 |
### 服务地址与集成 ### 服务地址与集成
...@@ -226,22 +226,18 @@ description: projects/app、projects/code-sandbox 与 pro/admin 环境变量说 ...@@ -226,22 +226,18 @@ description: projects/app、projects/code-sandbox 与 pro/admin 环境变量说
以下变量主要由 `pro/admin` 读取。Admin 同时也会使用上面的 App/Admin 共享变量。 以下变量主要由 `pro/admin` 读取。Admin 同时也会使用上面的 App/Admin 共享变量。
| 变量 | 默认值 | 说明 | | 变量 | 默认值 | 说明 |
| ------------------------------------- | ----------- | -------------------------------------------------------- | | ------------------------------------- | ------------ | ---------------------------------------------------------------------------------- |
| `EVAL_CONCURRENCY` | `3` | 批量评估任务并发数。 | | `PRO_TOKEN` | 无,**必填** | FastGPT app 服务端调用 pro/admin 内部接口的服务间凭证,至少 32 位,需与 App 一致。 |
| `EVAL_LINE_LIMIT` | `1000` | 单次创建评估任务允许的最大数据行数,也会下发给前端配置。 | | `EVAL_LINE_LIMIT` | `1000` | 单次创建评估任务允许的最大数据行数,也会下发给前端配置。 |
| `UPDATE_BALANCE_DELAY` | 空 | 历史余额更新频率变量,当前类型中保留。 |
| `BATCH_UPDATE_TIME` | `3000` | 钱包余额批量更新间隔,单位毫秒。 | | `BATCH_UPDATE_TIME` | `3000` | 钱包余额批量更新间隔,单位毫秒。 |
| `INVOICE_FEISHU_WEBHOOK_URL` | 空 | 发票申请通知飞书 Webhook 地址。 | | `INVOICE_FEISHU_WEBHOOK_URL` | 空 | 发票申请通知飞书 Webhook 地址。 |
| `INVOICE_FEISHU_WEBHOOK_CALLBACK_URL` | 空 | 发票通知中按钮回调地址。 | | `INVOICE_FEISHU_WEBHOOK_CALLBACK_URL` | 空 | 发票通知中按钮回调地址。 |
| `SMS_PROXY` | 空 | 短信发送代理服务地址。 | | `SMS_PROXY` | 空 | 短信发送代理服务地址。 |
| `MAX_CRAWL_PAGE` | `2000` | 网站同步最大抓取页面数。 | | `MAX_CRAWL_PAGE` | `2000` | 网站同步最大抓取页面数。 |
| `CRAWL_DYNAMIC_WEBSITE` | `false` | 是否启用动态页面爬虫。 |
| `PLUGIN_URL` | 空 | 动态页面爬虫插件服务地址。 |
| `CRAWL_MAX_HTML_SIZE` | `10` | 静态网页爬虫单页 HTML 估算大小上限,单位 MB。 | | `CRAWL_MAX_HTML_SIZE` | `10` | 静态网页爬虫单页 HTML 估算大小上限,单位 MB。 |
| `CRAWL_EXCLUDE_LIST` | 空 | 爬虫排除域名或路径规则,多个值使用英文逗号分隔。 | | `CRAWL_EXCLUDE_LIST` | 空 | 爬虫排除域名或路径规则,多个值使用英文逗号分隔。 |
| `SHOW_GIT` | 空 | 是否在后台展示 Git 信息;不填写则不展示。 | | `SHOW_GIT` | `false` | 是否在后台展示 Git 信息。 |
| `WARN_FREE_ACCOUNT` | 空 | 免费账号清理前的提醒配置。 | | `CLEAR_FREE_ACCOUNT` | `false` | 是否启用免费账号资源清理任务。 |
| `CLEAR_FREE_ACCOUNT` | 空 | 免费账号清理配置。 |
| `SYNC_MEMBER_CRON` | 空 | 成员自动同步 Cron 表达式;为空则不启动同步任务。 | | `SYNC_MEMBER_CRON` | 空 | 成员自动同步 Cron 表达式;为空则不启动同步任务。 |
| `WORKORDER_BASE_URL` | 空 | 工单系统地址;配置后前端展示工单入口。 | | `WORKORDER_BASE_URL` | 空 | 工单系统地址;配置后前端展示工单入口。 |
| `WORKORDER_JWT_SECRET` | 空 | 创建工单时签发 JWT 使用的密钥。 | | `WORKORDER_JWT_SECRET` | 空 | 创建工单时签发 JWT 使用的密钥。 |
......
...@@ -9,7 +9,7 @@ This page covers shared Agent Sandbox configuration for both `opensandbox` and ` ...@@ -9,7 +9,7 @@ This page covers shared Agent Sandbox configuration for both `opensandbox` and `
### 1. Add the yml service ### 1. Add the yml service
Use [agent-sandbox-proxy.yml](/deploy/docker/v4.15/agent-proxy.yml) as a reference and add the service to your yml file. Expose the external access port and record the `AGENT_SANDBOX_PROXY_SECRET` value, which you will need in the next step. Use [agent-sandbox-proxy.yml](/deploy/sandbox_deploy/agent-proxy.yml) as a reference and add the service to your yml file. Expose the external access port and record the `AGENT_SANDBOX_PROXY_SECRET` value, which you will need in the next step.
FastGPT uses this proxy when accessing the sandbox file system. FastGPT uses this proxy when accessing the sandbox file system.
......
...@@ -9,7 +9,7 @@ description: FastGPT Agent Sandbox 通用配置 ...@@ -9,7 +9,7 @@ description: FastGPT Agent Sandbox 通用配置
### 1. 添加 yml ### 1. 添加 yml
可以参考 [agent-sandbox-proxy.yml](/deploy/docker/v4.15/agent-proxy.yml),将 service 加到 yml 文件里。并开放外网访问端口。并记录 `AGENT_SANDBOX_PROXY_SECRET` 环境变量,下一步需要使用。 可以参考 [agent-sandbox-proxy.yml](/deploy/sandbox_deploy/agent-proxy.yml),将 service 加到 yml 文件里。并开放外网访问端口。并记录 `AGENT_SANDBOX_PROXY_SECRET` 环境变量,下一步需要使用。
FastGPT 服务里访问沙盒内部文件系统,会通过 proxy 去代理访问。 FastGPT 服务里访问沙盒内部文件系统,会通过 proxy 去代理访问。
......
...@@ -16,7 +16,7 @@ The OpenSandbox setup flow is below. ...@@ -16,7 +16,7 @@ The OpenSandbox setup flow is below.
## 1. Add yml services ## 1. Add yml services
Use [opensandbox.yml](/deploy/docker/v4.15/opensandbox.yml) as a reference. Add `fastgpt-opensandbox-server`, `fastgpt-volume-manager`, the image pre-pull services, and `opensandbox-config` to your current FastGPT `docker-compose.yml`. Place them on the same `app` network as the FastGPT App service. You do not need to expose OpenSandbox or Volume Manager ports publicly. Deploy Agent Sandbox Proxy separately as described in [General Sandbox Configuration](./common). Use [opensandbox.yml](/deploy/sandbox_deploy/opensandbox.yml) as a reference. Add `fastgpt-opensandbox-server`, `fastgpt-volume-manager`, the image pre-pull services, and `opensandbox-config` to your current FastGPT `docker-compose.yml`. Place them on the same `app` network as the FastGPT App service. You do not need to expose OpenSandbox or Volume Manager ports publicly. Deploy Agent Sandbox Proxy separately as described in [General Sandbox Configuration](./common).
The sample uses China Mainland image registries. For deployments outside China Mainland, replace them with: The sample uses China Mainland image registries. For deployments outside China Mainland, replace them with:
......
...@@ -15,7 +15,7 @@ OpenSandbox 适合需要自托管 Agent/Skill 沙盒运行环境的场景。开 ...@@ -15,7 +15,7 @@ OpenSandbox 适合需要自托管 Agent/Skill 沙盒运行环境的场景。开
## 1. 添加 yml service ## 1. 添加 yml service
参考 [opensandbox.yml](/deploy/docker/v4.15/opensandbox.yml),将 `fastgpt-opensandbox-server`、`fastgpt-volume-manager`、预拉取镜像和 `opensandbox-config` 加入当前 FastGPT 部署的 `docker-compose.yml`,并放到 FastGPT App 所在的 `app` network 中;不需要对外暴露 OpenSandbox 或 Volume Manager 端口。Agent Sandbox Proxy 请按[沙盒通用配置](./common)单独部署。 参考 [opensandbox.yml](/deploy/sandbox_deploy/opensandbox.yml),将 `fastgpt-opensandbox-server`、`fastgpt-volume-manager`、预拉取镜像和 `opensandbox-config` 加入当前 FastGPT 部署的 `docker-compose.yml`,并放到 FastGPT App 所在的 `app` network 中;不需要对外暴露 OpenSandbox 或 Volume Manager 端口。Agent Sandbox Proxy 请按[沙盒通用配置](./common)单独部署。
下面示例使用国内镜像源。海外部署可将镜像替换为: 下面示例使用国内镜像源。海外部署可将镜像替换为:
......
...@@ -5,6 +5,22 @@ description: 'FastGPT V4.15.1 Release Notes' ...@@ -5,6 +5,22 @@ description: 'FastGPT V4.15.1 Release Notes'
## 📦 Upgrade Guide ## 📦 Upgrade Guide
### Pro Internal API Authentication
Starting from v4.15.1, the FastGPT app no longer uses `rootkey` when calling Pro/Admin internal APIs. These internal service-to-service calls now use a dedicated `PRO_TOKEN`. If you deploy the Pro edition, configure the same `PRO_TOKEN` in both the FastGPT app and the Pro/Admin service:
```bash
PRO_TOKEN=your_pro_token_at_least_32_chars
```
Notes:
1. `PRO_TOKEN` must be at least 32 characters long, and the value must be identical in the FastGPT app and Pro/Admin.
2. If the FastGPT app is configured with `PRO_URL`, `PRO_TOKEN` is also required. Otherwise, the service fails to start.
3. The Pro/Admin service must configure `PRO_TOKEN`; otherwise, internal API authentication fails.
4. `rootkey` is no longer used as the credential for FastGPT app calls to Pro/Admin internal APIs. It is only the admin secret for the current system and is used to call `/api/admin/**` APIs, such as the initialization script below.
5. Open-source deployment files do not include `PRO_TOKEN`. For Pro deployments, add it manually in your private deployment environment variables.
### API Key App Name Initialization ### API Key App Name Initialization
To keep older API keys compatible and make it easier to find keys previously associated with apps, v4.15.1 adds global API Key tag management and an `appName` display snapshot for historical app-level API Keys. After upgrading, run the initialization script once to backfill app names for existing API Keys whose `appId` field is still present. To keep older API keys compatible and make it easier to find keys previously associated with apps, v4.15.1 adds global API Key tag management and an `appName` display snapshot for historical app-level API Keys. After upgrading, run the initialization script once to backfill app names for existing API Keys whose `appId` field is still present.
......
...@@ -5,6 +5,23 @@ description: 'FastGPT V4.15.1 更新说明' ...@@ -5,6 +5,23 @@ description: 'FastGPT V4.15.1 更新说明'
## 📦 升级指南 ## 📦 升级指南
### fastgpt-pro 环境变量更新
v4.15.1 起,FastGPT 主应用访问 Pro/Admin 内部接口不再使用 `rootkey`,改为使用独立的服务间凭证 `PRO_TOKEN`。同时要求 `FE_DOMAIN` 变量必填,如果你部署了 Pro 版本,需要同时在 FastGPT 主应用和 Pro/Admin 服务中配置相同的 `PRO_TOKEN`:
```bash
PRO_TOKEN=your_pro_token_at_least_32_chars
FE_DOMAIN=fastgpt_domain
```
注意事项:
1. `PRO_TOKEN` 长度必须不少于 32 位,并且主应用与 Pro/Admin 必须保持一致。
2. 如果 FastGPT 主应用配置了 `PRO_URL`,则必须同时配置 `PRO_TOKEN`,否则服务会启动失败。
3. Pro/Admin 服务必须配置 `PRO_TOKEN`,否则内部接口鉴权会失败。
4. `rootkey` 不再作为 FastGPT 主应用访问 Pro/Admin 内部接口的凭证,仅作为当前系统的管理员密钥,用于调用 `/api/admin/**` 接口,例如下方初始化脚本。
5. 开源版部署配置文件不会内置 `PRO_TOKEN`。Pro 部署请在私有部署环境变量中手动增加该配置。
### API Key 应用名初始化 ### API Key 应用名初始化
为了兼容旧版 API 密钥,便于找到以前应用关联的密钥,v4.15.1 增加了全局 API Key 标签管理,并为历史应用级 API Key 增加 `appName` 展示快照。升级后建议执行一次初始化脚本,为已有 `appId` 的历史 API Key 自动回填应用名。 为了兼容旧版 API 密钥,便于找到以前应用关联的密钥,v4.15.1 增加了全局 API Key 标签管理,并为历史应用级 API Key 增加 `appName` 展示快照。升级后建议执行一次初始化脚本,为已有 `appId` 的历史 API Key 自动回填应用名。
......
...@@ -315,7 +315,7 @@ ...@@ -315,7 +315,7 @@
"content/self-host/upgrading/4-15/41507.en.mdx": "2026-06-30T17:31:43+08:00", "content/self-host/upgrading/4-15/41507.en.mdx": "2026-06-30T17:31:43+08:00",
"content/self-host/upgrading/4-15/41507.mdx": "2026-06-30T17:31:43+08:00", "content/self-host/upgrading/4-15/41507.mdx": "2026-06-30T17:31:43+08:00",
"content/self-host/upgrading/4-15/4151.en.mdx": "2026-07-02T09:56:55+08:00", "content/self-host/upgrading/4-15/4151.en.mdx": "2026-07-02T09:56:55+08:00",
"content/self-host/upgrading/4-15/4151.mdx": "2026-07-02T09:56:55+08:00", "content/self-host/upgrading/4-15/4151.mdx": "2026-07-02T11:55:16+08:00",
"content/self-host/upgrading/outdated/40.en.mdx": "2026-04-26T21:08:47+08:00", "content/self-host/upgrading/outdated/40.en.mdx": "2026-04-26T21:08:47+08:00",
"content/self-host/upgrading/outdated/40.mdx": "2026-04-26T21:08:47+08:00", "content/self-host/upgrading/outdated/40.mdx": "2026-04-26T21:08:47+08:00",
"content/self-host/upgrading/outdated/41.en.mdx": "2026-04-26T21:08:47+08:00", "content/self-host/upgrading/outdated/41.en.mdx": "2026-04-26T21:08:47+08:00",
......
...@@ -252,7 +252,6 @@ randomize_compose_credentials() { ...@@ -252,7 +252,6 @@ randomize_compose_credentials() {
replace_text 'x-aiproxy-token: &x-aiproxy-token "token"' "x-aiproxy-token: &x-aiproxy-token \"$aiproxy_token\"" replace_text 'x-aiproxy-token: &x-aiproxy-token "token"' "x-aiproxy-token: &x-aiproxy-token \"$aiproxy_token\""
# 旧版本没有为这些密钥设置 anchor,需要直接替换环境变量默认值。 # 旧版本没有为这些密钥设置 anchor,需要直接替换环境变量默认值。
replace_text "TOKEN_KEY: fastgpt" "TOKEN_KEY: $token_key"
replace_text "FILE_TOKEN_KEY: filetokenkey" "FILE_TOKEN_KEY: $file_token_key" replace_text "FILE_TOKEN_KEY: filetokenkey" "FILE_TOKEN_KEY: $file_token_key"
replace_text "AES256_SECRET_KEY: fastgptsecret" "AES256_SECRET_KEY: $aes256_secret_key" replace_text "AES256_SECRET_KEY: fastgptsecret" "AES256_SECRET_KEY: $aes256_secret_key"
replace_text "INVOKE_TOKEN_SECRET: fastgpt_invoke_token_secret_32_chars_min" "INVOKE_TOKEN_SECRET: $invoke_token_secret" replace_text "INVOKE_TOKEN_SECRET: fastgpt_invoke_token_secret_32_chars_min" "INVOKE_TOKEN_SECRET: $invoke_token_secret"
......
...@@ -40,6 +40,7 @@ export const proxyError: Record<string, boolean> = { ...@@ -40,6 +40,7 @@ export const proxyError: Record<string, boolean> = {
export enum ERROR_ENUM { export enum ERROR_ENUM {
unAuthorization = 'unAuthorization', unAuthorization = 'unAuthorization',
unAuthProToken = 'unAuthProToken',
insufficientQuota = 'insufficientQuota', insufficientQuota = 'insufficientQuota',
unAuthModel = 'unAuthModel', unAuthModel = 'unAuthModel',
unAuthApiKey = 'unAuthApiKey', unAuthApiKey = 'unAuthApiKey',
...@@ -76,6 +77,12 @@ export const ERROR_RESPONSE: Record< ...@@ -76,6 +77,12 @@ export const ERROR_RESPONSE: Record<
message: i18nT('common:code_error.error_message.403'), message: i18nT('common:code_error.error_message.403'),
data: null data: null
}, },
[ERROR_ENUM.unAuthProToken]: {
code: 403,
statusText: ERROR_ENUM.unAuthProToken,
message: 'PRO_TOKEN check error',
data: null
},
[ERROR_ENUM.tooManyRequest]: { [ERROR_ENUM.tooManyRequest]: {
code: 429, code: 429,
statusText: ERROR_ENUM.tooManyRequest, statusText: ERROR_ENUM.tooManyRequest,
......
...@@ -10,3 +10,5 @@ export const isDevEnv = process.env.NODE_ENV === 'development'; ...@@ -10,3 +10,5 @@ export const isDevEnv = process.env.NODE_ENV === 'development';
export const isProduction = process.env.NODE_ENV === 'production'; export const isProduction = process.env.NODE_ENV === 'production';
export const isTestEnv = process.env.NODE_ENV === 'test'; export const isTestEnv = process.env.NODE_ENV === 'test';
export const isPhaseProductionBuild = process.env.NEXT_PHASE === 'phase-production-build'; export const isPhaseProductionBuild = process.env.NEXT_PHASE === 'phase-production-build';
export const FASTGPT_PRO_TOKEN_HEADER = 'x-fastgpt-pro-token';
...@@ -10,19 +10,20 @@ import { createProxyAxios } from './axios'; ...@@ -10,19 +10,20 @@ import { createProxyAxios } from './axios';
import { getLogger, LogCategories } from '../logger'; import { getLogger, LogCategories } from '../logger';
import { assertRelativePath } from '../security/network'; import { assertRelativePath } from '../security/network';
import { serviceEnv } from '../../env'; import { serviceEnv } from '../../env';
import { FASTGPT_PRO_TOKEN_HEADER } from '@fastgpt/global/common/system/constants';
const logger = getLogger(LogCategories.HTTP.ERROR); const logger = getLogger(LogCategories.HTTP.ERROR);
interface ConfigType { type ConfigType = {
headers?: { [key: string]: string }; headers?: { [key: string]: string };
hold?: boolean; hold?: boolean;
timeout?: number; timeout?: number;
} };
interface ResponseDataType { type ResponseDataType = {
code: number; code: number;
message: string; message: string;
data: any; data: any;
} };
/** /**
* 请求开始 * 请求开始
...@@ -70,6 +71,27 @@ function responseError(err: any) { ...@@ -70,6 +71,27 @@ function responseError(err: any) {
return Promise.reject(err); return Promise.reject(err);
} }
/**
* 校验 FastGPT app 服务端访问 pro/admin 的内部请求配置。
*
* 本文件同时保留 GET/POST 等快捷封装和原始 axios config 两种入口;集中处理
* PRO_URL、PRO_TOKEN 和相对路径校验,避免两条入口出现鉴权语义分叉。
*/
const assertInternalProRequestConfig = ({ url }: { url?: string }) => {
if (!FastGPTProUrl) {
logger.warn('FastGPT Pro API is not configured', { url });
throw new UserError('The request was denied...');
}
if (!serviceEnv.PRO_TOKEN) {
logger.error('FastGPT Pro token is not configured', { url });
throw new UserError('FastGPT Pro token is not configured');
}
// plusRequest 仅用于访问商业版 Pro 服务,会自动携带内部 Pro token,SSRF 拦截已被显式关闭。
// 强制要求相对路径,防止调用方传入绝对 URL 覆盖 baseURL 形成带高权限头的 SSRF。
assertRelativePath(url, 'plusRequest');
};
/* 创建请求实例 */ /* 创建请求实例 */
const instance = createProxyAxios( const instance = createProxyAxios(
{ {
...@@ -77,7 +99,7 @@ const instance = createProxyAxios( ...@@ -77,7 +99,7 @@ const instance = createProxyAxios(
headers: { headers: {
'content-type': 'application/json', 'content-type': 'application/json',
'Cache-Control': 'no-cache', 'Cache-Control': 'no-cache',
rootkey: serviceEnv.ROOT_KEY [FASTGPT_PRO_TOKEN_HEADER]: serviceEnv.PRO_TOKEN
} }
}, },
false false
...@@ -89,15 +111,8 @@ instance.interceptors.request.use(requestStart, (err) => Promise.reject(err)); ...@@ -89,15 +111,8 @@ instance.interceptors.request.use(requestStart, (err) => Promise.reject(err));
instance.interceptors.response.use(responseSuccess, (err) => Promise.reject(err)); instance.interceptors.response.use(responseSuccess, (err) => Promise.reject(err));
export function request(url: string, data: any, config: ConfigType, method: Method): any { export function request(url: string, data: any, config: ConfigType, method: Method): any {
if (!FastGPTProUrl) {
logger.warn('FastGPT Pro API is not configured', { url });
return Promise.reject(new UserError('The request was denied...'));
}
// plusRequest 仅用于访问商业版 Pro 服务,会自动携带 rootkey,SSRF 拦截已被显式关闭。
// 强制要求相对路径,防止调用方传入绝对 URL 覆盖 baseURL 形成带高权限头的 SSRF。
try { try {
assertRelativePath(url, 'plusRequest'); assertInternalProRequestConfig({ url });
} catch (err) { } catch (err) {
return Promise.reject(err); return Promise.reject(err);
} }
...@@ -147,7 +162,7 @@ export function DELETE<T = undefined>(url: string, data = {}, config: ConfigType ...@@ -147,7 +162,7 @@ export function DELETE<T = undefined>(url: string, data = {}, config: ConfigType
export const plusRequest = (config: AxiosRequestConfig) => { export const plusRequest = (config: AxiosRequestConfig) => {
try { try {
assertRelativePath(config.url, 'plusRequest'); assertInternalProRequestConfig({ url: config.url });
} catch (err) { } catch (err) {
return Promise.reject(err); return Promise.reject(err);
} }
......
...@@ -35,10 +35,6 @@ export const serviceEnv = createEnv({ ...@@ -35,10 +35,6 @@ export const serviceEnv = createEnv({
.string() .string()
.min(6, 'ROOT_KEY must be at least 6 characters') .min(6, 'ROOT_KEY must be at least 6 characters')
.default('fastgpt_root_key'), .default('fastgpt_root_key'),
TOKEN_KEY: z
.string()
.min(6, 'TOKEN_KEY must be at least 6 characters')
.default('fastgpt_token_key'),
FILE_TOKEN_KEY: z.string().min(6, 'FILE_TOKEN_KEY must be at least 6 characters'), FILE_TOKEN_KEY: z.string().min(6, 'FILE_TOKEN_KEY must be at least 6 characters'),
AES256_SECRET_KEY: z.string().min(6, 'AES256_SECRET_KEY must be at least 6 characters'), AES256_SECRET_KEY: z.string().min(6, 'AES256_SECRET_KEY must be at least 6 characters'),
...@@ -61,6 +57,7 @@ export const serviceEnv = createEnv({ ...@@ -61,6 +57,7 @@ export const serviceEnv = createEnv({
CHAT_API_KEY: z.string().optional(), CHAT_API_KEY: z.string().optional(),
PRO_URL: UrlSchema.optional(), PRO_URL: UrlSchema.optional(),
PRO_TOKEN: z.string().min(32, 'PRO_TOKEN must be at least 32 characters').optional(),
// Agent sandbox proxy // Agent sandbox proxy
AGENT_SANDBOX_PROXY_SECRET: z AGENT_SANDBOX_PROXY_SECRET: z
...@@ -363,6 +360,12 @@ if (serviceEnv.WORKFLOW_PARALLEL_MAX_CONCURRENCY > serviceEnv.WORKFLOW_MAX_LOOP_ ...@@ -363,6 +360,12 @@ if (serviceEnv.WORKFLOW_PARALLEL_MAX_CONCURRENCY > serviceEnv.WORKFLOW_MAX_LOOP_
} }
if (!isPhaseProductionBuild) { if (!isPhaseProductionBuild) {
if (serviceEnv.PRO_URL && !serviceEnv.PRO_TOKEN) {
throw new Error(
'Invalid environment configuration: PRO_TOKEN is required when PRO_URL is configured.'
);
}
// 共享 serviceEnv 会被 pro/admin 等项目导入,这里只校验 provider 运行态必填环境变量。 // 共享 serviceEnv 会被 pro/admin 等项目导入,这里只校验 provider 运行态必填环境变量。
// 主站浏览器直连 agent-sandbox-proxy 的配置由 projects/app 启动流程单独校验。 // 主站浏览器直连 agent-sandbox-proxy 的配置由 projects/app 启动流程单独校验。
const missingAgentSandboxEnvKeys = getAgentSandboxMissingRequiredEnvKeys(process.env); const missingAgentSandboxEnvKeys = getAgentSandboxMissingRequiredEnvKeys(process.env);
......
...@@ -3,10 +3,11 @@ import { agentSandboxProviderList } from '@fastgpt/global/core/ai/sandbox/consta ...@@ -3,10 +3,11 @@ import { agentSandboxProviderList } from '@fastgpt/global/core/ai/sandbox/consta
import z from 'zod'; import z from 'zod';
const TEST_INVOKE_TOKEN_SECRET = 'fastgpt_test_invoke_token_secret_32'; const TEST_INVOKE_TOKEN_SECRET = 'fastgpt_test_invoke_token_secret_32';
const TEST_PRO_TOKEN = 'fastgpt_test_pro_token_32_chars_min';
/** /**
* 测试套件会在多个 workspace(包含 pro/admin 子模块)里直接导入 serviceEnv。 * 测试套件会在多个 workspace(包含 pro/admin 子模块)里直接导入 serviceEnv。
* 生产启动仍要求显式配置 INVOKE_TOKEN_SECRET;仅 Vitest/测试环境允许注入稳定测试密钥, * INVOKE_TOKEN_SECRET 生产启动仍要求显式配置;PRO_TOKEN 仅在启用 Pro 内部调用时配置。
* 避免每个测试项目都重复维护同一个必填运行时密钥。 * 仅 Vitest/测试环境允许注入稳定测试密钥,避免每个测试项目都重复维护同一组运行时密钥。
*/ */
export const getRuntimeEnv = (): NodeJS.ProcessEnv => ({ export const getRuntimeEnv = (): NodeJS.ProcessEnv => ({
...process.env, ...process.env,
...@@ -14,7 +15,10 @@ export const getRuntimeEnv = (): NodeJS.ProcessEnv => ({ ...@@ -14,7 +15,10 @@ export const getRuntimeEnv = (): NodeJS.ProcessEnv => ({
process.env.INVOKE_TOKEN_SECRET ?? process.env.INVOKE_TOKEN_SECRET ??
(process.env.VITEST === 'true' || process.env.NODE_ENV === 'test' (process.env.VITEST === 'true' || process.env.NODE_ENV === 'test'
? TEST_INVOKE_TOKEN_SECRET ? TEST_INVOKE_TOKEN_SECRET
: undefined) : undefined),
PRO_TOKEN:
process.env.PRO_TOKEN ??
(process.env.VITEST === 'true' || process.env.NODE_ENV === 'test' ? TEST_PRO_TOKEN : undefined)
}); });
/* ===== sandbox ===== */ /* ===== sandbox ===== */
......
import { afterEach, describe, expect, it, vi } from 'vitest';
import { FASTGPT_PRO_TOKEN_HEADER } from '@fastgpt/global/common/system/constants';
import { createProxyAxios } from '@fastgpt/service/common/api/axios';
const { mockCreateProxyAxios, mockRequest } = vi.hoisted(() => ({
mockCreateProxyAxios: vi.fn(),
mockRequest: vi.fn()
}));
const configuredProToken = 'configured_pro_token_32_chars_min';
vi.mock('@fastgpt/service/common/api/axios', () => ({
createProxyAxios: mockCreateProxyAxios.mockImplementation(() => ({
interceptors: {
request: { use: vi.fn() },
response: { use: vi.fn() }
},
request: mockRequest
}))
}));
const importPlusRequest = async () => {
vi.resetModules();
mockRequest.mockResolvedValue({
data: {
data: { ok: true }
}
});
return import('@fastgpt/service/common/api/plusRequest');
};
describe('plusRequest', () => {
afterEach(() => {
vi.unstubAllEnvs();
vi.resetModules();
mockCreateProxyAxios.mockClear();
mockRequest.mockReset();
});
it('创建请求实例时默认携带 PRO_TOKEN header', async () => {
vi.stubEnv('PRO_URL', 'https://pro.example.com');
vi.stubEnv('PRO_TOKEN', configuredProToken);
await importPlusRequest();
expect(createProxyAxios).toHaveBeenCalledWith(
expect.objectContaining({
headers: expect.objectContaining({
'content-type': 'application/json',
'Cache-Control': 'no-cache',
[FASTGPT_PRO_TOKEN_HEADER]: configuredProToken
})
}),
false
);
});
it('封装请求会校验内部 Pro 请求配置', async () => {
vi.stubEnv('PRO_URL', 'https://pro.example.com');
vi.stubEnv('PRO_TOKEN', configuredProToken);
const { POST } = await importPlusRequest();
await POST(
'/support/test',
{ value: 1 },
{
headers: {
'x-custom-header': 'custom',
[FASTGPT_PRO_TOKEN_HEADER]: 'caller_token'
}
}
);
expect(mockRequest).toHaveBeenCalledWith(
expect.objectContaining({
baseURL: 'https://pro.example.com/api',
url: '/support/test',
headers: expect.objectContaining({
'x-custom-header': 'custom'
})
})
);
});
it('原始 plusRequest 会校验内部 Pro 请求配置', async () => {
vi.stubEnv('PRO_URL', 'https://pro.example.com');
vi.stubEnv('PRO_TOKEN', configuredProToken);
const { plusRequest } = await importPlusRequest();
await plusRequest({
url: '/support/raw',
headers: {
'x-custom-header': 'custom',
[FASTGPT_PRO_TOKEN_HEADER]: 'caller_token'
}
});
expect(mockRequest).toHaveBeenCalledWith(
expect.objectContaining({
baseURL: 'https://pro.example.com/api',
url: '/support/raw',
headers: expect.objectContaining({
'x-custom-header': 'custom'
})
})
);
});
});
...@@ -8,6 +8,8 @@ const originalEnv = { ...@@ -8,6 +8,8 @@ const originalEnv = {
FILE_TOKEN_KEY: process.env.FILE_TOKEN_KEY, FILE_TOKEN_KEY: process.env.FILE_TOKEN_KEY,
AES256_SECRET_KEY: process.env.AES256_SECRET_KEY, AES256_SECRET_KEY: process.env.AES256_SECRET_KEY,
INVOKE_TOKEN_SECRET: process.env.INVOKE_TOKEN_SECRET, INVOKE_TOKEN_SECRET: process.env.INVOKE_TOKEN_SECRET,
PRO_URL: process.env.PRO_URL,
PRO_TOKEN: process.env.PRO_TOKEN,
VITEST: process.env.VITEST, VITEST: process.env.VITEST,
NODE_ENV: process.env.NODE_ENV, NODE_ENV: process.env.NODE_ENV,
AGENT_SANDBOX_PROVIDER: process.env.AGENT_SANDBOX_PROVIDER, AGENT_SANDBOX_PROVIDER: process.env.AGENT_SANDBOX_PROVIDER,
...@@ -32,6 +34,8 @@ describe('serviceEnv', () => { ...@@ -32,6 +34,8 @@ describe('serviceEnv', () => {
vi.stubEnv('FILE_TOKEN_KEY', originalEnv.FILE_TOKEN_KEY); vi.stubEnv('FILE_TOKEN_KEY', originalEnv.FILE_TOKEN_KEY);
vi.stubEnv('AES256_SECRET_KEY', originalEnv.AES256_SECRET_KEY); vi.stubEnv('AES256_SECRET_KEY', originalEnv.AES256_SECRET_KEY);
vi.stubEnv('INVOKE_TOKEN_SECRET', originalEnv.INVOKE_TOKEN_SECRET); vi.stubEnv('INVOKE_TOKEN_SECRET', originalEnv.INVOKE_TOKEN_SECRET);
vi.stubEnv('PRO_URL', originalEnv.PRO_URL);
vi.stubEnv('PRO_TOKEN', originalEnv.PRO_TOKEN);
vi.stubEnv('VITEST', originalEnv.VITEST); vi.stubEnv('VITEST', originalEnv.VITEST);
vi.stubEnv('NODE_ENV', originalEnv.NODE_ENV); vi.stubEnv('NODE_ENV', originalEnv.NODE_ENV);
vi.stubEnv('AGENT_SANDBOX_PROVIDER', originalEnv.AGENT_SANDBOX_PROVIDER); vi.stubEnv('AGENT_SANDBOX_PROVIDER', originalEnv.AGENT_SANDBOX_PROVIDER);
...@@ -113,6 +117,61 @@ describe('serviceEnv', () => { ...@@ -113,6 +117,61 @@ describe('serviceEnv', () => {
}); });
}); });
it('uses PRO_TOKEN only when configured or running tests', async () => {
vi.stubEnv('FILE_TOKEN_KEY', 'filetokenkey');
vi.stubEnv('AES256_SECRET_KEY', 'fastgptsecret');
vi.stubEnv('INVOKE_TOKEN_SECRET', validInvokeTokenSecret);
vi.stubEnv('PRO_URL', undefined);
vi.stubEnv('PRO_TOKEN', undefined);
vi.stubEnv('VITEST', undefined);
vi.stubEnv('NODE_ENV', 'production');
await expect(importServiceEnv()).resolves.toMatchObject({
serviceEnv: {
PRO_TOKEN: undefined
}
});
vi.stubEnv('VITEST', 'true');
await expect(importServiceEnv()).resolves.toMatchObject({
serviceEnv: {
PRO_TOKEN: 'fastgpt_test_pro_token_32_chars_min'
}
});
vi.stubEnv('PRO_TOKEN', 'custom_pro_token_32_chars_minimum');
await expect(importServiceEnv()).resolves.toMatchObject({
serviceEnv: {
PRO_TOKEN: 'custom_pro_token_32_chars_minimum'
}
});
});
it('配置 PRO_URL 后必须同时配置合法 PRO_TOKEN', async () => {
vi.stubEnv('FILE_TOKEN_KEY', 'filetokenkey');
vi.stubEnv('AES256_SECRET_KEY', 'fastgptsecret');
vi.stubEnv('INVOKE_TOKEN_SECRET', validInvokeTokenSecret);
vi.stubEnv('VITEST', undefined);
vi.stubEnv('NODE_ENV', 'production');
vi.stubEnv('PRO_URL', 'https://pro.example.com');
vi.stubEnv('PRO_TOKEN', undefined);
await expect(importServiceEnv()).rejects.toThrow(
'PRO_TOKEN is required when PRO_URL is configured'
);
vi.stubEnv('PRO_TOKEN', 'short-token');
await expect(importServiceEnv()).rejects.toThrow('Invalid environment variables');
vi.stubEnv('PRO_TOKEN', 'custom_pro_token_32_chars_minimum');
await expect(importServiceEnv()).resolves.toMatchObject({
serviceEnv: {
PRO_URL: 'https://pro.example.com',
PRO_TOKEN: 'custom_pro_token_32_chars_minimum'
}
});
});
it('validates AGENT_SANDBOX_DISK_MB during service env init', async () => { it('validates AGENT_SANDBOX_DISK_MB during service env init', async () => {
vi.stubEnv('FILE_TOKEN_KEY', 'filetokenkey'); vi.stubEnv('FILE_TOKEN_KEY', 'filetokenkey');
vi.stubEnv('AES256_SECRET_KEY', 'fastgptsecret'); vi.stubEnv('AES256_SECRET_KEY', 'fastgptsecret');
......
...@@ -924,6 +924,9 @@ importers: ...@@ -924,6 +924,9 @@ importers:
'@scalar/api-reference-react': '@scalar/api-reference-react':
specifier: ^0.8.1 specifier: ^0.8.1
version: 0.8.1(axios@1.16.0)(nprogress@0.2.0)(qrcode@1.5.4)(react@18.3.1)(tailwindcss@3.4.18(tsx@4.20.6)(yaml@2.8.4))(typescript@6.0.3) version: 0.8.1(axios@1.16.0)(nprogress@0.2.0)(qrcode@1.5.4)(react@18.3.1)(tailwindcss@3.4.18(tsx@4.20.6)(yaml@2.8.4))(typescript@6.0.3)
'@t3-oss/env-core':
specifier: 'catalog:'
version: 0.13.10(typescript@6.0.3)(zod@4.1.12)
'@tanstack/react-query': '@tanstack/react-query':
specifier: 'catalog:' specifier: 'catalog:'
version: 4.36.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) version: 4.36.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
Subproject commit 120262a5c3bdf2b0a7b7d16a851ad200dfe6edcb Subproject commit b705b31d509d54457aeee463322f93b974b741d3
...@@ -8,8 +8,6 @@ DB_MAX_LINK=5 ...@@ -8,8 +8,6 @@ DB_MAX_LINK=5
SYNC_INDEX=true SYNC_INDEX=true
# ==================== 密钥 ==================== # ==================== 密钥 ====================
# 用户账号密钥
TOKEN_KEY=fastgpt
# 文件阅读时的密钥 # 文件阅读时的密钥
FILE_TOKEN_KEY= FILE_TOKEN_KEY=
# 密钥加密 key # 密钥加密 key
...@@ -19,9 +17,11 @@ INVOKE_TOKEN_SECRET=fastgpt_invoke_token_secret_32_chars_min ...@@ -19,9 +17,11 @@ INVOKE_TOKEN_SECRET=fastgpt_invoke_token_secret_32_chars_min
# root key(最高权限) # root key(最高权限)
ROOT_KEY=fdafasd ROOT_KEY=fdafasd
# ==================== 服务地址与集成 ==================== # ==================== 服务地址与集成 ====================
# 商业版地址 # 商业版地址
# PRO_URL= # PRO_URL=
# PRO_TOKEN=
# 插件服务 # 插件服务
PLUGIN_BASE_URL=http://localhost:3004 PLUGIN_BASE_URL=http://localhost:3004
......
...@@ -3,6 +3,7 @@ import { jsonRes } from '@fastgpt/service/common/response'; ...@@ -3,6 +3,7 @@ import { jsonRes } from '@fastgpt/service/common/response';
import { FastGPTProUrl } from '@fastgpt/service/common/system/constants'; import { FastGPTProUrl } from '@fastgpt/service/common/system/constants';
import { buildSameOriginUrl } from '@fastgpt/service/common/security/network'; import { buildSameOriginUrl } from '@fastgpt/service/common/security/network';
import { Readable } from 'stream'; import { Readable } from 'stream';
import { FASTGPT_PRO_TOKEN_HEADER } from '@fastgpt/global/common/system/constants';
export default async function handler(req: NextApiRequest, res: NextApiResponse) { export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try { try {
...@@ -21,7 +22,14 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) ...@@ -21,7 +22,14 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
const headers: Record<string, string> = {}; const headers: Record<string, string> = {};
for (const [key, value] of Object.entries(req.headers)) { for (const [key, value] of Object.entries(req.headers)) {
if (key === 'rootkey' || key === 'host' || key === 'connection') continue; if (
key === 'rootkey' ||
key === FASTGPT_PRO_TOKEN_HEADER ||
key === 'host' ||
key === 'connection'
) {
continue;
}
if (value) { if (value) {
headers[key] = Array.isArray(value) ? value.join(', ') : value; headers[key] = Array.isArray(value) ? value.join(', ') : value;
} }
......
...@@ -39,9 +39,9 @@ export const instrumentationCheck = async () => { ...@@ -39,9 +39,9 @@ export const instrumentationCheck = async () => {
// pro // pro
if (global.feConfigs?.isPlus) { if (global.feConfigs?.isPlus) {
try { try {
const data = await POST<{ auth: boolean; data: string }>('/admin/common/health'); const data = await POST<{ auth: boolean; data: string }>('/health');
if (!data.auth) { if (!data.auth) {
throw new Error('Root key is invalid'); throw new Error('PRO_TOKEN is invalid');
} }
} catch (error) { } catch (error) {
const message = `[${InitialErrorEnum.PRO_ERROR}]: ${getErrText(error)}`; const message = `[${InitialErrorEnum.PRO_ERROR}]: ${getErrText(error)}`;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment