mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-10-30 07:05:13 +08:00
💋 Sync 2026-03-21 18:21:15
This commit is contained in:
1 parent
7147204921
commit
5168976bf5
36 files changed
+24624
-9
No files matched your search
+2
-2
@@ -6,8 +6,8 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=gowebdav
|
||||
PKG_VERSION:=5.11.2
|
||||
PKG_RELEASE:=2
|
||||
PKG_VERSION:=5.11.3
|
||||
PKG_RELEASE:=3
|
||||
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
|
||||
PKG_SOURCE_URL:=https://codeload.github.com/hacdias/webdav/tar.gz/v$(PKG_VERSION)?
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_VERSION:=1.1.0
|
||||
PKG_RELEASE:=1
|
||||
|
||||
LUCI_TITLE:=LuCI Support for momo
|
||||
LUCI_DEPENDS:=+luci-base +momo
|
||||
|
||||
include $(TOPDIR)/feeds/luci/luci.mk
|
||||
|
||||
# call BuildPackage - OpenWrt buildroot signature
|
||||
@@ -0,0 +1,152 @@
|
||||
'use strict';
|
||||
'require baseclass';
|
||||
'require uci';
|
||||
'require fs';
|
||||
'require rpc';
|
||||
'require request';
|
||||
|
||||
const callRCList = rpc.declare({
|
||||
object: 'rc',
|
||||
method: 'list',
|
||||
params: ['name'],
|
||||
expect: { '': {} }
|
||||
});
|
||||
|
||||
const callRCInit = rpc.declare({
|
||||
object: 'rc',
|
||||
method: 'init',
|
||||
params: ['name', 'action'],
|
||||
expect: { '': {} }
|
||||
});
|
||||
|
||||
const callMomoGetPaths = rpc.declare({
|
||||
object: 'luci.momo',
|
||||
method: 'get_paths',
|
||||
expect: { '': {} }
|
||||
});
|
||||
|
||||
const callMomoVersion = rpc.declare({
|
||||
object: 'luci.momo',
|
||||
method: 'version',
|
||||
expect: { '': {} }
|
||||
});
|
||||
|
||||
const callMomoProfile = rpc.declare({
|
||||
object: 'luci.momo',
|
||||
method: 'profile',
|
||||
params: ['defaults'],
|
||||
expect: { '': {} }
|
||||
});
|
||||
|
||||
const callMomoUpdateSubscription = rpc.declare({
|
||||
object: 'luci.momo',
|
||||
method: 'update_subscription',
|
||||
params: ['section_id'],
|
||||
expect: { '': {} }
|
||||
});
|
||||
|
||||
const callMomoAPI = rpc.declare({
|
||||
object: 'luci.momo',
|
||||
method: 'api',
|
||||
params: ['method', 'path', 'query', 'body'],
|
||||
expect: { '': {} }
|
||||
});
|
||||
|
||||
const callMomoGetIdentifiers = rpc.declare({
|
||||
object: 'luci.momo',
|
||||
method: 'get_identifiers',
|
||||
expect: { '': {} }
|
||||
});
|
||||
|
||||
const callMomoDebug = rpc.declare({
|
||||
object: 'luci.momo',
|
||||
method: 'debug',
|
||||
expect: { '': {} }
|
||||
});
|
||||
|
||||
return baseclass.extend({
|
||||
getPaths: async function () {
|
||||
return callMomoGetPaths();
|
||||
},
|
||||
|
||||
status: async function () {
|
||||
return (await callRCList('momo'))?.momo?.running;
|
||||
},
|
||||
|
||||
reload: function () {
|
||||
return callRCInit('momo', 'reload');
|
||||
},
|
||||
|
||||
restart: function () {
|
||||
return callRCInit('momo', 'restart');
|
||||
},
|
||||
|
||||
version: function () {
|
||||
return callMomoVersion();
|
||||
},
|
||||
|
||||
profile: function (defaults) {
|
||||
return callMomoProfile(defaults);
|
||||
},
|
||||
|
||||
updateSubscription: function (section_id) {
|
||||
return callMomoUpdateSubscription(section_id);
|
||||
},
|
||||
|
||||
updateDashboard: function () {
|
||||
return callMomoAPI('POST', '/upgrade/ui');
|
||||
},
|
||||
|
||||
openDashboard: async function () {
|
||||
const profile = await callMomoProfile({ 'experimental': { 'clash_api': { 'external_controller': null, 'secret': null } } });
|
||||
const apiListen = profile?.['experimental']?.['clash_api']?.['external_controller'];
|
||||
const apiSecret = profile?.['experimental']?.['clash_api']?.['secret'] ?? '';
|
||||
if (!apiListen) {
|
||||
return Promise.reject('Clash API has not been configured');
|
||||
}
|
||||
const apiPort = apiListen.substring(apiListen.lastIndexOf(':') + 1);
|
||||
const params = {
|
||||
host: window.location.hostname,
|
||||
hostname: window.location.hostname,
|
||||
port: apiPort,
|
||||
secret: apiSecret
|
||||
};
|
||||
const query = new URLSearchParams(params).toString();
|
||||
const url = `http://${window.location.hostname}:${apiPort}/ui/?${query}`;
|
||||
setTimeout(function () { window.open(url, '_blank') }, 0);
|
||||
return Promise.resolve();
|
||||
},
|
||||
|
||||
getIdentifiers: function () {
|
||||
return callMomoGetIdentifiers();
|
||||
},
|
||||
|
||||
listProfiles: async function () {
|
||||
const paths = await this.getPaths();
|
||||
return L.resolveDefault(fs.list(paths.profiles_dir), []);
|
||||
},
|
||||
|
||||
getAppLog: async function () {
|
||||
const paths = await this.getPaths();
|
||||
return L.resolveDefault(fs.read_direct(paths.app_log_path));
|
||||
},
|
||||
|
||||
getCoreLog: async function () {
|
||||
const paths = await this.getPaths();
|
||||
return L.resolveDefault(fs.read_direct(paths.core_log_path));
|
||||
},
|
||||
|
||||
clearAppLog: async function () {
|
||||
const paths = await this.getPaths();
|
||||
return fs.write(paths.app_log_path);
|
||||
},
|
||||
|
||||
clearCoreLog: async function () {
|
||||
const paths = await this.getPaths();
|
||||
return fs.write(paths.core_log_path);
|
||||
},
|
||||
|
||||
debug: function () {
|
||||
return callMomoDebug();
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,169 @@
|
||||
'use strict';
|
||||
'require form';
|
||||
'require view';
|
||||
'require uci';
|
||||
'require poll';
|
||||
'require tools.momo as momo';
|
||||
|
||||
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('momo'),
|
||||
momo.version(),
|
||||
momo.status(),
|
||||
momo.listProfiles()
|
||||
]);
|
||||
},
|
||||
render: function (data) {
|
||||
const subscriptions = uci.sections('momo', 'subscription');
|
||||
const appVersion = data[1].app ?? '';
|
||||
const coreVersion = data[1].core ?? '';
|
||||
const running = data[2];
|
||||
const profiles = data[3];
|
||||
|
||||
let m, s, o;
|
||||
|
||||
m = new form.Map('momo', _('Momo'), `${_('Transparent Proxy with sing-box on OpenWrt.')} <a href="https://github.com/nikkinikki-org/OpenWrt-momo/wiki" target="_blank">${_('How To Use')}</a>`);
|
||||
|
||||
s = m.section(form.TableSection, 'placeholder', _('Status'));
|
||||
s.anonymous = true;
|
||||
|
||||
o = s.option(form.Value, '_app_version', _('App Version'));
|
||||
o.readonly = true;
|
||||
o.load = function () {
|
||||
return appVersion;
|
||||
};
|
||||
o.write = function () { };
|
||||
|
||||
o = s.option(form.Value, '_core_version', _('Core Version'));
|
||||
o.readonly = true;
|
||||
o.load = function () {
|
||||
return coreVersion;
|
||||
};
|
||||
o.write = function () { };
|
||||
|
||||
o = s.option(form.DummyValue, '_core_status', _('Core Status'));
|
||||
o.cfgvalue = function () {
|
||||
return renderStatus(running);
|
||||
};
|
||||
poll.add(function () {
|
||||
return L.resolveDefault(momo.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 momo.reload();
|
||||
};
|
||||
|
||||
o = s.option(form.Button, 'restart');
|
||||
o.inputstyle = 'negative';
|
||||
o.inputtitle = _('Restart Service');
|
||||
o.onclick = function () {
|
||||
return momo.restart();
|
||||
};
|
||||
|
||||
o = s.option(form.Button, 'update_dashboard');
|
||||
o.inputstyle = 'positive';
|
||||
o.inputtitle = _('Update Dashboard');
|
||||
o.onclick = function () {
|
||||
return momo.updateDashboard();
|
||||
};
|
||||
|
||||
o = s.option(form.Button, 'open_dashboard');
|
||||
o.inputtitle = _('Open Dashboard');
|
||||
o.onclick = function () {
|
||||
return momo.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 = _('Start Immidiately');
|
||||
|
||||
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, 'core_only', _('Core Only'));
|
||||
o.rmempty = false;
|
||||
|
||||
s = m.section(form.NamedSection, 'procd', 'procd', _('procd Config'));
|
||||
|
||||
s.tab('general', _('General Config'));
|
||||
|
||||
o = s.taboption('general', form.Flag, 'fast_reload', _('Fast Reload'));
|
||||
o.rmempty = false;
|
||||
|
||||
s.tab('rlimit', _('RLIMIT Config'));
|
||||
|
||||
o = s.taboption('rlimit', form.Value, 'rlimit_address_space_soft', _('Address Space Size Soft Limit'));
|
||||
o.datatype = 'uinteger';
|
||||
o.placeholder = _('Unlimited');
|
||||
|
||||
o = s.taboption('rlimit', form.Value, 'rlimit_address_space_hard', _('Address Space Size Hard Limit'));
|
||||
o.datatype = 'uinteger';
|
||||
o.placeholder = _('Unlimited');
|
||||
|
||||
o = s.taboption('rlimit', form.Value, 'rlimit_data_soft', _('Heap Size Soft Limit'));
|
||||
o.datatype = 'uinteger';
|
||||
o.placeholder = _('Unlimited');
|
||||
|
||||
o = s.taboption('rlimit', form.Value, 'rlimit_data_hard', _('Heap Size Hard Limit'));
|
||||
o.datatype = 'uinteger';
|
||||
o.placeholder = _('Unlimited');
|
||||
|
||||
o = s.taboption('rlimit', form.Value, 'rlimit_stack_soft', _('Stack Size Soft Limit'));
|
||||
o.datatype = 'uinteger';
|
||||
o.placeholder = _('Unlimited');
|
||||
|
||||
o = s.taboption('rlimit', form.Value, 'rlimit_stack_hard', _('Stack Size Hard Limit'));
|
||||
o.datatype = 'uinteger';
|
||||
o.placeholder = _('Unlimited');
|
||||
|
||||
o = s.taboption('rlimit', form.Value, 'rlimit_nofile_soft', _('Number of Open Files Soft Limit'));
|
||||
o.datatype = 'uinteger';
|
||||
|
||||
o = s.taboption('rlimit', form.Value, 'rlimit_nofile_hard', _('Number of Open Files Hard Limit'));
|
||||
o.datatype = 'uinteger';
|
||||
|
||||
return m.render();
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,69 @@
|
||||
'use strict';
|
||||
'require form';
|
||||
'require view';
|
||||
'require uci';
|
||||
'require fs';
|
||||
'require tools.momo as momo'
|
||||
|
||||
return view.extend({
|
||||
load: function () {
|
||||
return Promise.all([
|
||||
uci.load('momo'),
|
||||
momo.getPaths(),
|
||||
momo.listProfiles(),
|
||||
]);
|
||||
},
|
||||
render: function (data) {
|
||||
const subscriptions = uci.sections('momo', 'subscription');
|
||||
const paths = data[1];
|
||||
const profiles = data[2];
|
||||
|
||||
let m, s, o;
|
||||
|
||||
m = new form.Map('momo');
|
||||
|
||||
s = m.section(form.NamedSection, 'placeholder', 'placeholder', _('Editor'));
|
||||
|
||||
o = s.option(form.ListValue, '_file', _('Choose File'));
|
||||
o.optional = true;
|
||||
|
||||
for (const profile of profiles) {
|
||||
o.value(paths.profiles_dir + '/' + profile.name, _('File:') + profile.name);
|
||||
};
|
||||
|
||||
for (const subscription of subscriptions) {
|
||||
o.value(paths.subscriptions_dir + '/' + subscription['.name'] + '.json', _('Subscription:') + subscription.name);
|
||||
};
|
||||
|
||||
o.value(paths.run_profile_path, _('Profile for Startup'));
|
||||
|
||||
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('_file_content', section_id)[0].getUIElement(section_id).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('_file', section_id)[0].formvalue(section_id);
|
||||
return fs.write(path, formvalue);
|
||||
};
|
||||
o.remove = function (section_id) {
|
||||
const path = m.lookupOption('_file', section_id)[0].formvalue(section_id);
|
||||
return fs.write(path);
|
||||
};
|
||||
|
||||
return m.render();
|
||||
},
|
||||
handleSaveApply: function (ev, mode) {
|
||||
return this.handleSave(ev).finally(function () {
|
||||
return mode === '0' ? momo.reload() : momo.restart();
|
||||
});
|
||||
},
|
||||
handleReset: null
|
||||
});
|
||||
@@ -0,0 +1,126 @@
|
||||
'use strict';
|
||||
'require form';
|
||||
'require view';
|
||||
'require uci';
|
||||
'require fs';
|
||||
'require poll';
|
||||
'require tools.momo as momo';
|
||||
|
||||
return view.extend({
|
||||
load: function () {
|
||||
return Promise.all([
|
||||
uci.load('momo'),
|
||||
momo.getPaths(),
|
||||
momo.getAppLog(),
|
||||
momo.getCoreLog()
|
||||
]);
|
||||
},
|
||||
render: function (data) {
|
||||
const paths = data[1];
|
||||
const appLog = data[2];
|
||||
const coreLog = data[3];
|
||||
|
||||
let m, s, o;
|
||||
|
||||
m = new form.Map('momo');
|
||||
|
||||
s = m.section(form.NamedSection, 'placeholder', 'placeholder', _('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 (_, section_id) {
|
||||
m.lookupOption('_app_log', section_id)[0].getUIElement(section_id).setValue('');
|
||||
return momo.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(momo.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 (_, section_id) {
|
||||
const element = m.lookupOption('_app_log', section_id)[0].getUIElement(section_id).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 (_, section_id) {
|
||||
m.lookupOption('_core_log', section_id)[0].getUIElement(section_id).setValue('');
|
||||
return momo.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(momo.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 (_, section_id) {
|
||||
const element = m.lookupOption('_core_log', section_id)[0].getUIElement(section_id).node.firstChild;
|
||||
element.scrollTop = element.scrollHeight;
|
||||
};
|
||||
|
||||
s.tab('debug_log', _('Debug Log'));
|
||||
|
||||
o = s.taboption('debug_log', form.Button, '_generate_download_debug_log');
|
||||
o.inputstyle = 'negative';
|
||||
o.inputtitle = _('Generate & Download');
|
||||
o.onclick = function () {
|
||||
return momo.debug().then(function () {
|
||||
fs.read_direct(paths.debug_log_path, 'blob').then(function (data) {
|
||||
// create url
|
||||
const url = window.URL.createObjectURL(data, { type: 'text/markdown' });
|
||||
// create link
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = 'debug.log';
|
||||
// append to body
|
||||
document.body.appendChild(link);
|
||||
// download
|
||||
link.click();
|
||||
// remove from body
|
||||
document.body.removeChild(link);
|
||||
// revoke url
|
||||
window.URL.revokeObjectURL(url);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
return m.render();
|
||||
},
|
||||
handleSaveApply: null,
|
||||
handleSave: null,
|
||||
handleReset: null
|
||||
});
|
||||
@@ -0,0 +1,90 @@
|
||||
'use strict';
|
||||
'require form';
|
||||
'require view';
|
||||
'require uci';
|
||||
'require tools.momo as momo';
|
||||
|
||||
return view.extend({
|
||||
load: function () {
|
||||
return Promise.all([
|
||||
uci.load('momo'),
|
||||
momo.getPaths(),
|
||||
]);
|
||||
},
|
||||
render: function (data) {
|
||||
const paths = data[1];
|
||||
|
||||
let m, s, o, so;
|
||||
|
||||
m = new form.Map('momo');
|
||||
|
||||
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 = paths.profiles_dir;
|
||||
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 momo.updateSubscription(section_id);
|
||||
};
|
||||
|
||||
o = s.option(form.Value, 'info_url', _('Subscription Info Url'));
|
||||
o.modalonly = true;
|
||||
|
||||
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 = 'sing-box';
|
||||
o.modalonly = true;
|
||||
o.rmempty = false;
|
||||
o.value('sing-box');
|
||||
|
||||
o = s.option(form.ListValue, 'prefer', _('Prefer'));
|
||||
o.default = 'remote';
|
||||
o.modalonly = true;
|
||||
o.value('remote', _('Remote'));
|
||||
o.value('local', _('Local'));
|
||||
|
||||
return m.render();
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,212 @@
|
||||
'use strict';
|
||||
'require form';
|
||||
'require view';
|
||||
'require uci';
|
||||
'require network';
|
||||
'require tools.widgets as widgets';
|
||||
'require tools.momo as momo';
|
||||
|
||||
return view.extend({
|
||||
load: function () {
|
||||
return Promise.all([
|
||||
uci.load('momo'),
|
||||
network.getHostHints(),
|
||||
network.getNetworks(),
|
||||
momo.getIdentifiers(),
|
||||
]);
|
||||
},
|
||||
render: function (data) {
|
||||
const hosts = data[1].hosts;
|
||||
const networks = data[2];
|
||||
const users = data[3]?.users ?? [];
|
||||
const groups = data[3]?.groups ?? [];
|
||||
const cgroups = data[3]?.cgroups ?? [];
|
||||
|
||||
let m, s, o, so;
|
||||
|
||||
m = new form.Map('momo');
|
||||
|
||||
s = m.section(form.NamedSection, 'proxy', 'proxy', _('Proxy Config'));
|
||||
|
||||
s.tab('proxy', _('Proxy Config'));
|
||||
|
||||
o = s.taboption('proxy', form.Flag, 'enabled', _('Enable'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('proxy', form.Flag, 'ipv4_dns_hijack', _('IPv4 DNS Hijack'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('proxy', form.Flag, 'ipv6_dns_hijack', _('IPv6 DNS Hijack'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('proxy', form.Flag, 'ipv4_proxy', _('IPv4 Proxy'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('proxy', form.Flag, 'ipv6_proxy', _('IPv6 Proxy'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('proxy', form.Flag, 'fake_ip_ping_hijack', _('Fake-IP Ping Hijack'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('proxy', form.ListValue, 'tcp_mode', _('TCP Mode'));
|
||||
o.optional = true;
|
||||
o.placeholder = _('Disable');
|
||||
o.value('redirect', _('Redirect Mode'));
|
||||
o.value('tproxy', _('TPROXY Mode'));
|
||||
o.value('tun', _('TUN Mode'));
|
||||
|
||||
o = s.taboption('proxy', form.ListValue, 'udp_mode', _('UDP Mode'));
|
||||
o.optional = true;
|
||||
o.placeholder = _('Disable');
|
||||
o.value('tproxy', _('TPROXY Mode'));
|
||||
o.value('tun', _('TUN Mode'));
|
||||
|
||||
s.tab('router', _('Router Proxy'));
|
||||
|
||||
o = s.taboption('router', form.Flag, 'router_proxy', _('Enable'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('router', form.SectionValue, '_router_access_control', form.TableSection, 'router_access_control', _('Access Control'));
|
||||
o.retain = true;
|
||||
o.depends('router_proxy', '1');
|
||||
|
||||
o.subsection.addremove = true;
|
||||
o.subsection.anonymous = true;
|
||||
o.subsection.sortable = true;
|
||||
|
||||
so = o.subsection.option(form.Flag, 'enabled', _('Enable'));
|
||||
so.default = '1';
|
||||
so.rmempty = false;
|
||||
|
||||
so = o.subsection.option(form.DynamicList, 'user', _('User'));
|
||||
|
||||
for (const user of users) {
|
||||
so.value(user);
|
||||
};
|
||||
|
||||
so = o.subsection.option(form.DynamicList, 'group', _('Group'));
|
||||
|
||||
for (const group of groups) {
|
||||
so.value(group);
|
||||
};
|
||||
|
||||
so = o.subsection.option(form.DynamicList, 'cgroup', _('CGroup'));
|
||||
|
||||
for (const cgroup of cgroups) {
|
||||
so.value(cgroup);
|
||||
};
|
||||
|
||||
so = o.subsection.option(form.Flag, 'dns', _('DNS'));
|
||||
so.rmempty = false;
|
||||
|
||||
so = o.subsection.option(form.Flag, 'proxy', _('Proxy'));
|
||||
so.rmempty = false;
|
||||
|
||||
s.tab('lan', _('LAN Proxy'));
|
||||
|
||||
o = s.taboption('lan', form.Flag, 'lan_proxy', _('Enable'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('lan', form.DynamicList, 'lan_inbound_interface', _('Inbound Interface'));
|
||||
o.retain = true;
|
||||
o.rmempty = false;
|
||||
o.depends('lan_proxy', '1');
|
||||
|
||||
for (const network of networks) {
|
||||
if (network.getName() === 'loopback') {
|
||||
continue;
|
||||
}
|
||||
o.value(network.getName());
|
||||
}
|
||||
|
||||
o = s.taboption('lan', form.SectionValue, '_lan_access_control', form.TableSection, 'lan_access_control', _('Access Control'));
|
||||
o.retain = true;
|
||||
o.depends('lan_proxy', '1');
|
||||
|
||||
o.subsection.addremove = true;
|
||||
o.subsection.anonymous = true;
|
||||
o.subsection.sortable = true;
|
||||
|
||||
so = o.subsection.option(form.Flag, 'enabled', _('Enable'));
|
||||
so.default = '1';
|
||||
so.rmempty = false;
|
||||
|
||||
so = o.subsection.option(form.DynamicList, 'ip', 'IP');
|
||||
so.datatype = 'ip4addr';
|
||||
|
||||
for (const mac in hosts) {
|
||||
const host = hosts[mac];
|
||||
for (const ip of host.ipaddrs) {
|
||||
const hint = host.name ?? mac;
|
||||
so.value(ip, hint ? '%s (%s)'.format(ip, hint) : ip);
|
||||
};
|
||||
};
|
||||
|
||||
so = o.subsection.option(form.DynamicList, 'ip6', 'IP6');
|
||||
so.datatype = 'ip6addr';
|
||||
|
||||
for (const mac in hosts) {
|
||||
const host = hosts[mac];
|
||||
for (const ip of host.ip6addrs) {
|
||||
const hint = host.name ?? mac;
|
||||
so.value(ip, hint ? '%s (%s)'.format(ip, hint) : ip);
|
||||
};
|
||||
};
|
||||
|
||||
so = o.subsection.option(form.DynamicList, 'mac', 'MAC');
|
||||
so.datatype = 'macaddr';
|
||||
|
||||
for (const mac in hosts) {
|
||||
const host = hosts[mac];
|
||||
const hint = host.name ?? host.ipaddrs[0];
|
||||
so.value(mac, hint ? '%s (%s)'.format(mac, hint) : mac);
|
||||
};
|
||||
|
||||
so = o.subsection.option(form.Flag, 'dns', _('DNS'));
|
||||
so.rmempty = false;
|
||||
|
||||
so = o.subsection.option(form.Flag, 'proxy', _('Proxy'));
|
||||
so.rmempty = false;
|
||||
|
||||
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.Flag, 'bypass_china_mainland_ip6', _('Bypass China Mainland IP6'));
|
||||
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)';
|
||||
|
||||
o = s.taboption('bypass', form.DynamicList, 'bypass_fwmark', _('Bypass FWMark'));
|
||||
|
||||
s.tab('misc', _('Misc'));
|
||||
|
||||
o = s.taboption('misc', form.DynamicList, 'reserved_ip', _('Reserved IP'));
|
||||
o.datatype = 'ip4addr';
|
||||
|
||||
o = s.taboption('misc', form.DynamicList, 'reserved_ip6', _('Reserved IP6'));
|
||||
o.datatype = 'ip6addr';
|
||||
|
||||
o = s.taboption('misc', form.Value, 'tun_timeout', _('TUN Timeout'));
|
||||
o.datatype = 'uinteger';
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('misc', form.Value, 'tun_interval', _('TUN Interval'));
|
||||
o.datatype = 'uinteger';
|
||||
o.rmempty = false;
|
||||
|
||||
return m.render();
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,427 @@
|
||||
msgid ""
|
||||
msgstr "Content-Type: text/plain; charset=UTF-8"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:69
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:122
|
||||
msgid "Access Control"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:141
|
||||
msgid "Address Space Size Hard Limit"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:137
|
||||
msgid "Address Space Size Soft Limit"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:181
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:186
|
||||
msgid "All Port"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:94
|
||||
#: applications/luci-app-momo/root/usr/share/luci/menu.d/luci-app-momo.json:13
|
||||
msgid "App Config"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/log.js:29
|
||||
msgid "App Log"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:43
|
||||
msgid "App Version"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:171
|
||||
msgid "Bypass"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:173
|
||||
msgid "Bypass China Mainland IP"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:176
|
||||
msgid "Bypass China Mainland IP6"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:189
|
||||
msgid "Bypass DSCP"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:192
|
||||
msgid "Bypass FWMark"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:93
|
||||
msgid "CGroup"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/editor.js:27
|
||||
msgid "Choose File"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:99
|
||||
msgid "Choose Profile"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/log.js:33
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/log.js:66
|
||||
msgid "Clear Log"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:182
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:187
|
||||
msgid "Commonly Used Port"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/log.js:62
|
||||
msgid "Core Log"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:125
|
||||
msgid "Core Only"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:57
|
||||
msgid "Core Status"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:50
|
||||
msgid "Core Version"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:117
|
||||
msgid "Cron Expression"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:99
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:165
|
||||
msgid "DNS"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/log.js:95
|
||||
msgid "Debug Log"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:179
|
||||
msgid "Destination TCP Port to Proxy"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:184
|
||||
msgid "Destination UDP Port to Proxy"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:53
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:60
|
||||
msgid "Disable"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/profile.js:35
|
||||
msgid "Edit Subscription"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/editor.js:25
|
||||
#: applications/luci-app-momo/root/usr/share/luci/menu.d/luci-app-momo.json:37
|
||||
msgid "Editor"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:96
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:33
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:66
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:77
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:107
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:130
|
||||
msgid "Enable"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/profile.js:50
|
||||
msgid "Expire At"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:48
|
||||
msgid "Fake-IP Ping Hijack"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:132
|
||||
msgid "Fast Reload"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:103
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/editor.js:31
|
||||
msgid "File:"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:130
|
||||
msgid "General Config"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/log.js:99
|
||||
msgid "Generate & Download"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/root/usr/share/rpcd/acl.d/luci-app-momo.json:3
|
||||
msgid "Grant access to momo procedures"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:87
|
||||
msgid "Group"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:149
|
||||
msgid "Heap Size Hard Limit"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:145
|
||||
msgid "Heap Size Soft Limit"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:38
|
||||
msgid "How To Use"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:36
|
||||
msgid "IPv4 DNS Hijack"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:42
|
||||
msgid "IPv4 Proxy"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:39
|
||||
msgid "IPv6 DNS Hijack"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:45
|
||||
msgid "IPv6 Proxy"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:110
|
||||
msgid "Inbound Interface"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:105
|
||||
msgid "LAN Proxy"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/profile.js:86
|
||||
msgid "Local"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/log.js:27
|
||||
#: applications/luci-app-momo/root/usr/share/luci/menu.d/luci-app-momo.json:45
|
||||
msgid "Log"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:194
|
||||
msgid "Misc"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:38
|
||||
#: applications/luci-app-momo/root/usr/share/luci/menu.d/luci-app-momo.json:3
|
||||
msgid "Momo"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:15
|
||||
msgid "Not Running"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:164
|
||||
msgid "Number of Open Files Hard Limit"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:161
|
||||
msgid "Number of Open Files Soft Limit"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:89
|
||||
msgid "Open Dashboard"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/profile.js:82
|
||||
msgid "Prefer"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/profile.js:21
|
||||
#: applications/luci-app-momo/root/usr/share/luci/menu.d/luci-app-momo.json:21
|
||||
msgid "Profile"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/editor.js:38
|
||||
msgid "Profile for Startup"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:102
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:168
|
||||
msgid "Proxy"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:29
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:31
|
||||
#: applications/luci-app-momo/root/usr/share/luci/menu.d/luci-app-momo.json:29
|
||||
msgid "Proxy Config"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:135
|
||||
msgid "RLIMIT Config"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:54
|
||||
msgid "Redirect Mode"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:69
|
||||
msgid "Reload Service"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/profile.js:85
|
||||
msgid "Remote"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:196
|
||||
msgid "Reserved IP"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:199
|
||||
msgid "Reserved IP6"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:76
|
||||
msgid "Restart Service"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:64
|
||||
msgid "Router Proxy"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:15
|
||||
msgid "Running"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:114
|
||||
msgid "Scheduled Restart"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/log.js:56
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/log.js:89
|
||||
msgid "Scroll To Bottom"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:157
|
||||
msgid "Stack Size Hard Limit"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:153
|
||||
msgid "Stack Size Soft Limit"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:110
|
||||
msgid "Start Delay"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:112
|
||||
msgid "Start Immidiately"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:40
|
||||
msgid "Status"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/profile.js:31
|
||||
msgid "Subscription"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/profile.js:69
|
||||
msgid "Subscription Info Url"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/profile.js:37
|
||||
msgid "Subscription Name"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/profile.js:72
|
||||
msgid "Subscription Url"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:107
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/editor.js:35
|
||||
msgid "Subscription:"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:51
|
||||
msgid "TCP Mode"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:55
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:61
|
||||
msgid "TPROXY Mode"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:206
|
||||
msgid "TUN Interval"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:56
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:62
|
||||
msgid "TUN Mode"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:202
|
||||
msgid "TUN Timeout"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:122
|
||||
msgid "Test Profile"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/profile.js:45
|
||||
msgid "Total"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:38
|
||||
msgid "Transparent Proxy with sing-box on OpenWrt."
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:58
|
||||
msgid "UDP Mode"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:139
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:143
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:147
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:151
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:155
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:159
|
||||
msgid "Unlimited"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/profile.js:63
|
||||
msgid "Update"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/profile.js:55
|
||||
msgid "Update At"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:83
|
||||
msgid "Update Dashboard"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/profile.js:23
|
||||
msgid "Upload Profile"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/profile.js:40
|
||||
msgid "Used"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:81
|
||||
msgid "User"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/profile.js:76
|
||||
msgid "User Agent"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:128
|
||||
msgid "procd Config"
|
||||
msgstr ""
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
zh_Hans
|
||||
@@ -0,0 +1,434 @@
|
||||
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-momo/htdocs/luci-static/resources/view/momo/proxy.js:69
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:122
|
||||
msgid "Access Control"
|
||||
msgstr "访问控制"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:141
|
||||
msgid "Address Space Size Hard Limit"
|
||||
msgstr "地址空间大小硬限制"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:137
|
||||
msgid "Address Space Size Soft Limit"
|
||||
msgstr "地址空间大小软限制"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:181
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:186
|
||||
msgid "All Port"
|
||||
msgstr "全部端口"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:94
|
||||
#: applications/luci-app-momo/root/usr/share/luci/menu.d/luci-app-momo.json:13
|
||||
msgid "App Config"
|
||||
msgstr "插件配置"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/log.js:29
|
||||
msgid "App Log"
|
||||
msgstr "插件日志"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:43
|
||||
msgid "App Version"
|
||||
msgstr "插件版本"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:171
|
||||
msgid "Bypass"
|
||||
msgstr "绕过"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:173
|
||||
msgid "Bypass China Mainland IP"
|
||||
msgstr "绕过中国大陆 IP"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:176
|
||||
msgid "Bypass China Mainland IP6"
|
||||
msgstr "绕过中国大陆 IP6"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:189
|
||||
msgid "Bypass DSCP"
|
||||
msgstr "绕过 DSCP"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:192
|
||||
msgid "Bypass FWMark"
|
||||
msgstr "绕过 FWMark"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:93
|
||||
msgid "CGroup"
|
||||
msgstr "控制组"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/editor.js:27
|
||||
msgid "Choose File"
|
||||
msgstr "选择文件"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:99
|
||||
msgid "Choose Profile"
|
||||
msgstr "选择配置文件"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/log.js:33
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/log.js:66
|
||||
msgid "Clear Log"
|
||||
msgstr "清空日志"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:182
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:187
|
||||
msgid "Commonly Used Port"
|
||||
msgstr "常用端口"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/log.js:62
|
||||
msgid "Core Log"
|
||||
msgstr "核心日志"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:125
|
||||
msgid "Core Only"
|
||||
msgstr "仅核心"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:57
|
||||
msgid "Core Status"
|
||||
msgstr "核心状态"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:50
|
||||
msgid "Core Version"
|
||||
msgstr "核心版本"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:117
|
||||
msgid "Cron Expression"
|
||||
msgstr "Cron 表达式"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:99
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:165
|
||||
msgid "DNS"
|
||||
msgstr "DNS"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/log.js:95
|
||||
msgid "Debug Log"
|
||||
msgstr "调试日志"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:179
|
||||
msgid "Destination TCP Port to Proxy"
|
||||
msgstr "要代理的 TCP 目标端口"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:184
|
||||
msgid "Destination UDP Port to Proxy"
|
||||
msgstr "要代理的 UDP 目标端口"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:53
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:60
|
||||
msgid "Disable"
|
||||
msgstr "禁用"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/profile.js:35
|
||||
msgid "Edit Subscription"
|
||||
msgstr "编辑订阅"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/editor.js:25
|
||||
#: applications/luci-app-momo/root/usr/share/luci/menu.d/luci-app-momo.json:37
|
||||
msgid "Editor"
|
||||
msgstr "编辑器"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:96
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:33
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:66
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:77
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:107
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:130
|
||||
msgid "Enable"
|
||||
msgstr "启用"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/profile.js:50
|
||||
msgid "Expire At"
|
||||
msgstr "到期时间"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:48
|
||||
msgid "Fake-IP Ping Hijack"
|
||||
msgstr "Fake-IP Ping 劫持"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:132
|
||||
msgid "Fast Reload"
|
||||
msgstr "快速重载"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:103
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/editor.js:31
|
||||
msgid "File:"
|
||||
msgstr "文件:"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:130
|
||||
msgid "General Config"
|
||||
msgstr "全局配置"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/log.js:99
|
||||
msgid "Generate & Download"
|
||||
msgstr "生成并下载"
|
||||
|
||||
#: applications/luci-app-momo/root/usr/share/rpcd/acl.d/luci-app-momo.json:3
|
||||
msgid "Grant access to momo procedures"
|
||||
msgstr "授予访问 momo 程序的权限"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:87
|
||||
msgid "Group"
|
||||
msgstr "用户组"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:149
|
||||
msgid "Heap Size Hard Limit"
|
||||
msgstr "堆大小硬限制"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:145
|
||||
msgid "Heap Size Soft Limit"
|
||||
msgstr "堆大小软限制"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:38
|
||||
msgid "How To Use"
|
||||
msgstr "使用说明"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:36
|
||||
msgid "IPv4 DNS Hijack"
|
||||
msgstr "IPv4 DNS 劫持"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:42
|
||||
msgid "IPv4 Proxy"
|
||||
msgstr "IPv4 代理"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:39
|
||||
msgid "IPv6 DNS Hijack"
|
||||
msgstr "IPv6 DNS 劫持"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:45
|
||||
msgid "IPv6 Proxy"
|
||||
msgstr "IPv6 代理"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:110
|
||||
msgid "Inbound Interface"
|
||||
msgstr "入站接口"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:105
|
||||
msgid "LAN Proxy"
|
||||
msgstr "局域网代理"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/profile.js:86
|
||||
msgid "Local"
|
||||
msgstr "本地"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/log.js:27
|
||||
#: applications/luci-app-momo/root/usr/share/luci/menu.d/luci-app-momo.json:45
|
||||
msgid "Log"
|
||||
msgstr "日志"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:194
|
||||
msgid "Misc"
|
||||
msgstr "杂项"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:38
|
||||
#: applications/luci-app-momo/root/usr/share/luci/menu.d/luci-app-momo.json:3
|
||||
msgid "Momo"
|
||||
msgstr "Momo"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:15
|
||||
msgid "Not Running"
|
||||
msgstr "未在运行"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:164
|
||||
msgid "Number of Open Files Hard Limit"
|
||||
msgstr "打开文件数量硬限制"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:161
|
||||
msgid "Number of Open Files Soft Limit"
|
||||
msgstr "打开文件数量软限制"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:89
|
||||
msgid "Open Dashboard"
|
||||
msgstr "打开面板"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/profile.js:82
|
||||
msgid "Prefer"
|
||||
msgstr "优先"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/profile.js:21
|
||||
#: applications/luci-app-momo/root/usr/share/luci/menu.d/luci-app-momo.json:21
|
||||
msgid "Profile"
|
||||
msgstr "配置文件"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/editor.js:38
|
||||
msgid "Profile for Startup"
|
||||
msgstr "用于启动的配置文件"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:102
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:168
|
||||
msgid "Proxy"
|
||||
msgstr "代理"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:29
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:31
|
||||
#: applications/luci-app-momo/root/usr/share/luci/menu.d/luci-app-momo.json:29
|
||||
msgid "Proxy Config"
|
||||
msgstr "代理配置"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:135
|
||||
msgid "RLIMIT Config"
|
||||
msgstr "资源限制配置"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:54
|
||||
msgid "Redirect Mode"
|
||||
msgstr "Redirect 模式"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:69
|
||||
msgid "Reload Service"
|
||||
msgstr "重载服务"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/profile.js:85
|
||||
msgid "Remote"
|
||||
msgstr "远程"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:196
|
||||
msgid "Reserved IP"
|
||||
msgstr "IPv4 保留地址"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:199
|
||||
msgid "Reserved IP6"
|
||||
msgstr "IPv6 保留地址"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:76
|
||||
msgid "Restart Service"
|
||||
msgstr "重启服务"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:64
|
||||
msgid "Router Proxy"
|
||||
msgstr "路由器代理"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:15
|
||||
msgid "Running"
|
||||
msgstr "运行中"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:114
|
||||
msgid "Scheduled Restart"
|
||||
msgstr "定时重启"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/log.js:56
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/log.js:89
|
||||
msgid "Scroll To Bottom"
|
||||
msgstr "滚动到底部"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:157
|
||||
msgid "Stack Size Hard Limit"
|
||||
msgstr "栈大小硬限制"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:153
|
||||
msgid "Stack Size Soft Limit"
|
||||
msgstr "栈大小软限制"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:110
|
||||
msgid "Start Delay"
|
||||
msgstr "启动延迟"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:112
|
||||
msgid "Start Immidiately"
|
||||
msgstr "立即启动"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:40
|
||||
msgid "Status"
|
||||
msgstr "状态"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/profile.js:31
|
||||
msgid "Subscription"
|
||||
msgstr "订阅"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/profile.js:69
|
||||
msgid "Subscription Info Url"
|
||||
msgstr "订阅信息链接"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/profile.js:37
|
||||
msgid "Subscription Name"
|
||||
msgstr "订阅名称"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/profile.js:72
|
||||
msgid "Subscription Url"
|
||||
msgstr "订阅链接"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:107
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/editor.js:35
|
||||
msgid "Subscription:"
|
||||
msgstr "订阅:"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:51
|
||||
msgid "TCP Mode"
|
||||
msgstr "TCP 模式"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:55
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:61
|
||||
msgid "TPROXY Mode"
|
||||
msgstr "TPROXY 模式"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:206
|
||||
msgid "TUN Interval"
|
||||
msgstr "TUN 等待间隔"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:56
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:62
|
||||
msgid "TUN Mode"
|
||||
msgstr "TUN 模式"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:202
|
||||
msgid "TUN Timeout"
|
||||
msgstr "TUN 等待超时"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:122
|
||||
msgid "Test Profile"
|
||||
msgstr "检查配置文件"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/profile.js:45
|
||||
msgid "Total"
|
||||
msgstr "总量"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:38
|
||||
msgid "Transparent Proxy with sing-box on OpenWrt."
|
||||
msgstr "在 OpenWrt 上使用 sing-box 进行透明代理"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:58
|
||||
msgid "UDP Mode"
|
||||
msgstr "UDP 模式"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:139
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:143
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:147
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:151
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:155
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:159
|
||||
msgid "Unlimited"
|
||||
msgstr "无限制"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/profile.js:63
|
||||
msgid "Update"
|
||||
msgstr "更新"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/profile.js:55
|
||||
msgid "Update At"
|
||||
msgstr "更新时间"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:83
|
||||
msgid "Update Dashboard"
|
||||
msgstr "更新面板"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/profile.js:23
|
||||
msgid "Upload Profile"
|
||||
msgstr "上传配置文件"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/profile.js:40
|
||||
msgid "Used"
|
||||
msgstr "已使用"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/proxy.js:81
|
||||
msgid "User"
|
||||
msgstr "用户"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/profile.js:76
|
||||
msgid "User Agent"
|
||||
msgstr "用户代理(UA)"
|
||||
|
||||
#: applications/luci-app-momo/htdocs/luci-static/resources/view/momo/app.js:128
|
||||
msgid "procd Config"
|
||||
msgstr "procd 配置"
|
||||
@@ -0,0 +1,52 @@
|
||||
{
|
||||
"admin/services/momo": {
|
||||
"title": "Momo",
|
||||
"action": {
|
||||
"type": "firstchild"
|
||||
},
|
||||
"depends": {
|
||||
"acl": [ "luci-app-momo" ],
|
||||
"uci": { "momo": true }
|
||||
}
|
||||
},
|
||||
"admin/services/momo/config": {
|
||||
"title": "App Config",
|
||||
"order": 10,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "momo/app"
|
||||
}
|
||||
},
|
||||
"admin/services/momo/profile": {
|
||||
"title": "Profile",
|
||||
"order": 20,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "momo/profile"
|
||||
}
|
||||
},
|
||||
"admin/services/momo/proxy": {
|
||||
"title": "Proxy Config",
|
||||
"order": 30,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "momo/proxy"
|
||||
}
|
||||
},
|
||||
"admin/services/momo/editor": {
|
||||
"title": "Editor",
|
||||
"order": 40,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "momo/editor"
|
||||
}
|
||||
},
|
||||
"admin/services/momo/log": {
|
||||
"title": "Log",
|
||||
"order": 50,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "momo/log"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"luci-app-momo": {
|
||||
"description": "Grant access to momo procedures",
|
||||
"read": {
|
||||
"uci": [ "momo" ],
|
||||
"ubus": {
|
||||
"rc": [ "*" ],
|
||||
"luci.momo": [ "*" ]
|
||||
},
|
||||
"file": {
|
||||
"/etc/momo/profiles/*.json": ["read"],
|
||||
"/etc/momo/subscriptions/*.json": ["read"],
|
||||
"/etc/momo/run/config.json": ["read"],
|
||||
"/var/log/momo/*.log": ["read"]
|
||||
}
|
||||
},
|
||||
"write": {
|
||||
"uci": [ "momo" ],
|
||||
"file": {
|
||||
"/etc/momo/profiles/*.json": ["write"],
|
||||
"/etc/momo/subscriptions/*.json": ["write"],
|
||||
"/etc/momo/run/config.json": ["write"],
|
||||
"/var/log/momo/*.log": ["write"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
#!/usr/bin/ucode
|
||||
|
||||
'use strict';
|
||||
|
||||
import { access, popen, readfile, writefile } from 'fs';
|
||||
import { get_paths, merge_exists, get_users, get_groups, get_cgroups } from '/etc/momo/ucode/include.uc';
|
||||
|
||||
const methods = {
|
||||
get_paths: {
|
||||
call: function() {
|
||||
return get_paths();
|
||||
}
|
||||
},
|
||||
version: {
|
||||
call: function() {
|
||||
let process;
|
||||
let app = '';
|
||||
if (system('command -v opkg') == 0) {
|
||||
process = popen(`opkg list-installed luci-app-momo | cut -d ' ' -f 3`);
|
||||
if (process) {
|
||||
app = trim(process.read('all'));
|
||||
process.close();
|
||||
}
|
||||
} else if (system('command -v apk') == 0) {
|
||||
process = popen(`apk list -I luci-app-momo | cut -d ' ' -f 1 | cut -d '-' -f 4`);
|
||||
if (process) {
|
||||
app = trim(process.read('all'));
|
||||
process.close();
|
||||
}
|
||||
}
|
||||
let core = '';
|
||||
process = popen(`sing-box version | grep sing-box | cut -d ' ' -f 3`);
|
||||
if (process) {
|
||||
core = trim(process.read('all'));
|
||||
process.close();
|
||||
}
|
||||
return { app: app, core: core };
|
||||
}
|
||||
},
|
||||
profile: {
|
||||
args: { defaults: {} },
|
||||
call: function(req) {
|
||||
const paths = get_paths();
|
||||
const defaults = req.args?.defaults ?? {};
|
||||
const profile = json(readfile(paths.run_profile_path));
|
||||
return merge_exists(defaults, profile);
|
||||
}
|
||||
},
|
||||
update_subscription: {
|
||||
args: { section_id: 'section_id' },
|
||||
call: function(req) {
|
||||
let success = false;
|
||||
const section_id = req.args?.section_id;
|
||||
if (section_id) {
|
||||
success = system(['service', 'momo', 'update_subscription', section_id]) == 0;
|
||||
}
|
||||
return { success: success };
|
||||
}
|
||||
},
|
||||
api: {
|
||||
args: { method: 'method', path: 'path', query: 'query', body: 'body' },
|
||||
call: function(req) {
|
||||
let result = {};
|
||||
|
||||
const method = req.args?.method;
|
||||
const path = req.args?.path;
|
||||
const query = req.args?.query;
|
||||
const body = req.args?.body;
|
||||
|
||||
const paths = get_paths();
|
||||
const profile = json(readfile(paths.run_profile_path));
|
||||
const api_listen = profile['experimental']['clash_api']['external_controller'];
|
||||
const api_secret = profile['experimental']['clash_api']['secret'];
|
||||
|
||||
if (!api_listen) {
|
||||
return result;
|
||||
}
|
||||
|
||||
const url = api_listen + path;
|
||||
|
||||
const process = popen(`curl --request '${method}' --oauth2-bearer '${api_secret}' --url-query '${query}' --data '${body}' '${url}'`);
|
||||
if (process) {
|
||||
result = json(process);
|
||||
process.close();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
},
|
||||
get_identifiers: {
|
||||
call: function() {
|
||||
const users = filter(get_users(), (x) => x != '');
|
||||
const groups = filter(get_groups(), (x) => x != '');
|
||||
const cgroups = filter(get_cgroups(), (x) => x != '' && index(x, 'services/momo') < 0);
|
||||
return { users: users, groups: groups, cgroups: cgroups };
|
||||
}
|
||||
},
|
||||
debug: {
|
||||
call: function() {
|
||||
const paths = get_paths();
|
||||
const success = system(`${paths.debug_sh} > ${paths.debug_log_path}`) == 0;
|
||||
return { success: success };
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return { 'luci.momo': methods };
|
||||
@@ -2,7 +2,7 @@ include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=luci-app-mosdns
|
||||
PKG_VERSION:=1.7.0
|
||||
PKG_RELEASE:=3
|
||||
PKG_RELEASE:=4
|
||||
|
||||
LUCI_TITLE:=LuCI Support for mosdns
|
||||
LUCI_PKGARCH:=all
|
||||
|
||||
@@ -749,7 +749,7 @@ restore_dhcp_setting() {
|
||||
}
|
||||
|
||||
reload_dnsmasq() {
|
||||
/etc/init.d/dnsmasq reload
|
||||
[ "$(/etc/init.d/dnsmasq status)" = "running" ] && /etc/init.d/dnsmasq reload
|
||||
}
|
||||
|
||||
reload_service() {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_VERSION:=0.1.2
|
||||
PKG_RELEASE:=4
|
||||
PKG_VERSION:=0.1.3
|
||||
PKG_RELEASE:=5
|
||||
|
||||
LUCI_TITLE:=LuCI support for OpenClaw Launcher
|
||||
LUCI_PKGARCH:=all
|
||||
LUCI_DEPENDS:=+luci-base +luci-compat +luci-lib-taskd +curl +ca-bundle +tar +xz-utils +jsonfilter +git +git-http
|
||||
LUCI_DEPENDS:=+luci-base +luci-compat +luci-lib-taskd +curl +ca-bundle +tar +xz-utils +jsonfilter +git +git-http +ttyd
|
||||
|
||||
define Package/luci-app-openclawmgr/conffiles
|
||||
/etc/config/openclawmgr
|
||||
|
||||
@@ -10,12 +10,17 @@ function index()
|
||||
local page = entry({"admin", "services", "openclawmgr", "config"}, call("action_app"), _("Config"), 10)
|
||||
page.leaf = true
|
||||
|
||||
entry({"admin", "services", "openclawmgr", "cli"}, form("openclawmgr/cli"), _("命令行"), 20).leaf = true
|
||||
entry({"admin", "services", "openclawmgr", "logs"}, template("openclawmgr/logs"), _("日志"), 30).leaf = true
|
||||
|
||||
entry({"admin", "services", "openclawmgr", "status"}, call("action_status")).leaf = true
|
||||
entry({"admin", "services", "openclawmgr", "ready"}, call("action_ready")).leaf = true
|
||||
entry({"admin", "services", "openclawmgr", "op"}, call("action_op")).leaf = true
|
||||
entry({"admin", "services", "openclawmgr", "config_data"}, call("action_config_data")).leaf = true
|
||||
entry({"admin", "services", "openclawmgr", "apply_config"}, call("action_apply_config")).leaf = true
|
||||
|
||||
entry({"admin", "services", "openclawmgr", "logs_api"}, call("action_logs")).leaf = true
|
||||
|
||||
entry({"admin", "services", "openclawmgr", "diag_info"}, call("action_diag_info")).leaf = true
|
||||
entry({"admin", "services", "openclawmgr", "diag_run"}, call("action_diag_run")).leaf = true
|
||||
entry({"admin", "services", "openclawmgr", "diag_poll"}, call("action_diag_poll")).leaf = true
|
||||
@@ -136,6 +141,22 @@ local function get_task_state(task_id)
|
||||
}
|
||||
end
|
||||
|
||||
local function safe_int(v, def, minv, maxv)
|
||||
v = tostring(v or "")
|
||||
local n = tonumber(v)
|
||||
if not n then
|
||||
return def
|
||||
end
|
||||
n = math.floor(n)
|
||||
if minv and n < minv then
|
||||
return minv
|
||||
end
|
||||
if maxv and n > maxv then
|
||||
return maxv
|
||||
end
|
||||
return n
|
||||
end
|
||||
|
||||
local function require_csrf()
|
||||
local http = require "luci.http"
|
||||
local disp = require "luci.dispatcher"
|
||||
@@ -179,6 +200,36 @@ local function get_host()
|
||||
return host
|
||||
end
|
||||
|
||||
function action_logs()
|
||||
local http = require "luci.http"
|
||||
local sys = require "luci.sys"
|
||||
local fs = require "nixio.fs"
|
||||
local util = require "luci.util"
|
||||
|
||||
local limit = safe_int(http.formvalue("limit"), 200, 50, 2000)
|
||||
local kind = (http.formvalue("kind") or "openclaw"):lower()
|
||||
|
||||
local cmd = ""
|
||||
if kind == "tasks" then
|
||||
local f = "/var/log/tasks/openclawmgr.log"
|
||||
if fs.access(f) then
|
||||
cmd = "tail -n " .. limit .. " " .. util.shellquote(f) .. " 2>/dev/null"
|
||||
else
|
||||
cmd = "echo '(task log not found: " .. f .. ")'"
|
||||
end
|
||||
elseif kind == "openclawmgr" then
|
||||
cmd = "logread 2>/dev/null | grep -i openclawmgr | tail -n " .. limit
|
||||
elseif kind == "all" then
|
||||
cmd = "logread 2>/dev/null | tail -n " .. limit
|
||||
else
|
||||
-- default: openclaw gateway logs
|
||||
cmd = "logread 2>/dev/null | grep -i openclaw | tail -n " .. limit
|
||||
end
|
||||
|
||||
local log = sys.exec(cmd) or ""
|
||||
write_json({ ok = true, kind = kind, limit = limit, server_time = os.time(), log = log })
|
||||
end
|
||||
|
||||
local function configured_base_path(base_dir)
|
||||
local jsonc = require "luci.jsonc"
|
||||
if type(base_dir) ~= "string" or base_dir == "" then
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
--[[
|
||||
LuCI - Lua Configuration Interface
|
||||
]]--
|
||||
|
||||
require "luci.util"
|
||||
|
||||
local uci = require "luci.model.uci".cursor()
|
||||
|
||||
local m, s, o
|
||||
|
||||
local cmd_ttyd = luci.util.exec("command -v ttyd"):match("^.+ttyd") or nil
|
||||
local base_dir = uci:get("openclawmgr", "main", "base_dir") or ""
|
||||
|
||||
m = SimpleForm("OpenClawCLI", "", translate("Only works in LAN"))
|
||||
m.submit = false
|
||||
m.reset = false
|
||||
|
||||
if not cmd_ttyd or cmd_ttyd:match("^%s*$") then
|
||||
s = m:section(SimpleSection, nil, translate("ttyd not found"))
|
||||
return m
|
||||
end
|
||||
|
||||
if base_dir == "" then
|
||||
s = m:section(SimpleSection, nil, translate("base_dir is not configured"))
|
||||
return m
|
||||
end
|
||||
|
||||
local node_dir = base_dir .. "/node"
|
||||
local global_dir = base_dir .. "/global"
|
||||
local data_dir = base_dir .. "/data"
|
||||
|
||||
local cmdline = "sh /usr/share/openclawmgr/oc-config.sh"
|
||||
|
||||
s = m:section(SimpleSection)
|
||||
|
||||
o = s:option(Value, "mode", translate("Mode"))
|
||||
o:value("menu", translate("Interactive menu"))
|
||||
o.default = "menu"
|
||||
o.forcewrite = true
|
||||
o.write = function(self, section, value)
|
||||
if value == "menu" then
|
||||
cmdline = "sh /usr/share/openclawmgr/oc-config.sh"
|
||||
end
|
||||
end
|
||||
|
||||
o = s:option(DummyValue, "_tip", translate("CLI Config"))
|
||||
o.rawhtml = true
|
||||
o.cfgvalue = function()
|
||||
return translate("Starts an interactive OpenClaw configuration menu in a web terminal (LAN only).")
|
||||
end
|
||||
|
||||
o = s:option(Button, "connect")
|
||||
o.render = function(self, section, scope)
|
||||
self.inputstyle = "add"
|
||||
self.title = " "
|
||||
self.inputtitle = translate("Connect")
|
||||
Button.render(self, section, scope)
|
||||
end
|
||||
|
||||
o.write = function(self, section)
|
||||
local cmd_ttyd = luci.util.exec("command -v ttyd"):match("^.+ttyd") or nil
|
||||
if not cmd_ttyd or cmd_ttyd:match("^%s*$") then
|
||||
return
|
||||
end
|
||||
|
||||
local pid = luci.util.trim(luci.util.exec("netstat -lnpt | grep :7682 | grep ttyd | tr -s ' ' | cut -d ' ' -f7 | cut -d'/' -f1"))
|
||||
if pid and pid ~= "" then
|
||||
luci.util.exec("kill -9 " .. pid)
|
||||
end
|
||||
|
||||
local current_path = os.getenv("PATH") or "/usr/sbin:/usr/bin:/sbin:/bin"
|
||||
local env_path = node_dir .. "/bin:" .. global_dir .. "/bin:" .. current_path
|
||||
local env_prefix = table.concat({
|
||||
"BASE_DIR=" .. luci.util.shellquote(base_dir),
|
||||
"NODE_DIR=" .. luci.util.shellquote(node_dir),
|
||||
"GLOBAL_DIR=" .. luci.util.shellquote(global_dir),
|
||||
"DATA_DIR=" .. luci.util.shellquote(data_dir),
|
||||
"HOME=" .. luci.util.shellquote(data_dir),
|
||||
"OPENCLAW_HOME=" .. luci.util.shellquote(data_dir),
|
||||
"OPENCLAW_STATE_DIR=" .. luci.util.shellquote(data_dir .. "/.openclaw"),
|
||||
"OPENCLAW_CONFIG_PATH=" .. luci.util.shellquote(data_dir .. "/.openclaw/openclaw.json"),
|
||||
"PATH=" .. luci.util.shellquote(env_path),
|
||||
}, " ")
|
||||
|
||||
local start_cmd = string.format(
|
||||
"%s %s -d 2 --once -p 7682 sh -lc %s &",
|
||||
env_prefix,
|
||||
cmd_ttyd,
|
||||
luci.util.shellquote(cmdline)
|
||||
)
|
||||
os.execute(start_cmd)
|
||||
|
||||
m.children[#m.children] = nil
|
||||
s = m:section(SimpleSection)
|
||||
o = s:option(DummyValue, "console")
|
||||
o.template = "openclawmgr/cli"
|
||||
end
|
||||
|
||||
return m
|
||||
@@ -0,0 +1,11 @@
|
||||
<div class="cbi-map">
|
||||
<iframe id="terminal" style="width: 100%; min-height: 600px; border: none; border-radius: 3px;"></iframe>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
const el = document.querySelectorAll(".cbi-value");
|
||||
for (let i = 0; i < el.length; i++) {
|
||||
el[i].style.display = "none";
|
||||
}
|
||||
document.getElementById("terminal").src = "http://" + window.location.hostname + ":7682";
|
||||
</script>
|
||||
|
||||
@@ -0,0 +1,228 @@
|
||||
<%+header%>
|
||||
|
||||
<%
|
||||
local logs_url = luci.dispatcher.build_url("admin", "services", "openclawmgr", "logs_api")
|
||||
%>
|
||||
|
||||
<style type="text/css">
|
||||
.oclm-logbar { display:flex; gap:10px; align-items:center; flex-wrap:wrap; margin: 8px 0 10px 0; }
|
||||
.oclm-logbar label { color:#555; font-size:12px; }
|
||||
.oclm-logbar input[type="text"] { padding: 6px 10px; border: 1px solid #d0d7de; border-radius: 6px; background:#fff; min-width: 220px; }
|
||||
.oclm-logbar select { padding: 6px 10px; border: 1px solid #d0d7de; border-radius: 6px; background:#fff; }
|
||||
.oclm-logbar .chk { display:flex; align-items:center; gap:6px; font-size:12px; color:#555; }
|
||||
.oclm-logbar button { cursor:pointer; }
|
||||
.oclm-logbar .meta { margin-left:auto; font-size:12px; color:#666; }
|
||||
.oclm-logwrap { border:1px solid #d0d7de; border-radius:8px; background:#0b1020; color:#dbe2ff; padding:10px; }
|
||||
.oclm-logpre { white-space: pre-wrap; word-break: break-word; font-family: monospace; font-size: 12px; line-height: 1.5; margin:0; max-height: 70vh; overflow:auto; }
|
||||
.oclm-hint { color:#666; font-size:12px; line-height:1.6; margin-top:8px; }
|
||||
</style>
|
||||
|
||||
<h2><%:日志%></h2>
|
||||
|
||||
<div class="oclm-logbar">
|
||||
<label for="oclm-log-kind"><%:来源%></label>
|
||||
<select id="oclm-log-kind">
|
||||
<option value="openclaw"><%:OpenClaw%></option>
|
||||
<option value="openclawmgr"><%:OpenClawMgr%></option>
|
||||
<option value="tasks"><%:任务日志%></option>
|
||||
<option value="all"><%:全部%></option>
|
||||
</select>
|
||||
<label for="oclm-log-limit"><%:行数%></label>
|
||||
<select id="oclm-log-limit">
|
||||
<option value="200">200</option>
|
||||
<option value="500">500</option>
|
||||
<option value="1000">1000</option>
|
||||
<option value="2000">2000</option>
|
||||
</select>
|
||||
<label for="oclm-log-range"><%:时间范围%></label>
|
||||
<select id="oclm-log-range">
|
||||
<option value="all"><%:全部%></option>
|
||||
<option value="300"><%:最近 5 分钟%></option>
|
||||
<option value="900"><%:最近 15 分钟%></option>
|
||||
<option value="3600"><%:最近 1 小时%></option>
|
||||
<option value="21600"><%:最近 6 小时%></option>
|
||||
<option value="86400"><%:最近 24 小时%></option>
|
||||
</select>
|
||||
<label for="oclm-log-keyword"><%:关键字%></label>
|
||||
<input id="oclm-log-keyword" type="text" placeholder="<%:输入关键字过滤...%>">
|
||||
<span class="chk"><input id="oclm-log-autoscroll" type="checkbox" checked> <%:自动滚动%></span>
|
||||
<button type="button" class="cbi-button cbi-button-action" id="oclm-log-apply"><%:确定%></button>
|
||||
<span id="oclm-meta" class="meta">-</span>
|
||||
</div>
|
||||
|
||||
<div class="oclm-logwrap">
|
||||
<pre class="oclm-logpre" id="oclm-logpre">-</pre>
|
||||
</div>
|
||||
|
||||
<div class="oclm-hint">
|
||||
<div><%:OpenClaw / OpenClawMgr 日志来自系统 logd,可在 SSH 运行%> <code>logread</code> <%:查看。%></div>
|
||||
<div><%:任务日志为操作安装/升级/重启等时 taskd 输出,路径通常为%> <code>/var/log/tasks/openclawmgr.log</code>。</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
//<![CDATA[
|
||||
(function() {
|
||||
var url = '<%=logs_url%>';
|
||||
var pre = document.getElementById('oclm-logpre');
|
||||
var kindSel = document.getElementById('oclm-log-kind');
|
||||
var limitSel = document.getElementById('oclm-log-limit');
|
||||
var rangeSel = document.getElementById('oclm-log-range');
|
||||
var kwInput = document.getElementById('oclm-log-keyword');
|
||||
var autoScroll = document.getElementById('oclm-log-autoscroll');
|
||||
var btn = document.getElementById('oclm-log-apply');
|
||||
var meta = document.getElementById('oclm-meta');
|
||||
|
||||
var rawLog = '';
|
||||
var serverNowSec = null;
|
||||
var pollTimer = null;
|
||||
var reqSeq = 0;
|
||||
var filtersActive = false;
|
||||
var activeKeyword = '';
|
||||
var activeRange = 'all';
|
||||
|
||||
function monthToNum(mon) {
|
||||
var m = {Jan:0,Feb:1,Mar:2,Apr:3,May:4,Jun:5,Jul:6,Aug:7,Sep:8,Oct:9,Nov:10,Dec:11};
|
||||
return (m[mon] !== undefined) ? m[mon] : null;
|
||||
}
|
||||
|
||||
function parseLogTimeSec(line) {
|
||||
// Common logread formats:
|
||||
// - "Mar 21 12:34:56 ..." (ubox)
|
||||
// - "Sat Mar 21 12:34:56 2026 ..." / "Sat Mar 21 12:34:56 ..." (busybox variants)
|
||||
var now = serverNowSec ? new Date(serverNowSec * 1000) : new Date();
|
||||
var year = now.getFullYear();
|
||||
var m = line.match(/^([A-Z][a-z]{2})\s+(\d{1,2})\s+(\d{2}):(\d{2}):(\d{2})\b/);
|
||||
if (!m) {
|
||||
m = line.match(/^[A-Z][a-z]{2}\s+([A-Z][a-z]{2})\s+(\d{1,2})\s+(\d{2}):(\d{2}):(\d{2})\b/);
|
||||
if (m) m.splice(1, 0, m[1]); // keep shape
|
||||
}
|
||||
if (!m) return null;
|
||||
var monStr = m[1];
|
||||
var mon = monthToNum(monStr);
|
||||
if (mon === null) return null;
|
||||
var day = parseInt(m[2], 10);
|
||||
var hh = parseInt(m[3], 10);
|
||||
var mm = parseInt(m[4], 10);
|
||||
var ss = parseInt(m[5], 10);
|
||||
var dt = new Date(year, mon, day, hh, mm, ss);
|
||||
// If parsed time is > 36h in the future, assume it belongs to last year (year rollover).
|
||||
var delta = dt.getTime() - now.getTime();
|
||||
if (delta > 36 * 3600 * 1000) dt = new Date(year - 1, mon, day, hh, mm, ss);
|
||||
return Math.floor(dt.getTime() / 1000);
|
||||
}
|
||||
|
||||
function applyFilters(raw) {
|
||||
if (!filtersActive) {
|
||||
var total0 = (raw || '').split(/\r?\n/).filter(Boolean).length;
|
||||
meta.textContent = total0 + ' lines';
|
||||
return raw || '';
|
||||
}
|
||||
|
||||
var kw = (activeKeyword || '').trim();
|
||||
var range = activeRange || 'all';
|
||||
var since = null;
|
||||
if (range !== 'all') {
|
||||
var sec = parseInt(range, 10);
|
||||
if (!isNaN(sec) && sec > 0) {
|
||||
var base = serverNowSec || Math.floor(Date.now() / 1000);
|
||||
since = base - sec;
|
||||
}
|
||||
}
|
||||
|
||||
var lines = (raw || '').split(/\r?\n/);
|
||||
var out = [];
|
||||
var matched = 0;
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
var line = lines[i];
|
||||
if (!line) continue;
|
||||
|
||||
if (since !== null) {
|
||||
var ts = parseLogTimeSec(line);
|
||||
if (ts !== null && ts < since) continue;
|
||||
}
|
||||
|
||||
if (kw) {
|
||||
if (line.toLowerCase().indexOf(kw.toLowerCase()) === -1) continue;
|
||||
}
|
||||
|
||||
out.push(line);
|
||||
matched++;
|
||||
}
|
||||
|
||||
var total = (raw || '').split(/\r?\n/).filter(Boolean).length;
|
||||
meta.textContent = matched + '/' + total + ' lines';
|
||||
return out.join('\n');
|
||||
}
|
||||
|
||||
function render() {
|
||||
var text = applyFilters(rawLog);
|
||||
pre.textContent = text || '';
|
||||
if (autoScroll.checked) {
|
||||
pre.scrollTop = pre.scrollHeight;
|
||||
}
|
||||
}
|
||||
|
||||
function fetchOnce() {
|
||||
var kind = kindSel.value || 'openclaw';
|
||||
var limit = limitSel.value || '200';
|
||||
var cur = ++reqSeq;
|
||||
(new XHR()).get(url, { kind: kind, limit: limit }, function(x) {
|
||||
if (cur !== reqSeq) return;
|
||||
try {
|
||||
var d = JSON.parse(x.responseText || '{}');
|
||||
rawLog = d.log || '';
|
||||
serverNowSec = d.server_time || null;
|
||||
render();
|
||||
} catch(e) {
|
||||
rawLog = x.responseText || '';
|
||||
serverNowSec = null;
|
||||
render();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
btn.addEventListener('click', function() {
|
||||
filtersActive = true;
|
||||
activeKeyword = (kwInput.value || '').trim();
|
||||
activeRange = rangeSel.value || 'all';
|
||||
render();
|
||||
fetchOnce();
|
||||
});
|
||||
kindSel.addEventListener('change', fetchOnce);
|
||||
limitSel.addEventListener('change', fetchOnce);
|
||||
rangeSel.addEventListener('change', function() {
|
||||
if (filtersActive) {
|
||||
activeRange = rangeSel.value || 'all';
|
||||
render();
|
||||
}
|
||||
});
|
||||
kwInput.addEventListener('keydown', function(ev) {
|
||||
if (ev.key === 'Enter') {
|
||||
ev.preventDefault();
|
||||
btn.click();
|
||||
}
|
||||
});
|
||||
kwInput.addEventListener('compositionend', function() {
|
||||
// No-op: filter applies on "确定"
|
||||
});
|
||||
autoScroll.addEventListener('change', function() { render(); });
|
||||
|
||||
function startPoll() {
|
||||
if (pollTimer) window.clearInterval(pollTimer);
|
||||
pollTimer = window.setInterval(fetchOnce, 2000);
|
||||
}
|
||||
|
||||
function stopPoll() {
|
||||
if (pollTimer) window.clearInterval(pollTimer);
|
||||
pollTimer = null;
|
||||
}
|
||||
|
||||
window.addEventListener('beforeunload', stopPoll);
|
||||
|
||||
fetchOnce();
|
||||
startPoll();
|
||||
})();
|
||||
//]]>
|
||||
</script>
|
||||
|
||||
<%+footer%>
|
||||
@@ -0,0 +1,217 @@
|
||||
#!/bin/sh
|
||||
# ============================================================================
|
||||
# OpenClawMgr CLI Config Helper (LAN ttyd)
|
||||
# - Provides an interactive menu to run OpenClaw official configure wizard,
|
||||
# manage config file, and restart gateway.
|
||||
# ============================================================================
|
||||
|
||||
set -e
|
||||
|
||||
# Ensure a sensible TERM for ttyd/xterm.js.
|
||||
export TERM="${TERM:-xterm-256color}"
|
||||
|
||||
ESC="$(printf '\033')"
|
||||
RED="${ESC}[0;31m"; GREEN="${ESC}[0;32m"; YELLOW="${ESC}[1;33m"
|
||||
CYAN="${ESC}[0;36m"; BOLD="${ESC}[1m"; DIM="${ESC}[2m"; NC="${ESC}[0m"
|
||||
|
||||
prompt_with_default() {
|
||||
local prompt="$1" def="$2" var="$3" v=""
|
||||
if [ -n "$def" ]; then
|
||||
printf "%s%s[%s]%s: " "$prompt" "$DIM" "$def" "$NC"
|
||||
else
|
||||
printf "%s: " "$prompt"
|
||||
fi
|
||||
# shellcheck disable=SC2162
|
||||
read v
|
||||
[ -z "$v" ] && v="$def"
|
||||
eval "$var=\$v"
|
||||
}
|
||||
|
||||
pause_enter() {
|
||||
printf "\n%s按回车继续...%s" "$DIM" "$NC"
|
||||
# shellcheck disable=SC2162
|
||||
read _
|
||||
}
|
||||
|
||||
uci_get() { uci -q get "openclawmgr.main.$1" 2>/dev/null || true; }
|
||||
|
||||
BASE_DIR="${BASE_DIR:-$(uci_get base_dir)}"
|
||||
if [ -z "$BASE_DIR" ]; then
|
||||
printf '%s\n' "${RED}ERROR: base_dir 未配置。请先在 OpenClaw 启动器页面设置数据目录并保存应用。${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
NODE_DIR="${BASE_DIR}/node"
|
||||
GLOBAL_DIR="${BASE_DIR}/global"
|
||||
DATA_DIR="${BASE_DIR}/data"
|
||||
|
||||
NODE_BIN="${NODE_DIR}/bin/node"
|
||||
OPENCLAW_BIN="${GLOBAL_DIR}/bin/openclaw"
|
||||
CONFIG_FILE="${DATA_DIR}/.openclaw/openclaw.json"
|
||||
BACKUP_DIR="${DATA_DIR}/.openclaw/backups"
|
||||
|
||||
export HOME="$DATA_DIR"
|
||||
export OPENCLAW_HOME="$DATA_DIR"
|
||||
export OPENCLAW_STATE_DIR="${DATA_DIR}/.openclaw"
|
||||
export OPENCLAW_CONFIG_PATH="$CONFIG_FILE"
|
||||
export PATH="${NODE_DIR}/bin:${GLOBAL_DIR}/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
||||
|
||||
find_entry() {
|
||||
local d="${GLOBAL_DIR}/lib/node_modules/openclaw"
|
||||
[ -f "${d}/openclaw.mjs" ] && { echo "${d}/openclaw.mjs"; return 0; }
|
||||
[ -f "${d}/dist/cli.js" ] && { echo "${d}/dist/cli.js"; return 0; }
|
||||
return 1
|
||||
}
|
||||
|
||||
OPENCLAW_ENTRY="$(find_entry 2>/dev/null || true)"
|
||||
|
||||
oc_cmd() {
|
||||
if [ -x "$OPENCLAW_BIN" ]; then
|
||||
"$OPENCLAW_BIN" "$@"
|
||||
return $?
|
||||
fi
|
||||
if [ -x "$NODE_BIN" ] && [ -n "$OPENCLAW_ENTRY" ]; then
|
||||
"$NODE_BIN" "$OPENCLAW_ENTRY" "$@"
|
||||
return $?
|
||||
fi
|
||||
printf '%s\n' "${RED}ERROR: OpenClaw 未安装或运行时不完整。${NC}"
|
||||
printf '%s\n' "HINT: 在 OpenClaw 启动器页面执行安装/升级。"
|
||||
return 127
|
||||
}
|
||||
|
||||
restart_gateway() {
|
||||
printf '\n'
|
||||
printf '%s\n' "${CYAN}=== 重启 Gateway ===${NC}"
|
||||
/usr/libexec/istorec/openclawmgr.sh restart 2>&1 || true
|
||||
}
|
||||
|
||||
apply_config() {
|
||||
printf '\n'
|
||||
printf '%s\n' "${CYAN}=== 重新生成/应用配置(以 OpenClawMgr 配置为准)===${NC}"
|
||||
/usr/libexec/istorec/openclawmgr.sh apply_config 2>&1 || true
|
||||
}
|
||||
|
||||
show_config() {
|
||||
printf '\n'
|
||||
printf '%s\n' "${CYAN}=== 配置文件 ===${NC}"
|
||||
printf '%s\n' "路径: ${DIM}${CONFIG_FILE}${NC}"
|
||||
printf '\n'
|
||||
if [ ! -f "$CONFIG_FILE" ]; then
|
||||
printf '%s\n' "${YELLOW}(配置文件不存在)${NC}"
|
||||
return 0
|
||||
fi
|
||||
if [ -x "$NODE_BIN" ]; then
|
||||
"$NODE_BIN" -e "console.log(JSON.stringify(JSON.parse(require('fs').readFileSync(process.env.OPENCLAW_CONFIG_PATH,'utf8')),null,2))" 2>/dev/null \
|
||||
|| cat "$CONFIG_FILE"
|
||||
else
|
||||
cat "$CONFIG_FILE"
|
||||
fi
|
||||
}
|
||||
|
||||
edit_config() {
|
||||
if [ ! -f "$CONFIG_FILE" ]; then
|
||||
printf '%s\n' "${YELLOW}配置文件不存在:${CONFIG_FILE}${NC}"
|
||||
return 1
|
||||
fi
|
||||
if command -v vi >/dev/null 2>&1; then
|
||||
vi "$CONFIG_FILE"
|
||||
elif command -v nano >/dev/null 2>&1; then
|
||||
nano "$CONFIG_FILE"
|
||||
else
|
||||
printf '%s\n' "${YELLOW}未找到编辑器(vi/nano)。${NC}"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
tail_logs() {
|
||||
printf '\n'
|
||||
printf '%s\n' "${CYAN}=== 最近日志(logread -e openclaw)===${NC}"
|
||||
printf '\n'
|
||||
logread -e openclaw 2>/dev/null | tail -200 || printf '%s\n' "${YELLOW}(无法读取日志)${NC}"
|
||||
}
|
||||
|
||||
backup_config() {
|
||||
mkdir -p "$BACKUP_DIR" 2>/dev/null || true
|
||||
if [ ! -f "$CONFIG_FILE" ]; then
|
||||
printf '%s\n' "${YELLOW}配置文件不存在,无法备份。${NC}"
|
||||
return 1
|
||||
fi
|
||||
local ts dst
|
||||
ts="$(date -u +%Y-%m-%dT%H-%M-%SZ 2>/dev/null || date +%s)"
|
||||
dst="${BACKUP_DIR}/${ts}-openclaw.json"
|
||||
cp -f "$CONFIG_FILE" "$dst"
|
||||
printf '%s\n' "${GREEN}✅ 已备份到: ${dst}${NC}"
|
||||
}
|
||||
|
||||
restore_config() {
|
||||
mkdir -p "$BACKUP_DIR" 2>/dev/null || true
|
||||
printf '\n'
|
||||
printf '%s\n' "${CYAN}=== 可用备份 ===${NC}"
|
||||
ls -lt "$BACKUP_DIR"/*.json 2>/dev/null | head -10 | awk '{print " " $NF}' || printf '%s\n' "${YELLOW}(无备份)${NC}"
|
||||
printf '\n'
|
||||
local fp=""
|
||||
prompt_with_default "请输入要恢复的备份文件路径" "" fp
|
||||
[ -n "$fp" ] || return 0
|
||||
if [ ! -f "$fp" ]; then
|
||||
printf '%s\n' "${YELLOW}文件不存在:${fp}${NC}"
|
||||
return 1
|
||||
fi
|
||||
cp -f "$fp" "$CONFIG_FILE"
|
||||
printf '%s\n' "${GREEN}✅ 已恢复配置到: ${CONFIG_FILE}${NC}"
|
||||
}
|
||||
|
||||
main_menu() {
|
||||
while true; do
|
||||
printf '\n'
|
||||
printf '%s\n' "${BOLD}OpenClaw AI Gateway — CLI 配置入口(OpenClawMgr)${NC}"
|
||||
printf '%s\n' "${DIM}base_dir: ${BASE_DIR}${NC}"
|
||||
printf '\n'
|
||||
printf '%s\n' " ${CYAN}1)${NC} 🧭 官方配置向导 ${DIM}(openclaw configure)${NC}"
|
||||
printf '%s\n' " ${CYAN}2)${NC} ℹ️ OpenClaw 版本/帮助 ${DIM}(openclaw --version / --help)${NC}"
|
||||
printf '%s\n' " ${CYAN}3)${NC} 📄 查看配置文件 ${DIM}(openclaw.json)${NC}"
|
||||
printf '%s\n' " ${CYAN}4)${NC} ✍️ 编辑配置文件 ${DIM}(vi/nano)${NC}"
|
||||
printf '%s\n' " ${CYAN}5)${NC} ♻️ 应用/重建配置 ${DIM}(openclawmgr.sh apply_config)${NC}"
|
||||
printf '%s\n' " ${CYAN}6)${NC} 🔄 重启 Gateway ${DIM}(openclawmgr.sh restart)${NC}"
|
||||
printf '%s\n' " ${CYAN}7)${NC} 📋 查看日志 ${DIM}(logread -e openclaw)${NC}"
|
||||
printf '%s\n' " ${CYAN}8)${NC} 💾 备份配置"
|
||||
printf '%s\n' " ${CYAN}9)${NC} 📥 恢复配置"
|
||||
printf '\n'
|
||||
printf '%s\n' " ${CYAN}0)${NC} 退出"
|
||||
printf '\n'
|
||||
local c=""
|
||||
prompt_with_default "请选择" "1" c
|
||||
case "$c" in
|
||||
1)
|
||||
printf '\n'
|
||||
printf '%s\n' "${CYAN}=== openclaw configure ===${NC}"
|
||||
oc_cmd configure || true
|
||||
pause_enter
|
||||
;;
|
||||
2)
|
||||
printf '\n'
|
||||
oc_cmd --version 2>/dev/null || true
|
||||
printf '\n'
|
||||
oc_cmd --help 2>/dev/null | sed -n '1,120p' || true
|
||||
pause_enter
|
||||
;;
|
||||
3) show_config; pause_enter ;;
|
||||
4) edit_config || true; pause_enter ;;
|
||||
5) apply_config; pause_enter ;;
|
||||
6) restart_gateway; pause_enter ;;
|
||||
7) tail_logs; pause_enter ;;
|
||||
8) backup_config || true; pause_enter ;;
|
||||
9) restore_config || true; pause_enter ;;
|
||||
0) printf '%s\n' "${GREEN}再见!${NC}"; exit 0 ;;
|
||||
*) printf '%s\n' "${YELLOW}无效选择${NC}" ;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
case "${1:-}" in
|
||||
--help|-h)
|
||||
echo "Usage: oc-config.sh [--help]"
|
||||
;;
|
||||
*)
|
||||
main_menu
|
||||
;;
|
||||
esac
|
||||
@@ -0,0 +1,65 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=momo
|
||||
PKG_VERSION:=2025.12.31
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_LICENSE:=GPL-3.0+
|
||||
PKG_MAINTAINER:=Joseph Mory <morytyann@gmail.com>
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define Package/momo
|
||||
SECTION:=net
|
||||
CATEGORY:=Network
|
||||
TITLE:=The universal proxy platform.
|
||||
URL:=https://sing-box.sagernet.org
|
||||
DEPENDS:=+ca-bundle +curl firewall4 +ip-full +kmod-inet-diag +kmod-nft-socket +kmod-nft-tproxy +kmod-tun +sing-box
|
||||
endef
|
||||
|
||||
define Package/momo/description
|
||||
The universal proxy platform.
|
||||
endef
|
||||
|
||||
define Package/momo/conffiles
|
||||
/etc/config/momo
|
||||
endef
|
||||
|
||||
define Package/momo/install
|
||||
$(INSTALL_DIR) $(1)/etc/momo
|
||||
$(INSTALL_DIR) $(1)/etc/momo/ucode
|
||||
$(INSTALL_DIR) $(1)/etc/momo/scripts
|
||||
$(INSTALL_DIR) $(1)/etc/momo/firewall
|
||||
$(INSTALL_DIR) $(1)/etc/momo/profiles
|
||||
$(INSTALL_DIR) $(1)/etc/momo/subscriptions
|
||||
$(INSTALL_DIR) $(1)/etc/momo/run
|
||||
|
||||
$(INSTALL_BIN) $(CURDIR)/files/ucode/include.uc $(1)/etc/momo/ucode/include.uc
|
||||
$(INSTALL_BIN) $(CURDIR)/files/ucode/hijack.ut $(1)/etc/momo/ucode/hijack.ut
|
||||
|
||||
$(INSTALL_BIN) $(CURDIR)/files/scripts/include.sh $(1)/etc/momo/scripts/include.sh
|
||||
$(INSTALL_BIN) $(CURDIR)/files/scripts/firewall_include.sh $(1)/etc/momo/scripts/firewall_include.sh
|
||||
$(INSTALL_BIN) $(CURDIR)/files/scripts/debug.sh $(1)/etc/momo/scripts/debug.sh
|
||||
|
||||
$(INSTALL_BIN) $(CURDIR)/files/firewall/geoip_cn.nft $(1)/etc/momo/firewall/geoip_cn.nft
|
||||
$(INSTALL_BIN) $(CURDIR)/files/firewall/geoip6_cn.nft $(1)/etc/momo/firewall/geoip6_cn.nft
|
||||
|
||||
$(INSTALL_DIR) $(1)/etc/config
|
||||
$(INSTALL_CONF) $(CURDIR)/files/momo.conf $(1)/etc/config/momo
|
||||
|
||||
$(INSTALL_DIR) $(1)/etc/init.d
|
||||
$(INSTALL_BIN) $(CURDIR)/files/momo.init $(1)/etc/init.d/momo
|
||||
|
||||
$(INSTALL_DIR) $(1)/etc/uci-defaults
|
||||
$(INSTALL_BIN) $(CURDIR)/files/uci-defaults/firewall.sh $(1)/etc/uci-defaults/99_firewall_momo
|
||||
$(INSTALL_BIN) $(CURDIR)/files/uci-defaults/migrate.sh $(1)/etc/uci-defaults/99_migrate_momo
|
||||
|
||||
$(INSTALL_DIR) $(1)/lib/upgrade/keep.d
|
||||
$(INSTALL_DATA) $(CURDIR)/files/momo.upgrade $(1)/lib/upgrade/keep.d/momo
|
||||
endef
|
||||
|
||||
define Build/Compile
|
||||
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,momo))
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,110 @@
|
||||
config config 'config'
|
||||
option 'init' '1'
|
||||
option 'enabled' '0'
|
||||
option 'profile' 'file:example.json'
|
||||
option 'start_delay' '0'
|
||||
option 'scheduled_restart' '0'
|
||||
option 'cron_expression' '0 3 * * *'
|
||||
option 'test_profile' '1'
|
||||
option 'core_only' '0'
|
||||
|
||||
config procd 'procd'
|
||||
option 'fast_reload' '0'
|
||||
|
||||
config core 'core'
|
||||
option 'redirect_inbound_tag' 'redirect-in'
|
||||
option 'tproxy_inbound_tag' 'tproxy-in'
|
||||
option 'tun_inbound_tag' 'tun-in'
|
||||
option 'dns_inbound_tag' 'dns-in'
|
||||
option 'fake_ip_dns_server_tag' 'fake-ip-dns-server'
|
||||
|
||||
config proxy 'proxy'
|
||||
option 'enabled' '1'
|
||||
option 'ipv4_dns_hijack' '1'
|
||||
option 'ipv6_dns_hijack' '1'
|
||||
option 'ipv4_proxy' '1'
|
||||
option 'ipv6_proxy' '1'
|
||||
option 'fake_ip_ping_hijack' '1'
|
||||
option 'tcp_mode' 'redirect'
|
||||
option 'udp_mode' 'tun'
|
||||
option 'router_proxy' '1'
|
||||
option 'lan_proxy' '1'
|
||||
list 'lan_inbound_interface' 'lan'
|
||||
list 'reserved_ip' '0.0.0.0/8'
|
||||
list 'reserved_ip' '10.0.0.0/8'
|
||||
list 'reserved_ip' '127.0.0.0/8'
|
||||
list 'reserved_ip' '100.64.0.0/10'
|
||||
list 'reserved_ip' '169.254.0.0/16'
|
||||
list 'reserved_ip' '172.16.0.0/12'
|
||||
list 'reserved_ip' '192.168.0.0/16'
|
||||
list 'reserved_ip' '224.0.0.0/4'
|
||||
list 'reserved_ip' '240.0.0.0/4'
|
||||
list 'reserved_ip6' '::/128'
|
||||
list 'reserved_ip6' '::1/128'
|
||||
list 'reserved_ip6' '::ffff:0:0/96'
|
||||
list 'reserved_ip6' '100::/64'
|
||||
list 'reserved_ip6' '64:ff9b::/96'
|
||||
list 'reserved_ip6' '2001::/32'
|
||||
list 'reserved_ip6' '2001:10::/28'
|
||||
list 'reserved_ip6' '2001:20::/28'
|
||||
list 'reserved_ip6' '2001:db8::/32'
|
||||
list 'reserved_ip6' '2002::/16'
|
||||
list 'reserved_ip6' 'fc00::/7'
|
||||
list 'reserved_ip6' 'fe80::/10'
|
||||
list 'reserved_ip6' 'ff00::/8'
|
||||
option 'bypass_china_mainland_ip' '0'
|
||||
option 'bypass_china_mainland_ip6' '0'
|
||||
option 'proxy_tcp_dport' '0-65535'
|
||||
option 'proxy_udp_dport' '0-65535'
|
||||
list 'bypass_dscp' '4'
|
||||
option 'tun_timeout' '30'
|
||||
option 'tun_interval' '1'
|
||||
|
||||
config router_access_control
|
||||
option 'enabled' '1'
|
||||
list 'user' 'dnsmasq'
|
||||
list 'user' 'ftp'
|
||||
list 'user' 'logd'
|
||||
list 'user' 'nobody'
|
||||
list 'user' 'ntp'
|
||||
list 'user' 'ubus'
|
||||
list 'group' 'dnsmasq'
|
||||
list 'group' 'ftp'
|
||||
list 'group' 'logd'
|
||||
list 'group' 'nogroup'
|
||||
list 'group' 'ntp'
|
||||
list 'group' 'ubus'
|
||||
list 'cgroup' 'services/adguardhome'
|
||||
list 'cgroup' 'services/aria2'
|
||||
list 'cgroup' 'services/dnsmasq'
|
||||
list 'cgroup' 'services/netbird'
|
||||
list 'cgroup' 'services/qbittorrent'
|
||||
list 'cgroup' 'services/sysntpd'
|
||||
list 'cgroup' 'services/tailscale'
|
||||
list 'cgroup' 'services/zerotier'
|
||||
option 'dns' '0'
|
||||
option 'proxy' '0'
|
||||
|
||||
config router_access_control
|
||||
option 'enabled' '1'
|
||||
option 'dns' '1'
|
||||
option 'proxy' '1'
|
||||
|
||||
config lan_access_control
|
||||
option 'enabled' '1'
|
||||
option 'dns' '1'
|
||||
option 'proxy' '1'
|
||||
|
||||
config routing 'routing'
|
||||
option 'tproxy_fw_mark' '0x80'
|
||||
option 'tproxy_fw_mask' '0xFF'
|
||||
option 'tun_fw_mark' '0x81'
|
||||
option 'tun_fw_mask' '0xFF'
|
||||
option 'tproxy_rule_pref' '1024'
|
||||
option 'tun_rule_pref' '1025'
|
||||
option 'tproxy_route_table' '80'
|
||||
option 'tun_route_table' '81'
|
||||
option 'cgroup_id' '0x07250725'
|
||||
option 'cgroup_name' 'momo'
|
||||
|
||||
config placeholder 'placeholder'
|
||||
@@ -0,0 +1,500 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
|
||||
START=99
|
||||
STOP=10
|
||||
USE_PROCD=1
|
||||
|
||||
PROG="/usr/bin/sing-box"
|
||||
|
||||
. "$IPKG_INSTROOT/etc/momo/scripts/include.sh"
|
||||
|
||||
extra_command 'update_subscription' 'Update subscription by section id'
|
||||
|
||||
boot_func() {
|
||||
# prepare files
|
||||
prepare_files
|
||||
# load config
|
||||
config_load momo
|
||||
# start delay
|
||||
local enabled start_delay
|
||||
config_get_bool enabled "config" "enabled" 0
|
||||
config_get start_delay "config" "start_delay" 0
|
||||
if [ "$enabled" = 1 ] && [ "$start_delay" -gt 0 ]; then
|
||||
log "App" "Start after $start_delay seconds."
|
||||
sleep "$start_delay"
|
||||
fi
|
||||
# start
|
||||
start
|
||||
}
|
||||
|
||||
boot() {
|
||||
boot_func &
|
||||
}
|
||||
|
||||
start_service() {
|
||||
# prepare files
|
||||
prepare_files
|
||||
# load config
|
||||
config_load momo
|
||||
# check if enabled
|
||||
local enabled
|
||||
config_get_bool enabled "config" "enabled" 0
|
||||
if [ "$enabled" = 0 ]; then
|
||||
log "App" "Disabled."
|
||||
log "App" "Exit."
|
||||
return
|
||||
fi
|
||||
# start
|
||||
log "App" "Enabled."
|
||||
log "App" "Start."
|
||||
# check if executable
|
||||
if [ ! -x $PROG ]; then
|
||||
log "App" "Core is not executable."
|
||||
log "App" "Exit."
|
||||
return
|
||||
fi
|
||||
# get config
|
||||
## app config
|
||||
local scheduled_restart cron_expression profile test_profile core_only
|
||||
config_get_bool scheduled_restart "config" "scheduled_restart" 0
|
||||
config_get cron_expression "config" "cron_expression"
|
||||
config_get profile "config" "profile"
|
||||
config_get_bool test_profile "config" "test_profile" 0
|
||||
config_get_bool core_only "config" "core_only" 0
|
||||
## procd config
|
||||
### general
|
||||
local fast_reload
|
||||
config_get_bool fast_reload "procd" "fast_reload" 0
|
||||
### rlimit
|
||||
local rlimit_address_space_soft rlimit_address_space_hard rlimit_data_soft rlimit_data_hard rlimit_stack_soft rlimit_stack_hard rlimit_nofile_soft rlimit_nofile_hard
|
||||
config_get rlimit_address_space_soft "procd" "rlimit_address_space_soft"
|
||||
config_get rlimit_address_space_hard "procd" "rlimit_address_space_hard"
|
||||
config_get rlimit_data_soft "procd" "rlimit_data_soft"
|
||||
config_get rlimit_data_hard "procd" "rlimit_data_hard"
|
||||
config_get rlimit_stack_soft "procd" "rlimit_stack_soft"
|
||||
config_get rlimit_stack_hard "procd" "rlimit_stack_hard"
|
||||
config_get rlimit_nofile_soft "procd" "rlimit_nofile_soft" "$(ulimit -Sn)"
|
||||
config_get rlimit_nofile_hard "procd" "rlimit_nofile_hard" "$(ulimit -Hn)"
|
||||
## core config
|
||||
local redirect_inbound_tag tproxy_inbound_tag tun_inbound_tag dns_inbound_tag fake_ip_dns_server_tag
|
||||
config_get redirect_inbound_tag "core" "redirect_inbound_tag"
|
||||
config_get tproxy_inbound_tag "core" "tproxy_inbound_tag"
|
||||
config_get tun_inbound_tag "core" "tun_inbound_tag"
|
||||
config_get dns_inbound_tag "core" "dns_inbound_tag"
|
||||
config_get fake_ip_dns_server_tag "core" "fake_ip_dns_server_tag"
|
||||
## proxy config
|
||||
local proxy_enabled ipv4_dns_hijack ipv6_dns_hijack tcp_mode udp_mode
|
||||
config_get_bool proxy_enabled "proxy" "enabled" 0
|
||||
config_get_bool ipv4_dns_hijack "proxy" "ipv4_dns_hijack" 0
|
||||
config_get_bool ipv6_dns_hijack "proxy" "ipv6_dns_hijack" 0
|
||||
config_get tcp_mode "proxy" "tcp_mode"
|
||||
config_get udp_mode "proxy" "udp_mode"
|
||||
# get profile
|
||||
local profile_type; profile_type=$(echo "$profile" | cut -d ':' -f 1)
|
||||
local profile_id; profile_id=$(echo "$profile" | cut -d ':' -f 2)
|
||||
if [ "$profile_type" = "file" ]; then
|
||||
local profile_name; profile_name="$profile_id"
|
||||
local profile_file; profile_file="$PROFILES_DIR/$profile_name"
|
||||
log "Profile" "Use file: $profile_name."
|
||||
if [ ! -f "$profile_file" ]; then
|
||||
log "Profile" "File not found."
|
||||
log "App" "Exit."
|
||||
return
|
||||
fi
|
||||
cp -f "$profile_file" "$RUN_PROFILE_PATH"
|
||||
elif [ "$profile_type" = "subscription" ]; then
|
||||
local subscription_section; subscription_section="$profile_id"
|
||||
local subscription_name subscription_prefer
|
||||
config_get subscription_name "$subscription_section" "name"
|
||||
config_get subscription_prefer "$subscription_section" "prefer" "remote"
|
||||
log "Profile" "Use subscription: $subscription_name."
|
||||
local subscription_file; subscription_file="$SUBSCRIPTIONS_DIR/$subscription_section.json"
|
||||
if [ "$subscription_prefer" = "remote" ] || [ ! -f "$subscription_file" ]; then
|
||||
update_subscription "$subscription_section"
|
||||
fi
|
||||
if [ ! -f "$subscription_file" ]; then
|
||||
log "Profile" "Subscription file not found."
|
||||
log "App" "Exit."
|
||||
return
|
||||
fi
|
||||
cp -f "$subscription_file" "$RUN_PROFILE_PATH"
|
||||
else
|
||||
log "Profile" "No profile/subscription selected."
|
||||
log "App" "Exit."
|
||||
return
|
||||
fi
|
||||
# check profile
|
||||
if [ "$core_only" = 0 ] && [ "$proxy_enabled" = 1 ]; then
|
||||
log "Profile" "Checking..."
|
||||
if [ "$ipv4_dns_hijack" = 1 ] || [ "$ipv6_dns_hijack" = 1 ]; then
|
||||
local dns_port; dns_port=$(jsonfilter -q -i "$RUN_PROFILE_PATH" -e "@['inbounds'][*]" | jsonfilter -q -a -e "@[@['tag']='$dns_inbound_tag']" | jsonfilter -q -a -e "@[@['type']='direct']" | jsonfilter -q -e "@['listen_port']")
|
||||
if [ -z "$dns_port" ]; then
|
||||
log "Profile" "Check failed."
|
||||
log "Profile" "DNS inbound not found, should be tagged with $dns_inbound_tag and define listen_port."
|
||||
log "App" "Exit."
|
||||
return
|
||||
fi
|
||||
fi
|
||||
if [ "$tcp_mode" = "redirect" ]; then
|
||||
local redirect_port; redirect_port=$(jsonfilter -q -i "$RUN_PROFILE_PATH" -e "@['inbounds'][*]" | jsonfilter -q -a -e "@[@['tag']='$redirect_inbound_tag']" | jsonfilter -q -a -e "@[@['type']='redirect']" | jsonfilter -q -e "@['listen_port']")
|
||||
if [ -z "$redirect_port" ]; then
|
||||
log "Profile" "Check failed."
|
||||
log "Profile" "Redirect inbound not found, should be tagged with $redirect_inbound_tag and define listen_port."
|
||||
log "App" "Exit."
|
||||
return
|
||||
fi
|
||||
fi
|
||||
if [ "$tcp_mode" = "tproxy" ] || [ "$udp_mode" = "tproxy" ]; then
|
||||
local tproxy_port; tproxy_port=$(jsonfilter -q -i "$RUN_PROFILE_PATH" -e "@['inbounds'][*]" | jsonfilter -q -a -e "@[@['tag']='$tproxy_inbound_tag']" | jsonfilter -q -a -e "@[@['type']='tproxy']" | jsonfilter -q -e "@['listen_port']")
|
||||
if [ -z "$tproxy_port" ]; then
|
||||
log "Profile" "Check failed."
|
||||
log "Profile" "TPROXY inbound not found, should be tagged with $tproxy_inbound_tag and define listen_port."
|
||||
log "App" "Exit."
|
||||
return
|
||||
fi
|
||||
fi
|
||||
if [ "$tcp_mode" = "tun" ] || [ "$udp_mode" = "tun" ]; then
|
||||
local tun_device; tun_device=$(jsonfilter -q -i "$RUN_PROFILE_PATH" -e "@['inbounds'][*]" | jsonfilter -q -a -e "@[@['tag']='$tun_inbound_tag']" | jsonfilter -q -a -e "@[@['type']='tun']" | jsonfilter -q -e "@['interface_name']")
|
||||
if [ -z "$tun_device" ]; then
|
||||
log "Profile" "Check failed."
|
||||
log "Profile" "TUN inbound not found, should be tagged with $tun_inbound_tag and define interface_name."
|
||||
log "App" "Exit."
|
||||
return
|
||||
fi
|
||||
fi
|
||||
log "Profile" "Check passed."
|
||||
fi
|
||||
# test profile
|
||||
if [ "$test_profile" = 1 ]; then
|
||||
log "Profile" "Testing..."
|
||||
if $PROG -D "$RUN_DIR" check >> "$CORE_LOG_PATH" 2>&1; then
|
||||
log "Profile" "Test passed."
|
||||
else
|
||||
log "Profile" "Test failed."
|
||||
log "Profile" "Please check the core log to find out the problem."
|
||||
log "App" "Exit."
|
||||
return
|
||||
fi
|
||||
fi
|
||||
# start core
|
||||
log "Core" "Start."
|
||||
procd_open_instance momo
|
||||
|
||||
procd_set_param command /bin/sh -c "$PROG -D $RUN_DIR run --disable-color >> $CORE_LOG_PATH 2>&1"
|
||||
procd_set_param file "$RUN_PROFILE_PATH"
|
||||
if [ "$fast_reload" = 1 ]; then
|
||||
procd_set_param reload_signal HUP
|
||||
fi
|
||||
procd_set_param pidfile "$PID_FILE_PATH"
|
||||
procd_set_param respawn
|
||||
procd_append_param limits as="${rlimit_address_space_soft:-unlimited} ${rlimit_address_space_hard:-unlimited}"
|
||||
procd_append_param limits data="${rlimit_data_soft:-unlimited} ${rlimit_data_hard:-unlimited}"
|
||||
procd_append_param limits stack="${rlimit_stack_soft:-unlimited} ${rlimit_stack_hard:-unlimited}"
|
||||
procd_append_param limits nofile="${rlimit_nofile_soft} ${rlimit_nofile_hard}"
|
||||
|
||||
procd_close_instance
|
||||
# cron
|
||||
if [ "$scheduled_restart" = 1 ] && [ -n "$cron_expression" ]; then
|
||||
log "App" "Set scheduled restart."
|
||||
echo "$cron_expression /etc/init.d/momo restart #momo" >> "/etc/crontabs/root"
|
||||
/etc/init.d/cron restart
|
||||
fi
|
||||
# set started flag
|
||||
touch "$STARTED_FLAG_PATH"
|
||||
}
|
||||
|
||||
service_started() {
|
||||
# check if started
|
||||
if [ ! -f "$STARTED_FLAG_PATH" ]; then
|
||||
return
|
||||
fi
|
||||
# load config
|
||||
config_load momo
|
||||
# check if proxy enabled
|
||||
local proxy_enabled
|
||||
config_get_bool proxy_enabled "proxy" "enabled" 0
|
||||
if [ "$proxy_enabled" = 0 ]; then
|
||||
log "Proxy" "Disabled."
|
||||
return
|
||||
fi
|
||||
# get config
|
||||
## app config
|
||||
local core_only
|
||||
config_get_bool core_only "config" "core_only" 0
|
||||
## core
|
||||
local tun_inbound_tag
|
||||
config_get tun_inbound_tag "core" "tun_inbound_tag"
|
||||
## proxy config
|
||||
### general
|
||||
local ipv4_proxy ipv6_proxy tcp_mode udp_mode tun_timeout tun_interval
|
||||
config_get_bool ipv4_proxy "proxy" "ipv4_proxy" 0
|
||||
config_get_bool ipv6_proxy "proxy" "ipv6_proxy" 0
|
||||
config_get tcp_mode "proxy" "tcp_mode"
|
||||
config_get udp_mode "proxy" "udp_mode"
|
||||
config_get tun_timeout "proxy" "tun_timeout" 30
|
||||
config_get tun_interval "proxy" "tun_interval" 1
|
||||
## routing config
|
||||
local tproxy_fw_mark tproxy_fw_mask tun_fw_mark tun_fw_mask tproxy_rule_pref tun_rule_pref tproxy_route_table tun_route_table cgroup_id cgroup_name
|
||||
config_get tproxy_fw_mark "routing" "tproxy_fw_mark" "0x80"
|
||||
config_get tproxy_fw_mask "routing" "tproxy_fw_mask" "0xFF"
|
||||
config_get tun_fw_mark "routing" "tun_fw_mark" "0x81"
|
||||
config_get tun_fw_mask "routing" "tun_fw_mask" "0xFF"
|
||||
config_get tproxy_rule_pref "routing" "tproxy_rule_pref" "1024"
|
||||
config_get tun_rule_pref "routing" "tun_rule_pref" "1025"
|
||||
config_get tproxy_route_table "routing" "tproxy_route_table" "80"
|
||||
config_get tun_route_table "routing" "tun_route_table" "81"
|
||||
config_get cgroup_id "routing" "cgroup_id" "0x12061206"
|
||||
config_get cgroup_name "routing" "cgroup_name" "momo"
|
||||
# prepare config
|
||||
local tproxy_enable; tproxy_enable=0
|
||||
if [ "$tcp_mode" = "tproxy" ] || [ "$udp_mode" = "tproxy" ]; then
|
||||
tproxy_enable=1
|
||||
fi
|
||||
local tun_enable; tun_enable=0
|
||||
if [ "$tcp_mode" = "tun" ] || [ "$udp_mode" = "tun" ]; then
|
||||
tun_enable=1
|
||||
fi
|
||||
local tun_device; tun_device=$(jsonfilter -q -i "$RUN_PROFILE_PATH" -e "@['inbounds'][*]" | jsonfilter -q -a -e "@[@['tag']='$tun_inbound_tag']" | jsonfilter -q -a -e "@[@['type']='tun']" | jsonfilter -q -e "@['interface_name']")
|
||||
if [ "$core_only" = 0 ]; then
|
||||
# proxy
|
||||
log "Proxy" "Enabled."
|
||||
# wait for tun device online
|
||||
if [ "$tun_enable" = 1 ]; then
|
||||
log "Proxy" "Waiting for tun device online within $tun_timeout seconds..."
|
||||
while [ "$tun_timeout" -gt 0 ]; do
|
||||
if ip -j link show dev "$tun_device" 2>/dev/null | jsonfilter -q -e "@[@['flags'][@='UP']]" > /dev/null 2>&1; then
|
||||
log "Proxy" "TUN device is online."
|
||||
break
|
||||
fi
|
||||
tun_timeout=$((tun_timeout - tun_interval))
|
||||
sleep "$tun_interval"
|
||||
done
|
||||
if [ "$tun_timeout" -le 0 ]; then
|
||||
log "Proxy" "Timeout, TUN device is not online."
|
||||
log "App" "Exit."
|
||||
return
|
||||
fi
|
||||
fi
|
||||
# fix compatible with dockerd
|
||||
## cgroupfs-mount
|
||||
### when cgroupfs-mount is installed, cgroupv1 will mounted instead of cgroupv2, we need to create cgroup manually
|
||||
if mount | grep -q -w "^cgroup"; then
|
||||
mkdir -p "/sys/fs/cgroup/net_cls/$cgroup_name"
|
||||
echo "$cgroup_id" > "/sys/fs/cgroup/net_cls/$cgroup_name/net_cls.classid"
|
||||
cat "$PID_FILE_PATH" > "/sys/fs/cgroup/net_cls/$cgroup_name/cgroup.procs"
|
||||
fi
|
||||
## kmod-br-netfilter
|
||||
### when kmod-br-netfilter is loaded, bridge-nf-call-iptables and bridge-nf-call-ip6tables are set to 1, we need to set them to 0 if tproxy is enabled
|
||||
if [ "$tproxy_enable" = 1 ] && (lsmod | grep -q br_netfilter); then
|
||||
if [ "$ipv4_proxy" = 1 ]; then
|
||||
local bridge_nf_call_iptables; bridge_nf_call_iptables=$(sysctl -e -n net.bridge.bridge-nf-call-iptables)
|
||||
if [ "$bridge_nf_call_iptables" = 1 ]; then
|
||||
touch "$BRIDGE_NF_CALL_IPTABLES_FLAG_PATH"
|
||||
sysctl -q -w net.bridge.bridge-nf-call-iptables=0
|
||||
fi
|
||||
fi
|
||||
if [ "$ipv6_proxy" = 1 ]; then
|
||||
local bridge_nf_call_ip6tables; bridge_nf_call_ip6tables=$(sysctl -e -n net.bridge.bridge-nf-call-ip6tables)
|
||||
if [ "$bridge_nf_call_ip6tables" = 1 ]; then
|
||||
touch "$BRIDGE_NF_CALL_IP6TABLES_FLAG_PATH"
|
||||
sysctl -q -w net.bridge.bridge-nf-call-ip6tables=0
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
# ip route and rule
|
||||
if [ "$tproxy_enable" = 1 ]; then
|
||||
if [ "$ipv4_proxy" = 1 ]; then
|
||||
ip -4 route add local default dev lo table "$tproxy_route_table"
|
||||
ip -4 rule add pref "$tproxy_rule_pref" fwmark "$tproxy_fw_mark/$tproxy_fw_mask" table "$tproxy_route_table"
|
||||
fi
|
||||
if [ "$ipv6_proxy" = 1 ]; then
|
||||
ip -6 route add local default dev lo table "$tproxy_route_table"
|
||||
ip -6 rule add pref "$tproxy_rule_pref" fwmark "$tproxy_fw_mark/$tproxy_fw_mask" table "$tproxy_route_table"
|
||||
fi
|
||||
fi
|
||||
if [ "$tun_enable" = 1 ]; then
|
||||
if [ "$ipv4_proxy" = 1 ]; then
|
||||
ip -4 route add unicast default dev "$tun_device" table "$tun_route_table"
|
||||
ip -4 rule add pref "$tun_rule_pref" fwmark "$tun_fw_mark/$tun_fw_mask" table "$tun_route_table"
|
||||
fi
|
||||
if [ "$ipv6_proxy" = 1 ]; then
|
||||
ip -6 route add unicast default dev "$tun_device" table "$tun_route_table"
|
||||
ip -6 rule add pref "$tun_rule_pref" fwmark "$tun_fw_mark/$tun_fw_mask" table "$tun_route_table"
|
||||
fi
|
||||
$FIREWALL_INCLUDE_SH
|
||||
fi
|
||||
# hijack
|
||||
utpl -S "$HIJACK_UT" | nft -f -
|
||||
# check hijack
|
||||
if nft list tables | grep -q momo; then
|
||||
log "Proxy" "Hijack successful."
|
||||
else
|
||||
log "Proxy" "Hijack failed."
|
||||
log "App" "Exit."
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
service_stopped() {
|
||||
cleanup
|
||||
}
|
||||
|
||||
reload_service() {
|
||||
cleanup
|
||||
start
|
||||
}
|
||||
|
||||
service_triggers() {
|
||||
procd_add_reload_trigger "momo"
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
# clear log
|
||||
clear_log
|
||||
# load config
|
||||
config_load momo
|
||||
# get config
|
||||
## routing config
|
||||
local tproxy_route_table tun_route_table
|
||||
config_get tproxy_route_table "routing" "tproxy_route_table" "80"
|
||||
config_get tun_route_table "routing" "tun_route_table" "81"
|
||||
# delete routing policy
|
||||
ip -4 rule del table "$tproxy_route_table" > /dev/null 2>&1
|
||||
ip -4 rule del table "$tun_route_table" > /dev/null 2>&1
|
||||
ip -6 rule del table "$tproxy_route_table" > /dev/null 2>&1
|
||||
ip -6 rule del table "$tun_route_table" > /dev/null 2>&1
|
||||
# delete routing table
|
||||
ip -4 route flush table "$tproxy_route_table" > /dev/null 2>&1
|
||||
ip -4 route flush table "$tun_route_table" > /dev/null 2>&1
|
||||
ip -6 route flush table "$tproxy_route_table" > /dev/null 2>&1
|
||||
ip -6 route flush table "$tun_route_table" > /dev/null 2>&1
|
||||
# delete hijack
|
||||
nft delete table inet momo > /dev/null 2>&1
|
||||
local handles handle
|
||||
handles=$(nft --json list table inet fw4 | jsonfilter -q -e "@['nftables'][*]['rule']" | jsonfilter -q -a -e "@[@['chain']='input']" | jsonfilter -q -a -e "@[@['comment']='momo']" | jsonfilter -q -a -e "@[*]['handle']")
|
||||
for handle in $handles; do
|
||||
nft delete rule inet fw4 input handle "$handle"
|
||||
done
|
||||
handles=$(nft --json list table inet fw4 | jsonfilter -q -e "@['nftables'][*]['rule']" | jsonfilter -q -a -e "@[@['chain']='forward']" | jsonfilter -q -a -e "@[@['comment']='momo']" | jsonfilter -q -a -e "@[*]['handle']")
|
||||
for handle in $handles; do
|
||||
nft delete rule inet fw4 forward handle "$handle"
|
||||
done
|
||||
# delete started flag
|
||||
rm "$STARTED_FLAG_PATH" > /dev/null 2>&1
|
||||
# revert fix compatible with dockerd
|
||||
## kmod-br-netfilter
|
||||
if rm "$BRIDGE_NF_CALL_IPTABLES_FLAG_PATH" > /dev/null 2>&1; then
|
||||
sysctl -q -w net.bridge.bridge-nf-call-iptables=1
|
||||
fi
|
||||
if rm "$BRIDGE_NF_CALL_IP6TABLES_FLAG_PATH" > /dev/null 2>&1; then
|
||||
sysctl -q -w net.bridge.bridge-nf-call-ip6tables=1
|
||||
fi
|
||||
# delete cron
|
||||
sed -i "/#momo/d" "/etc/crontabs/root" > /dev/null 2>&1
|
||||
/etc/init.d/cron restart
|
||||
}
|
||||
|
||||
update_subscription() {
|
||||
local subscription_section; subscription_section="$1"
|
||||
if [ -z "$subscription_section" ]; then
|
||||
return
|
||||
fi
|
||||
# load config
|
||||
config_load momo
|
||||
# get subscription config
|
||||
local subscription_name subscription_info_url subscription_url subscription_user_agent
|
||||
config_get subscription_name "$subscription_section" "name"
|
||||
config_get subscription_info_url "$subscription_section" "info_url"
|
||||
config_get subscription_url "$subscription_section" "url"
|
||||
config_get subscription_user_agent "$subscription_section" "user_agent"
|
||||
# reset subscription info
|
||||
uci_remove "momo" "$subscription_section" "expire" > /dev/null 2>&1
|
||||
uci_remove "momo" "$subscription_section" "upload" > /dev/null 2>&1
|
||||
uci_remove "momo" "$subscription_section" "download" > /dev/null 2>&1
|
||||
uci_remove "momo" "$subscription_section" "total" > /dev/null 2>&1
|
||||
uci_remove "momo" "$subscription_section" "used" > /dev/null 2>&1
|
||||
uci_remove "momo" "$subscription_section" "avaliable" > /dev/null 2>&1
|
||||
uci_remove "momo" "$subscription_section" "update" > /dev/null 2>&1
|
||||
uci_remove "momo" "$subscription_section" "success" > /dev/null 2>&1
|
||||
# update subscription
|
||||
log "Profile" "Update subscription: $subscription_name."
|
||||
local success
|
||||
local subscription_info_header_tmpfile; subscription_info_header_tmpfile="$TEMP_DIR/$subscription_section.info.header"
|
||||
local subscription_header_tmpfile; subscription_header_tmpfile="$TEMP_DIR/$subscription_section.header"
|
||||
local subscription_tmpfile; subscription_tmpfile="$TEMP_DIR/$subscription_section.json"
|
||||
local subscription_info_file
|
||||
local subscription_file; subscription_file="$SUBSCRIPTIONS_DIR/$subscription_section.json"
|
||||
# fetch subscription info
|
||||
if [ -n "$subscription_info_url" ]; then
|
||||
log "Profile" "Fetch subscription info."
|
||||
if curl -s -f -m 120 --connect-timeout 15 --retry 3 -L -X GET -A "$subscription_user_agent" -D "$subscription_info_header_tmpfile" "$subscription_info_url" > /dev/null 2>&1; then
|
||||
if [ ! -f "$subscription_info_file" ] && grep -q -i "subscription-userinfo:" "$subscription_info_header_tmpfile"; then
|
||||
subscription_info_file="$subscription_info_header_tmpfile"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
# download subscription
|
||||
log "Profile" "Download subscription."
|
||||
if curl -s -f -m 120 --connect-timeout 15 --retry 3 -L -X GET -A "$subscription_user_agent" -D "$subscription_header_tmpfile" -o "$subscription_tmpfile" "$subscription_url" > /dev/null 2>&1; then
|
||||
log "Profile" "Subscription download successful."
|
||||
if [ ! -f "$subscription_info_file" ] && grep -q -i "subscription-userinfo:" "$subscription_header_tmpfile"; then
|
||||
subscription_info_file="$subscription_header_tmpfile"
|
||||
fi
|
||||
log "Profile" "Subscription download successful."
|
||||
success=1
|
||||
else
|
||||
log "Profile" "Subscription download failed."
|
||||
success=0
|
||||
fi
|
||||
# check if success
|
||||
if [ "$success" = 1 ]; then
|
||||
log "Profile" "Subscription update successful."
|
||||
if [ -f "$subscription_info_file" ]; then
|
||||
local subscription_expire subscription_upload subscription_download subscription_total subscription_used subscription_avaliable
|
||||
subscription_expire=$(grep -i "subscription-userinfo: " "$subscription_info_file" | grep -i -o -E "expire=[[:digit:]]+" | cut -d '=' -f 2)
|
||||
subscription_upload=$(grep -i "subscription-userinfo: " "$subscription_info_file" | grep -i -o -E "upload=[[:digit:]]+" | cut -d '=' -f 2)
|
||||
subscription_download=$(grep -i "subscription-userinfo: " "$subscription_info_file" | grep -i -o -E "download=[[:digit:]]+" | cut -d '=' -f 2)
|
||||
subscription_total=$(grep -i "subscription-userinfo: " "$subscription_info_file" | grep -i -o -E "total=[[:digit:]]+" | cut -d '=' -f 2)
|
||||
if [ -n "$subscription_upload" ] && [ -n "$subscription_download" ]; then
|
||||
subscription_used=$((subscription_upload + subscription_download))
|
||||
if [ -n "$subscription_total" ]; then
|
||||
subscription_avaliable=$((subscription_total - subscription_used))
|
||||
fi
|
||||
fi
|
||||
# update subscription info
|
||||
if [ -n "$subscription_expire" ]; then
|
||||
uci_set "momo" "$subscription_section" "expire" "$(date "+%Y-%m-%d %H:%M:%S" -d "@$subscription_expire")"
|
||||
fi
|
||||
if [ -n "$subscription_upload" ]; then
|
||||
uci_set "momo" "$subscription_section" "upload" "$(format_filesize "$subscription_upload")"
|
||||
fi
|
||||
if [ -n "$subscription_download" ]; then
|
||||
uci_set "momo" "$subscription_section" "download" "$(format_filesize "$subscription_download")"
|
||||
fi
|
||||
if [ -n "$subscription_total" ]; then
|
||||
uci_set "momo" "$subscription_section" "total" "$(format_filesize "$subscription_total")"
|
||||
fi
|
||||
if [ -n "$subscription_used" ]; then
|
||||
uci_set "momo" "$subscription_section" "used" "$(format_filesize "$subscription_used")"
|
||||
fi
|
||||
if [ -n "$subscription_avaliable" ]; then
|
||||
uci_set "momo" "$subscription_section" "avaliable" "$(format_filesize "$subscription_avaliable")"
|
||||
fi
|
||||
fi
|
||||
uci_set "momo" "$subscription_section" "update" "$(date "+%Y-%m-%d %H:%M:%S")"
|
||||
uci_set "momo" "$subscription_section" "success" "1"
|
||||
# update subscription file
|
||||
rm -f "$subscription_info_header_tmpfile"
|
||||
rm -f "$subscription_header_tmpfile"
|
||||
mv -f "$subscription_tmpfile" "$subscription_file"
|
||||
elif [ "$success" = 0 ]; then
|
||||
log "Profile" "Subscription update failed."
|
||||
# update subscription info
|
||||
uci_set "momo" "$subscription_section" "success" "0"
|
||||
# remove tmpfile
|
||||
rm -f "$subscription_info_header_tmpfile"
|
||||
rm -f "$subscription_header_tmpfile"
|
||||
rm -f "$subscription_tmpfile"
|
||||
fi
|
||||
uci_commit "momo"
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
/etc/momo/profiles/
|
||||
/etc/momo/subscriptions/
|
||||
@@ -0,0 +1,343 @@
|
||||
#!/bin/sh
|
||||
|
||||
. "$IPKG_INSTROOT/etc/momo/scripts/include.sh"
|
||||
|
||||
enabled=`uci get momo.config.enabled`
|
||||
|
||||
if [ "$enabled" == "0" ]; then
|
||||
uci set momo.config.enabled=1
|
||||
uci commit momo
|
||||
/etc/init.d/momo restart
|
||||
fi
|
||||
|
||||
echo \
|
||||
"
|
||||
# Momo Debug Info
|
||||
## system
|
||||
\`\`\`shell
|
||||
`
|
||||
cat /etc/openwrt_release
|
||||
`
|
||||
\`\`\`
|
||||
## kernel
|
||||
\`\`\`
|
||||
`
|
||||
uname -a
|
||||
`
|
||||
\`\`\`
|
||||
## application
|
||||
\`\`\`
|
||||
`
|
||||
if [ -x "/bin/opkg" ]; then
|
||||
opkg list-installed "momo"
|
||||
opkg list-installed "luci-app-momo"
|
||||
elif [ -x "/usr/bin/apk" ]; then
|
||||
apk list -I "momo"
|
||||
apk list -I "luci-app-momo"
|
||||
fi
|
||||
`
|
||||
\`\`\`
|
||||
## config
|
||||
\`\`\`json
|
||||
`
|
||||
ucode -S -e '
|
||||
import { cursor } from "uci";
|
||||
|
||||
const uci = cursor();
|
||||
|
||||
const config = uci.get_all("momo");
|
||||
const result = {};
|
||||
|
||||
for (let section_id in config) {
|
||||
const section = config[section_id];
|
||||
const section_type = section[".type"];
|
||||
if (result[section_type] == null) {
|
||||
result[section_type] = [];
|
||||
}
|
||||
push(result[section_type], section);
|
||||
}
|
||||
for (let section_type in result) {
|
||||
for (let section in result[section_type]) {
|
||||
delete section[".anonymous"];
|
||||
delete section[".type"];
|
||||
delete section[".name"];
|
||||
delete section[".index"];
|
||||
}
|
||||
}
|
||||
if (exists(result, "subscription")) {
|
||||
for (let x in result["subscription"]) {
|
||||
if (exists(x, "url")) {
|
||||
x["url"] = "*";
|
||||
}
|
||||
}
|
||||
}
|
||||
if (exists(result, "lan_access_control")) {
|
||||
for (let x in result["lan_access_control"]) {
|
||||
if (exists(x, "ip")) {
|
||||
for (let i = 0; i < length(x["ip"]); i++) {
|
||||
x["ip"][i] = "*";
|
||||
}
|
||||
}
|
||||
if (exists(x, "ip6")) {
|
||||
for (let i = 0; i < length(x["ip6"]); i++) {
|
||||
x["ip6"][i] = "*";
|
||||
}
|
||||
}
|
||||
if (exists(x, "mac")) {
|
||||
for (let i = 0; i < length(x["mac"]); i++) {
|
||||
x["mac"][i] = "*";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
delete result["placeholder"];
|
||||
print(result);
|
||||
'
|
||||
`
|
||||
\`\`\`
|
||||
## profile
|
||||
\`\`\`json
|
||||
`
|
||||
ucode -S -e '
|
||||
import { readfile } from "fs";
|
||||
|
||||
function desensitize_inbounds(inbounds) {
|
||||
for (let x in inbounds) {
|
||||
if (exists(x, "password")) {
|
||||
x["password"] = "*";
|
||||
}
|
||||
if (exists(x, "users")) {
|
||||
x["users"] = "*";
|
||||
}
|
||||
if (exists(x, "obfs")) {
|
||||
if (exists(x["obfs"], "password")) {
|
||||
x["obfs"]["password"] = "*";
|
||||
}
|
||||
}
|
||||
if (exists(x, "tls")) {
|
||||
if (exists(x["tls"], "server_name")) {
|
||||
x["tls"]["server_name"] = "*";
|
||||
}
|
||||
if (exists(x["tls"], "certificate")) {
|
||||
x["tls"]["certificate"] = "*";
|
||||
}
|
||||
if (exists(x["tls"], "key")) {
|
||||
x["tls"]["key"] = "*";
|
||||
}
|
||||
if (exists(x["tls"], "acme")) {
|
||||
x["tls"]["acme"] = {};
|
||||
}
|
||||
if (exists(x["tls"], "reality")) {
|
||||
if (exists(x["tls"]["reality"], "private_key")) {
|
||||
x["tls"]["reality"]["private_key"] = "*";
|
||||
}
|
||||
if (exists(x["tls"]["reality"], "short_id")) {
|
||||
x["tls"]["reality"]["short_id"] = "*";
|
||||
}
|
||||
}
|
||||
}
|
||||
if (exists(x, "transport")) {
|
||||
if (exists(x["transport"], "host")) {
|
||||
x["transport"]["host"] = "*";
|
||||
}
|
||||
if (exists(x["transport"], "path")) {
|
||||
x["transport"]["path"] = "*";
|
||||
}
|
||||
if (exists(x["transport"], "server_name")) {
|
||||
x["transport"]["server_name"] = "*";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function desensitize_outbounds(outbounds) {
|
||||
for (let x in outbounds) {
|
||||
if (exists(x, "server")) {
|
||||
x["server"] = "*";
|
||||
}
|
||||
if (exists(x, "server_port")) {
|
||||
x["server_port"] = "*";
|
||||
}
|
||||
if (exists(x, "server_ports")) {
|
||||
x["server_port"] = "*";
|
||||
}
|
||||
if (exists(x, "sni")) {
|
||||
x["sni"] = "*";
|
||||
}
|
||||
if (exists(x, "ports")) {
|
||||
x["ports"] = "*";
|
||||
}
|
||||
if (exists(x, "port-range")) {
|
||||
x["port-range"] = "*";
|
||||
}
|
||||
if (exists(x, "uuid")) {
|
||||
x["uuid"] = "*";
|
||||
}
|
||||
if (exists(x, "private-key")) {
|
||||
x["private-key"] = "*";
|
||||
}
|
||||
if (exists(x, "public-key")) {
|
||||
x["public-key"] = "*";
|
||||
}
|
||||
if (exists(x, "token")) {
|
||||
x["token"] = "*";
|
||||
}
|
||||
if (exists(x, "user")) {
|
||||
x["user"] = "*";
|
||||
}
|
||||
if (exists(x, "username")) {
|
||||
x["username"] = "*";
|
||||
}
|
||||
if (exists(x, "password")) {
|
||||
x["password"] = "*";
|
||||
}
|
||||
if (exists(x, "obfs")) {
|
||||
if (exists(x["obfs"], "password")) {
|
||||
x["obfs"]["password"] = "*";
|
||||
}
|
||||
}
|
||||
if (exists(x, "tls")) {
|
||||
if (exists(x["tls"], "server_name")) {
|
||||
x["tls"]["server_name"] = "*";
|
||||
}
|
||||
if (exists(x["tls"], "certificate")) {
|
||||
x["tls"]["certificate"] = "*";
|
||||
}
|
||||
if (exists(x["tls"], "reality")) {
|
||||
if (exists(x["tls"]["reality"], "public_key")) {
|
||||
x["tls"]["reality"]["public_key"] = "*";
|
||||
}
|
||||
if (exists(x["tls"]["reality"], "short_id")) {
|
||||
x["tls"]["reality"]["short_id"] = "*";
|
||||
}
|
||||
}
|
||||
}
|
||||
if (exists(x, "transport")) {
|
||||
if (exists(x["transport"], "host")) {
|
||||
x["transport"]["host"] = "*";
|
||||
}
|
||||
if (exists(x["transport"], "path")) {
|
||||
x["transport"]["path"] = "*";
|
||||
}
|
||||
if (exists(x["transport"], "server_name")) {
|
||||
x["transport"]["server_name"] = "*";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function desensitize_endpoints(endpoints) {
|
||||
for (let x in endpoints) {
|
||||
if (exists(x, "private_key")) {
|
||||
x["private_key"] = "*";
|
||||
}
|
||||
if (exists(x, "peers")) {
|
||||
for (let y in x["peers"]) {
|
||||
if (exists(y, "address")) {
|
||||
y["address"] = "*";
|
||||
}
|
||||
if (exists(y, "port")) {
|
||||
y["port"] = "*";
|
||||
}
|
||||
if (exists(y, "public_key")) {
|
||||
y["public_key"] = "*";
|
||||
}
|
||||
if (exists(y, "pre_shared_key")) {
|
||||
y["pre_shared_key"] = "*";
|
||||
}
|
||||
}
|
||||
}
|
||||
if (exists(x, "auth_key")) {
|
||||
x["auth_key"] = "*";
|
||||
}
|
||||
if (exists(x, "control_url")) {
|
||||
x["control_url"] = "*";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function desensitize_profile() {
|
||||
const profile = json(readfile("/etc/momo/run/config.json"));
|
||||
if (exists(profile, "experimental") && exists(profile["experimental"], "clash_api") && exists(profile["experimental"]["clash_api"], "secret")) {
|
||||
profile["secret"] = "*";
|
||||
}
|
||||
if (exists(profile, "inbounds")) {
|
||||
desensitize_inbounds(profile["inbounds"]);
|
||||
}
|
||||
if (exists(profile, "outbounds")) {
|
||||
desensitize_outbounds(profile["outbounds"]);
|
||||
}
|
||||
if (exists(profile, "endpoints")) {
|
||||
desensitize_endpoints(profile["endpoints"]);
|
||||
}
|
||||
if (exists(profile, "services")) {
|
||||
profile["services"] = [];
|
||||
}
|
||||
return profile;
|
||||
}
|
||||
|
||||
print(desensitize_profile());
|
||||
'
|
||||
`
|
||||
\`\`\`
|
||||
## ip rule
|
||||
\`\`\`
|
||||
`
|
||||
ip rule list
|
||||
`
|
||||
\`\`\`
|
||||
## ip route
|
||||
\`\`\`
|
||||
TPROXY:
|
||||
`
|
||||
ip route list table "$(uci get momo.routing.tproxy_route_table)"
|
||||
`
|
||||
|
||||
TUN:
|
||||
`
|
||||
ip route list table "$(uci get momo.routing.tun_route_table)"
|
||||
`
|
||||
\`\`\`
|
||||
## ip6 rule
|
||||
\`\`\`
|
||||
`
|
||||
ip -6 rule list
|
||||
`
|
||||
\`\`\`
|
||||
## ip6 route
|
||||
\`\`\`
|
||||
TPROXY:
|
||||
`
|
||||
ip -6 route list table "$(uci get momo.routing.tproxy_route_table)"
|
||||
`
|
||||
|
||||
TUN:
|
||||
`
|
||||
ip -6 route list table "$(uci get momo.routing.tun_route_table)"
|
||||
`
|
||||
\`\`\`
|
||||
## firewall tables
|
||||
\`\`\`
|
||||
`
|
||||
nft list tables
|
||||
`
|
||||
\`\`\`
|
||||
## firewall
|
||||
\`\`\`
|
||||
`
|
||||
nft list table inet momo
|
||||
`
|
||||
\`\`\`
|
||||
## service
|
||||
\`\`\`json
|
||||
`
|
||||
/etc/init.d/momo info
|
||||
`
|
||||
\`\`\`
|
||||
"
|
||||
|
||||
if [ "$enabled" == "0" ]; then
|
||||
uci set momo.config.enabled=0
|
||||
uci commit momo
|
||||
/etc/init.d/momo restart
|
||||
fi
|
||||
@@ -0,0 +1,23 @@
|
||||
#!/bin/sh
|
||||
|
||||
. "$IPKG_INSTROOT/lib/functions.sh"
|
||||
. "$IPKG_INSTROOT/etc/momo/scripts/include.sh"
|
||||
|
||||
config_load momo
|
||||
config_get_bool enabled "config" "enabled" 0
|
||||
config_get_bool core_only "config" "core_only" 0
|
||||
config_get_bool proxy_enabled "proxy" "enabled" 0
|
||||
config_get tcp_mode "proxy" "tcp_mode"
|
||||
config_get udp_mode "proxy" "udp_mode"
|
||||
config_get tun_inbound_tag "core" "tun_inbound_tag"
|
||||
|
||||
if [ "$enabled" = 1 ] && [ "$core_only" = 0 ] && [ "$proxy_enabled" = 1 ]; then
|
||||
if [ "$tcp_mode" = "tun" ] || [ "$udp_mode" = "tun" ]; then
|
||||
tun_device=$(jsonfilter -q -i "$RUN_PROFILE_PATH" -e "@['inbounds'][*]" | jsonfilter -q -a -e "@[@['tag']='$tun_inbound_tag']" | jsonfilter -q -a -e "@[@['type']='tun']" | jsonfilter -q -e "@['interface_name']")
|
||||
nft insert rule inet fw4 input iifname "$tun_device" counter accept comment "momo"
|
||||
nft insert rule inet fw4 forward oifname "$tun_device" counter accept comment "momo"
|
||||
nft insert rule inet fw4 forward iifname "$tun_device" counter accept comment "momo"
|
||||
fi
|
||||
fi
|
||||
|
||||
exit 0
|
||||
@@ -0,0 +1,133 @@
|
||||
#!/bin/sh
|
||||
|
||||
. "$IPKG_INSTROOT/usr/share/libubox/jshn.sh"
|
||||
|
||||
# paths
|
||||
|
||||
## home
|
||||
HOME_DIR="/etc/momo"
|
||||
PROFILES_DIR="$HOME_DIR/profiles"
|
||||
SUBSCRIPTIONS_DIR="$HOME_DIR/subscriptions"
|
||||
|
||||
## run
|
||||
RUN_DIR="$HOME_DIR/run"
|
||||
RUN_PROFILE_PATH="$RUN_DIR/config.json"
|
||||
|
||||
## ucode
|
||||
UCODE_DIR="$HOME_DIR/ucode"
|
||||
INCLUDE_UC="$UCODE_DIR/include.uc"
|
||||
MIXIN_UC="$UCODE_DIR/mixin.uc"
|
||||
HIJACK_UT="$UCODE_DIR/hijack.ut"
|
||||
|
||||
## scripts
|
||||
SH_DIR="$HOME_DIR/scripts"
|
||||
INCLUDE_SH="$SH_DIR/include.sh"
|
||||
FIREWALL_INCLUDE_SH="$SH_DIR/firewall_include.sh"
|
||||
DEBUG_SH="$SH_DIR/debug.sh"
|
||||
|
||||
## firewall
|
||||
NFT_DIR="$HOME_DIR/firewall"
|
||||
GEOIP_CN_NFT="$NFT_DIR/geoip_cn.nft"
|
||||
GEOIP6_CN_NFT="$NFT_DIR/geoip6_cn.nft"
|
||||
|
||||
## log
|
||||
LOG_DIR="/var/log/momo"
|
||||
APP_LOG_PATH="$LOG_DIR/app.log"
|
||||
CORE_LOG_PATH="$LOG_DIR/core.log"
|
||||
DEBUG_LOG_PATH="$LOG_DIR/debug.log"
|
||||
|
||||
## temp
|
||||
TEMP_DIR="/var/run/momo"
|
||||
PID_FILE_PATH="$TEMP_DIR/momo.pid"
|
||||
STARTED_FLAG_PATH="$TEMP_DIR/started.flag"
|
||||
BRIDGE_NF_CALL_IPTABLES_FLAG_PATH="$TEMP_DIR/bridge_nf_call_iptables.flag"
|
||||
BRIDGE_NF_CALL_IP6TABLES_FLAG_PATH="$TEMP_DIR/bridge_nf_call_ip6tables.flag"
|
||||
|
||||
# functions
|
||||
get_paths() {
|
||||
json_init
|
||||
|
||||
json_add_string home_dir "$HOME_DIR"
|
||||
json_add_string profiles_dir "$PROFILES_DIR"
|
||||
json_add_string subscriptions_dir "$SUBSCRIPTIONS_DIR"
|
||||
|
||||
json_add_string run_dir "$RUN_DIR"
|
||||
json_add_string run_profile_path "$RUN_PROFILE_PATH"
|
||||
|
||||
json_add_string ucode_dir "$UCODE_DIR"
|
||||
json_add_string include_uc "$INCLUDE_UC"
|
||||
json_add_string hijack_ut "$HIJACK_UT"
|
||||
|
||||
json_add_string sh_dir "$SH_DIR"
|
||||
json_add_string include_sh "$INCLUDE_SH"
|
||||
json_add_string firewall_include_sh "$FIREWALL_INCLUDE_SH"
|
||||
json_add_string debug_sh "$DEBUG_SH"
|
||||
|
||||
json_add_string nft_dir "$NFT_DIR"
|
||||
json_add_string geoip_cn_nft "$GEOIP_CN_NFT"
|
||||
json_add_string geoip6_cn_nft "$GEOIP6_CN_NFT"
|
||||
|
||||
json_add_string log_dir "$LOG_DIR"
|
||||
json_add_string app_log_path "$APP_LOG_PATH"
|
||||
json_add_string core_log_path "$CORE_LOG_PATH"
|
||||
json_add_string debug_log_path "$DEBUG_LOG_PATH"
|
||||
|
||||
json_add_string temp_dir "$TEMP_DIR"
|
||||
json_add_string pid_file_path "$PID_FILE_PATH"
|
||||
json_add_string started_flag_path "$STARTED_FLAG_PATH"
|
||||
json_add_string bridge_nf_call_iptables_flag_path "$BRIDGE_NF_CALL_IPTABLES_FLAG_PATH"
|
||||
json_add_string bridge_nf_call_ip6tables_flag_path "$BRIDGE_NF_CALL_IP6TABLES_FLAG_PATH"
|
||||
|
||||
json_dump
|
||||
|
||||
json_cleanup
|
||||
}
|
||||
|
||||
format_filesize() {
|
||||
local b; b=1
|
||||
local kb; kb=$((b * 1024))
|
||||
local mb; mb=$((kb * 1024))
|
||||
local gb; gb=$((mb * 1024))
|
||||
local tb; tb=$((gb * 1024))
|
||||
local pb; pb=$((tb * 1024))
|
||||
local size; size=$1
|
||||
if [ -n "$size" ]; then
|
||||
if [ "$size" -lt "$kb" ]; then
|
||||
echo "$(awk "BEGIN {print $size / $b}") B"
|
||||
elif [ "$size" -lt "$mb" ]; then
|
||||
echo "$(awk "BEGIN {print $size / $kb}") KB"
|
||||
elif [ "$size" -lt "$gb" ]; then
|
||||
echo "$(awk "BEGIN {print $size / $mb}") MB"
|
||||
elif [ "$size" -lt "$tb" ]; then
|
||||
echo "$(awk "BEGIN {print $size / $gb}") GB"
|
||||
elif [ "$size" -lt "$pb" ]; then
|
||||
echo "$(awk "BEGIN {print $size / $tb}") TB"
|
||||
else
|
||||
echo "$(awk "BEGIN {print $size / $pb}") PB"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
prepare_files() {
|
||||
if [ ! -d "$LOG_DIR" ]; then
|
||||
mkdir -p "$LOG_DIR"
|
||||
fi
|
||||
if [ ! -f "$APP_LOG_PATH" ]; then
|
||||
touch "$APP_LOG_PATH"
|
||||
fi
|
||||
if [ ! -f "$CORE_LOG_PATH" ]; then
|
||||
touch "$CORE_LOG_PATH"
|
||||
fi
|
||||
if [ ! -d "$TEMP_DIR" ]; then
|
||||
mkdir -p "$TEMP_DIR"
|
||||
fi
|
||||
}
|
||||
|
||||
clear_log() {
|
||||
echo -n > "$APP_LOG_PATH"
|
||||
echo -n > "$CORE_LOG_PATH"
|
||||
}
|
||||
|
||||
log() {
|
||||
echo "[$(date "+%Y-%m-%d %H:%M:%S")] [$1] $2" >> "$APP_LOG_PATH"
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
#!/bin/sh
|
||||
|
||||
. "$IPKG_INSTROOT/etc/momo/scripts/include.sh"
|
||||
|
||||
uci -q batch <<-EOF > /dev/null
|
||||
del firewall.momo
|
||||
set firewall.momo=include
|
||||
set firewall.momo.type=script
|
||||
set firewall.momo.path=$FIREWALL_INCLUDE_SH
|
||||
set firewall.momo.fw4_compatible=1
|
||||
commit firewall
|
||||
EOF
|
||||
@@ -0,0 +1,28 @@
|
||||
#!/bin/sh
|
||||
|
||||
. "$IPKG_INSTROOT/etc/momo/scripts/include.sh"
|
||||
|
||||
# since v1.0.2
|
||||
|
||||
section_placeholder=$(uci -q get momo.placeholder); [ -z "$section_placeholder" ] && uci set momo.placeholder="placeholder"
|
||||
|
||||
# since v1.1.0
|
||||
|
||||
proxy_bypass_china_mainland_ip=$(uci -q get momo.proxy.bypass_china_mainland_ip)
|
||||
proxy_bypass_china_mainland_ip6=$(uci -q get momo.proxy.bypass_china_mainland_ip6)
|
||||
[ -z "$proxy_bypass_china_mainland_ip6" ] && uci set momo.proxy.bypass_china_mainland_ip6=$proxy_bypass_china_mainland_ip
|
||||
|
||||
routing_tproxy_fw_mask=$(uci -q get momo.routing.tproxy_fw_mask); [ -z "$routing_tproxy_fw_mask" ] && uci set momo.routing.tproxy_fw_mask=0xFF
|
||||
routing_tun_fw_mask=$(uci -q get momo.routing.tun_fw_mask); [ -z "$routing_tun_fw_mask" ] && uci set momo.routing.tun_fw_mask=0xFF
|
||||
|
||||
procd=$(uci -q get momo.procd); [ -z "$procd" ] && {
|
||||
uci set momo.procd=procd
|
||||
uci set momo.procd.fast_reload=$(uci -q get momo.config.fast_reload)
|
||||
uci del momo.config.fast_reload
|
||||
}
|
||||
|
||||
# commit
|
||||
uci commit momo
|
||||
|
||||
# exit with 0
|
||||
exit 0
|
||||
@@ -0,0 +1,618 @@
|
||||
#!/usr/bin/utpl
|
||||
|
||||
{%-
|
||||
'use strict';
|
||||
|
||||
import { readfile } from 'fs';
|
||||
import { cursor } from 'uci';
|
||||
import { connect } from 'ubus';
|
||||
import { get_paths, uci_bool, uci_array, get_cgroups_version, get_users, get_groups, get_cgroups } from '/etc/momo/ucode/include.uc';
|
||||
|
||||
const fw4 = require('fw4');
|
||||
|
||||
const paths = get_paths();
|
||||
|
||||
const cgroups_version = get_cgroups_version();
|
||||
|
||||
const users = get_users();
|
||||
const groups = get_groups();
|
||||
const cgroups = get_cgroups();
|
||||
|
||||
const uci = cursor();
|
||||
const ubus = connect();
|
||||
|
||||
const profile = json(readfile(paths.run_profile_path));
|
||||
|
||||
uci.load('momo');
|
||||
|
||||
let redir_port;
|
||||
let tproxy_port;
|
||||
let tun_device;
|
||||
let dns_port;
|
||||
let fake_ip_range;
|
||||
let fake_ip6_range;
|
||||
|
||||
const redirect_inbound_tag = uci.get('momo', 'core', 'redirect_inbound_tag');
|
||||
const tproxy_inbound_tag = uci.get('momo', 'core', 'tproxy_inbound_tag');
|
||||
const tun_inbound_tag = uci.get('momo', 'core', 'tun_inbound_tag');
|
||||
const dns_inbound_tag = uci.get('momo', 'core', 'dns_inbound_tag');
|
||||
const fake_ip_dns_server_tag = uci.get('momo', 'core', 'fake_ip_dns_server_tag');
|
||||
|
||||
let profile_array;
|
||||
profile_array = filter(profile['inbounds'], (x) => x['tag'] == redirect_inbound_tag);
|
||||
if (length(profile_array) > 0) {
|
||||
redir_port = profile_array[0]['listen_port'];
|
||||
}
|
||||
profile_array = filter(profile['inbounds'], (x) => x['tag'] == tproxy_inbound_tag);
|
||||
if (length(profile_array) > 0) {
|
||||
tproxy_port = profile_array[0]['listen_port'];
|
||||
}
|
||||
profile_array = filter(profile['inbounds'], (x) => x['tag'] == tun_inbound_tag);
|
||||
if (length(profile_array) > 0) {
|
||||
tun_device = profile_array[0]['interface_name'];
|
||||
}
|
||||
profile_array = filter(profile['inbounds'], (x) => x['tag'] == dns_inbound_tag);
|
||||
if (length(profile_array) > 0) {
|
||||
dns_port = profile_array[0]['listen_port'];
|
||||
}
|
||||
profile_array = filter(profile['dns']['servers'], (x) => x['tag'] == fake_ip_dns_server_tag);
|
||||
if (length(profile_array) > 0) {
|
||||
fake_ip_range = profile_array[0]['inet4_range'];
|
||||
fake_ip6_range = profile_array[0]['inet6_range'];
|
||||
}
|
||||
|
||||
const ipv4_dns_hijack = uci_bool(uci.get('momo', 'proxy', 'ipv4_dns_hijack'));
|
||||
const ipv6_dns_hijack = uci_bool(uci.get('momo', 'proxy', 'ipv6_dns_hijack'));
|
||||
const ipv4_proxy = uci_bool(uci.get('momo', 'proxy', 'ipv4_proxy'));
|
||||
const ipv6_proxy = uci_bool(uci.get('momo', 'proxy', 'ipv6_proxy'));
|
||||
const fake_ip_ping_hijack = uci_bool(uci.get('momo', 'proxy', 'fake_ip_ping_hijack'));
|
||||
const tcp_mode = uci.get('momo', 'proxy', 'tcp_mode');
|
||||
const udp_mode = uci.get('momo', 'proxy', 'udp_mode');
|
||||
|
||||
const router_proxy = uci_bool(uci.get('momo', 'proxy', 'router_proxy'));
|
||||
const router_access_control = [];
|
||||
uci.foreach('momo', 'router_access_control', (access_control) => {
|
||||
access_control['enabled'] = uci_bool(access_control['enabled']);
|
||||
access_control['user'] = filter(uci_array(access_control['user']), (x) => index(users, x) >= 0);
|
||||
access_control['group'] = filter(uci_array(access_control['group']), (x) => index(groups, x) >= 0);
|
||||
access_control['cgroup'] = filter(uci_array(access_control['cgroup']), (x) => index(cgroups, x) >= 0);
|
||||
access_control['proxy'] = uci_bool(access_control['proxy']);
|
||||
access_control['dns'] = uci_bool(access_control['dns']);
|
||||
push(router_access_control, access_control);
|
||||
});
|
||||
|
||||
const lan_proxy = uci_bool(uci.get('momo', 'proxy', 'lan_proxy'));
|
||||
const lan_inbound_interface = uci_array(uci.get('momo', 'proxy', 'lan_inbound_interface'));
|
||||
const lan_inbound_device = [];
|
||||
for (let interface in lan_inbound_interface) {
|
||||
const status = ubus.call('network.interface', 'status', { 'interface': interface });
|
||||
const device = status?.l3_device ?? status?.device ?? '';
|
||||
if (device != '') {
|
||||
push(lan_inbound_device, device);
|
||||
}
|
||||
}
|
||||
const lan_access_control = [];
|
||||
uci.foreach('momo', 'lan_access_control', (access_control) => {
|
||||
access_control['enabled'] = uci_bool(access_control['enabled']);
|
||||
access_control['ip'] = uci_array(access_control['ip']);
|
||||
access_control['ip6'] = uci_array(access_control['ip6']);
|
||||
access_control['mac'] = uci_array(access_control['mac']);
|
||||
access_control['proxy'] = uci_bool(access_control['proxy']);
|
||||
access_control['dns'] = uci_bool(access_control['dns']);
|
||||
push(lan_access_control, access_control);
|
||||
});
|
||||
|
||||
const reserved_ip = uci_array(uci.get('momo', 'proxy', 'reserved_ip'));
|
||||
const reserved_ip6 = uci_array(uci.get('momo', 'proxy', 'reserved_ip6'));
|
||||
const bypass_china_mainland_ip = uci_bool(uci.get('momo', 'proxy', 'bypass_china_mainland_ip'));
|
||||
const bypass_china_mainland_ip6 = uci_bool(uci.get('momo', 'proxy', 'bypass_china_mainland_ip6'));
|
||||
const proxy_tcp_dport = split((uci.get('momo', 'proxy', 'proxy_tcp_dport') ?? '0-65535'), ' ');
|
||||
const proxy_udp_dport = split((uci.get('momo', 'proxy', 'proxy_udp_dport') ?? '0-65535'), ' ');
|
||||
const bypass_dscp = uci_array(uci.get('momo', 'proxy', 'bypass_dscp'));
|
||||
const bypass_fwmark = [];
|
||||
for (let fwmark in uci_array(uci.get('momo', 'proxy', 'bypass_fwmark'))) {
|
||||
let mark;
|
||||
let mask = '0xFFFFFFFF';
|
||||
if (index(fwmark, '/') >= 0) {
|
||||
const parts = split(fwmark, '/');
|
||||
mark = parts[0];
|
||||
mask = parts[1];
|
||||
} else {
|
||||
mark = fwmark;
|
||||
}
|
||||
push(bypass_fwmark, { mark: mark, mask: mask });
|
||||
}
|
||||
|
||||
const cgroup_id = uci.get('momo', 'routing', 'cgroup_id') ?? '0x07250725';
|
||||
const cgroup_name = uci.get('momo', 'routing', 'cgroup_name') ?? 'momo';
|
||||
const tproxy_fw_mark = uci.get('momo', 'routing', 'tproxy_fw_mark') ?? '0x80';
|
||||
const tproxy_fw_mask = uci.get('momo', 'routing', 'tproxy_fw_mask') ?? '0xFF';
|
||||
const tproxy_fw_umask = fw4.hex(~tproxy_fw_mask & 0xFFFFFFFF);
|
||||
const tun_fw_mark = uci.get('momo', 'routing', 'tun_fw_mark') ?? '0x81';
|
||||
const tun_fw_mask = uci.get('momo', 'routing', 'tun_fw_mask') ?? '0xFF';
|
||||
const tun_fw_umask = fw4.hex(~tun_fw_mask & 0xFFFFFFFF);
|
||||
|
||||
const dns_hijack_nfproto = [];
|
||||
if (ipv4_dns_hijack) {
|
||||
push(dns_hijack_nfproto, 'ipv4');
|
||||
}
|
||||
if (ipv6_dns_hijack) {
|
||||
push(dns_hijack_nfproto, 'ipv6');
|
||||
}
|
||||
|
||||
const proxy_nfproto = [];
|
||||
if (ipv4_proxy) {
|
||||
push(proxy_nfproto, 'ipv4');
|
||||
}
|
||||
if (ipv6_proxy) {
|
||||
push(proxy_nfproto, 'ipv6');
|
||||
}
|
||||
|
||||
const proxy_dport = [];
|
||||
for (let port in proxy_tcp_dport) {
|
||||
push(proxy_dport, `tcp . ${port}`);
|
||||
}
|
||||
for (let port in proxy_udp_dport) {
|
||||
push(proxy_dport, `udp . ${port}`);
|
||||
}
|
||||
-%}
|
||||
|
||||
table inet momo {
|
||||
{% if (length(dns_hijack_nfproto) > 0): %}
|
||||
set dns_hijack_nfproto {
|
||||
type nf_proto
|
||||
flags interval
|
||||
elements = {
|
||||
{{ join(', ', dns_hijack_nfproto) }}
|
||||
}
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
set proxy_nfproto {
|
||||
type nf_proto
|
||||
flags interval
|
||||
{% if (length(proxy_nfproto) > 0): %}
|
||||
elements = {
|
||||
{{ join(', ', proxy_nfproto) }}
|
||||
}
|
||||
{% endif %}
|
||||
}
|
||||
|
||||
set reserved_ip {
|
||||
type ipv4_addr
|
||||
flags interval
|
||||
auto-merge
|
||||
{% if (length(reserved_ip) > 0): %}
|
||||
elements = {
|
||||
{{ join(', ', reserved_ip) }}
|
||||
}
|
||||
{% endif %}
|
||||
}
|
||||
|
||||
set reserved_ip6 {
|
||||
type ipv6_addr
|
||||
flags interval
|
||||
auto-merge
|
||||
{% if (length(reserved_ip6) > 0): %}
|
||||
elements = {
|
||||
{{ join(', ', reserved_ip6) }}
|
||||
}
|
||||
{% endif %}
|
||||
}
|
||||
|
||||
set lan_inbound_device {
|
||||
type ifname
|
||||
flags interval
|
||||
auto-merge
|
||||
{% if (length(lan_inbound_device) > 0): %}
|
||||
elements = {
|
||||
{{ join(', ', map(lan_inbound_device, (x) => `"${x}"`)) }}
|
||||
}
|
||||
{% endif %}
|
||||
}
|
||||
|
||||
set china_ip {
|
||||
type ipv4_addr
|
||||
flags interval
|
||||
}
|
||||
|
||||
set china_ip6 {
|
||||
type ipv6_addr
|
||||
flags interval
|
||||
}
|
||||
|
||||
set proxy_dport {
|
||||
type inet_proto . inet_service
|
||||
flags interval
|
||||
auto-merge
|
||||
{% if (length(proxy_dport) > 0): %}
|
||||
elements = {
|
||||
{{ join(', ', proxy_dport) }}
|
||||
}
|
||||
{% endif %}
|
||||
}
|
||||
|
||||
set bypass_dscp {
|
||||
type dscp
|
||||
flags interval
|
||||
auto-merge
|
||||
{% if (length(bypass_dscp) > 0): %}
|
||||
elements = {
|
||||
{{ join(', ', bypass_dscp) }}
|
||||
}
|
||||
{% endif %}
|
||||
}
|
||||
|
||||
{% if (router_proxy): %}
|
||||
{% if (length(dns_hijack_nfproto) > 0): %}
|
||||
chain router_dns_hijack {
|
||||
{% for (let access_control in router_access_control): %}
|
||||
{% if (access_control['enabled']): %}
|
||||
{% if (length(access_control['user']) == 0 && length(access_control['group']) == 0 && length(access_control['cgroup']) == 0): %}
|
||||
meta l4proto { tcp, udp } th dport 53 counter {% if (access_control.dns == '1'): %} redirect to :{{ dns_port }} {% else %} return {% endif %}
|
||||
|
||||
{% else %}
|
||||
{% if (length(access_control['user']) > 0): %}
|
||||
meta l4proto { tcp, udp } meta skuid { {{ join(', ', access_control['user']) }} } th dport 53 counter {% if (access_control.dns == '1'): %} redirect to :{{ dns_port }} {% else %} return {% endif %}
|
||||
|
||||
{% endif %}
|
||||
{% if (length(access_control['group']) > 0): %}
|
||||
meta l4proto { tcp, udp } meta skgid { {{ join(', ', access_control['group']) }} } th dport 53 counter {% if (access_control.dns == '1'): %} redirect to :{{ dns_port }} {% else %} return {% endif %}
|
||||
|
||||
{% endif %}
|
||||
{% if (cgroups_version == 2 && length(access_control['cgroup']) > 0): %}
|
||||
{% for (let cgroup in access_control['cgroup']): %}
|
||||
meta l4proto { tcp, udp } socket cgroupv2 level {{ length(split(cgroup, '/')) }} "{{ cgroup }}" th dport 53 counter {% if (access_control.dns == '1'): %} redirect to :{{ dns_port }} {% else %} return {% endif %}
|
||||
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
{% if (tcp_mode == 'redirect'): %}
|
||||
chain router_redirect {
|
||||
{% for (let access_control in router_access_control): %}
|
||||
{% if (access_control['enabled']): %}
|
||||
{% if (length(access_control['user']) == 0 && length(access_control['group']) == 0 && length(access_control['cgroup']) == 0): %}
|
||||
meta l4proto tcp counter {% if (access_control.proxy == '1'): %} redirect to :{{ redir_port }} {% else %} return {% endif %}
|
||||
|
||||
{% else %}
|
||||
{% if (length(access_control['user']) > 0): %}
|
||||
meta l4proto tcp meta skuid { {{ join(', ', access_control['user']) }} } counter {% if (access_control.proxy == '1'): %} redirect to :{{ redir_port }} {% else %} return {% endif %}
|
||||
|
||||
{% endif %}
|
||||
{% if (length(access_control['group']) > 0): %}
|
||||
meta l4proto tcp meta skgid { {{ join(', ', access_control['group']) }} } counter {% if (access_control.proxy == '1'): %} redirect to :{{ redir_port }} {% else %} return {% endif %}
|
||||
|
||||
{% endif %}
|
||||
{% if (cgroups_version == 2 && length(access_control['cgroup']) > 0): %}
|
||||
{% for (let cgroup in access_control['cgroup']): %}
|
||||
meta l4proto tcp socket cgroupv2 level {{ length(split(cgroup, '/')) }} "{{ cgroup }}" counter {% if (access_control.proxy == '1'): %} redirect to :{{ redir_port }} {% else %} return {% endif %}
|
||||
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
{% if (tcp_mode == 'tproxy' || udp_mode == 'tproxy'): %}
|
||||
chain router_tproxy {
|
||||
{% for (let access_control in router_access_control): %}
|
||||
{% if (access_control['enabled']): %}
|
||||
{% if (length(access_control['user']) == 0 && length(access_control['group']) == 0 && length(access_control['cgroup']) == 0): %}
|
||||
meta l4proto { tcp, udp } {% if (access_control.proxy == '1'): %} meta mark set meta mark & {{ tproxy_fw_umask }} | {{ tproxy_fw_mark }} counter accept {% else %} counter return {% endif %}
|
||||
|
||||
{% else %}
|
||||
{% if (length(access_control['user']) > 0): %}
|
||||
meta l4proto { tcp, udp } meta skuid { {{ join(', ', access_control['user']) }} } {% if (access_control.proxy == '1'): %} meta mark set meta mark & {{ tproxy_fw_umask }} | {{ tproxy_fw_mark }} counter accept {% else %} counter return {% endif %}
|
||||
|
||||
{% endif %}
|
||||
{% if (length(access_control['group']) > 0): %}
|
||||
meta l4proto { tcp, udp } meta skgid { {{ join(', ', access_control['group']) }} } {% if (access_control.proxy == '1'): %} meta mark set meta mark & {{ tproxy_fw_umask }} | {{ tproxy_fw_mark }} counter accept {% else %} counter return {% endif %}
|
||||
|
||||
{% endif %}
|
||||
{% if (cgroups_version == 2 && length(access_control['cgroup']) > 0): %}
|
||||
{% for (let cgroup in access_control['cgroup']): %}
|
||||
meta l4proto { tcp, udp } socket cgroupv2 level {{ length(split(cgroup, '/')) }} "{{ cgroup }}" {% if (access_control.proxy == '1'): %} meta mark set meta mark & {{ tproxy_fw_umask }} | {{ tproxy_fw_mark }} counter accept {% else %} counter return {% endif %}
|
||||
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
{% if (tcp_mode == 'tun' || udp_mode == 'tun'): %}
|
||||
chain router_tun {
|
||||
{% for (let access_control in router_access_control): %}
|
||||
{% if (access_control['enabled']): %}
|
||||
{% if (length(access_control['user']) == 0 && length(access_control['group']) == 0 && length(access_control['cgroup']) == 0): %}
|
||||
meta l4proto { tcp, udp } {% if (access_control.proxy == '1'): %} meta mark set meta mark & {{ tun_fw_umask }} | {{ tun_fw_mark }} counter accept {% else %} counter return {% endif %}
|
||||
|
||||
{% else %}
|
||||
{% if (length(access_control['user']) > 0): %}
|
||||
meta l4proto { tcp, udp } meta skuid { {{ join(', ', access_control['user']) }} } {% if (access_control.proxy == '1'): %} meta mark set meta mark & {{ tun_fw_umask }} | {{ tun_fw_mark }} counter accept {% else %} counter return {% endif %}
|
||||
|
||||
{% endif %}
|
||||
{% if (length(access_control['group']) > 0): %}
|
||||
meta l4proto { tcp, udp } meta skgid { {{ join(', ', access_control['group']) }} } {% if (access_control.proxy == '1'): %} meta mark set meta mark & {{ tun_fw_umask }} | {{ tun_fw_mark }} counter accept {% else %} counter return {% endif %}
|
||||
|
||||
{% endif %}
|
||||
{% if (cgroups_version == 2 && length(access_control['cgroup']) > 0): %}
|
||||
{% for (let cgroup in access_control['cgroup']): %}
|
||||
meta l4proto { tcp, udp } socket cgroupv2 level {{ length(split(cgroup, '/')) }} "{{ cgroup }}" {% if (access_control.proxy == '1'): %} meta mark set meta mark & {{ tun_fw_umask }} | {{ tun_fw_mark }} counter accept {% else %} counter return {% endif %}
|
||||
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% if (lan_proxy): %}
|
||||
{% if (length(dns_hijack_nfproto) > 0): %}
|
||||
chain lan_dns_hijack {
|
||||
{% for (let access_control in lan_access_control): %}
|
||||
{% if (access_control['enabled']): %}
|
||||
{% if (length(access_control['ip']) == 0 && length(access_control['ip6']) == 0 && length(access_control['mac']) == 0): %}
|
||||
meta l4proto { tcp, udp } th dport 53 counter {% if (access_control.dns == '1'): %} redirect to :{{ dns_port }} {% else %} return {% endif %}
|
||||
|
||||
{% else %}
|
||||
{% if (length(access_control['ip']) > 0): %}
|
||||
meta l4proto { tcp, udp } ip saddr { {{ join(', ', access_control['ip']) }} } th dport 53 counter {% if (access_control.dns == '1'): %} redirect to :{{ dns_port }} {% else %} return {% endif %}
|
||||
|
||||
{% endif %}
|
||||
{% if (length(access_control['ip6']) > 0): %}
|
||||
meta l4proto { tcp, udp } ip6 saddr { {{ join(', ', access_control['ip6']) }} } th dport 53 counter {% if (access_control.dns == '1'): %} redirect to :{{ dns_port }} {% else %} return {% endif %}
|
||||
|
||||
{% endif %}
|
||||
{% if (length(access_control['mac']) > 0): %}
|
||||
meta l4proto { tcp, udp } ether saddr { {{ join(', ', access_control['mac']) }} } th dport 53 counter {% if (access_control.dns == '1'): %} redirect to :{{ dns_port }} {% else %} return {% endif %}
|
||||
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
{% if (tcp_mode == 'redirect'): %}
|
||||
chain lan_redirect {
|
||||
{% for (let access_control in lan_access_control): %}
|
||||
{% if (access_control['enabled']): %}
|
||||
{% if (length(access_control['ip']) == 0 && length(access_control['ip6']) == 0 && length(access_control['mac']) == 0): %}
|
||||
meta l4proto tcp counter {% if (access_control.proxy == '1'): %} redirect to :{{ redir_port }} {% else %} counter return {% endif %}
|
||||
|
||||
{% else %}
|
||||
{% if (length(access_control['ip']) > 0): %}
|
||||
meta l4proto tcp ip saddr { {{ join(', ', access_control['ip']) }} } counter {% if (access_control.proxy == '1'): %} redirect to :{{ redir_port }} {% else %} return {% endif %}
|
||||
|
||||
{% endif %}
|
||||
{% if (length(access_control['ip6']) > 0): %}
|
||||
meta l4proto tcp ip6 saddr { {{ join(', ', access_control['ip6']) }} } counter {% if (access_control.proxy == '1'): %} redirect to :{{ redir_port }} {% else %} return {% endif %}
|
||||
|
||||
{% endif %}
|
||||
{% if (length(access_control['mac']) > 0): %}
|
||||
meta l4proto tcp ether saddr { {{ join(', ', access_control['mac']) }} } counter {% if (access_control.proxy == '1'): %} redirect to :{{ redir_port }} {% else %} return {% endif %}
|
||||
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
{% if (tcp_mode == 'tproxy' || udp_mode == 'tproxy'): %}
|
||||
chain lan_tproxy {
|
||||
{% for (let access_control in lan_access_control): %}
|
||||
{% if (access_control['enabled']): %}
|
||||
{% if (length(access_control['ip']) == 0 && length(access_control['ip6']) == 0 && length(access_control['mac']) == 0): %}
|
||||
meta l4proto { tcp, udp } {% if (access_control.proxy == '1'): %} meta mark set meta mark & {{ tproxy_fw_umask }} | {{ tproxy_fw_mark }} tproxy to :{{ tproxy_port }} counter accept {% else %} counter return {% endif %}
|
||||
|
||||
{% else %}
|
||||
{% if (length(access_control['ip']) > 0): %}
|
||||
meta l4proto { tcp, udp } ip saddr { {{ join(', ', access_control['ip']) }} } {% if (access_control.proxy == '1'): %} meta mark set meta mark & {{ tproxy_fw_umask }} | {{ tproxy_fw_mark }} tproxy ip to :{{ tproxy_port }} counter accept {% else %} counter return {% endif %}
|
||||
|
||||
{% endif %}
|
||||
{% if (length(access_control['ip6']) > 0): %}
|
||||
meta l4proto { tcp, udp } ip6 saddr { {{ join(', ', access_control['ip6']) }} } {% if (access_control.proxy == '1'): %} meta mark set meta mark & {{ tproxy_fw_umask }} | {{ tproxy_fw_mark }} tproxy ip6 to :{{ tproxy_port }} counter accept {% else %} counter return {% endif %}
|
||||
|
||||
{% endif %}
|
||||
{% if (length(access_control['mac']) > 0): %}
|
||||
meta l4proto { tcp, udp } ether saddr { {{ join(', ', access_control['mac']) }} } {% if (access_control.proxy == '1'): %} meta mark set meta mark & {{ tproxy_fw_umask }} | {{ tproxy_fw_mark }} tproxy to :{{ tproxy_port }} counter accept {% else %} counter return {% endif %}
|
||||
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
{% if (tcp_mode == 'tun' || udp_mode == 'tun'): %}
|
||||
chain lan_tun {
|
||||
{% for (let access_control in lan_access_control): %}
|
||||
{% if (access_control['enabled']): %}
|
||||
{% if (length(access_control['ip']) == 0 && length(access_control['ip6']) == 0 && length(access_control['mac']) == 0): %}
|
||||
meta l4proto { tcp, udp } {% if (access_control.proxy == '1'): %} meta mark set meta mark & {{ tun_fw_umask }} | {{ tun_fw_mark }} counter accept {% else %}counter return {% endif %}
|
||||
|
||||
{% else %}
|
||||
{% if (length(access_control['ip']) > 0): %}
|
||||
meta l4proto { tcp, udp } ip saddr { {{ join(', ', access_control['ip']) }} } {% if (access_control.proxy == '1'): %} meta mark set meta mark & {{ tun_fw_umask }} | {{ tun_fw_mark }} counter accept {% else %}counter return {% endif %}
|
||||
|
||||
{% endif %}
|
||||
{% if (length(access_control['ip6']) > 0): %}
|
||||
meta l4proto { tcp, udp } ip6 saddr { {{ join(', ', access_control['ip6']) }} } {% if (access_control.proxy == '1'): %} meta mark set meta mark & {{ tun_fw_umask }} | {{ tun_fw_mark }} counter accept {% else %}counter return {% endif %}
|
||||
|
||||
{% endif %}
|
||||
{% if (length(access_control['mac']) > 0): %}
|
||||
meta l4proto { tcp, udp } ether saddr { {{ join(', ', access_control['mac']) }} } {% if (access_control.proxy == '1'): %} meta mark set meta mark & {{ tun_fw_umask }} | {{ tun_fw_mark }} counter accept {% else %}counter return {% endif %}
|
||||
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% if (router_proxy): %}
|
||||
chain nat_output {
|
||||
type nat hook output priority filter; policy accept;
|
||||
{% if (cgroups_version == 1): %}
|
||||
meta cgroup {{ cgroup_id }} counter return
|
||||
{% elif (cgroups_version == 2): %}
|
||||
socket cgroupv2 level 2 "services/{{ cgroup_name }}" counter return
|
||||
{% endif %}
|
||||
{% if (length(dns_hijack_nfproto) > 0): %}
|
||||
meta nfproto @dns_hijack_nfproto jump router_dns_hijack
|
||||
{% endif %}
|
||||
{% if (tcp_mode == 'redirect'): %}
|
||||
fib daddr type { local, broadcast, anycast, multicast } counter return
|
||||
ct direction reply counter return
|
||||
ip daddr @reserved_ip {% if (fake_ip_range): %} ip daddr != {{ fake_ip_range }} {% endif %} counter return
|
||||
ip6 daddr @reserved_ip6 {% if (fake_ip6_range): %} ip6 daddr != {{ fake_ip6_range }} {% endif %} counter return
|
||||
ip daddr @china_ip counter return
|
||||
ip6 daddr @china_ip6 counter return
|
||||
meta nfproto ipv4 meta l4proto . th dport != @proxy_dport {% if (fake_ip_range): %} ip daddr != {{ fake_ip_range }} {% endif %} counter return
|
||||
meta nfproto ipv6 meta l4proto . th dport != @proxy_dport {% if (fake_ip6_range): %} ip6 daddr != {{ fake_ip6_range }} {% endif %} counter return
|
||||
meta l4proto { tcp, udp } ip dscp @bypass_dscp {% if (fake_ip_range): %} ip daddr != {{ fake_ip_range }} {% endif %} counter return
|
||||
meta l4proto { tcp, udp } ip6 dscp @bypass_dscp {% if (fake_ip6_range): %} ip6 daddr != {{ fake_ip6_range }} {% endif %} counter return
|
||||
{% for (let fwmark in bypass_fwmark): %}
|
||||
meta mark & {{ fwmark.mask }} == {{ fwmark.mark }} counter return
|
||||
{% endfor %}
|
||||
meta nfproto @proxy_nfproto jump router_redirect
|
||||
{% endif %}
|
||||
{% if (fake_ip_ping_hijack): %}
|
||||
{% if (fake_ip_range): %}
|
||||
icmp type echo-request ip daddr {{ fake_ip_range }} counter redirect
|
||||
{% endif %}
|
||||
{% if (fake_ip6_range): %}
|
||||
icmpv6 type echo-request ip6 daddr {{ fake_ip6_range }} counter redirect
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
}
|
||||
|
||||
chain mangle_output {
|
||||
type route hook output priority mangle; policy accept;
|
||||
{% if (cgroups_version == 1): %}
|
||||
meta cgroup {{ cgroup_id }} counter return
|
||||
{% elif (cgroups_version == 2): %}
|
||||
socket cgroupv2 level 2 "services/{{ cgroup_name }}" counter return
|
||||
{% endif %}
|
||||
fib daddr type { local, broadcast, anycast, multicast } counter return
|
||||
ct direction reply counter return
|
||||
ip daddr @reserved_ip {% if (fake_ip_range): %} ip daddr != {{ fake_ip_range }} {% endif %} counter return
|
||||
ip6 daddr @reserved_ip6 {% if (fake_ip6_range): %} ip6 daddr != {{ fake_ip6_range }} {% endif %} counter return
|
||||
ip daddr @china_ip counter return
|
||||
ip6 daddr @china_ip6 counter return
|
||||
meta nfproto ipv4 meta l4proto . th dport != @proxy_dport {% if (fake_ip_range): %} ip daddr != {{ fake_ip_range }} {% endif %} counter return
|
||||
meta nfproto ipv6 meta l4proto . th dport != @proxy_dport {% if (fake_ip6_range): %} ip6 daddr != {{ fake_ip6_range }} {% endif %} counter return
|
||||
meta l4proto { tcp, udp } ip dscp @bypass_dscp {% if (fake_ip_range): %} ip daddr != {{ fake_ip_range }} {% endif %} counter return
|
||||
meta l4proto { tcp, udp } ip6 dscp @bypass_dscp {% if (fake_ip6_range): %} ip6 daddr != {{ fake_ip6_range }} {% endif %} counter return
|
||||
{% for (let fwmark in bypass_fwmark): %}
|
||||
meta mark & {{ fwmark.mask }} == {{ fwmark.mark }} counter return
|
||||
{% endfor %}
|
||||
{% if (length(dns_hijack_nfproto) > 0): %}
|
||||
meta nfproto @dns_hijack_nfproto meta l4proto { tcp, udp } th dport 53 counter return
|
||||
{% endif %}
|
||||
{% if (tcp_mode == 'tproxy'): %}
|
||||
meta nfproto @proxy_nfproto meta l4proto tcp jump router_tproxy
|
||||
{% elif (tcp_mode == 'tun'): %}
|
||||
meta nfproto @proxy_nfproto meta l4proto tcp jump router_tun
|
||||
{% endif %}
|
||||
{% if (udp_mode == 'tproxy'): %}
|
||||
meta nfproto @proxy_nfproto meta l4proto udp jump router_tproxy
|
||||
{% elif (udp_mode == 'tun'): %}
|
||||
meta nfproto @proxy_nfproto meta l4proto udp jump router_tun
|
||||
{% endif %}
|
||||
}
|
||||
|
||||
chain mangle_prerouting_router {
|
||||
type filter hook prerouting priority mangle - 1; policy accept;
|
||||
{% if (tcp_mode == 'tproxy' || udp_mode == 'tproxy'): %}
|
||||
iifname lo meta l4proto { tcp, udp } meta mark & {{ tproxy_fw_mask }} == {{ tproxy_fw_mark }} tproxy to :{{ tproxy_port }} counter accept
|
||||
{% endif %}
|
||||
{% if (tcp_mode == 'tun' || udp_mode == 'tun'): %}
|
||||
iifname "{{ tun_device }}" meta l4proto { icmp, tcp, udp } counter accept
|
||||
{% endif %}
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
{% if (lan_proxy): %}
|
||||
chain dstnat {
|
||||
type nat hook prerouting priority dstnat + 1; policy accept;
|
||||
{% if (length(dns_hijack_nfproto) > 0): %}
|
||||
iifname @lan_inbound_device meta nfproto @dns_hijack_nfproto jump lan_dns_hijack
|
||||
{% endif %}
|
||||
{% if (tcp_mode == 'redirect'): %}
|
||||
fib daddr type { local, broadcast, anycast, multicast } counter return
|
||||
ct direction reply counter return
|
||||
ip daddr @reserved_ip {% if (fake_ip_range): %} ip daddr != {{ fake_ip_range }} {% endif %} counter return
|
||||
ip6 daddr @reserved_ip6 {% if (fake_ip6_range): %} ip6 daddr != {{ fake_ip6_range }} {% endif %} counter return
|
||||
ip daddr @china_ip counter return
|
||||
ip6 daddr @china_ip6 counter return
|
||||
meta nfproto ipv4 meta l4proto . th dport != @proxy_dport {% if (fake_ip_range): %} ip daddr != {{ fake_ip_range }} {% endif %} counter return
|
||||
meta nfproto ipv6 meta l4proto . th dport != @proxy_dport {% if (fake_ip6_range): %} ip6 daddr != {{ fake_ip6_range }} {% endif %} counter return
|
||||
meta l4proto { tcp, udp } ip dscp @bypass_dscp {% if (fake_ip_range): %} ip daddr != {{ fake_ip_range }} {% endif %} counter return
|
||||
meta l4proto { tcp, udp } ip6 dscp @bypass_dscp {% if (fake_ip6_range): %} ip6 daddr != {{ fake_ip6_range }} {% endif %} counter return
|
||||
{% for (let fwmark in bypass_fwmark): %}
|
||||
meta mark & {{ fwmark.mask }} == {{ fwmark.mark }} counter return
|
||||
{% endfor %}
|
||||
iifname @lan_inbound_device meta nfproto @proxy_nfproto jump lan_redirect
|
||||
{% endif %}
|
||||
{% if (fake_ip_ping_hijack): %}
|
||||
{% if (fake_ip_range): %}
|
||||
icmp type echo-request ip daddr {{ fake_ip_range }} counter redirect
|
||||
{% endif %}
|
||||
{% if (fake_ip6_range): %}
|
||||
icmpv6 type echo-request ip6 daddr {{ fake_ip6_range }} counter redirect
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
}
|
||||
|
||||
chain mangle_prerouting_lan {
|
||||
type filter hook prerouting priority mangle; policy accept;
|
||||
fib daddr type { local, broadcast, anycast, multicast } counter return
|
||||
ct direction reply counter return
|
||||
ip daddr @reserved_ip {% if (fake_ip_range): %} ip daddr != {{ fake_ip_range }} {% endif %} counter return
|
||||
ip6 daddr @reserved_ip6 {% if (fake_ip6_range): %} ip6 daddr != {{ fake_ip6_range }} {% endif %} counter return
|
||||
ip daddr @china_ip counter return
|
||||
ip6 daddr @china_ip6 counter return
|
||||
meta nfproto ipv4 meta l4proto . th dport != @proxy_dport {% if (fake_ip_range): %} ip daddr != {{ fake_ip_range }} {% endif %} counter return
|
||||
meta nfproto ipv6 meta l4proto . th dport != @proxy_dport {% if (fake_ip6_range): %} ip6 daddr != {{ fake_ip6_range }} {% endif %} counter return
|
||||
meta l4proto { tcp, udp } ip dscp @bypass_dscp {% if (fake_ip_range): %} ip daddr != {{ fake_ip_range }} {% endif %} counter return
|
||||
meta l4proto { tcp, udp } ip6 dscp @bypass_dscp {% if (fake_ip6_range): %} ip6 daddr != {{ fake_ip6_range }} {% endif %} counter return
|
||||
{% for (let fwmark in bypass_fwmark): %}
|
||||
meta mark & {{ fwmark.mask }} == {{ fwmark.mark }} counter return
|
||||
{% endfor %}
|
||||
{% if (length(dns_hijack_nfproto) > 0): %}
|
||||
meta nfproto @dns_hijack_nfproto meta l4proto { tcp, udp } th dport 53 counter return
|
||||
{% endif %}
|
||||
{% if (tcp_mode == 'tproxy'): %}
|
||||
iifname @lan_inbound_device meta nfproto @proxy_nfproto meta l4proto tcp jump lan_tproxy
|
||||
{% elif (tcp_mode == 'tun'): %}
|
||||
iifname @lan_inbound_device meta nfproto @proxy_nfproto meta l4proto tcp jump lan_tun
|
||||
{% endif %}
|
||||
{% if (udp_mode == 'tproxy'): %}
|
||||
iifname @lan_inbound_device meta nfproto @proxy_nfproto meta l4proto udp jump lan_tproxy
|
||||
{% elif (udp_mode == 'tun'): %}
|
||||
iifname @lan_inbound_device meta nfproto @proxy_nfproto meta l4proto udp jump lan_tun
|
||||
{% endif %}
|
||||
}
|
||||
{% endif %}
|
||||
}
|
||||
|
||||
{% if (bypass_china_mainland_ip): %}
|
||||
include "/etc/momo/firewall/geoip_cn.nft"
|
||||
{% endif %}
|
||||
{% if (bypass_china_mainland_ip6): %}
|
||||
include "/etc/momo/firewall/geoip6_cn.nft"
|
||||
{% endif %}
|
||||
@@ -0,0 +1,123 @@
|
||||
import { readfile, popen } from 'fs';
|
||||
|
||||
export function get_paths() {
|
||||
let result = {};
|
||||
const process = popen('. /etc/momo/scripts/include.sh && get_paths');
|
||||
if (process) {
|
||||
result = json(process);
|
||||
process.close();
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
export function uci_bool(obj) {
|
||||
return obj == null ? null : obj == '1';
|
||||
};
|
||||
|
||||
export function uci_int(obj) {
|
||||
return obj == null ? null : int(obj);
|
||||
};
|
||||
|
||||
export function uci_array(obj) {
|
||||
if (obj == null) {
|
||||
return [];
|
||||
}
|
||||
if (type(obj) == 'array') {
|
||||
return uniq(obj);
|
||||
}
|
||||
return [obj];
|
||||
};
|
||||
|
||||
export function merge(target, ...sources) {
|
||||
for (let source in sources) {
|
||||
for (let key in keys(source)) {
|
||||
const target_value = target[key];
|
||||
const target_value_type = type(target_value);
|
||||
const source_value = source[key];
|
||||
const source_value_type = type(source_value);
|
||||
if (target_value_type === 'object' && source_value_type === 'object') {
|
||||
target[key] = merge(target_value, source_value);
|
||||
} else {
|
||||
target[key] = source[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
return target;
|
||||
};
|
||||
|
||||
export function merge_exists(target, ...sources) {
|
||||
for (let source in sources) {
|
||||
for (let key in keys(source)) {
|
||||
if (exists(target, key)) {
|
||||
const target_value = target[key];
|
||||
const target_value_type = type(target_value);
|
||||
const source_value = source[key];
|
||||
const source_value_type = type(source_value);
|
||||
if (target_value_type === 'object' && source_value_type === 'object') {
|
||||
target[key] = merge_exists(target_value, source_value);
|
||||
} else {
|
||||
target[key] = source[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return target;
|
||||
};
|
||||
|
||||
export function trim_all(obj) {
|
||||
if (obj == null) {
|
||||
return null;
|
||||
}
|
||||
if (type(obj) == 'string') {
|
||||
if (length(obj) == 0) {
|
||||
return null;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
if (type(obj) == 'array') {
|
||||
if (length(obj) == 0) {
|
||||
return null;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
if (type(obj) == 'object') {
|
||||
const obj_keys = keys(obj);
|
||||
for (let key in obj_keys) {
|
||||
obj[key] = trim_all(obj[key]);
|
||||
if (obj[key] == null) {
|
||||
delete obj[key];
|
||||
}
|
||||
}
|
||||
if (length(keys(obj)) == 0) {
|
||||
return null;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
return obj;
|
||||
};
|
||||
|
||||
export function get_cgroups_version() {
|
||||
return system('mount | grep -q -w "^cgroup"') == 0 ? 1 : 2;
|
||||
};
|
||||
|
||||
export function get_users() {
|
||||
return map(split(readfile('/etc/passwd'), '\n'), (x) => split(x, ':')[0]);
|
||||
};
|
||||
|
||||
export function get_groups() {
|
||||
return map(split(readfile('/etc/group'), '\n'), (x) => split(x, ':')[0]);
|
||||
};
|
||||
|
||||
export function get_cgroups() {
|
||||
const result = [];
|
||||
if (get_cgroups_version() == 2) {
|
||||
const cgroup_path = '/sys/fs/cgroup/';
|
||||
const process = popen(`find ${cgroup_path} -type d -mindepth 1`);
|
||||
if (process) {
|
||||
for (let line = process.read('line'); length(line); line = process.read('line')) {
|
||||
push(result, substr(trim(line), length(cgroup_path)));
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=nexttrace
|
||||
PKG_VERSION:=1.5.0
|
||||
PKG_RELEASE:=1
|
||||
PKG_VERSION:=1.6.1
|
||||
PKG_RELEASE:=2
|
||||
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
|
||||
PKG_SOURCE_URL:=https://codeload.github.com/nxtrace/NTrace-core/tar.gz/v$(PKG_VERSION)?
|
||||
|
||||
Reference in new issue
Block a user