()
| 300 | * 环境变量覆盖始终保持最高优先级,不受热重载影响。 |
| 301 | */ |
| 302 | export function initConfigWatcher(): void { |
| 303 | if (watcher) return; // 避免重复初始化 |
| 304 | if (!existsSync('config.yaml')) { |
| 305 | console.log('[Config] config.yaml 不存在,跳过热重载监听'); |
| 306 | return; |
| 307 | } |
| 308 | |
| 309 | const DEBOUNCE_MS = 500; |
| 310 | |
| 311 | watcher = watch('config.yaml', (eventType) => { |
| 312 | if (eventType !== 'change') return; |
| 313 | |
| 314 | // 防抖:多次快速写入只触发一次重载 |
| 315 | if (debounceTimer) clearTimeout(debounceTimer); |
| 316 | debounceTimer = setTimeout(() => { |
| 317 | try { |
| 318 | if (!existsSync('config.yaml')) { |
| 319 | console.warn('[Config] ⚠️ config.yaml 已被删除,保持当前配置'); |
| 320 | return; |
| 321 | } |
| 322 | |
| 323 | const oldConfig = config; |
| 324 | const oldPort = oldConfig.port; |
| 325 | |
| 326 | // 重新解析 YAML + 环境变量覆盖 |
| 327 | const defaults = defaultConfig(); |
| 328 | const { config: newConfig } = parseYamlConfig(defaults); |
| 329 | applyEnvOverrides(newConfig); |
| 330 | |
| 331 | // 检测变更 |
| 332 | const changes = detectChanges(oldConfig, newConfig); |
| 333 | if (changes.length === 0) return; // 无实质变更 |
| 334 | |
| 335 | // ★ 端口变更特殊处理:仅警告,不生效 |
| 336 | if (newConfig.port !== oldPort) { |
| 337 | console.warn(`[Config] ⚠️ 检测到 port 变更 (${oldPort} → ${newConfig.port}),端口变更需要重启服务才能生效`); |
| 338 | newConfig.port = oldPort; // 保持原端口 |
| 339 | } |
| 340 | |
| 341 | // 替换全局配置对象(下一次 getConfig() 调用即返回新配置) |
| 342 | config = newConfig; |
| 343 | |
| 344 | console.log(`[Config] 🔄 config.yaml 已热重载,${changes.length} 项变更:`); |
| 345 | changes.forEach(c => console.log(` └─ ${c}`)); |
| 346 | |
| 347 | // 触发回调 |
| 348 | for (const cb of reloadCallbacks) { |
| 349 | try { |
| 350 | cb(newConfig, changes); |
| 351 | } catch (e) { |
| 352 | console.warn('[Config] 热重载回调执行失败:', e); |
| 353 | } |
| 354 | } |
| 355 | } catch (e) { |
| 356 | console.error('[Config] ❌ 热重载失败,保持当前配置:', e); |
| 357 | } |
| 358 | }, DEBOUNCE_MS); |
| 359 | }); |
no test coverage detected