mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-11-15 04:53:00 +08:00
💋 Sync 2026-11-02 01:02:56
This commit is contained in:
1 parent
d97dc35e3d
commit
2f941f0ffb
48 files changed
+4891
-1002
No files matched your search
@@ -0,0 +1,101 @@
|
||||
'use strict';
|
||||
'require view';
|
||||
'require rpc';
|
||||
'require poll';
|
||||
'require ui';
|
||||
'require dom';
|
||||
|
||||
var callGetLogs = rpc.declare({
|
||||
object: 'luci.cf-optimizer',
|
||||
method: 'get_logs'
|
||||
});
|
||||
|
||||
var callClearLogs = rpc.declare({
|
||||
object: 'luci.cf-optimizer',
|
||||
method: 'clear_logs'
|
||||
});
|
||||
|
||||
return view.extend({
|
||||
handleSaveApply: null,
|
||||
handleSave: null,
|
||||
handleReset: null,
|
||||
|
||||
load: function() {
|
||||
return callGetLogs();
|
||||
},
|
||||
|
||||
handleClearLogs: function(ev) {
|
||||
return callClearLogs().then(function() {
|
||||
var textarea = document.getElementById('cf-log-textarea');
|
||||
if (textarea) textarea.value = '';
|
||||
ui.addNotification(null, E('p', {}, _('日志已清空')), 'info');
|
||||
}).catch(function(e) {
|
||||
ui.addNotification(null, E('p', {}, e.message));
|
||||
});
|
||||
},
|
||||
|
||||
render: function(data) {
|
||||
data = data || {};
|
||||
var lines = Array.isArray(data.logs) ? data.logs : [];
|
||||
var logText = lines.join('\n');
|
||||
|
||||
var textarea = E('textarea', {
|
||||
'id': 'cf-log-textarea',
|
||||
'class': 'cbi-input-textarea',
|
||||
'style': 'width: 100%; height: 480px; font-family: monospace; font-size: 12px; line-height: 1.5; resize: vertical; box-sizing: border-box; padding: 10px; border-radius: 4px;',
|
||||
'readonly': 'readonly',
|
||||
'wrap': 'off'
|
||||
}, logText);
|
||||
|
||||
// 默认滚动到底部
|
||||
setTimeout(function() {
|
||||
textarea.scrollTop = textarea.scrollHeight;
|
||||
}, 100);
|
||||
|
||||
var autoRefreshCheckbox = E('input', {
|
||||
'type': 'checkbox',
|
||||
'id': 'cf-auto-refresh',
|
||||
'checked': 'checked',
|
||||
'style': 'margin-right: 6px; vertical-align: middle;'
|
||||
});
|
||||
|
||||
var controls = E('div', {
|
||||
'style': 'display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;'
|
||||
}, [
|
||||
E('label', { 'style': 'cursor: pointer; user-select: none; font-size: 13px;' }, [
|
||||
autoRefreshCheckbox,
|
||||
_('自动刷新 (每 4 秒)')
|
||||
]),
|
||||
E('button', {
|
||||
'class': 'btn cbi-button cbi-button-remove',
|
||||
'click': ui.createHandlerFn(this, 'handleClearLogs')
|
||||
}, _('清空日志'))
|
||||
]);
|
||||
|
||||
poll.add(function() {
|
||||
var chk = document.getElementById('cf-auto-refresh');
|
||||
if (!chk || !chk.checked) return;
|
||||
|
||||
return callGetLogs().then(function(res) {
|
||||
res = res || {};
|
||||
var ta = document.getElementById('cf-log-textarea');
|
||||
if (ta && Array.isArray(res.logs)) {
|
||||
var wasAtBottom = (ta.scrollHeight - ta.clientHeight <= ta.scrollTop + 50);
|
||||
ta.value = res.logs.join('\n');
|
||||
if (wasAtBottom) {
|
||||
ta.scrollTop = ta.scrollHeight;
|
||||
}
|
||||
}
|
||||
});
|
||||
}, 4);
|
||||
|
||||
return E('div', { 'class': 'cbi-map' }, [
|
||||
E('h2', {}, _('Cloudflare 优选加速') + ' - ' + _('运行日志')),
|
||||
E('div', { 'class': 'cbi-map-descr' }, _('查看后台优选测速与 hosts 重载的实时运行记录。')),
|
||||
E('fieldset', { 'class': 'cbi-section' }, [
|
||||
controls,
|
||||
textarea
|
||||
])
|
||||
]);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,244 @@
|
||||
'use strict';
|
||||
'require view';
|
||||
'require rpc';
|
||||
'require poll';
|
||||
'require ui';
|
||||
'require dom';
|
||||
|
||||
var callGetStatus = rpc.declare({
|
||||
object: 'luci.cf-optimizer',
|
||||
method: 'get_status'
|
||||
});
|
||||
|
||||
var callRunOnce = rpc.declare({
|
||||
object: 'luci.cf-optimizer',
|
||||
method: 'run_once'
|
||||
});
|
||||
|
||||
var callServiceAction = rpc.declare({
|
||||
object: 'luci.cf-optimizer',
|
||||
method: 'service_action',
|
||||
params: [ 'action' ]
|
||||
});
|
||||
|
||||
return view.extend({
|
||||
handleSaveApply: null,
|
||||
handleSave: null,
|
||||
handleReset: null,
|
||||
|
||||
load: function() {
|
||||
return callGetStatus();
|
||||
},
|
||||
|
||||
handleRunOnce: function(ev) {
|
||||
ui.showModal(_('执行优选'), [
|
||||
E('p', { 'class': 'spinning' }, _('正在后台启动优选探测测速任务,请稍候...'))
|
||||
]);
|
||||
return callRunOnce().then(function() {
|
||||
setTimeout(function() {
|
||||
ui.hideModal();
|
||||
window.location.reload();
|
||||
}, 3000);
|
||||
}).catch(function(e) {
|
||||
ui.addNotification(null, E('p', {}, e.message));
|
||||
ui.hideModal();
|
||||
});
|
||||
},
|
||||
|
||||
handleServiceAction: function(action, ev) {
|
||||
ui.showModal(_('服务操作'), [
|
||||
E('p', { 'class': 'spinning' }, _('正在执行操作,请稍候...'))
|
||||
]);
|
||||
return callServiceAction(action).then(function() {
|
||||
setTimeout(function() {
|
||||
ui.hideModal();
|
||||
window.location.reload();
|
||||
}, 1500);
|
||||
}).catch(function(e) {
|
||||
ui.addNotification(null, E('p', {}, e.message));
|
||||
ui.hideModal();
|
||||
});
|
||||
},
|
||||
|
||||
render: function(data) {
|
||||
data = data || {};
|
||||
|
||||
var container = E('div', { 'class': 'cbi-map' }, [
|
||||
E('h2', {}, _('Cloudflare 优选加速') + ' - ' + _('状态概览')),
|
||||
E('div', { 'class': 'cbi-map-descr' }, _('监控当前生效的 Cloudflare 优选节点状态,展示各代理域名定向情况与最新测速排行榜。'))
|
||||
]);
|
||||
|
||||
// 服务状态卡片
|
||||
var isRunning = data.service_running === true;
|
||||
var statusText = isRunning ? (_('运行中') + (data.service_pid ? ' [PID: ' + data.service_pid + ']' : '')) : _('已停止');
|
||||
var statusColor = isRunning ? '#059669' : '#64748b';
|
||||
|
||||
var statusSection = E('fieldset', { 'class': 'cbi-section' }, [
|
||||
E('legend', {}, _('服务状态')),
|
||||
E('div', { 'class': 'cbi-section-node' }, [
|
||||
E('table', { 'class': 'table cbi-section-table' }, [
|
||||
E('tr', { 'class': 'tr' }, [
|
||||
E('td', { 'class': 'td left', 'width': '25%' }, _('服务状态')),
|
||||
E('td', { 'class': 'td left' }, [
|
||||
E('span', { 'id': 'cf-service-status', 'style': 'font-weight:bold; color:' + statusColor + '; margin-right:15px;' }, statusText),
|
||||
E('button', {
|
||||
'class': 'btn cbi-button cbi-button-action',
|
||||
'style': 'margin-right:10px;',
|
||||
'click': ui.createHandlerFn(this, 'handleRunOnce')
|
||||
}, _('立即执行优选')),
|
||||
E('button', {
|
||||
'class': 'btn cbi-button cbi-button-reload',
|
||||
'click': ui.createHandlerFn(this, 'handleServiceAction', 'restart')
|
||||
}, _('重启服务'))
|
||||
])
|
||||
]),
|
||||
E('tr', { 'class': 'tr' }, [
|
||||
E('td', { 'class': 'td left' }, _('上次更新时间')),
|
||||
E('td', { 'class': 'td left', 'id': 'cf-last-update' }, data.last_update || '-')
|
||||
]),
|
||||
E('tr', { 'class': 'tr' }, [
|
||||
E('td', { 'class': 'td left' }, _('生效优选 IP')),
|
||||
E('td', { 'class': 'td left', 'id': 'cf-best-ip', 'style': 'font-weight:bold;' }, data.best_ip || '-')
|
||||
]),
|
||||
E('tr', { 'class': 'tr' }, [
|
||||
E('td', { 'class': 'td left' }, _('次优备选 IP')),
|
||||
E('td', { 'class': 'td left', 'id': 'cf-standby-ip' }, data.standby_ip ? (data.standby_ip + ' (' + (data.standby_colo || '-') + ')') : _('暂无备选'))
|
||||
]),
|
||||
E('tr', { 'class': 'tr' }, [
|
||||
E('td', { 'class': 'td left' }, _('健康看门狗')),
|
||||
E('td', { 'class': 'td left', 'id': 'cf-watchdog-status', 'style': 'font-weight:bold; color:' + ((data.watchdog_status && data.watchdog_status.indexOf('倒换') !== -1) ? '#d97706' : '#059669') }, data.watchdog_status || _('未运行'))
|
||||
]),
|
||||
E('tr', { 'class': 'tr' }, [
|
||||
E('td', { 'class': 'td left' }, _('数据中心')),
|
||||
E('td', { 'class': 'td left', 'id': 'cf-best-colo' }, data.best_colo || '-')
|
||||
]),
|
||||
E('tr', { 'class': 'tr' }, [
|
||||
E('td', { 'class': 'td left' }, _('测速延迟')),
|
||||
E('td', { 'class': 'td left', 'id': 'cf-best-latency' }, [
|
||||
E('span', { 'style': 'color:#2563eb; font-weight:bold;' }, data.best_tcp_lat ? 'TCP: ' + data.best_tcp_lat : '-'),
|
||||
E('span', { 'style': 'margin:0 8px; color:#cbd5e1;' }, '|'),
|
||||
E('span', { 'style': 'color:#059669; font-weight:bold;' }, data.best_tls_lat ? 'TLS: ' + data.best_tls_lat : '-')
|
||||
])
|
||||
])
|
||||
])
|
||||
])
|
||||
]);
|
||||
|
||||
// 域名定向列表
|
||||
var domainRows = [];
|
||||
if (Array.isArray(data.domains) && data.domains.length > 0) {
|
||||
for (var i = 0; i < data.domains.length; i++) {
|
||||
var d = data.domains[i];
|
||||
domainRows.push(E('tr', { 'class': 'tr' }, [
|
||||
E('td', { 'class': 'td' }, E('strong', {}, d.domain)),
|
||||
E('td', { 'class': 'td' }, d.ip || '-'),
|
||||
E('td', { 'class': 'td' }, d.colo || '-'),
|
||||
E('td', { 'class': 'td' }, d.tcp_lat || '-'),
|
||||
E('td', { 'class': 'td' }, d.tls_lat || '-'),
|
||||
E('td', { 'class': 'td' }, E('span', { 'style': 'color:#059669; font-weight:bold;' }, d.status || _('正常')))
|
||||
]));
|
||||
}
|
||||
} else {
|
||||
domainRows.push(E('tr', { 'class': 'tr' }, [
|
||||
E('td', { 'class': 'td', 'colspan': '6', 'style': 'text-align:center; color:#94a3b8;' }, _('暂无生效的域名定向记录,请在参数配置中添加域名并启动服务。'))
|
||||
]));
|
||||
}
|
||||
|
||||
var domainSection = E('fieldset', { 'class': 'cbi-section' }, [
|
||||
E('legend', {}, _('域名定向列表')),
|
||||
E('div', { 'class': 'cbi-section-node' }, [
|
||||
E('table', { 'class': 'table cbi-section-table', 'id': 'cf-domain-table' }, [
|
||||
E('tr', { 'class': 'tr table-titles' }, [
|
||||
E('th', { 'class': 'th' }, _('目标域名')),
|
||||
E('th', { 'class': 'th' }, _('生效优选 IP')),
|
||||
E('th', { 'class': 'th' }, _('数据中心')),
|
||||
E('th', { 'class': 'th' }, _('TCP 延迟')),
|
||||
E('th', { 'class': 'th' }, _('TLS 延迟')),
|
||||
E('th', { 'class': 'th' }, _('状态'))
|
||||
])
|
||||
].concat(domainRows))
|
||||
])
|
||||
]);
|
||||
|
||||
// 优选候选排行榜
|
||||
var rankingRows = [];
|
||||
if (Array.isArray(data.rankings) && data.rankings.length > 0) {
|
||||
for (var j = 0; j < data.rankings.length; j++) {
|
||||
var r = data.rankings[j];
|
||||
rankingRows.push(E('tr', { 'class': 'tr' }, [
|
||||
E('td', { 'class': 'td' }, r.rank),
|
||||
E('td', { 'class': 'td' }, E('strong', {}, r.ip)),
|
||||
E('td', { 'class': 'td' }, r.colo || '-'),
|
||||
E('td', { 'class': 'td', 'style': 'color:#059669; font-weight:bold;' }, r.avg_lat),
|
||||
E('td', { 'class': 'td' }, r.lat_range || '-'),
|
||||
E('td', { 'class': 'td' }, r.loss_rate || '0.0%')
|
||||
]));
|
||||
}
|
||||
} else {
|
||||
rankingRows.push(E('tr', { 'class': 'tr' }, [
|
||||
E('td', { 'class': 'td', 'colspan': '6', 'style': 'text-align:center; color:#94a3b8;' }, _('暂无排行榜数据'))
|
||||
]));
|
||||
}
|
||||
|
||||
var rankingSection = E('fieldset', { 'class': 'cbi-section' }, [
|
||||
E('legend', {}, _('优选候选节点排行榜')),
|
||||
E('div', { 'class': 'cbi-section-node' }, [
|
||||
E('table', { 'class': 'table cbi-section-table', 'id': 'cf-ranking-table' }, [
|
||||
E('tr', { 'class': 'tr table-titles' }, [
|
||||
E('th', { 'class': 'th' }, _('排名')),
|
||||
E('th', { 'class': 'th' }, _('候选 IP')),
|
||||
E('th', { 'class': 'th' }, _('数据中心')),
|
||||
E('th', { 'class': 'th' }, _('平均延迟')),
|
||||
E('th', { 'class': 'th' }, _('时延范围')),
|
||||
E('th', { 'class': 'th' }, _('丢包率'))
|
||||
])
|
||||
].concat(rankingRows))
|
||||
])
|
||||
]);
|
||||
|
||||
container.appendChild(statusSection);
|
||||
container.appendChild(domainSection);
|
||||
container.appendChild(rankingSection);
|
||||
|
||||
// 定时轮询更新
|
||||
poll.add(function() {
|
||||
return callGetStatus().then(function(res) {
|
||||
res = res || {};
|
||||
var running = res.service_running === true;
|
||||
var stEl = document.getElementById('cf-service-status');
|
||||
if (stEl) {
|
||||
stEl.textContent = running ? (_('运行中') + (res.service_pid ? ' [PID: ' + res.service_pid + ']' : '')) : _('已停止');
|
||||
stEl.style.color = running ? '#059669' : '#64748b';
|
||||
}
|
||||
var upEl = document.getElementById('cf-last-update');
|
||||
if (upEl) upEl.textContent = res.last_update || '-';
|
||||
|
||||
var ipEl = document.getElementById('cf-best-ip');
|
||||
if (ipEl) ipEl.textContent = res.best_ip || '-';
|
||||
|
||||
var sbEl = document.getElementById('cf-standby-ip');
|
||||
if (sbEl) sbEl.textContent = res.standby_ip ? (res.standby_ip + ' (' + (res.standby_colo || '-') + ')') : _('暂无备选');
|
||||
|
||||
var wdEl = document.getElementById('cf-watchdog-status');
|
||||
if (wdEl) {
|
||||
wdEl.textContent = res.watchdog_status || _('未运行');
|
||||
wdEl.style.color = (res.watchdog_status && res.watchdog_status.indexOf('倒换') !== -1) ? '#d97706' : '#059669';
|
||||
}
|
||||
|
||||
var coloEl = document.getElementById('cf-best-colo');
|
||||
if (coloEl) coloEl.textContent = res.best_colo || '-';
|
||||
|
||||
var latEl = document.getElementById('cf-best-latency');
|
||||
if (latEl) {
|
||||
dom.content(latEl, [
|
||||
E('span', { 'style': 'color:#2563eb; font-weight:bold;' }, res.best_tcp_lat ? 'TCP: ' + res.best_tcp_lat : '-'),
|
||||
E('span', { 'style': 'margin:0 8px; color:#cbd5e1;' }, '|'),
|
||||
E('span', { 'style': 'color:#059669; font-weight:bold;' }, res.best_tls_lat ? 'TLS: ' + res.best_tls_lat : '-')
|
||||
]);
|
||||
}
|
||||
});
|
||||
}, 3);
|
||||
|
||||
return container;
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,153 @@
|
||||
'use strict';
|
||||
'require view';
|
||||
'require form';
|
||||
'require uci';
|
||||
'require fs';
|
||||
'require ui';
|
||||
|
||||
return view.extend({
|
||||
handleSaveApply: function(ev, mode) {
|
||||
return this.handleSave(ev).then(function() {
|
||||
ui.showModal(_('应用配置'), [
|
||||
E('p', { 'class': 'spinning' }, _('正在下发配置并同步 hosts 域名定向,请稍候...'))
|
||||
]);
|
||||
return fs.exec_direct('/etc/init.d/cf-optimizer', ['reload']).catch(function() {}).then(function() {
|
||||
setTimeout(function() {
|
||||
ui.hideModal();
|
||||
window.location.reload();
|
||||
}, 1500);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var m, s, o;
|
||||
|
||||
m = new form.Map('cf-optimizer', _('Cloudflare 优选加速') + ' - ' + _('参数配置'),
|
||||
_('配置待优选的 Cloudflare 代理域名、测速周期及采样算法参数。'));
|
||||
|
||||
s = m.section(form.NamedSection, 'settings', 'cf-optimizer', _('基础设置'));
|
||||
s.anonymous = true;
|
||||
|
||||
o = s.option(form.Flag, 'enabled', _('启用服务'),
|
||||
_('开启后守护进程将按设定周期自动执行优选测速,并通过 dnsmasq 内存级重载生效。'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.option(form.DynamicList, 'domains', _('加速域名列表'),
|
||||
_('填入需要优选加速的域名,支持 Cloudflare Tunnel 穿透域名和小黄云代理域名。'));
|
||||
o.datatype = 'host';
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.option(form.ListValue, 'interval', _('自动优选周期'),
|
||||
_('两次自动测速优选之间的休眠间隔时间。'));
|
||||
o.value('30', _('30 分钟'));
|
||||
o.value('60', _('1 小时'));
|
||||
o.value('120', _('2 小时'));
|
||||
o.value('240', _('4 小时'));
|
||||
o.value('360', _('6 小时'));
|
||||
o.value('720', _('12 小时'));
|
||||
o.value('1440', _('24 小时'));
|
||||
o.default = '120';
|
||||
|
||||
s = m.section(form.NamedSection, 'settings', 'cf-optimizer', _('高级探测参数'));
|
||||
s.anonymous = true;
|
||||
|
||||
o = s.option(form.Value, 'sample_count', _('采样探测次数'),
|
||||
_('初筛后对候选节点进行连续采样的次数,用于计算平均时延与丢包率,推荐范围 3-10 次。'));
|
||||
o.datatype = 'range(2, 20)';
|
||||
o.default = '4';
|
||||
|
||||
o = s.option(form.ListValue, 'hysteresis_ms', _('防震荡迟滞阈值'),
|
||||
_('新节点平均延迟相比在用节点必须降低超过此绝对时延(毫秒)才触发切换,避免网络微小波动导致局域网长连接频繁重连。'));
|
||||
o.value('0', _('关闭 (任意优化均切换)'));
|
||||
o.value('10', '10 ms');
|
||||
o.value('20', '20 ms');
|
||||
o.value('30', '30 ms');
|
||||
o.value('50', '50 ms');
|
||||
o.value('80', '80 ms');
|
||||
o.default = '30';
|
||||
|
||||
o = s.option(form.Value, 'concurrency', _('初筛并发数'),
|
||||
_('初筛阶段并发探测的进程数量,推荐范围 5-20。'));
|
||||
o.datatype = 'range(2, 30)';
|
||||
o.default = '10';
|
||||
|
||||
o = s.option(form.ListValue, 'ip_source', _('候选 IP 来源'),
|
||||
_('选择优选测速的候选节点 IP 来源。'));
|
||||
o.value('builtin', _('内置优选 IP 池'));
|
||||
o.value('custom', _('自定义 IP 或网段'));
|
||||
o.value('mixed', _('混合模式'));
|
||||
o.default = 'builtin';
|
||||
|
||||
o = s.option(form.DynamicList, 'custom_ips', _('自定义候选 IP 列表'),
|
||||
_('填入自定义的 Cloudflare Anycast IP 地址或网段。'));
|
||||
o.datatype = 'or(ip4addr, ip4prefix)';
|
||||
o.depends('ip_source', 'custom');
|
||||
o.depends('ip_source', 'mixed');
|
||||
|
||||
s = m.section(form.NamedSection, 'settings', 'cf-optimizer', _('故障快速倒换'));
|
||||
s.anonymous = true;
|
||||
|
||||
o = s.option(form.Flag, 'failover_enabled', _('启用快速故障倒换'),
|
||||
_('开启后短周期看门狗将持续监控在用节点健康度,发生严重丢包或阻断时自动秒级倒换至次优备选节点。'));
|
||||
o.rmempty = false;
|
||||
o.default = '1';
|
||||
|
||||
o = s.option(form.ListValue, 'health_interval', _('健康巡检间隔'),
|
||||
_('看门狗对在用节点执行极轻量 HTTP 探活的间隔时间。'));
|
||||
o.value('30', _('30 秒'));
|
||||
o.value('60', _('60 秒'));
|
||||
o.value('120', _('120 秒'));
|
||||
o.default = '60';
|
||||
o.depends('failover_enabled', '1');
|
||||
|
||||
o = s.option(form.Value, 'fail_threshold', _('连续故障判定阈值'),
|
||||
_('探活连续失败达到指定次数后触发次优节点核验与自动倒换,推荐设置为 2 次以避免偶发网络抖动误切换。'));
|
||||
o.datatype = 'range(1, 10)';
|
||||
o.default = '2';
|
||||
o.depends('failover_enabled', '1');
|
||||
|
||||
o = s.option(form.ListValue, 'latency_multiplier', _('延迟劣化容忍倍数'),
|
||||
_('当在用节点实测延迟超过基线延迟指定倍数且绝对恶化超过 50ms 时判定为信道拥塞,连续触发将倒换至次优节点。'));
|
||||
o.value('1.5', _('1.5 倍'));
|
||||
o.value('2.0', _('2.0 倍'));
|
||||
o.value('3.0', _('3.0 倍'));
|
||||
o.value('0', _('禁用'));
|
||||
o.default = '2.0';
|
||||
o.depends('failover_enabled', '1');
|
||||
|
||||
s = m.section(form.NamedSection, 'settings', 'cf-optimizer', _('机房区域过滤'));
|
||||
s.anonymous = true;
|
||||
|
||||
o = s.option(form.ListValue, 'colo_filter_mode', _('区域过滤模式'),
|
||||
_('设置对 Cloudflare 数据中心机房的筛选策略。'));
|
||||
o.value('none', _('不限制'));
|
||||
o.value('exclude', _('排除指定机房'));
|
||||
o.value('include', _('仅限指定机房'));
|
||||
o.default = 'none';
|
||||
|
||||
o = s.option(form.MultiValue, 'colo_list', _('目标机房列表'),
|
||||
_('选择需要排除或仅保留的 Cloudflare 常用数据中心机房。'));
|
||||
o.value('SIN', _('新加坡 SIN'));
|
||||
o.value('HKG', _('香港 HKG'));
|
||||
o.value('TPE', _('台北 TPE'));
|
||||
o.value('NRT', _('东京 NRT'));
|
||||
o.value('KIX', _('大阪 KIX'));
|
||||
o.value('ICN', _('首尔 ICN'));
|
||||
o.value('KUL', _('吉隆坡 KUL'));
|
||||
o.value('BKK', _('曼谷 BKK'));
|
||||
o.value('SJC', _('圣何塞 SJC'));
|
||||
o.value('LAX', _('洛杉矶 LAX'));
|
||||
o.value('FRA', _('法兰克福 FRA'));
|
||||
o.value('LHR', _('伦敦 LHR'));
|
||||
o.depends('colo_filter_mode', 'exclude');
|
||||
o.depends('colo_filter_mode', 'include');
|
||||
|
||||
o = s.option(form.DynamicList, 'custom_colos', _('自定义机房代码'),
|
||||
_('填入其他三位大写机场代码,例如 SFO、SEA 等。'));
|
||||
o.depends('colo_filter_mode', 'exclude');
|
||||
o.depends('colo_filter_mode', 'include');
|
||||
|
||||
return m.render();
|
||||
}
|
||||
});
|
||||
Reference in new issue
Block a user