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();