@morndream/mihomo-tui 0.5.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +93 -0
- package/LICENSE +21 -0
- package/README.md +462 -0
- package/bin/mihomo-tui +34 -0
- package/dist/App.js +161 -0
- package/dist/App.js.map +1 -0
- package/dist/api/client.js +239 -0
- package/dist/api/client.js.map +1 -0
- package/dist/api/status.js +43 -0
- package/dist/api/status.js.map +1 -0
- package/dist/api/stream.js +139 -0
- package/dist/api/stream.js.map +1 -0
- package/dist/api/types.js +6 -0
- package/dist/api/types.js.map +1 -0
- package/dist/cli.js +221 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/conn.js +122 -0
- package/dist/commands/conn.js.map +1 -0
- package/dist/commands/logs.js +99 -0
- package/dist/commands/logs.js.map +1 -0
- package/dist/commands/output.js +177 -0
- package/dist/commands/output.js.map +1 -0
- package/dist/commands/provider.js +140 -0
- package/dist/commands/provider.js.map +1 -0
- package/dist/commands/proxy.js +161 -0
- package/dist/commands/proxy.js.map +1 -0
- package/dist/commands/proxyEnv.js +193 -0
- package/dist/commands/proxyEnv.js.map +1 -0
- package/dist/commands/rules.js +87 -0
- package/dist/commands/rules.js.map +1 -0
- package/dist/commands/status.js +75 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/components/ConfirmDialog.js +31 -0
- package/dist/components/ConfirmDialog.js.map +1 -0
- package/dist/components/DelayBadge.js +27 -0
- package/dist/components/DelayBadge.js.map +1 -0
- package/dist/components/InputDialog.js +325 -0
- package/dist/components/InputDialog.js.map +1 -0
- package/dist/components/ListDialog.js +86 -0
- package/dist/components/ListDialog.js.map +1 -0
- package/dist/components/ProgressDialog.js +34 -0
- package/dist/components/ProgressDialog.js.map +1 -0
- package/dist/components/ScrollList.js +26 -0
- package/dist/components/ScrollList.js.map +1 -0
- package/dist/components/StatusBar.js +58 -0
- package/dist/components/StatusBar.js.map +1 -0
- package/dist/components/TextEditor.js +112 -0
- package/dist/components/TextEditor.js.map +1 -0
- package/dist/components/textEditorModel.js +154 -0
- package/dist/components/textEditorModel.js.map +1 -0
- package/dist/config/manager.js +296 -0
- package/dist/config/manager.js.map +1 -0
- package/dist/config/preserveGroups.js +9 -0
- package/dist/config/preserveGroups.js.map +1 -0
- package/dist/config/ruleDocument.js +88 -0
- package/dist/config/ruleDocument.js.map +1 -0
- package/dist/config/ruleService.js +256 -0
- package/dist/config/ruleService.js.map +1 -0
- package/dist/config/ruleValidation.js +98 -0
- package/dist/config/ruleValidation.js.map +1 -0
- package/dist/config/service.js +63 -0
- package/dist/config/service.js.map +1 -0
- package/dist/config/settingsService.js +162 -0
- package/dist/config/settingsService.js.map +1 -0
- package/dist/config/skeleton.js +324 -0
- package/dist/config/skeleton.js.map +1 -0
- package/dist/config/subscriptionService.js +284 -0
- package/dist/config/subscriptionService.js.map +1 -0
- package/dist/config/subscriptions.js +200 -0
- package/dist/config/subscriptions.js.map +1 -0
- package/dist/config/types.js +5 -0
- package/dist/config/types.js.map +1 -0
- package/dist/config.js +101 -0
- package/dist/config.js.map +1 -0
- package/dist/hooks/useProviders.js +138 -0
- package/dist/hooks/useProviders.js.map +1 -0
- package/dist/hooks/useProxies.js +298 -0
- package/dist/hooks/useProxies.js.map +1 -0
- package/dist/hooks/useStream.js +127 -0
- package/dist/hooks/useStream.js.map +1 -0
- package/dist/kernel/installer.js +317 -0
- package/dist/kernel/installer.js.map +1 -0
- package/dist/kernel/releases.js +111 -0
- package/dist/kernel/releases.js.map +1 -0
- package/dist/kernel/sources.js +98 -0
- package/dist/kernel/sources.js.map +1 -0
- package/dist/rules/editor.js +181 -0
- package/dist/rules/editor.js.map +1 -0
- package/dist/rules/matcher.js +83 -0
- package/dist/rules/matcher.js.map +1 -0
- package/dist/rules/model.js +25 -0
- package/dist/rules/model.js.map +1 -0
- package/dist/rules/runtime.js +46 -0
- package/dist/rules/runtime.js.map +1 -0
- package/dist/rules/types.js +10 -0
- package/dist/rules/types.js.map +1 -0
- package/dist/ui/FooterLine.js +62 -0
- package/dist/ui/FooterLine.js.map +1 -0
- package/dist/ui/Panel.js +47 -0
- package/dist/ui/Panel.js.map +1 -0
- package/dist/ui/RowIndicator.js +25 -0
- package/dist/ui/RowIndicator.js.map +1 -0
- package/dist/ui/SectionHeader.js +14 -0
- package/dist/ui/SectionHeader.js.map +1 -0
- package/dist/ui/StatusDot.js +15 -0
- package/dist/ui/StatusDot.js.map +1 -0
- package/dist/ui/TopBar.js +81 -0
- package/dist/ui/TopBar.js.map +1 -0
- package/dist/ui/exitGuard.js +20 -0
- package/dist/ui/exitGuard.js.map +1 -0
- package/dist/ui/keyCapture.js +28 -0
- package/dist/ui/keyCapture.js.map +1 -0
- package/dist/ui/layout.js +32 -0
- package/dist/ui/layout.js.map +1 -0
- package/dist/ui/rowIndicator.js +42 -0
- package/dist/ui/rowIndicator.js.map +1 -0
- package/dist/ui/tableHeader.js +33 -0
- package/dist/ui/tableHeader.js.map +1 -0
- package/dist/ui/theme.js +47 -0
- package/dist/ui/theme.js.map +1 -0
- package/dist/ui/tone.js +131 -0
- package/dist/ui/tone.js.map +1 -0
- package/dist/version.js +27 -0
- package/dist/version.js.map +1 -0
- package/dist/views/Conns.js +107 -0
- package/dist/views/Conns.js.map +1 -0
- package/dist/views/Logs.js +89 -0
- package/dist/views/Logs.js.map +1 -0
- package/dist/views/Providers.js +392 -0
- package/dist/views/Providers.js.map +1 -0
- package/dist/views/Proxies.js +233 -0
- package/dist/views/Proxies.js.map +1 -0
- package/dist/views/RuleEditor.js +356 -0
- package/dist/views/RuleEditor.js.map +1 -0
- package/dist/views/Rules.js +286 -0
- package/dist/views/Rules.js.map +1 -0
- package/dist/views/Settings.js +734 -0
- package/dist/views/Settings.js.map +1 -0
- package/dist/views/groupDisplay.js +53 -0
- package/dist/views/groupDisplay.js.map +1 -0
- package/dist/views/subscriptionFields.js +59 -0
- package/dist/views/subscriptionFields.js.map +1 -0
- package/dist/views/yamlFile.js +32 -0
- package/dist/views/yamlFile.js.map +1 -0
- package/package.json +66 -0
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
import { preserveLegacyRelayGroups } from './preserveGroups.js';
|
|
2
|
+
/** 机场塞进节点列表里的伪装条目(实测三家订阅都有),统一过滤 */
|
|
3
|
+
export const EXCLUDE_FILTER = '(?i)(剩余|到期|官网|流量|套餐|重置|过期|群组|订阅|邀请|客服|网址|建议|丢失|重置)';
|
|
4
|
+
/** 单机场组统一用「机场-<订阅名>」命名;[1] 节点页按该前缀动态识别机场组 */
|
|
5
|
+
export const AIRPORT_GROUP_PREFIX = '机场-';
|
|
6
|
+
export const TEST_URL = 'https://www.gstatic.com/generate_204';
|
|
7
|
+
/**
|
|
8
|
+
* DNS 上游。**这组值是本机实测得出的,不能照搬旧配置。**
|
|
9
|
+
*
|
|
10
|
+
* 本机网络环境实测(2026-08-17):
|
|
11
|
+
* - UDP 53 → 223.5.5.5 / 8.8.8.8 / 1.1.1.1 / 114.114.114.114 全部不通
|
|
12
|
+
* - DoT 853 → 223.5.5.5 / 119.29.29.29 不通
|
|
13
|
+
* - UDP 53 → 网关 192.168.200.1 ✅ 可用
|
|
14
|
+
* - DoH 443 → 1.12.12.12 / 120.53.53.53(IP 直连) ✅ 可用
|
|
15
|
+
*
|
|
16
|
+
* 旧配置的致命链条:`nameserver: doh.pub` 需要先解析 `doh.pub`,
|
|
17
|
+
* 而 bootstrap 用的 853 端口在本机不通 → 所有解析失败。
|
|
18
|
+
* 因此 bootstrap 用网关 IP,主上游用 IP 字面量的 DoH,完全绕开域名 bootstrap。
|
|
19
|
+
*/
|
|
20
|
+
export const DNS_UPSTREAM = {
|
|
21
|
+
// bootstrap 只能填纯 IP,且必须是本机真正可达的
|
|
22
|
+
'default-nameserver': ['192.168.200.1'],
|
|
23
|
+
// IP 字面量 DoH,无需 bootstrap 解析
|
|
24
|
+
nameserver: ['https://1.12.12.12/dns-query', 'https://120.53.53.53/dns-query'],
|
|
25
|
+
'direct-nameserver': ['https://1.12.12.12/dns-query', 'https://120.53.53.53/dns-query'],
|
|
26
|
+
// respect-rules: true 要求此项非空,否则 mihomo -t 直接拒绝
|
|
27
|
+
'proxy-server-nameserver': ['https://1.12.12.12/dns-query', 'https://120.53.53.53/dns-query'],
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* 区域组定义。filter 为 Go RE2 语法,已用真实 216 个节点名验证过归类结果。
|
|
31
|
+
* 注意:台湾组含 🇨🇳 —— 实测三家订阅把台湾节点标为 🇨🇳,且没有真正的中国大陆节点。
|
|
32
|
+
*/
|
|
33
|
+
export const REGIONS = [
|
|
34
|
+
{ name: '香港', filter: '(?i)(香港|hk|hong ?kong|🇭🇰)' },
|
|
35
|
+
{ name: '台湾', filter: '(?i)(台湾|台灣|臺灣|taiwan|🇹🇼|🇨🇳)' },
|
|
36
|
+
{ name: '日本', filter: '(?i)(日本|jp|japan|东京|大阪|🇯🇵)' },
|
|
37
|
+
{ name: '韩国', filter: '(?i)(韩国|韓國|korea|首尔|🇰🇷)' },
|
|
38
|
+
{ name: '新加坡', filter: '(?i)(新加坡|狮城|singapore|🇸🇬)' },
|
|
39
|
+
{ name: '美国', filter: '(?i)(美国|美國|united ?states|洛杉矶|圣何塞|西雅图|🇺🇸)' },
|
|
40
|
+
{ name: '英国', filter: '(?i)(英国|英國|united ?kingdom|britain|伦敦|🇬🇧)' },
|
|
41
|
+
{ name: '德国', filter: '(?i)(德国|德國|germany|法兰克福|🇩🇪)' },
|
|
42
|
+
{ name: '荷兰', filter: '(?i)(荷兰|荷蘭|netherlands|🇳🇱)' },
|
|
43
|
+
];
|
|
44
|
+
/** 未归入区域的节点(实测 67 个)走这里 */
|
|
45
|
+
export const OTHERS_NAME = '其他地区';
|
|
46
|
+
/**
|
|
47
|
+
* 旧配置里的主选择组名是机场品牌名「Yue.to | 悦通」,重命名为 PROXY,
|
|
48
|
+
* rules 里的引用由 rewriteRules 同步改写(实测 61 条)。
|
|
49
|
+
*/
|
|
50
|
+
export const RENAME_GROUPS = new Map([['Yue.to | 悦通', 'PROXY']]);
|
|
51
|
+
/** 旧配置中按用途分流的组,保留原名以免动 234 条 rules 的语义 */
|
|
52
|
+
export const PURPOSE_GROUPS = [
|
|
53
|
+
'AI',
|
|
54
|
+
'Google',
|
|
55
|
+
'YouTube',
|
|
56
|
+
'TikTok',
|
|
57
|
+
'Telegram',
|
|
58
|
+
'社交媒体',
|
|
59
|
+
'流媒体',
|
|
60
|
+
'游戏平台',
|
|
61
|
+
'兜底分流',
|
|
62
|
+
];
|
|
63
|
+
/**
|
|
64
|
+
* 订阅域名必须直连。
|
|
65
|
+
* 内核拉取订阅时也走自身 rules,若落到 MATCH 指向的代理组,
|
|
66
|
+
* 而该组的节点又来自尚未拉取成功的 provider,就会形成死锁(实测踩到过)。
|
|
67
|
+
*/
|
|
68
|
+
export function subscriptionDirectRules(subs) {
|
|
69
|
+
const hosts = new Set();
|
|
70
|
+
for (const sub of subs) {
|
|
71
|
+
if (!sub.url)
|
|
72
|
+
continue;
|
|
73
|
+
try {
|
|
74
|
+
hosts.add(new URL(sub.url).hostname);
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
// URL 非法在 loadSubscriptions 已拦,这里兜底忽略
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return [...hosts].sort().map((host) => `DOMAIN,${host},DIRECT`);
|
|
81
|
+
}
|
|
82
|
+
/** 生成 proxy-providers 段。
|
|
83
|
+
* remote 且未锁定 → http provider,按 URL 拉取;
|
|
84
|
+
* local,或 remote 但已锁定(手动编辑过)→ file provider,读本地 YAML,
|
|
85
|
+
* 避免内核按 URL 重新拉取覆盖手改内容。
|
|
86
|
+
* interval 以分钟存储,这里换算成秒;缺省 / 0 / 锁定一律写 0(禁用自动更新)。
|
|
87
|
+
*/
|
|
88
|
+
export function buildProviders(subs, testUrl = TEST_URL) {
|
|
89
|
+
const providers = {};
|
|
90
|
+
for (const sub of subs) {
|
|
91
|
+
const fileBacked = sub.type === 'local' || sub.locked === true;
|
|
92
|
+
const intervalSec = fileBacked || !sub.interval ? 0 : sub.interval * 60;
|
|
93
|
+
providers[sub.name] = {
|
|
94
|
+
...(fileBacked
|
|
95
|
+
? { type: 'file' }
|
|
96
|
+
: { type: 'http', url: sub.url }),
|
|
97
|
+
// 必须是相对路径:mihomo 对 provider path 有安全校验,
|
|
98
|
+
// 配置文件外的绝对路径会被拒绝
|
|
99
|
+
path: `./providers/${sub.name}.yaml`,
|
|
100
|
+
interval: intervalSec,
|
|
101
|
+
'exclude-filter': EXCLUDE_FILTER,
|
|
102
|
+
...(sub.prefix ? { override: { 'additional-prefix': sub.prefix } } : {}),
|
|
103
|
+
'health-check': {
|
|
104
|
+
enable: true,
|
|
105
|
+
url: testUrl,
|
|
106
|
+
interval: 300,
|
|
107
|
+
lazy: true,
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
return providers;
|
|
112
|
+
}
|
|
113
|
+
/** 生成 proxy-groups 段 */
|
|
114
|
+
export function buildGroups(subs, regions = REGIONS, testUrl = TEST_URL) {
|
|
115
|
+
const regionNames = regions.map((region) => region.name);
|
|
116
|
+
const airportNames = subs.map((sub) => `${AIRPORT_GROUP_PREFIX}${sub.name}`);
|
|
117
|
+
// 主组:手动选择,成员为自动组 + 区域组 + 机场组 + DIRECT
|
|
118
|
+
const main = {
|
|
119
|
+
name: 'PROXY',
|
|
120
|
+
type: 'select',
|
|
121
|
+
proxies: ['AUTO', 'FALLBACK', ...regionNames, OTHERS_NAME, ...airportNames, 'DIRECT'],
|
|
122
|
+
};
|
|
123
|
+
const auto = {
|
|
124
|
+
name: 'AUTO',
|
|
125
|
+
type: 'url-test',
|
|
126
|
+
'include-all-providers': true,
|
|
127
|
+
url: testUrl,
|
|
128
|
+
interval: 300,
|
|
129
|
+
tolerance: 100,
|
|
130
|
+
lazy: true,
|
|
131
|
+
};
|
|
132
|
+
const fallback = {
|
|
133
|
+
name: 'FALLBACK',
|
|
134
|
+
type: 'fallback',
|
|
135
|
+
'include-all-providers': true,
|
|
136
|
+
url: testUrl,
|
|
137
|
+
interval: 300,
|
|
138
|
+
lazy: true,
|
|
139
|
+
};
|
|
140
|
+
// 区域组:跨机场混用
|
|
141
|
+
const regionGroups = regions.map((region) => ({
|
|
142
|
+
name: region.name,
|
|
143
|
+
type: 'url-test',
|
|
144
|
+
'include-all-providers': true,
|
|
145
|
+
filter: region.filter,
|
|
146
|
+
url: testUrl,
|
|
147
|
+
interval: 300,
|
|
148
|
+
tolerance: 100,
|
|
149
|
+
lazy: true,
|
|
150
|
+
}));
|
|
151
|
+
// 其他地区:把不属于上述区域的节点兜起来,用 select 由人工挑
|
|
152
|
+
const others = {
|
|
153
|
+
name: OTHERS_NAME,
|
|
154
|
+
type: 'select',
|
|
155
|
+
'include-all-providers': true,
|
|
156
|
+
'exclude-filter': regions.map((r) => r.filter.replace(/^\(\?i\)/, '')).join('|'),
|
|
157
|
+
};
|
|
158
|
+
// 单机场组:按机场分别列出,便于定位某家机场的问题
|
|
159
|
+
const airports = subs.map((sub) => ({
|
|
160
|
+
name: `${AIRPORT_GROUP_PREFIX}${sub.name}`,
|
|
161
|
+
type: 'select',
|
|
162
|
+
use: [sub.name],
|
|
163
|
+
}));
|
|
164
|
+
// 用途组:保留旧配置的组名,使 234 条 rules 无需大改
|
|
165
|
+
const purposes = PURPOSE_GROUPS.map((name) => ({
|
|
166
|
+
name,
|
|
167
|
+
type: 'select',
|
|
168
|
+
proxies: name === '游戏平台' || name === '兜底分流'
|
|
169
|
+
? ['DIRECT', 'PROXY', 'AUTO', ...regionNames, OTHERS_NAME]
|
|
170
|
+
: ['PROXY', 'AUTO', ...regionNames, OTHERS_NAME, 'DIRECT'],
|
|
171
|
+
}));
|
|
172
|
+
return [main, auto, fallback, ...regionGroups, others, ...airports, ...purposes];
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* 改写 rules 里对已重命名/已消失组的引用。
|
|
176
|
+
* 只替换规则的「目标」字段(最后一段,或 no-resolve 前一段),不碰域名部分。
|
|
177
|
+
*/
|
|
178
|
+
export function rewriteRules(rules, groupNames) {
|
|
179
|
+
const known = new Set([...groupNames, 'DIRECT', 'REJECT', 'REJECT-DROP', 'PASS', 'COMPATIBLE']);
|
|
180
|
+
const rewritten = [];
|
|
181
|
+
const missing = new Map();
|
|
182
|
+
for (const rule of rules) {
|
|
183
|
+
const parts = rule.split(',');
|
|
184
|
+
const tailIndex = parts.at(-1)?.trim() === 'no-resolve' ? parts.length - 2 : parts.length - 1;
|
|
185
|
+
const target = parts[tailIndex]?.trim();
|
|
186
|
+
if (!target) {
|
|
187
|
+
rewritten.push(rule);
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
const renamed = RENAME_GROUPS.get(target);
|
|
191
|
+
if (renamed) {
|
|
192
|
+
parts[tailIndex] = renamed;
|
|
193
|
+
rewritten.push(parts.join(','));
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
// 目标组在新骨架里不存在 —— 改指向兜底组并记录
|
|
197
|
+
if (!known.has(target)) {
|
|
198
|
+
missing.set(target, (missing.get(target) ?? 0) + 1);
|
|
199
|
+
parts[tailIndex] = '兜底分流';
|
|
200
|
+
rewritten.push(parts.join(','));
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
rewritten.push(rule);
|
|
204
|
+
}
|
|
205
|
+
return { rules: rewritten, missing };
|
|
206
|
+
}
|
|
207
|
+
// ---- 从 ParsedYaml 收敛字段的守卫:旧配置字段类型不受信任 ----
|
|
208
|
+
function isRecord(value) {
|
|
209
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
210
|
+
}
|
|
211
|
+
function isString(value) {
|
|
212
|
+
return typeof value === 'string';
|
|
213
|
+
}
|
|
214
|
+
function isNumber(value) {
|
|
215
|
+
return typeof value === 'number' && Number.isFinite(value);
|
|
216
|
+
}
|
|
217
|
+
function isBoolean(value) {
|
|
218
|
+
return typeof value === 'boolean';
|
|
219
|
+
}
|
|
220
|
+
function isStringArray(value) {
|
|
221
|
+
return Array.isArray(value) && value.every(isString);
|
|
222
|
+
}
|
|
223
|
+
function pickString(source, key, fallback) {
|
|
224
|
+
const value = source[key];
|
|
225
|
+
return isString(value) && value !== '' ? value : fallback;
|
|
226
|
+
}
|
|
227
|
+
function pickBool(source, key, fallback) {
|
|
228
|
+
const value = source[key];
|
|
229
|
+
return isBoolean(value) ? value : fallback;
|
|
230
|
+
}
|
|
231
|
+
function pickNumber(source, key, fallback) {
|
|
232
|
+
const value = source[key];
|
|
233
|
+
return isNumber(value) ? value : fallback;
|
|
234
|
+
}
|
|
235
|
+
function pickRecord(source, key) {
|
|
236
|
+
const value = source[key];
|
|
237
|
+
return isRecord(value) ? value : undefined;
|
|
238
|
+
}
|
|
239
|
+
function hostOf(url) {
|
|
240
|
+
if (!url)
|
|
241
|
+
return undefined;
|
|
242
|
+
try {
|
|
243
|
+
return new URL(url).hostname;
|
|
244
|
+
}
|
|
245
|
+
catch {
|
|
246
|
+
return undefined;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* 构造完整的新配置对象,保留旧配置里所有需要维护的手工设置。
|
|
251
|
+
* 问题不抛异常、不打印,统一进 warnings(设计稿 4.2)。
|
|
252
|
+
*/
|
|
253
|
+
export function buildSkeleton(oldConfig, subscriptions, options = {}) {
|
|
254
|
+
// DNS 接管开关与 mixed-port 等字段同语义:缺省从旧配置派生(设置页关掉后,
|
|
255
|
+
// 后续增删订阅不会被静默翻回来);显式传参(旧测试/调用方)行为不变
|
|
256
|
+
const oldDns = pickRecord(oldConfig, 'dns');
|
|
257
|
+
const enableDns = options.enableDns ?? (oldDns ? oldDns['enable'] !== false : true);
|
|
258
|
+
const testUrl = options.testUrl ?? TEST_URL;
|
|
259
|
+
const regions = options.regions ?? REGIONS;
|
|
260
|
+
const groups = buildGroups(subscriptions, regions, testUrl);
|
|
261
|
+
// 用户手写的 relay 组不属于骨架生成物:订阅事务重新生成配置时按名保留,
|
|
262
|
+
// 同名以用户原配置为准;未知字段一起保留,不依赖未启用的编辑器。
|
|
263
|
+
const withRelays = preserveLegacyRelayGroups(groups, oldConfig['proxy-groups']);
|
|
264
|
+
const groupNames = withRelays.map((group) => String(group.name));
|
|
265
|
+
const oldRules = isStringArray(oldConfig.rules) ? oldConfig.rules : [];
|
|
266
|
+
const { rules: rewritten, missing } = rewriteRules(oldRules, groupNames);
|
|
267
|
+
// 订阅域名的 DIRECT 规则必须在最前面。去重之外还要剥离「已删除订阅」
|
|
268
|
+
// 遗留的直连规则:上一轮清单里有、这一轮没有的主机名,其规则是孤儿
|
|
269
|
+
// (手工为无关域名添加的 DIRECT 规则不受影响)。
|
|
270
|
+
const directRules = subscriptionDirectRules(subscriptions);
|
|
271
|
+
const directSet = new Set(directRules);
|
|
272
|
+
const currentHosts = new Set(subscriptions.map((sub) => hostOf(sub.url)).filter((host) => host !== undefined));
|
|
273
|
+
const staleRules = new Set((options.previousSubscriptions ?? [])
|
|
274
|
+
.map((sub) => hostOf(sub.url))
|
|
275
|
+
.filter((host) => host !== undefined && !currentHosts.has(host))
|
|
276
|
+
.map((host) => `DOMAIN,${host},DIRECT`));
|
|
277
|
+
const rules = [
|
|
278
|
+
...directRules,
|
|
279
|
+
...rewritten.filter((rule) => !directSet.has(rule) && !staleRules.has(rule)),
|
|
280
|
+
];
|
|
281
|
+
// dns 段整体保留,只按需翻开 enable 并替换上游
|
|
282
|
+
const dns = { ...(pickRecord(oldConfig, 'dns') ?? {}) };
|
|
283
|
+
const dnsWasDisabled = dns['enable'] === false;
|
|
284
|
+
if (enableDns) {
|
|
285
|
+
dns['enable'] = true;
|
|
286
|
+
// 上游必须换成本机实测可达的,照搬旧值会导致 bootstrap 失败
|
|
287
|
+
Object.assign(dns, DNS_UPSTREAM);
|
|
288
|
+
// respect-rules 让 DNS 查询遵循 rules 分流,配合非空 proxy-server-nameserver 使用
|
|
289
|
+
dns['respect-rules'] = true;
|
|
290
|
+
}
|
|
291
|
+
const skeleton = {
|
|
292
|
+
// 通用设置:沿用旧值,端口与控制口保持不变以免破坏现有 proxy_on 与本工具
|
|
293
|
+
'mixed-port': pickNumber(oldConfig, 'mixed-port', 17890),
|
|
294
|
+
'allow-lan': pickBool(oldConfig, 'allow-lan', false),
|
|
295
|
+
'bind-address': pickString(oldConfig, 'bind-address', '127.0.0.1'),
|
|
296
|
+
ipv6: pickBool(oldConfig, 'ipv6', true),
|
|
297
|
+
mode: pickString(oldConfig, 'mode', 'rule'),
|
|
298
|
+
'log-level': pickString(oldConfig, 'log-level', 'info'),
|
|
299
|
+
'external-controller': pickString(oldConfig, 'external-controller', '127.0.0.1:19090'),
|
|
300
|
+
'tcp-concurrent': pickBool(oldConfig, 'tcp-concurrent', true),
|
|
301
|
+
'unified-delay': pickBool(oldConfig, 'unified-delay', true),
|
|
302
|
+
'keep-alive-interval': pickNumber(oldConfig, 'keep-alive-interval', 15),
|
|
303
|
+
'keep-alive-idle': pickNumber(oldConfig, 'keep-alive-idle', 300),
|
|
304
|
+
...(pickRecord(oldConfig, 'experimental') ? { experimental: pickRecord(oldConfig, 'experimental') } : {}),
|
|
305
|
+
...(pickRecord(oldConfig, 'profile') ? { profile: pickRecord(oldConfig, 'profile') } : {}),
|
|
306
|
+
...(pickRecord(oldConfig, 'sniffer') ? { sniffer: pickRecord(oldConfig, 'sniffer') } : {}),
|
|
307
|
+
dns,
|
|
308
|
+
'proxy-providers': buildProviders(subscriptions, testUrl),
|
|
309
|
+
'proxy-groups': withRelays,
|
|
310
|
+
...(pickRecord(oldConfig, 'rule-providers')
|
|
311
|
+
? { 'rule-providers': pickRecord(oldConfig, 'rule-providers') }
|
|
312
|
+
: {}),
|
|
313
|
+
rules,
|
|
314
|
+
};
|
|
315
|
+
const warnings = [];
|
|
316
|
+
for (const [name, count] of [...missing].sort((a, b) => b[1] - a[1])) {
|
|
317
|
+
warnings.push(`rules 中 ${count} 条指向的组 '${name}' 已不存在,改指向'兜底分流'`);
|
|
318
|
+
}
|
|
319
|
+
if (dnsWasDisabled && enableDns) {
|
|
320
|
+
warnings.push('dns.enable: false → true,旧配置中的 nameserver-policy 将开始生效,DNS 上游已替换为本机实测可达地址');
|
|
321
|
+
}
|
|
322
|
+
return { skeleton, warnings };
|
|
323
|
+
}
|
|
324
|
+
//# sourceMappingURL=skeleton.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skeleton.js","sourceRoot":"","sources":["../../src/config/skeleton.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 订阅生命周期编排(设计稿 §6,补齐其 3.2 依赖图中缺失的 SubscriptionService 层)。
|
|
3
|
+
*
|
|
4
|
+
* 事务模型:订阅清单(subscriptions.json)、config.yaml、mihomo 服务三者
|
|
5
|
+
* 必须一起变更或一起回到原状。任何一步失败按设计稿 6.1 回滚表恢复:
|
|
6
|
+
* - 动过 config.yaml → 从备份回滚并重启服务;
|
|
7
|
+
* - 动过清单 → 从内存中的旧清单恢复;
|
|
8
|
+
* - 回滚本身失败 → 把原错误与回滚错误一起上抛,绝不静默。
|
|
9
|
+
*
|
|
10
|
+
* 本模块不直接被 TUI 之外的入口调用;进度通过 onProgress 上报,
|
|
11
|
+
* 重启后的 1.5s 宽限等待与 sleep 都可注入,测试无需真实计时。
|
|
12
|
+
*/
|
|
13
|
+
import YAML from 'yaml';
|
|
14
|
+
import { existsSync, writeFileSync } from 'node:fs';
|
|
15
|
+
import { ConfigManager } from './manager.js';
|
|
16
|
+
import { ServiceManager } from './service.js';
|
|
17
|
+
import { buildSkeleton } from './skeleton.js';
|
|
18
|
+
import { isDuplicateName, loadSubscriptions, prefixFromName, saveSubscriptions, validateSubscription, } from './subscriptions.js';
|
|
19
|
+
export class SubscriptionError extends Error {
|
|
20
|
+
step;
|
|
21
|
+
detail;
|
|
22
|
+
constructor(message, step, detail) {
|
|
23
|
+
super(message);
|
|
24
|
+
this.step = step;
|
|
25
|
+
this.detail = detail;
|
|
26
|
+
this.name = 'SubscriptionError';
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export const ADD_STEPS = [
|
|
30
|
+
{ key: 'validate', label: '校验输入' },
|
|
31
|
+
{ key: 'manifest', label: '更新订阅清单' },
|
|
32
|
+
{ key: 'generate', label: '生成新配置' },
|
|
33
|
+
{ key: 'backup', label: '备份旧配置' },
|
|
34
|
+
{ key: 'write', label: '校验并写入' },
|
|
35
|
+
{ key: 'restart', label: '重启服务' },
|
|
36
|
+
{ key: 'verify', label: '确认服务状态' },
|
|
37
|
+
];
|
|
38
|
+
export const DELETE_STEPS = [
|
|
39
|
+
...ADD_STEPS,
|
|
40
|
+
{ key: 'cache', label: '清理订阅缓存' },
|
|
41
|
+
];
|
|
42
|
+
/** 编辑与新增共用同一套步骤(差异只在第 1 步的校验内容) */
|
|
43
|
+
export const EDIT_STEPS = ADD_STEPS;
|
|
44
|
+
const defaultSleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
45
|
+
/**
|
|
46
|
+
* 从应用配置构造事务依赖。mihomoDir 来自 config.json(v0.2.0 起是关键配置,
|
|
47
|
+
* 指向内核配置目录);测试通过 overrides 注入临时目录与 stub 二进制。
|
|
48
|
+
*/
|
|
49
|
+
export function depsFromAppConfig(config, overrides = {}) {
|
|
50
|
+
return {
|
|
51
|
+
manager: new ConfigManager(config.mihomoDir, config.mihomoBin),
|
|
52
|
+
service: new ServiceManager(),
|
|
53
|
+
...overrides,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function msg(err) {
|
|
57
|
+
return err instanceof Error ? err.message : String(err);
|
|
58
|
+
}
|
|
59
|
+
function sortSubs(subs) {
|
|
60
|
+
return subs.slice().sort((a, b) => a.name.localeCompare(b.name));
|
|
61
|
+
}
|
|
62
|
+
/** 三个入口的第一步都是校验:在改动任何文件之前发出该步进度 */
|
|
63
|
+
function emitValidate(deps, steps) {
|
|
64
|
+
const first = steps[0];
|
|
65
|
+
if (first?.key === 'validate')
|
|
66
|
+
deps.onProgress?.(first, []);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* 事务主体:从「写入订阅清单」开始到「确认服务状态」结束。
|
|
70
|
+
* oldSubs 由调用方在改文件之前取好,失败回滚用的就是它。
|
|
71
|
+
*/
|
|
72
|
+
async function applyChange(oldSubs, newSubs, steps, deps) {
|
|
73
|
+
const manager = deps.manager ?? new ConfigManager();
|
|
74
|
+
const service = deps.service ?? new ServiceManager();
|
|
75
|
+
const subsPath = deps.subscriptionsPath;
|
|
76
|
+
const sleep = deps.sleep ?? defaultSleep;
|
|
77
|
+
const graceMs = deps.graceMs ?? 1500;
|
|
78
|
+
const onProgress = deps.onProgress;
|
|
79
|
+
const completed = [];
|
|
80
|
+
// 第 1 步(校验输入)由入口函数在调用本事务之前完成并上报
|
|
81
|
+
if (steps[0]?.key === 'validate')
|
|
82
|
+
completed.push(steps[0]);
|
|
83
|
+
const begin = (key) => {
|
|
84
|
+
const step = steps.find((candidate) => candidate.key === key);
|
|
85
|
+
if (step)
|
|
86
|
+
onProgress?.(step, [...completed]);
|
|
87
|
+
};
|
|
88
|
+
const finish = (key) => {
|
|
89
|
+
const step = steps.find((candidate) => candidate.key === key);
|
|
90
|
+
if (step)
|
|
91
|
+
completed.push(step);
|
|
92
|
+
};
|
|
93
|
+
let backupPath;
|
|
94
|
+
let configWritten = false;
|
|
95
|
+
let manifestWritten = false;
|
|
96
|
+
try {
|
|
97
|
+
begin('manifest');
|
|
98
|
+
saveSubscriptions(newSubs, subsPath);
|
|
99
|
+
manifestWritten = true;
|
|
100
|
+
finish('manifest');
|
|
101
|
+
begin('generate');
|
|
102
|
+
const old = manager.loadConfig();
|
|
103
|
+
// oldSubs 作为「上一轮清单」传入:删除订阅时同步剥离其置顶直连规则
|
|
104
|
+
const { skeleton, warnings } = buildSkeleton(old, newSubs, {
|
|
105
|
+
previousSubscriptions: oldSubs,
|
|
106
|
+
});
|
|
107
|
+
const yamlText = YAML.stringify(skeleton, { lineWidth: 0, singleQuote: true });
|
|
108
|
+
finish('generate');
|
|
109
|
+
begin('backup');
|
|
110
|
+
backupPath = manager.backup();
|
|
111
|
+
finish('backup');
|
|
112
|
+
begin('write');
|
|
113
|
+
const pre = manager.validate(yamlText);
|
|
114
|
+
if (!pre.ok) {
|
|
115
|
+
throw new SubscriptionError('生成的配置未通过校验,已中止写入', 'write', pre.output);
|
|
116
|
+
}
|
|
117
|
+
manager.writeConfig(yamlText);
|
|
118
|
+
configWritten = true;
|
|
119
|
+
const post = manager.validate(manager.readConfigText());
|
|
120
|
+
if (!post.ok) {
|
|
121
|
+
manager.rollback(backupPath);
|
|
122
|
+
configWritten = false;
|
|
123
|
+
throw new SubscriptionError('配置写入后校验失败(配置已回滚)', 'write', post.output);
|
|
124
|
+
}
|
|
125
|
+
manager.ensureProvidersDir();
|
|
126
|
+
finish('write');
|
|
127
|
+
begin('restart');
|
|
128
|
+
await service.restart();
|
|
129
|
+
await sleep(graceMs);
|
|
130
|
+
finish('restart');
|
|
131
|
+
begin('verify');
|
|
132
|
+
if (!(await service.isActive())) {
|
|
133
|
+
// 服务起不来:完整回滚(配置 + 清单)并再重启,把 systemctl status 带给用户
|
|
134
|
+
manager.rollback(backupPath);
|
|
135
|
+
configWritten = false;
|
|
136
|
+
saveSubscriptions(oldSubs, subsPath);
|
|
137
|
+
manifestWritten = false;
|
|
138
|
+
await service.restart();
|
|
139
|
+
throw new SubscriptionError('服务启动失败(已回滚)', 'verify', await service.status());
|
|
140
|
+
}
|
|
141
|
+
finish('verify');
|
|
142
|
+
return { warnings };
|
|
143
|
+
}
|
|
144
|
+
catch (err) {
|
|
145
|
+
// 兜底回滚(设计稿 6.1):动了配置才回滚配置并重启;清单尽量恢复原状
|
|
146
|
+
const problems = [];
|
|
147
|
+
if (configWritten && backupPath) {
|
|
148
|
+
try {
|
|
149
|
+
manager.rollback(backupPath);
|
|
150
|
+
await service.restart();
|
|
151
|
+
}
|
|
152
|
+
catch (rollbackErr) {
|
|
153
|
+
problems.push(msg(rollbackErr));
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
if (manifestWritten) {
|
|
157
|
+
try {
|
|
158
|
+
saveSubscriptions(oldSubs, subsPath);
|
|
159
|
+
}
|
|
160
|
+
catch (rollbackErr) {
|
|
161
|
+
problems.push(msg(rollbackErr));
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
if (problems.length > 0) {
|
|
165
|
+
throw new SubscriptionError(`${msg(err)};回滚失败:${problems.join(';')}`, err instanceof SubscriptionError ? err.step : 'write');
|
|
166
|
+
}
|
|
167
|
+
throw err;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
/** 新增订阅。任何文件被改动之前先完成全部输入校验。 */
|
|
171
|
+
export async function addSubscription(input, deps = {}) {
|
|
172
|
+
const oldSubs = loadSubscriptions(deps.subscriptionsPath);
|
|
173
|
+
emitValidate(deps, ADD_STEPS);
|
|
174
|
+
const validated = validateSubscription({ ...input, prefix: prefixFromName(input.name) });
|
|
175
|
+
if (!validated.ok) {
|
|
176
|
+
throw new SubscriptionError(validated.error, 'validate');
|
|
177
|
+
}
|
|
178
|
+
if (isDuplicateName(validated.data.name, oldSubs)) {
|
|
179
|
+
throw new SubscriptionError(`订阅名称 "${validated.data.name}" 已存在`, 'validate');
|
|
180
|
+
}
|
|
181
|
+
const manager = deps.manager ?? new ConfigManager();
|
|
182
|
+
// 本地订阅需要一份内容文件作为 file provider 的起点
|
|
183
|
+
if (validated.data.type === 'local')
|
|
184
|
+
ensureLocalProxyFile(manager, validated.data.name);
|
|
185
|
+
return applyChange(oldSubs, sortSubs([...oldSubs, validated.data]), ADD_STEPS, deps);
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* 编辑订阅:可改名称(重命名)、类型、URL、前缀、分组、更新间隔与锁定状态。
|
|
189
|
+
* 重命名会迁移 provider 缓存文件,失败时连同配置一起回滚。
|
|
190
|
+
*/
|
|
191
|
+
export async function editSubscription(name, input, deps = {}) {
|
|
192
|
+
const oldSubs = loadSubscriptions(deps.subscriptionsPath);
|
|
193
|
+
emitValidate(deps, EDIT_STEPS);
|
|
194
|
+
const target = oldSubs.find((sub) => sub.name === name);
|
|
195
|
+
if (!target) {
|
|
196
|
+
throw new SubscriptionError(`订阅 "${name}" 不存在`, 'validate');
|
|
197
|
+
}
|
|
198
|
+
const merged = { ...target };
|
|
199
|
+
if (input.name !== undefined)
|
|
200
|
+
merged.name = input.name;
|
|
201
|
+
if (input.type !== undefined)
|
|
202
|
+
merged.type = input.type;
|
|
203
|
+
if (input.url !== undefined)
|
|
204
|
+
merged.url = input.url;
|
|
205
|
+
if (input.group !== undefined)
|
|
206
|
+
merged.group = input.group;
|
|
207
|
+
if (input.interval !== undefined)
|
|
208
|
+
merged.interval = input.interval;
|
|
209
|
+
if (input.locked !== undefined)
|
|
210
|
+
merged.locked = input.locked === null ? undefined : input.locked;
|
|
211
|
+
// 前缀由名字派生,不接受手工设置;名字变了前缀跟着变
|
|
212
|
+
if (merged.name)
|
|
213
|
+
merged.prefix = prefixFromName(merged.name);
|
|
214
|
+
// 切到本地类型时清空远程专属字段
|
|
215
|
+
if (merged.type === 'local') {
|
|
216
|
+
merged.url = undefined;
|
|
217
|
+
merged.interval = undefined;
|
|
218
|
+
merged.locked = undefined;
|
|
219
|
+
}
|
|
220
|
+
const validated = validateSubscription(merged);
|
|
221
|
+
if (!validated.ok) {
|
|
222
|
+
throw new SubscriptionError(validated.error, 'validate');
|
|
223
|
+
}
|
|
224
|
+
const next = validated.data;
|
|
225
|
+
const renaming = next.name !== target.name;
|
|
226
|
+
if (renaming && isDuplicateName(next.name, oldSubs.filter((sub) => sub.name !== name))) {
|
|
227
|
+
throw new SubscriptionError(`订阅名称 "${next.name}" 已存在`, 'validate');
|
|
228
|
+
}
|
|
229
|
+
if (JSON.stringify(next) === JSON.stringify(target)) {
|
|
230
|
+
return { warnings: [], unchanged: true };
|
|
231
|
+
}
|
|
232
|
+
const manager = deps.manager ?? new ConfigManager();
|
|
233
|
+
if (renaming)
|
|
234
|
+
manager.renameProviderCache(target.name, next.name);
|
|
235
|
+
if (next.type === 'local')
|
|
236
|
+
ensureLocalProxyFile(manager, next.name);
|
|
237
|
+
try {
|
|
238
|
+
const newSubs = oldSubs.map((sub) => (sub.name === name ? next : sub));
|
|
239
|
+
const result = await applyChange(oldSubs, sortSubs(newSubs), EDIT_STEPS, deps);
|
|
240
|
+
return { ...result, unchanged: false };
|
|
241
|
+
}
|
|
242
|
+
catch (err) {
|
|
243
|
+
// 事务失败:把已改名的缓存文件改回去(applyChange 只管 config 与清单)
|
|
244
|
+
if (renaming) {
|
|
245
|
+
try {
|
|
246
|
+
manager.renameProviderCache(next.name, target.name);
|
|
247
|
+
}
|
|
248
|
+
catch {
|
|
249
|
+
// 改回失败不掩盖原始错误
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
throw err;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
/** 编辑订阅前缀的兼容入口(只改前缀,名称与 URL 不动)。 */
|
|
256
|
+
export async function editSubscriptionPrefix(name, prefix, deps = {}) {
|
|
257
|
+
return editSubscription(name, { prefix }, deps);
|
|
258
|
+
}
|
|
259
|
+
/** 本地订阅的内容文件:不存在时创建空的 proxies 骨架。 */
|
|
260
|
+
function ensureLocalProxyFile(manager, name) {
|
|
261
|
+
manager.ensureProvidersDir();
|
|
262
|
+
const path = manager.providerCachePath(name);
|
|
263
|
+
if (!existsSync(path))
|
|
264
|
+
writeFileSync(path, 'proxies: []\n', 'utf8');
|
|
265
|
+
}
|
|
266
|
+
/** 删除订阅(含清理 provider 缓存文件;最后一个订阅不允许删)。 */
|
|
267
|
+
export async function deleteSubscription(name, deps = {}) {
|
|
268
|
+
const oldSubs = loadSubscriptions(deps.subscriptionsPath);
|
|
269
|
+
emitValidate(deps, DELETE_STEPS);
|
|
270
|
+
if (!oldSubs.some((sub) => sub.name === name)) {
|
|
271
|
+
throw new SubscriptionError(`订阅 "${name}" 不存在`, 'validate');
|
|
272
|
+
}
|
|
273
|
+
if (oldSubs.length <= 1) {
|
|
274
|
+
throw new SubscriptionError('不能删除最后一个订阅', 'validate');
|
|
275
|
+
}
|
|
276
|
+
const result = await applyChange(oldSubs, oldSubs.filter((sub) => sub.name !== name), DELETE_STEPS, deps);
|
|
277
|
+
const manager = deps.manager ?? new ConfigManager();
|
|
278
|
+
manager.deleteProviderCache(name);
|
|
279
|
+
const cacheStep = DELETE_STEPS.at(-1);
|
|
280
|
+
if (cacheStep)
|
|
281
|
+
deps.onProgress?.(cacheStep, DELETE_STEPS.slice(0, -1));
|
|
282
|
+
return result;
|
|
283
|
+
}
|
|
284
|
+
//# sourceMappingURL=subscriptionService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subscriptionService.js","sourceRoot":"","sources":["../../src/config/subscriptionService.ts"],"names":[],"mappings":"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"}
|