diff --git a/messages/en/settings.json b/messages/en/settings.json
index 51a8cac30..a825d4cda 100644
--- a/messages/en/settings.json
+++ b/messages/en/settings.json
@@ -608,6 +608,7 @@
"copySuccess": "Copied to clipboard",
"copyFailed": "Failed to copy",
"copyResult": "Copy Result",
+ "close": "Close",
"success": "Success",
"failed": "Failed"
},
diff --git a/messages/zh-CN/settings.json b/messages/zh-CN/settings.json
index 1724e3a73..dc1a7aaa6 100644
--- a/messages/zh-CN/settings.json
+++ b/messages/zh-CN/settings.json
@@ -242,6 +242,7 @@
"copySuccess": "已复制到剪贴板",
"copyFailed": "复制失败",
"copyResult": "复制结果",
+ "close": "关闭",
"success": "成功",
"failed": "失败"
},
diff --git a/messages/zh-TW/settings.json b/messages/zh-TW/settings.json
index 2abac1326..12fb94da2 100644
--- a/messages/zh-TW/settings.json
+++ b/messages/zh-TW/settings.json
@@ -600,6 +600,7 @@
"copySuccess": "已複製到剪貼簿",
"copyFailed": "複製失敗",
"copyResult": "複製結果",
+ "close": "關閉",
"success": "成功",
"failed": "失敗"
},
diff --git a/src/actions/providers.ts b/src/actions/providers.ts
index f88a3cb04..dd9348468 100644
--- a/src/actions/providers.ts
+++ b/src/actions/providers.ts
@@ -1568,11 +1568,16 @@ async function executeProviderApiTest(
errorDetail = undefined;
}
+ // 使用 errorDetail 或 errorText 的前 200 字符作为错误详情
+ // 添加防御性检查,避免空字符串产生误导性错误消息
+ const finalErrorDetail =
+ errorDetail ?? (errorText ? clipText(errorText, 200) : "No error details available");
+
logger.error("Provider API test failed", {
providerUrl: normalizedProviderUrl.replace(/:\/\/[^@]*@/, "://***@"),
path: typeof options.path === "string" ? options.path : "dynamic",
status: response.status,
- errorDetail: errorDetail ?? clipText(errorText, 200),
+ errorDetail: finalErrorDetail,
});
return {
@@ -1582,7 +1587,7 @@ async function executeProviderApiTest(
message: `API 返回错误: HTTP ${response.status}`,
details: {
responseTime,
- error: "API 请求失败,查看日志以获得更多信息",
+ error: finalErrorDetail,
},
},
};
diff --git a/src/app/[locale]/settings/providers/_components/forms/api-test-button.tsx b/src/app/[locale]/settings/providers/_components/forms/api-test-button.tsx
index cf763183a..65558e050 100644
--- a/src/app/[locale]/settings/providers/_components/forms/api-test-button.tsx
+++ b/src/app/[locale]/settings/providers/_components/forms/api-test-button.tsx
@@ -527,17 +527,28 @@ export function ApiTestButton({
)}
- {/* 复制按钮 */}
-
+ {/* 操作按钮 */}
+
+
+
+
diff --git a/src/instrumentation.ts b/src/instrumentation.ts
index 7e4613df7..eab93c87f 100644
--- a/src/instrumentation.ts
+++ b/src/instrumentation.ts
@@ -5,6 +5,24 @@
import { logger } from "@/lib/logger";
+/**
+ * 初始化错误规则检测器
+ * 提取为独立函数以避免代码重复
+ *
+ * 注意: 此函数会传播关键错误,调用者应决定是否需要优雅降级
+ */
+async function initializeErrorRuleDetector(): Promise {
+ // 初始化默认错误规则 - 让关键错误传播
+ const { initializeDefaultErrorRules } = await import("@/repository/error-rules");
+ await initializeDefaultErrorRules();
+ logger.info("Default error rules initialized successfully");
+
+ // 加载错误规则缓存 - 让关键错误传播
+ const { errorRuleDetector } = await import("@/lib/error-rule-detector");
+ await errorRuleDetector.reload();
+ logger.info("Error rule detector cache loaded successfully");
+}
+
export async function register() {
// 仅在服务器端执行
if (process.env.NEXT_RUNTIME === "nodejs") {
@@ -36,13 +54,15 @@ export async function register() {
const { ensurePriceTable } = await import("@/lib/price-sync/seed-initializer");
await ensurePriceTable();
- // 初始化默认错误规则
- const { initializeDefaultErrorRules } = await import("@/repository/error-rules");
+ // 初始化错误规则检测器(非关键功能,允许优雅降级)
try {
- await initializeDefaultErrorRules();
- logger.info("Default error rules initialized successfully");
+ await initializeErrorRuleDetector();
} catch (error) {
- logger.error("Failed to initialize default error rules:", error);
+ logger.error(
+ "[Instrumentation] Non-critical: Error rule detector initialization failed",
+ error
+ );
+ // 继续启动 - 错误检测不是核心功能的关键依赖
}
// 初始化日志清理任务队列(如果启用)
@@ -72,13 +92,15 @@ export async function register() {
const { ensurePriceTable } = await import("@/lib/price-sync/seed-initializer");
await ensurePriceTable();
- // 初始化默认错误规则
- const { initializeDefaultErrorRules } = await import("@/repository/error-rules");
+ // 初始化错误规则检测器(非关键功能,允许优雅降级)
try {
- await initializeDefaultErrorRules();
- logger.info("Default error rules initialized successfully");
+ await initializeErrorRuleDetector();
} catch (error) {
- logger.error("Failed to initialize default error rules:", error);
+ logger.error(
+ "[Instrumentation] Non-critical: Error rule detector initialization failed",
+ error
+ );
+ // 继续启动 - 错误检测不是核心功能的关键依赖
}
// ⚠️ 开发环境禁用通知队列(Bull + Turbopack 不兼容)
diff --git a/src/lib/error-rule-detector.ts b/src/lib/error-rule-detector.ts
index a5b1ec870..d16d49176 100644
--- a/src/lib/error-rule-detector.ts
+++ b/src/lib/error-rule-detector.ts
@@ -61,13 +61,9 @@ class ErrorRuleDetector {
private exactPatterns: Map = new Map();
private lastReloadTime: number = 0;
private isLoading: boolean = false;
+ private isInitialized: boolean = false; // 跟踪初始化状态
constructor() {
- // 初始化时立即加载缓存(异步,不阻塞构造函数)
- this.reload().catch((error) => {
- logger.error("[ErrorRuleDetector] Failed to initialize cache:", error);
- });
-
// 监听数据库变更事件,自动刷新缓存
eventEmitter.on("errorRulesUpdated", () => {
this.reload().catch((error) => {
@@ -76,6 +72,17 @@ class ErrorRuleDetector {
});
}
+ /**
+ * 确保规则已加载(懒加载,首次使用时或显式 reload 时调用)
+ * 避免在数据库未准备好时过早加载
+ */
+ private async ensureInitialized(): Promise {
+ if (this.isInitialized || this.isLoading) {
+ return;
+ }
+ await this.reload();
+ }
+
/**
* 从数据库重新加载错误规则
*/
@@ -169,6 +176,7 @@ class ErrorRuleDetector {
}
this.lastReloadTime = Date.now();
+ this.isInitialized = true; // 标记为已初始化
logger.info(
`[ErrorRuleDetector] Loaded ${rules.length} error rules: ` +
@@ -184,7 +192,22 @@ class ErrorRuleDetector {
}
/**
- * 检测错误消息是否匹配任何规则
+ * 异步检测错误消息(推荐使用)
+ * 确保规则已加载后再进行检测
+ *
+ * @param errorMessage - 错误消息
+ * @returns 检测结果
+ */
+ async detectAsync(errorMessage: string): Promise {
+ await this.ensureInitialized();
+ return this.detect(errorMessage);
+ }
+
+ /**
+ * 检测错误消息是否匹配任何规则(同步版本)
+ *
+ * 注意:如果规则未初始化,会记录警告并返回 false
+ * 推荐使用 detectAsync() 以确保规则已加载
*
* 检测顺序(性能优先):
* 1. 包含匹配(最快,O(n*m))
@@ -199,6 +222,13 @@ class ErrorRuleDetector {
return { matched: false };
}
+ // 如果未初始化,记录警告
+ if (!this.isInitialized && !this.isLoading) {
+ logger.warn(
+ "[ErrorRuleDetector] detect() called before initialization, results may be incomplete. Consider using detectAsync() instead."
+ );
+ }
+
const lowerMessage = errorMessage.toLowerCase();
const trimmedMessage = lowerMessage.trim();