From 67504e81d09b2899be253c939b6b8a3e9d9c6cff Mon Sep 17 00:00:00 2001 From: ZenEcho Date: Sun, 1 Mar 2026 17:22:54 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=20GeoBypass=20?= =?UTF-8?q?=E8=A7=84=E5=88=99=E9=9B=86=E9=85=8D=E7=BD=AE=E4=B8=8E=E8=BF=90?= =?UTF-8?q?=E8=A1=8C=E6=97=B6=E6=97=A5=E5=BF=97=E7=9B=91=E6=8E=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 在游戏代理启动时,增加对 bypass_cn 模式下 GeoIP 和 Geosite 规则集的配置摘要日志输出。 在 singbox 服务中新增启动前配置检测与运行时日志监控,实时捕获规则集下载状态和错误信息,便于调试网络问题。 --- electron/services/singbox/index.ts | 52 ++++++++++++++++++++++++++++++ src/stores/games.ts | 12 +++++++ src/utils/singbox-config.ts | 6 ++++ 3 files changed, 70 insertions(+) diff --git a/electron/services/singbox/index.ts b/electron/services/singbox/index.ts index 45bbc68..f73986f 100644 --- a/electron/services/singbox/index.ts +++ b/electron/services/singbox/index.ts @@ -106,6 +106,7 @@ export class SingBoxService { this.safeSend('singbox-error', e.message) throw e } + await this.logGeoBypassConfigSummary(configPath) this.log('正在启动 sing-box...') this.lastLogs = [] @@ -122,12 +123,14 @@ export class SingBoxService { const text = data.toString().trim() pushLog(text) this.log(stripAnsi(text)) + this.inspectGeoBypassRuntimeLine(text) }) this.process.stderr?.on('data', (data) => { const text = data.toString().trim() pushLog(text) this.log(stripAnsi(text), 'error') + this.inspectGeoBypassRuntimeLine(text) }) const started = await new Promise((resolve, reject) => { @@ -229,6 +232,55 @@ export class SingBoxService { this.safeSend('singbox-log', { message, type, timestamp: Date.now() }) } + private async logGeoBypassConfigSummary(configPath: string): Promise { + try { + const raw = await fs.readFile(configPath, 'utf8') + const parsed = JSON.parse(raw) as any + const routeRuleSet = Array.isArray(parsed?.route?.rule_set) ? parsed.route.rule_set : [] + const geoip = routeRuleSet.find((item: any) => String(item?.tag || '') === 'geoip-cn') + const geosite = routeRuleSet.find((item: any) => String(item?.tag || '') === 'geosite-cn') + const enabled = !!(geoip || geosite) + this.log( + `[GeoBypass] 启动前配置检测: enabled=${enabled}, geoipUrl=${String(geoip?.url || '(none)')}, geositeUrl=${String(geosite?.url || '(none)')}` + ) + } catch (e: any) { + this.log(`[GeoBypass] 启动前配置检测失败: ${String(e?.message || e)}`, 'error') + } + } + + private inspectGeoBypassRuntimeLine(rawLine: string): void { + const line = stripAnsi(String(rawLine || '').trim()) + if (!line) return + const lower = line.toLowerCase() + const related = lower.includes('geoip-cn') + || lower.includes('geosite-cn') + || lower.includes('cn.srs') + || lower.includes('rule-set') + if (!related) return + + const failed = lower.includes('fail') + || lower.includes('error') + || lower.includes('timeout') + || lower.includes('refused') + || lower.includes('not found') + || lower.includes('forbidden') + if (failed) { + this.log(`[GeoBypass] 规则下载失败迹象: ${line}`, 'error') + return + } + + const downloaded = lower.includes('download') + || lower.includes('update') + || lower.includes('fetch') + || lower.includes('loaded') + if (downloaded) { + this.log(`[GeoBypass] 规则下载日志: ${line}`) + return + } + + this.log(`[GeoBypass] 规则相关日志: ${line}`) + } + private sendStatus(status: 'running' | 'stopped') { this.safeSend('singbox-status', status) } diff --git a/src/stores/games.ts b/src/stores/games.ts index 4bb82c3..a688789 100644 --- a/src/stores/games.ts +++ b/src/stores/games.ts @@ -419,6 +419,18 @@ export const useGameStore = defineStore('games', () => { port: systemProxyPortToUse } })) + try { + const parsed = JSON.parse(config) as any + const routeRuleSet = Array.isArray(parsed?.route?.rule_set) ? parsed.route.rule_set : [] + const geoip = routeRuleSet.find((item: any) => String(item?.tag || '') === 'geoip-cn') + const geosite = routeRuleSet.find((item: any) => String(item?.tag || '') === 'geosite-cn') + const enabled = !!(geoip || geosite) + console.info( + `[GeoBypass] 启动配置摘要 game=${rawGame.name || rawGame.id}: enabled=${enabled}, geoipUrl=${String(geoip?.url || '(none)')}, geositeUrl=${String(geosite?.url || '(none)')}` + ) + } catch (e) { + console.warn('[GeoBypass] 启动前解析 config 摘要失败:', e) + } await singboxApi.restart(config) if (useSystemProxy) { diff --git a/src/utils/singbox-config.ts b/src/utils/singbox-config.ts index e5d37f7..9d21d1d 100644 --- a/src/utils/singbox-config.ts +++ b/src/utils/singbox-config.ts @@ -330,6 +330,9 @@ export function generateSingboxConfig(game: Game, node: NodeConfig, dnsOptions?: if (hasBypassCn) { // -- 绕过大陆模式 (Bypass CN) -- + console.info( + `[GeoBypass] bypass_cn 已命中: proxyMode=${game.proxyMode}, processScope=${hasRoutingProcessScope ? 'on' : 'off'}, processes=${processes.join(',') || '(none)'}` + ) // 私有 IP 直连 const privateRule: Record = { @@ -349,6 +352,7 @@ export function generateSingboxConfig(game: Game, node: NodeConfig, dnsOptions?: url: LOYALSOLDIER_GEOIP_CN_SRS_URL, download_detour: 'proxy' // 规则集下载走代理 }) + console.info(`[GeoBypass] 已注入规则集: tag=${GEOIP_CN_RULE_SET_TAG}, url=${LOYALSOLDIER_GEOIP_CN_SRS_URL}, download_detour=proxy`) // 下载并配置 CN 域名规则集 ensureRemoteRuleSet(config, { @@ -358,6 +362,7 @@ export function generateSingboxConfig(game: Game, node: NodeConfig, dnsOptions?: url: SAGERNET_GEOSITE_CN_SRS_URL, download_detour: 'proxy' }) + console.info(`[GeoBypass] 已注入规则集: tag=${GEOSITE_CN_RULE_SET_TAG}, url=${SAGERNET_GEOSITE_CN_SRS_URL}, download_detour=proxy`) // CN IP 直连规则 const geoipRule: Record = { @@ -378,6 +383,7 @@ export function generateSingboxConfig(game: Game, node: NodeConfig, dnsOptions?: geositeRule.process_name = processes } config.route.rules.push(geositeRule) + console.info('[GeoBypass] 已注入直连规则: geoip-cn + geosite-cn -> direct') // 如果有进程限制,则剩下的流量中,只有该进程走代理 // (全局 final 默认为 direct)