diff --git a/messages/en/settings/config.json b/messages/en/settings/config.json index dcff573c3..d75b54fc4 100644 --- a/messages/en/settings/config.json +++ b/messages/en/settings/config.json @@ -171,7 +171,7 @@ "enforce": "Enabled" }, "replayEnabled": "Request Replay", - "replayEnabledDesc": "Caches upstream responses and reuses upstream connections: identical concurrent or reconnecting requests attach to the in-flight stream instead of re-hitting the provider. Follows the ENABLE_REQUEST_REPLAY environment variable until saved here. Default off.", + "replayEnabledDesc": "Caches upstream responses and reuses upstream connections: identical concurrent or reconnecting requests attach to the in-flight stream instead of re-hitting the provider. Follows the ENABLE_REQUEST_REPLAY environment variable until saved here. Default on.", "cacheEffectivenessEnabled": "Prefix Cache Simulation", "cacheEffectivenessEnabledDesc": "Simulates longest-prefix cache hit rates (theoretical vs actual) for observability only; never affects routing. Follows the ENABLE_CACHE_EFFECTIVENESS environment variable until saved here. Default on." }, diff --git a/messages/ja/settings/config.json b/messages/ja/settings/config.json index c1007ad2b..8a9aa2bae 100644 --- a/messages/ja/settings/config.json +++ b/messages/ja/settings/config.json @@ -171,7 +171,7 @@ "enforce": "有効" }, "replayEnabled": "リクエスト Replay", - "replayEnabledDesc": "上流レスポンスをキャッシュし上流接続を再利用します。同一リクエストの並行実行や再接続は進行中のストリームに追随し、プロバイダーへ再送しません。保存するまでは環境変数 ENABLE_REQUEST_REPLAY に従います。デフォルトはオフ。", + "replayEnabledDesc": "上流レスポンスをキャッシュし上流接続を再利用します。同一リクエストの並行実行や再接続は進行中のストリームに追随し、プロバイダーへ再送しません。保存するまでは環境変数 ENABLE_REQUEST_REPLAY に従います。デフォルトはオン。", "cacheEffectivenessEnabled": "プレフィックスキャッシュシミュレーション", "cacheEffectivenessEnabledDesc": "最長プレフィックス一致のキャッシュヒット率(理論値 vs 実測値)を観測目的でシミュレートします。ルーティングには影響しません。保存するまでは環境変数 ENABLE_CACHE_EFFECTIVENESS に従います。デフォルトはオン。" }, diff --git a/messages/ru/settings/config.json b/messages/ru/settings/config.json index eeb0d775b..3bc6d24f6 100644 --- a/messages/ru/settings/config.json +++ b/messages/ru/settings/config.json @@ -171,7 +171,7 @@ "enforce": "Включено" }, "replayEnabled": "Replay запросов", - "replayEnabledDesc": "Кэширует ответы провайдера и переиспользует соединения: одинаковые параллельные или переподключающиеся запросы присоединяются к текущему потоку вместо повторного обращения к провайдеру. До сохранения следует переменной окружения ENABLE_REQUEST_REPLAY. По умолчанию выключено.", + "replayEnabledDesc": "Кэширует ответы провайдера и переиспользует соединения: одинаковые параллельные или переподключающиеся запросы присоединяются к текущему потоку вместо повторного обращения к провайдеру. До сохранения следует переменной окружения ENABLE_REQUEST_REPLAY. По умолчанию включено.", "cacheEffectivenessEnabled": "Симуляция префиксного кэша", "cacheEffectivenessEnabledDesc": "Симулирует хит-рейт кэша по наибольшему префиксу (теория и факт) только для наблюдаемости; не влияет на маршрутизацию. До сохранения следует переменной окружения ENABLE_CACHE_EFFECTIVENESS. По умолчанию включено." }, diff --git a/messages/zh-CN/settings/config.json b/messages/zh-CN/settings/config.json index 3d56741b4..51dd007fb 100644 --- a/messages/zh-CN/settings/config.json +++ b/messages/zh-CN/settings/config.json @@ -95,7 +95,7 @@ "enforce": "启用" }, "replayEnabled": "请求 Replay", - "replayEnabledDesc": "缓存上游响应并复用上游连接:并发或断线重连的相同请求直接跟尾在途流,不再重复请求供应商。保存前跟随环境变量 ENABLE_REQUEST_REPLAY,默认关闭。", + "replayEnabledDesc": "缓存上游响应并复用上游连接:并发或断线重连的相同请求直接跟尾在途流,不再重复请求供应商。保存前跟随环境变量 ENABLE_REQUEST_REPLAY,默认开启。", "cacheEffectivenessEnabled": "前缀缓存模拟", "cacheEffectivenessEnabledDesc": "模拟最长前缀匹配的缓存命中率(理论 vs 实际),仅用于观测,不影响路由。保存前跟随环境变量 ENABLE_CACHE_EFFECTIVENESS,默认开启。", "affinityIgnoreClientSessionId": "忽略客户端 Session ID", diff --git a/messages/zh-TW/settings/config.json b/messages/zh-TW/settings/config.json index 1a3475e3e..b233d57ce 100644 --- a/messages/zh-TW/settings/config.json +++ b/messages/zh-TW/settings/config.json @@ -171,7 +171,7 @@ "enforce": "啟用" }, "replayEnabled": "請求 Replay", - "replayEnabledDesc": "快取上游回應並重用上游連線:並發或斷線重連的相同請求直接跟尾在途串流,不再重複請求供應商。儲存前跟隨環境變數 ENABLE_REQUEST_REPLAY,預設關閉。", + "replayEnabledDesc": "快取上游回應並重用上游連線:並發或斷線重連的相同請求直接跟尾在途串流,不再重複請求供應商。儲存前跟隨環境變數 ENABLE_REQUEST_REPLAY,預設開啟。", "cacheEffectivenessEnabled": "前綴快取模擬", "cacheEffectivenessEnabledDesc": "模擬最長前綴匹配的快取命中率(理論 vs 實際),僅用於觀測,不影響路由。儲存前跟隨環境變數 ENABLE_CACHE_EFFECTIVENESS,預設開啟。" }, diff --git a/src/app/[locale]/settings/config/_components/system-settings-form.tsx b/src/app/[locale]/settings/config/_components/system-settings-form.tsx index 1969576cf..8a371b85c 100644 --- a/src/app/[locale]/settings/config/_components/system-settings-form.tsx +++ b/src/app/[locale]/settings/config/_components/system-settings-form.tsx @@ -64,6 +64,8 @@ import type { interface SystemSettingsFormProps { /** Read-only runtime value; SESSION_TTL is not a persisted system setting. */ sessionTtlSeconds?: number; + /** Read-only env fallback used while replayEnabled remains null. */ + replayDefaultEnabled?: boolean; initialSettings: Pick< SystemSettings, | "siteTitle" @@ -131,6 +133,7 @@ type DiscoveryNumberValue = number | ""; export function SystemSettingsForm({ initialSettings, sessionTtlSeconds = 300, + replayDefaultEnabled = true, }: SystemSettingsFormProps) { const router = useRouter(); const t = useTranslations("settings.config.form"); @@ -214,7 +217,7 @@ export function SystemSettingsForm({ const [affinityIgnoreClientSessionId, setAffinityIgnoreClientSessionId] = useState( initialSettings.affinityIgnoreClientSessionId ); - // null = 跟随环境变量(Replay 默认关 / 缓存模拟默认开):未触碰开关时按 null 原样保存, + // null = 跟随环境变量:未触碰开关时按 null 原样保存, // 避免无关字段的保存把覆写写死为布尔值;仅用户切换后才落显式值 const [replayEnabled, setReplayEnabled] = useState(initialSettings.replayEnabled); const [cacheEffectivenessEnabled, setCacheEffectivenessEnabled] = useState( @@ -1187,7 +1190,7 @@ export function SystemSettingsForm({ setReplayEnabled(checked)} disabled={isPending} /> diff --git a/src/app/[locale]/settings/config/page.tsx b/src/app/[locale]/settings/config/page.tsx index 7da0a1264..c66ae4e87 100644 --- a/src/app/[locale]/settings/config/page.tsx +++ b/src/app/[locale]/settings/config/page.tsx @@ -35,7 +35,8 @@ export default async function SettingsConfigPage({ async function SettingsConfigContent({ locale }: { locale: string }) { const t = await getTranslations({ locale, namespace: "settings" }); const settings = await getSystemSettings(); - const sessionTtlSeconds = Math.max(1, Math.floor(getEnvConfig().SESSION_TTL)); + const env = getEnvConfig(); + const sessionTtlSeconds = Math.max(1, Math.floor(env.SESSION_TTL)); return ( <> @@ -47,6 +48,7 @@ async function SettingsConfigContent({ locale }: { locale: string }) { > { + test("defaults to enabled when unset", () => { + expect(EnvSchema.parse({}).ENABLE_REQUEST_REPLAY).toBe(true); + }); + + test.each([ + ["true", true], + ["false", false], + ] as const)("preserves an explicit %s value", (value, expected) => { + expect(EnvSchema.parse({ ENABLE_REQUEST_REPLAY: value }).ENABLE_REQUEST_REPLAY).toBe(expected); + }); +}); diff --git a/tests/unit/settings/system-settings-form-replay-cache-toggles.test.tsx b/tests/unit/settings/system-settings-form-replay-cache-toggles.test.tsx index 712aad6de..b7f15aa2c 100644 --- a/tests/unit/settings/system-settings-form-replay-cache-toggles.test.tsx +++ b/tests/unit/settings/system-settings-form-replay-cache-toggles.test.tsx @@ -155,7 +155,11 @@ describe("SystemSettingsForm replay/cache-effectiveness null 三态", () => { }); test("未触碰开关时保存保持 null(跟随环境变量),不写死布尔覆写", async () => { - const { unmount } = render(); + const { unmount } = render( + + ); + + expect(document.getElementById("replay-enabled")?.getAttribute("aria-checked")).toBe("true"); await submitForm(); @@ -170,7 +174,9 @@ describe("SystemSettingsForm replay/cache-effectiveness null 三态", () => { }); test("用户切换开关后保存为显式覆写,未动的另一开关仍为 null", async () => { - const { unmount } = render(); + const { unmount } = render( + + ); const switchEl = document.getElementById("replay-enabled"); if (!switchEl) throw new Error("未找到 replay-enabled 开关"); @@ -183,11 +189,21 @@ describe("SystemSettingsForm replay/cache-effectiveness null 三态", () => { expect(systemConfigActionMocks.saveSystemSettings).toHaveBeenCalledWith( expect.objectContaining({ - replayEnabled: true, + replayEnabled: false, cacheEffectivenessEnabled: null, }) ); unmount(); }); + + test("环境变量显式关闭时,null 覆写显示为关闭", () => { + const { unmount } = render( + + ); + + expect(document.getElementById("replay-enabled")?.getAttribute("aria-checked")).toBe("false"); + + unmount(); + }); });