Bläddra i källkod

fix: address review comments from PR #731

- fix(auth): prevent scoped session access widening via ?? -> && guard
- fix(i18n): standardize zh-CN provider terminology to "服务商"
- fix(i18n): use consistent Russian translations for circuit status
- fix(i18n): replace raw formatDistanceToNow with locale-aware RelativeTime
- fix(gemini): log warning for unknown google search preference values
- fix(error-rules): check subscribeCacheInvalidation return value
- fix(test): correct endpoint hover sort test to assert URLs not labels

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
ding113 1 vecka sedan
förälder
incheckning
fda5063042

+ 2 - 2
messages/ru/settings/providers/strings.json

@@ -111,7 +111,7 @@
     "healthy": "Доступен",
     "unhealthy": "Недоступен",
     "unknown": "Неизвестно",
-    "circuitOpen": "Circuit открыт",
-    "circuitHalfOpen": "Circuit полуоткрыт"
+    "circuitOpen": "Цепь открыта",
+    "circuitHalfOpen": "Цепь полуоткрыта"
   }
 }

+ 3 - 3
messages/zh-CN/settings/providers/form/errors.json

@@ -1,9 +1,9 @@
 {
   "invalidUrl": "请输入有效的 API 地址",
-  "invalidWebsiteUrl": "请输入有效的供应商官网地址",
+  "invalidWebsiteUrl": "请输入有效的服务商官网地址",
   "groupTagTooLong": "分组标签总长度不能超过 {max} 个字符",
-  "nameRequired": "请输入供应商名称",
-  "urlRequired": "请先填写供应商 URL",
+  "nameRequired": "请输入服务商名称",
+  "urlRequired": "请先填写服务商 URL",
   "keyRequired": "请输入 API 密钥",
   "addFailed": "添加服务商失败",
   "updateFailed": "更新服务商失败",

+ 7 - 8
src/app/[locale]/settings/providers/_components/provider-endpoints-table.tsx

@@ -1,7 +1,6 @@
 "use client";
 
 import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
-import { formatDistanceToNow } from "date-fns";
 import { Edit2, Loader2, MoreHorizontal, Play, Plus, Trash2 } from "lucide-react";
 import { useTranslations } from "next-intl";
 import { useEffect, useMemo, useState } from "react";
@@ -33,6 +32,7 @@ import {
 } from "@/components/ui/dropdown-menu";
 import { Input } from "@/components/ui/input";
 import { Label } from "@/components/ui/label";
+import { RelativeTime } from "@/components/ui/relative-time";
 import {
   Select,
   SelectContent,
@@ -301,13 +301,12 @@ function EndpointRow({
       <TableCell>
         <div className="flex items-center gap-3">
           <EndpointLatencySparkline endpointId={endpoint.id} limit={12} />
-          {endpoint.lastProbedAt ? (
-            <span className="text-muted-foreground text-[10px] whitespace-nowrap">
-              {formatDistanceToNow(new Date(endpoint.lastProbedAt), { addSuffix: true })}
-            </span>
-          ) : (
-            <span className="text-muted-foreground text-[10px]">-</span>
-          )}
+          <RelativeTime
+            date={endpoint.lastProbedAt}
+            format="short"
+            fallback="-"
+            className="text-muted-foreground text-[10px] whitespace-nowrap"
+          />
         </div>
       </TableCell>
       {!readOnly && (

+ 3 - 2
src/lib/auth.ts

@@ -162,8 +162,9 @@ export async function getSession(options?: {
   // 优先读取 adapter 注入的请求级会话(适配 /api/actions 等非 Next 原生上下文场景)
   const scoped = getScopedAuthContext();
   if (scoped) {
-    // 关键:scoped 会话必须遵循其“创建时语义”,并允许内部显式降权校验
-    const effectiveAllowReadOnlyAccess = options?.allowReadOnlyAccess ?? scoped.allowReadOnlyAccess;
+    // 关键:scoped 会话必须遵循其"创建时语义",仅允许内部显式降权(不允许提权)
+    const effectiveAllowReadOnlyAccess =
+      scoped.allowReadOnlyAccess && (options?.allowReadOnlyAccess ?? true);
     if (!effectiveAllowReadOnlyAccess && !scoped.session.key.canLoginWebUi) {
       return null;
     }

+ 7 - 1
src/lib/error-rule-detector.ts

@@ -113,7 +113,13 @@ class ErrorRuleDetector {
         const { CHANNEL_ERROR_RULES_UPDATED, subscribeCacheInvalidation } = await import(
           "@/lib/redis/pubsub"
         );
-        await subscribeCacheInvalidation(CHANNEL_ERROR_RULES_UPDATED, handleUpdated);
+        const cleanup = await subscribeCacheInvalidation(
+          CHANNEL_ERROR_RULES_UPDATED,
+          handleUpdated
+        );
+        if (cleanup) {
+          logger.info("[ErrorRuleDetector] Subscribed to Redis pub/sub channel");
+        }
       } catch {
         // 忽略导入错误(可能在 Edge runtime 中)
       }

+ 5 - 1
src/lib/gemini/provider-overrides.ts

@@ -1,3 +1,4 @@
+import { logger } from "@/lib/logger";
 import type { GeminiGoogleSearchPreference, ProviderType } from "@/types/provider";
 import type { GeminiGoogleSearchOverrideSpecialSetting } from "@/types/special-settings";
 
@@ -117,7 +118,10 @@ export function applyGeminiGoogleSearchOverrideWithAudit(
   } else if (preference === "disabled") {
     action = hadGoogleSearch ? "remove" : "passthrough";
   } else {
-    // Unreachable with current validation, but handle gracefully
+    logger.warn("applyGeminiGoogleSearchOverrideWithAudit: unknown preference value", {
+      preference,
+      providerId: provider.id,
+    });
     return { request, audit: null };
   }
 

+ 5 - 1
tests/unit/settings/providers/provider-endpoint-hover.test.tsx

@@ -223,7 +223,11 @@ describe("ProviderEndpointHover", () => {
       document.querySelectorAll("[data-testid='tooltip-content'] span.truncate")
     ).map((el) => el.textContent);
 
-    expect(labels).toEqual(["Healthy Endpoint", "Unknown Endpoint", "Unhealthy Endpoint"]);
+    expect(labels).toEqual([
+      "https://api.anthropic.com/v1",
+      "https://api.anthropic.com/v3",
+      "https://api.anthropic.com/v2",
+    ]);
 
     unmount();
   });