diff --git a/luci-app-nikki/Makefile b/luci-app-nikki/Makefile new file mode 100644 index 000000000..dd754c720 --- /dev/null +++ b/luci-app-nikki/Makefile @@ -0,0 +1,10 @@ +include $(TOPDIR)/rules.mk + +PKG_VERSION:=1.17.5 + +LUCI_TITLE:=LuCI Support for nikki +LUCI_DEPENDS:=+luci-base +nikki + +include $(TOPDIR)/feeds/luci/luci.mk + +# call BuildPackage - OpenWrt buildroot signature \ No newline at end of file diff --git a/luci-app-nikki/htdocs/luci-static/resources/tools/nikki.js b/luci-app-nikki/htdocs/luci-static/resources/tools/nikki.js new file mode 100644 index 000000000..672c57b1e --- /dev/null +++ b/luci-app-nikki/htdocs/luci-static/resources/tools/nikki.js @@ -0,0 +1,133 @@ +'use strict'; +'require baseclass'; +'require uci'; +'require fs'; +'require rpc'; + +const homeDir = '/etc/nikki'; +const profilesDir = `${homeDir}/profiles`; +const subscriptionsDir = `${homeDir}/subscriptions`; +const mixinFilePath = `${homeDir}/mixin.yaml`; +const runDir = `${homeDir}/run`; +const runProfilePath = `${runDir}/config.yaml`; +const logDir = `/var/log/nikki`; +const appLogPath = `${logDir}/app.log`; +const coreLogPath = `${logDir}/core.log`; +const nftDir = `${homeDir}/nftables`; +const reservedIPNFT = `${nftDir}/reserved_ip.nft`; +const reservedIP6NFT = `${nftDir}/reserved_ip6.nft`; + +return baseclass.extend({ + homeDir: homeDir, + profilesDir: profilesDir, + subscriptionsDir: subscriptionsDir, + mixinFilePath: mixinFilePath, + runDir: runDir, + appLogPath: appLogPath, + coreLogPath: coreLogPath, + 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.appLogPath)); + }, + + getCoreLog: function () { + return L.resolveDefault(fs.read_direct(this.coreLogPath)); + }, + + clearAppLog: function () { + return fs.exec_direct('/usr/libexec/nikki-call', ['clear_log', 'app']); + }, + + clearCoreLog: function () { + return fs.exec_direct('/usr/libexec/nikki-call', ['clear_log', 'core']); + }, + + listProfiles: function () { + return L.resolveDefault(fs.list(this.profilesDir), []); + }, + + updateSubscription: function (section_id) { + return fs.exec_direct('/usr/libexec/nikki-call', ['subscription', 'update', section_id]); + }, + + status: async function () { + try { + return (await this.callServiceList('nikki'))['nikki']['instances']['nikki']['running']; + } catch (ignored) { + return false; + } + }, + + reload: function () { + return fs.exec_direct('/usr/libexec/nikki-call', ['service', 'reload']); + }, + + restart: function () { + return fs.exec_direct('/usr/libexec/nikki-call', ['service', 'restart']); + }, + + appVersion: function () { + return L.resolveDefault(fs.exec_direct('/usr/libexec/nikki-call', ['version', 'app']), _('Unknown')); + }, + + coreVersion: function () { + return L.resolveDefault(fs.exec_direct('/usr/libexec/nikki-call', ['version', 'core']), _('Unknown')); + }, + + callMihomoAPI: async function (method, path, params, body) { + const running = await this.status(); + if (running) { + const apiPort = uci.get('nikki', 'mixin', 'api_port'); + const apiSecret = uci.get('nikki', 'mixin', 'api_secret'); + const query = new URLSearchParams(params).toString(); + const url = `http://${window.location.hostname}:${apiPort}${path}?${query}`; + await fetch(url, { + method: method, + headers: { 'Authorization': `Bearer ${apiSecret}` }, + body: JSON.stringify(body) + }) + } else { + alert(_('Service is not running.')); + } + }, + + openDashboard: async function () { + const running = await this.status(); + if (running) { + const uiName = uci.get('nikki', 'mixin', 'ui_name'); + const apiPort = uci.get('nikki', 'mixin', 'api_port'); + const apiSecret = encodeURIComponent(uci.get('nikki', '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(function () { window.open(url, '_blank') }, 0); + } else { + alert(_('Service is not running.')); + } + }, + + getUsers: function () { + return fs.lines('/etc/passwd').then(function (lines) { + return lines.map(function (line) { return line.split(/:/)[0] }).filter(function (user) { return user !== 'root' && user !== 'nikki' }); + }); + }, + + getGroups: function () { + return fs.lines('/etc/group').then(function (lines) { + return lines.map(function (line) { return line.split(/:/)[0] }).filter(function (group) { return group !== 'root' && group !== 'nikki' }); + }); + }, +}) diff --git a/luci-app-nikki/htdocs/luci-static/resources/view/nikki/app.js b/luci-app-nikki/htdocs/luci-static/resources/view/nikki/app.js new file mode 100644 index 000000000..c4ee1a1b7 --- /dev/null +++ b/luci-app-nikki/htdocs/luci-static/resources/view/nikki/app.js @@ -0,0 +1,148 @@ +'use strict'; +'require form'; +'require view'; +'require uci'; +'require poll'; +'require tools.nikki as nikki'; + +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('nikki'), + nikki.appVersion(), + nikki.coreVersion(), + nikki.status(), + nikki.listProfiles() + ]); + }, + render: function (data) { + const subscriptions = uci.sections('nikki', 'subscription'); + const appVersion = data[1]; + const coreVersion = data[2]; + const running = data[3]; + const profiles = data[4]; + + let m, s, o; + + m = new form.Map('nikki', _('Nikki'), `${_('Transparent Proxy with Mihomo on OpenWrt.')} ${_('How To Use')}`); + + s = m.section(form.NamedSection, 'status', 'status', _('Status')); + + o = s.option(form.Value, '_app_version', _('App Version')); + o.readonly = true; + o.load = function () { + return appVersion.trim(); + }; + o.write = function () { }; + + o = s.option(form.Value, '_core_version', _('Core Version')); + o.readonly = true; + o.load = function () { + return coreVersion.trim(); + }; + o.write = function () { }; + + o = s.option(form.DummyValue, '_core_status', _('Core Status')); + o.cfgvalue = function () { + return renderStatus(running); + }; + poll.add(function () { + return L.resolveDefault(nikki.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 nikki.reload(); + }; + + o = s.option(form.Button, 'restart', '-'); + o.inputstyle = 'negative'; + o.inputtitle = _('Restart Service'); + o.onclick = function () { + return nikki.restart(); + }; + + o = s.option(form.Button, 'update_dashboard', '-'); + o.inputstyle = 'positive'; + o.inputtitle = _('Update Dashboard'); + o.onclick = function () { + return nikki.callMihomoAPI('POST', '/upgrade/ui'); + }; + + o = s.option(form.Button, 'open_dashboard', '-'); + o.inputtitle = _('Open Dashboard'); + o.onclick = function () { + return nikki.openDashboard(); + }; + + s = m.section(form.NamedSection, 'config', 'config', _('App Config')); + + o = s.option(form.Flag, 'enabled', _('Enable')); + o.rmempty = false; + + o = s.option(form.ListValue, 'profile', _('Choose Profile')); + o.optional = true; + + 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.Value, 'start_delay', _('Start Delay')); + o.datatype = 'uinteger'; + o.placeholder = '0'; + + 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.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, 'config', 'config', _('Core Environment Variable Config')); + + o = s.option(form.Flag, 'disable_safe_path_check', _('Disable Safe Path Check')); + o.ucisection = 'env'; + o.rmempty = false; + + o = s.option(form.Flag, 'disable_loopback_detector', _('Disable Loopback Detector')); + o.ucisection = 'env'; + o.rmempty = false; + + o = s.option(form.Flag, 'disable_quic_go_gso', _('Disable GSO of quic-go')); + o.ucisection = 'env'; + o.rmempty = false; + + o = s.option(form.Flag, 'disable_quic_go_ecn', _('Disable ECN of quic-go')); + o.ucisection = 'env'; + o.rmempty = false; + + return m.render(); + } +}); diff --git a/luci-app-nikki/htdocs/luci-static/resources/view/nikki/editor.js b/luci-app-nikki/htdocs/luci-static/resources/view/nikki/editor.js new file mode 100644 index 000000000..3d0096518 --- /dev/null +++ b/luci-app-nikki/htdocs/luci-static/resources/view/nikki/editor.js @@ -0,0 +1,70 @@ +'use strict'; +'require form'; +'require view'; +'require uci'; +'require fs'; +'require tools.nikki as nikki' + +return view.extend({ + load: function () { + return Promise.all([ + uci.load('nikki'), + nikki.listProfiles() + ]); + }, + render: function (data) { + const subscriptions = uci.sections('nikki', 'subscription'); + const profiles = data[1]; + + let m, s, o; + + m = new form.Map('nikki'); + + s = m.section(form.NamedSection, 'editor', 'editor', _('Editor')); + + o = s.option(form.ListValue, '_file', _('Choose File')); + o.optional = true; + + for (const profile of profiles) { + o.value(nikki.profilesDir + '/' + profile.name, _('File:') + profile.name); + }; + + for (const subscription of subscriptions) { + o.value(nikki.subscriptionsDir + '/' + subscription['.name'] + '.yaml', _('Subscription:') + subscription.name); + }; + + o.value(nikki.mixinFilePath, _('File for Mixin')); + o.value(nikki.runProfilePath, _('Profile for Startup')); + o.value(nikki.reservedIPNFT, _('File for Reserved IP')); + o.value(nikki.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('nikki.editor._file_content')[0].getUIElement('editor').setValue(content); + }); + }; + + o = s.option(form.TextValue, '_file_content',); + o.rows = 25; + o.wrap = false; + o.write = function (section_id, formvalue) { + const path = m.lookupOption('nikki.editor._file')[0].formvalue('editor'); + return fs.write(path, formvalue); + }; + o.remove = function (section_id) { + const path = m.lookupOption('nikki.editor._file')[0].formvalue('editor'); + return fs.write(path); + }; + + return m.render(); + }, + handleSaveApply: function (ev, mode) { + return this.handleSave(ev).finally(function () { + return mode === '0' ? nikki.reload() : nikki.restart(); + }); + }, + handleReset: null +}); diff --git a/luci-app-nikki/htdocs/luci-static/resources/view/nikki/log.js b/luci-app-nikki/htdocs/luci-static/resources/view/nikki/log.js new file mode 100644 index 000000000..3aee8d6fe --- /dev/null +++ b/luci-app-nikki/htdocs/luci-static/resources/view/nikki/log.js @@ -0,0 +1,97 @@ +'use strict'; +'require form'; +'require view'; +'require uci'; +'require poll'; +'require tools.nikki as nikki'; + +return view.extend({ + load: function () { + return Promise.all([ + uci.load('nikki'), + nikki.getAppLog(), + nikki.getCoreLog() + ]); + }, + render: function (data) { + const appLog = data[1]; + const coreLog = data[2]; + + let m, s, o; + + m = new form.Map('nikki'); + + s = m.section(form.NamedSection, 'log', '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('nikki.log._app_log')[0].getUIElement('log').setValue(''); + return nikki.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(nikki.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('nikki.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('nikki.log._core_log')[0].getUIElement('log').setValue(''); + return nikki.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(nikki.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('nikki.log._core_log')[0].getUIElement('log').node.firstChild; + element.scrollTop = element.scrollHeight; + }; + + return m.render(); + }, + handleSaveApply: null, + handleSave: null, + handleReset: null +}); \ No newline at end of file diff --git a/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js b/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js new file mode 100644 index 000000000..813ae1774 --- /dev/null +++ b/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js @@ -0,0 +1,371 @@ +'use strict'; +'require form'; +'require view'; +'require uci'; +'require fs'; +'require poll'; +'require tools.widgets as widgets'; +'require tools.nikki as nikki'; + +return view.extend({ + load: function () { + return Promise.all([ + uci.load('nikki') + ]); + }, + render: function (data) { + let m, s, o, so; + + m = new form.Map('nikki'); + + s = m.section(form.NamedSection, 'config', 'config', _('Mixin Config')); + + o = s.option(form.Flag, 'mixin', _('Enable')); + o.rmempty = false; + + s = m.section(form.NamedSection, 'mixin', 'mixin', _('Mixin Option')); + + 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', '*' + ' ' + _('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 = s.taboption('general', form.Flag, 'ipv6', '*' + ' ' + _('IPv6')); + o.rmempty = false; + + o = s.taboption('general', form.Flag, 'unify_delay', _('Unify Delay')); + o.rmempty = false; + + o = s.taboption('general', form.Flag, 'tcp_concurrent', _('TCP Concurrent')); + o.rmempty = false; + + o = s.taboption('general', form.Value, 'tcp_keep_alive_idle', _('TCP Keep Alive Idle')); + o.datatype = 'uinteger'; + o.placeholder = '600'; + + o = s.taboption('general', form.Value, 'tcp_keep_alive_interval', _('TCP Keep Alive Interval')); + o.datatype = 'uinteger'; + o.placeholder = '15'; + + s.tab('external_control', _('External Control Config')); + + o = s.taboption('external_control', form.Value, 'ui_name', '*' + ' ' + _('UI Name')); + + o = s.taboption('external_control', form.Value, 'ui_url', '*' + ' ' + _('UI Url')); + o.rmempty = false; + o.value('https://github.com/Zephyruso/zashboard/archive/refs/heads/gh-pages.zip', 'Zashboard'); + o.value('https://github.com/MetaCubeX/metacubexd/archive/refs/heads/gh-pages.zip', 'MetaCubeXD'); + o.value('https://github.com/MetaCubeX/Yacd-meta/archive/refs/heads/gh-pages.zip', 'YACD'); + o.value('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.password = true; + 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', '*' + ' ' + _('Overwrite 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.addremove = true; + o.subsection.anonymous = true; + o.subsection.sortable = 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.password = true; + so.rmempty = false; + + s.tab('tun', _('TUN Config')); + + o = s.taboption('tun', form.Value, 'tun_device', '*' + ' ' + _('Device')); + o.rmempty = false; + + 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.datatype = 'uinteger'; + 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.datatype = 'uinteger'; + o.placeholder = '65536'; + o.retain = true; + o.depends('tun_gso', '1'); + + o = s.taboption('tun', form.Flag, 'tun_dns_hijack', '*' + ' ' + _('Overwrite DNS Hijack')); + o.rmempty = false; + + o = s.taboption('tun', form.DynamicList, 'tun_dns_hijacks', '*' + ' ' + _('Edit DNS Hijacks')); + o.retain = true; + o.rmempty = false; + o.depends('tun_dns_hijack', '1'); + o.value('tcp://any:53'); + o.value('udp://any:53'); + + 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.retain = true; + 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_doh_prefer_http3', _('DoH Prefer HTTP/3')); + 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, 'hosts', _('Edit Hosts')); + o.retain = true; + o.depends('hosts', '1'); + + o.subsection.addremove = true; + o.subsection.anonymous = true; + o.subsection.sortable = 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_nameservers', form.TableSection, 'nameserver', _('Edit Nameservers')); + o.retain = true; + o.depends('dns_nameserver', '1'); + + o.subsection.addremove = true; + o.subsection.anonymous = true; + o.subsection.sortable = true; + + so = o.subsection.option(form.Flag, 'enabled', _('Enable')); + so.rmempty = false; + + so = o.subsection.option(form.ListValue, 'type', _('Type')); + so.value('default-nameserver'); + so.value('proxy-server-nameserver'); + so.value('direct-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.addremove = true; + o.subsection.anonymous = true; + o.subsection.sortable = 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('sniffer', _('Sniffer Config')); + + o = s.taboption('sniffer', form.Flag, 'sniffer', _('Enable')); + o.rmempty = false; + + o = s.taboption('sniffer', form.Flag, 'sniffer_sniff_dns_mapping', _('Sniff Redir-Host')); + o.rmempty = false; + + o = s.taboption('sniffer', form.Flag, 'sniffer_sniff_pure_ip', _('Sniff Pure IP')); + o.rmempty = false; + + o = s.taboption('sniffer', form.Flag, 'sniffer_overwrite_destination', _('Overwrite Destination')); + o.rmempty = false; + + o = s.taboption('sniffer', form.Flag, 'sniffer_force_domain_name', _('Overwrite Force Sniff Domain Name')); + o.rmempty = false; + + o = s.taboption('sniffer', form.DynamicList, 'sniffer_force_domain_names', _('Force Sniff Domain Name')); + o.depends('sniffer_force_domain_name', '1'); + + o = s.taboption('sniffer', form.Flag, 'sniffer_ignore_domain_name', _('Overwrite Ignore Sniff Domain Name')); + o.rmempty = false; + + o = s.taboption('sniffer', form.DynamicList, 'sniffer_ignore_domain_names', _('Ignore Sniff Domain Name')); + o.depends('sniffer_ignore_domain_name', '1'); + + o = s.taboption('sniffer', form.Flag, 'sniffer_sniff', _('Overwrite Sniff By Protocol')); + o.rmempty = false; + + o = s.taboption('sniffer', form.SectionValue, '_sniffer_sniffs', form.TableSection, 'sniff', _('Sniff By Protocol')); + o.subsection.anonymous = true; + o.subsection.addremove = false; + o.depends('sniffer_sniff', '1'); + + so = o.subsection.option(form.Flag, 'enabled', _('Enable')); + so.rmempty = false; + + so = o.subsection.option(form.ListValue, 'protocol', _('Protocol')); + so.value('HTTP'); + so.value('TLS'); + so.value('QUIC'); + so.readonly = true; + + so = o.subsection.option(form.DynamicList, 'port', _('Port')); + so.datatype = 'portrange'; + + so = o.subsection.option(form.Flag, 'overwrite_destination', _('Overwrite Destination')); + so.rmempty = false; + + 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')); + o.datatype = 'uinteger'; + o.placeholder = '24'; + o.retain = true; + o.depends('geox_auto_update', '1'); + + s.tab('mixin_file_content', _('Mixin File Content')); + + o = s.taboption('mixin_file_content', form.Flag, 'mixin_file_content', '*' + ' ' + _('Enable'), _('Please go to the editor tab to edit the file for mixin')); + o.rmempty = false; + + return m.render(); + } +}); \ No newline at end of file diff --git a/luci-app-nikki/htdocs/luci-static/resources/view/nikki/profile.js b/luci-app-nikki/htdocs/luci-static/resources/view/nikki/profile.js new file mode 100644 index 000000000..d11b6fd10 --- /dev/null +++ b/luci-app-nikki/htdocs/luci-static/resources/view/nikki/profile.js @@ -0,0 +1,86 @@ +'use strict'; +'require form'; +'require view'; +'require uci'; +'require tools.nikki as nikki'; + +return view.extend({ + load: function () { + return Promise.all([ + uci.load('nikki') + ]); + }, + render: function (data) { + let m, s, o, so; + + m = new form.Map('nikki'); + + s = m.section(form.NamedSection, 'config', 'config', _('Profile')); + + o = s.option(form.FileUpload, '_upload_profile', _('Upload Profile')); + o.browser = true; + o.enable_download = true; + o.root_directory = nikki.profilesDir; + o.write = function (section_id, formvalue) { + return true; + }; + + s = m.section(form.GridSection, 'subscription', _('Subscription')); + s.addremove = true; + s.anonymous = true; + s.sortable = true; + s.modaltitle = _('Edit Subscription'); + + o = s.option(form.Value, 'name', _('Subscription Name')); + o.rmempty = false; + + o = s.option(form.Value, 'used', _('Used')); + o.modalonly = false; + o.optional = true; + o.readonly = true; + + o = s.option(form.Value, 'total', _('Total')); + o.modalonly = false; + o.optional = true; + o.readonly = true; + + o = s.option(form.Value, 'expire', _('Expire At')); + o.modalonly = false; + o.optional = true; + o.readonly = true; + + o = s.option(form.Value, 'update', _('Update At')); + o.modalonly = false; + o.optional = true; + o.readonly = true; + + o = s.option(form.Button, 'update_subscription'); + o.editable = true; + o.inputstyle = 'positive'; + o.inputtitle = _('Update'); + o.modalonly = false; + o.onclick = function (_, section_id) { + return nikki.updateSubscription(section_id); + }; + + o = s.option(form.Value, 'url', _('Subscription Url')); + o.modalonly = true; + o.rmempty = false; + + o = s.option(form.Value, 'user_agent', _('User Agent')); + o.default = 'clash'; + o.modalonly = true; + o.rmempty = false; + o.value('clash'); + o.value('clash.meta'); + o.value('mihomo'); + + o = s.option(form.ListValue, 'prefer', _('Prefer')); + o.default = 'remote'; + o.modalonly = true; + o.value('remote', _('Remote')); + o.value('local', _('Local')); + + return m.render(); + } +}); diff --git a/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js b/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js new file mode 100644 index 000000000..e7758dc08 --- /dev/null +++ b/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js @@ -0,0 +1,149 @@ +'use strict'; +'require form'; +'require view'; +'require uci'; +'require network'; +'require tools.widgets as widgets'; +'require tools.nikki as nikki'; + +return view.extend({ + load: function () { + return Promise.all([ + uci.load('nikki'), + network.getHostHints(), + nikki.getUsers(), + nikki.getGroups() + ]); + }, + render: function (data) { + const hosts = data[1].hosts; + const users = data[2]; + const groups = data[3]; + + let m, s, o; + + m = new form.Map('nikki'); + + 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); + }; + + o = s.taboption('access_control', widgets.NetworkSelect, 'acl_interface', _('Interface')); + o.multiple = true; + o.optional = true; + o.retain = true; + o.depends('access_control_mode', 'allow'); + o.depends('access_control_mode', 'block'); + + s.tab('bypass', _('Bypass')); + + o = s.taboption('bypass', form.MultiValue, 'bypass_user', _('Bypass User')); + o.create = true; + + for (const user of users) { + o.value(user); + }; + + o = s.taboption('bypass', form.MultiValue, 'bypass_group', _('Bypass Group')); + o.create = true; + + for (const group of groups) { + o.value(group); + }; + + o = s.taboption('bypass', form.Flag, 'bypass_china_mainland_ip', _('Bypass China Mainland IP')); + o.rmempty = false; + + o = s.taboption('bypass', form.Value, 'proxy_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 853 993 995 8080 8443', _('Commonly Used Port')); + + o = s.taboption('bypass', form.Value, 'proxy_udp_dport', _('Destination UDP Port to Proxy')); + o.rmempty = false; + o.value('0-65535', _('All Port')); + o.value('123 443 8443', _('Commonly Used Port')); + + o = s.taboption('bypass', form.DynamicList, 'bypass_dscp', _('Bypass DSCP')); + o.datatype = 'range(0, 63)'; + + return m.render(); + } +}); diff --git a/luci-app-nikki/po/templates/nikki.pot b/luci-app-nikki/po/templates/nikki.pot new file mode 100644 index 000000000..a8fc655ca --- /dev/null +++ b/luci-app-nikki/po/templates/nikki.pot @@ -0,0 +1,735 @@ +msgid "" +msgstr "Content-Type: text/plain; charset=UTF-8" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:78 +msgid "API Port" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:82 +msgid "API Secret" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:61 +msgid "Access Control" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:64 +msgid "All Mode" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:136 +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:141 +msgid "All Port" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:91 +msgid "Allow Lan" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:200 +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:65 +msgid "Allow Mode" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/app.js:94 +#: applications/luci-app-nikki/root/usr/share/luci/menu.d/luci-app-nikki.json:13 +msgid "App Config" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/log.js:26 +msgid "App Log" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/app.js:43 +msgid "App Version" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:43 +msgid "Auto" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:199 +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:66 +msgid "Block Mode" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:115 +msgid "Bypass" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:131 +msgid "Bypass China Mainland IP" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:144 +msgid "Bypass DSCP" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:124 +msgid "Bypass Group" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:117 +msgid "Bypass User" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/editor.js:25 +msgid "Choose File" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/app.js:99 +msgid "Choose Profile" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/log.js:30 +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/log.js:63 +msgid "Clear Log" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:137 +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:142 +msgid "Commonly Used Port" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/app.js:128 +msgid "Core Environment Variable Config" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/log.js:59 +msgid "Core Log" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/app.js:57 +msgid "Core Status" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/app.js:50 +msgid "Core Version" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/app.js:117 +msgid "Cron Expression" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:171 +msgid "DNS Config" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:177 +msgid "DNS Mode" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:173 +msgid "DNS Port" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:134 +msgid "Destination TCP Port to Proxy" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:139 +msgid "Destination UDP Port to Proxy" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:137 +msgid "Device" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:40 +msgid "Direct Mode" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:45 +msgid "Disable" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/app.js:142 +msgid "Disable ECN of quic-go" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/app.js:138 +msgid "Disable GSO of quic-go" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/app.js:134 +msgid "Disable Loopback Detector" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/app.js:130 +msgid "Disable Safe Path Check" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:211 +msgid "DoH Prefer HTTP/3" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:237 +msgid "Domain Name" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:117 +msgid "Edit Authentications" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:161 +msgid "Edit DNS Hijacks" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:193 +msgid "Edit Fake-IP Filters" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:226 +msgid "Edit Hosts" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:268 +msgid "Edit Nameserver Policies" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:245 +msgid "Edit Nameservers" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/profile.js:32 +msgid "Edit Subscription" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/editor.js:23 +#: applications/luci-app-nikki/root/usr/share/luci/menu.d/luci-app-nikki.json:45 +msgid "Editor" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/app.js:96 +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:23 +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:44 +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:125 +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:234 +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:253 +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:276 +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:286 +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:318 +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:366 +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:31 +msgid "Enable" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:168 +msgid "Endpoint Independent NAT" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/profile.js:47 +msgid "Expire At" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:67 +msgid "External Control Config" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:203 +msgid "Fake-IP Cache" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:197 +msgid "Fake-IP Filter Mode" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:182 +msgid "Fake-IP Range" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/app.js:125 +msgid "Fast Reload" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/editor.js:36 +msgid "File for Mixin" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/editor.js:38 +msgid "File for Reserved IP" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/editor.js:39 +msgid "File for Reserved IP6" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/app.js:103 +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/editor.js:29 +msgid "File:" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:301 +msgid "Force Sniff Domain Name" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:149 +msgid "GSO" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:152 +msgid "GSO Max Size" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:28 +msgid "General Config" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:339 +msgid "GeoData Loader" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:335 +msgid "GeoIP Format" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:352 +msgid "GeoIP(ASN) Url" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:349 +msgid "GeoIP(DAT) Url" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:346 +msgid "GeoIP(MMDB) Url" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:343 +msgid "GeoSite Url" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:355 +msgid "GeoX Auto Update" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:333 +msgid "GeoX Config" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:358 +msgid "GeoX Update Interval" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:38 +msgid "Global Mode" +msgstr "" + +#: applications/luci-app-nikki/root/usr/share/rpcd/acl.d/luci-app-nikki.json:3 +msgid "Grant access to nikki procedures" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:94 +msgid "HTTP Port" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/app.js:39 +msgid "How To Use" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:240 +msgid "IP" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:43 +msgid "IPv4 DNS Hijack" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:49 +msgid "IPv4 Proxy" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:50 +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:214 +msgid "IPv6" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:46 +msgid "IPv6 DNS Hijack" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:52 +msgid "IPv6 Proxy" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:307 +msgid "Ignore Sniff Domain Name" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:89 +msgid "Inbound Config" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:108 +msgid "Interface" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:58 +msgid "Lan Proxy" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/profile.js:82 +msgid "Local" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/log.js:24 +#: applications/luci-app-nikki/root/usr/share/luci/menu.d/luci-app-nikki.json:53 +msgid "Log" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:30 +msgid "Log Level" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:145 +msgid "MTU" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:42 +msgid "Match Process" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:279 +msgid "Matcher" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:341 +msgid "Memory Conservative Loader" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:102 +msgid "Mixed Port" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:21 +#: applications/luci-app-nikki/root/usr/share/luci/menu.d/luci-app-nikki.json:29 +msgid "Mixin Config" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:364 +msgid "Mixin File Content" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:26 +msgid "Mixin Option" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:37 +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:63 +msgid "Mode" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:263 +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:282 +msgid "Nameserver" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/app.js:39 +#: applications/luci-app-nikki/root/usr/share/luci/menu.d/luci-app-nikki.json:3 +msgid "Nikki" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/app.js:15 +msgid "Not Running" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/app.js:89 +msgid "Open Dashboard" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:47 +msgid "Outbound Interface" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:114 +msgid "Overwrite Authentication" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:158 +msgid "Overwrite DNS Hijack" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:295 +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:330 +msgid "Overwrite Destination" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:188 +msgid "Overwrite Fake-IP Filter" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:298 +msgid "Overwrite Force Sniff Domain Name" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:223 +msgid "Overwrite Hosts" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:304 +msgid "Overwrite Ignore Sniff Domain Name" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:242 +msgid "Overwrite Nameserver" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:265 +msgid "Overwrite Nameserver Policy" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:310 +msgid "Overwrite Sniff By Protocol" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:131 +msgid "Password" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:366 +msgid "Please go to the editor tab to edit the file for mixin" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:327 +msgid "Port" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/profile.js:78 +msgid "Prefer" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/profile.js:18 +#: applications/luci-app-nikki/root/usr/share/luci/menu.d/luci-app-nikki.json:21 +msgid "Profile" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/editor.js:37 +msgid "Profile for Startup" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:321 +msgid "Protocol" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:27 +#: applications/luci-app-nikki/root/usr/share/luci/menu.d/luci-app-nikki.json:37 +msgid "Proxy Config" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:35 +msgid "Redirect Mode" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:106 +msgid "Redirect Port" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/app.js:69 +msgid "Reload Service" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/profile.js:81 +msgid "Remote" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:208 +msgid "Respect Rules" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/app.js:76 +msgid "Restart Service" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:55 +msgid "Router Proxy" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:39 +msgid "Rule Mode" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/app.js:15 +msgid "Running" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:98 +msgid "SOCKS Port" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:86 +msgid "Save Proxy Selection" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/app.js:114 +msgid "Scheduled Restart" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/log.js:53 +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/log.js:86 +msgid "Scroll To Bottom" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/tools/nikki.js:100 +#: applications/luci-app-nikki/htdocs/luci-static/resources/tools/nikki.js:118 +msgid "Service is not running." +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:313 +msgid "Sniff By Protocol" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:292 +msgid "Sniff Pure IP" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:289 +msgid "Sniff Redir-Host" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:284 +msgid "Sniffer Config" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:140 +msgid "Stack" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:340 +msgid "Standard Loader" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/app.js:110 +msgid "Start Delay" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/app.js:41 +msgid "Status" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/profile.js:28 +msgid "Subscription" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/profile.js:34 +msgid "Subscription Name" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/profile.js:66 +msgid "Subscription Url" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/app.js:107 +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/editor.js:33 +msgid "Subscription:" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:56 +msgid "TCP Concurrent" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:59 +msgid "TCP Keep Alive Idle" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:63 +msgid "TCP Keep Alive Interval" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:34 +msgid "TCP Proxy Mode" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:36 +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:40 +msgid "TPROXY Mode" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:110 +msgid "TPROXY Port" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:135 +msgid "TUN Config" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:37 +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:41 +msgid "TUN Mode" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/app.js:122 +msgid "Test Profile" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/profile.js:42 +msgid "Total" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:29 +msgid "Transparent Proxy" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/app.js:39 +msgid "Transparent Proxy with Mihomo on OpenWrt." +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:256 +msgid "Type" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:39 +msgid "UDP Proxy Mode" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:69 +msgid "UI Name" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:71 +msgid "UI Url" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:53 +msgid "Unify Delay" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/tools/nikki.js:80 +#: applications/luci-app-nikki/htdocs/luci-static/resources/tools/nikki.js:84 +msgid "Unknown" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/profile.js:60 +msgid "Update" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/profile.js:52 +msgid "Update At" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/app.js:83 +msgid "Update Dashboard" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/profile.js:20 +msgid "Upload Profile" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:220 +msgid "Use Hosts" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:217 +msgid "Use System Hosts" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/profile.js:37 +msgid "Used" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/profile.js:70 +msgid "User Agent" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:128 +msgid "Username" +msgstr "" diff --git a/luci-app-nikki/po/zh_Hans/nikki.po b/luci-app-nikki/po/zh_Hans/nikki.po new file mode 100644 index 000000000..3b987b2a3 --- /dev/null +++ b/luci-app-nikki/po/zh_Hans/nikki.po @@ -0,0 +1,742 @@ +msgid "" +msgstr "" +"Content-Type: text/plain; charset=UTF-8\n" +"Project-Id-Version: PACKAGE VERSION\n" +"Last-Translator: Automatically generated\n" +"Language-Team: none\n" +"Language: zh_Hans\n" +"MIME-Version: 1.0\n" +"Content-Transfer-Encoding: 8bit\n" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:78 +msgid "API Port" +msgstr "API 端口" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:82 +msgid "API Secret" +msgstr "API 密钥" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:61 +msgid "Access Control" +msgstr "访问控制" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:64 +msgid "All Mode" +msgstr "全部模式" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:136 +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:141 +msgid "All Port" +msgstr "全部端口" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:91 +msgid "Allow Lan" +msgstr "允许局域网访问" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:200 +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:65 +msgid "Allow Mode" +msgstr "白名单模式" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/app.js:94 +#: applications/luci-app-nikki/root/usr/share/luci/menu.d/luci-app-nikki.json:13 +msgid "App Config" +msgstr "插件配置" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/log.js:26 +msgid "App Log" +msgstr "插件日志" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/app.js:43 +msgid "App Version" +msgstr "插件版本" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:43 +msgid "Auto" +msgstr "自动" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:199 +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:66 +msgid "Block Mode" +msgstr "黑名单模式" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:115 +msgid "Bypass" +msgstr "绕过" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:131 +msgid "Bypass China Mainland IP" +msgstr "绕过中国大陆 IP" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:144 +msgid "Bypass DSCP" +msgstr "绕过 DSCP" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:124 +msgid "Bypass Group" +msgstr "绕过用户组" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:117 +msgid "Bypass User" +msgstr "绕过用户" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/editor.js:25 +msgid "Choose File" +msgstr "选择文件" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/app.js:99 +msgid "Choose Profile" +msgstr "选择配置文件" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/log.js:30 +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/log.js:63 +msgid "Clear Log" +msgstr "清空日志" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:137 +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:142 +msgid "Commonly Used Port" +msgstr "常用端口" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/app.js:128 +msgid "Core Environment Variable Config" +msgstr "核心环境变量配置" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/log.js:59 +msgid "Core Log" +msgstr "核心日志" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/app.js:57 +msgid "Core Status" +msgstr "核心状态" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/app.js:50 +msgid "Core Version" +msgstr "核心版本" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/app.js:117 +msgid "Cron Expression" +msgstr "Cron 表达式" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:171 +msgid "DNS Config" +msgstr "DNS 配置" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:177 +msgid "DNS Mode" +msgstr "DNS 模式" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:173 +msgid "DNS Port" +msgstr "DNS 端口" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:134 +msgid "Destination TCP Port to Proxy" +msgstr "要代理的 TCP 目标端口" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:139 +msgid "Destination UDP Port to Proxy" +msgstr "要代理的 UDP 目标端口" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:137 +msgid "Device" +msgstr "设备名称" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:40 +msgid "Direct Mode" +msgstr "直连模式" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:45 +msgid "Disable" +msgstr "禁用" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/app.js:142 +msgid "Disable ECN of quic-go" +msgstr "禁用 quic-go 的显式拥塞通知" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/app.js:138 +msgid "Disable GSO of quic-go" +msgstr "禁用 quic-go 的通用分段卸载" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/app.js:134 +msgid "Disable Loopback Detector" +msgstr "禁用回环检测" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/app.js:130 +msgid "Disable Safe Path Check" +msgstr "禁用安全路径检查" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:211 +msgid "DoH Prefer HTTP/3" +msgstr "DoH 优先 HTTP/3" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:237 +msgid "Domain Name" +msgstr "域名" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:117 +msgid "Edit Authentications" +msgstr "编辑身份验证" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:161 +msgid "Edit DNS Hijacks" +msgstr "编辑 DNS 劫持" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:193 +msgid "Edit Fake-IP Filters" +msgstr "编辑 Fake-IP 过滤列表" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:226 +msgid "Edit Hosts" +msgstr "编辑 Hosts" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:268 +msgid "Edit Nameserver Policies" +msgstr "编辑 DNS 服务器查询策略" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:245 +msgid "Edit Nameservers" +msgstr "编辑 DNS 服务器" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/profile.js:32 +msgid "Edit Subscription" +msgstr "编辑订阅" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/editor.js:23 +#: applications/luci-app-nikki/root/usr/share/luci/menu.d/luci-app-nikki.json:45 +msgid "Editor" +msgstr "编辑器" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/app.js:96 +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:23 +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:44 +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:125 +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:234 +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:253 +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:276 +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:286 +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:318 +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:366 +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:31 +msgid "Enable" +msgstr "启用" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:168 +msgid "Endpoint Independent NAT" +msgstr "独立于端点的 NAT" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/profile.js:47 +msgid "Expire At" +msgstr "到期时间" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:67 +msgid "External Control Config" +msgstr "外部控制配置" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:203 +msgid "Fake-IP Cache" +msgstr "Fake-IP 缓存" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:197 +msgid "Fake-IP Filter Mode" +msgstr "Fake-IP 过滤模式" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:182 +msgid "Fake-IP Range" +msgstr "Fake-IP 范围" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/app.js:125 +msgid "Fast Reload" +msgstr "快速重载" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/editor.js:36 +msgid "File for Mixin" +msgstr "用于混入的文件" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/editor.js:38 +msgid "File for Reserved IP" +msgstr "IPv4 保留地址" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/editor.js:39 +msgid "File for Reserved IP6" +msgstr "IPv6 保留地址" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/app.js:103 +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/editor.js:29 +msgid "File:" +msgstr "文件:" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:301 +msgid "Force Sniff Domain Name" +msgstr "强制嗅探的域名" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:149 +msgid "GSO" +msgstr "通用分段卸载" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:152 +msgid "GSO Max Size" +msgstr "分段最大长度" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:28 +msgid "General Config" +msgstr "全局配置" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:339 +msgid "GeoData Loader" +msgstr "GeoData 加载器" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:335 +msgid "GeoIP Format" +msgstr "GeoIP 格式" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:352 +msgid "GeoIP(ASN) Url" +msgstr "GeoIP(ASN) 下载地址" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:349 +msgid "GeoIP(DAT) Url" +msgstr "GeoIP(DAT) 下载地址" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:346 +msgid "GeoIP(MMDB) Url" +msgstr "GeoIP(MMDB) 下载地址" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:343 +msgid "GeoSite Url" +msgstr "GeoSite 下载地址" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:355 +msgid "GeoX Auto Update" +msgstr "定时更新GeoX文件" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:333 +msgid "GeoX Config" +msgstr "GeoX 配置" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:358 +msgid "GeoX Update Interval" +msgstr "GeoX 文件更新间隔" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:38 +msgid "Global Mode" +msgstr "全局模式" + +#: applications/luci-app-nikki/root/usr/share/rpcd/acl.d/luci-app-nikki.json:3 +msgid "Grant access to nikki procedures" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:94 +msgid "HTTP Port" +msgstr "HTTP 端口" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/app.js:39 +msgid "How To Use" +msgstr "使用说明" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:240 +msgid "IP" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:43 +msgid "IPv4 DNS Hijack" +msgstr "IPv4 DNS 劫持" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:49 +msgid "IPv4 Proxy" +msgstr "IPv4 代理" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:50 +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:214 +msgid "IPv6" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:46 +msgid "IPv6 DNS Hijack" +msgstr "IPv6 DNS 劫持" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:52 +msgid "IPv6 Proxy" +msgstr "IPv6 代理" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:307 +msgid "Ignore Sniff Domain Name" +msgstr "忽略嗅探的域名" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:89 +msgid "Inbound Config" +msgstr "入站配置" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:108 +msgid "Interface" +msgstr "接口" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:58 +msgid "Lan Proxy" +msgstr "局域网代理" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/profile.js:82 +msgid "Local" +msgstr "本地" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/log.js:24 +#: applications/luci-app-nikki/root/usr/share/luci/menu.d/luci-app-nikki.json:53 +msgid "Log" +msgstr "日志" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:30 +msgid "Log Level" +msgstr "日志级别" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:145 +msgid "MTU" +msgstr "最大传输单元" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:42 +msgid "Match Process" +msgstr "匹配进程" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:279 +msgid "Matcher" +msgstr "匹配" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:341 +msgid "Memory Conservative Loader" +msgstr "为内存受限设备优化的加载器" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:102 +msgid "Mixed Port" +msgstr "混合端口" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:21 +#: applications/luci-app-nikki/root/usr/share/luci/menu.d/luci-app-nikki.json:29 +msgid "Mixin Config" +msgstr "混入配置" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:364 +msgid "Mixin File Content" +msgstr "混入文件内容" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:26 +msgid "Mixin Option" +msgstr "混入选项" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:37 +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:63 +msgid "Mode" +msgstr "模式" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:263 +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:282 +msgid "Nameserver" +msgstr "DNS 服务器" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/app.js:39 +#: applications/luci-app-nikki/root/usr/share/luci/menu.d/luci-app-nikki.json:3 +msgid "Nikki" +msgstr "" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/app.js:15 +msgid "Not Running" +msgstr "未在运行" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/app.js:89 +msgid "Open Dashboard" +msgstr "打开面板" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:47 +msgid "Outbound Interface" +msgstr "出站接口" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:114 +msgid "Overwrite Authentication" +msgstr "覆盖身份验证" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:158 +msgid "Overwrite DNS Hijack" +msgstr "覆盖 DNS 劫持" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:295 +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:330 +msgid "Overwrite Destination" +msgstr "将嗅探结果作为连接目标" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:188 +msgid "Overwrite Fake-IP Filter" +msgstr "覆盖 Fake-IP 过滤列表" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:298 +msgid "Overwrite Force Sniff Domain Name" +msgstr "覆盖强制嗅探的域名" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:223 +msgid "Overwrite Hosts" +msgstr "覆盖 Hosts" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:304 +msgid "Overwrite Ignore Sniff Domain Name" +msgstr "覆盖忽略嗅探的域名" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:242 +msgid "Overwrite Nameserver" +msgstr "覆盖 DNS 服务器" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:265 +msgid "Overwrite Nameserver Policy" +msgstr "覆盖 DNS 服务器查询策略" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:310 +msgid "Overwrite Sniff By Protocol" +msgstr "覆盖按协议嗅探" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:131 +msgid "Password" +msgstr "密码" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:366 +msgid "Please go to the editor tab to edit the file for mixin" +msgstr "请前往编辑器标签编辑用于混入的文件" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:327 +msgid "Port" +msgstr "端口" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/profile.js:78 +msgid "Prefer" +msgstr "优先" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/profile.js:18 +#: applications/luci-app-nikki/root/usr/share/luci/menu.d/luci-app-nikki.json:21 +msgid "Profile" +msgstr "配置文件" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/editor.js:37 +msgid "Profile for Startup" +msgstr "用于启动的配置文件" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:321 +msgid "Protocol" +msgstr "协议" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:27 +#: applications/luci-app-nikki/root/usr/share/luci/menu.d/luci-app-nikki.json:37 +msgid "Proxy Config" +msgstr "代理配置" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:35 +msgid "Redirect Mode" +msgstr "Redirect 模式" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:106 +msgid "Redirect Port" +msgstr "Redirect 端口" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/app.js:69 +msgid "Reload Service" +msgstr "重载服务" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/profile.js:81 +msgid "Remote" +msgstr "远程" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:208 +msgid "Respect Rules" +msgstr "遵循分流规则" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/app.js:76 +msgid "Restart Service" +msgstr "重启服务" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:55 +msgid "Router Proxy" +msgstr "路由器代理" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:39 +msgid "Rule Mode" +msgstr "规则模式" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/app.js:15 +msgid "Running" +msgstr "运行中" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:98 +msgid "SOCKS Port" +msgstr "SOCKS 端口" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:86 +msgid "Save Proxy Selection" +msgstr "保存节点/策略组选择" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/app.js:114 +msgid "Scheduled Restart" +msgstr "定时重启" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/log.js:53 +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/log.js:86 +msgid "Scroll To Bottom" +msgstr "滚动到底部" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/tools/nikki.js:100 +#: applications/luci-app-nikki/htdocs/luci-static/resources/tools/nikki.js:118 +msgid "Service is not running." +msgstr "服务未在运行。" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:313 +msgid "Sniff By Protocol" +msgstr "按协议嗅探" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:292 +msgid "Sniff Pure IP" +msgstr "嗅探纯 IP 连接" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:289 +msgid "Sniff Redir-Host" +msgstr "嗅探 Redir-Host 流量" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:284 +msgid "Sniffer Config" +msgstr "嗅探器配置" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:140 +msgid "Stack" +msgstr "栈" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:340 +msgid "Standard Loader" +msgstr "标准加载器" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/app.js:110 +msgid "Start Delay" +msgstr "启动延迟" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/app.js:41 +msgid "Status" +msgstr "状态" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/profile.js:28 +msgid "Subscription" +msgstr "订阅" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/profile.js:34 +msgid "Subscription Name" +msgstr "订阅名称" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/profile.js:66 +msgid "Subscription Url" +msgstr "订阅链接" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/app.js:107 +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/editor.js:33 +msgid "Subscription:" +msgstr "订阅:" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:56 +msgid "TCP Concurrent" +msgstr "TCP 并发" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:59 +msgid "TCP Keep Alive Idle" +msgstr "TCP Keep Alive 空闲" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:63 +msgid "TCP Keep Alive Interval" +msgstr "TCP Keep Alive 间隔" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:34 +msgid "TCP Proxy Mode" +msgstr "TCP 代理模式" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:36 +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:40 +msgid "TPROXY Mode" +msgstr "TPROXY 模式" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:110 +msgid "TPROXY Port" +msgstr "TPROXY 端口" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:135 +msgid "TUN Config" +msgstr "TUN 配置" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:37 +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:41 +msgid "TUN Mode" +msgstr "TUN 模式" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/app.js:122 +msgid "Test Profile" +msgstr "检查配置文件" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/profile.js:42 +msgid "Total" +msgstr "总量" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:29 +msgid "Transparent Proxy" +msgstr "透明代理" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/app.js:39 +msgid "Transparent Proxy with Mihomo on OpenWrt." +msgstr "在 OpenWrt 上使用 Mihomo 进行透明代理。" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:256 +msgid "Type" +msgstr "类型" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/proxy.js:39 +msgid "UDP Proxy Mode" +msgstr "UDP 代理模式" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:69 +msgid "UI Name" +msgstr "UI 名称" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:71 +msgid "UI Url" +msgstr "UI 下载地址" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:53 +msgid "Unify Delay" +msgstr "统一延迟" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/tools/nikki.js:80 +#: applications/luci-app-nikki/htdocs/luci-static/resources/tools/nikki.js:84 +msgid "Unknown" +msgstr "未知" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/profile.js:60 +msgid "Update" +msgstr "更新" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/profile.js:52 +msgid "Update At" +msgstr "更新时间" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/app.js:83 +msgid "Update Dashboard" +msgstr "更新面板" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/profile.js:20 +msgid "Upload Profile" +msgstr "上传配置文件" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:220 +msgid "Use Hosts" +msgstr "使用 Hosts" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:217 +msgid "Use System Hosts" +msgstr "使用系统的 Hosts" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/profile.js:37 +msgid "Used" +msgstr "已使用" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/profile.js:70 +msgid "User Agent" +msgstr "用户代理(UA)" + +#: applications/luci-app-nikki/htdocs/luci-static/resources/view/nikki/mixin.js:128 +msgid "Username" +msgstr "用户名" diff --git a/luci-app-nikki/root/etc/uci-defaults/40_luci-nikki b/luci-app-nikki/root/etc/uci-defaults/40_luci-nikki new file mode 100644 index 000000000..080086891 --- /dev/null +++ b/luci-app-nikki/root/etc/uci-defaults/40_luci-nikki @@ -0,0 +1,4 @@ +#!/bin/sh +rm -rf /var/luci-modulecache/; rm -f /var/luci-indexcache; +[ -x /etc/init.d/rpcd ] && /etc/init.d/rpcd reload; +exit 0 diff --git a/luci-app-nikki/root/usr/libexec/nikki-call b/luci-app-nikki/root/usr/libexec/nikki-call new file mode 100755 index 000000000..1ddda1705 --- /dev/null +++ b/luci-app-nikki/root/usr/libexec/nikki-call @@ -0,0 +1,57 @@ +#!/bin/sh + +. $IPKG_INSTROOT/etc/nikki/scripts/include.sh + +action=$1 +shift + +case "$action" in + clear_log) + case "$1" in + app) + echo -n > "$APP_LOG_PATH" + ;; + core) + echo -n > "$CORE_LOG_PATH" + ;; + esac + ;; + subscription) + case "$1" in + update) + /etc/init.d/nikki update_subscription "$2" + ;; + esac + ;; + load) + case "$1" in + profile) + yq -M -o json < "$RUN_PROFILE_PATH" + ;; + esac + ;; + service) + case "$1" in + reload) + /etc/init.d/nikki reload + ;; + restart) + /etc/init.d/nikki restart + ;; + esac + ;; + version) + case "$1" in + app) + if [ -x "/bin/opkg" ]; then + opkg list-installed "luci-app-nikki" | cut -d " " -f 3 + elif [ -x "/usr/bin/apk" ]; then + apk list -I "luci-app-nikki" | cut -d ' ' -f 1 | cut -d '-' -f 4 + fi + ;; + core) + mihomo -v | grep "Mihomo" | cut -d " " -f 3 + ;; + esac + ;; +esac diff --git a/luci-app-nikki/root/usr/share/luci/menu.d/luci-app-nikki.json b/luci-app-nikki/root/usr/share/luci/menu.d/luci-app-nikki.json new file mode 100644 index 000000000..35aa9dba6 --- /dev/null +++ b/luci-app-nikki/root/usr/share/luci/menu.d/luci-app-nikki.json @@ -0,0 +1,60 @@ +{ + "admin/services/nikki": { + "title": "Nikki", + "action": { + "type": "firstchild" + }, + "depends": { + "acl": [ "luci-app-nikki" ], + "uci": { "nikki": true } + } + }, + "admin/services/nikki/config": { + "title": "App Config", + "order": 10, + "action": { + "type": "view", + "path": "nikki/app" + } + }, + "admin/services/nikki/profile": { + "title": "Profile", + "order": 20, + "action": { + "type": "view", + "path": "nikki/profile" + } + }, + "admin/services/nikki/mixin": { + "title": "Mixin Config", + "order": 30, + "action": { + "type": "view", + "path": "nikki/mixin" + } + }, + "admin/services/nikki/proxy": { + "title": "Proxy Config", + "order": 40, + "action": { + "type": "view", + "path": "nikki/proxy" + } + }, + "admin/services/nikki/editor": { + "title": "Editor", + "order": 50, + "action": { + "type": "view", + "path": "nikki/editor" + } + }, + "admin/services/nikki/log": { + "title": "Log", + "order": 60, + "action": { + "type": "view", + "path": "nikki/log" + } + } +} \ No newline at end of file diff --git a/luci-app-nikki/root/usr/share/rpcd/acl.d/luci-app-nikki.json b/luci-app-nikki/root/usr/share/rpcd/acl.d/luci-app-nikki.json new file mode 100644 index 000000000..e9a23a7f0 --- /dev/null +++ b/luci-app-nikki/root/usr/share/rpcd/acl.d/luci-app-nikki.json @@ -0,0 +1,38 @@ +{ + "luci-app-nikki": { + "description": "Grant access to nikki procedures", + "read": { + "uci": [ "nikki" ], + "ubus": { + "service": [ "list" ] + }, + "file": { + "/etc/passwd": ["read"], + "/etc/group": ["read"], + "/etc/nikki/profiles/*.yaml": ["read"], + "/etc/nikki/profiles/*.yml": ["read"], + "/etc/nikki/subscriptions/*.yaml": ["read"], + "/etc/nikki/subscriptions/*.yml": ["read"], + "/etc/nikki/mixin.yaml": ["read"], + "/etc/nikki/run/config.yaml": ["read"], + "/etc/nikki/nftables/reserved_ip.nft": ["read"], + "/etc/nikki/nftables/reserved_ip6.nft": ["read"], + "/var/log/nikki/*.log": ["read"], + "/usr/libexec/nikki-call": ["exec"] + } + }, + "write": { + "uci": [ "nikki" ], + "file": { + "/etc/nikki/profiles/*.yaml": ["write"], + "/etc/nikki/profiles/*.yml": ["write"], + "/etc/nikki/subscriptions/*.yaml": ["write"], + "/etc/nikki/subscriptions/*.yml": ["write"], + "/etc/nikki/mixin.yaml": ["write"], + "/etc/nikki/run/config.yaml": ["write"], + "/etc/nikki/nftables/reserved_ip.nft": ["write"], + "/etc/nikki/nftables/reserved_ip6.nft": ["write"] + } + } + } +} \ No newline at end of file