Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,12 @@ STORE_SESSION_RESPONSE_BODY=true # 是否在 Redis 中存储会话响应
# - 启用:适用于网络稳定环境,连续网络错误也应触发熔断保护,避免持续请求不可达的供应商
ENABLE_CIRCUIT_BREAKER_ON_NETWORK_ERRORS=false

# 端点级别熔断器
# 功能说明:控制是否启用端点级别的熔断器
# - false (默认):禁用端点熔断器,所有启用的端点均可使用
# - true:启用端点熔断器,连续失败的端点会被临时屏蔽(默认 3 次失败后熔断 5 分钟)
ENABLE_ENDPOINT_CIRCUIT_BREAKER=false

# 供应商缓存配置
# 功能说明:控制是否启用供应商进程级缓存
# - true (默认):启用缓存,30s TTL + Redis Pub/Sub 跨实例即时失效,提升供应商查询性能
Expand Down
2 changes: 1 addition & 1 deletion biome.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"$schema": "https://biomejs.dev/schemas/2.3.10/schema.json",
"$schema": "https://biomejs.dev/schemas/2.3.14/schema.json",
"vcs": {
"enabled": true,
"clientKind": "git",
Expand Down
3 changes: 2 additions & 1 deletion messages/en/dashboard.json
Original file line number Diff line number Diff line change
Expand Up @@ -994,7 +994,8 @@
"expiresAt": {
"label": "Expiration Date",
"placeholder": "Leave empty for never expires",
"description": "User will be automatically disabled after expiration"
"description": "User will be automatically disabled after expiration",
"pastWarning": "Selected date is in the past. The user will expire and be disabled immediately after saving."
},
"allowedClients": {
"label": "Allowed Clients",
Expand Down
16 changes: 13 additions & 3 deletions messages/en/provider-chain.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@
"concurrentLimit": "Concurrent Limit",
"http2Fallback": "HTTP/2 Fallback",
"clientError": "Client Error",
"endpointPoolExhausted": "Endpoint Pool Exhausted"
"endpointPoolExhausted": "Endpoint Pool Exhausted",
"vendorTypeAllTimeout": "Vendor-Type All Endpoints Timeout"
},
"reasons": {
"request_success": "Success",
Expand All @@ -50,7 +51,8 @@
"http2_fallback": "HTTP/2 Fallback",
"session_reuse": "Session Reuse",
"initial_selection": "Initial Selection",
"endpoint_pool_exhausted": "Endpoint Pool Exhausted"
"endpoint_pool_exhausted": "Endpoint Pool Exhausted",
"vendor_type_all_timeout": "Vendor-Type All Endpoints Timeout"
},
"filterReasons": {
"rate_limited": "Rate Limited",
Expand All @@ -67,6 +69,12 @@
"endpoint_circuit_open": "Endpoint Circuit Open",
"endpoint_disabled": "Endpoint Disabled"
},
"filterDetails": {
"vendor_type_circuit_open": "Vendor-type temporarily circuit-broken",
"circuit_open": "Circuit breaker open",
"circuit_half_open": "Circuit breaker half-open",
"rate_limited": "Rate limited"
},
"details": {
"selectionMethod": "Selection",
"attemptNumber": "Attempt",
Expand Down Expand Up @@ -197,6 +205,8 @@
"endpointStatsCircuitOpen": "Circuit-Open Endpoints: {count}",
"endpointStatsAvailable": "Available Endpoints: {count}",
"strictBlockNoEndpoints": "Strict mode: no endpoint candidates available, provider skipped without fallback",
"strictBlockSelectorError": "Strict mode: endpoint selector encountered an error, provider skipped without fallback"
"strictBlockSelectorError": "Strict mode: endpoint selector encountered an error, provider skipped without fallback",
"vendorTypeAllTimeout": "Vendor-Type All Endpoints Timeout (524)",
"vendorTypeAllTimeoutNote": "All endpoints for this vendor-type timed out. Vendor-type circuit breaker triggered."
}
}
8 changes: 7 additions & 1 deletion messages/en/settings/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,13 @@
"leasePercentMonthly": "Monthly Window Lease Percentage",
"leasePercentMonthlyDesc": "Percentage of monthly limit for each lease slice (0-1)",
"leaseCapUsd": "Lease Cap (USD)",
"leaseCapUsdDesc": "Maximum absolute cap per lease slice in USD, leave empty for no limit"
"leaseCapUsdDesc": "Maximum absolute cap per lease slice in USD, leave empty for no limit",
"warnings": {
"dbRefreshIntervalTooLow": "Refresh interval is {value}s. This may increase DB load.",
"dbRefreshIntervalTooHigh": "Refresh interval is {value}s. Quota/limit updates may be delayed.",
"leasePercentZero": "Percentage is 0. This may cause the lease budget to always be 0.",
"leaseCapZero": "Lease cap is 0. This may cause the per-lease budget to be 0."
}
}
},
"section": {
Expand Down
9 changes: 8 additions & 1 deletion messages/en/settings/providers/form/key.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,12 @@
"labelEdit": "API Key (Leave empty to keep unchanged)",
"leaveEmpty": "(Leave empty to keep unchanged)",
"leaveEmptyDesc": "Leave empty to keep existing key",
"placeholder": "Enter API Key"
"placeholder": "Enter API Key",
"warnings": {
"looks_like_auth_header": "Looks like you pasted a request header (e.g., Bearer/Authorization/x-api-key). Please enter the key value only.",
"wrapped_in_quotes": "Wrapped in quotes. Usually the quotes are not needed.",
"contains_non_ascii": "Contains non-ASCII characters. This is uncommon for API keys.",
"contains_whitespace": "Contains whitespace (spaces/newlines). This is uncommon for API keys.",
"contains_uncommon_ascii": "Contains uncommon symbols. This is uncommon for API keys."
}
}
3 changes: 2 additions & 1 deletion messages/ja/dashboard.json
Original file line number Diff line number Diff line change
Expand Up @@ -981,7 +981,8 @@
"expiresAt": {
"label": "有効期限",
"placeholder": "空白の場合は無期限",
"description": "有効期限切れ後、ユーザーは自動的に無効化されます"
"description": "有効期限切れ後、ユーザーは自動的に無効化されます",
"pastWarning": "選択した日付は過去です。保存するとユーザーは直ちに期限切れとなり無効化されます。"
},
"allowedClients": {
"label": "許可されたクライアント",
Expand Down
16 changes: 13 additions & 3 deletions messages/ja/provider-chain.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@
"concurrentLimit": "同時実行制限",
"http2Fallback": "HTTP/2 フォールバック",
"clientError": "クライアントエラー",
"endpointPoolExhausted": "エンドポイントプール枯渇"
"endpointPoolExhausted": "エンドポイントプール枯渇",
"vendorTypeAllTimeout": "ベンダータイプ全エンドポイントタイムアウト"
},
"reasons": {
"request_success": "成功",
Expand All @@ -50,7 +51,8 @@
"http2_fallback": "HTTP/2 フォールバック",
"session_reuse": "セッション再利用",
"initial_selection": "初期選択",
"endpoint_pool_exhausted": "エンドポイントプール枯渇"
"endpoint_pool_exhausted": "エンドポイントプール枯渇",
"vendor_type_all_timeout": "ベンダータイプ全エンドポイントタイムアウト"
},
"filterReasons": {
"rate_limited": "レート制限",
Expand All @@ -67,6 +69,12 @@
"endpoint_circuit_open": "エンドポイントサーキットオープン",
"endpoint_disabled": "エンドポイント無効"
},
"filterDetails": {
"vendor_type_circuit_open": "ベンダータイプ一時サーキットブレイク",
"circuit_open": "サーキットブレーカーオープン",
"circuit_half_open": "サーキットブレーカーハーフオープン",
"rate_limited": "レート制限"
},
"details": {
"selectionMethod": "選択方法",
"attemptNumber": "試行回数",
Expand Down Expand Up @@ -197,6 +205,8 @@
"endpointStatsCircuitOpen": "サーキットオープンのエンドポイント: {count}",
"endpointStatsAvailable": "利用可能なエンドポイント: {count}",
"strictBlockNoEndpoints": "厳格モード:利用可能なエンドポイント候補がないため、フォールバックなしでプロバイダーをスキップ",
"strictBlockSelectorError": "厳格モード:エンドポイントセレクターでエラーが発生したため、フォールバックなしでプロバイダーをスキップ"
"strictBlockSelectorError": "厳格モード:エンドポイントセレクターでエラーが発生したため、フォールバックなしでプロバイダーをスキップ",
"vendorTypeAllTimeout": "ベンダータイプ全エンドポイントタイムアウト(524)",
"vendorTypeAllTimeoutNote": "このベンダータイプの全エンドポイントがタイムアウトしました。ベンダータイプサーキットブレーカーが発動しました。"
}
}
8 changes: 7 additions & 1 deletion messages/ja/settings/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,13 @@
"leasePercentMonthly": "月次ウィンドウリース比率",
"leasePercentMonthlyDesc": "各リーススライスの月次制限に対する比率(0-1)",
"leaseCapUsd": "リース上限(USD)",
"leaseCapUsdDesc": "リーススライスごとの絶対上限(米ドル)、空の場合は無制限"
"leaseCapUsdDesc": "リーススライスごとの絶対上限(米ドル)、空の場合は無制限",
"warnings": {
"dbRefreshIntervalTooLow": "更新間隔が {value}s です。DB 負荷が増える可能性があります。",
"dbRefreshIntervalTooHigh": "更新間隔が {value}s です。クォータ/制限の反映が遅れる可能性があります。",
"leasePercentZero": "比率が 0 です。リース予算が常に 0 になる可能性があります。",
"leaseCapZero": "上限が 0 です。リースごとの予算が 0 になる可能性があります。"
}
}
},
"section": {
Expand Down
9 changes: 8 additions & 1 deletion messages/ja/settings/providers/form/key.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,12 @@
"labelEdit": "API キー(空欄のままにすると変更しません)",
"leaveEmpty": "(空欄のままにすると変更しません)",
"leaveEmptyDesc": "空欄のままにすると既存のキーを保持します",
"placeholder": "API キーを入力"
"placeholder": "API キーを入力",
"warnings": {
"looks_like_auth_header": "リクエストヘッダー(例: Bearer/Authorization/x-api-key)を貼り付けたようです。キー本体のみを入力してください。",
"wrapped_in_quotes": "前後が引用符で囲まれています。通常、引用符は不要です。",
"contains_non_ascii": "非 ASCII 文字を含んでいます。API キーとしては一般的ではありません。",
"contains_whitespace": "空白文字(スペース/改行)を含んでいます。API キーとしては一般的ではありません。",
"contains_uncommon_ascii": "一般的でない記号を含んでいます。API キーとしては一般的ではありません。"
}
}
3 changes: 2 additions & 1 deletion messages/ru/dashboard.json
Original file line number Diff line number Diff line change
Expand Up @@ -983,7 +983,8 @@
"expiresAt": {
"label": "Срок действия",
"placeholder": "Оставьте пустым для бессрочного",
"description": "Пользователь будет автоматически отключен после истечения срока"
"description": "Пользователь будет автоматически отключен после истечения срока",
"pastWarning": "Выбранная дата в прошлом. После сохранения пользователь сразу станет просроченным и будет отключен."
},
"allowedClients": {
"label": "Разрешённые клиенты",
Expand Down
30 changes: 20 additions & 10 deletions messages/ru/provider-chain.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@
"concurrentLimit": "Лимит параллельных запросов",
"http2Fallback": "Откат HTTP/2",
"clientError": "Ошибка клиента",
"endpointPoolExhausted": "Пул конечная точкаов исчерпан"
"endpointPoolExhausted": "Пул конечных точек исчерпан",
"vendorTypeAllTimeout": "Тайм-аут всех конечных точек"
},
"reasons": {
"request_success": "Успешно",
Expand All @@ -50,7 +51,8 @@
"http2_fallback": "Откат HTTP/2",
"session_reuse": "Повторное использование сессии",
"initial_selection": "Первоначальный выбор",
"endpoint_pool_exhausted": "Пул конечная точкаов исчерпан"
"endpoint_pool_exhausted": "Пул конечных точек исчерпан",
"vendor_type_all_timeout": "Тайм-аут всех конечных точек типа поставщика"
},
"filterReasons": {
"rate_limited": "Ограничение скорости",
Expand All @@ -64,9 +66,15 @@
"model_not_supported": "Модель не поддерживается",
"group_mismatch": "Несоответствие группы",
"health_check_failed": "Проверка состояния не пройдена",
"endpoint_circuit_open": "Автомат конечная точкаа открыт",
"endpoint_circuit_open": "Автомат конечной точки открыт",
"endpoint_disabled": "Эндпоинт отключен"
},
"filterDetails": {
"vendor_type_circuit_open": "Временное размыкание типа поставщика",
"circuit_open": "Размыкатель открыт",
"circuit_half_open": "Размыкатель полуоткрыт",
"rate_limited": "Ограничение скорости"
},
"details": {
"selectionMethod": "Метод выбора",
"attemptNumber": "Номер попытки",
Expand Down Expand Up @@ -190,13 +198,15 @@
"ruleDescription": "Описание: {description}",
"ruleHasOverride": "Переопределения: response={response}, statusCode={statusCode}",
"clientErrorNote": "Эта ошибка вызвана вводом клиента, не повторяется и не учитывается в автомате защиты.",
"endpointPoolExhausted": "Пул конечная точкаов исчерпан (все конечная точкаы недоступны)",
"endpointStats": "Статистика фильтрации конечная точкаов",
"endpointStatsTotal": "Всего конечная точкаов: {count}",
"endpointStatsEnabled": "Включено конечная точкаов: {count}",
"endpointPoolExhausted": "Пул конечных точек исчерпан (все конечные точки недоступны)",
"endpointStats": "Статистика фильтрации конечных точек",
"endpointStatsTotal": "Всего конечных точек: {count}",
"endpointStatsEnabled": "Включено конечных точек: {count}",
"endpointStatsCircuitOpen": "Эндпоинтов с открытым автоматом: {count}",
"endpointStatsAvailable": "Доступных конечная точкаов: {count}",
"strictBlockNoEndpoints": "Строгий режим: нет доступных кандидатов конечная точкаов, провайдер пропущен без отката",
"strictBlockSelectorError": "Строгий режим: ошибка селектора конечная точкаов, провайдер пропущен без отката"
"endpointStatsAvailable": "Доступных конечных точек: {count}",
"strictBlockNoEndpoints": "Строгий режим: нет доступных кандидатов конечных точек, провайдер пропущен без отката",
"strictBlockSelectorError": "Строгий режим: ошибка селектора конечных точек, провайдер пропущен без отката",
"vendorTypeAllTimeout": "Тайм-аут всех конечных точек типа поставщика (524)",
"vendorTypeAllTimeoutNote": "Все конечные точки этого типа поставщика превысили тайм-аут. Активирован размыкатель типа поставщика."
}
}
8 changes: 7 additions & 1 deletion messages/ru/settings/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,13 @@
"leasePercentMonthly": "Процент аренды месячного окна",
"leasePercentMonthlyDesc": "Процент месячного лимита для каждого среза аренды (0-1)",
"leaseCapUsd": "Предел аренды (USD)",
"leaseCapUsdDesc": "Максимальный абсолютный предел на срез аренды в долларах США, оставьте пустым для отсутствия ограничения"
"leaseCapUsdDesc": "Максимальный абсолютный предел на срез аренды в долларах США, оставьте пустым для отсутствия ограничения",
"warnings": {
"dbRefreshIntervalTooLow": "Интервал {value}s. Это может увеличить нагрузку на БД.",
"dbRefreshIntervalTooHigh": "Интервал {value}s. Обновление квот/лимитов может запаздывать.",
"leasePercentZero": "Процент равен 0. Бюджет аренды может всегда быть 0.",
"leaseCapZero": "Предел аренды равен 0. Бюджет на срез может быть 0."
}
}
},
"section": {
Expand Down
9 changes: 8 additions & 1 deletion messages/ru/settings/providers/form/key.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,12 @@
"labelEdit": "API ключ (Оставьте пустым, чтобы не менять)",
"leaveEmpty": "(Оставьте пустым, чтобы не менять)",
"leaveEmptyDesc": "Пустое значение — без изменений",
"placeholder": "Введите API ключ"
"placeholder": "Введите API ключ",
"warnings": {
"looks_like_auth_header": "Похоже, вы вставили заголовок запроса (например, Bearer/Authorization/x-api-key). Введите только значение ключа.",
"wrapped_in_quotes": "Обрамлено кавычками. Обычно кавычки не нужны.",
"contains_non_ascii": "Содержит не-ASCII символы. Для API ключей это обычно нетипично.",
"contains_whitespace": "Содержит пробелы/переносы строк. Для API ключей это обычно нетипично.",
"contains_uncommon_ascii": "Содержит нетипичные символы. Для API ключей это обычно нетипично."
}
}
3 changes: 2 additions & 1 deletion messages/zh-CN/dashboard.json
Original file line number Diff line number Diff line change
Expand Up @@ -995,7 +995,8 @@
"expiresAt": {
"label": "过期时间",
"placeholder": "留空表示永不过期",
"description": "用户过期后将自动禁用"
"description": "用户过期后将自动禁用",
"pastWarning": "选择的日期已在过去,保存后用户将立即过期并被禁用。"
},
"allowedClients": {
"label": "允许的客户端",
Expand Down
16 changes: 13 additions & 3 deletions messages/zh-CN/provider-chain.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@
"concurrentLimit": "并发限制",
"http2Fallback": "HTTP/2 回退",
"clientError": "客户端错误",
"endpointPoolExhausted": "端点池耗尽"
"endpointPoolExhausted": "端点池耗尽",
"vendorTypeAllTimeout": "供应商类型全端点超时"
},
"reasons": {
"request_success": "成功",
Expand All @@ -50,7 +51,8 @@
"http2_fallback": "HTTP/2 回退",
"session_reuse": "会话复用",
"initial_selection": "首次选择",
"endpoint_pool_exhausted": "端点池耗尽"
"endpoint_pool_exhausted": "端点池耗尽",
"vendor_type_all_timeout": "供应商类型全端点超时"
},
"filterReasons": {
"rate_limited": "速率限制",
Expand All @@ -67,6 +69,12 @@
"endpoint_circuit_open": "端点已熔断",
"endpoint_disabled": "端点已禁用"
},
"filterDetails": {
"vendor_type_circuit_open": "供应商类型临时熔断",
"circuit_open": "熔断器打开",
"circuit_half_open": "熔断器半开",
"rate_limited": "速率限制"
},
"details": {
"selectionMethod": "选择方式",
"attemptNumber": "尝试次数",
Expand Down Expand Up @@ -197,6 +205,8 @@
"endpointStatsCircuitOpen": "已熔断端点: {count}",
"endpointStatsAvailable": "可用端点: {count}",
"strictBlockNoEndpoints": "严格模式:无可用端点候选,跳过该供应商且不降级",
"strictBlockSelectorError": "严格模式:端点选择器发生错误,跳过该供应商且不降级"
"strictBlockSelectorError": "严格模式:端点选择器发生错误,跳过该供应商且不降级",
"vendorTypeAllTimeout": "供应商类型全端点超时(524)",
"vendorTypeAllTimeoutNote": "该供应商类型的所有端点均超时,已触发供应商类型临时熔断。"
}
}
Loading
Loading