diff --git a/web/src/components/common/modals/RiskAcknowledgementModal.jsx b/web/src/components/common/modals/RiskAcknowledgementModal.jsx index 63806ad73..54aa62eaa 100644 --- a/web/src/components/common/modals/RiskAcknowledgementModal.jsx +++ b/web/src/components/common/modals/RiskAcknowledgementModal.jsx @@ -44,8 +44,6 @@ const RiskMarkdownBlock = React.memo(function RiskMarkdownBlock({ className='rounded-lg' style={{ border: '1px solid var(--semi-color-warning-light-hover)', - background: - 'linear-gradient(180deg, var(--semi-color-warning-light-default) 0%, var(--semi-color-fill-0) 100%)', padding: '12px', contentVisibility: 'auto', }} @@ -136,15 +134,6 @@ const RiskAcknowledgementModal = React.memo(function RiskAcknowledgementModal({ } >
-
-
diff --git a/web/src/components/table/channels/modals/EditChannelModal.jsx b/web/src/components/table/channels/modals/EditChannelModal.jsx index 2935006dd..621df3012 100644 --- a/web/src/components/table/channels/modals/EditChannelModal.jsx +++ b/web/src/components/table/channels/modals/EditChannelModal.jsx @@ -65,7 +65,10 @@ import StatusCodeRiskGuardModal from './StatusCodeRiskGuardModal'; import ChannelKeyDisplay from '../../../common/ui/ChannelKeyDisplay'; import { useSecureVerification } from '../../../../hooks/common/useSecureVerification'; import { createApiCalls } from '../../../../services/secureVerification'; -import { collectNewDisallowedStatusCodeRedirects } from './statusCodeRiskGuard'; +import { + collectInvalidStatusCodeEntries, + collectNewDisallowedStatusCodeRedirects, +} from './statusCodeRiskGuard'; import { IconSave, IconClose, @@ -1377,6 +1380,16 @@ const EditChannelModal = (props) => { } } + const invalidStatusCodeEntries = collectInvalidStatusCodeEntries( + localInputs.status_code_mapping, + ); + if (invalidStatusCodeEntries.length > 0) { + showError( + `${t('状态码复写包含无效的状态码')}: ${invalidStatusCodeEntries.join(', ')}`, + ); + return; + } + const riskyStatusCodeRedirects = collectNewDisallowedStatusCodeRedirects( initialStatusCodeMappingRef.current, localInputs.status_code_mapping, diff --git a/web/src/components/table/channels/modals/statusCodeRiskGuard.js b/web/src/components/table/channels/modals/statusCodeRiskGuard.js index 7ea983f86..169736baa 100644 --- a/web/src/components/table/channels/modals/statusCodeRiskGuard.js +++ b/web/src/components/table/channels/modals/statusCodeRiskGuard.js @@ -44,6 +44,37 @@ function parseStatusCodeMappingTarget(rawValue) { return null; } +export function collectInvalidStatusCodeEntries(statusCodeMappingStr) { + if ( + typeof statusCodeMappingStr !== 'string' || + statusCodeMappingStr.trim() === '' + ) { + return []; + } + + let parsed; + try { + parsed = JSON.parse(statusCodeMappingStr); + } catch { + return []; + } + + if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) { + return []; + } + + const invalid = []; + for (const [rawKey, rawValue] of Object.entries(parsed)) { + const fromCode = parseStatusCodeKey(rawKey); + const toCode = parseStatusCodeMappingTarget(rawValue); + if (fromCode === null || toCode === null) { + invalid.push(`${rawKey} → ${rawValue}`); + } + } + + return invalid; +} + export function collectDisallowedStatusCodeRedirects(statusCodeMappingStr) { if ( typeof statusCodeMappingStr !== 'string' || diff --git a/web/src/i18n/locales/en.json b/web/src/i18n/locales/en.json index b5ab87353..4c37baa76 100644 --- a/web/src/i18n/locales/en.json +++ b/web/src/i18n/locales/en.json @@ -1638,6 +1638,7 @@ "版权所有": "All rights reserved", "状态": "Status", "状态码复写": "Status Code Override", + "状态码复写包含无效的状态码": "Status code override contains invalid status codes", "状态筛选": "Status filter", "状态页面Slug": "Status Page Slug", "环境变量": "Environment Variables", @@ -1954,7 +1955,7 @@ "检测到以下高危状态码重定向规则": "Detected high-risk status-code redirect rules", "操作确认": "Operation confirmation", "我确认开启高危重试": "I confirm enabling high-risk retry", - "高危状态码重试风险告知与免责声明Markdown": "### ⚠️ High-Risk Operation: Risk Notice and Disclaimer for 504/524 Retry\n\n[Background]\nBy default, this project does not retry for status codes `400` (bad request), `504` (gateway timeout), and `524` (timeout occurred). In many cases, `504` and `524` mean the request has reached the upstream AI service and processing has started, but the connection was closed due to long processing time.\n\nEnabling redirection/retry for these timeout status codes is a **high-risk operation**. Before enabling it, you must read and understand the consequences below:\n\n#### 1. Core Risks (Read Carefully)\n1. 💸 Duplicate/multiple billing risk: Most upstream AI providers **still charge** for requests that started processing but got interrupted by network timeout (`504`/`524`). If retry is triggered, a new upstream request will be sent, which can lead to **duplicate or multiple charges**.\n2. ⏳ Severe client timeout: If a single request already timed out, adding retries can multiply total latency and cause severe or unacceptable timeout behavior for your final client/caller.\n3. 💥 Request backlog and system crash risk: Forcing retries on timeout requests keeps threads and connections occupied for longer. Under high concurrency, this can cause serious backlog, exhaust system resources, trigger a cascading failure, and crash your proxy service.\n\n#### 2. Risk Acknowledgement\nIf you still choose to enable this feature, you acknowledge all of the following:\n\n- [ ] I have fully read and understood the risks and fully understand the destructive consequences of forcing retries for status codes `504` and `524`.\n- [ ] I have communicated with the upstream provider and confirmed that the timeout issue is an upstream bottleneck and cannot be resolved upstream at this time.\n- [ ] I voluntarily accept all duplicate/multiple billing risks and will not file issues or complaints in this project repository regarding billing anomalies caused by this retry behavior.\n- [ ] I voluntarily accept system stability risks, including severe client timeout and possible service crash. Any consequences caused by enabling this feature are my own responsibility.\n\n> **[Operation Confirmation]**\n> To unlock this feature, manually type the text below in the input box:\n> I understand the duplicate billing and crash risks, and confirm enabling it.", + "高危状态码重试风险告知与免责声明Markdown": "### ⚠️ High-Risk Operation: Risk Notice and Disclaimer for 504/524 Retry\nBy default, this project does not retry for status codes `400` (bad request), `504` (gateway timeout), and `524` (timeout occurred).\n In many cases, 504 and 524 mean the request has reached the upstream AI service and processing has started, but the connection was closed due to long processing time.\n\nEnabling redirection/retry for these timeout status codes is a **high-risk operation**. Before enabling it, you must read and understand the consequences below:\n\n#### 1. Core Risks (Read Carefully)\n1. 💸 Duplicate/multiple billing risk: Most upstream AI providers **still charge** for requests that started processing but got interrupted by network timeout (504/524). If retry is triggered, a new upstream request will be sent, which can lead to **duplicate or multiple charges**.\n2. ⏳ Severe client timeout: If a single request already timed out, adding retries can multiply total latency and cause severe or unacceptable timeout behavior for your final client/caller.\n3. 💥 Request backlog and system crash risk: Forcing retries on timeout requests keeps threads and connections occupied for longer. Under high concurrency, this can cause serious backlog, exhaust system resources, trigger a cascading failure, and crash your proxy service.\n\n#### 2. Risk Acknowledgement\nIf you still choose to enable this feature, you acknowledge all of the following:", "高危状态码重试风险确认输入文本": "I understand the duplicate billing and crash risks, and confirm enabling it.", "高危状态码重试风险确认项1": "I have fully read and understood the risks and fully understand the destructive consequences of forcing retries for status codes 504 and 524.", "高危状态码重试风险确认项2": "I have communicated with the upstream provider and confirmed that the timeout issue is an upstream bottleneck and cannot be resolved upstream at this time.", diff --git a/web/src/i18n/locales/fr.json b/web/src/i18n/locales/fr.json index 702a61dee..4fdca8b86 100644 --- a/web/src/i18n/locales/fr.json +++ b/web/src/i18n/locales/fr.json @@ -1650,6 +1650,7 @@ "版权所有": "Tous droits réservés", "状态": "Statut", "状态码复写": "Remplacement du code d'état", + "状态码复写包含无效的状态码": "Le remplacement du code d'état contient des codes d'état invalides", "状态筛选": "Filtre d'état", "状态页面Slug": "Slug de la page d'état", "环境变量": "Environment Variables", diff --git a/web/src/i18n/locales/ja.json b/web/src/i18n/locales/ja.json index d1e770e9e..d9f739a5d 100644 --- a/web/src/i18n/locales/ja.json +++ b/web/src/i18n/locales/ja.json @@ -1635,6 +1635,7 @@ "版权所有": "All rights reserved", "状态": "ステータス", "状态码复写": "ステータスコードの上書き", + "状态码复写包含无效的状态码": "ステータスコードの上書きに無効なステータスコードが含まれています", "状态筛选": "ステータスフィルター", "状态页面Slug": "ステータスページスラッグ", "环境变量": "Environment Variables", diff --git a/web/src/i18n/locales/ru.json b/web/src/i18n/locales/ru.json index e2a529041..cb2dec1c6 100644 --- a/web/src/i18n/locales/ru.json +++ b/web/src/i18n/locales/ru.json @@ -1661,6 +1661,7 @@ "版权所有": "Все права защищены", "状态": "Статус", "状态码复写": "Перезапись кода состояния", + "状态码复写包含无效的状态码": "Перезапись кода состояния содержит недопустимые коды состояния", "状态筛选": "Фильтр по статусу", "状态页面Slug": "Slug страницы статуса", "环境变量": "Environment Variables", diff --git a/web/src/i18n/locales/vi.json b/web/src/i18n/locales/vi.json index a311ca9ec..4b81fac6e 100644 --- a/web/src/i18n/locales/vi.json +++ b/web/src/i18n/locales/vi.json @@ -1782,6 +1782,7 @@ "状态": "Trạng thái", "状态更新时间": "Thời gian cập nhật trạng thái", "状态码复写": "Ghi đè mã trạng thái", + "状态码复写包含无效的状态码": "Ghi đè mã trạng thái chứa mã trạng thái không hợp lệ", "状态筛选": "Lọc trạng thái", "状态页面Slug": "Slug trang trạng thái", "环境变量": "Environment Variables", diff --git a/web/src/i18n/locales/zh-CN.json b/web/src/i18n/locales/zh-CN.json index e99441050..a41db52d0 100644 --- a/web/src/i18n/locales/zh-CN.json +++ b/web/src/i18n/locales/zh-CN.json @@ -1628,6 +1628,7 @@ "版权所有": "版权所有", "状态": "状态", "状态码复写": "状态码复写", + "状态码复写包含无效的状态码": "状态码复写包含无效的状态码", "状态筛选": "状态筛选", "状态页面Slug": "状态页面Slug", "环境变量": "环境变量", @@ -1941,7 +1942,7 @@ "检测到以下高危状态码重定向规则": "检测到以下高危状态码重定向规则", "操作确认": "操作确认", "我确认开启高危重试": "我确认开启高危重试", - "高危状态码重试风险告知与免责声明Markdown": "### ⚠️ 高危操作:504/524 状态码重试风险告知与免责声明\n\n【背景提示】\n本项目默认对 `400`(请求错误)、`504`(网关超时)和 `524`(发生超时)状态码不进行重试。504 和 524 错误通常意味着**请求已成功送达上游 AI 服务,且上游正在处理,但因处理时间过长导致连接断开**。\n\n开启对此类超时状态码的重定向/重试属于**极高风险操作**。作为本开源项目的使用者,在开启该功能前,您必须仔细阅读并知悉以下严重后果:\n\n#### 一、 核心风险告知(请仔细阅读)\n1. 💸 双重/多重计费风险: 绝大多数 AI 上游厂商对于已经开始处理但因网络原因中断(504/524)的请求**依然会进行扣费**。此时若触发重试,将会向上游发起全新请求,导致您被**双重甚至多重计费**。\n2. ⏳ 客户端严重超时: 单次请求已经触发超时,叠加重试机制将会使总请求耗时成倍增加,导致您的最终客户端(或调用方)出现严重甚至完全无法接受的超时现象。\n3. 💥 请求积压与系统崩溃风险: 强制重试超时请求会长时间占用系统线程和连接数。在高并发场景下,这会导致严重的**请求积压**,进而耗尽系统资源,引发雪崩效应,导致您的整个代理服务崩溃。\n\n#### 二、 风险确认声明\n如果您坚持开启该功能,即代表您作出以下确认:\n\n- [ ] 我已充分阅读并理解**:本人已完整阅读上述全部风险提示,完全理解强制重试 `504` 和 `524` 状态码可能带来的破坏性后果。\n- [ ] **我已与上游沟通并确认**:本人确认,当前出现的超时问题属于上游服务的瓶颈。**本人已与上游提供商进行过沟通,确认上游无法解决该超时问题**,因此才采取强制重试方案作为妥协手段。\n- [ ] **我自愿承担计费损失**:本人知晓并接受由此产生的全部双重/多重计费风险,**承诺不会因重试导致的账单异常在本项目仓库中提交 Issue 或抱怨**。\n- [ ] **我自愿承担系统稳定性风险**:本人知晓该操作可能导致客户端严重超时及服务崩溃。若因本人开启此功能导致请求积压或服务不可用,后果由本人自行承担。\n\n> **【操作确认】\n> 为确认您已清晰了解上述风险,请在下方输入框内手动输入以下文字以解锁功能:\n> 我已了解多重计费与崩溃风险,确认开启", + "高危状态码重试风险告知与免责声明Markdown": "### ⚠️ 高危操作:504/524 状态码重试风险告知与免责声明\n本项目默认对 `400 (请求错误)`、`504 (网关超时)`和 `524 (cdn发生超时)`状态码不进行重试。\n504 和 524 错误通常意味着**请求已成功送达上游 AI 服务,且上游正在处理,但因处理时间过长导致连接断开**。\n\n开启对此类超时状态码的重定向/重试属于**极高风险操作**。作为本开源项目的使用者,在开启该功能前,您必须仔细阅读并知悉以下严重后果:\n\n#### 一、 核心风险告知(请仔细阅读)\n1. 💸 双重/多重计费风险: 绝大多数 AI 上游厂商对于已经开始处理但因网络原因中断(504/524)的请求**依然会进行扣费**。此时若触发重试,将会向上游发起全新请求,导致您被**双重甚至多重计费**。\n2. ⏳ 客户端严重超时: 单次请求已经触发超时,叠加重试机制将会使总请求耗时成倍增加,导致您的最终客户端(或调用方)出现严重甚至完全无法接受的超时现象。\n3. 💥 请求积压与系统崩溃风险: 强制重试超时请求会长时间占用系统线程和连接数。在高并发场景下,这会导致严重的**请求积压**,进而耗尽系统资源,引发雪崩效应,导致您的整个代理服务崩溃。\n\n#### 二、 风险确认声明\n如果您坚持开启该功能,即代表您作出以下确认:", "高危状态码重试风险确认输入文本": "我已了解多重计费与崩溃风险,确认开启", "高危状态码重试风险确认项1": "我已充分阅读并理解:本人已完整阅读上述全部风险提示,完全理解强制重试 504 和 524 状态码可能带来的破坏性后果。", "高危状态码重试风险确认项2": "我已与上游沟通并确认:本人确认,当前出现的超时问题属于上游服务的瓶颈。本人已与上游提供商进行过沟通,确认上游无法解决该超时问题,因此才采取强制重试方案作为妥协手段。", diff --git a/web/src/i18n/locales/zh-TW.json b/web/src/i18n/locales/zh-TW.json index 635255c79..7ca22d2a6 100644 --- a/web/src/i18n/locales/zh-TW.json +++ b/web/src/i18n/locales/zh-TW.json @@ -1632,6 +1632,7 @@ "版权所有": "版權所有", "状态": "狀態", "状态码复写": "狀態碼複寫", + "状态码复写包含无效的状态码": "狀態碼複寫包含無效的狀態碼", "状态筛选": "狀態篩選", "状态页面Slug": "狀態頁面Slug", "环境变量": "環境變數", @@ -1947,7 +1948,7 @@ "检测到以下高危状态码重定向规则": "檢測到以下高風險狀態碼重定向規則", "操作确认": "操作確認", "我确认开启高危重试": "我確認開啟高風險重試", - "高危状态码重试风险告知与免责声明Markdown": "### ⚠️ 高風險操作:504/524 狀態碼重試風險告知與免責聲明\n\n【背景提示】\n本專案預設對 `400`(請求錯誤)、`504`(閘道逾時)與 `524`(發生逾時)狀態碼不進行重試。504 與 524 錯誤通常代表**請求已成功送達上游 AI 服務,且上游正在處理,但因處理時間過長導致連線中斷**。\n\n開啟此類逾時狀態碼的重定向/重試屬於**極高風險操作**。作為本開源專案使用者,在開啟該功能前,您必須仔細閱讀並知悉以下嚴重後果:\n\n#### 一、 核心風險告知(請仔細閱讀)\n1. 💸 雙重/多重計費風險:多數 AI 上游廠商對於已開始處理但因網路原因中斷(504/524)的請求**仍然會扣費**。此時若觸發重試,將會向上游發起全新請求,導致您被**雙重甚至多重計費**。\n2. ⏳ 用戶端嚴重逾時:單次請求已觸發逾時,疊加重試機制會使總請求耗時成倍增加,導致最終用戶端(或呼叫方)出現嚴重甚至無法接受的逾時現象。\n3. 💥 請求積壓與系統崩潰風險:強制重試逾時請求會長時間占用系統執行緒與連線數。在高併發場景下,這將導致嚴重**請求積壓**,進而耗盡系統資源,引發雪崩效應,造成整個代理服務崩潰。\n\n#### 二、 風險確認聲明\n若您堅持開啟該功能,即代表您作出以下確認:\n\n- [ ] 我已充分閱讀並理解:本人已完整閱讀上述全部風險提示,完全理解強制重試 `504` 與 `524` 狀態碼可能帶來的破壞性後果。\n- [ ] 我已與上游溝通並確認:本人確認,當前逾時問題屬於上游服務瓶頸。本人已與上游供應商溝通,確認上游無法解決該逾時問題,因此才採取強制重試方案作為妥協手段。\n- [ ] 我自願承擔計費損失:本人知悉並接受由此產生的全部雙重/多重計費風險,承諾不會因重試導致的帳單異常在本專案倉庫提交 Issue 或抱怨。\n- [ ] 我自願承擔系統穩定性風險:本人知悉該操作可能導致用戶端嚴重逾時及服務崩潰。若因本人開啟此功能導致請求積壓或服務不可用,後果由本人自行承擔。\n\n> **【操作確認】**\n> 為確認您已清楚了解上述風險,請在下方輸入框內手動輸入以下文字以解鎖功能:\n> 我已了解多重計費與崩潰風險,確認開啟", + "高危状态码重试风险告知与免责声明Markdown": "### ⚠️ 高風險操作:504/524 狀態碼重試風險告知與免責聲明\n\n【背景提示】\n本專案預設對 `400`(請求錯誤)、`504`(閘道逾時)與 `524`(發生逾時)狀態碼不進行重試。504 與 524 錯誤通常代表**請求已成功送達上游 AI 服務,且上游正在處理,但因處理時間過長導致連線中斷**。\n\n開啟此類逾時狀態碼的重定向/重試屬於**極高風險操作**。作為本開源專案使用者,在開啟該功能前,您必須仔細閱讀並知悉以下嚴重後果:\n\n#### 一、 核心風險告知(請仔細閱讀)\n1. 💸 雙重/多重計費風險:多數 AI 上游廠商對於已開始處理但因網路原因中斷(504/524)的請求**仍然會扣費**。此時若觸發重試,將會向上游發起全新請求,導致您被**雙重甚至多重計費**。\n2. ⏳ 用戶端嚴重逾時:單次請求已觸發逾時,疊加重試機制會使總請求耗時成倍增加,導致最終用戶端(或呼叫方)出現嚴重甚至無法接受的逾時現象。\n3. 💥 請求積壓與系統崩潰風險:強制重試逾時請求會長時間占用系統執行緒與連線數。在高併發場景下,這將導致嚴重**請求積壓**,進而耗盡系統資源,引發雪崩效應,造成整個代理服務崩潰。\n\n#### 二、 風險確認聲明\n若您堅持開啟該功能,即代表您作出以下確認:", "高危状态码重试风险确认输入文本": "我已了解多重計費與崩潰風險,確認開啟", "高危状态码重试风险确认项1": "我已充分閱讀並理解:本人已完整閱讀上述全部風險提示,完全理解強制重試 504 與 524 狀態碼可能帶來的破壞性後果。", "高危状态码重试风险确认项2": "我已與上游溝通並確認:本人確認,當前逾時問題屬於上游服務瓶頸。本人已與上游供應商溝通,確認上游無法解決該逾時問題,因此才採取強制重試方案作為妥協手段。",