mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-11-14 20:48:17 +08:00
🎨 Sync 2026-10-29 20:07:12
This commit is contained in:
1 parent
0546d1d8e4
commit
737acd5a18
182 files changed
+10470
-1649
No files matched your search
@@ -103,10 +103,11 @@ function isSetName(s) {
|
||||
return view.extend({
|
||||
load: function() {
|
||||
/* 配置字段走 conf get;数据源健康度走 health --json。
|
||||
health 失败不能拖垮整个配置页:捕获后渲染成“读取失败”。 */
|
||||
health/policy-check 失败不能拖垮整个配置页:捕获后渲染成“读取失败”。 */
|
||||
return Promise.all([
|
||||
run(['conf', 'get']),
|
||||
fs.exec(CMD, ['health', '--json']).catch(function() { return null; })
|
||||
fs.exec(CMD, ['health', '--json']).catch(function() { return null; }),
|
||||
fs.exec(CMD, ['policy-check', '--json']).catch(function() { return null; })
|
||||
]);
|
||||
},
|
||||
|
||||
@@ -119,13 +120,24 @@ return view.extend({
|
||||
} catch (e) {
|
||||
health = null;
|
||||
}
|
||||
/* BUG-25:首装用户 mwan3 里没有 wan_only/wanb_only 策略时,
|
||||
wire 会静默失效。这里在页面顶部给一条醒目提示 + 一键 init。 */
|
||||
var pcheck = null;
|
||||
try {
|
||||
pcheck = JSON.parse(((res[2] && res[2].stdout) || '').trim() || 'null');
|
||||
} catch (e) {
|
||||
pcheck = null;
|
||||
}
|
||||
|
||||
var model = {
|
||||
mode: 'bind',
|
||||
policies: [],
|
||||
isp: [],
|
||||
dns: [],
|
||||
domains: []
|
||||
domains: [],
|
||||
/* lc:begin */
|
||||
lc: { enable: true, geo: true, provider: 'auto', interval: 60 }
|
||||
/* lc:end */
|
||||
};
|
||||
|
||||
out.split('\n').forEach(function(line) {
|
||||
@@ -146,6 +158,16 @@ return view.extend({
|
||||
model.dns.push({ policy: f[1], dns: f[2], test: null });
|
||||
else if (f[0] === 'domain' && f.length >= 3)
|
||||
model.domains.push({ domain: f[1], policy: f[2] });
|
||||
/* lc:begin */
|
||||
else if (f[0] === 'line_check_enable')
|
||||
model.lc.enable = (f[1] || '1') === '1';
|
||||
else if (f[0] === 'line_check_geo')
|
||||
model.lc.geo = (f[1] || '1') === '1';
|
||||
else if (f[0] === 'line_check_provider')
|
||||
model.lc.provider = f[1] || 'auto';
|
||||
else if (f[0] === 'line_check_interval')
|
||||
model.lc.interval = parseInt(f[1], 10) || 60;
|
||||
/* lc:end */
|
||||
});
|
||||
|
||||
if (!model.isp.length) {
|
||||
@@ -528,6 +550,86 @@ return view.extend({
|
||||
}
|
||||
}, [ svgIcon('plus'), E('span', {}, [_('添加域名')]) ]);
|
||||
|
||||
/* lc:begin */
|
||||
/* ============================================================ 三、线路体检 */
|
||||
|
||||
var lcEnable = E('input', { 'type': 'checkbox', 'checked': model.lc.enable ? '' : null });
|
||||
lcEnable.addEventListener('change', function() { model.lc.enable = lcEnable.checked; });
|
||||
|
||||
var lcGeo = E('input', { 'type': 'checkbox', 'checked': model.lc.geo ? '' : null });
|
||||
lcGeo.addEventListener('change', function() {
|
||||
model.lc.geo = lcGeo.checked;
|
||||
lcProvider.disabled = !lcGeo.checked;
|
||||
});
|
||||
|
||||
var lcProvider = E('select', { 'class': 'cbi-input-select', 'disabled': model.lc.geo ? null : '' },
|
||||
[
|
||||
['auto', _('自动(百度优先,失败转 ip-api)')],
|
||||
['baidu', _('只用百度(HTTPS)')],
|
||||
['ip-api', _('只用 ip-api(HTTP,明文)')],
|
||||
['off', _('关闭')]
|
||||
].map(function(o) {
|
||||
return E('option', { 'value': o[0], 'selected': model.lc.provider === o[0] ? '' : null }, [ o[1] ]);
|
||||
}));
|
||||
lcProvider.addEventListener('change', function() { model.lc.provider = lcProvider.value; });
|
||||
|
||||
var lcInterval = E('input', {
|
||||
'type': 'number', 'class': 'cbi-input-text', 'min': '15', 'max': '3600',
|
||||
'value': String(model.lc.interval || 60)
|
||||
});
|
||||
var lcIntervalErr = errBox();
|
||||
lcInterval.addEventListener('input', function() {
|
||||
var n = parseInt(lcInterval.value, 10);
|
||||
model.lc.interval = isNaN(n) ? 0 : n;
|
||||
if (model.lc.interval < 15 || model.lc.interval > 3600) {
|
||||
setErr(lcIntervalErr, _('必须是 15-3600 之间的整数秒'));
|
||||
model.lc._valid = false;
|
||||
} else {
|
||||
setErr(lcIntervalErr, '');
|
||||
model.lc._valid = true;
|
||||
}
|
||||
});
|
||||
model.lc._valid = !(model.lc.interval < 15 || model.lc.interval > 3600);
|
||||
|
||||
var lcSection = E('section', { 'class': 'isp-section' }, [
|
||||
E('h3', {}, [ _('线路体检') ]),
|
||||
E('p', { 'class': 'isp-desc' }, [
|
||||
_('对每条 WAN 做出口类型、四段延迟、NAT 与归属地检查。体检只读,不修改 mwan3/nft 规则。')
|
||||
]),
|
||||
E('table', { 'class': 'isp-form-table' }, [
|
||||
E('tbody', {}, [
|
||||
E('tr', {}, [
|
||||
E('td', { 'data-th': _('启用') }, [
|
||||
E('label', { 'class': 'isp-switch' }, [ lcEnable, E('span') ]),
|
||||
E('div', { 'class': 'lc-hint' }, [ _('关闭后命令行与页面都不再执行体检') ])
|
||||
]),
|
||||
E('td', { 'data-th': _('归属地查询') }, [
|
||||
E('label', { 'class': 'isp-switch' }, [ lcGeo, E('span') ]),
|
||||
E('div', { 'class': 'lc-hint' }, [
|
||||
_('关闭后只停止归属地查询;出口 IP 回显、STUN 与延迟探测仍会发出。卡片显示“未查询”')
|
||||
])
|
||||
])
|
||||
]),
|
||||
E('tr', {}, [
|
||||
E('td', { 'data-th': _('归属地服务商') }, [ lcProvider ]),
|
||||
E('td', { 'data-th': _('最小间隔') }, [
|
||||
E('div', { 'class': 'lc-inline' }, [
|
||||
lcInterval, E('span', { 'class': 'lc-hint' }, [ _('秒(15-3600)') ])
|
||||
]),
|
||||
lcIntervalErr
|
||||
])
|
||||
])
|
||||
])
|
||||
]),
|
||||
E('div', { 'class': 'isp-note lc-privacy-note' }, [
|
||||
_('隐私说明:开启归属地后,体检会把每条 WAN 的出口公网 IP 发送给第三方(默认百度,失败时 ip-api.com)查询归属地。'),
|
||||
_('只发送出口 IP,不发送内网地址、设备名、MAC、流量内容。ip-api 使用 HTTP,传输不加密;介意时请选择“关闭”或“只用百度”。'),
|
||||
_('关闭后省/市显示“未查询”,但仍会用本地 @isp_* 集合判断运营商大类(本机判断,不外发)。'),
|
||||
_('注意:关闭归属地只停止归属地查询;出口 IP 回显、STUN 与延迟探测仍会按配置的服务器发出。要完全停发,请关闭“启用”。')
|
||||
])
|
||||
]);
|
||||
/* lc:end */
|
||||
|
||||
/* ============================================================ 四、数据源(只读) */
|
||||
|
||||
/* 数据源地址是 root 路径的下载入口,配置页不开放编辑:
|
||||
@@ -616,7 +718,13 @@ return view.extend({
|
||||
isp: ispVal,
|
||||
dns: dnsVal,
|
||||
dom: domVal,
|
||||
mode: modeSel.value
|
||||
mode: modeSel.value,
|
||||
/* lc:begin */
|
||||
lcEnable: model.lc.enable ? '1' : '0',
|
||||
lcGeo: model.lc.geo ? '1' : '0',
|
||||
lcProvider: model.lc.provider,
|
||||
lcInterval: String(model.lc.interval || 60)
|
||||
/* lc:end */
|
||||
};
|
||||
}
|
||||
|
||||
@@ -633,6 +741,14 @@ return view.extend({
|
||||
]), 'danger');
|
||||
ok = false;
|
||||
}
|
||||
/* lc:begin */
|
||||
if (!model.lc._valid) {
|
||||
ui.addNotification(null, E('p', {}, [
|
||||
_('线路体检的最小间隔必须是 15-3600 之间的整数秒')
|
||||
]), 'danger');
|
||||
ok = false;
|
||||
}
|
||||
/* lc:end */
|
||||
return ok;
|
||||
}
|
||||
|
||||
@@ -675,6 +791,12 @@ return view.extend({
|
||||
'dns_table', v.dns,
|
||||
'domain_split', v.dom,
|
||||
'dns_mode', v.mode
|
||||
/* lc:begin */
|
||||
,'line_check_enable', v.lcEnable,
|
||||
'line_check_geo', v.lcGeo,
|
||||
'line_check_provider', v.lcProvider,
|
||||
'line_check_interval', v.lcInterval
|
||||
/* lc:end */
|
||||
]).then(function(r) {
|
||||
showResult(((r.stdout || '') + (r.stderr || '')) || '(无输出)');
|
||||
ui.addNotification(null, E('p', {}, [_('预检完成,配置未被修改')]), 'info');
|
||||
@@ -698,6 +820,12 @@ return view.extend({
|
||||
.then(function() { return step(['conf', 'set', 'dns_table', v.dns], log); })
|
||||
.then(function() { return step(['conf', 'set', 'domain_split', v.dom], log); })
|
||||
.then(function() { return step(['conf', 'set', 'dns_mode', v.mode], log); })
|
||||
/* lc:begin */
|
||||
.then(function() { return step(['conf', 'set', 'line_check_enable', v.lcEnable], log); })
|
||||
.then(function() { return step(['conf', 'set', 'line_check_geo', v.lcGeo], log); })
|
||||
.then(function() { return step(['conf', 'set', 'line_check_provider', v.lcProvider], log); })
|
||||
.then(function() { return step(['conf', 'set', 'line_check_interval', v.lcInterval], log); })
|
||||
/* lc:end */
|
||||
.then(function() {
|
||||
if (!apply)
|
||||
return null;
|
||||
@@ -719,6 +847,48 @@ return view.extend({
|
||||
});
|
||||
}
|
||||
|
||||
/* BUG-25:策略缺失/空成员时页面顶部的醒目提示条。
|
||||
只有 policy-check 明确返回 ok=false 才显示;读不到(旧版脚本/命令失败)
|
||||
时保持安静,不用一个误报吓用户。 */
|
||||
var policyBanner = E('div', { 'class': 'isp-banner-wrap' });
|
||||
if (pcheck && pcheck.ok === false) {
|
||||
var bad = (pcheck.policies || []).filter(function(p) { return !p.ok; });
|
||||
var badText = bad.map(function(p) {
|
||||
return p.name + '(' + (p.detail || _('不可用')) + ')';
|
||||
}).join('、');
|
||||
var bannerErr = E('div', { 'class': 'isp-banner-err' });
|
||||
var bannerBtn;
|
||||
bannerBtn = E('button', {
|
||||
'class': 'cbi-button cbi-button-action isp-banner-btn',
|
||||
'click': function() {
|
||||
bannerBtn.disabled = true;
|
||||
bannerBtn.textContent = _('正在初始化…');
|
||||
bannerErr.textContent = '';
|
||||
return run(['init', '--assume-yes']).then(function() {
|
||||
ui.addNotification(null, E('p', {}, [ _('已绑定线路,页面将刷新') ]), 'info');
|
||||
window.location.reload();
|
||||
}).catch(function(e) {
|
||||
bannerBtn.disabled = false;
|
||||
bannerBtn.textContent = _('重新尝试初始化');
|
||||
bannerErr.textContent = String(e).replace(/^.*失败([^)]*):/, '');
|
||||
});
|
||||
}
|
||||
}, [ _('运行初始化绑定线路') ]);
|
||||
policyBanner = E('div', { 'class': 'isp-banner is-warn' }, [
|
||||
E('div', { 'class': 'isp-banner-msg' }, [
|
||||
E('strong', {}, [ _('mwan3 策略缺失或没有成员:分流规则不会生效') ]),
|
||||
E('div', {}, [ _('需要绑定的策略:') + badText ]),
|
||||
E('div', { 'class': 'isp-banner-hint' }, [
|
||||
_('按钮会按 ISP 表顺序自动绑定(wan_only→wan1、wanb_only→wan2…);也可以 SSH 执行 '),
|
||||
E('code', {}, [ 'isp-split init' ]),
|
||||
_(' 逐个选择接口。')
|
||||
]),
|
||||
bannerErr
|
||||
]),
|
||||
bannerBtn
|
||||
]);
|
||||
}
|
||||
|
||||
/* ============================================================ 页面 */
|
||||
|
||||
return E('div', { 'class': 'isp-app cbi-map' }, [
|
||||
@@ -731,6 +901,8 @@ return view.extend({
|
||||
])
|
||||
]),
|
||||
|
||||
policyBanner,
|
||||
|
||||
/* ---- 策略映射 ---- */
|
||||
E('section', { 'class': 'isp-section' }, [
|
||||
E('h3', {}, [ _('策略映射') ]),
|
||||
@@ -802,6 +974,11 @@ return view.extend({
|
||||
E('div', { 'class': 'isp-btn-row' }, [ addDomBtn ])
|
||||
]),
|
||||
|
||||
/* lc:begin */
|
||||
/* ---- 线路体检 ---- */
|
||||
lcSection,
|
||||
/* lc:end */
|
||||
|
||||
/* ---- 数据源(只读) ---- */
|
||||
E('section', { 'class': 'isp-section' }, [
|
||||
E('h3', {}, [ _('数据源'), E('span', { 'class': 'isp-tag' }, [ _('只读') ]) ]),
|
||||
|
||||
Reference in new issue
Block a user