parent
7868a95774
commit
300ac6e0dd
10
luci-app-mihomo/Makefile
Normal file
10
luci-app-mihomo/Makefile
Normal file
@ -0,0 +1,10 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_VERSION:=1.8.3
|
||||
|
||||
LUCI_TITLE:=LuCI Support for mihomo
|
||||
LUCI_DEPENDS:=+luci-base +mihomo
|
||||
|
||||
include $(TOPDIR)/feeds/luci/luci.mk
|
||||
|
||||
# call BuildPackage - OpenWrt buildroot signature
|
113
luci-app-mihomo/htdocs/luci-static/resources/tools/mihomo.js
Normal file
113
luci-app-mihomo/htdocs/luci-static/resources/tools/mihomo.js
Normal file
@ -0,0 +1,113 @@
|
||||
'use strict';
|
||||
'require baseclass';
|
||||
'require uci';
|
||||
'require fs';
|
||||
'require rpc';
|
||||
|
||||
const homeDir = '/etc/mihomo';
|
||||
const profilesDir = `${homeDir}/profiles`;
|
||||
const mixinFilePath = `${homeDir}/mixin.yaml`;
|
||||
const runDir = `${homeDir}/run`;
|
||||
const runAppLogPath = `${runDir}/app.log`;
|
||||
const runCoreLogPath = `${runDir}/core.log`;
|
||||
const runProfilePath = `${runDir}/config.yaml`;
|
||||
const nftDir = `${homeDir}/nftables`;
|
||||
const reservedIPNFT = `${nftDir}/reserved_ip.nft`;
|
||||
const reservedIP6NFT = `${nftDir}/reserved_ip6.nft`;
|
||||
|
||||
return baseclass.extend({
|
||||
homeDir: homeDir,
|
||||
profilesDir: profilesDir,
|
||||
mixinFilePath: mixinFilePath,
|
||||
runDir: runDir,
|
||||
runAppLogPath: runAppLogPath,
|
||||
runCoreLogPath: runCoreLogPath,
|
||||
runProfilePath: runProfilePath,
|
||||
reservedIPNFT: reservedIPNFT,
|
||||
reservedIP6NFT: reservedIP6NFT,
|
||||
|
||||
callServiceList: rpc.declare({
|
||||
object: 'service',
|
||||
method: 'list',
|
||||
params: ['name'],
|
||||
expect: { '': {} }
|
||||
}),
|
||||
|
||||
getAppLog: function () {
|
||||
return L.resolveDefault(fs.read_direct(this.runAppLogPath));
|
||||
},
|
||||
|
||||
getCoreLog: function () {
|
||||
return L.resolveDefault(fs.read_direct(this.runCoreLogPath));
|
||||
},
|
||||
|
||||
clearAppLog: function () {
|
||||
return fs.exec_direct('/usr/libexec/mihomo-call', ['clear', 'app_log']);
|
||||
},
|
||||
|
||||
clearCoreLog: function () {
|
||||
return fs.exec_direct('/usr/libexec/mihomo-call', ['clear', 'core_log']);
|
||||
},
|
||||
|
||||
listProfiles: function () {
|
||||
return L.resolveDefault(fs.list(this.profilesDir), []);
|
||||
},
|
||||
|
||||
status: async function () {
|
||||
try {
|
||||
return (await this.callServiceList('mihomo'))['mihomo']['instances']['mihomo']['running'];
|
||||
} catch (ignored) {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
reload: function () {
|
||||
return fs.exec_direct('/usr/libexec/mihomo-call', ['service', 'reload']);
|
||||
},
|
||||
|
||||
restart: function () {
|
||||
return fs.exec_direct('/usr/libexec/mihomo-call', ['service', 'restart']);
|
||||
},
|
||||
|
||||
appVersion: function () {
|
||||
return L.resolveDefault(fs.exec_direct('/usr/libexec/mihomo-call', ['version', 'app']), 'Unknown');
|
||||
},
|
||||
|
||||
coreVersion: function () {
|
||||
return L.resolveDefault(fs.exec_direct('/usr/libexec/mihomo-call', ['version', 'core']), 'Unknown');
|
||||
},
|
||||
|
||||
callMihomoAPI: async function (method, path, body) {
|
||||
const running = await this.status();
|
||||
if (running) {
|
||||
const apiPort = uci.get('mihomo', 'mixin', 'api_port');
|
||||
const apiSecret = uci.get('mihomo', 'mixin', 'api_secret');
|
||||
const url = `http://${window.location.hostname}:${apiPort}${path}`;
|
||||
await fetch(url, {
|
||||
method: method,
|
||||
headers: { 'Authorization': `Bearer ${apiSecret}` },
|
||||
body: body
|
||||
})
|
||||
} else {
|
||||
alert(_('Service is not running.'));
|
||||
}
|
||||
},
|
||||
|
||||
openDashboard: async function () {
|
||||
const running = await this.status();
|
||||
if (running) {
|
||||
const uiName = uci.get('mihomo', 'mixin', 'ui_name');
|
||||
const apiPort = uci.get('mihomo', 'mixin', 'api_port');
|
||||
const apiSecret = uci.get('mihomo', 'mixin', 'api_secret');
|
||||
let url;
|
||||
if (uiName) {
|
||||
url = `http://${window.location.hostname}:${apiPort}/ui/${uiName}/?host=${window.location.hostname}&hostname=${window.location.hostname}&port=${apiPort}&secret=${apiSecret}`;
|
||||
} else {
|
||||
url = `http://${window.location.hostname}:${apiPort}/ui/?host=${window.location.hostname}&hostname=${window.location.hostname}&port=${apiPort}&secret=${apiSecret}`;
|
||||
}
|
||||
setTimeout(() => window.open(url, '_blank'), 0);
|
||||
} else {
|
||||
alert(_('Service is not running.'));
|
||||
}
|
||||
},
|
||||
})
|
@ -0,0 +1,515 @@
|
||||
'use strict';
|
||||
'require form';
|
||||
'require view';
|
||||
'require uci';
|
||||
'require fs';
|
||||
'require network';
|
||||
'require rpc';
|
||||
'require poll';
|
||||
'require tools.widgets as widgets';
|
||||
'require tools.mihomo as mihomo';
|
||||
|
||||
function renderStatus(running) {
|
||||
return updateStatus(E('input', { id: 'core_status', style: 'border: unset; font-style: italic; font-weight: bold;', readonly: '' }), running);
|
||||
}
|
||||
|
||||
function updateStatus(element, running) {
|
||||
if (element) {
|
||||
element.style.color = running ? 'green' : 'red';
|
||||
element.value = running ? _('Running') : _('Not Running');
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
return view.extend({
|
||||
load: function () {
|
||||
return Promise.all([
|
||||
uci.load('mihomo'),
|
||||
mihomo.listProfiles(),
|
||||
mihomo.appVersion(),
|
||||
mihomo.coreVersion(),
|
||||
mihomo.status(),
|
||||
network.getHostHints(),
|
||||
]);
|
||||
},
|
||||
render: function (data) {
|
||||
const subscriptions = uci.sections('mihomo', 'subscription');
|
||||
const profiles = data[1];
|
||||
const appVersion = data[2];
|
||||
const coreVersion = data[3];
|
||||
const running = data[4];
|
||||
const hosts = data[5].hosts;
|
||||
|
||||
let m, s, o, so;
|
||||
|
||||
m = new form.Map('mihomo', _('MihomoTProxy'), `${_('Transparent Proxy with Mihomo on OpenWrt.')} <a href="https://github.com/morytyann/OpenWrt-mihomo/wiki" target="_blank">${_('How To Use')}</a>`);
|
||||
|
||||
s = m.section(form.NamedSection, 'status', 'status', _('Status'));
|
||||
|
||||
o = s.option(form.Value, '_app_version', _('App Version'));
|
||||
o.readonly = true;
|
||||
o.load = function (section_id) {
|
||||
return appVersion.trim();
|
||||
};
|
||||
o.write = function () { };
|
||||
|
||||
o = s.option(form.Value, '_core_version', _('Core Version'));
|
||||
o.readonly = true;
|
||||
o.load = function (section_id) {
|
||||
return coreVersion.trim();
|
||||
};
|
||||
o.write = function () { };
|
||||
|
||||
o = s.option(form.DummyValue, '_core_status', _('Core Status'));
|
||||
o.cfgvalue = function (section_id) {
|
||||
return renderStatus(running);
|
||||
};
|
||||
poll.add(function () {
|
||||
return L.resolveDefault(mihomo.status()).then(function (running) {
|
||||
updateStatus(document.getElementById('core_status'), running);
|
||||
});
|
||||
});
|
||||
|
||||
o = s.option(form.Button, 'reload', '-');
|
||||
o.inputstyle = 'action';
|
||||
o.inputtitle = _('Reload Service');
|
||||
o.onclick = function () {
|
||||
return mihomo.reload();
|
||||
};
|
||||
|
||||
o = s.option(form.Button, 'restart', '-');
|
||||
o.inputstyle = 'negative';
|
||||
o.inputtitle = _('Restart Service');
|
||||
o.onclick = function () {
|
||||
return mihomo.restart();
|
||||
};
|
||||
|
||||
o = s.option(form.Button, 'update_dashboard', '-');
|
||||
o.inputstyle = 'positive';
|
||||
o.inputtitle = _('Update Dashboard');
|
||||
o.onclick = function () {
|
||||
return mihomo.callMihomoAPI('POST', '/upgrade/ui');
|
||||
};
|
||||
|
||||
o = s.option(form.Button, 'open_dashboard', '-');
|
||||
o.inputtitle = _('Open Dashboard');
|
||||
o.onclick = function () {
|
||||
return mihomo.openDashboard();
|
||||
};
|
||||
|
||||
s = m.section(form.NamedSection, 'config', 'config', _('Basic Config'));
|
||||
|
||||
o = s.option(form.Flag, 'enabled', _('Enable'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.option(form.Flag, 'scheduled_restart', _('Scheduled Restart'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.option(form.Value, 'cron_expression', _('Cron Expression'));
|
||||
o.retain = true;
|
||||
o.rmempty = false;
|
||||
o.depends('scheduled_restart', '1');
|
||||
|
||||
o = s.option(form.ListValue, 'profile', _('Choose Profile'));
|
||||
o.rmempty = false;
|
||||
|
||||
for (const profile of profiles) {
|
||||
o.value('file:' + profile.name, _('File:') + profile.name);
|
||||
}
|
||||
|
||||
for (const subscription of subscriptions) {
|
||||
o.value('subscription:' + subscription['.name'], _('Subscription:') + subscription.name);
|
||||
}
|
||||
|
||||
o = s.option(form.FileUpload, 'upload_profile', _('Upload Profile'));
|
||||
o.root_directory = mihomo.profilesDir;
|
||||
|
||||
o = s.option(form.Flag, 'mixin', _('Mixin'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.option(form.Flag, 'test_profile', _('Test Profile'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.option(form.Flag, 'fast_reload', _('Fast Reload'));
|
||||
o.rmempty = false;
|
||||
|
||||
s = m.section(form.NamedSection, 'proxy', 'proxy', _('Proxy Config'));
|
||||
|
||||
s.tab('transparent_proxy', _('Transparent Proxy'));
|
||||
|
||||
o = s.taboption('transparent_proxy', form.Flag, 'transparent_proxy', _('Enable'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('transparent_proxy', form.ListValue, 'tcp_transparent_proxy_mode', _('TCP Proxy Mode'));
|
||||
o.value('redirect', _('Redirect Mode'));
|
||||
o.value('tproxy', _('TPROXY Mode'));
|
||||
o.value('tun', _('TUN Mode'));
|
||||
|
||||
o = s.taboption('transparent_proxy', form.ListValue, 'udp_transparent_proxy_mode', _('UDP Proxy Mode'));
|
||||
o.value('tproxy', _('TPROXY Mode'));
|
||||
o.value('tun', _('TUN Mode'));
|
||||
|
||||
o = s.taboption('transparent_proxy', form.Flag, 'ipv4_dns_hijack', _('IPv4 DNS Hijack'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('transparent_proxy', form.Flag, 'ipv6_dns_hijack', _('IPv6 DNS Hijack'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('transparent_proxy', form.Flag, 'ipv4_proxy', _('IPv4 Proxy'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('transparent_proxy', form.Flag, 'ipv6_proxy', _('IPv6 Proxy'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('transparent_proxy', form.Flag, 'router_proxy', _('Router Proxy'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('transparent_proxy', form.Flag, 'lan_proxy', _('Lan Proxy'));
|
||||
o.rmempty = false;
|
||||
|
||||
s.tab('access_control', _('Access Control'));
|
||||
|
||||
o = s.taboption('access_control', form.ListValue, 'access_control_mode', _('Mode'));
|
||||
o.value('all', _('All Mode'));
|
||||
o.value('allow', _('Allow Mode'));
|
||||
o.value('block', _('Block Mode'));
|
||||
|
||||
o = s.taboption('access_control', form.DynamicList, 'acl_ip', 'IP');
|
||||
o.datatype = 'ipmask4';
|
||||
o.retain = true;
|
||||
o.depends('access_control_mode', 'allow');
|
||||
o.depends('access_control_mode', 'block');
|
||||
|
||||
for (const mac in hosts) {
|
||||
const host = hosts[mac];
|
||||
for (const ip of host.ipaddrs) {
|
||||
const hint = host.name || mac;
|
||||
o.value(ip, hint ? '%s (%s)'.format(ip, hint) : ip);
|
||||
}
|
||||
}
|
||||
|
||||
o = s.taboption('access_control', form.DynamicList, 'acl_ip6', 'IP6');
|
||||
o.datatype = 'ipmask6';
|
||||
o.retain = true;
|
||||
o.depends('access_control_mode', 'allow');
|
||||
o.depends('access_control_mode', 'block');
|
||||
|
||||
for (const mac in hosts) {
|
||||
const host = hosts[mac];
|
||||
for (const ip of host.ip6addrs) {
|
||||
const hint = host.name || mac;
|
||||
o.value(ip, hint ? '%s (%s)'.format(ip, hint) : ip);
|
||||
}
|
||||
}
|
||||
|
||||
o = s.taboption('access_control', form.DynamicList, 'acl_mac', 'MAC');
|
||||
o.datatype = 'macaddr';
|
||||
o.retain = true;
|
||||
o.depends('access_control_mode', 'allow');
|
||||
o.depends('access_control_mode', 'block');
|
||||
|
||||
for (const mac in hosts) {
|
||||
const host = hosts[mac];
|
||||
const hint = host.name || host.ipaddrs[0];
|
||||
o.value(mac, hint ? '%s (%s)'.format(mac, hint) : mac);
|
||||
}
|
||||
|
||||
s.tab('bypass', _('Bypass'));
|
||||
|
||||
o = s.taboption('bypass', form.Flag, 'bypass_china_mainland_ip', _('Bypass China Mainland IP'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('bypass', form.Value, 'acl_tcp_dport', _('Destination TCP Port to Proxy'));
|
||||
o.rmempty = false;
|
||||
o.value('0-65535', _('All Port'));
|
||||
o.value('21 22 80 110 143 194 443 465 993 995 8080 8443', _('Commonly Used Port'));
|
||||
|
||||
o = s.taboption('bypass', form.Value, 'acl_udp_dport', _('Destination UDP Port to Proxy'));
|
||||
o.rmempty = false;
|
||||
o.value('0-65535', _('All Port'));
|
||||
o.value('123 443 8443', _('Commonly Used Port'));
|
||||
|
||||
s = m.section(form.TableSection, 'subscription', _('Subscription Config'));
|
||||
s.addremove = true;
|
||||
s.anonymous = true;
|
||||
|
||||
o = s.option(form.Value, 'name', _('Subscription Name'));
|
||||
o.rmempty = false;
|
||||
o.width = '15%';
|
||||
|
||||
o = s.option(form.Value, 'url', _('Subscription Url'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.option(form.Value, 'user_agent', _('User Agent'));
|
||||
o.default = 'mihomo';
|
||||
o.rmempty = false;
|
||||
o.width = '15%';
|
||||
o.value('mihomo');
|
||||
o.value('clash.meta');
|
||||
o.value('clash');
|
||||
|
||||
s = m.section(form.NamedSection, 'mixin', 'mixin', _('Mixin Config'));
|
||||
|
||||
s.tab('general', _('General Config'));
|
||||
|
||||
o = s.taboption('general', form.ListValue, 'log_level', _('Log Level'));
|
||||
o.value('silent');
|
||||
o.value('error');
|
||||
o.value('warning');
|
||||
o.value('info');
|
||||
o.value('debug');
|
||||
|
||||
o = s.taboption('general', form.ListValue, 'mode', _('Proxy Mode'));
|
||||
o.value('global', _('Global Mode'));
|
||||
o.value('rule', _('Rule Mode'));
|
||||
o.value('direct', _('Direct Mode'));
|
||||
|
||||
o = s.taboption('general', form.ListValue, 'match_process', _('Match Process'));
|
||||
o.value('strict', _('Auto'));
|
||||
o.value('always', _('Enable'));
|
||||
o.value('off', _('Disable'));
|
||||
|
||||
o = s.taboption('general', widgets.NetworkSelect, 'outbound_interface', _('Outbound Interface'));
|
||||
o.optional = true;
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('general', form.Flag, 'ipv6', _('IPv6'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('general', form.Value, 'tcp_keep_alive_idle', _('TCP Keep Alive Idle'));
|
||||
o.datatype = 'integer';
|
||||
o.placeholder = '600';
|
||||
|
||||
o = s.taboption('general', form.Value, 'tcp_keep_alive_interval', _('TCP Keep Alive Interval'));
|
||||
o.datatype = 'integer';
|
||||
o.placeholder = '15';
|
||||
|
||||
s.tab('external_control', _('External Control Config'));
|
||||
|
||||
o = s.taboption('external_control', form.Value, 'ui_name', _('UI Name'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('external_control', form.Value, 'ui_url', _('UI Url'));
|
||||
o.rmempty = false;
|
||||
o.value('https://mirror.ghproxy.com/https://github.com/MetaCubeX/metacubexd/archive/refs/heads/gh-pages.zip', 'MetaCubeXD')
|
||||
o.value('https://mirror.ghproxy.com/https://github.com/MetaCubeX/Yacd-meta/archive/refs/heads/gh-pages.zip', 'YACD')
|
||||
o.value('https://mirror.ghproxy.com/https://github.com/MetaCubeX/Razord-meta/archive/refs/heads/gh-pages.zip', 'Razord')
|
||||
|
||||
o = s.taboption('external_control', form.Value, 'api_port', _('API Port'));
|
||||
o.datatype = 'port';
|
||||
o.placeholder = '9090';
|
||||
|
||||
o = s.taboption('external_control', form.Value, 'api_secret', _('API Secret'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('external_control', form.Flag, 'selection_cache', _('Save Proxy Selection'));
|
||||
o.rmempty = false;
|
||||
|
||||
s.tab('inbound', _('Inbound Config'));
|
||||
|
||||
o = s.taboption('inbound', form.Flag, 'allow_lan', _('Allow Lan'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('inbound', form.Value, 'http_port', _('HTTP Port'));
|
||||
o.datatype = 'port';
|
||||
o.placeholder = '8080';
|
||||
|
||||
o = s.taboption('inbound', form.Value, 'socks_port', _('SOCKS Port'));
|
||||
o.datatype = 'port';
|
||||
o.placeholder = '1080';
|
||||
|
||||
o = s.taboption('inbound', form.Value, 'mixed_port', _('Mixed Port'));
|
||||
o.datatype = 'port';
|
||||
o.placeholder = '7890';
|
||||
|
||||
o = s.taboption('inbound', form.Value, 'redir_port', _('Redirect Port'));
|
||||
o.datatype = 'port';
|
||||
o.placeholder = '7891';
|
||||
|
||||
o = s.taboption('inbound', form.Value, 'tproxy_port', _('TPROXY Port'));
|
||||
o.datatype = 'port';
|
||||
o.placeholder = '7892';
|
||||
|
||||
o = s.taboption('inbound', form.Flag, 'authentication', _('Authentication'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('inbound', form.SectionValue, '_authentications', form.TableSection, 'authentication', _('Edit Authentications'));
|
||||
o.retain = true;
|
||||
o.depends('authentication', '1');
|
||||
|
||||
o.subsection.anonymous = true;
|
||||
o.subsection.addremove = true;
|
||||
|
||||
so = o.subsection.option(form.Flag, 'enabled', _('Enable'));
|
||||
so.rmempty = false;
|
||||
|
||||
so = o.subsection.option(form.Value, 'username', _('Username'));
|
||||
so.rmempty = false;
|
||||
|
||||
so = o.subsection.option(form.Value, 'password', _('Password'));
|
||||
so.rmempty = false;
|
||||
|
||||
s.tab('tun', _('TUN Config'));
|
||||
|
||||
o = s.taboption('tun', form.ListValue, 'tun_stack', _('Stack'));
|
||||
o.value('system', 'System');
|
||||
o.value('gvisor', 'gVisor');
|
||||
o.value('mixed', 'Mixed');
|
||||
|
||||
o = s.taboption('tun', form.Value, 'tun_mtu', _('MTU'));
|
||||
o.placeholder = '9000';
|
||||
|
||||
o = s.taboption('tun', form.Flag, 'tun_gso', _('GSO'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('tun', form.Value, 'tun_gso_max_size', _('GSO Max Size'));
|
||||
o.placeholder = '65536';
|
||||
o.depends('tun_gso', '1');
|
||||
|
||||
o = s.taboption('tun', form.Flag, 'tun_endpoint_independent_nat', _('Endpoint Independent NAT'));
|
||||
o.rmempty = false;
|
||||
|
||||
s.tab('dns', _('DNS Config'));
|
||||
|
||||
o = s.taboption('dns', form.Value, 'dns_port', _('DNS Port'));
|
||||
o.datatype = 'port';
|
||||
o.placeholder = '1053';
|
||||
|
||||
o = s.taboption('dns', form.ListValue, 'dns_mode', _('DNS Mode'));
|
||||
o.value('normal', 'Normal');
|
||||
o.value('fake-ip', 'Fake-IP');
|
||||
o.value('redir-host', 'Redir-Host');
|
||||
|
||||
o = s.taboption('dns', form.Value, 'fake_ip_range', _('Fake-IP Range'));
|
||||
o.datatype = 'cidr4';
|
||||
o.placeholder = '198.18.0.1/16';
|
||||
o.retain = true;
|
||||
o.depends('dns_mode', 'fake-ip');
|
||||
|
||||
o = s.taboption('dns', form.Flag, 'fake_ip_filter', _('Overwrite Fake-IP Filter'));
|
||||
o.retain = true;
|
||||
o.rmempty = false;
|
||||
o.depends('dns_mode', 'fake-ip');
|
||||
|
||||
o = s.taboption('dns', form.DynamicList, 'fake_ip_filters', _('Edit Fake-IP Filters'));
|
||||
o.retain = true;
|
||||
o.depends({ 'dns_mode': 'fake-ip', 'fake_ip_filter': '1' });
|
||||
|
||||
o = s.taboption('dns', form.ListValue, 'fake_ip_filter_mode', _('Fake-IP Filter Mode'))
|
||||
o.value('blacklist', _('Block Mode'));
|
||||
o.value('whitelist', _('Allow Mode'));
|
||||
o.depends({ 'dns_mode': 'fake-ip', 'fake_ip_filter': '1' });
|
||||
|
||||
o = s.taboption('dns', form.Flag, 'fake_ip_cache', _('Fake-IP Cache'));
|
||||
o.retain = true;
|
||||
o.rmempty = false;
|
||||
o.depends('dns_mode', 'fake-ip');
|
||||
|
||||
o = s.taboption('dns', form.Flag, 'dns_respect_rules', _('Respect Rules'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('dns', form.Flag, 'dns_ipv6', _('IPv6'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('dns', form.Flag, 'dns_system_hosts', _('Use System Hosts'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('dns', form.Flag, 'dns_hosts', _('Use Hosts'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('dns', form.Flag, 'hosts', _('Overwrite Hosts'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('dns', form.SectionValue, '_hosts', form.TableSection, 'host', _('Edit Hosts'));
|
||||
o.retain = true;
|
||||
o.depends('hosts', '1');
|
||||
|
||||
o.subsection.anonymous = true;
|
||||
o.subsection.addremove = true;
|
||||
|
||||
so = o.subsection.option(form.Flag, 'enabled', _('Enable'));
|
||||
so.rmempty = false;
|
||||
|
||||
so = o.subsection.option(form.Value, 'domain_name', _('Domain Name'));
|
||||
so.rmempty = false;
|
||||
|
||||
so = o.subsection.option(form.DynamicList, 'ip', _('IP'));
|
||||
|
||||
o = s.taboption('dns', form.Flag, 'dns_nameserver', _('Overwrite Nameserver'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('dns', form.SectionValue, '_dns_nameserver', form.TableSection, 'nameserver', _('Edit Nameservers'));
|
||||
o.retain = true;
|
||||
o.depends('dns_nameserver', '1');
|
||||
|
||||
o.subsection.anonymous = true;
|
||||
o.subsection.addremove = false;
|
||||
|
||||
so = o.subsection.option(form.Flag, 'enabled', _('Enable'));
|
||||
so.rmempty = false;
|
||||
|
||||
so = o.subsection.option(form.ListValue, 'type', _('Type'));
|
||||
so.readonly = true;
|
||||
so.value('default-nameserver');
|
||||
so.value('proxy-server-nameserver');
|
||||
so.value('nameserver');
|
||||
so.value('fallback');
|
||||
|
||||
so = o.subsection.option(form.DynamicList, 'nameserver', _('Nameserver'));
|
||||
|
||||
o = s.taboption('dns', form.Flag, 'dns_nameserver_policy', _('Overwrite Nameserver Policy'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('dns', form.SectionValue, '_dns_nameserver_policies', form.TableSection, 'nameserver_policy', _('Edit Nameserver Policies'));
|
||||
o.retain = true;
|
||||
o.depends('dns_nameserver_policy', '1');
|
||||
|
||||
o.subsection.anonymous = true;
|
||||
o.subsection.addremove = true;
|
||||
|
||||
so = o.subsection.option(form.Flag, 'enabled', _('Enable'));
|
||||
so.rmempty = false;
|
||||
|
||||
so = o.subsection.option(form.Value, 'matcher', _('Matcher'));
|
||||
so.rmempty = false;
|
||||
|
||||
so = o.subsection.option(form.DynamicList, 'nameserver', _('Nameserver'));
|
||||
|
||||
s.tab('geox', _('GeoX Config'));
|
||||
|
||||
o = s.taboption('geox', form.ListValue, 'geoip_format', _('GeoIP Format'));
|
||||
o.value('dat', 'DAT');
|
||||
o.value('mmdb', 'MMDB');
|
||||
|
||||
o = s.taboption('geox', form.ListValue, 'geodata_loader', _('GeoData Loader'));
|
||||
o.value('standard', _('Standard Loader'));
|
||||
o.value('memconservative', _('Memory Conservative Loader'));
|
||||
|
||||
o = s.taboption('geox', form.Value, 'geosite_url', _('GeoSite Url'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('geox', form.Value, 'geoip_mmdb_url', _('GeoIP(MMDB) Url'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('geox', form.Value, 'geoip_dat_url', _('GeoIP(DAT) Url'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('geox', form.Value, 'geoip_asn_url', _('GeoIP(ASN) Url'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('geox', form.Flag, 'geox_auto_update', _('GeoX Auto Update'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('geox', form.Value, 'geox_update_interval', _('GeoX Update Interval'), _('Hour'));
|
||||
o.datatype = 'integer';
|
||||
o.placeholder = '24';
|
||||
o.retain = true;
|
||||
o.depends('geox_auto_update', '1');
|
||||
|
||||
s.tab('mixin_file_content', _('Mixin File Content'), _('Please go to the editor tab to edit the file for mixin'));
|
||||
|
||||
o = s.taboption('mixin_file_content', form.HiddenValue, '_mixin_file_content');
|
||||
|
||||
return m.render();
|
||||
}
|
||||
});
|
@ -0,0 +1,63 @@
|
||||
'use strict';
|
||||
'require form';
|
||||
'require view';
|
||||
'require uci';
|
||||
'require fs';
|
||||
'require tools.mihomo as mihomo'
|
||||
|
||||
return view.extend({
|
||||
load: function () {
|
||||
return Promise.all([
|
||||
uci.load('mihomo'),
|
||||
mihomo.listProfiles(),
|
||||
]);
|
||||
},
|
||||
render: function (data) {
|
||||
const profiles = data[1];
|
||||
|
||||
let m, s, o;
|
||||
|
||||
m = new form.Map('mihomo');
|
||||
|
||||
s = m.section(form.NamedSection, 'editor', 'editor');
|
||||
|
||||
o = s.option(form.ListValue, '_profile', _('Choose Profile'));
|
||||
o.optional = true;
|
||||
|
||||
for (const profile of profiles) {
|
||||
o.value(mihomo.profilesDir + '/' + profile.name, _('File:') + profile.name);
|
||||
}
|
||||
o.value(mihomo.mixinFilePath, _('File for Mixin'));
|
||||
o.value(mihomo.runProfilePath, _('Profile for Startup'));
|
||||
o.value(mihomo.reservedIPNFT, _('File for Reserved IP'));
|
||||
o.value(mihomo.reservedIP6NFT, _('File for Reserved IP6'));
|
||||
|
||||
o.write = function (section_id, formvalue) {
|
||||
return true;
|
||||
};
|
||||
o.onchange = function (event, section_id, value) {
|
||||
return L.resolveDefault(fs.read_direct(value), '').then(function (content) {
|
||||
m.lookupOption('mihomo.editor._profile_content')[0].getUIElement('editor').setValue(content);
|
||||
});
|
||||
};
|
||||
|
||||
o = s.option(form.TextValue, '_profile_content',);
|
||||
o.rows = 25;
|
||||
o.write = function (section_id, formvalue) {
|
||||
const path = m.lookupOption('mihomo.editor._profile')[0].formvalue('editor');
|
||||
return fs.write(path, formvalue);
|
||||
};
|
||||
o.remove = function (section_id) {
|
||||
const path = m.lookupOption('mihomo.editor._profile')[0].formvalue('editor');
|
||||
return fs.write(path);
|
||||
};
|
||||
|
||||
return m.render();
|
||||
},
|
||||
handleSaveApply: function (ev, mode) {
|
||||
return this.handleSave(ev).finally(function() {
|
||||
return mode === '0' ? mihomo.reload() : mihomo.restart();
|
||||
});
|
||||
},
|
||||
handleReset: null
|
||||
});
|
@ -0,0 +1,98 @@
|
||||
'use strict';
|
||||
'require form';
|
||||
'require view';
|
||||
'require uci';
|
||||
'require fs';
|
||||
'require poll';
|
||||
'require tools.mihomo as mihomo'
|
||||
|
||||
return view.extend({
|
||||
load: function () {
|
||||
return Promise.all([
|
||||
uci.load('mihomo'),
|
||||
mihomo.getAppLog(),
|
||||
mihomo.getCoreLog()
|
||||
]);
|
||||
},
|
||||
render: function (data) {
|
||||
const appLog = data[1];
|
||||
const coreLog = data[2];
|
||||
|
||||
let m, s, o;
|
||||
|
||||
m = new form.Map('mihomo');
|
||||
|
||||
s = m.section(form.NamedSection, 'log', 'log');
|
||||
|
||||
s.tab('app_log', _('App Log'));
|
||||
|
||||
o = s.taboption('app_log', form.Button, 'clear_app_log');
|
||||
o.inputstyle = 'negative';
|
||||
o.inputtitle = _('Clear Log');
|
||||
o.onclick = function () {
|
||||
m.lookupOption('mihomo.log._app_log')[0].getUIElement('log').setValue('');
|
||||
return mihomo.clearAppLog();
|
||||
};
|
||||
|
||||
o = s.taboption('app_log', form.TextValue, '_app_log');
|
||||
o.rows = 25;
|
||||
o.wrap = false;
|
||||
o.load = function (section_id) {
|
||||
return appLog;
|
||||
};
|
||||
o.write = function (section_id, formvalue) {
|
||||
return true;
|
||||
};
|
||||
poll.add(L.bind(function () {
|
||||
const option = this;
|
||||
return L.resolveDefault(mihomo.getAppLog()).then(function (log) {
|
||||
option.getUIElement('log').setValue(log);
|
||||
});
|
||||
}, o));
|
||||
|
||||
o = s.taboption('app_log', form.Button, 'scroll_app_log_to_bottom');
|
||||
o.inputtitle = _('Scroll To Bottom');
|
||||
o.onclick = function () {
|
||||
const element = m.lookupOption('mihomo.log._app_log')[0].getUIElement('log').node.firstChild;
|
||||
element.scrollTop = element.scrollHeight;
|
||||
};
|
||||
|
||||
s.tab('core_log', _('Core Log'));
|
||||
|
||||
o = s.taboption('core_log', form.Button, 'clear_core_log');
|
||||
o.inputstyle = 'negative';
|
||||
o.inputtitle = _('Clear Log');
|
||||
o.onclick = function () {
|
||||
m.lookupOption('mihomo.log._core_log')[0].getUIElement('log').setValue('');
|
||||
return mihomo.clearCoreLog();
|
||||
};
|
||||
|
||||
o = s.taboption('core_log', form.TextValue, '_core_log');
|
||||
o.rows = 25;
|
||||
o.wrap = false;
|
||||
o.load = function (section_id) {
|
||||
return coreLog;
|
||||
};
|
||||
o.write = function (section_id, formvalue) {
|
||||
return true;
|
||||
};
|
||||
poll.add(L.bind(function () {
|
||||
const option = this;
|
||||
return L.resolveDefault(mihomo.getCoreLog()).then(function (log) {
|
||||
option.getUIElement('log').setValue(log);
|
||||
});
|
||||
}, o));
|
||||
|
||||
o = s.taboption('core_log', form.Button, 'scroll_core_log_to_bottom');
|
||||
o.inputtitle = _('Scroll To Bottom');
|
||||
o.onclick = function () {
|
||||
const element = m.lookupOption('mihomo.log._core_log')[0].getUIElement('log').node.firstChild;
|
||||
element.scrollTop = element.scrollHeight;
|
||||
};
|
||||
|
||||
return m.render();
|
||||
},
|
||||
handleSaveApply: null,
|
||||
handleSave: null,
|
||||
handleReset: null
|
||||
});
|
416
luci-app-mihomo/po/zh_Hans/mihomo.po
Normal file
416
luci-app-mihomo/po/zh_Hans/mihomo.po
Normal file
@ -0,0 +1,416 @@
|
||||
msgid "MihomoTProxy"
|
||||
msgstr "MihomoTProxy"
|
||||
|
||||
msgid "Transparent Proxy with Mihomo on OpenWrt."
|
||||
msgstr "在 OpenWrt 上使用 Mihomo 进行透明代理。"
|
||||
|
||||
msgid "How To Use"
|
||||
msgstr "使用说明"
|
||||
|
||||
msgid "Config"
|
||||
msgstr "配置"
|
||||
|
||||
msgid "Status"
|
||||
msgstr "状态"
|
||||
|
||||
msgid "App Version"
|
||||
msgstr "插件版本"
|
||||
|
||||
msgid "Core Version"
|
||||
msgstr "核心版本"
|
||||
|
||||
msgid "Core Status"
|
||||
msgstr "核心状态"
|
||||
|
||||
msgid "Running"
|
||||
msgstr "运行中"
|
||||
|
||||
msgid "Not Running"
|
||||
msgstr "未在运行"
|
||||
|
||||
msgid "Reload Service"
|
||||
msgstr "重载服务"
|
||||
|
||||
msgid "Restart Service"
|
||||
msgstr "重启服务"
|
||||
|
||||
msgid "Update Dashboard"
|
||||
msgstr "更新面板"
|
||||
|
||||
msgid "Open Dashboard"
|
||||
msgstr "打开面板"
|
||||
|
||||
msgid "Basic Config"
|
||||
msgstr "基础配置"
|
||||
|
||||
msgid "Enable"
|
||||
msgstr "启用"
|
||||
|
||||
msgid "Disable"
|
||||
msgstr "禁用"
|
||||
|
||||
msgid "Auto"
|
||||
msgstr "自动"
|
||||
|
||||
msgid "Mode"
|
||||
msgstr "模式"
|
||||
|
||||
msgid "Type"
|
||||
msgstr "类型"
|
||||
|
||||
msgid "Value"
|
||||
msgstr "值"
|
||||
|
||||
msgid "Scheduled Restart"
|
||||
msgstr "定时重启"
|
||||
|
||||
msgid "Cron Expression"
|
||||
msgstr "Cron 表达式"
|
||||
|
||||
msgid "Choose Profile"
|
||||
msgstr "选择配置文件"
|
||||
|
||||
msgid "File:"
|
||||
msgstr "文件:"
|
||||
|
||||
msgid "Subscription:"
|
||||
msgstr "订阅:"
|
||||
|
||||
msgid "Upload Profile"
|
||||
msgstr "上传配置文件"
|
||||
|
||||
msgid "Mixin"
|
||||
msgstr "混入"
|
||||
|
||||
msgid "Test Profile"
|
||||
msgstr "检查配置文件"
|
||||
|
||||
msgid "Fast Reload"
|
||||
msgstr "快速重载"
|
||||
|
||||
msgid "Proxy Config"
|
||||
msgstr "代理配置"
|
||||
|
||||
msgid "Transparent Proxy"
|
||||
msgstr "透明代理"
|
||||
|
||||
msgid "TCP Proxy Mode"
|
||||
msgstr "TCP 代理模式"
|
||||
|
||||
msgid "UDP Proxy Mode"
|
||||
msgstr "UDP 代理模式"
|
||||
|
||||
msgid "Redirect Mode"
|
||||
msgstr "Redirect 模式"
|
||||
|
||||
msgid "TPROXY Mode"
|
||||
msgstr "TPROXY 模式"
|
||||
|
||||
msgid "TUN Mode"
|
||||
msgstr "TUN 模式"
|
||||
|
||||
msgid "IPv4 DNS Hijack"
|
||||
msgstr "IPv4 DNS 劫持"
|
||||
|
||||
msgid "IPv6 DNS Hijack"
|
||||
msgstr "IPv6 DNS 劫持"
|
||||
|
||||
msgid "IPv4 Proxy"
|
||||
msgstr "IPv4 代理"
|
||||
|
||||
msgid "IPv6 Proxy"
|
||||
msgstr "IPv6 代理"
|
||||
|
||||
msgid "Router Proxy"
|
||||
msgstr "路由器代理"
|
||||
|
||||
msgid "Lan Proxy"
|
||||
msgstr "局域网代理"
|
||||
|
||||
msgid "Access Control"
|
||||
msgstr "访问控制"
|
||||
|
||||
msgid "All Mode"
|
||||
msgstr "全部模式"
|
||||
|
||||
msgid "Allow Mode"
|
||||
msgstr "白名单模式"
|
||||
|
||||
msgid "Block Mode"
|
||||
msgstr "黑名单模式"
|
||||
|
||||
msgid "Bypass"
|
||||
msgstr "绕过"
|
||||
|
||||
msgid "Bypass China Mainland IP"
|
||||
msgstr "绕过中国大陆 IP"
|
||||
|
||||
msgid "Destination TCP Port to Proxy"
|
||||
msgstr "要代理的 TCP 目标端口"
|
||||
|
||||
msgid "Destination UDP Port to Proxy"
|
||||
msgstr "要代理的 UDP 目标端口"
|
||||
|
||||
msgid "All Port"
|
||||
msgstr "全部端口"
|
||||
|
||||
msgid "Commonly Used Port"
|
||||
msgstr "常用端口"
|
||||
|
||||
msgid "Subscription Config"
|
||||
msgstr "订阅配置"
|
||||
|
||||
msgid "Subscription Name"
|
||||
msgstr "订阅名称"
|
||||
|
||||
msgid "Subscription Url"
|
||||
msgstr "订阅链接"
|
||||
|
||||
msgid "User Agent"
|
||||
msgstr "用户代理(UA)"
|
||||
|
||||
msgid "Mixin Config"
|
||||
msgstr "混入配置"
|
||||
|
||||
msgid "General Config"
|
||||
msgstr "全局配置"
|
||||
|
||||
msgid "Proxy Mode"
|
||||
msgstr "代理模式"
|
||||
|
||||
msgid "Global Mode"
|
||||
msgstr "全局模式"
|
||||
|
||||
msgid "Rule Mode"
|
||||
msgstr "规则模式"
|
||||
|
||||
msgid "Direct Mode"
|
||||
msgstr "直连模式"
|
||||
|
||||
msgid "Match Process"
|
||||
msgstr "匹配进程"
|
||||
|
||||
msgid "Outbound Interface"
|
||||
msgstr "出站接口"
|
||||
|
||||
msgid "TCP Keep Alive Idle"
|
||||
msgstr "TCP Keep Alive 空闲"
|
||||
|
||||
msgid "TCP Keep Alive Interval"
|
||||
msgstr "TCP Keep Alive 间隔"
|
||||
|
||||
msgid "Log Level"
|
||||
msgstr "日志级别"
|
||||
|
||||
msgid "External Control Config"
|
||||
msgstr "外部控制配置"
|
||||
|
||||
msgid "UI Name"
|
||||
msgstr "UI 名称"
|
||||
|
||||
msgid "UI Url"
|
||||
msgstr "UI 下载地址"
|
||||
|
||||
msgid "Service is not running."
|
||||
msgstr "服务未在运行。"
|
||||
|
||||
msgid "API Port"
|
||||
msgstr "API 端口"
|
||||
|
||||
msgid "API Secret"
|
||||
msgstr "API 密钥"
|
||||
|
||||
msgid "Save Proxy Selection"
|
||||
msgstr "保存节点/策略组选择"
|
||||
|
||||
msgid "Inbound Config"
|
||||
msgstr "入站配置"
|
||||
|
||||
msgid "Allow Lan"
|
||||
msgstr "允许局域网访问"
|
||||
|
||||
msgid "HTTP Port"
|
||||
msgstr "HTTP 端口"
|
||||
|
||||
msgid "SOCKS Port"
|
||||
msgstr "SOCKS 端口"
|
||||
|
||||
msgid "Mixed Port"
|
||||
msgstr "混合端口"
|
||||
|
||||
msgid "Redirect Port"
|
||||
msgstr "Redirect 端口"
|
||||
|
||||
msgid "TPROXY Port"
|
||||
msgstr "TPROXY 端口"
|
||||
|
||||
msgid "Authentication"
|
||||
msgid "身份验证"
|
||||
|
||||
msgid "Edit Authentications"
|
||||
msgstr "编辑身份验证"
|
||||
|
||||
msgid "username"
|
||||
msgstr "用户名"
|
||||
|
||||
msgid "password"
|
||||
msgstr "密码"
|
||||
|
||||
msgid "TUN Config"
|
||||
msgstr "TUN 配置"
|
||||
|
||||
msgid "Stack"
|
||||
msgstr "栈"
|
||||
|
||||
msgid "Device"
|
||||
msgstr "设备"
|
||||
|
||||
msgid "MTU"
|
||||
msgstr "最大传输单元"
|
||||
|
||||
msgid "GSO"
|
||||
msgstr "通用分段卸载"
|
||||
|
||||
msgid "GSO Max Size"
|
||||
msgstr "分段最大长度"
|
||||
|
||||
msgid "Endpoint Independent NAT"
|
||||
msgstr "独立于端点的 NAT"
|
||||
|
||||
msgid "DNS Config"
|
||||
msgstr "DNS 配置"
|
||||
|
||||
msgid "DNS Port"
|
||||
msgstr "DNS 端口"
|
||||
|
||||
msgid "DNS Mode"
|
||||
msgstr "DNS 模式"
|
||||
|
||||
msgid "Fake-IP Range"
|
||||
msgstr "Fake-IP 范围"
|
||||
|
||||
msgid "Overwrite Fake-IP Filter"
|
||||
msgstr "覆盖 Fake-IP 过滤列表"
|
||||
|
||||
msgid "Edit Fake-IP Filters"
|
||||
msgstr "编辑 Fake-IP 过滤列表"
|
||||
|
||||
msgid "Fake-IP Filter Mode"
|
||||
msgstr "Fake-IP 过滤模式"
|
||||
|
||||
msgid "Fake-IP Cache"
|
||||
msgstr "Fake-IP 缓存"
|
||||
|
||||
msgid "Respect Rules"
|
||||
msgstr "遵循分流规则"
|
||||
|
||||
msgid "Use System Hosts"
|
||||
msgstr "使用系统的 Hosts"
|
||||
|
||||
msgid "Use Hosts"
|
||||
msgstr "使用 Hosts"
|
||||
|
||||
msgid "Overwrite Hosts"
|
||||
msgstr "覆盖 Hosts"
|
||||
|
||||
msgid "Edit Hosts"
|
||||
msgstr "编辑 Hosts"
|
||||
|
||||
msgid "Domain Name"
|
||||
msgstr "域名"
|
||||
|
||||
msgid "Overwrite Nameserver"
|
||||
msgstr "覆盖 DNS 服务器"
|
||||
|
||||
msgid "Edit Nameservers"
|
||||
msgstr "编辑 DNS 服务器"
|
||||
|
||||
msgid "Nameserver"
|
||||
msgstr "DNS 服务器"
|
||||
|
||||
msgid "Overwrite Fallback Filter"
|
||||
msgstr "覆盖 Fallback 过滤列表"
|
||||
|
||||
msgid "Edit Fallback Filters"
|
||||
msgstr "编辑 Fallback 过滤列表"
|
||||
|
||||
msgid "Overwrite Nameserver Policy"
|
||||
msgstr "覆盖 DNS 服务器查询策略"
|
||||
|
||||
msgid "Edit Nameserver Policies"
|
||||
msgstr "编辑 DNS 服务器查询策略"
|
||||
|
||||
msgid "Matcher"
|
||||
msgstr "匹配"
|
||||
|
||||
msgid "GeoX Config"
|
||||
msgstr "GeoX 配置"
|
||||
|
||||
msgid "GeoIP Format"
|
||||
msgstr "GeoIP 格式"
|
||||
|
||||
msgid "GeoData Loader"
|
||||
msgstr "GeoData 加载器"
|
||||
|
||||
msgid "Standard Loader"
|
||||
msgstr "标准加载器"
|
||||
|
||||
msgid "Memory Conservative Loader"
|
||||
msgstr "为内存受限设备优化的加载器"
|
||||
|
||||
msgid "GeoSite Url"
|
||||
msgstr "GeoSite 下载地址"
|
||||
|
||||
msgid "GeoIP(MMDB) Url"
|
||||
msgstr "GeoIP(MMDB) 下载地址"
|
||||
|
||||
msgid "GeoIP(DAT) Url"
|
||||
msgstr "GeoIP(DAT) 下载地址"
|
||||
|
||||
msgid "GeoIP(ASN) Url"
|
||||
msgstr "GeoIP(ASN) 下载地址"
|
||||
|
||||
msgid "GeoX Auto Update"
|
||||
msgstr "定时更新GeoX文件"
|
||||
|
||||
msgid "GeoX Update Interval"
|
||||
msgstr "GeoX 文件更新间隔"
|
||||
|
||||
msgid "Hour"
|
||||
msgstr "小时"
|
||||
|
||||
msgid "Mixin File Content"
|
||||
msgstr "混入文件内容"
|
||||
|
||||
msgid "Please go to the editor tab to edit the file for mixin"
|
||||
msgstr "请前往编辑器标签编辑用于混入的文件"
|
||||
|
||||
msgid "Editor"
|
||||
msgstr "编辑器"
|
||||
|
||||
msgid "File for Mixin"
|
||||
msgstr "用于混入的文件"
|
||||
|
||||
msgid "Profile for Startup"
|
||||
msgstr "用于启动的配置文件"
|
||||
|
||||
msgid "File for Reserved IP"
|
||||
msgstr "IPv4 保留地址"
|
||||
|
||||
msgid "File for Reserved IP6"
|
||||
msgstr "IPv6 保留地址"
|
||||
|
||||
msgid "Log"
|
||||
msgstr "日志"
|
||||
|
||||
msgid "App Log"
|
||||
msgstr "应用日志"
|
||||
|
||||
msgid "Core Log"
|
||||
msgstr "核心日志"
|
||||
|
||||
msgid "Clear Log"
|
||||
msgstr "清空日志"
|
||||
|
||||
msgid "Scroll To Bottom"
|
||||
msgstr "滚动到底部"
|
46
luci-app-mihomo/root/usr/libexec/mihomo-call
Executable file
46
luci-app-mihomo/root/usr/libexec/mihomo-call
Executable file
@ -0,0 +1,46 @@
|
||||
#!/bin/sh
|
||||
|
||||
. $IPKG_INSTROOT/etc/mihomo/scripts/constants.sh
|
||||
|
||||
action=$1
|
||||
shift
|
||||
|
||||
case "$action" in
|
||||
clear)
|
||||
case "$1" in
|
||||
app_log)
|
||||
echo -n > "$RUN_APP_LOG_PATH"
|
||||
;;
|
||||
core_log)
|
||||
echo -n > "$RUN_CORE_LOG_PATH"
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
load)
|
||||
case "$1" in
|
||||
profile)
|
||||
yq -M -o json < "$RUN_PROFILE_PATH"
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
service)
|
||||
case "$1" in
|
||||
reload)
|
||||
/etc/init.d/mihomo reload
|
||||
;;
|
||||
restart)
|
||||
/etc/init.d/mihomo restart
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
version)
|
||||
case "$1" in
|
||||
app)
|
||||
opkg list-installed | grep "luci-app-mihomo" | cut -d " " -f 3
|
||||
;;
|
||||
core)
|
||||
mihomo -v | grep "Mihomo" | cut -d " " -f 3
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
@ -0,0 +1,37 @@
|
||||
{
|
||||
"admin/services/mihomo": {
|
||||
"title": "MihomoTProxy",
|
||||
"action": {
|
||||
"type": "alias",
|
||||
"path": "admin/services/mihomo/config"
|
||||
},
|
||||
"depends": {
|
||||
"acl": [ "luci-app-mihomo" ],
|
||||
"uci": { "mihomo": true }
|
||||
}
|
||||
},
|
||||
"admin/services/mihomo/config": {
|
||||
"title": "Config",
|
||||
"order": 10,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "mihomo/config"
|
||||
}
|
||||
},
|
||||
"admin/services/mihomo/editor": {
|
||||
"title": "Editor",
|
||||
"order": 20,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "mihomo/editor"
|
||||
}
|
||||
},
|
||||
"admin/services/mihomo/log": {
|
||||
"title": "Log",
|
||||
"order": 30,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "mihomo/log"
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
{
|
||||
"luci-app-mihomo": {
|
||||
"description": "Grant access to mihomo procedures",
|
||||
"read": {
|
||||
"uci": [ "mihomo" ],
|
||||
"ubus": {
|
||||
"service": [ "list" ]
|
||||
},
|
||||
"file": {
|
||||
"/etc/mihomo/profiles/*.yaml": ["read"],
|
||||
"/etc/mihomo/profiles/*.yml": ["read"],
|
||||
"/etc/mihomo/mixin.yaml": ["read"],
|
||||
"/etc/mihomo/run/config.yaml": ["read"],
|
||||
"/etc/mihomo/nftables/reserved_ip.nft": ["read"],
|
||||
"/etc/mihomo/nftables/reserved_ip6.nft": ["read"],
|
||||
"/etc/mihomo/run/*.log": ["read"],
|
||||
"/usr/libexec/mihomo-call": ["exec"]
|
||||
}
|
||||
},
|
||||
"write": {
|
||||
"uci": [ "mihomo" ],
|
||||
"file": {
|
||||
"/etc/mihomo/profiles/*.yaml": ["write"],
|
||||
"/etc/mihomo/profiles/*.yml": ["write"],
|
||||
"/etc/mihomo/mixin.yaml": ["write"],
|
||||
"/etc/mihomo/run/config.yaml": ["write"],
|
||||
"/etc/mihomo/nftables/reserved_ip.nft": ["write"],
|
||||
"/etc/mihomo/nftables/reserved_ip6.nft": ["write"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user