Skip to content

Commit 9e191de

Browse files
hhh2210xream
authored andcommitted
fix(SurgeMac): 合并 mihomo 配置覆盖层一次, 避免 GLOBAL 出现重复节点 (#619)
延迟合并 mihomo 配置覆盖层,避免 GLOBAL 共享数组被逐节点追加;同时保留多个节点覆盖层的逐 key 浅合并语义,并补充回归测试。
1 parent a4c8741 commit 9e191de

4 files changed

Lines changed: 78 additions & 5 deletions

File tree

backend/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "sub-store",
3-
"version": "2.36.29",
3+
"version": "2.36.30",
44
"description": "Advanced Subscription Manager for QX, Loon, Surge, Stash and Shadowrocket.",
55
"main": "src/main.js",
66
"packageManager": "pnpm@11.0.9",

backend/src/core/proxy-utils/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -598,6 +598,7 @@ function produce(proxies, targetPlatform, type, opts = {}) {
598598
Base64.encode(
599599
JSON.stringify({
600600
...opts._merged.config,
601+
...(opts._merged.configOverride || {}),
601602
'mixed-port': opts.localPort,
602603
}),
603604
),

backend/src/core/proxy-utils/producers/surgemac.js

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -206,10 +206,16 @@ function mihomo(proxy, type, opts) {
206206
...clashProxy,
207207
name: proxyName,
208208
});
209-
opts._merged.config = {
210-
...opts._merged.config,
211-
...(opts?.config || proxy._config || {}),
212-
};
209+
// 只记录覆盖层, 在所有节点都并入后由 index.js 统一合并一次.
210+
// 若在这里就合并, 覆盖层里的 proxy-groups 会成为后续节点
211+
// `proxy-groups[0].proxies.push()` 的目标, 使 GLOBAL 出现重复项.
212+
const configOverride = opts?.config || proxy._config;
213+
if (configOverride) {
214+
opts._merged.configOverride = {
215+
...(opts._merged.configOverride || {}),
216+
...configOverride,
217+
};
218+
}
213219
} else {
214220
const external_proxy = {
215221
name: proxy.name,

backend/src/test/proxy-producers/text.spec.js

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2067,6 +2067,72 @@ describe('Proxy text producers', function () {
20672067
expect(output).to.not.include('Forced Mihomo=tuic-v5');
20682068
});
20692069

2070+
it('merges SurgeMac _config once so GLOBAL lists each merged node once', function () {
2071+
// A Script Operator naturally builds the override once and assigns the
2072+
// same object to every node, so the shared reference must survive.
2073+
const override = {
2074+
'proxy-groups': [
2075+
{
2076+
name: 'GLOBAL',
2077+
type: 'fallback',
2078+
proxies: ['20000', '19999', '19998'],
2079+
url: 'http://cp.cloudflare.com/generate_204',
2080+
interval: 300,
2081+
},
2082+
],
2083+
};
2084+
const proxies = ['A', 'B', 'C'].map((name) => ({
2085+
type: 'trojan',
2086+
name,
2087+
server: `${name.toLowerCase()}.example.com`,
2088+
port: 443,
2089+
password: 'secret',
2090+
_merge: true,
2091+
_mihomoExternal: true,
2092+
_localPort: 20000,
2093+
_config: override,
2094+
}));
2095+
2096+
const output = ProxyUtils.produce(proxies, 'SurgeMac', 'external', {});
2097+
2098+
const args = [...output.matchAll(/args="([^"]+)"/g)].map((m) => m[1]);
2099+
const config = JSON.parse(Base64.decode(args[args.length - 1]));
2100+
const globalGroup = config['proxy-groups'].find(
2101+
(group) => group.name === 'GLOBAL',
2102+
);
2103+
2104+
expect(globalGroup.proxies).to.deep.equal(['20000', '19999', '19998']);
2105+
expect(globalGroup.url).to.equal(
2106+
'http://cp.cloudflare.com/generate_204',
2107+
);
2108+
expect(globalGroup.interval).to.equal(300);
2109+
expect(config.listeners).to.have.lengthOf(3);
2110+
expect(config.proxies).to.have.lengthOf(3);
2111+
expect(config['mixed-port']).to.equal(19997);
2112+
});
2113+
2114+
it('keeps earlier SurgeMac _config fields when later nodes add partial overrides', function () {
2115+
const overrides = [{ 'allow-lan': true }, { 'log-level': 'debug' }, {}];
2116+
const proxies = ['A', 'B', 'C'].map((name, index) => ({
2117+
type: 'trojan',
2118+
name,
2119+
server: `${name.toLowerCase()}.example.com`,
2120+
port: 443,
2121+
password: 'secret',
2122+
_merge: true,
2123+
_mihomoExternal: true,
2124+
_localPort: 20000,
2125+
_config: overrides[index],
2126+
}));
2127+
2128+
const output = ProxyUtils.produce(proxies, 'SurgeMac', 'external', {});
2129+
const args = [...output.matchAll(/args="([^"]+)"/g)].map((m) => m[1]);
2130+
const config = JSON.parse(Base64.decode(args[args.length - 1]));
2131+
2132+
expect(config['allow-lan']).to.equal(true);
2133+
expect(config['log-level']).to.equal('debug');
2134+
});
2135+
20702136
it('produces URI WireGuard links with stored and default CIDR suffixes', function () {
20712137
const output = produceExternal('URI', [
20722138
{

0 commit comments

Comments
 (0)