update 2026-08-15 20:26:22

This commit is contained in:
action committed 2026-08-15 20:26:22 +08:00
1 parent d5fe2d005a
commit a459a74f19
32 files changed
+1991 -1606

No files matched your search

+1 -1
View File
@@ -10,7 +10,7 @@ PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_PROTO:=git PKG_SOURCE_PROTO:=git
PKG_SOURCE_VERSION:=d44529e9525a82ae3f9a42c9cb674256b943f67e PKG_SOURCE_VERSION:=1f2d6de4a6d1fc9718e7179a124aad0f70002d2d
PKG_SOURCE_URL:=https://github.com/olicesx/dae.git PKG_SOURCE_URL:=https://github.com/olicesx/dae.git
PKG_MIRROR_HASH:=skip PKG_MIRROR_HASH:=skip
+1 -1
View File
@@ -16,7 +16,7 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-amlogic PKG_NAME:=luci-app-amlogic
PKG_VERSION:=3.1.320 PKG_VERSION:=3.1.321
PKG_RELEASE:=2 PKG_RELEASE:=2
PKG_LICENSE:=GPL-2.0 License PKG_LICENSE:=GPL-2.0 License
@@ -68,7 +68,7 @@ get_textoffset() {
temp_dir="$(mktemp -d)" temp_dir="$(mktemp -d)"
( (
cd ${temp_dir} cd ${temp_dir}
tar -xf "${boot_tgz_file}" "${vmlinuz_name}" tar --no-same-owner -xf "${boot_tgz_file}" "${vmlinuz_name}"
) )
# With TEXT_OFFSET patch is [ 0108 ], without TEXT_OFFSET patch is [ 0000 ] # With TEXT_OFFSET patch is [ 0108 ], without TEXT_OFFSET patch is [ 0000 ]
[[ "$(hexdump -n 15 -x "${temp_dir}/${vmlinuz_name}" 2>/dev/null | head -n 1 | awk '{print $7}')" == "0108" ]] && K510="0" [[ "$(hexdump -n 15 -x "${temp_dir}/${vmlinuz_name}" 2>/dev/null | head -n 1 | awk '{print $7}')" == "0108" ]] && K510="0"
@@ -274,7 +274,7 @@ update_kernel() {
dtb* 2>/dev/null dtb* 2>/dev/null
) )
# Extract the new kernel # Extract the new kernel
tar -xf ${P4_PATH}/boot-${kernel_name}.tar.gz -C /boot tar --no-same-owner -xf ${P4_PATH}/boot-${kernel_name}.tar.gz -C /boot
# Check if the file exists # Check if the file exists
local valid_files local valid_files
@@ -319,14 +319,14 @@ update_kernel() {
else else
(cd /boot && mkdir -p dtb-${kernel_name}/${PLATFORM} && ln -sf dtb-${kernel_name} dtb) (cd /boot && mkdir -p dtb-${kernel_name}/${PLATFORM} && ln -sf dtb-${kernel_name} dtb)
fi fi
tar -xf ${P4_PATH}/dtb-${PLATFORM}-${kernel_name}.tar.gz -C /boot/dtb/${PLATFORM} tar --no-same-owner -xf ${P4_PATH}/dtb-${PLATFORM}-${kernel_name}.tar.gz -C /boot/dtb/${PLATFORM}
[[ "$(ls /boot/dtb/${PLATFORM} -l 2>/dev/null | grep "^-" | wc -l)" -ge "1" ]] || error_msg "/boot/dtb/${PLATFORM} file is missing." [[ "$(ls /boot/dtb/${PLATFORM} -l 2>/dev/null | grep "^-" | wc -l)" -ge "1" ]] || error_msg "/boot/dtb/${PLATFORM} file is missing."
echo -e "(2/3) Unpacking [ dtb-${PLATFORM}-${kernel_name}.tar.gz ] done." echo -e "(2/3) Unpacking [ dtb-${PLATFORM}-${kernel_name}.tar.gz ] done."
fi fi
# 03. for /lib/modules/* # 03. for /lib/modules/*
rm -rf /lib/modules/* rm -rf /lib/modules/*
tar -xf ${P4_PATH}/modules-${kernel_name}.tar.gz -C /lib/modules tar --no-same-owner -xf ${P4_PATH}/modules-${kernel_name}.tar.gz -C /lib/modules
( (
cd /lib/modules/${kernel_name} cd /lib/modules/${kernel_name}
rm -f *.ko rm -f *.ko
@@ -210,6 +210,14 @@ case $MYDEVICE_NAME in
fi fi
SOC="ht2" SOC="ht2"
;; ;;
"XYLink XR3528")
if [ -n "${CURRENT_FDTFILE}" ]; then
MYDTB_FDTFILE="${CURRENT_FDTFILE}"
else
MYDTB_FDTFILE="rk3528-xylink-xr3528.dtb"
fi
SOC="xylink-xr3528"
;;
"Radxa E20C") "Radxa E20C")
if [ -n "${CURRENT_FDTFILE}" ]; then if [ -n "${CURRENT_FDTFILE}" ]; then
MYDTB_FDTFILE="${CURRENT_FDTFILE}" MYDTB_FDTFILE="${CURRENT_FDTFILE}"
+9
View File
@@ -4,6 +4,15 @@
`DNS-over-TLS`, `DNS-over-HTTPS`, `DNSCrypt`, and `DNS-over-QUIC`. Moreover, `DNS-over-TLS`, `DNS-over-HTTPS`, `DNSCrypt`, and `DNS-over-QUIC`. Moreover,
it can work as a `DNS-over-HTTPS`, `DNS-over-TLS` or `DNS-over-QUIC` server. it can work as a `DNS-over-HTTPS`, `DNS-over-TLS` or `DNS-over-QUIC` server.
The LuCI interface also supports reusable DNS profiles. Each profile stores
its own bootstrap, upstream, and fallback server lists and can apply all three
lists at once. Editable example profiles for Cloudflare, Quad9, Google, and a
mixed parallel setup are included to demonstrate the feature.
## Requirements
- OpenWrt >= 24.10
## How to install ## How to install
1. Go to [here](https://fantastic-packages.github.io/releases/) 1. Go to [here](https://fantastic-packages.github.io/releases/)
@@ -2,13 +2,22 @@
'require form'; 'require form';
'require fs'; 'require fs';
'require uci'; 'require uci';
'require dom';
'require rpc'; 'require rpc';
'require poll'; 'require poll';
'require ui';
'require view'; 'require view';
'require validation';
'require tools.widgets as widgets'; 'require tools.widgets as widgets';
const conf = 'dnsproxy'; const conf = 'dnsproxy';
const instance = 'dnsproxy'; const instance = 'dnsproxy';
const profileListOptions = ['bootstrap', 'upstream', 'fallback'];
const upstream_mode = [
['load_balance', _('Load balance'), _('one upstream per request.')],
['parallel', _('Parallel'), _('first DNS response wins.')],
['fastest_addr', _('Fastest address'), _('tests returned IP addresses.')]
];
const callServiceList = rpc.declare({ const callServiceList = rpc.declare({
object: 'service', object: 'service',
@@ -34,6 +43,91 @@ function getServiceStatus() {
}); });
} }
function profileTitle(profile) {
return profile.label || profile['.name'];
}
function applyProfile(select) {
const profile = uci.sections(conf, 'profile').find((item) => item['.name'] === select.value);
if (!profile)
return ui.addNotification(null, E('p', _('The selected DNS profile no longer exists. Reload the page and try again.')), 'error');
const upstreams = L.toArray(profile.upstream).filter(Boolean);
if (!upstreams.length)
return ui.addNotification(null, E('p', _('The selected DNS profile has no upstream servers.')), 'error');
profileListOptions.forEach((option) => {
const values = L.toArray(profile[option]).filter(Boolean);
uci.set(conf, 'servers', option, values.length ? values : null);
});
if (profile.upstream_mode)
uci.set(conf, 'global', 'upstream_mode', profile.upstream_mode);
return uci.save()
.then(L.bind(this.map.load, this.map))
.then(L.bind(this.map.reset, this.map))
.then(() => {
ui.addNotification(null, E('p', _('DNS profile “%s” has been applied.').format(profileTitle(profile))), 'info');
})
.catch((err) => {
ui.addNotification(null, E('p', _('Failed to apply DNS profile: %s').format(err.message || err)), 'error');
});
}
function validateUniqueValue(section_id, value) {
if (!value)
return _('Expecting: %s').format(_('non-empty value'));
let duplicate = false;
uci.sections(this.config, this.section.sectiontype, (res) => {
if (res['.name'] !== section_id)
if (res[this.option] === value)
duplicate = true;
});
if (duplicate)
return _('Expecting: %s').format(_('unique value'));
return true;
}
function validateServerValue(section_id, value) {
if (!value)
return true;
if (!value.includes('://')) {
let stubValidator = {
factory: validation,
apply(type, value, args) {
if (value != null)
this.value = value;
return validation.types[type].apply(this, args);
},
assert(condition) {
return !!condition;
}
};
if (!stubValidator.apply('host', value) && !stubValidator.apply('hostport', value))
return _('Expecting: %s').format(_('valid host or hostport'));
return true;
}
try {
const url = new URL(value);
if (!url.hostname)
return _('Expecting: %s').format(_('valid URL'));
return true;
}
catch (e) {
return _('Invalid URI');
}
}
return view.extend({ return view.extend({
load() { load() {
@@ -116,7 +210,8 @@ return view.extend({
o = s.taboption('main', form.Flag, 'insecure', _('Disable secure TLS cert validation')); o = s.taboption('main', form.Flag, 'insecure', _('Disable secure TLS cert validation'));
o = s.taboption('main', form.Flag, 'http3', _('DoH uses H3 first')); o = s.taboption('main', form.Flag, 'http3', _('Enable HTTP/3 for DoH'));
o.description = _('Use HTTP/3 when it is faster. HTTPS fallback remains available.');
o = s.taboption('main', form.Value, 'timeout', _('Timeout for queries to remote upstream (default: 10s)')); o = s.taboption('main', form.Value, 'timeout', _('Timeout for queries to remote upstream (default: 10s)'));
o.datatype = 'string'; o.datatype = 'string';
@@ -127,14 +222,17 @@ return view.extend({
o = s.taboption('main', form.Value, 'udp_buf_size', _('Size of the UDP buffer in bytes. Set 0 use the system default')); o = s.taboption('main', form.Value, 'udp_buf_size', _('Size of the UDP buffer in bytes. Set 0 use the system default'));
o.datatype = 'uinteger'; o.datatype = 'uinteger';
o = s.taboption('main', form.Flag, 'all_servers', _('Parallel queries all upstream')); o = s.taboption('main', form.RichListValue, 'upstream_mode', _('Upstream selection mode'),
_('For the lowest DNS response time choose Parallel.') + '</br>' +
o = s.taboption('main', form.Flag, 'fastest_addr', _('Respond to A or AAAA requests only with the fastest IP address')); _('Fastest address performs additional IP reachability tests and is a different, slower operation.'));
o.depends('all_servers', '1'); upstream_mode.forEach((res) => {
o.value.apply(o, res);
})
o.rmempty = false;
s.tab('cache', _('Cache')); s.tab('cache', _('Cache'));
o = s.taboption('cache', form.SectionValue, '_cache', form.NamedSection, 'cache', 'homeproxy'); o = s.taboption('cache', form.SectionValue, '_cache', form.NamedSection, 'cache', 'dnsproxy');
ss = o.subsection; ss = o.subsection;
so = ss.option(form.Flag, 'enabled', _('Enable Cache')); so = ss.option(form.Flag, 'enabled', _('Enable Cache'));
@@ -161,7 +259,7 @@ return view.extend({
s.tab('dns64', _('DNS64')); s.tab('dns64', _('DNS64'));
o = s.taboption('dns64', form.SectionValue, '_dns64', form.NamedSection, 'dns64', 'homeproxy'); o = s.taboption('dns64', form.SectionValue, '_dns64', form.NamedSection, 'dns64', 'dnsproxy');
ss = o.subsection; ss = o.subsection;
so = ss.option(form.Flag, 'enabled', _('Enable DNS64')); so = ss.option(form.Flag, 'enabled', _('Enable DNS64'));
@@ -174,7 +272,7 @@ return view.extend({
s.tab('edns', _('EDNS')); s.tab('edns', _('EDNS'));
o = s.taboption('edns', form.SectionValue, '_edns', form.NamedSection, 'edns', 'homeproxy'); o = s.taboption('edns', form.SectionValue, '_edns', form.NamedSection, 'edns', 'dnsproxy');
ss = o.subsection; ss = o.subsection;
so = ss.option(form.Flag, 'enabled', _('Enable EDNS')); so = ss.option(form.Flag, 'enabled', _('Enable EDNS'));
@@ -186,7 +284,7 @@ return view.extend({
s.tab('bogus_nxdomain', _('Bogus-NXDOMAIN')); s.tab('bogus_nxdomain', _('Bogus-NXDOMAIN'));
o = s.taboption('bogus_nxdomain', form.SectionValue, '_bogus_nxdomain', form.NamedSection, 'bogus_nxdomain', 'homeproxy'); o = s.taboption('bogus_nxdomain', form.SectionValue, '_bogus_nxdomain', form.NamedSection, 'bogus_nxdomain', 'dnsproxy');
ss = o.subsection; ss = o.subsection;
so = ss.option(form.DynamicList, 'ip_addr', _('Convert matching single IP responses to NXDOMAIN')); so = ss.option(form.DynamicList, 'ip_addr', _('Convert matching single IP responses to NXDOMAIN'));
@@ -194,15 +292,107 @@ return view.extend({
s.tab('servers', _('Upstreams')); s.tab('servers', _('Upstreams'));
o = s.taboption('servers', form.SectionValue, '_servers', form.NamedSection, 'servers', 'homeproxy'); o = s.taboption('servers', form.DummyValue, '_profile_switcher', _('DNS profiles'),
_('A profile replaces Bootstrap, Upstream and Fallback lists together. Unsaved changes elsewhere on this page are not included.'));
o.renderWidget = function () {
const profiles = uci.sections(conf, 'profile');
const select = E('select', {
'id': 'dnsproxy-profile-select',
'class': 'cbi-input-select',
'disabled': profiles.length ? null : ''
}, profiles.map((profile) => E('option', {
'value': profile['.name']
}, profileTitle(profile))));
return E('div', {}, [
E('div', { 'style': 'display:flex;gap:.75em;align-items:center;flex-wrap:wrap' }, [
select,
E('button', {
'class': 'cbi-button cbi-button-positive important',
'disabled': profiles.length ? null : '',
'click': ui.createHandlerFn(this, applyProfile, select)
}, _('Use selected profile'))
]),
profiles.length ? '' : E('p', {}, _('Create and save a profile in the “DNS profile templates” section below first.'))
]);
};
o = s.taboption('servers', form.SectionValue, '_servers', form.NamedSection, 'servers', 'dnsproxy');
ss = o.subsection; ss = o.subsection;
so = ss.option(form.DynamicList, 'bootstrap', _('Bootstrap DNS Server')); so = ss.option(form.DynamicList, 'bootstrap', _('Bootstrap DNS Server'));
so.readonly = true
so = ss.option(form.DynamicList, 'upstream', _('Upstream DNS Server')); so = ss.option(form.DynamicList, 'upstream', _('Upstream DNS Server'));
so.readonly = true
so.rmempty = false; so.rmempty = false;
so = ss.option(form.DynamicList, 'fallback', _('Fallback DNS Server')); so = ss.option(form.DynamicList, 'fallback', _('Fallback DNS Server'));
so.readonly = true
o = s.taboption('servers', form.SectionValue, '_profiles', form.GridSection, 'profile', _('DNS profile templates'),
_('Create reusable templates here. Applying a template does not modify the template itself.'));
ss = o.subsection;
ss.anonymous = true;
ss.addremove = true;
ss.rowcolors = true;
ss.sortable = true;
ss.nodescriptions = true;
ss.addbtntitle = _('Add DNS profile');
ss.renderRowActions = function(section_id, more_label, trEl) {
trEl = form.GridSection.prototype.renderRowActions.apply(this, arguments);
const preset_ids = [
'default'
];
if (preset_ids.includes(section_id))
trEl?.lastChild.querySelector('.cbi-button-remove')?.remove();
return trEl;
}
so = ss.option(form.Value, 'label', _('Profile name'));
so.rmempty = false;
so.validate = validateUniqueValue;
so = ss.option(form.RichListValue, 'upstream_mode', _('Upstream selection mode'));
so.value(' ', _('Keep current mode'));
upstream_mode.forEach((res) => {
so.value.apply(so, res);
})
so.load = function(section_id) {
let value = this.super('load', section_id);
if (!value)
return ' ';
return value;
}
so.write = function(section_id, value) {
if (!value.trim())
return this.super('remove', section_id);
return form.RichListValue.prototype.write.call(this, section_id, value);
}
so.textvalue = function(section_id) {
let cval = this.cfgvalue(section_id);
let i = this.keylist.indexOf(cval);
let val = this.vallist[i];
return dom.elem(val) ? val.firstChild.textContent : val ?? cval;
}
so = ss.option(form.DynamicList, 'bootstrap', _('Bootstrap DNS'));
so.validate = validateServerValue;
so.modalonly = true;
so = ss.option(form.DynamicList, 'upstream', _('Upstream DNS'));
so.rmempty = false;
so.validate = validateServerValue;
so.modalonly = true;
so = ss.option(form.DynamicList, 'fallback', _('Fallback DNS'));
so.validate = validateServerValue;
so.modalonly = true;
return m.render() return m.render()
.then(L.bind(function(m, nodes) { .then(L.bind(function(m, nodes) {
+178 -46
View File
@@ -1,155 +1,287 @@
msgid "" msgid ""
msgstr "Content-Type: text/plain; charset=UTF-8" msgstr "Content-Type: text/plain; charset=UTF-8"
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:187 #: htdocs/luci-static/resources/view/dnsproxy.js:296
msgid ""
"A profile replaces Bootstrap, Upstream and Fallback lists together. Unsaved "
"changes elsewhere on this page are not included."
msgstr ""
#: htdocs/luci-static/resources/view/dnsproxy.js:341
msgid "Add DNS profile"
msgstr ""
#: htdocs/luci-static/resources/view/dnsproxy.js:285
msgid "Bogus-NXDOMAIN" msgid "Bogus-NXDOMAIN"
msgstr "" msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:200 #: htdocs/luci-static/resources/view/dnsproxy.js:384
msgid "Bootstrap DNS"
msgstr ""
#: htdocs/luci-static/resources/view/dnsproxy.js:323
msgid "Bootstrap DNS Server" msgid "Bootstrap DNS Server"
msgstr "" msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:135 #: htdocs/luci-static/resources/view/dnsproxy.js:233
msgid "Cache" msgid "Cache"
msgstr "" msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:146 #: htdocs/luci-static/resources/view/dnsproxy.js:244
msgid "Cache size (in bytes)" msgid "Cache size (in bytes)"
msgstr "" msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:70 #: htdocs/luci-static/resources/view/dnsproxy.js:164
msgid "Collecting data ..." msgid "Collecting data ..."
msgstr "" msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:192 #: htdocs/luci-static/resources/view/dnsproxy.js:290
msgid "Convert matching single IP responses to NXDOMAIN" msgid "Convert matching single IP responses to NXDOMAIN"
msgstr "" msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:65 #: htdocs/luci-static/resources/view/dnsproxy.js:316
#: applications/luci-app-dnsproxy/root/usr/share/luci/menu.d/luci-app-dnsproxy.json:3 msgid ""
"Create and save a profile in the “DNS profile templates” section below first."
msgstr ""
#: htdocs/luci-static/resources/view/dnsproxy.js:334
msgid ""
"Create reusable templates here. Applying a template does not modify the "
"template itself."
msgstr ""
#: htdocs/luci-static/resources/view/dnsproxy.js:159
#: root/usr/share/luci/menu.d/luci-app-dnsproxy.json:3
msgid "DNS Proxy" msgid "DNS Proxy"
msgstr "" msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:162 #: htdocs/luci-static/resources/view/dnsproxy.js:333
msgid "DNS profile templates"
msgstr ""
#: htdocs/luci-static/resources/view/dnsproxy.js:72
msgid "DNS profile “%s” has been applied."
msgstr ""
#: htdocs/luci-static/resources/view/dnsproxy.js:295
msgid "DNS profiles"
msgstr ""
#: htdocs/luci-static/resources/view/dnsproxy.js:260
msgid "DNS64" msgid "DNS64"
msgstr "" msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:169 #: htdocs/luci-static/resources/view/dnsproxy.js:267
msgid "DNS64 Prefix" msgid "DNS64 Prefix"
msgstr "" msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:113 #: htdocs/luci-static/resources/view/dnsproxy.js:207
msgid "Disable IPv6" msgid "Disable IPv6"
msgstr "" msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:117 #: htdocs/luci-static/resources/view/dnsproxy.js:211
msgid "Disable secure TLS cert validation" msgid "Disable secure TLS cert validation"
msgstr "" msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:119 #: htdocs/luci-static/resources/view/dnsproxy.js:273
msgid "DoH uses H3 first"
msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:175
msgid "EDNS" msgid "EDNS"
msgstr "" msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:182 #: htdocs/luci-static/resources/view/dnsproxy.js:280
msgid "EDNS Client Address" msgid "EDNS Client Address"
msgstr "" msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:78 #: htdocs/luci-static/resources/view/dnsproxy.js:172
msgid "Enable" msgid "Enable"
msgstr "" msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:140 #: htdocs/luci-static/resources/view/dnsproxy.js:238
msgid "Enable Cache" msgid "Enable Cache"
msgstr "" msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:167 #: htdocs/luci-static/resources/view/dnsproxy.js:265
msgid "Enable DNS64" msgid "Enable DNS64"
msgstr "" msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:180 #: htdocs/luci-static/resources/view/dnsproxy.js:278
msgid "Enable EDNS" msgid "Enable EDNS"
msgstr "" msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:205 #: htdocs/luci-static/resources/view/dnsproxy.js:213
msgid "Enable HTTP/3 for DoH"
msgstr ""
#: htdocs/luci-static/resources/view/dnsproxy.js:81
#: htdocs/luci-static/resources/view/dnsproxy.js:90
#: htdocs/luci-static/resources/view/dnsproxy.js:114
#: htdocs/luci-static/resources/view/dnsproxy.js:122
msgid "Expecting: %s"
msgstr ""
#: htdocs/luci-static/resources/view/dnsproxy.js:75
msgid "Failed to apply DNS profile: %s"
msgstr ""
#: htdocs/luci-static/resources/view/dnsproxy.js:393
msgid "Fallback DNS"
msgstr ""
#: htdocs/luci-static/resources/view/dnsproxy.js:330
msgid "Fallback DNS Server" msgid "Fallback DNS Server"
msgstr "" msgstr ""
#: applications/luci-app-dnsproxy/root/usr/share/rpcd/acl.d/luci-app-dnsproxy.json:3 #: htdocs/luci-static/resources/view/dnsproxy.js:19
msgid "Fastest address"
msgstr ""
#: htdocs/luci-static/resources/view/dnsproxy.js:227
msgid ""
"Fastest address performs additional IP reachability tests and is a "
"different, slower operation."
msgstr ""
#: htdocs/luci-static/resources/view/dnsproxy.js:226
msgid "For the lowest DNS response time choose Parallel."
msgstr ""
#: root/usr/share/rpcd/acl.d/luci-app-dnsproxy.json:3
msgid "Grant access to LuCI app dnsproxy" msgid "Grant access to LuCI app dnsproxy"
msgstr "" msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:86 #: htdocs/luci-static/resources/view/dnsproxy.js:127
msgid "Invalid URI"
msgstr ""
#: htdocs/luci-static/resources/view/dnsproxy.js:359
msgid "Keep current mode"
msgstr ""
#: htdocs/luci-static/resources/view/dnsproxy.js:180
msgid "Listen address" msgid "Listen address"
msgstr "" msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:108 #: htdocs/luci-static/resources/view/dnsproxy.js:202
msgid "Listen ports" msgid "Listen ports"
msgstr "" msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:83 #: htdocs/luci-static/resources/view/dnsproxy.js:17
msgid "Load balance"
msgstr ""
#: htdocs/luci-static/resources/view/dnsproxy.js:177
msgid "Log file path" msgid "Log file path"
msgstr "" msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:76 #: htdocs/luci-static/resources/view/dnsproxy.js:170
msgid "Main" msgid "Main"
msgstr "" msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:157 #: htdocs/luci-static/resources/view/dnsproxy.js:255
msgid "Max TTL value for DNS entries, in seconds" msgid "Max TTL value for DNS entries, in seconds"
msgstr "" msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:152 #: htdocs/luci-static/resources/view/dnsproxy.js:250
msgid "Min TTL value for DNS entries, in seconds" msgid "Min TTL value for DNS entries, in seconds"
msgstr "" msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:142 #: htdocs/luci-static/resources/view/dnsproxy.js:240
msgid "Optimistic Cache" msgid "Optimistic Cache"
msgstr "" msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:130 #: htdocs/luci-static/resources/view/dnsproxy.js:18
msgid "Parallel queries all upstream" msgid "Parallel"
msgstr "" msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:124 #: htdocs/luci-static/resources/view/dnsproxy.js:354
msgid "Profile name"
msgstr ""
#: htdocs/luci-static/resources/view/dnsproxy.js:219
msgid "Ratelimit (requests per second)" msgid "Ratelimit (requests per second)"
msgstr "" msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:115 #: htdocs/luci-static/resources/view/dnsproxy.js:209
msgid "Refuse <code>ANY</code> requests" msgid "Refuse <code>ANY</code> requests"
msgstr "" msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:132 #: htdocs/luci-static/resources/view/dnsproxy.js:148
msgid "Respond to A or AAAA requests only with the fastest IP address"
msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:54
msgid "SERVER NOT RUNNING" msgid "SERVER NOT RUNNING"
msgstr "" msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:52 #: htdocs/luci-static/resources/view/dnsproxy.js:146
msgid "SERVER RUNNING" msgid "SERVER RUNNING"
msgstr "" msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:127 #: htdocs/luci-static/resources/view/dnsproxy.js:222
msgid "Size of the UDP buffer in bytes. Set 0 use the system default" msgid "Size of the UDP buffer in bytes. Set 0 use the system default"
msgstr "" msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:121 #: htdocs/luci-static/resources/view/dnsproxy.js:58
msgid "The selected DNS profile has no upstream servers."
msgstr ""
#: htdocs/luci-static/resources/view/dnsproxy.js:54
msgid ""
"The selected DNS profile no longer exists. Reload the page and try again."
msgstr ""
#: htdocs/luci-static/resources/view/dnsproxy.js:216
msgid "Timeout for queries to remote upstream (default: 10s)" msgid "Timeout for queries to remote upstream (default: 10s)"
msgstr "" msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:202 #: htdocs/luci-static/resources/view/dnsproxy.js:388
msgid "Upstream DNS"
msgstr ""
#: htdocs/luci-static/resources/view/dnsproxy.js:326
msgid "Upstream DNS Server" msgid "Upstream DNS Server"
msgstr "" msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:195 #: htdocs/luci-static/resources/view/dnsproxy.js:225
#: htdocs/luci-static/resources/view/dnsproxy.js:358
msgid "Upstream selection mode"
msgstr ""
#: htdocs/luci-static/resources/view/dnsproxy.js:293
msgid "Upstreams" msgid "Upstreams"
msgstr "" msgstr ""
#: applications/luci-app-dnsproxy/htdocs/luci-static/resources/view/dnsproxy.js:81 #: htdocs/luci-static/resources/view/dnsproxy.js:214
msgid "Use HTTP/3 when it is faster. HTTPS fallback remains available."
msgstr ""
#: htdocs/luci-static/resources/view/dnsproxy.js:314
msgid "Use selected profile"
msgstr ""
#: htdocs/luci-static/resources/view/dnsproxy.js:175
msgid "Verbose" msgid "Verbose"
msgstr "" msgstr ""
#: htdocs/luci-static/resources/view/dnsproxy.js:18
msgid "first DNS response wins."
msgstr ""
#: htdocs/luci-static/resources/view/dnsproxy.js:81
msgid "non-empty value"
msgstr ""
#: htdocs/luci-static/resources/view/dnsproxy.js:17
msgid "one upstream per request."
msgstr ""
#: htdocs/luci-static/resources/view/dnsproxy.js:19
msgid "tests returned IP addresses."
msgstr ""
#: htdocs/luci-static/resources/view/dnsproxy.js:90
msgid "unique value"
msgstr ""
#: htdocs/luci-static/resources/view/dnsproxy.js:122
msgid "valid URL"
msgstr ""
#: htdocs/luci-static/resources/view/dnsproxy.js:114
msgid "valid host or hostport"
msgstr ""
@@ -0,0 +1,58 @@
#!/bin/sh
add_cloudflare_profile() {
uci set dnsproxy.preset_cloudflare='profile'
uci set dnsproxy.preset_cloudflare.label='Preset: Cloudflare DoH'
uci add_list dnsproxy.preset_cloudflare.bootstrap='1.1.1.1:53'
uci add_list dnsproxy.preset_cloudflare.bootstrap='1.0.0.1:53'
uci add_list dnsproxy.preset_cloudflare.upstream='https://cloudflare-dns.com/dns-query'
uci add_list dnsproxy.preset_cloudflare.fallback='https://dns.quad9.net/dns-query'
uci set dnsproxy.preset_cloudflare.upstream_mode='load_balance'
}
add_quad9_profile() {
uci set dnsproxy.preset_quad9='profile'
uci set dnsproxy.preset_quad9.label='Preset: Quad9 DoH'
uci add_list dnsproxy.preset_quad9.bootstrap='9.9.9.9:53'
uci add_list dnsproxy.preset_quad9.bootstrap='149.112.112.112:53'
uci add_list dnsproxy.preset_quad9.upstream='https://dns.quad9.net/dns-query'
uci add_list dnsproxy.preset_quad9.fallback='https://cloudflare-dns.com/dns-query'
uci set dnsproxy.preset_quad9.upstream_mode='load_balance'
}
add_google_profile() {
uci set dnsproxy.preset_google='profile'
uci set dnsproxy.preset_google.label='Preset: Google DoH'
uci add_list dnsproxy.preset_google.bootstrap='8.8.8.8:53'
uci add_list dnsproxy.preset_google.bootstrap='8.8.4.4:53'
uci add_list dnsproxy.preset_google.upstream='https://dns.google/dns-query'
uci add_list dnsproxy.preset_google.fallback='https://cloudflare-dns.com/dns-query'
uci set dnsproxy.preset_google.upstream_mode='load_balance'
}
add_fast_mixed_profile() {
uci set dnsproxy.preset_fast_mixed='profile'
uci set dnsproxy.preset_fast_mixed.label='Preset: Fast mixed DoH'
uci add_list dnsproxy.preset_fast_mixed.bootstrap='1.1.1.1:53'
uci add_list dnsproxy.preset_fast_mixed.bootstrap='9.9.9.9:53'
uci add_list dnsproxy.preset_fast_mixed.bootstrap='8.8.8.8:53'
uci add_list dnsproxy.preset_fast_mixed.upstream='https://cloudflare-dns.com/dns-query'
uci add_list dnsproxy.preset_fast_mixed.upstream='https://dns.quad9.net/dns-query'
uci add_list dnsproxy.preset_fast_mixed.fallback='https://dns.google/dns-query'
uci set dnsproxy.preset_fast_mixed.upstream_mode='parallel'
}
# preset
if [ -z "$(uci -q get dnsproxy.default)" ]; then
uci -q get dnsproxy.preset_cloudflare >/dev/null || add_cloudflare_profile
uci -q get dnsproxy.preset_quad9 >/dev/null || add_quad9_profile
uci -q get dnsproxy.preset_google >/dev/null || add_google_profile
uci -q get dnsproxy.preset_fast_mixed >/dev/null || add_fast_mixed_profile
uci commit dnsproxy
# default
echo "config profile 'default'" >> /etc/config/dnsproxy
echo " option label 'Default'" >> /etc/config/dnsproxy
sed -n "/config dnsproxy 'servers'/,/^[^\t]/{/^\t/p}" /etc/config/dnsproxy >> /etc/config/dnsproxy
fi
exit 0
@@ -249,7 +249,7 @@ end
function get_redir_log() function get_redir_log()
local id = http.formvalue("id") local id = http.formvalue("id")
local name = http.formvalue("name") local name = http.formvalue("name")
local file_path = "/tmp/etc/passwall2/acl/" .. id .. "/" .. name .. ".log" local file_path = api.TMP_PATH .. "/acl/" .. id .. "/" .. name .. ".log"
if nixio.fs.access(file_path) then if nixio.fs.access(file_path) then
local content = luci.sys.exec("tail -n 19999 '" .. file_path .. "'") local content = luci.sys.exec("tail -n 19999 '" .. file_path .. "'")
content = content:gsub("\n", "<br />") content = content:gsub("\n", "<br />")
@@ -261,7 +261,7 @@ end
function get_socks_log() function get_socks_log()
local name = http.formvalue("name") local name = http.formvalue("name")
local path = "/tmp/etc/passwall2/" .. name .. ".log" local path = api.TMP_PATH .. "/" .. name .. ".log"
if nixio.fs.access(path) then if nixio.fs.access(path) then
local content = luci.sys.exec("tail -n 5000 ".. path) local content = luci.sys.exec("tail -n 5000 ".. path)
content = content:gsub("\n", "<br />") content = content:gsub("\n", "<br />")
@@ -294,7 +294,7 @@ end
function index_status() function index_status()
local e = {} local e = {}
e["global_status"] = luci.sys.call("/bin/busybox top -bn1 | grep -v 'grep' | grep '/tmp/etc/passwall2/bin/' | grep 'default' | grep 'global' >/dev/null") == 0 e["global_status"] = luci.sys.call("/bin/busybox top -bn1 | grep -v 'grep' | grep '%s/bin/' | grep 'default' | grep 'global' >/dev/null" % api.TMP_PATH) == 0
http_write_json(e) http_write_json(e)
end end
@@ -665,8 +665,9 @@ end
function server_log() function server_log()
local id = http.formvalue("id") local id = http.formvalue("id")
if nixio.fs.access("/tmp/etc/passwall2_server/" .. id .. ".log") then local f_file = api.S_TMP_PATH .. "/" .. id .. ".log"
local content = luci.sys.exec("cat /tmp/etc/passwall2_server/" .. id .. ".log") if nixio.fs.access(f_file) then
local content = luci.sys.exec("cat " .. f_file)
content = content:gsub("\n", "<br />") content = content:gsub("\n", "<br />")
http.write(content) http.write(content)
else else
@@ -2,9 +2,10 @@ local api = require "luci.passwall2.api"
api.set_default_cbi() api.set_default_cbi()
m = Map() m = Map()
m.redirect = api.url("acl")
if not arg[1] or not m:get(arg[1]) then if not arg[1] or not m:get(arg[1]) then
luci.http.redirect(api.url("acl")) luci.http.redirect(m.redirect)
end end
m:appendTemplate("/cbi/nodes_listvalue_com") m:appendTemplate("/cbi/nodes_listvalue_com")
@@ -2,13 +2,12 @@ api = require "luci.passwall2.api"
api.set_default_cbi() api.set_default_cbi()
m = Map() m = Map()
m.redirect = api.url() m.redirect = api.url("node_list")
if not arg[1] or not m:get(arg[1]) then if not arg[1] or not m:get(arg[1]) then
luci.http.redirect(api.url("node_list")) luci.http.redirect(m.redirect)
end end
fs = require "nixio.fs"
formvalue_key = "cbid." .. m.config .. "." .. arg[1] .. "." formvalue_key = "cbid." .. m.config .. "." .. arg[1] .. "."
m:appendTemplate("/node_config/header", {section = arg[1]}) m:appendTemplate("/node_config/header", {section = arg[1]})
@@ -24,7 +23,7 @@ m:foreach("nodes", function(s)
end end
end) end)
s = m:section(NamedSection, arg[1], "nodes", translate("Node Config")) local s = m:section(NamedSection, arg[1], "nodes", translate("Node Config"))
s.addremove = false s.addremove = false
s.dynamic = false s.dynamic = false
@@ -74,7 +73,7 @@ end
o = s:option(ListValue, "type", translate("Type")) o = s:option(ListValue, "type", translate("Type"))
local type_table = {} local type_table = {}
for filename in fs.dir(types_dir) do for filename in api.fs.dir(types_dir) do
table.insert(type_table, filename) table.insert(type_table, filename)
end end
table.sort(type_table) table.sort(type_table)
@@ -2,9 +2,10 @@ local api = require "luci.passwall2.api"
api.set_default_cbi() api.set_default_cbi()
m = Map() m = Map()
m.redirect = api.url()
if not arg[1] or not m:get(arg[1]) then if not arg[1] or not m:get(arg[1]) then
luci.http.redirect(api.url()) luci.http.redirect(m.redirect)
end end
m:appendTemplate("/cbi/nodes_dynamiclist_com") m:appendTemplate("/cbi/nodes_dynamiclist_com")
@@ -75,6 +76,20 @@ o = s:option(Flag, "log", translate("Enable") .. " " .. translate("Log"))
o.default = 1 o.default = 1
o.rmempty = false o.rmempty = false
o = s:option(DummyValue, "_log", translate("Log File"))
o.rawhtml = true
o.cfgvalue = function(t, n)
local log_path = api.TMP_PATH .. "/" .. arg[1] .. ".log"
local log_url = api.url("get_socks_log") .. "?name=" .. arg[1]
return string.format(
'<code>%s</code>&nbsp;&nbsp;<input class="btn cbi-button cbi-button-apply" type="button" value="%s" onclick="window.open(\'%s\', \'_blank\')" />',
log_path,
translate("View Log"),
log_url
)
end
o:depends("log", true)
o = s:option(Flag, "enable_autoswitch", translate("Auto Switch")) o = s:option(Flag, "enable_autoswitch", translate("Auto Switch"))
o.default = 0 o.default = 0
o.rmempty = false o.rmempty = false
@@ -1,51 +1,43 @@
local m, s = ...
if not api.finded_com("hysteria") then if not api.finded_com("hysteria") then
return return
end end
type_name = "Hysteria2"
-- [[ Hysteria2 ]] -- [[ Hysteria2 ]]
local m, s1 = ...
local type_name = "Hysteria2"
s.fields["type"]:value(type_name, "Hysteria2") s1.fields["type"]:value(type_name, "Hysteria2")
if s.val["type"] ~= type_name then if s1.val["type"] ~= type_name then
return return
end end
local option_prefix = "hysteria2_" local s = NamedSection(m, arg[1], "server")
s.type_name = type_name
s.option_prefix = "hysteria2_"
local function _n(name) o = s:option(Value, "address", translate("Address (Support Domain Name)"))
return option_prefix .. name o:depends({ realms = false })
end
o = s:option(Value, _n("address"), translate("Address (Support Domain Name)")) o = s:option(Value, "port", translate("Port"))
o:depends({ [_n("realms")] = false })
o = s:option(Value, _n("port"), translate("Port"))
o.datatype = "port" o.datatype = "port"
o:depends({ [_n("realms")] = false }) o:depends({ realms = false })
o = s:option(Value, _n("hop"), translate("Port hopping range")) o = s:option(Value, "hop", translate("Port hopping range"))
o.description = translate("Format as 1000:2000 or 1000-2000 Multiple groups are separated by commas (,).") o.description = translate("Format as 1000:2000 or 1000-2000 Multiple groups are separated by commas (,).")
o.rewrite_option = o.option o:depends({ realms = false })
o:depends({ [_n("realms")] = false })
o = s:option(Value, _n("hop_interval"), translate("Hop Interval(second)"), translate("Supports a fixed value or a random range (e.g., 30, 5-30), minimum 5.")) o = s:option(Value, "hop_interval", translate("Hop Interval(second)"), translate("Supports a fixed value or a random range (e.g., 30, 5-30), minimum 5."))
o.datatype = "or(uinteger,portrange)" o.datatype = "or(uinteger,portrange)"
o.placeholder = "30" o.placeholder = "30"
o.default = "30" o.default = "30"
o.rewrite_option = o.option o:depends({ realms = false })
o:depends({ [_n("realms")] = false })
o = s:option(Flag, _n("realms"), translate("Realms")) o = s:option(Flag, "realms", translate("Realms"))
o.default = "0" o.default = "0"
o.rewrite_option = o.option
o = s:option(Value, _n("realm_url"), translate("Realm URL"), translate("Example:") .. "realm://public@realm.hy2.io/your-realm-name") o = s:option(Value, "realm_url", translate("Realm URL"), translate("Example:") .. "realm://public@realm.hy2.io/your-realm-name")
o.rewrite_option = o.option o:depends({ realms = "1" })
o:depends({ [_n("realms")] = "1" })
o.validate = function(self, value) o.validate = function(self, value)
value = api.trim(value) value = api.trim(value)
local realm = api.parse_realm_uri(value) local realm = api.parse_realm_uri(value)
@@ -53,76 +45,62 @@ o.validate = function(self, value)
return nil, translate("Invalid Realm URL.") return nil, translate("Invalid Realm URL.")
end end
o = s:option(DynamicList, _n("realm_stun"), translate("Realm STUN")) o = s:option(DynamicList, "realm_stun", translate("Realm STUN"))
o.default = { "stun.sip.us:3478", "stun.nextcloud.com:3478", "global.stun.twilio.com:3478" } o.default = { "stun.sip.us:3478", "stun.nextcloud.com:3478", "global.stun.twilio.com:3478" }
o.rewrite_option = o.option o:depends({ realms = "1" })
o:depends({ [_n("realms")] = "1" })
o = s:option(Value, _n("auth_password"), translate("Auth Password")) o = s:option(Value, "auth_password", translate("Auth Password"))
o.password = true o.password = true
o.rewrite_option = o.option
o = s:option(ListValue, _n("obfs_type"), translate("Obfs Type")) o = s:option(ListValue, "obfs_type", translate("Obfs Type"))
o:value("", translate("Disable")) o:value("", translate("Disable"))
o:value("salamander") o:value("salamander")
o:value("gecko") o:value("gecko")
o.rewrite_option = o.option
o = s:option(Value, _n("obfs_password"), translate("Obfs Password")) o = s:option(Value, "obfs_password", translate("Obfs Password"))
o.rewrite_option = o.option o:depends({ obfs_type = "salamander" })
o:depends({ [_n("obfs_type")] = "salamander" }) o:depends({ obfs_type = "gecko" })
o:depends({ [_n("obfs_type")] = "gecko" })
o = s:option(Value, _n("obfs_MinPacketSize"), translate("Gecko Packet Size (min)")) o = s:option(Value, "obfs_MinPacketSize", translate("Gecko Packet Size (min)"))
o.datatype = "uinteger" o.datatype = "uinteger"
o.placeholder = "512" o.placeholder = "512"
o.default = "512" o.default = "512"
o:depends({ [_n("obfs_type")] = "gecko" }) o:depends({ obfs_type = "gecko" })
o.rewrite_option = o.option
o = s:option(Value, _n("obfs_MaxPacketSize"), translate("Gecko Packet Size (max)")) o = s:option(Value, "obfs_MaxPacketSize", translate("Gecko Packet Size (max)"))
o.datatype = "uinteger" o.datatype = "uinteger"
o.placeholder = "1200" o.placeholder = "1200"
o.default = "1200" o.default = "1200"
o:depends({ [_n("obfs_type")] = "gecko" }) o:depends({ obfs_type = "gecko" })
o.rewrite_option = o.option
o = s:option(Flag, _n("fast_open"), translate("Fast Open")) o = s:option(Flag, "fast_open", translate("Fast Open"))
o.default = "0" o.default = "0"
o = s:option(Value, _n("tls_serverName"), translate("Domain")) o = s:option(Value, "tls_serverName", translate("Domain"))
o = s:option(Flag, _n("tls_allowInsecure"), translate("allowInsecure"), translate("Whether unsafe connections are allowed. When checked, Certificate validation will be skipped.")) o = s:option(Flag, "tls_allowInsecure", translate("allowInsecure"), translate("Whether unsafe connections are allowed. When checked, Certificate validation will be skipped."))
o.default = "0" o.default = "0"
o = s:option(Value, _n("tls_pinSHA256"), translate("PinSHA256"),translate("Certificate fingerprint")) o = s:option(Value, "tls_pinSHA256", translate("PinSHA256"),translate("Certificate fingerprint"))
o = s:option(Value, _n("up_mbps"), translate("Max upload Mbps")) o = s:option(Value, "up_mbps", translate("Max upload Mbps"))
o.rewrite_option = o.option
o = s:option(Value, _n("down_mbps"), translate("Max download Mbps")) o = s:option(Value, "down_mbps", translate("Max download Mbps"))
o.rewrite_option = o.option
o = s:option(Value, _n("recv_window"), translate("QUIC stream receive window")) o = s:option(Value, "recv_window", translate("QUIC stream receive window"))
o.rewrite_option = o.option
o = s:option(Value, _n("recv_window_conn"), translate("QUIC connection receive window")) o = s:option(Value, "recv_window_conn", translate("QUIC connection receive window"))
o.rewrite_option = o.option
o = s:option(Value, _n("idle_timeout"), translate("Idle Timeout"), translate("Units:seconds") .. " (4~120)") o = s:option(Value, "idle_timeout", translate("Idle Timeout"), translate("Units:seconds") .. " (4~120)")
o.datatype = "range(4,120)" o.datatype = "range(4,120)"
o.rewrite_option = o.option
o = s:option(Value, _n("keep_alive_period"), translate("QUIC KeepAlive interval"), translate("Units:seconds") .. " (2~60)") o = s:option(Value, "keep_alive_period", translate("QUIC KeepAlive interval"), translate("Units:seconds") .. " (2~60)")
o.datatype = "range(2,60)" o.datatype = "range(2,60)"
o.rewrite_option = o.option
o = s:option(Flag, _n("disable_mtu_discovery"), translate("Disable MTU detection")) o = s:option(Flag, "disable_mtu_discovery", translate("Disable MTU detection"))
o.default = "0" o.default = "0"
o.rewrite_option = o.option
o = s:option(Flag, _n("lazy_start"), translate("Lazy Start")) o = s:option(Flag, "lazy_start", translate("Lazy Start"))
o.default = "0" o.default = "0"
o.rewrite_option = o.option
api.luci_types(arg[1], m, s, type_name, option_prefix) api.luci_types(s1, s)
@@ -1,37 +1,33 @@
local m, s = ...
if not api.is_finded("naive") then if not api.is_finded("naive") then
return return
end end
type_name = "Naiveproxy"
-- [[ Naive ]] -- [[ Naive ]]
local m, s1 = ...
local type_name = "Naiveproxy"
s.fields["type"]:value(type_name, "NaiveProxy") s1.fields["type"]:value(type_name, "NaiveProxy")
if s.val["type"] ~= type_name then if s1.val["type"] ~= type_name then
return return
end end
local option_prefix = "naive_" local s = NamedSection(m, arg[1], "server")
s.type_name = type_name
s.option_prefix = "naive_"
local function _n(name) o = s:option(ListValue, "protocol", translate("Protocol"))
return option_prefix .. name
end
o = s:option(ListValue, _n("protocol"), translate("Protocol"))
o:value("https", translate("HTTPS")) o:value("https", translate("HTTPS"))
o:value("quic", translate("QUIC")) o:value("quic", translate("QUIC"))
o = s:option(Value, _n("address"), translate("Address (Support Domain Name)")) o = s:option(Value, "address", translate("Address (Support Domain Name)"))
o = s:option(Value, _n("port"), translate("Port")) o = s:option(Value, "port", translate("Port"))
o.datatype = "port" o.datatype = "port"
o = s:option(Value, _n("username"), translate("Username")) o = s:option(Value, "username", translate("Username"))
o = s:option(Value, _n("password"), translate("Password")) o = s:option(Value, "password", translate("Password"))
o.password = true o.password = true
api.luci_types(arg[1], m, s, type_name, option_prefix) api.luci_types(s1, s)
@@ -1,33 +1,27 @@
local m, s = ...
if not api.finded_com("xray") then if not api.finded_com("xray") then
return return
end end
local jsonc = api.jsonc
type_name = "Xray"
-- [[ Xray ]] -- [[ Xray ]]
local m, s1 = ...
local type_name = "Xray"
s.fields["type"]:value(type_name, "Xray") s1.fields["type"]:value(type_name, "Xray")
if not s.fields["type"].default then if not s1.fields["type"].default then
s.fields["type"].default = type_name s1.fields["type"].default = type_name
end end
if s.val["type"] ~= type_name then if s1.val["type"] ~= type_name then
return return
end end
local option_prefix = "xray_" local s = NamedSection(m, arg[1], "server")
s.type_name = type_name
s.option_prefix = "xray_"
local function _n(name) local formvalue_proto = luci.http.formvalue(formvalue_key .. "protocol")
return option_prefix .. name
end
local formvalue_proto = luci.http.formvalue(formvalue_key .. _n("protocol")) if formvalue_proto then s1.val["protocol"] = formvalue_proto end
if formvalue_proto then s.val["protocol"] = formvalue_proto end
local arg_select_proto = luci.http.formvalue("select_proto") or "" local arg_select_proto = luci.http.formvalue("select_proto") or ""
@@ -43,7 +37,7 @@ local header_type_list = {
local xray_version = api.get_app_version("xray") local xray_version = api.get_app_version("xray")
o = s:option(ListValue, _n("protocol"), translate("Protocol")) o = s:option(ListValue, "protocol", translate("Protocol"))
o:value("vmess", translate("Vmess")) o:value("vmess", translate("Vmess"))
o:value("vless", translate("VLESS")) o:value("vless", translate("VLESS"))
o:value("http", translate("HTTP")) o:value("http", translate("HTTP"))
@@ -63,13 +57,13 @@ function o.custom_cfgvalue(self, section)
if arg_select_proto ~= "" then if arg_select_proto ~= "" then
return arg_select_proto return arg_select_proto
else else
return m:get(section, self.option:sub(1 + #option_prefix)) return m:get(section, self.config_option)
end end
end end
local load_balancing_options = s.val["protocol"] == "_balancing" or arg_select_proto == "_balancing" local load_balancing_options = s1.val["protocol"] == "_balancing" or arg_select_proto == "_balancing"
local load_shunt_options = s.val["protocol"] == "_shunt" or arg_select_proto == "_shunt" local load_shunt_options = s1.val["protocol"] == "_shunt" or arg_select_proto == "_shunt"
local load_iface_options = s.val["protocol"] == "_iface" or arg_select_proto == "_iface" local load_iface_options = s1.val["protocol"] == "_iface" or arg_select_proto == "_iface"
local load_normal_options = true local load_normal_options = true
if load_balancing_options or load_shunt_options or load_iface_options then if load_balancing_options or load_shunt_options or load_iface_options then
load_normal_options = nil load_normal_options = nil
@@ -96,14 +90,14 @@ for k, e in ipairs(node_list.balancing_list or {}) do
end end
if load_balancing_options then -- [[ Load balancing Start ]] if load_balancing_options then -- [[ Load balancing Start ]]
o = s:option(ListValue, _n("node_add_mode"), translate("Node Addition Method")) o = s:option(ListValue, "node_add_mode", translate("Node Addition Method"))
o:depends({ [_n("protocol")] = "_balancing" }) o:depends({ protocol = "_balancing" })
o.default = "manual" o.default = "manual"
o:value("manual", translate("Manual")) o:value("manual", translate("Manual"))
o:value("batch", translate("Batch")) o:value("batch", translate("Batch"))
o = s:option(MultiValue, _n("balancing_node"), translate("Load balancing node list"), translate("Load balancing node list, <a target='_blank' href='https://xtls.github.io/config/routing.html#balancerobject'>document</a>")) o = s:option(MultiValue, "balancing_node", translate("Load balancing node list"), translate("Load balancing node list, <a target='_blank' href='https://xtls.github.io/config/routing.html#balancerobject'>document</a>"))
o:depends({ [_n("node_add_mode")] = "manual" }) o:depends({ node_add_mode = "manual" })
o.widget = "checkbox" o.widget = "checkbox"
o.template = m:template_path("/cbi/nodes_multivalue") o.template = m:template_path("/cbi/nodes_multivalue")
o.group = {} o.group = {}
@@ -140,16 +134,16 @@ if load_balancing_options then -- [[ Load balancing Start ]]
end end
end end
o = s:option(MultiValue, _n("node_group"), translate("Select Group")) o = s:option(MultiValue, "node_group", translate("Select Group"))
o:depends({ [_n("node_add_mode")] = "batch" }) o:depends({ node_add_mode = "batch" })
o.widget = "checkbox" o.widget = "checkbox"
o:value("default", translate("default")) o:value("default", translate("default"))
for k, v in pairs(groups) do for k, v in pairs(groups) do
o:value(api.UrlEncode(k), k) o:value(api.UrlEncode(k), k)
end end
o = s:option(Value, _n("node_match_rule"), translate("Node Matching Rules")) o = s:option(Value, "node_match_rule", translate("Node Matching Rules"))
o:depends({ [_n("node_add_mode")] = "batch" }) o:depends({ node_add_mode = "batch" })
local descrStr = "Example: <code>^A && B && !C && D$</code><br>" local descrStr = "Example: <code>^A && B && !C && D$</code><br>"
descrStr = descrStr .. "This means the node remark must start with A (^), include B, exclude C (!), and end with D ($).<br>" descrStr = descrStr .. "This means the node remark must start with A (^), include B, exclude C (!), and end with D ($).<br>"
descrStr = descrStr .. "Conditions are joined by <code>&&</code> (AND), and their order does not affect the result.<br>" descrStr = descrStr .. "Conditions are joined by <code>&&</code> (AND), and their order does not affect the result.<br>"
@@ -157,8 +151,8 @@ if load_balancing_options then -- [[ Load balancing Start ]]
descrStr = descrStr .. "Example: <code>A && B || C && D</code> means (A AND B) OR (C AND D)." descrStr = descrStr .. "Example: <code>A && B || C && D</code> means (A AND B) OR (C AND D)."
o.description = translate(descrStr) o.description = translate(descrStr)
o = s:option(ListValue, _n("balancingStrategy"), translate("Balancing Strategy")) o = s:option(ListValue, "balancingStrategy", translate("Balancing Strategy"))
o:depends({ [_n("protocol")] = "_balancing" }) o:depends({ protocol = "_balancing" })
o:value("random") o:value("random")
o:value("roundRobin") o:value("roundRobin")
o:value("leastPing") o:value("leastPing")
@@ -166,11 +160,11 @@ if load_balancing_options then -- [[ Load balancing Start ]]
o.default = "random" o.default = "random"
-- Fallback Node -- Fallback Node
o = s:option(ListValue, _n("fallback_node"), translate("Fallback Node")) o = s:option(ListValue, "fallback_node", translate("Fallback Node"))
o.group = {"",""} o.group = {"",""}
o:value("", translate("Close(Not use)")) o:value("", translate("Close(Not use)"))
o:value("_direct", translate("Direct Connection")) o:value("_direct", translate("Direct Connection"))
o:depends({ [_n("protocol")] = "_balancing" }) o:depends({ protocol = "_balancing" })
o.template = m:template_path("/cbi/nodes_listvalue") o.template = m:template_path("/cbi/nodes_listvalue")
-- Maximum number of fallback nesting layers -- Maximum number of fallback nesting layers
local MAX_FALLBACK_DEPTH = 3 local MAX_FALLBACK_DEPTH = 3
@@ -234,11 +228,11 @@ if load_balancing_options then -- [[ Load balancing Start ]]
end end
end end
o = s:option(Flag, _n("useCustomProbeUrl"), translate("Use Custom Probe URL"), translate("By default the built-in probe URL will be used, enable this option to use a custom probe URL.")) o = s:option(Flag, "useCustomProbeUrl", translate("Use Custom Probe URL"), translate("By default the built-in probe URL will be used, enable this option to use a custom probe URL."))
o:depends({ [_n("protocol")] = "_balancing" }) o:depends({ protocol = "_balancing" })
o = s:option(Value, _n("probeUrl"), translate("Probe URL")) o = s:option(Value, "probeUrl", translate("Probe URL"))
o:depends({ [_n("useCustomProbeUrl")] = true }) o:depends({ useCustomProbeUrl = true })
o:value("https://cp.cloudflare.com/", "Cloudflare") o:value("https://cp.cloudflare.com/", "Cloudflare")
o:value("https://www.gstatic.com/generate_204", "Gstatic") o:value("https://www.gstatic.com/generate_204", "Gstatic")
o:value("https://www.google.com/generate_204", "Google") o:value("https://www.google.com/generate_204", "Google")
@@ -249,23 +243,23 @@ if load_balancing_options then -- [[ Load balancing Start ]]
o.default = o.keylist[3] o.default = o.keylist[3]
o.description = translate("The URL used to detect the connection status.") o.description = translate("The URL used to detect the connection status.")
o = s:option(Value, _n("probeInterval"), translate("Probe Interval")) o = s:option(Value, "probeInterval", translate("Probe Interval"))
o:depends({ [_n("protocol")] = "_balancing" }) o:depends({ protocol = "_balancing" })
o.default = "1m" o.default = "1m"
o.placeholder = "1m" o.placeholder = "1m"
o.description = translate("The interval between initiating probes.") .. "<br>" .. o.description = translate("The interval between initiating probes.") .. "<br>" ..
translate("The time format is numbers + units, such as '10s', '2h45m', and the supported time units are <code>s</code>, <code>m</code>, <code>h</code>, which correspond to seconds, minutes, and hours, respectively.") .. "<br>" .. translate("The time format is numbers + units, such as '10s', '2h45m', and the supported time units are <code>s</code>, <code>m</code>, <code>h</code>, which correspond to seconds, minutes, and hours, respectively.") .. "<br>" ..
translate("When the unit is not filled in, it defaults to seconds.") translate("When the unit is not filled in, it defaults to seconds.")
o = s:option(Value, _n("expected"), translate("Preferred Node Count")) o = s:option(Value, "expected", translate("Preferred Node Count"))
o:depends({ [_n("balancingStrategy")] = "leastLoad" }) o:depends({ balancingStrategy = "leastLoad" })
o.datatype = "uinteger" o.datatype = "uinteger"
o.default = "2" o.default = "2"
o.placeholder = "2" o.placeholder = "2"
o.description = translate("The load balancer selects the optimal number of nodes, and traffic is randomly distributed among them.") o.description = translate("The load balancer selects the optimal number of nodes, and traffic is randomly distributed among them.")
o = s:option(Value, _n("tolerance"), translate("Failure Tolerance (%)")) o = s:option(Value, "tolerance", translate("Failure Tolerance (%)"))
o:depends({ [_n("balancingStrategy")] = "leastLoad" }) o:depends({ balancingStrategy = "leastLoad" })
o.datatype = "uinteger" o.datatype = "uinteger"
o.default = "10" o.default = "10"
o.placeholder = "10" o.placeholder = "10"
@@ -273,8 +267,8 @@ if load_balancing_options then -- [[ Load balancing Start ]]
end -- [[ Load balancing End ]] end -- [[ Load balancing End ]]
if load_iface_options then -- [[ Custom Interface Start ]] if load_iface_options then -- [[ Custom Interface Start ]]
o = s:option(Value, _n("iface"), translate("Interface")) o = s:option(Value, "iface", translate("Interface"))
o:depends({ [_n("protocol")] = "_iface" }) o:depends({ protocol = "_iface" })
for _, d in ipairs(netdev_list) do for _, d in ipairs(netdev_list) do
o:value(d.name, d.label) o:value(d.name, d.label)
end end
@@ -284,73 +278,72 @@ end -- [[ Custom Interface End ]]
-- [[ Normal single node Start ]] -- [[ Normal single node Start ]]
if load_normal_options then if load_normal_options then
o = s:option(Value, _n("address"), translate("Address (Support Domain Name)")) o = s:option(Value, "address", translate("Address (Support Domain Name)"))
o = s:option(Value, _n("port"), translate("Port")) o = s:option(Value, "port", translate("Port"))
o.datatype = "port" o.datatype = "port"
o = s:option(Value, _n("uuid"), translate("ID")) o = s:option(Value, "uuid", translate("ID"))
o.password = true o.password = true
o:depends({ [_n("protocol")] = "vmess" }) o:depends({ protocol = "vmess" })
o:depends({ [_n("protocol")] = "vless" }) o:depends({ protocol = "vless" })
o = s:option(Value, _n("username"), translate("Username")) o = s:option(Value, "username", translate("Username"))
o:depends({ [_n("protocol")] = "http" }) o:depends({ protocol = "http" })
o:depends({ [_n("protocol")] = "socks" }) o:depends({ protocol = "socks" })
o = s:option(Value, _n("password"), translate("Password")) o = s:option(Value, "password", translate("Password"))
o.password = true o.password = true
o:depends({ [_n("protocol")] = "http" }) o:depends({ protocol = "http" })
o:depends({ [_n("protocol")] = "socks" }) o:depends({ protocol = "socks" })
o:depends({ [_n("protocol")] = "shadowsocks" }) o:depends({ protocol = "shadowsocks" })
o:depends({ [_n("protocol")] = "trojan" }) o:depends({ protocol = "trojan" })
o = s:option(ListValue, _n("security"), translate("Encrypt Method")) o = s:option(ListValue, "security", translate("Encrypt Method"))
for a, t in ipairs(security_list) do o:value(t) end for a, t in ipairs(security_list) do o:value(t) end
o:depends({ [_n("protocol")] = "vmess" }) o:depends({ protocol = "vmess" })
o = s:option(Value, _n("encryption"), translate("Encrypt Method") .. " (encryption)") o = s:option(Value, "encryption", translate("Encrypt Method") .. " (encryption)")
o.default = "none" o.default = "none"
o.placeholder = "none" o.placeholder = "none"
o:depends({ [_n("protocol")] = "vless" }) o:depends({ protocol = "vless" })
o.validate = function(self, value) o.validate = function(self, value)
value = api.trim(value) value = api.trim(value)
return (value == "" and "none" or value) return (value == "" and "none" or value)
end end
o = s:option(ListValue, _n("ss_method"), translate("Encrypt Method")) o = s:option(ListValue, "ss_method", translate("Encrypt Method"))
o.rewrite_option = "method"
for a, t in ipairs(ss_method_list) do o:value(t) end for a, t in ipairs(ss_method_list) do o:value(t) end
o:depends({ [_n("protocol")] = "shadowsocks" }) o:depends({ protocol = "shadowsocks" })
o = s:option(ListValue, _n("flow"), translate("flow")) o = s:option(ListValue, "flow", translate("flow"))
o.default = "" o.default = ""
o:value("", translate("Disable")) o:value("", translate("Disable"))
o:value("xtls-rprx-vision") o:value("xtls-rprx-vision")
o:value("xtls-rprx-vision-udp443") o:value("xtls-rprx-vision-udp443")
o:depends({ [_n("protocol")] = "vless" }) o:depends({ protocol = "vless" })
---- [[hysteria2]] ---- [[hysteria2]]
o = s:option(Value, _n("hysteria2_hop"), translate("Port hopping range")) o = s:option(Value, "hysteria2_hop", translate("Port hopping range"))
o.description = translate("Format as 1000:2000 or 1000-2000 Multiple groups are separated by commas (,).") o.description = translate("Format as 1000:2000 or 1000-2000 Multiple groups are separated by commas (,).")
o:depends({ [_n("protocol")] = "hysteria2", [_n("hysteria2_realms")] = false }) o:depends({ protocol = "hysteria2", hysteria2_realms = false })
o = s:option(Value, _n("hysteria2_hop_interval"), translate("Hop Interval(second)"), translate("Supports a fixed value or a random range (e.g., 30, 5-30), minimum 5.")) o = s:option(Value, "hysteria2_hop_interval", translate("Hop Interval(second)"), translate("Supports a fixed value or a random range (e.g., 30, 5-30), minimum 5."))
o.datatype = "or(uinteger,portrange)" o.datatype = "or(uinteger,portrange)"
o.placeholder = "30" o.placeholder = "30"
o.default = "30" o.default = "30"
o:depends({ [_n("protocol")] = "hysteria2", [_n("hysteria2_realms")] = false }) o:depends({ protocol = "hysteria2", hysteria2_realms = false })
o = s:option(Flag, _n("hysteria2_realms"), translate("Realms")) o = s:option(Flag, "hysteria2_realms", translate("Realms"))
o.default = "0" o.default = "0"
if api.compare_versions(xray_version, ">", "26.5.9") then if api.compare_versions(xray_version, ">", "26.5.9") then
o:depends({ [_n("protocol")] = "hysteria2"}) o:depends({ protocol = "hysteria2"})
else else
o:depends({ [_n("protocol")] = "__hide"}) o:depends({ protocol = "__hide"})
end end
o = s:option(Value, _n("hysteria2_realm_url"), translate("Realm URL"), translate("Example:") .. "realm://public@realm.hy2.io/your-realm-name") o = s:option(Value, "hysteria2_realm_url", translate("Realm URL"), translate("Example:") .. "realm://public@realm.hy2.io/your-realm-name")
o:depends({ [_n("hysteria2_realms")] = "1" }) o:depends({ hysteria2_realms = "1" })
o.validate = function(self, value) o.validate = function(self, value)
value = api.trim(value) value = api.trim(value)
local realm = api.parse_realm_uri(value) local realm = api.parse_realm_uri(value)
@@ -358,73 +351,73 @@ o.validate = function(self, value)
return nil, translate("Invalid Realm URL.") return nil, translate("Invalid Realm URL.")
end end
o = s:option(DynamicList, _n("hysteria2_realm_stun"), translate("Realm STUN")) o = s:option(DynamicList, "hysteria2_realm_stun", translate("Realm STUN"))
o.default = { "stun.sip.us:3478", "stun.nextcloud.com:3478", "global.stun.twilio.com:3478" } o.default = { "stun.sip.us:3478", "stun.nextcloud.com:3478", "global.stun.twilio.com:3478" }
o:depends({ [_n("hysteria2_realms")] = "1" }) o:depends({ hysteria2_realms = "1" })
o = s:option(Value, _n("hysteria2_auth_password"), translate("Auth Password")) o = s:option(Value, "hysteria2_auth_password", translate("Auth Password"))
o.password = true o.password = true
o:depends({ [_n("protocol")] = "hysteria2"}) o:depends({ protocol = "hysteria2"})
o = s:option(ListValue, _n("hysteria2_obfs_type"), translate("Obfs Type")) o = s:option(ListValue, "hysteria2_obfs_type", translate("Obfs Type"))
o:value("", translate("Disable")) o:value("", translate("Disable"))
o:value("salamander") o:value("salamander")
o:value("gecko") o:value("gecko")
o:depends({ [_n("protocol")] = "hysteria2" }) o:depends({ protocol = "hysteria2" })
o = s:option(Value, _n("hysteria2_obfs_password"), translate("Obfs Password")) o = s:option(Value, "hysteria2_obfs_password", translate("Obfs Password"))
o:depends({ [_n("hysteria2_obfs_type")] = "salamander" }) o:depends({ hysteria2_obfs_type = "salamander" })
o:depends({ [_n("hysteria2_obfs_type")] = "gecko" }) o:depends({ hysteria2_obfs_type = "gecko" })
o = s:option(Value, _n("hysteria2_obfs_MinPacketSize"), translate("Gecko Packet Size (min)")) o = s:option(Value, "hysteria2_obfs_MinPacketSize", translate("Gecko Packet Size (min)"))
o.datatype = "uinteger" o.datatype = "uinteger"
o.placeholder = "512" o.placeholder = "512"
o.default = "512" o.default = "512"
o:depends({ [_n("hysteria2_obfs_type")] = "gecko" }) o:depends({ hysteria2_obfs_type = "gecko" })
o = s:option(Value, _n("hysteria2_obfs_MaxPacketSize"), translate("Gecko Packet Size (max)")) o = s:option(Value, "hysteria2_obfs_MaxPacketSize", translate("Gecko Packet Size (max)"))
o.datatype = "uinteger" o.datatype = "uinteger"
o.placeholder = "1200" o.placeholder = "1200"
o.default = "1200" o.default = "1200"
o:depends({ [_n("hysteria2_obfs_type")] = "gecko" }) o:depends({ hysteria2_obfs_type = "gecko" })
o = s:option(Value, _n("hysteria2_up_mbps"), translate("Max upload Mbps")) o = s:option(Value, "hysteria2_up_mbps", translate("Max upload Mbps"))
o:depends({ [_n("protocol")] = "hysteria2" }) o:depends({ protocol = "hysteria2" })
o = s:option(Value, _n("hysteria2_down_mbps"), translate("Max download Mbps")) o = s:option(Value, "hysteria2_down_mbps", translate("Max download Mbps"))
o:depends({ [_n("protocol")] = "hysteria2" }) o:depends({ protocol = "hysteria2" })
o = s:option(Value, _n("hysteria2_idle_timeout"), translate("Idle Timeout"), translate("Units:seconds") .. " (4~120)") o = s:option(Value, "hysteria2_idle_timeout", translate("Idle Timeout"), translate("Units:seconds") .. " (4~120)")
o.datatype = "range(4,120)" o.datatype = "range(4,120)"
o:depends({ [_n("protocol")] = "hysteria2"}) o:depends({ protocol = "hysteria2"})
o = s:option(Value, _n("hysteria2_keep_alive_period"), translate("QUIC KeepAlive interval"), translate("Units:seconds") .. " (2~60)") o = s:option(Value, "hysteria2_keep_alive_period", translate("QUIC KeepAlive interval"), translate("Units:seconds") .. " (2~60)")
o.datatype = "range(2,60)" o.datatype = "range(2,60)"
o:depends({ [_n("protocol")] = "hysteria2"}) o:depends({ protocol = "hysteria2"})
o = s:option(Flag, _n("hysteria2_disable_mtu_discovery"), translate("Disable MTU detection")) o = s:option(Flag, "hysteria2_disable_mtu_discovery", translate("Disable MTU detection"))
o.default = "0" o.default = "0"
o:depends({ [_n("protocol")] = "hysteria2"}) o:depends({ protocol = "hysteria2"})
---- [[hysteria2 end]] ---- [[hysteria2 end]]
o = s:option(Flag, _n("tls"), translate("TLS")) o = s:option(Flag, "tls", translate("TLS"))
o.default = 0 o.default = 0
o:depends({ [_n("protocol")] = "vmess" }) o:depends({ protocol = "vmess" })
o:depends({ [_n("protocol")] = "vless" }) o:depends({ protocol = "vless" })
o:depends({ [_n("protocol")] = "http" }) o:depends({ protocol = "http" })
o:depends({ [_n("protocol")] = "socks" }) o:depends({ protocol = "socks" })
o:depends({ [_n("protocol")] = "trojan" }) o:depends({ protocol = "trojan" })
o:depends({ [_n("protocol")] = "shadowsocks" }) o:depends({ protocol = "shadowsocks" })
o = s:option(Flag, _n("reality"), translate("REALITY")) o = s:option(Flag, "reality", translate("REALITY"))
o.default = 0 o.default = 0
o:depends({ [_n("tls")] = true, [_n("transport")] = "raw" }) o:depends({ tls = true, transport = "raw" })
o:depends({ [_n("tls")] = true, [_n("transport")] = "ws" }) o:depends({ tls = true, transport = "ws" })
o:depends({ [_n("tls")] = true, [_n("transport")] = "grpc" }) o:depends({ tls = true, transport = "grpc" })
o:depends({ [_n("tls")] = true, [_n("transport")] = "httpupgrade" }) o:depends({ tls = true, transport = "httpupgrade" })
o:depends({ [_n("tls")] = true, [_n("transport")] = "xhttp" }) o:depends({ tls = true, transport = "xhttp" })
o = s:option(ListValue, _n("alpn"), translate("alpn")) o = s:option(ListValue, "alpn", translate("alpn"))
o.default = "default" o.default = "default"
o:value("default", translate("Default")) o:value("default", translate("Default"))
o:value("h3") o:value("h3")
@@ -433,72 +426,72 @@ o:value("h3,h2")
o:value("http/1.1") o:value("http/1.1")
o:value("h2,http/1.1") o:value("h2,http/1.1")
o:value("h3,h2,http/1.1") o:value("h3,h2,http/1.1")
o:depends({ [_n("tls")] = true, [_n("reality")] = false }) o:depends({ tls = true, reality = false })
-- o = s:option(Value, _n("minversion"), translate("minversion")) -- o = s:option(Value, "minversion", translate("minversion"))
-- o.default = "1.3" -- o.default = "1.3"
-- o:value("1.3") -- o:value("1.3")
-- o:depends({ [_n("tls")] = true }) -- o:depends({ tls = true })
o = s:option(Value, _n("tls_serverName"), "SNI " .. translate("Domain")) o = s:option(Value, "tls_serverName", "SNI " .. translate("Domain"))
o:depends({ [_n("tls")] = true }) o:depends({ tls = true })
o:depends({ [_n("protocol")] = "hysteria2" }) o:depends({ protocol = "hysteria2" })
o = s:option(Value, _n("tls_pinSHA256"), translate("TLS Chain Fingerprint (SHA256)")) o = s:option(Value, "tls_pinSHA256", translate("TLS Chain Fingerprint (SHA256)"))
o:depends({ [_n("tls")] = true, [_n("reality")] = false }) o:depends({ tls = true, reality = false })
o:depends({ [_n("protocol")] = "hysteria2" }) o:depends({ protocol = "hysteria2" })
o.description = translate("Once set, connects only when the server’s chain fingerprint matches.") .. o.description = translate("Once set, connects only when the server’s chain fingerprint matches.") ..
string.format("<a href='javascript:void(0)' onclick='javascript:fetchCertSha256(this)'>%s</a>", "→ " .. translate("Fetch Manually")) string.format("<a href='javascript:void(0)' onclick='javascript:fetchCertSha256(this)'>%s</a>", "→ " .. translate("Fetch Manually"))
o = s:option(Value, _n("tls_CertByName"), translate("TLS Certificate Name (CertName)"), translate("TLS is used to verify the leaf certificate name.")) o = s:option(Value, "tls_CertByName", translate("TLS Certificate Name (CertName)"), translate("TLS is used to verify the leaf certificate name."))
o:depends({ [_n("tls")] = true, [_n("reality")] = false }) o:depends({ tls = true, reality = false })
o:depends({ [_n("protocol")] = "hysteria2" }) o:depends({ protocol = "hysteria2" })
o = s:option(Flag, _n("tls_certificate"), translate("TLS Certificate (PEM)")) o = s:option(Flag, "tls_certificate", translate("TLS Certificate (PEM)"))
o.default = "0" o.default = "0"
o:depends({ [_n("tls")] = true, [_n("reality")] = false }) o:depends({ tls = true, reality = false })
o:depends({ [_n("protocol")] = "hysteria2" }) o:depends({ protocol = "hysteria2" })
o = s:option(TextValue, _n("tls_certificate_pem"), " ", translate("Full certificate (chain), PEM format.")) o = s:option(TextValue, "tls_certificate_pem", " ", translate("Full certificate (chain), PEM format."))
o.default = "" o.default = ""
o.rows = 5 o.rows = 5
o.wrap = "off" o.wrap = "off"
o:depends({ [_n("tls_certificate")] = true }) o:depends({ tls_certificate = true })
o.validate = function(self, value) o.validate = function(self, value)
value = api.trim(value):gsub("\r\n", "\n"):gsub("[ \t]*\n[ \t]*", "\n"):gsub("\n+", "\n") value = api.trim(value):gsub("\r\n", "\n"):gsub("[ \t]*\n[ \t]*", "\n"):gsub("\n+", "\n")
return value return value
end end
o = s:option(Flag, _n("ech"), translate("ECH")) o = s:option(Flag, "ech", translate("ECH"))
o.default = "0" o.default = "0"
o:depends({ [_n("tls")] = true, [_n("reality")] = false }) o:depends({ tls = true, reality = false })
o:depends({ [_n("protocol")] = "hysteria2", [_n("hysteria2_realms")] = false }) o:depends({ protocol = "hysteria2", hysteria2_realms = false })
o = s:option(TextValue, _n("ech_config"), translate("ECH Config")) o = s:option(TextValue, "ech_config", translate("ECH Config"))
o.default = "" o.default = ""
o.rows = 5 o.rows = 5
o.wrap = "soft" o.wrap = "soft"
o:depends({ [_n("ech")] = true }) o:depends({ ech = true })
o.validate = function(self, value) o.validate = function(self, value)
return api.trim(value:gsub("[\r\n]", "")) return api.trim(value:gsub("[\r\n]", ""))
end end
-- [[ REALITY ]] -- -- [[ REALITY ]] --
o = s:option(Value, _n("reality_publicKey"), translate("Public Key")) o = s:option(Value, "reality_publicKey", translate("Public Key"))
o:depends({ [_n("tls")] = true, [_n("reality")] = true }) o:depends({ tls = true, reality = true })
o = s:option(Value, _n("reality_shortId"), translate("Short Id")) o = s:option(Value, "reality_shortId", translate("Short Id"))
o:depends({ [_n("tls")] = true, [_n("reality")] = true }) o:depends({ tls = true, reality = true })
o = s:option(Value, _n("reality_spiderX"), translate("Spider X")) o = s:option(Value, "reality_spiderX", translate("Spider X"))
o.placeholder = "/" o.placeholder = "/"
o:depends({ [_n("tls")] = true, [_n("reality")] = true }) o:depends({ tls = true, reality = true })
o = s:option(Flag, _n("utls"), translate("uTLS")) o = s:option(Flag, "utls", translate("uTLS"))
o.default = "0" o.default = "0"
o:depends({ [_n("tls")] = true, [_n("reality")] = false }) o:depends({ tls = true, reality = false })
o = s:option(ListValue, _n("fingerprint"), translate("Finger Print")) o = s:option(ListValue, "fingerprint", translate("Finger Print"))
o:value("chrome") o:value("chrome")
o:value("firefox") o:value("firefox")
o:value("edge") o:value("edge")
@@ -511,14 +504,14 @@ o:value("random")
o:value("randomized") o:value("randomized")
o:value("unsafe") o:value("unsafe")
o.default = "chrome" o.default = "chrome"
o:depends({ [_n("tls")] = true, [_n("utls")] = true }) o:depends({ tls = true, utls = true })
o:depends({ [_n("tls")] = true, [_n("reality")] = true }) o:depends({ tls = true, reality = true })
o = s:option(Flag, _n("use_mldsa65Verify"), translate("ML-DSA-65")) o = s:option(Flag, "use_mldsa65Verify", translate("ML-DSA-65"))
o.default = "0" o.default = "0"
o:depends({ [_n("tls")] = true, [_n("reality")] = true }) o:depends({ tls = true, reality = true })
o = s:option(DynamicList, _n("cipherSuites"), translate("Cipher Suites"), '<a href="https://go.dev/src/crypto/tls/cipher_suites.go#L44" target="_blank">***</a>' .. " " .. translate("Configures the list of supported cipher suites.")) o = s:option(DynamicList, "cipherSuites", translate("Cipher Suites"), '<a href="https://go.dev/src/crypto/tls/cipher_suites.go#L44" target="_blank">***</a>' .. " " .. translate("Configures the list of supported cipher suites."))
o:value("TLS_AES_128_GCM_SHA256") o:value("TLS_AES_128_GCM_SHA256")
o:value("TLS_AES_256_GCM_SHA384") o:value("TLS_AES_256_GCM_SHA384")
o:value("TLS_CHACHA20_POLY1305_SHA256") o:value("TLS_CHACHA20_POLY1305_SHA256")
@@ -532,154 +525,154 @@ o:value("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256")
o:value("TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384") o:value("TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384")
o:value("TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256") o:value("TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256")
o:value("TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256") o:value("TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256")
o:depends({ [_n("tls")] = true, [_n("reality")] = false }) o:depends({ tls = true, reality = false })
o = s:option(TextValue, _n("reality_mldsa65Verify"), "ML-DSA-65 " .. translate("Public key")) o = s:option(TextValue, "reality_mldsa65Verify", "ML-DSA-65 " .. translate("Public key"))
o.default = "" o.default = ""
o.rows = 5 o.rows = 5
o.wrap = "soft" o.wrap = "soft"
o:depends({ [_n("use_mldsa65Verify")] = true }) o:depends({ use_mldsa65Verify = true })
o.validate = function(self, value) o.validate = function(self, value)
return api.trim(value:gsub("[\r\n]", "")) return api.trim(value:gsub("[\r\n]", ""))
end end
o = s:option(ListValue, _n("transport"), translate("Transport")) o = s:option(ListValue, "transport", translate("Transport"))
o:value("raw", "RAW (TCP)") o:value("raw", "RAW (TCP)")
o:value("mkcp", "mKCP") o:value("mkcp", "mKCP")
o:value("ws", "WebSocket") o:value("ws", "WebSocket")
o:value("grpc", "gRPC") o:value("grpc", "gRPC")
o:value("httpupgrade", "HttpUpgrade") o:value("httpupgrade", "HttpUpgrade")
o:value("xhttp", "XHTTP") o:value("xhttp", "XHTTP")
o:depends({ [_n("protocol")] = "vmess" }) o:depends({ protocol = "vmess" })
o:depends({ [_n("protocol")] = "vless" }) o:depends({ protocol = "vless" })
o:depends({ [_n("protocol")] = "socks" }) o:depends({ protocol = "socks" })
o:depends({ [_n("protocol")] = "shadowsocks" }) o:depends({ protocol = "shadowsocks" })
o:depends({ [_n("protocol")] = "trojan" }) o:depends({ protocol = "trojan" })
o = s:option(Value, _n("wireguard_public_key"), translate("Public Key")) o = s:option(Value, "wireguard_public_key", translate("Public Key"))
o:depends({ [_n("protocol")] = "wireguard" }) o:depends({ protocol = "wireguard" })
o = s:option(Value, _n("wireguard_secret_key"), translate("Private Key")) o = s:option(Value, "wireguard_secret_key", translate("Private Key"))
o:depends({ [_n("protocol")] = "wireguard" }) o:depends({ protocol = "wireguard" })
o = s:option(Value, _n("wireguard_preSharedKey"), translate("Pre shared key")) o = s:option(Value, "wireguard_preSharedKey", translate("Pre shared key"))
o:depends({ [_n("protocol")] = "wireguard" }) o:depends({ protocol = "wireguard" })
o = s:option(DynamicList, _n("wireguard_local_address"), translate("Local Address")) o = s:option(DynamicList, "wireguard_local_address", translate("Local Address"))
o:depends({ [_n("protocol")] = "wireguard" }) o:depends({ protocol = "wireguard" })
o = s:option(Value, _n("wireguard_mtu"), translate("MTU")) o = s:option(Value, "wireguard_mtu", translate("MTU"))
o.default = "1420" o.default = "1420"
o:depends({ [_n("protocol")] = "wireguard" }) o:depends({ protocol = "wireguard" })
if api.compare_versions(xray_version, ">=", "1.8.0") then if api.compare_versions(xray_version, ">=", "1.8.0") then
o = s:option(Value, _n("wireguard_reserved"), translate("Reserved"), translate("Decimal numbers separated by \",\" or Base64-encoded strings.")) o = s:option(Value, "wireguard_reserved", translate("Reserved"), translate("Decimal numbers separated by \",\" or Base64-encoded strings."))
o:depends({ [_n("protocol")] = "wireguard" }) o:depends({ protocol = "wireguard" })
end end
o = s:option(Value, _n("wireguard_keepAlive"), translate("Keep Alive")) o = s:option(Value, "wireguard_keepAlive", translate("Keep Alive"))
o.default = "0" o.default = "0"
o:depends({ [_n("protocol")] = "wireguard" }) o:depends({ protocol = "wireguard" })
-- [[ RAW ]]-- -- [[ RAW ]]--
o = s:option(ListValue, _n("tcp_guise"), translate("Camouflage Type")) o = s:option(ListValue, "tcp_guise", translate("Camouflage Type"))
o:value("none", "none") o:value("none", "none")
o:value("http", "http") o:value("http", "http")
o:depends({ [_n("transport")] = "raw" }) o:depends({ transport = "raw" })
o = s:option(DynamicList, _n("tcp_guise_http_host"), translate("HTTP Host")) o = s:option(DynamicList, "tcp_guise_http_host", translate("HTTP Host"))
o:depends({ [_n("tcp_guise")] = "http" }) o:depends({ tcp_guise = "http" })
o = s:option(DynamicList, _n("tcp_guise_http_path"), translate("HTTP Path")) o = s:option(DynamicList, "tcp_guise_http_path", translate("HTTP Path"))
o.placeholder = "/" o.placeholder = "/"
o:depends({ [_n("tcp_guise")] = "http" }) o:depends({ tcp_guise = "http" })
-- [[ mKCP ]]-- -- [[ mKCP ]]--
o = s:option(ListValue, _n("mkcp_guise"), translate("Camouflage Type"), translate('<br />none: default, no masquerade, data sent is packets with no characteristics.<br />srtp: disguised as an SRTP packet, it will be recognized as video call data (such as FaceTime).<br />utp: packets disguised as uTP will be recognized as bittorrent downloaded data.<br />wechat-video: packets disguised as WeChat video calls.<br />dtls: disguised as DTLS 1.2 packet.<br />wireguard: disguised as a WireGuard packet. (not really WireGuard protocol)<br />dns: Disguising traffic as DNS requests.')) o = s:option(ListValue, "mkcp_guise", translate("Camouflage Type"), translate('<br />none: default, no masquerade, data sent is packets with no characteristics.<br />srtp: disguised as an SRTP packet, it will be recognized as video call data (such as FaceTime).<br />utp: packets disguised as uTP will be recognized as bittorrent downloaded data.<br />wechat-video: packets disguised as WeChat video calls.<br />dtls: disguised as DTLS 1.2 packet.<br />wireguard: disguised as a WireGuard packet. (not really WireGuard protocol)<br />dns: Disguising traffic as DNS requests.'))
for a, t in ipairs(header_type_list) do o:value(t) end for a, t in ipairs(header_type_list) do o:value(t) end
o:depends({ [_n("transport")] = "mkcp" }) o:depends({ transport = "mkcp" })
o = s:option(Value, _n("mkcp_domain"), translate("Camouflage Domain"), translate("Use it together with the DNS disguised type. You can fill in any domain.")) o = s:option(Value, "mkcp_domain", translate("Camouflage Domain"), translate("Use it together with the DNS disguised type. You can fill in any domain."))
o:depends({ [_n("mkcp_guise")] = "dns" }) o:depends({ mkcp_guise = "dns" })
o = s:option(Value, _n("mkcp_mtu"), translate("KCP MTU")) o = s:option(Value, "mkcp_mtu", translate("KCP MTU"))
o.datatype = "uinteger" o.datatype = "uinteger"
o.default = 1350 o.default = 1350
o:depends({ [_n("transport")] = "mkcp" }) o:depends({ transport = "mkcp" })
o = s:option(Value, _n("mkcp_seed"), translate("KCP Seed")) o = s:option(Value, "mkcp_seed", translate("KCP Seed"))
o:depends({ [_n("transport")] = "mkcp" }) o:depends({ transport = "mkcp" })
-- [[ WebSocket ]]-- -- [[ WebSocket ]]--
o = s:option(Value, _n("ws_host"), translate("WebSocket Host")) o = s:option(Value, "ws_host", translate("WebSocket Host"))
o:depends({ [_n("transport")] = "ws" }) o:depends({ transport = "ws" })
o = s:option(Value, _n("ws_path"), translate("WebSocket Path")) o = s:option(Value, "ws_path", translate("WebSocket Path"))
o.placeholder = "/" o.placeholder = "/"
o:depends({ [_n("transport")] = "ws" }) o:depends({ transport = "ws" })
o = s:option(Value, _n("ws_heartbeatPeriod"), translate("HeartbeatPeriod(second)")) o = s:option(Value, "ws_heartbeatPeriod", translate("HeartbeatPeriod(second)"))
o.datatype = "integer" o.datatype = "integer"
o:depends({ [_n("transport")] = "ws" }) o:depends({ transport = "ws" })
-- [[ gRPC ]]-- -- [[ gRPC ]]--
o = s:option(Value, _n("grpc_serviceName"), "ServiceName") o = s:option(Value, "grpc_serviceName", "ServiceName")
o:depends({ [_n("transport")] = "grpc" }) o:depends({ transport = "grpc" })
o = s:option(ListValue, _n("grpc_mode"), "gRPC " .. translate("Transfer mode")) o = s:option(ListValue, "grpc_mode", "gRPC " .. translate("Transfer mode"))
o:value("gun") o:value("gun")
o:value("multi") o:value("multi")
o:depends({ [_n("transport")] = "grpc" }) o:depends({ transport = "grpc" })
o = s:option(Flag, _n("grpc_health_check"), translate("Health check")) o = s:option(Flag, "grpc_health_check", translate("Health check"))
o:depends({ [_n("transport")] = "grpc" }) o:depends({ transport = "grpc" })
o = s:option(Value, _n("grpc_idle_timeout"), translate("Idle timeout")) o = s:option(Value, "grpc_idle_timeout", translate("Idle timeout"))
o.default = "10" o.default = "10"
o:depends({ [_n("grpc_health_check")] = true }) o:depends({ grpc_health_check = true })
o = s:option(Value, _n("grpc_health_check_timeout"), translate("Health check timeout")) o = s:option(Value, "grpc_health_check_timeout", translate("Health check timeout"))
o.default = "20" o.default = "20"
o:depends({ [_n("grpc_health_check")] = true }) o:depends({ grpc_health_check = true })
o = s:option(Flag, _n("grpc_permit_without_stream"), translate("Permit without stream")) o = s:option(Flag, "grpc_permit_without_stream", translate("Permit without stream"))
o.default = "0" o.default = "0"
o:depends({ [_n("grpc_health_check")] = true }) o:depends({ grpc_health_check = true })
o = s:option(Value, _n("grpc_initial_windows_size"), translate("Initial Windows Size")) o = s:option(Value, "grpc_initial_windows_size", translate("Initial Windows Size"))
o.default = "0" o.default = "0"
o:depends({ [_n("transport")] = "grpc" }) o:depends({ transport = "grpc" })
-- [[ HttpUpgrade ]]-- -- [[ HttpUpgrade ]]--
o = s:option(Value, _n("httpupgrade_host"), translate("HttpUpgrade Host")) o = s:option(Value, "httpupgrade_host", translate("HttpUpgrade Host"))
o:depends({ [_n("transport")] = "httpupgrade" }) o:depends({ transport = "httpupgrade" })
o = s:option(Value, _n("httpupgrade_path"), translate("HttpUpgrade Path")) o = s:option(Value, "httpupgrade_path", translate("HttpUpgrade Path"))
o.placeholder = "/" o.placeholder = "/"
o:depends({ [_n("transport")] = "httpupgrade" }) o:depends({ transport = "httpupgrade" })
-- [[ XHTTP ]]-- -- [[ XHTTP ]]--
o = s:option(ListValue, _n("xhttp_mode"), "XHTTP " .. translate("Mode")) o = s:option(ListValue, "xhttp_mode", "XHTTP " .. translate("Mode"))
o:depends({ [_n("transport")] = "xhttp" }) o:depends({ transport = "xhttp" })
o.default = "auto" o.default = "auto"
o:value("auto") o:value("auto")
o:value("packet-up") o:value("packet-up")
o:value("stream-up") o:value("stream-up")
o:value("stream-one") o:value("stream-one")
o = s:option(Value, _n("xhttp_host"), translate("XHTTP Host")) o = s:option(Value, "xhttp_host", translate("XHTTP Host"))
o:depends({ [_n("transport")] = "xhttp" }) o:depends({ transport = "xhttp" })
o = s:option(Value, _n("xhttp_path"), translate("XHTTP Path")) o = s:option(Value, "xhttp_path", translate("XHTTP Path"))
o.placeholder = "/" o.placeholder = "/"
o:depends({ [_n("transport")] = "xhttp" }) o:depends({ transport = "xhttp" })
o = s:option(Flag, _n("use_xhttp_extra"), translate("XHTTP Extra")) o = s:option(Flag, "use_xhttp_extra", translate("XHTTP Extra"))
o.default = "0" o.default = "0"
o:depends({ [_n("transport")] = "xhttp" }) o:depends({ transport = "xhttp" })
o = s:option(TextValue, _n("xhttp_extra"), " ", translate("An XHttpObject in JSON format, used for sharing.")) o = s:option(TextValue, "xhttp_extra", " ", translate("An XHttpObject in JSON format, used for sharing."))
o:depends({ [_n("use_xhttp_extra")] = true }) o:depends({ use_xhttp_extra = true })
o.rows = 10 o.rows = 10
o.wrap = "off" o.wrap = "off"
o.datatype = "json" o.datatype = "json"
@@ -698,7 +691,7 @@ o.custom_cfgvalue = function(self, section, value)
end end
o.custom_write = function(self, section, value) o.custom_write = function(self, section, value)
m:set(section, "xhttp_extra", api.base64Encode(value) or "") m:set(section, "xhttp_extra", api.base64Encode(value) or "")
local success, data = pcall(jsonc.parse, value) local success, data = pcall(api.jsonc.parse, value)
if success and data then if success and data then
local address = (data.extra and data.extra.downloadSettings and data.extra.downloadSettings.address) local address = (data.extra and data.extra.downloadSettings and data.extra.downloadSettings.address)
or (data.downloadSettings and data.downloadSettings.address) or (data.downloadSettings and data.downloadSettings.address)
@@ -718,7 +711,7 @@ o.custom_remove = function(self, section, value)
end end
-- [[ User-Agent ]]-- -- [[ User-Agent ]]--
o = s:option(Value, _n("user_agent"), translate("User-Agent")) o = s:option(Value, "user_agent", translate("User-Agent"))
o.default = "" o.default = ""
o:value("", translate("default")) o:value("", translate("default"))
o:value("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36", "chrome") o:value("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36", "chrome")
@@ -727,44 +720,44 @@ o:value("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (K
o:value("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36 Edg/91.0.864.70", "edge") o:value("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36 Edg/91.0.864.70", "edge")
o:value("Go-http-client/1.1", "golang") o:value("Go-http-client/1.1", "golang")
o:value("curl/7.68.0", "curl") o:value("curl/7.68.0", "curl")
o:depends({ [_n("tcp_guise")] = "http" }) o:depends({ tcp_guise = "http" })
o:depends({ [_n("transport")] = "ws" }) o:depends({ transport = "ws" })
o:depends({ [_n("transport")] = "httpupgrade" }) o:depends({ transport = "httpupgrade" })
o:depends({ [_n("transport")] = "xhttp" }) o:depends({ transport = "xhttp" })
o:depends({ [_n("transport")] = "grpc" }) o:depends({ transport = "grpc" })
-- [[ Mux.Cool ]]-- -- [[ Mux.Cool ]]--
o = s:option(Flag, _n("mux"), "Mux", translate("Enable Mux.Cool")) o = s:option(Flag, "mux", "Mux", translate("Enable Mux.Cool"))
o:depends({ [_n("protocol")] = "vmess" }) o:depends({ protocol = "vmess" })
o:depends({ [_n("protocol")] = "vless", [_n("transport")] = "raw" }) o:depends({ protocol = "vless", transport = "raw" })
o:depends({ [_n("protocol")] = "vless", [_n("transport")] = "ws" }) o:depends({ protocol = "vless", transport = "ws" })
o:depends({ [_n("protocol")] = "vless", [_n("transport")] = "grpc" }) o:depends({ protocol = "vless", transport = "grpc" })
o:depends({ [_n("protocol")] = "vless", [_n("transport")] = "httpupgrade" }) o:depends({ protocol = "vless", transport = "httpupgrade" })
o:depends({ [_n("protocol")] = "http" }) o:depends({ protocol = "http" })
o:depends({ [_n("protocol")] = "socks" }) o:depends({ protocol = "socks" })
o:depends({ [_n("protocol")] = "shadowsocks" }) o:depends({ protocol = "shadowsocks" })
o:depends({ [_n("protocol")] = "trojan" }) o:depends({ protocol = "trojan" })
o = s:option(Value, _n("mux_concurrency"), translate("Mux concurrency")) o = s:option(Value, "mux_concurrency", translate("Mux concurrency"))
o.default = -1 o.default = -1
o:depends({ [_n("mux")] = true }) o:depends({ mux = true })
o = s:option(Value, _n("xudp_concurrency"), translate("XUDP Mux concurrency")) o = s:option(Value, "xudp_concurrency", translate("XUDP Mux concurrency"))
o.default = 8 o.default = 8
o:depends({ [_n("mux")] = true }) o:depends({ mux = true })
--[[FinalMask]] --[[FinalMask]]
o = s:option(Flag, _n("use_finalmask"), "FinalMask") o = s:option(Flag, "use_finalmask", "FinalMask")
o.default = "0" o.default = "0"
o:depends({ [_n("protocol")] = "vmess" }) o:depends({ protocol = "vmess" })
o:depends({ [_n("protocol")] = "vless" }) o:depends({ protocol = "vless" })
o:depends({ [_n("protocol")] = "trojan" }) o:depends({ protocol = "trojan" })
o:depends({ [_n("protocol")] = "shadowsocks" }) o:depends({ protocol = "shadowsocks" })
o:depends({ [_n("protocol")] = "wireguard" }) o:depends({ protocol = "wireguard" })
o:depends({ [_n("protocol")] = "hysteria2", [_n("hysteria2_realms")] = false }) o:depends({ protocol = "hysteria2", hysteria2_realms = false })
o = s:option(TextValue, _n("finalmask"), " ") o = s:option(TextValue, "finalmask", " ")
o:depends({ [_n("use_finalmask")] = true }) o:depends({ use_finalmask = true })
o.rows = 10 o.rows = 10
o.wrap = "off" o.wrap = "off"
o.description = translate("An FinalMaskObject in JSON format, used for sharing.") .. "<br>" .. o.description = translate("An FinalMaskObject in JSON format, used for sharing.") .. "<br>" ..
@@ -788,62 +781,62 @@ o.custom_write = function(self, section, value)
end end
--[[Fast Open]] --[[Fast Open]]
o = s:option(Flag, _n("tcp_fast_open"), "TCP " .. translate("Fast Open"), translate("Need node support required")) o = s:option(Flag, "tcp_fast_open", "TCP " .. translate("Fast Open"), translate("Need node support required"))
o.default = 0 o.default = 0
--[[tcpMptcp]] --[[tcpMptcp]]
o = s:option(Flag, _n("tcpMptcp"), "tcpMptcp", translate("Enable Multipath TCP, need to be enabled in both server and client configuration.")) o = s:option(Flag, "tcpMptcp", "tcpMptcp", translate("Enable Multipath TCP, need to be enabled in both server and client configuration."))
o.default = 0 o.default = 0
o = s:option(ListValue, _n("domain_resolver"), translate("Domain DNS Resolve"), translate("If the node address is a domain name, this DNS will be used for resolution.")) o = s:option(ListValue, "domain_resolver", translate("Domain DNS Resolve"), translate("If the node address is a domain name, this DNS will be used for resolution."))
o:value("", translate("Auto")) o:value("", translate("Auto"))
o:value("tcp", "TCP") o:value("tcp", "TCP")
o:value("udp", "UDP") o:value("udp", "UDP")
o:value("https", "HTTPS") o:value("https", "HTTPS")
o = s:option(Value, _n("domain_resolver_dns"), "DNS") o = s:option(Value, "domain_resolver_dns", "DNS")
o.datatype = "or(ipaddr,ipaddrport)" o.datatype = "or(ipaddr,ipaddrport)"
o:value("114.114.114.114") o:value("114.114.114.114")
o:value("223.5.5.5:53") o:value("223.5.5.5:53")
o.default = o.keylist[1] o.default = o.keylist[1]
o:depends({ [_n("domain_resolver")] = "tcp" }) o:depends({ domain_resolver = "tcp" })
o:depends({ [_n("domain_resolver")] = "udp" }) o:depends({ domain_resolver = "udp" })
o = s:option(Value, _n("domain_resolver_dns_https"), "DNS") o = s:option(Value, "domain_resolver_dns_https", "DNS")
o:value("https://120.53.53.53/dns-query", "DNSPod") o:value("https://120.53.53.53/dns-query", "DNSPod")
o:value("https://223.5.5.5/dns-query", "AliDNS") o:value("https://223.5.5.5/dns-query", "AliDNS")
o.default = o.keylist[1] o.default = o.keylist[1]
o:depends({ [_n("domain_resolver")] = "https" }) o:depends({ domain_resolver = "https" })
o = s:option(ListValue, _n("domain_strategy"), translate("Domain Strategy"), translate("If is domain name, The requested domain name will be resolved to IP before connect.")) o = s:option(ListValue, "domain_strategy", translate("Domain Strategy"), translate("If is domain name, The requested domain name will be resolved to IP before connect."))
o.default = "" o.default = ""
o:value("", translate("Auto")) o:value("", translate("Auto"))
o:value("UseIPv4", translate("IPv4 Only")) o:value("UseIPv4", translate("IPv4 Only"))
o:value("UseIPv6", translate("IPv6 Only")) o:value("UseIPv6", translate("IPv6 Only"))
o = s:option(Flag, _n("happy_eyeballs"), translate("Enable Happy Eyeballs"), translate("Attempts IPv4 and IPv6 simultaneously; automatically uses the faster connection.")) o = s:option(Flag, "happy_eyeballs", translate("Enable Happy Eyeballs"), translate("Attempts IPv4 and IPv6 simultaneously; automatically uses the faster connection."))
o.default = 0 o.default = 0
local protocols = s.fields[_n("protocol")].keylist local protocols = s.fields["protocol"].keylist
if #protocols > 0 then if #protocols > 0 then
for i, v in ipairs(protocols) do for i, v in ipairs(protocols) do
if not v:find("^_") then if not v:find("^_") then
local depends_condition = { [_n("protocol")] = v } local depends_condition = { protocol = v }
if v == "hysteria2" then if v == "hysteria2" then
depends_condition[_n("hysteria2_realms")] = false depends_condition["hysteria2_realms"] = false
end end
s.fields[_n("address")]:depends(depends_condition) s.fields["address"]:depends(depends_condition)
s.fields[_n("port")]:depends(depends_condition) s.fields["port"]:depends(depends_condition)
s.fields[_n("domain_resolver")]:depends(depends_condition) s.fields["domain_resolver"]:depends(depends_condition)
s.fields[_n("happy_eyeballs")]:depends(depends_condition) s.fields["happy_eyeballs"]:depends(depends_condition)
local strategy_depends = api.clone(depends_condition) local strategy_depends = api.clone(depends_condition)
strategy_depends[_n("happy_eyeballs")] = false strategy_depends["happy_eyeballs"] = false
s.fields[_n("domain_strategy")]:depends(strategy_depends) s.fields["domain_strategy"]:depends(strategy_depends)
if v ~= "hysteria2" then if v ~= "hysteria2" then
s.fields[_n("tcp_fast_open")]:depends({ [_n("protocol")] = v }) s.fields["tcp_fast_open"]:depends({ protocol = v })
s.fields[_n("tcpMptcp")]:depends({ [_n("protocol")] = v }) s.fields["tcpMptcp"]:depends({ protocol = v })
end end
end end
end end
@@ -852,7 +845,7 @@ end
-- [[ Normal single node End ]] -- [[ Normal single node End ]]
if not load_shunt_options then if not load_shunt_options then
o = s:option(ListValue, _n("chain_proxy"), translate("Chain Proxy")) o = s:option(ListValue, "chain_proxy", translate("Chain Proxy"))
o:value("", translate("Close(Not use)")) o:value("", translate("Close(Not use)"))
if not (load_iface_options or load_balancing_options) then if not (load_iface_options or load_balancing_options) then
-- Special node cannot be use pre-proxy. -- Special node cannot be use pre-proxy.
@@ -861,20 +854,20 @@ if not load_shunt_options then
end end
o:value("2", translate("Landing Node")) o:value("2", translate("Landing Node"))
o1 = s:option(ListValue, _n("preproxy_node"), translate("Preproxy Node"), translate("Only support a layer of proxy.")) o1 = s:option(ListValue, "preproxy_node", translate("Preproxy Node"), translate("Only support a layer of proxy."))
o1:depends({ [_n("chain_proxy")] = "1", [_n("hysteria2_realms")] = false }) o1:depends({ chain_proxy = "1", hysteria2_realms = false })
o1.template = m:template_path("/cbi/nodes_listvalue") o1.template = m:template_path("/cbi/nodes_listvalue")
o1.group = {} o1.group = {}
o3 = s:option(Value, _n("outbound_iface"), translate("Outbound Interface")) o3 = s:option(Value, "outbound_iface", translate("Outbound Interface"))
o3:depends({ [_n("chain_proxy")] = "3" }) o3:depends({ chain_proxy = "3" })
o3:value("", translate("All")) o3:value("", translate("All"))
for _, d in ipairs(netdev_list) do for _, d in ipairs(netdev_list) do
o3:value(d.name, d.label) o3:value(d.name, d.label)
end end
o2 = s:option(ListValue, _n("to_node"), translate("Landing Node"), translate("Only support a layer of proxy.")) o2 = s:option(ListValue, "to_node", translate("Landing Node"), translate("Only support a layer of proxy."))
o2:depends({ [_n("chain_proxy")] = "2", [_n("hysteria2_realms")] = false }) o2:depends({ chain_proxy = "2", hysteria2_realms = false })
o2.template = m:template_path("/cbi/nodes_listvalue") o2.template = m:template_path("/cbi/nodes_listvalue")
o2.group = {} o2.group = {}
@@ -895,12 +888,12 @@ if not load_shunt_options then
end end
end end
api.luci_types(arg[1], m, s, type_name, option_prefix) api.luci_types(s1, s)
if load_shunt_options then if load_shunt_options then
local current_node = m:get(arg[1]) or {} local current_node = m:get(arg[1]) or {}
local shunt_lua = loadfile("/usr/lib/lua/luci/model/cbi/passwall2/client/include/shunt_options.lua") local shunt_lua = loadfile("/usr/lib/lua/luci/model/cbi/passwall2/client/include/shunt_options.lua")
setfenv(shunt_lua, getfenv(1))(m, s, { setfenv(shunt_lua, getfenv(1))(m, s1, {
node_id = arg[1], node_id = arg[1],
node = current_node, node = current_node,
node_list = node_list, node_list = node_list,
File diff suppressed because it is too large. Load diff
@@ -1,24 +1,20 @@
local m, s = ...
if not api.is_finded("sslocal") then if not api.is_finded("sslocal") then
return return
end end
type_name = "SS-Rust"
-- [[ Shadowsocks Rust ]] -- [[ Shadowsocks Rust ]]
local m, s1 = ...
local type_name = "SS-Rust"
s.fields["type"]:value(type_name, "Shadowsocks Rust") s1.fields["type"]:value(type_name, "Shadowsocks Rust")
if s.val["type"] ~= type_name then if s1.val["type"] ~= type_name then
return return
end end
local option_prefix = "ssrust_" local s = NamedSection(m, arg[1], "server")
s.type_name = type_name
local function _n(name) s.option_prefix = "ssrust_"
return option_prefix .. name
end
local ssrust_encrypt_method_list = { local ssrust_encrypt_method_list = {
"none", "plain", "none", "plain",
@@ -26,35 +22,35 @@ local ssrust_encrypt_method_list = {
"2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha8-poly1305", "2022-blake3-chacha20-poly1305" "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha8-poly1305", "2022-blake3-chacha20-poly1305"
} }
o = s:option(Value, _n("address"), translate("Address (Support Domain Name)")) o = s:option(Value, "address", translate("Address (Support Domain Name)"))
o = s:option(Value, _n("port"), translate("Port")) o = s:option(Value, "port", translate("Port"))
o.datatype = "port" o.datatype = "port"
o = s:option(Value, _n("password"), translate("Password")) o = s:option(Value, "password", translate("Password"))
o.password = true o.password = true
o = s:option(Value, _n("method"), translate("Encrypt Method")) o = s:option(Value, "method", translate("Encrypt Method"))
for a, t in ipairs(ssrust_encrypt_method_list) do o:value(t) end for a, t in ipairs(ssrust_encrypt_method_list) do o:value(t) end
o = s:option(Value, _n("timeout"), translate("Connection Timeout")) o = s:option(Value, "timeout", translate("Connection Timeout"))
o.datatype = "uinteger" o.datatype = "uinteger"
o.default = 300 o.default = 300
o = s:option(Flag, _n("tcp_fast_open"), "TCP " .. translate("Fast Open"), translate("Need node support required")) o = s:option(Flag, "tcp_fast_open", "TCP " .. translate("Fast Open"), translate("Need node support required"))
o.default = 0 o.default = 0
o = s:option(Flag, _n("plugin_enabled"), translate("plugin")) o = s:option(Flag, "plugin_enabled", translate("plugin"))
o.default = 0 o.default = 0
o = s:option(Value, _n("plugin"), "SIP003 " .. translate("plugin"), translate("Supports custom SIP003 plugins, Make sure the plugin is installed.")) o = s:option(Value, "plugin", "SIP003 " .. translate("plugin"), translate("Supports custom SIP003 plugins, Make sure the plugin is installed."))
o.default = "none" o.default = "none"
o:value("none", translate("none")) o:value("none", translate("none"))
if api.is_finded("xray-plugin") then o:value("xray-plugin") end if api.is_finded("xray-plugin") then o:value("xray-plugin") end
if api.is_finded("v2ray-plugin") then o:value("v2ray-plugin") end if api.is_finded("v2ray-plugin") then o:value("v2ray-plugin") end
if api.is_finded("obfs-local") then o:value("obfs-local") end if api.is_finded("obfs-local") then o:value("obfs-local") end
if api.is_finded("shadow-tls") then o:value("shadow-tls") end if api.is_finded("shadow-tls") then o:value("shadow-tls") end
o:depends({ [_n("plugin_enabled")] = true }) o:depends({ plugin_enabled = true })
o.validate = function(self, value, t) o.validate = function(self, value, t)
if value and value ~= "" and value ~= "none" then if value and value ~= "" and value ~= "none" then
if not api.is_finded(value) then if not api.is_finded(value) then
@@ -66,7 +62,7 @@ o.validate = function(self, value, t)
return nil return nil
end end
o = s:option(Value, _n("plugin_opts"), translate("opts")) o = s:option(Value, "plugin_opts", translate("opts"))
o:depends({ [_n("plugin_enabled")] = true }) o:depends({ plugin_enabled = true })
api.luci_types(arg[1], m, s, type_name, option_prefix) api.luci_types(s1, s)
@@ -1,24 +1,20 @@
local m, s = ...
if not api.is_finded("ssr-local") then if not api.is_finded("ssr-local") then
return return
end end
type_name = "SSR"
-- [[ ShadowsocksR Libev ]] -- [[ ShadowsocksR Libev ]]
local m, s1 = ...
local type_name = "SSR"
s.fields["type"]:value(type_name, "ShadowsocksR Libev") s1.fields["type"]:value(type_name, "ShadowsocksR Libev")
if s.val["type"] ~= type_name then if s1.val["type"] ~= type_name then
return return
end end
local option_prefix = "ssr_" local s = NamedSection(m, arg[1], "server")
s.type_name = type_name
local function _n(name) s.option_prefix = "ssr_"
return option_prefix .. name
end
local ssr_encrypt_method_list = { local ssr_encrypt_method_list = {
"none", "table", "rc2-cfb", "rc4", "rc4-md5", "rc4-md5-6", "aes-128-cfb", "none", "table", "rc2-cfb", "rc4", "rc4-md5", "rc4-md5-6", "aes-128-cfb",
@@ -39,32 +35,32 @@ local ssr_obfs_list = {
"tls1.0_session_auth", "tls1.2_ticket_auth" "tls1.0_session_auth", "tls1.2_ticket_auth"
} }
o = s:option(Value, _n("address"), translate("Address (Support Domain Name)")) o = s:option(Value, "address", translate("Address (Support Domain Name)"))
o = s:option(Value, _n("port"), translate("Port")) o = s:option(Value, "port", translate("Port"))
o.datatype = "port" o.datatype = "port"
o = s:option(Value, _n("password"), translate("Password")) o = s:option(Value, "password", translate("Password"))
o.password = true o.password = true
o = s:option(ListValue, _n("method"), translate("Encrypt Method")) o = s:option(ListValue, "method", translate("Encrypt Method"))
for a, t in ipairs(ssr_encrypt_method_list) do o:value(t) end for a, t in ipairs(ssr_encrypt_method_list) do o:value(t) end
o = s:option(ListValue, _n("protocol"), translate("Protocol")) o = s:option(ListValue, "protocol", translate("Protocol"))
for a, t in ipairs(ssr_protocol_list) do o:value(t) end for a, t in ipairs(ssr_protocol_list) do o:value(t) end
o = s:option(Value, _n("protocol_param"), translate("Protocol_param")) o = s:option(Value, "protocol_param", translate("Protocol_param"))
o = s:option(ListValue, _n("obfs"), translate("Obfs")) o = s:option(ListValue, "obfs", translate("Obfs"))
for a, t in ipairs(ssr_obfs_list) do o:value(t) end for a, t in ipairs(ssr_obfs_list) do o:value(t) end
o = s:option(Value, _n("obfs_param"), translate("Obfs_param")) o = s:option(Value, "obfs_param", translate("Obfs_param"))
o = s:option(Value, _n("timeout"), translate("Connection Timeout")) o = s:option(Value, "timeout", translate("Connection Timeout"))
o.datatype = "uinteger" o.datatype = "uinteger"
o.default = 300 o.default = 300
o = s:option(Flag, _n("tcp_fast_open"), "TCP " .. translate("Fast Open"), translate("Need node support required")) o = s:option(Flag, "tcp_fast_open", "TCP " .. translate("Fast Open"), translate("Need node support required"))
o.default = 0 o.default = 0
api.luci_types(arg[1], m, s, type_name, option_prefix) api.luci_types(s1, s)
@@ -17,7 +17,7 @@ m:foreach("user", function(s)
user_list[#user_list + 1] = s user_list[#user_list + 1] = s
end) end)
s = m:section(NamedSection, arg[1], "server", translate("Server Config")) local s = m:section(NamedSection, arg[1], "server", translate("Server Config"))
s.addremove = false s.addremove = false
s.dynamic = false s.dynamic = false
@@ -1,135 +1,27 @@
local m, s = ...
if not api.finded_com("hysteria") then if not api.finded_com("hysteria") then
return return
end end
-- [[ Hysteria2 ]]
local m, s1 = ...
local type_name = "Hysteria2" local type_name = "Hysteria2"
-- [[ Hysteria2 ]] s1.fields["type"]:value(type_name, "Hysteria2")
s.fields["type"]:value(type_name, "Hysteria2") if s1.val["type"] and s1.val["type"] ~= type_name then
if s.val["type"] and s.val["type"] ~= type_name then
return return
end end
local option_prefix = "hysteria2_" local s = NamedSection(m, arg[1], "server")
s.type_name = type_name
s.option_prefix = "hysteria2_"
local function _n(name) o = s:option(Flag, "custom", translate("Use Custom Config"))
return option_prefix .. name
end
o = s:option(Flag, _n("custom"), translate("Use Custom Config")) o = s:option(TextValue, "custom_config", translate("Custom Config"))
o = s:option(Value, _n("port"), translate("Listen Port"))
o.datatype = "port"
o:depends({ [_n("custom")] = false })
o = s:option(DynamicList, _n("users"), translate("User"))
for i, v in ipairs(user_list) do
o:value(v[".name"], v.username)
end
o:depends({ [_n("custom")] = false })
o = s:option(Flag, _n("realms"), translate("Realms"))
o.default = "0"
o.rewrite_option = o.option
o:depends({ [_n("custom")] = false })
o = s:option(Value, _n("realm_url"), translate("Realm URL"), translate("Example:") .. "realm://public@realm.hy2.io/your-realm-name")
o.rewrite_option = o.option
o:depends({ [_n("realms")] = "1" })
o.validate = function(self, value)
value = api.trim(value)
local realm = api.parse_realm_uri(value)
if realm then return value end
return nil, translate("Invalid Realm URL.")
end
o = s:option(DynamicList, _n("realm_stun"), translate("Realm STUN"))
o.default = { "stun.sip.us:3478", "stun.nextcloud.com:3478", "global.stun.twilio.com:3478" }
o.rewrite_option = o.option
o:depends({ [_n("realms")] = "1" })
o = s:option(ListValue, _n("obfs_type"), translate("Obfs Type"))
o:value("", translate("Disable"))
o:value("salamander")
o:value("gecko")
o.rewrite_option = o.option
o:depends({ [_n("custom")] = false })
o = s:option(Value, _n("obfs_password"), translate("Obfs Password"))
o.rewrite_option = o.option
o:depends({ [_n("obfs_type")] = "salamander" })
o:depends({ [_n("obfs_type")] = "gecko" })
o = s:option(Value, _n("obfs_MinPacketSize"), translate("Gecko Packet Size (min)"))
o.datatype = "uinteger"
o.placeholder = "512"
o.default = "512"
o:depends({ [_n("obfs_type")] = "gecko" })
o.rewrite_option = o.option
o = s:option(Value, _n("obfs_MaxPacketSize"), translate("Gecko Packet Size (max)"))
o.datatype = "uinteger"
o.placeholder = "1200"
o.default = "1200"
o:depends({ [_n("obfs_type")] = "gecko" })
o.rewrite_option = o.option
o = s:option(Flag, _n("udp"), translate("UDP"))
o.default = "1"
o.rewrite_option = o.option
o:depends({ [_n("custom")] = false })
o = s:option(Value, _n("up_mbps"), translate("Max upload Mbps"))
o.rewrite_option = o.option
o:depends({ [_n("custom")] = false })
o = s:option(Value, _n("down_mbps"), translate("Max download Mbps"))
o.rewrite_option = o.option
o:depends({ [_n("custom")] = false })
o = s:option(Flag, _n("ignoreClientBandwidth"), translate("ignoreClientBandwidth"))
o.default = "0"
o.rewrite_option = o.option
o:depends({ [_n("custom")] = false })
o = s:option(FileUpload, _n("tls_certificateFile"), translate("Public key absolute path"), translate("as:") .. "/etc/ssl/fullchain.pem")
o.default = m:get(s.section, "tls_certificateFile") or "/etc/config/ssl/" .. arg[1] .. ".pem"
if o and o:formvalue(arg[1]) then o.default = o:formvalue(arg[1]) end
o.validate = function(self, value, t)
if value and value ~= "" then
if not nixio.fs.access(value) then
return nil, translate("Can't find this file!")
else
return value
end
end
return nil
end
o:depends({ [_n("custom")] = false })
o = s:option(FileUpload, _n("tls_keyFile"), translate("Private key absolute path"), translate("as:") .. "/etc/ssl/private.key")
o.default = m:get(s.section, "tls_keyFile") or "/etc/config/ssl/" .. arg[1] .. ".key"
if o and o:formvalue(arg[1]) then o.default = o:formvalue(arg[1]) end
o.validate = function(self, value, t)
if value and value ~= "" then
if not nixio.fs.access(value) then
return nil, translate("Can't find this file!")
else
return value
end
end
return nil
end
o:depends({ [_n("custom")] = false })
o = s:option(TextValue, _n("custom_config"), translate("Custom Config"))
o.rows = 10 o.rows = 10
o.wrap = "off" o.wrap = "off"
o:depends({ [_n("custom")] = true }) o:depends({ custom = true })
o.datatype = "json" o.datatype = "json"
local o_validate = o.validate local o_validate = o.validate
o.validate = function(self, value) o.validate = function(self, value)
@@ -147,8 +39,101 @@ o.custom_write = function(self, section, value)
m:set(section, "config_str", api.base64Encode(value) or "") m:set(section, "config_str", api.base64Encode(value) or "")
end end
o = s:option(Flag, _n("log"), translate("Log")) o = s:option(Value, "port", translate("Listen Port"))
o.datatype = "port"
o:depends({ custom = false })
o = s:option(DynamicList, "users", translate("User"))
for i, v in ipairs(user_list) do
o:value(v[".name"], v.username)
end
o:depends({ custom = false })
o = s:option(Flag, "realms", translate("Realms"))
o.default = "0"
o:depends({ custom = false })
o = s:option(Value, "realm_url", translate("Realm URL"), translate("Example:") .. "realm://public@realm.hy2.io/your-realm-name")
o:depends({ realms = true })
o.validate = function(self, value)
value = api.trim(value)
local realm = api.parse_realm_uri(value)
if realm then return value end
return nil, translate("Invalid Realm URL.")
end
o = s:option(DynamicList, "realm_stun", translate("Realm STUN"))
o.default = { "stun.sip.us:3478", "stun.nextcloud.com:3478", "global.stun.twilio.com:3478" }
o:depends({ realms = true })
o = s:option(ListValue, "obfs_type", translate("Obfs Type"))
o:value("", translate("Disable"))
o:value("salamander")
o:value("gecko")
o:depends({ custom = false })
o = s:option(Value, "obfs_password", translate("Obfs Password"))
o:depends({ obfs_type = "salamander" })
o:depends({ obfs_type = "gecko" })
o = s:option(Value, "obfs_MinPacketSize", translate("Gecko Packet Size (min)"))
o.datatype = "uinteger"
o.placeholder = "512"
o.default = "512"
o:depends({ obfs_type = "gecko" })
o = s:option(Value, "obfs_MaxPacketSize", translate("Gecko Packet Size (max)"))
o.datatype = "uinteger"
o.placeholder = "1200"
o.default = "1200"
o:depends({ obfs_type = "gecko" })
o = s:option(Flag, "udp", translate("UDP"))
o.default = "1"
o:depends({ custom = false })
o = s:option(Value, "up_mbps", translate("Max upload Mbps"))
o:depends({ custom = false })
o = s:option(Value, "down_mbps", translate("Max download Mbps"))
o:depends({ custom = false })
o = s:option(Flag, "ignoreClientBandwidth", translate("ignoreClientBandwidth"))
o.default = "0"
o:depends({ custom = false })
o = s:option(FileUpload, "tls_certificateFile", translate("Public key absolute path"), translate("as:") .. "/etc/ssl/fullchain.pem")
o.default = m:get(s.section, "tls_certificateFile") or "/etc/config/ssl/" .. arg[1] .. ".pem"
if o and o:formvalue(arg[1]) then o.default = o:formvalue(arg[1]) end
o.validate = function(self, value, t)
if value and value ~= "" then
if not api.fs.access(value) then
return nil, translate("Can't find this file!")
else
return value
end
end
return nil
end
o:depends({ custom = false })
o = s:option(FileUpload, "tls_keyFile", translate("Private key absolute path"), translate("as:") .. "/etc/ssl/private.key")
o.default = m:get(s.section, "tls_keyFile") or "/etc/config/ssl/" .. arg[1] .. ".key"
if o and o:formvalue(arg[1]) then o.default = o:formvalue(arg[1]) end
o.validate = function(self, value, t)
if value and value ~= "" then
if not api.fs.access(value) then
return nil, translate("Can't find this file!")
else
return value
end
end
return nil
end
o:depends({ custom = false })
o = s:option(Flag, "log", translate("Log"))
o.default = "1" o.default = "1"
o.rmempty = false o.rmempty = false
api.luci_types(arg[1], m, s, type_name, option_prefix) api.luci_types(s1, s)
@@ -1,29 +1,25 @@
local m, s = ...
if not api.finded_com("xray") then if not api.finded_com("xray") then
return return
end end
-- [[ Xray ]]
local m, s1 = ...
local type_name = "Xray" local type_name = "Xray"
-- [[ Xray ]] s1.fields["type"]:value(type_name, "Xray")
if not s1.fields["type"].default then
s.fields["type"]:value(type_name, "Xray") s1.fields["type"].default = type_name
if not s.fields["type"].default then
s.fields["type"].default = type_name
end end
if s.val["type"] and s.val["type"] ~= type_name then if s1.val["type"] and s1.val["type"] ~= type_name then
return return
end end
local option_prefix = "xray_" local s = NamedSection(m, arg[1], "server")
s.type_name = type_name
s.option_prefix = "xray_"
local function _n(name) local ss_method_list = {
return option_prefix .. name
end
local x_ss_method_list = {
"aes-128-gcm", "aes-256-gcm", "chacha20-poly1305", "xchacha20-poly1305", "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305" "aes-128-gcm", "aes-256-gcm", "chacha20-poly1305", "xchacha20-poly1305", "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305"
} }
@@ -31,90 +27,91 @@ local header_type_list = {
"none", "srtp", "utp", "wechat-video", "dtls", "wireguard", "dns" "none", "srtp", "utp", "wechat-video", "dtls", "wireguard", "dns"
} }
o = s:option(Flag, _n("custom"), translate("Use Custom Config")) o = s:option(Flag, "custom", translate("Use Custom Config"))
o = s:option(ListValue, _n("protocol"), translate("Protocol")) o = s:option(ListValue, "protocol", translate("Protocol"))
o:value("vmess", "Vmess") o:value("vmess", "Vmess")
o:value("vless", "VLESS") o:value("vless", "VLESS")
o:value("http", "HTTP") o:value("http", "HTTP")
o:value("socks", "Socks") o:value("socks", "Socks")
o:value("shadowsocks", "Shadowsocks") o:value("shadowsocks", "Shadowsocks")
o:value("trojan", "Trojan") o:value("trojan", "Trojan")
o:value("hysteria2", translate("Hysteria2")) o:value("hysteria2", "Hysteria2")
o:value("wireguard", "WireGuard")
o:value("dokodemo-door", "dokodemo-door") o:value("dokodemo-door", "dokodemo-door")
o:depends({ [_n("custom")] = false }) o:depends({ custom = false })
o = s:option(Value, _n("port"), translate("Listen Port")) o = s:option(Value, "port", translate("Listen Port"))
o.datatype = "port" o.datatype = "port"
o:depends({ [_n("custom")] = false }) o:depends({ custom = false })
o = s:option(DynamicList, _n("users"), translate("User")) o = s:option(DynamicList, "users", translate("User"))
for i, v in ipairs(user_list) do for i, v in ipairs(user_list) do
o:value(v[".name"], v.username) o:value(v[".name"], v.username)
end end
o:depends({ [_n("protocol")] = "http" }) o:depends({ protocol = "http" })
o:depends({ [_n("protocol")] = "socks" }) o:depends({ protocol = "socks" })
o:depends({ [_n("protocol")] = "shadowsocks" }) o:depends({ protocol = "shadowsocks" })
o:depends({ [_n("protocol")] = "vmess" }) o:depends({ protocol = "vmess" })
o:depends({ [_n("protocol")] = "vless" }) o:depends({ protocol = "vless" })
o:depends({ [_n("protocol")] = "trojan" }) o:depends({ protocol = "trojan" })
o:depends({ [_n("protocol")] = "hysteria2" }) o:depends({ protocol = "hysteria2" })
o:depends({ protocol = "wireguard" })
o = s:option(ListValue, _n("d_protocol"), translate("Destination protocol")) o = s:option(ListValue, "d_protocol", translate("Destination protocol"))
o:value("tcp", "TCP") o:value("tcp", "TCP")
o:value("udp", "UDP") o:value("udp", "UDP")
o:value("tcp,udp", "TCP,UDP") o:value("tcp,udp", "TCP,UDP")
o:depends({ [_n("protocol")] = "dokodemo-door" }) o:depends({ protocol = "dokodemo-door" })
o = s:option(Value, _n("d_address"), translate("Destination address")) o = s:option(Value, "d_address", translate("Destination address"))
o:depends({ [_n("protocol")] = "dokodemo-door" }) o:depends({ protocol = "dokodemo-door" })
o = s:option(Value, _n("d_port"), translate("Destination port")) o = s:option(Value, "d_port", translate("Destination port"))
o.datatype = "port" o.datatype = "port"
o:depends({ [_n("protocol")] = "dokodemo-door" }) o:depends({ protocol = "dokodemo-door" })
o = s:option(Value, _n("decryption"), translate("Encrypt Method") .. " (decryption)") o = s:option(Value, "decryption", translate("Encrypt Method") .. " (decryption)")
o.default = "none" o.default = "none"
o.placeholder = "none" o.placeholder = "none"
o:depends({ [_n("protocol")] = "vless" }) o:depends({ protocol = "vless" })
o.validate = function(self, value) o.validate = function(self, value)
value = api.trim(value) value = api.trim(value)
return (value == "" and "none" or value) return (value == "" and "none" or value)
end end
o = s:option(ListValue, _n("x_ss_method"), translate("Encrypt Method")) o = s:option(ListValue, "ss_method", translate("Encrypt Method"))
o.rewrite_option = "method" for a, t in ipairs(ss_method_list) do o:value(t) end
for a, t in ipairs(x_ss_method_list) do o:value(t) end o:depends({ protocol = "shadowsocks" })
o:depends({ [_n("protocol")] = "shadowsocks" })
o = s:option(Value, _n("ss_password"), translate("Password")) o = s:option(Value, "ss_password", translate("Password"))
o:depends({ [_n("protocol")] = "shadowsocks" }) o:depends({ protocol = "shadowsocks" })
o = s:option(ListValue, _n("ss_network"), translate("Transport")) o = s:option(ListValue, "ss_network", translate("Transport"))
o.default = "tcp,udp" o.default = "tcp,udp"
o:value("tcp", "TCP") o:value("tcp", "TCP")
o:value("udp", "UDP") o:value("udp", "UDP")
o:value("tcp,udp", "TCP,UDP") o:value("tcp,udp", "TCP,UDP")
o:depends({ [_n("protocol")] = "shadowsocks" }) o:depends({ protocol = "shadowsocks" })
o = s:option(Flag, _n("udp_forward"), translate("UDP Forward")) o = s:option(Flag, "udp_forward", translate("UDP Forward"))
o.default = "1" o.default = "1"
o.rmempty = false o.rmempty = false
o:depends({ [_n("protocol")] = "socks" }) o:depends({ protocol = "socks" })
o = s:option(ListValue, _n("flow"), translate("flow")) o = s:option(ListValue, "flow", translate("flow"))
o.default = "" o.default = ""
o:value("", translate("Disable")) o:value("", translate("Disable"))
o:value("xtls-rprx-vision") o:value("xtls-rprx-vision")
o:depends({ [_n("protocol")] = "vless" }) o:depends({ protocol = "vless" })
---- [[ hysteria2 ]] ---- [[ hysteria2 ]]
o = s:option(Flag, _n("hysteria2_realms"), translate("Realms")) o = s:option(Flag, "hysteria2_realms", translate("Realms"))
o.default = "0" o.default = "0"
o:depends({ [_n("protocol")] = "hysteria2"}) o:depends({ protocol = "hysteria2"})
o = s:option(Value, _n("hysteria2_realm_url"), translate("Realm URL"), translate("Example:") .. "realm://public@realm.hy2.io/your-realm-name") o = s:option(Value, "hysteria2_realm_url", translate("Realm URL"), translate("Example:") .. "realm://public@realm.hy2.io/your-realm-name")
o:depends({ [_n("hysteria2_realms")] = "1" }) o:depends({ hysteria2_realms = "1" })
o.validate = function(self, value) o.validate = function(self, value)
value = api.trim(value) value = api.trim(value)
local realm = api.parse_realm_uri(value) local realm = api.parse_realm_uri(value)
@@ -122,52 +119,52 @@ o.validate = function(self, value)
return nil, translate("Invalid Realm URL.") return nil, translate("Invalid Realm URL.")
end end
o = s:option(DynamicList, _n("hysteria2_realm_stun"), translate("Realm STUN")) o = s:option(DynamicList, "hysteria2_realm_stun", translate("Realm STUN"))
o.default = { "stun.sip.us:3478", "stun.nextcloud.com:3478", "global.stun.twilio.com:3478" } o.default = { "stun.sip.us:3478", "stun.nextcloud.com:3478", "global.stun.twilio.com:3478" }
o:depends({ [_n("hysteria2_realms")] = "1" }) o:depends({ hysteria2_realms = "1" })
o = s:option(ListValue, _n("hysteria2_obfs_type"), translate("Obfs Type")) o = s:option(ListValue, "hysteria2_obfs_type", translate("Obfs Type"))
o:value("", translate("Disable")) o:value("", translate("Disable"))
o:value("salamander") o:value("salamander")
o:value("gecko") o:value("gecko")
o:depends({ [_n("protocol")] = "hysteria2" }) o:depends({ protocol = "hysteria2" })
o = s:option(Value, _n("hysteria2_obfs_password"), translate("Obfs Password")) o = s:option(Value, "hysteria2_obfs_password", translate("Obfs Password"))
o:depends({ [_n("hysteria2_obfs_type")] = "salamander" }) o:depends({ hysteria2_obfs_type = "salamander" })
o:depends({ [_n("hysteria2_obfs_type")] = "gecko" }) o:depends({ hysteria2_obfs_type = "gecko" })
o = s:option(Value, _n("hysteria2_obfs_MinPacketSize"), translate("Gecko Packet Size (min)")) o = s:option(Value, "hysteria2_obfs_MinPacketSize", translate("Gecko Packet Size (min)"))
o.datatype = "uinteger" o.datatype = "uinteger"
o.placeholder = "512" o.placeholder = "512"
o.default = "512" o.default = "512"
o:depends({ [_n("hysteria2_obfs_type")] = "gecko" }) o:depends({ hysteria2_obfs_type = "gecko" })
o = s:option(Value, _n("hysteria2_obfs_MaxPacketSize"), translate("Gecko Packet Size (max)")) o = s:option(Value, "hysteria2_obfs_MaxPacketSize", translate("Gecko Packet Size (max)"))
o.datatype = "uinteger" o.datatype = "uinteger"
o.placeholder = "1200" o.placeholder = "1200"
o.default = "1200" o.default = "1200"
o:depends({ [_n("hysteria2_obfs_type")] = "gecko" }) o:depends({ hysteria2_obfs_type = "gecko" })
o = s:option(Flag, _n("hysteria2_ignore_client_bandwidth"), translate("Client BBR Flow Control")) o = s:option(Flag, "hysteria2_ignore_client_bandwidth", translate("Client BBR Flow Control"))
o.default = 0 o.default = 0
o:depends({ [_n("protocol")] = "hysteria2" }) o:depends({ protocol = "hysteria2" })
o = s:option(Value, _n("hysteria2_up_mbps"), translate("Max upload Mbps")) o = s:option(Value, "hysteria2_up_mbps", translate("Max upload Mbps"))
o:depends({ [_n("protocol")] = "hysteria2", [_n("hysteria2_ignore_client_bandwidth")] = false }) o:depends({ protocol = "hysteria2", hysteria2_ignore_client_bandwidth = false })
o = s:option(Value, _n("hysteria2_down_mbps"), translate("Max download Mbps")) o = s:option(Value, "hysteria2_down_mbps", translate("Max download Mbps"))
o:depends({ [_n("protocol")] = "hysteria2", [_n("hysteria2_ignore_client_bandwidth")] = false }) o:depends({ protocol = "hysteria2", hysteria2_ignore_client_bandwidth = false })
---- [[ TLS ]] ---- [[ TLS ]]
o = s:option(Flag, _n("tls"), translate("TLS")) o = s:option(Flag, "tls", "TLS")
o.default = 0 o.default = 0
o.validate = function(self, value, t) o.validate = function(self, value, t)
if value then if value then
local reality = s.fields[_n("reality")] and s.fields[_n("reality")]:formvalue(t) or nil local reality = s.fields["reality"] and s.fields["reality"]:formvalue(t) or nil
if reality and reality == "1" then return value end if reality and reality == "1" then return value end
if value == "1" then if value == "1" then
local ca = s.fields[_n("tls_certificateFile")] and s.fields[_n("tls_certificateFile")]:formvalue(t) or "" local ca = s.fields["tls_certificateFile"] and s.fields["tls_certificateFile"]:formvalue(t) or ""
local key = s.fields[_n("tls_keyFile")] and s.fields[_n("tls_keyFile")]:formvalue(t) or "" local key = s.fields["tls_keyFile"] and s.fields["tls_keyFile"]:formvalue(t) or ""
if ca == "" or key == "" then if ca == "" or key == "" then
return nil, translate("Public key and Private key path can not be empty!") return nil, translate("Public key and Private key path can not be empty!")
end end
@@ -175,32 +172,32 @@ o.validate = function(self, value, t)
return value return value
end end
end end
o:depends({ [_n("protocol")] = "vmess" }) o:depends({ protocol = "vmess" })
o:depends({ [_n("protocol")] = "vless" }) o:depends({ protocol = "vless" })
o:depends({ [_n("protocol")] = "http" }) o:depends({ protocol = "http" })
o:depends({ [_n("protocol")] = "socks" }) o:depends({ protocol = "socks" })
o:depends({ [_n("protocol")] = "shadowsocks" }) o:depends({ protocol = "shadowsocks" })
o:depends({ [_n("protocol")] = "trojan" }) o:depends({ protocol = "trojan" })
-- [[ REALITY ]] -- -- [[ REALITY ]] --
o = s:option(Flag, _n("reality"), translate("REALITY")) o = s:option(Flag, "reality", translate("REALITY"))
o.default = 0 o.default = 0
o:depends({ [_n("tls")] = true }) o:depends({ tls = true })
o = s:option(Value, _n("reality_private_key"), translate("Private Key")) o = s:option(Value, "reality_private_key", translate("Private Key"))
o:depends({ [_n("reality")] = true }) o:depends({ reality = true })
o = s:option(DynamicList, _n("reality_shortId"), translate("Short Id")) o = s:option(DynamicList, "reality_shortId", translate("Short Id"))
o:depends({ [_n("reality")] = true }) o:depends({ reality = true })
o = s:option(Value, _n("reality_dest"), translate("Dest")) o = s:option(Value, "reality_dest", translate("Dest"))
o.default = "google.com:443" o.default = "google.com:443"
o:depends({ [_n("reality")] = true }) o:depends({ reality = true })
o = s:option(DynamicList, _n("reality_serverNames"), translate("serverNames")) o = s:option(DynamicList, "reality_serverNames", translate("serverNames"))
o:depends({ [_n("reality")] = true }) o:depends({ reality = true })
o = s:option(ListValue, _n("alpn"), translate("alpn")) o = s:option(ListValue, "alpn", translate("alpn"))
o.default = "default" o.default = "default"
o:value("default", translate("Default")) o:value("default", translate("Default"))
o:value("h3") o:value("h3")
@@ -209,35 +206,35 @@ o:value("h3,h2")
o:value("http/1.1") o:value("http/1.1")
o:value("h2,http/1.1") o:value("h2,http/1.1")
o:value("h3,h2,http/1.1") o:value("h3,h2,http/1.1")
o:depends({ [_n("tls")] = true, [_n("reality")] = false }) o:depends({ tls = true, reality = false })
o = s:option(Flag, _n("use_mldsa65Seed"), translate("ML-DSA-65")) o = s:option(Flag, "use_mldsa65Seed", translate("ML-DSA-65"))
o.default = "0" o.default = "0"
o:depends({ [_n("reality")] = true }) o:depends({ reality = true })
o = s:option(TextValue, _n("reality_mldsa65Seed"), "ML-DSA-65 " .. translate("Private Key")) o = s:option(TextValue, "reality_mldsa65Seed", "ML-DSA-65 " .. translate("Private Key"))
o.default = "" o.default = ""
o.rows = 5 o.rows = 5
o.wrap = "soft" o.wrap = "soft"
o:depends({ [_n("use_mldsa65Seed")] = true }) o:depends({ use_mldsa65Seed = true })
o.validate = function(self, value) o.validate = function(self, value)
return api.trim(value:gsub("[\r\n]", "")) return api.trim(value:gsub("[\r\n]", ""))
end end
-- o = s:option(Value, _n("minversion"), translate("minversion")) -- o = s:option(Value, "minversion", translate("minversion"))
-- o.default = "1.3" -- o.default = "1.3"
-- o:value("1.3") -- o:value("1.3")
--o:depends({ [_n("tls")] = true }) --o:depends({ tls = true })
-- [[ TLS ]] -- -- [[ TLS ]] --
o = s:option(FileUpload, _n("tls_certificateFile"), translate("Public key absolute path"), translate("as:") .. "/etc/ssl/fullchain.pem") o = s:option(FileUpload, "tls_certificateFile", translate("Public key absolute path"), translate("as:") .. "/etc/ssl/fullchain.pem")
o.default = m:get(s.section, "tls_certificateFile") or "/etc/config/ssl/" .. arg[1] .. ".pem" o.default = m:get(s.section, "tls_certificateFile") or "/etc/config/ssl/" .. arg[1] .. ".pem"
if o and o:formvalue(arg[1]) then o.default = o:formvalue(arg[1]) end if o and o:formvalue(arg[1]) then o.default = o:formvalue(arg[1]) end
o:depends({ [_n("tls")] = true, [_n("reality")] = false }) o:depends({ tls = true, reality = false })
o:depends({ [_n("protocol")] = "hysteria2"}) o:depends({ protocol = "hysteria2"})
o.validate = function(self, value, t) o.validate = function(self, value, t)
if value and value ~= "" then if value and value ~= "" then
if not nixio.fs.access(value) then if not api.fs.access(value) then
return nil, translate("Can't find this file!") return nil, translate("Can't find this file!")
else else
return value return value
@@ -246,14 +243,14 @@ o.validate = function(self, value, t)
return nil return nil
end end
o = s:option(FileUpload, _n("tls_keyFile"), translate("Private key absolute path"), translate("as:") .. "/etc/ssl/private.key") o = s:option(FileUpload, "tls_keyFile", translate("Private key absolute path"), translate("as:") .. "/etc/ssl/private.key")
o.default = m:get(s.section, "tls_keyFile") or "/etc/config/ssl/" .. arg[1] .. ".key" o.default = m:get(s.section, "tls_keyFile") or "/etc/config/ssl/" .. arg[1] .. ".key"
if o and o:formvalue(arg[1]) then o.default = o:formvalue(arg[1]) end if o and o:formvalue(arg[1]) then o.default = o:formvalue(arg[1]) end
o:depends({ [_n("tls")] = true, [_n("reality")] = false }) o:depends({ tls = true, reality = false })
o:depends({ [_n("protocol")] = "hysteria2"}) o:depends({ protocol = "hysteria2"})
o.validate = function(self, value, t) o.validate = function(self, value, t)
if value and value ~= "" then if value and value ~= "" then
if not nixio.fs.access(value) then if not api.fs.access(value) then
return nil, translate("Can't find this file!") return nil, translate("Can't find this file!")
else else
return value return value
@@ -262,117 +259,116 @@ o.validate = function(self, value, t)
return nil return nil
end end
o = s:option(Flag, _n("ech"), translate("ECH")) o = s:option(Flag, "ech", translate("ECH"))
o.default = "0" o.default = "0"
o:depends({ [_n("tls")] = true, [_n("reality")] = false }) o:depends({ tls = true, reality = false })
o = s:option(TextValue, _n("ech_key"), translate("ECH Key")) o = s:option(TextValue, "ech_key", translate("ECH Key"))
o.default = "" o.default = ""
o.rows = 5 o.rows = 5
o.wrap = "soft" o.wrap = "soft"
o:depends({ [_n("ech")] = true }) o:depends({ ech = true })
o.validate = function(self, value) o.validate = function(self, value)
return api.trim(value:gsub("[\r\n]", "")) return api.trim(value:gsub("[\r\n]", ""))
end end
o = s:option(ListValue, _n("transport"), translate("Transport")) o = s:option(ListValue, "transport", translate("Transport"))
o:value("raw", "RAW") o:value("raw", "RAW")
o:value("mkcp", "mKCP") o:value("mkcp", "mKCP")
o:value("ws", "WebSocket") o:value("ws", "WebSocket")
o:value("grpc", "gRPC") o:value("grpc", "gRPC")
o:value("httpupgrade", "HttpUpgrade") o:value("httpupgrade", "HttpUpgrade")
o:value("xhttp", "XHTTP") o:value("xhttp", "XHTTP")
o:depends({ [_n("protocol")] = "vmess" }) o:depends({ protocol = "vmess" })
o:depends({ [_n("protocol")] = "vless" }) o:depends({ protocol = "vless" })
o:depends({ [_n("protocol")] = "socks" }) o:depends({ protocol = "socks" })
o:depends({ [_n("protocol")] = "shadowsocks" }) o:depends({ protocol = "shadowsocks" })
o:depends({ [_n("protocol")] = "trojan" }) o:depends({ protocol = "trojan" })
-- [[ WebSocket ]]-- -- [[ WebSocket ]]--
o = s:option(Value, _n("ws_host"), translate("WebSocket Host")) o = s:option(Value, "ws_host", translate("WebSocket Host"))
o:depends({ [_n("transport")] = "ws" }) o:depends({ transport = "ws" })
o = s:option(Value, _n("ws_path"), translate("WebSocket Path")) o = s:option(Value, "ws_path", translate("WebSocket Path"))
o:depends({ [_n("transport")] = "ws" }) o:depends({ transport = "ws" })
-- [[ HttpUpgrade ]]-- -- [[ HttpUpgrade ]]--
o = s:option(Value, _n("httpupgrade_host"), translate("HttpUpgrade Host")) o = s:option(Value, "httpupgrade_host", translate("HttpUpgrade Host"))
o:depends({ [_n("transport")] = "httpupgrade" }) o:depends({ transport = "httpupgrade" })
o = s:option(Value, _n("httpupgrade_path"), translate("HttpUpgrade Path")) o = s:option(Value, "httpupgrade_path", translate("HttpUpgrade Path"))
o.placeholder = "/" o.placeholder = "/"
o:depends({ [_n("transport")] = "httpupgrade" }) o:depends({ transport = "httpupgrade" })
-- [[ XHTTP ]]-- -- [[ XHTTP ]]--
o = s:option(Value, _n("xhttp_host"), translate("XHTTP Host")) o = s:option(Value, "xhttp_host", translate("XHTTP Host"))
o:depends({ [_n("transport")] = "xhttp" }) o:depends({ transport = "xhttp" })
o = s:option(Value, _n("xhttp_path"), translate("XHTTP Path")) o = s:option(Value, "xhttp_path", translate("XHTTP Path"))
o.placeholder = "/" o.placeholder = "/"
o:depends({ [_n("transport")] = "xhttp" }) o:depends({ transport = "xhttp" })
o = s:option(Value, _n("xhttp_maxuploadsize"), translate("maxUploadSize")) o = s:option(Value, "xhttp_maxuploadsize", translate("maxUploadSize"))
o.default = "1000000" o.default = "1000000"
o:depends({ [_n("transport")] = "xhttp" }) o:depends({ transport = "xhttp" })
o = s:option(Value, _n("xhttp_maxconcurrentuploads"), translate("maxConcurrentUploads")) o = s:option(Value, "xhttp_maxconcurrentuploads", translate("maxConcurrentUploads"))
o.default = "10" o.default = "10"
o:depends({ [_n("transport")] = "xhttp" }) o:depends({ transport = "xhttp" })
o = s:option(Value, _n("splithttp_maxuploadsize"), translate("maxUploadSize")) o = s:option(Value, "splithttp_maxuploadsize", translate("maxUploadSize"))
o.default = "1000000" o.default = "1000000"
o:depends({ [_n("transport")] = "splithttp" }) o:depends({ transport = "splithttp" })
o = s:option(Value, _n("splithttp_maxconcurrentuploads"), translate("maxConcurrentUploads")) o = s:option(Value, "splithttp_maxconcurrentuploads", translate("maxConcurrentUploads"))
o.default = "10" o.default = "10"
o:depends({ [_n("transport")] = "splithttp" }) o:depends({ transport = "splithttp" })
-- [[ TCP ]]-- -- [[ TCP ]]--
o = s:option(ListValue, _n("tcp_guise"), translate("Camouflage Type")) o = s:option(ListValue, "tcp_guise", translate("Camouflage Type"))
o:value("none", "none") o:value("none", "none")
o:value("http", "http") o:value("http", "http")
o:depends({ [_n("transport")] = "raw" }) o:depends({ transport = "raw" })
o = s:option(DynamicList, _n("tcp_guise_http_host"), translate("HTTP Host")) o = s:option(DynamicList, "tcp_guise_http_host", translate("HTTP Host"))
o:depends({ [_n("tcp_guise")] = "http" }) o:depends({ tcp_guise = "http" })
o = s:option(DynamicList, _n("tcp_guise_http_path"), translate("HTTP Path")) o = s:option(DynamicList, "tcp_guise_http_path", translate("HTTP Path"))
o:depends({ [_n("tcp_guise")] = "http" }) o:depends({ tcp_guise = "http" })
-- [[ mKCP ]]-- -- [[ mKCP ]]--
o = s:option(ListValue, _n("mkcp_guise"), translate("Camouflage Type"), translate('<br />none: default, no masquerade, data sent is packets with no characteristics.<br />srtp: disguised as an SRTP packet, it will be recognized as video call data (such as FaceTime).<br />utp: packets disguised as uTP will be recognized as bittorrent downloaded data.<br />wechat-video: packets disguised as WeChat video calls.<br />dtls: disguised as DTLS 1.2 packet.<br />wireguard: disguised as a WireGuard packet. (not really WireGuard protocol)<br />dns: Disguising traffic as DNS requests.')) o = s:option(ListValue, "mkcp_guise", translate("Camouflage Type"), translate('<br />none: default, no masquerade, data sent is packets with no characteristics.<br />srtp: disguised as an SRTP packet, it will be recognized as video call data (such as FaceTime).<br />utp: packets disguised as uTP will be recognized as bittorrent downloaded data.<br />wechat-video: packets disguised as WeChat video calls.<br />dtls: disguised as DTLS 1.2 packet.<br />wireguard: disguised as a WireGuard packet. (not really WireGuard protocol)<br />dns: Disguising traffic as DNS requests.'))
for a, t in ipairs(header_type_list) do o:value(t) end for a, t in ipairs(header_type_list) do o:value(t) end
o:depends({ [_n("transport")] = "mkcp" }) o:depends({ transport = "mkcp" })
o = s:option(Value, _n("mkcp_domain"), translate("Camouflage Domain"), translate("Use it together with the DNS disguised type. You can fill in any domain.")) o = s:option(Value, "mkcp_domain", translate("Camouflage Domain"), translate("Use it together with the DNS disguised type. You can fill in any domain."))
o:depends({ [_n("mkcp_guise")] = "dns" }) o:depends({ mkcp_guise = "dns" })
o = s:option(Value, _n("mkcp_mtu"), translate("KCP MTU")) o = s:option(Value, "mkcp_mtu", translate("KCP MTU"))
o.datatype = "uinteger" o.datatype = "uinteger"
o.default = 1350 o.default = 1350
o:depends({ [_n("transport")] = "mkcp" }) o:depends({ transport = "mkcp" })
o = s:option(Value, _n("mkcp_seed"), translate("KCP Seed")) o = s:option(Value, "mkcp_seed", translate("KCP Seed"))
o:depends({ [_n("transport")] = "mkcp" }) o:depends({ transport = "mkcp" })
-- [[ gRPC ]]-- -- [[ gRPC ]]--
o = s:option(Value, _n("grpc_serviceName"), "ServiceName") o = s:option(Value, "grpc_serviceName", "ServiceName")
o:depends({ [_n("transport")] = "grpc" }) o:depends({ transport = "grpc" })
--[[FinalMask]] --[[FinalMask]]
o = s:option(Flag, _n("use_finalmask"), "FinalMask") o = s:option(Flag, "use_finalmask", "FinalMask")
o.default = "0" o.default = "0"
o:depends({ [_n("custom")] = false, [_n("protocol")] = "vmess" }) o:depends({ custom = false, protocol = "vmess" })
o:depends({ [_n("custom")] = false, [_n("protocol")] = "vless" }) o:depends({ custom = false, protocol = "vless" })
o:depends({ [_n("custom")] = false, [_n("protocol")] = "trojan" }) o:depends({ custom = false, protocol = "trojan" })
o:depends({ [_n("custom")] = false, [_n("protocol")] = "shadowsocks" }) o:depends({ custom = false, protocol = "shadowsocks" })
o:depends({ [_n("custom")] = false, [_n("protocol")] = "wireguard" }) o:depends({ custom = false, protocol = "hysteria2", hysteria2_realms = false })
o:depends({ [_n("custom")] = false, [_n("protocol")] = "hysteria2", [_n("hysteria2_realms")] = false })
o = s:option(TextValue, _n("finalmask"), "FinalMask JSON") o = s:option(TextValue, "finalmask", "FinalMask JSON")
o:depends({ [_n("use_finalmask")] = true }) o:depends({ use_finalmask = true })
o.rows = 10 o.rows = 10
o.wrap = "off" o.wrap = "off"
o.datatype = "json" o.datatype = "json"
@@ -387,48 +383,78 @@ o.custom_write = function(self, section, value)
end end
--[[acceptProxyProtocol]] --[[acceptProxyProtocol]]
o = s:option(Flag, _n("acceptProxyProtocol"), translate("acceptProxyProtocol"), translate("Whether to receive PROXY protocol, when this node want to be fallback or forwarded by proxy, it must be enable, otherwise it cannot be used.")) o = s:option(Flag, "acceptProxyProtocol", translate("acceptProxyProtocol"), translate("Whether to receive PROXY protocol, when this node want to be fallback or forwarded by proxy, it must be enable, otherwise it cannot be used."))
o.default = "0" o.default = "0"
o:depends({ [_n("custom")] = false }) o:depends({ custom = false })
--[[Fast Open]] --[[Fast Open]]
o = s:option(Flag, _n("tcp_fast_open"), "TCP " .. translate("Fast Open")) o = s:option(Flag, "tcp_fast_open", "TCP " .. translate("Fast Open"))
o.default = "0" o.default = "0"
o:depends({ [_n("protocol")] = "vmess", [_n("custom")] = false }) o:depends({ protocol = "vmess", custom = false })
o:depends({ [_n("protocol")] = "vless", [_n("custom")] = false }) o:depends({ protocol = "vless", custom = false })
o:depends({ [_n("protocol")] = "shadowsocks", [_n("custom")] = false }) o:depends({ protocol = "shadowsocks", custom = false })
o:depends({ [_n("protocol")] = "trojan", [_n("custom")] = false }) o:depends({ protocol = "trojan", custom = false })
-- [[ Fallback ]]-- -- [[ Fallback ]]--
o = s:option(Flag, _n("fallback"), translate("Fallback")) o = s:option(Flag, "fallback", translate("Fallback"))
o:depends({ [_n("protocol")] = "vless", [_n("transport")] = "raw" }) o:depends({ protocol = "vless", transport = "raw" })
o:depends({ [_n("protocol")] = "trojan", [_n("transport")] = "raw" }) o:depends({ protocol = "trojan", transport = "raw" })
--[[ --[[
o = s:option(Value, _n("fallback_alpn"), "Fallback alpn") o = s:option(Value, "fallback_alpn", "Fallback alpn")
o:depends({ [_n("fallback")] = true }) o:depends({ fallback = true })
o = s:option(Value, _n("fallback_path"), "Fallback path") o = s:option(Value, "fallback_path", "Fallback path")
o:depends({ [_n("fallback")] = true }) o:depends({ fallback = true })
o = s:option(Value, _n("fallback_dest"), "Fallback dest") o = s:option(Value, "fallback_dest", "Fallback dest")
o:depends({ [_n("fallback")] = true }) o:depends({ fallback = true })
o = s:option(Value, _n("fallback_xver"), "Fallback xver") o = s:option(Value, "fallback_xver", "Fallback xver")
o.default = 0 o.default = 0
o:depends({ [_n("fallback")] = true }) o:depends({ fallback = true })
]]-- ]]--
o = s:option(DynamicList, _n("fallback_list"), "Fallback", translate("format: dest,path,xver")) o = s:option(DynamicList, "fallback_list", "Fallback", translate("format: dest,path,xver"))
o:depends({ [_n("fallback")] = true }) o:depends({ fallback = true })
o = s:option(Flag, _n("bind_local"), translate("Bind Local"), translate("When selected, it can only be accessed localhost.")) -- Not supported at present
o.default = "0" --[[
o:depends({ [_n("custom")] = false }) o = s:option(Flag, "wireguard_system_interface", translate("System interface"))
o.default = 0
o:depends({ protocol = "wireguard" })
]]--
o = s:option(Flag, _n("accept_lan"), translate("Accept LAN Access"), translate("When selected, it can accessed lan , this will not be safe!")) o = s:option(Value, "wireguard_mtu", "MTU")
o.default = "1420"
o:depends({ protocol = "wireguard" })
-- Not supported at present
--[[
o = s:option(DynamicList, "wireguard_local_address", translate("Local Address"))
o:depends({ protocol = "wireguard" })
]]--
o = s:option(Value, "wireguard_private_key", translate("Private Key"))
o.datatype = "base64"
o:depends({ protocol = "wireguard" })
o = s:option(Value, "wireguard_public_key", translate("Public Key"))
o.datatype = "base64"
o:depends({ protocol = "wireguard" })
o = s:option(DummyValue, "gen_wireguard_key")
o.prefix = option_prefix
o.template = m:template_path("/server/server_wireguard")
o:depends({ protocol = "wireguard" })
o = s:option(Flag, "bind_local", translate("Bind Local"), translate("When selected, it can only be accessed localhost."))
o.default = "0" o.default = "0"
o:depends({ [_n("custom")] = false }) o:depends({ custom = false })
o = s:option(Flag, "accept_lan", translate("Accept LAN Access"), translate("When selected, it can accessed lan , this will not be safe!"))
o.default = "0"
o:depends({ custom = false })
local nodes_table = {} local nodes_table = {}
for k, e in ipairs(api.get_valid_nodes()) do for k, e in ipairs(api.get_valid_nodes()) do
@@ -441,7 +467,7 @@ for k, e in ipairs(api.get_valid_nodes()) do
end end
end end
o = s:option(ListValue, _n("outbound_node"), translate("outbound node")) o = s:option(ListValue, "outbound_node", translate("outbound node"))
o:value("", translate("Close")) o:value("", translate("Close"))
o:value("_socks", translate("Custom Socks")) o:value("_socks", translate("Custom Socks"))
o:value("_http", translate("Custom HTTP")) o:value("_http", translate("Custom HTTP"))
@@ -452,37 +478,37 @@ for k, v in pairs(nodes_table) do
o:value(v.id, v.remarks) o:value(v.id, v.remarks)
o.group[#o.group+1] = (v.group and v.group ~= "") and v.group or translate("default") o.group[#o.group+1] = (v.group and v.group ~= "") and v.group or translate("default")
end end
o:depends({ [_n("custom")] = false }) o:depends({ custom = false })
o = s:option(Value, _n("outbound_node_address"), translate("Address (Support Domain Name)")) o = s:option(Value, "outbound_node_address", translate("Address (Support Domain Name)"))
o:depends({ [_n("outbound_node")] = "_socks"}) o:depends({ outbound_node = "_socks"})
o:depends({ [_n("outbound_node")] = "_http"}) o:depends({ outbound_node = "_http"})
o = s:option(Value, _n("outbound_node_port"), translate("Port")) o = s:option(Value, "outbound_node_port", translate("Port"))
o.datatype = "port" o.datatype = "port"
o:depends({ [_n("outbound_node")] = "_socks"}) o:depends({ outbound_node = "_socks"})
o:depends({ [_n("outbound_node")] = "_http"}) o:depends({ outbound_node = "_http"})
o = s:option(Value, _n("outbound_node_username"), translate("Username")) o = s:option(Value, "outbound_node_username", translate("Username"))
o:depends({ [_n("outbound_node")] = "_socks"}) o:depends({ outbound_node = "_socks"})
o:depends({ [_n("outbound_node")] = "_http"}) o:depends({ outbound_node = "_http"})
o = s:option(Value, _n("outbound_node_password"), translate("Password")) o = s:option(Value, "outbound_node_password", translate("Password"))
o.password = true o.password = true
o:depends({ [_n("outbound_node")] = "_socks"}) o:depends({ outbound_node = "_socks"})
o:depends({ [_n("outbound_node")] = "_http"}) o:depends({ outbound_node = "_http"})
o = s:option(Value, _n("outbound_node_iface"), translate("Interface")) o = s:option(Value, "outbound_node_iface", translate("Interface"))
o:depends({ [_n("outbound_node")] = "_iface"}) o:depends({ outbound_node = "_iface"})
local netdev_list = api.get_network_devices() local netdev_list = api.get_network_devices()
for _, d in ipairs(netdev_list) do for _, d in ipairs(netdev_list) do
o:value(d.name, d.label) o:value(d.name, d.label)
end end
o = s:option(TextValue, _n("custom_config"), translate("Custom Config") .. " (JSON)") o = s:option(TextValue, "custom_config", translate("Custom Config") .. " (JSON)")
o.rows = 10 o.rows = 10
o.wrap = "off" o.wrap = "off"
o:depends({ [_n("custom")] = true }) o:depends({ custom = true })
o.datatype = "json" o.datatype = "json"
local o_validate = o.validate local o_validate = o.validate
o.validate = function(self, value) o.validate = function(self, value)
@@ -500,16 +526,16 @@ o.custom_write = function(self, section, value)
m:set(section, "config_str", api.base64Encode(value) or "") m:set(section, "config_str", api.base64Encode(value) or "")
end end
o = s:option(Flag, _n("log"), translate("Log")) o = s:option(Flag, "log", translate("Log"))
o.default = "1" o.default = "1"
o.rmempty = false o.rmempty = false
o = s:option(ListValue, _n("loglevel"), translate("Log Level")) o = s:option(ListValue, "loglevel", translate("Log Level"))
o.default = "warning" o.default = "warning"
o:value("debug") o:value("debug")
o:value("info") o:value("info")
o:value("warning") o:value("warning")
o:value("error") o:value("error")
o:depends({ [_n("log")] = true }) o:depends({ log = true })
api.luci_types(arg[1], m, s, type_name, option_prefix) api.luci_types(s1, s)
@@ -1,29 +1,25 @@
local m, s = ...
local singbox_bin = api.finded_com("sing-box") local singbox_bin = api.finded_com("sing-box")
if not singbox_bin then if not singbox_bin then
return return
end end
-- [[ Sing-Box ]]
local m, s1 = ...
local type_name = "sing-box" local type_name = "sing-box"
-- [[ Sing-Box ]] s1.fields["type"]:value(type_name, "Sing-Box")
if not s1.fields["type"].default then
s.fields["type"]:value(type_name, "Sing-Box") s1.fields["type"].default = type_name
if not s.fields["type"].default then
s.fields["type"].default = type_name
end end
if s.val["type"] and s.val["type"] ~= type_name then if s1.val["type"] and s1.val["type"] ~= type_name then
return return
end end
local option_prefix = "singbox_" local s = NamedSection(m, arg[1], "server")
s.type_name = type_name
local function _n(name) s.option_prefix = "singbox_"
return option_prefix .. name
end
local singbox_tags = luci.sys.exec(singbox_bin .. " version | grep 'Tags:' | awk '{print $2}'") local singbox_tags = luci.sys.exec(singbox_bin .. " version | grep 'Tags:' | awk '{print $2}'")
@@ -32,9 +28,9 @@ local ss_method_list = {
"2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305" "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305"
} }
o = s:option(Flag, _n("custom"), translate("Use Custom Config")) o = s:option(Flag, "custom", translate("Use Custom Config"))
o = s:option(ListValue, _n("protocol"), translate("Protocol")) o = s:option(ListValue, "protocol", translate("Protocol"))
o:value("mixed", "Mixed") o:value("mixed", "Mixed")
o:value("socks", "Socks") o:value("socks", "Socks")
o:value("http", "HTTP") o:value("http", "HTTP")
@@ -57,74 +53,74 @@ if singbox_tags:find("with_wireguard") then
o:value("wireguard", "WireGuard") o:value("wireguard", "WireGuard")
end end
o:value("direct", "Direct") o:value("direct", "Direct")
o:depends({ [_n("custom")] = false }) o:depends({ custom = false })
o = s:option(Value, _n("port"), translate("Listen Port")) o = s:option(Value, "port", translate("Listen Port"))
o.datatype = "port" o.datatype = "port"
o:depends({ [_n("custom")] = false }) o:depends({ custom = false })
o = s:option(DynamicList, _n("users"), translate("User")) o = s:option(DynamicList, "users", translate("User"))
for i, v in ipairs(user_list) do for i, v in ipairs(user_list) do
o:value(v[".name"], v.username) o:value(v[".name"], v.username)
end end
o:depends({ [_n("protocol")] = "mixed" }) o:depends({ protocol = "mixed" })
o:depends({ [_n("protocol")] = "socks" }) o:depends({ protocol = "socks" })
o:depends({ [_n("protocol")] = "http" }) o:depends({ protocol = "http" })
o:depends({ [_n("protocol")] = "shadowsocks" }) o:depends({ protocol = "shadowsocks" })
o:depends({ [_n("protocol")] = "vmess" }) o:depends({ protocol = "vmess" })
o:depends({ [_n("protocol")] = "vless" }) o:depends({ protocol = "vless" })
o:depends({ [_n("protocol")] = "trojan" }) o:depends({ protocol = "trojan" })
o:depends({ [_n("protocol")] = "naive" }) o:depends({ protocol = "naive" })
o:depends({ [_n("protocol")] = "hysteria" }) o:depends({ protocol = "hysteria" })
o:depends({ [_n("protocol")] = "tuic" }) o:depends({ protocol = "tuic" })
o:depends({ [_n("protocol")] = "hysteria2" }) o:depends({ protocol = "hysteria2" })
o:depends({ [_n("protocol")] = "anytls" }) o:depends({ protocol = "anytls" })
o:depends({ [_n("protocol")] = "wireguard" }) o:depends({ protocol = "wireguard" })
if singbox_tags:find("with_quic") then if singbox_tags:find("with_quic") then
o = s:option(Value, _n("hysteria_obfs"), translate("Obfs Password")) o = s:option(Value, "hysteria_obfs", translate("Obfs Password"))
o:depends({ [_n("protocol")] = "hysteria" }) o:depends({ protocol = "hysteria" })
o = s:option(Value, _n("hysteria_up_mbps"), translate("Max upload Mbps")) o = s:option(Value, "hysteria_up_mbps", translate("Max upload Mbps"))
o.default = "100" o.default = "100"
o:depends({ [_n("protocol")] = "hysteria" }) o:depends({ protocol = "hysteria" })
o = s:option(Value, _n("hysteria_down_mbps"), translate("Max download Mbps")) o = s:option(Value, "hysteria_down_mbps", translate("Max download Mbps"))
o.default = "100" o.default = "100"
o:depends({ [_n("protocol")] = "hysteria" }) o:depends({ protocol = "hysteria" })
o = s:option(Value, _n("hysteria_recv_window_conn"), translate("QUIC stream receive window")) o = s:option(Value, "hysteria_recv_window_conn", translate("QUIC stream receive window"))
o:depends({ [_n("protocol")] = "hysteria" }) o:depends({ protocol = "hysteria" })
o = s:option(Value, _n("hysteria_recv_window_client"), translate("QUIC connection receive window")) o = s:option(Value, "hysteria_recv_window_client", translate("QUIC connection receive window"))
o:depends({ [_n("protocol")] = "hysteria" }) o:depends({ protocol = "hysteria" })
o = s:option(Value, _n("hysteria_max_conn_client"), translate("QUIC concurrent bidirectional streams")) o = s:option(Value, "hysteria_max_conn_client", translate("QUIC concurrent bidirectional streams"))
o.default = "1024" o.default = "1024"
o:depends({ [_n("protocol")] = "hysteria" }) o:depends({ protocol = "hysteria" })
o = s:option(Flag, _n("hysteria_disable_mtu_discovery"), translate("Disable MTU detection")) o = s:option(Flag, "hysteria_disable_mtu_discovery", translate("Disable MTU detection"))
o:depends({ [_n("protocol")] = "hysteria" }) o:depends({ protocol = "hysteria" })
end end
if singbox_tags:find("with_quic") then if singbox_tags:find("with_quic") then
o = s:option(ListValue, _n("tuic_congestion_control"), translate("Congestion control algorithm")) o = s:option(ListValue, "tuic_congestion_control", translate("Congestion control algorithm"))
o.default = "cubic" o.default = "cubic"
o:value("bbr", translate("BBR")) o:value("bbr", translate("BBR"))
o:value("cubic", translate("CUBIC")) o:value("cubic", translate("CUBIC"))
o:value("new_reno", translate("New Reno")) o:value("new_reno", translate("New Reno"))
o:depends({ [_n("protocol")] = "tuic" }) o:depends({ protocol = "tuic" })
o = s:option(Flag, _n("tuic_zero_rtt_handshake"), translate("Enable 0-RTT QUIC handshake")) o = s:option(Flag, "tuic_zero_rtt_handshake", translate("Enable 0-RTT QUIC handshake"))
o.default = 0 o.default = 0
o:depends({ [_n("protocol")] = "tuic" }) o:depends({ protocol = "tuic" })
o = s:option(Value, _n("tuic_heartbeat"), translate("Heartbeat interval(second)")) o = s:option(Value, "tuic_heartbeat", translate("Heartbeat interval(second)"))
o.datatype = "uinteger" o.datatype = "uinteger"
o.default = "3" o.default = "3"
o:depends({ [_n("protocol")] = "tuic" }) o:depends({ protocol = "tuic" })
o = s:option(ListValue, _n("tuic_alpn"), translate("QUIC TLS ALPN")) o = s:option(ListValue, "tuic_alpn", translate("QUIC TLS ALPN"))
o.default = "default" o.default = "default"
o:value("default", translate("Default")) o:value("default", translate("Default"))
o:value("h3") o:value("h3")
@@ -135,16 +131,16 @@ if singbox_tags:find("with_quic") then
o:value("h3,h2,http/1.1") o:value("h3,h2,http/1.1")
o:value("spdy/3.1") o:value("spdy/3.1")
o:value("h3,spdy/3.1") o:value("h3,spdy/3.1")
o:depends({ [_n("protocol")] = "tuic" }) o:depends({ protocol = "tuic" })
end end
if singbox_tags:find("with_quic") then if singbox_tags:find("with_quic") then
o = s:option(Flag, _n("hysteria2_realms"), translate("Realms")) o = s:option(Flag, "hysteria2_realms", translate("Realms"))
o.default = "0" o.default = "0"
o:depends({ [_n("protocol")] = "hysteria2"}) o:depends({ protocol = "hysteria2"})
o = s:option(Value, _n("hysteria2_realm_url"), translate("Realm URL"), translate("Example:") .. "realm://public@realm.hy2.io/your-realm-name") o = s:option(Value, "hysteria2_realm_url", translate("Realm URL"), translate("Example:") .. "realm://public@realm.hy2.io/your-realm-name")
o:depends({ [_n("hysteria2_realms")] = "1" }) o:depends({ hysteria2_realms = "1" })
o.validate = function(self, value) o.validate = function(self, value)
value = api.trim(value) value = api.trim(value)
local realm = api.parse_realm_uri(value) local realm = api.parse_realm_uri(value)
@@ -152,83 +148,82 @@ if singbox_tags:find("with_quic") then
return nil, translate("Invalid Realm URL.") return nil, translate("Invalid Realm URL.")
end end
o = s:option(DynamicList, _n("hysteria2_realm_stun"), translate("Realm STUN")) o = s:option(DynamicList, "hysteria2_realm_stun", translate("Realm STUN"))
o.default = { "stun.sip.us:3478", "stun.nextcloud.com:3478", "global.stun.twilio.com:3478" } o.default = { "stun.sip.us:3478", "stun.nextcloud.com:3478", "global.stun.twilio.com:3478" }
o:depends({ [_n("hysteria2_realms")] = "1" }) o:depends({ hysteria2_realms = "1" })
o = s:option(ListValue, _n("hysteria2_obfs_type"), translate("Obfs Type")) o = s:option(ListValue, "hysteria2_obfs_type", translate("Obfs Type"))
o:value("", translate("Disable")) o:value("", translate("Disable"))
o:value("salamander") o:value("salamander")
o:value("gecko") o:value("gecko")
o:depends({ [_n("protocol")] = "hysteria2" }) o:depends({ protocol = "hysteria2" })
o = s:option(Value, _n("hysteria2_obfs_password"), translate("Obfs Password")) o = s:option(Value, "hysteria2_obfs_password", translate("Obfs Password"))
o:depends({ [_n("hysteria2_obfs_type")] = "salamander" }) o:depends({ hysteria2_obfs_type = "salamander" })
o:depends({ [_n("hysteria2_obfs_type")] = "gecko" }) o:depends({ hysteria2_obfs_type = "gecko" })
o = s:option(Value, _n("hysteria2_obfs_MinPacketSize"), translate("Gecko Packet Size (min)")) o = s:option(Value, "hysteria2_obfs_MinPacketSize", translate("Gecko Packet Size (min)"))
o.datatype = "uinteger" o.datatype = "uinteger"
o.placeholder = "512" o.placeholder = "512"
o.default = "512" o.default = "512"
o:depends({ [_n("hysteria2_obfs_type")] = "gecko" }) o:depends({ hysteria2_obfs_type = "gecko" })
o = s:option(Value, _n("hysteria2_obfs_MaxPacketSize"), translate("Gecko Packet Size (max)")) o = s:option(Value, "hysteria2_obfs_MaxPacketSize", translate("Gecko Packet Size (max)"))
o.datatype = "uinteger" o.datatype = "uinteger"
o.placeholder = "1200" o.placeholder = "1200"
o.default = "1200" o.default = "1200"
o:depends({ [_n("hysteria2_obfs_type")] = "gecko" }) o:depends({ hysteria2_obfs_type = "gecko" })
o = s:option(Flag, _n("hysteria2_ignore_client_bandwidth"), translate("Client BBR Flow Control"), translate("Commands the client to use the BBR flow control algorithm")) o = s:option(Flag, "hysteria2_ignore_client_bandwidth", translate("Client BBR Flow Control"), translate("Commands the client to use the BBR flow control algorithm"))
o.default = 0 o.default = 0
o:depends({ [_n("protocol")] = "hysteria2" }) o:depends({ protocol = "hysteria2" })
o = s:option(Value, _n("hysteria2_up_mbps"), translate("Max upload Mbps")) o = s:option(Value, "hysteria2_up_mbps", translate("Max upload Mbps"))
o:depends({ [_n("protocol")] = "hysteria2", [_n("hysteria2_ignore_client_bandwidth")] = false }) o:depends({ protocol = "hysteria2", hysteria2_ignore_client_bandwidth = false })
o = s:option(Value, _n("hysteria2_down_mbps"), translate("Max download Mbps")) o = s:option(Value, "hysteria2_down_mbps", translate("Max download Mbps"))
o:depends({ [_n("protocol")] = "hysteria2", [_n("hysteria2_ignore_client_bandwidth")] = false }) o:depends({ protocol = "hysteria2", hysteria2_ignore_client_bandwidth = false })
end end
o = s:option(ListValue, _n("d_protocol"), translate("Destination protocol")) o = s:option(ListValue, "d_protocol", translate("Destination protocol"))
o:value("tcp", "TCP") o:value("tcp", "TCP")
o:value("udp", "UDP") o:value("udp", "UDP")
o:value("tcp,udp", "TCP,UDP") o:value("tcp,udp", "TCP,UDP")
o:depends({ [_n("protocol")] = "direct" }) o:depends({ protocol = "direct" })
o = s:option(Value, _n("d_address"), translate("Destination address")) o = s:option(Value, "d_address", translate("Destination address"))
o:depends({ [_n("protocol")] = "direct" }) o:depends({ protocol = "direct" })
o = s:option(Value, _n("d_port"), translate("Destination port")) o = s:option(Value, "d_port", translate("Destination port"))
o.datatype = "port" o.datatype = "port"
o:depends({ [_n("protocol")] = "direct" }) o:depends({ protocol = "direct" })
o = s:option(Value, _n("decryption"), translate("Encrypt Method")) o = s:option(Value, "decryption", translate("Encrypt Method"))
o.default = "none" o.default = "none"
o:depends({ [_n("protocol")] = "vless" }) o:depends({ protocol = "vless" })
o = s:option(ListValue, _n("ss_method"), translate("Encrypt Method")) o = s:option(ListValue, "ss_method", translate("Encrypt Method"))
o.rewrite_option = "method"
for a, t in ipairs(ss_method_list) do o:value(t) end for a, t in ipairs(ss_method_list) do o:value(t) end
o:depends({ [_n("protocol")] = "shadowsocks" }) o:depends({ protocol = "shadowsocks" })
o = s:option(Value, _n("ss_password"), translate("Password")) o = s:option(Value, "ss_password", translate("Password"))
o:depends({ [_n("protocol")] = "shadowsocks" }) o:depends({ protocol = "shadowsocks" })
o = s:option(ListValue, _n("flow"), translate("flow")) o = s:option(ListValue, "flow", translate("flow"))
o.default = "" o.default = ""
o:value("", translate("Disable")) o:value("", translate("Disable"))
o:value("xtls-rprx-vision") o:value("xtls-rprx-vision")
o:depends({ [_n("protocol")] = "vless" , [_n("tls")] = true }) o:depends({ protocol = "vless" , tls = true })
o = s:option(Flag, _n("tls"), translate("TLS")) o = s:option(Flag, "tls", translate("TLS"))
o.default = 0 o.default = 0
o.validate = function(self, value, t) o.validate = function(self, value, t)
if value then if value then
local reality = s.fields[_n("reality")] and s.fields[_n("reality")]:formvalue(t) or nil local reality = s.fields["reality"] and s.fields["reality"]:formvalue(t) or nil
if reality and reality == "1" then return value end if reality and reality == "1" then return value end
if value == "1" then if value == "1" then
local ca = s.fields[_n("tls_certificateFile")] and s.fields[_n("tls_certificateFile")]:formvalue(t) or "" local ca = s.fields["tls_certificateFile"] and s.fields["tls_certificateFile"]:formvalue(t) or ""
local key = s.fields[_n("tls_keyFile")] and s.fields[_n("tls_keyFile")]:formvalue(t) or "" local key = s.fields["tls_keyFile"] and s.fields["tls_keyFile"]:formvalue(t) or ""
if ca == "" or key == "" then if ca == "" or key == "" then
return nil, translate("Public key and Private key path can not be empty!") return nil, translate("Public key and Private key path can not be empty!")
end end
@@ -236,40 +231,40 @@ o.validate = function(self, value, t)
return value return value
end end
end end
o:depends({ [_n("protocol")] = "http" }) o:depends({ protocol = "http" })
o:depends({ [_n("protocol")] = "vmess" }) o:depends({ protocol = "vmess" })
o:depends({ [_n("protocol")] = "vless" }) o:depends({ protocol = "vless" })
o:depends({ [_n("protocol")] = "trojan" }) o:depends({ protocol = "trojan" })
o:depends({ [_n("protocol")] = "anytls" }) o:depends({ protocol = "anytls" })
-- https://github.com/SagerNet/sing-box/commit/d2a04c4e41e6cef0937331cb6d10211f431caaab -- https://github.com/SagerNet/sing-box/commit/d2a04c4e41e6cef0937331cb6d10211f431caaab
if singbox_tags:find("with_utls") then if singbox_tags:find("with_utls") then
-- [[ REALITY ]] -- -- [[ REALITY ]] --
o = s:option(Flag, _n("reality"), translate("REALITY")) o = s:option(Flag, "reality", translate("REALITY"))
o.default = 0 o.default = 0
o:depends({ [_n("protocol")] = "http", [_n("tls")] = true }) o:depends({ protocol = "http", tls = true })
o:depends({ [_n("protocol")] = "vmess", [_n("tls")] = true }) o:depends({ protocol = "vmess", tls = true })
o:depends({ [_n("protocol")] = "vless", [_n("tls")] = true }) o:depends({ protocol = "vless", tls = true })
o:depends({ [_n("protocol")] = "trojan", [_n("tls")] = true }) o:depends({ protocol = "trojan", tls = true })
o:depends({ [_n("protocol")] = "anytls", [_n("tls")] = true }) o:depends({ protocol = "anytls", tls = true })
o = s:option(Value, _n("reality_private_key"), translate("Private Key")) o = s:option(Value, "reality_private_key", translate("Private Key"))
o:depends({ [_n("reality")] = true }) o:depends({ reality = true })
o = s:option(Value, _n("reality_shortId"), translate("Short Id")) o = s:option(Value, "reality_shortId", translate("Short Id"))
o:depends({ [_n("reality")] = true }) o:depends({ reality = true })
o = s:option(Value, _n("reality_handshake_server"), translate("Handshake Server")) o = s:option(Value, "reality_handshake_server", translate("Handshake Server"))
o.default = "google.com" o.default = "google.com"
o:depends({ [_n("reality")] = true }) o:depends({ reality = true })
o = s:option(Value, _n("reality_handshake_server_port"), translate("Handshake Server Port")) o = s:option(Value, "reality_handshake_server_port", translate("Handshake Server Port"))
o.datatype = "port" o.datatype = "port"
o.default = "443" o.default = "443"
o:depends({ [_n("reality")] = true }) o:depends({ reality = true })
end end
o = s:option(ListValue, _n("alpn"), translate("ALPN")) o = s:option(ListValue, "alpn", translate("ALPN"))
o.default = "default" o.default = "default"
o:value("default", translate("Default")) o:value("default", translate("Default"))
o:value("h3") o:value("h3")
@@ -278,22 +273,22 @@ o:value("h3,h2")
o:value("http/1.1") o:value("http/1.1")
o:value("h2,http/1.1") o:value("h2,http/1.1")
o:value("h3,h2,http/1.1") o:value("h3,h2,http/1.1")
o:depends({ [_n("tls")] = true, [_n("reality")] = false }) o:depends({ tls = true, reality = false })
o:depends({ [_n("protocol")] = "hysteria" }) o:depends({ protocol = "hysteria" })
-- [[ TLS ]] -- -- [[ TLS ]] --
o = s:option(FileUpload, _n("tls_certificateFile"), translate("Public key absolute path"), translate("as:") .. "/etc/ssl/fullchain.pem") o = s:option(FileUpload, "tls_certificateFile", translate("Public key absolute path"), translate("as:") .. "/etc/ssl/fullchain.pem")
o.default = m:get(s.section, "tls_certificateFile") or "/etc/config/ssl/" .. arg[1] .. ".pem" o.default = m:get(s.section, "tls_certificateFile") or "/etc/config/ssl/" .. arg[1] .. ".pem"
if o and o:formvalue(arg[1]) then o.default = o:formvalue(arg[1]) end if o and o:formvalue(arg[1]) then o.default = o:formvalue(arg[1]) end
o:depends({ [_n("tls")] = true, [_n("reality")] = false }) o:depends({ tls = true, reality = false })
o:depends({ [_n("protocol")] = "naive" }) o:depends({ protocol = "naive" })
o:depends({ [_n("protocol")] = "hysteria" }) o:depends({ protocol = "hysteria" })
o:depends({ [_n("protocol")] = "tuic" }) o:depends({ protocol = "tuic" })
o:depends({ [_n("protocol")] = "hysteria2" }) o:depends({ protocol = "hysteria2" })
o.validate = function(self, value, t) o.validate = function(self, value, t)
if value and value ~= "" then if value and value ~= "" then
if not fs.access(value) then if not api.fs.access(value) then
return nil, translate("Can't find this file!") return nil, translate("Can't find this file!")
else else
return value return value
@@ -302,17 +297,17 @@ o.validate = function(self, value, t)
return nil return nil
end end
o = s:option(FileUpload, _n("tls_keyFile"), translate("Private key absolute path"), translate("as:") .. "/etc/ssl/private.key") o = s:option(FileUpload, "tls_keyFile", translate("Private key absolute path"), translate("as:") .. "/etc/ssl/private.key")
o.default = m:get(s.section, "tls_keyFile") or "/etc/config/ssl/" .. arg[1] .. ".key" o.default = m:get(s.section, "tls_keyFile") or "/etc/config/ssl/" .. arg[1] .. ".key"
if o and o:formvalue(arg[1]) then o.default = o:formvalue(arg[1]) end if o and o:formvalue(arg[1]) then o.default = o:formvalue(arg[1]) end
o:depends({ [_n("tls")] = true, [_n("reality")] = false }) o:depends({ tls = true, reality = false })
o:depends({ [_n("protocol")] = "naive" }) o:depends({ protocol = "naive" })
o:depends({ [_n("protocol")] = "hysteria" }) o:depends({ protocol = "hysteria" })
o:depends({ [_n("protocol")] = "tuic" }) o:depends({ protocol = "tuic" })
o:depends({ [_n("protocol")] = "hysteria2" }) o:depends({ protocol = "hysteria2" })
o.validate = function(self, value, t) o.validate = function(self, value, t)
if value and value ~= "" then if value and value ~= "" then
if not fs.access(value) then if not api.fs.access(value) then
return nil, translate("Can't find this file!") return nil, translate("Can't find this file!")
else else
return value return value
@@ -321,19 +316,19 @@ o.validate = function(self, value, t)
return nil return nil
end end
o = s:option(Flag, _n("ech"), translate("ECH")) o = s:option(Flag, "ech", translate("ECH"))
o.default = "0" o.default = "0"
o:depends({ [_n("tls")] = true, [_n("flow")] = "", [_n("reality")] = false }) o:depends({ tls = true, flow = "", reality = false })
o:depends({ [_n("protocol")] = "naive" }) o:depends({ protocol = "naive" })
o:depends({ [_n("protocol")] = "hysteria" }) o:depends({ protocol = "hysteria" })
o:depends({ [_n("protocol")] = "tuic" }) o:depends({ protocol = "tuic" })
o:depends({ [_n("protocol")] = "hysteria2", [_n("hysteria2_realms")] = false }) o:depends({ protocol = "hysteria2", hysteria2_realms = false })
o = s:option(TextValue, _n("ech_key"), translate("ECH Key")) o = s:option(TextValue, "ech_key", translate("ECH Key"))
o.default = "" o.default = ""
o.rows = 5 o.rows = 5
o.wrap = "off" o.wrap = "off"
o:depends({ [_n("ech")] = true }) o:depends({ ech = true })
o.validate = function(self, value) o.validate = function(self, value)
value = value:gsub("^%s+", ""):gsub("%s+$","\n"):gsub("\r\n","\n"):gsub("[ \t]*\n[ \t]*", "\n") value = value:gsub("^%s+", ""):gsub("%s+$","\n"):gsub("\r\n","\n"):gsub("[ \t]*\n[ \t]*", "\n")
value = value:gsub("^%s*\n", "") value = value:gsub("^%s*\n", "")
@@ -343,100 +338,100 @@ o.validate = function(self, value)
return value return value
end end
o = s:option(ListValue, _n("transport"), translate("Transport")) o = s:option(ListValue, "transport", translate("Transport"))
o:value("tcp", "TCP") o:value("tcp", "TCP")
o:value("http", "HTTP") o:value("http", "HTTP")
o:value("ws", "WebSocket") o:value("ws", "WebSocket")
o:value("httpupgrade", "HTTPUpgrade") o:value("httpupgrade", "HTTPUpgrade")
o:value("quic", "QUIC") o:value("quic", "QUIC")
o:value("grpc", "gRPC") o:value("grpc", "gRPC")
o:depends({ [_n("protocol")] = "shadowsocks" }) o:depends({ protocol = "shadowsocks" })
o:depends({ [_n("protocol")] = "vmess" }) o:depends({ protocol = "vmess" })
o:depends({ [_n("protocol")] = "vless" }) o:depends({ protocol = "vless" })
o:depends({ [_n("protocol")] = "trojan" }) o:depends({ protocol = "trojan" })
-- [[ HTTP ]]-- -- [[ HTTP ]]--
o = s:option(DynamicList, _n("http_host"), translate("HTTP Host")) o = s:option(DynamicList, "http_host", translate("HTTP Host"))
o:depends({ [_n("transport")] = "http" }) o:depends({ transport = "http" })
o = s:option(Value, _n("http_path"), translate("HTTP Path")) o = s:option(Value, "http_path", translate("HTTP Path"))
o:depends({ [_n("transport")] = "http" }) o:depends({ transport = "http" })
-- [[ WebSocket ]]-- -- [[ WebSocket ]]--
o = s:option(Value, _n("ws_host"), translate("WebSocket Host")) o = s:option(Value, "ws_host", translate("WebSocket Host"))
o:depends({ [_n("transport")] = "ws" }) o:depends({ transport = "ws" })
o = s:option(Value, _n("ws_path"), translate("WebSocket Path")) o = s:option(Value, "ws_path", translate("WebSocket Path"))
o:depends({ [_n("transport")] = "ws" }) o:depends({ transport = "ws" })
-- [[ HTTPUpgrade ]]-- -- [[ HTTPUpgrade ]]--
o = s:option(Value, _n("httpupgrade_host"), translate("HTTPUpgrade Host")) o = s:option(Value, "httpupgrade_host", translate("HTTPUpgrade Host"))
o:depends({ [_n("transport")] = "httpupgrade" }) o:depends({ transport = "httpupgrade" })
o = s:option(Value, _n("httpupgrade_path"), translate("HTTPUpgrade Path")) o = s:option(Value, "httpupgrade_path", translate("HTTPUpgrade Path"))
o:depends({ [_n("transport")] = "httpupgrade" }) o:depends({ transport = "httpupgrade" })
-- [[ gRPC ]]-- -- [[ gRPC ]]--
o = s:option(Value, _n("grpc_serviceName"), "ServiceName") o = s:option(Value, "grpc_serviceName", "ServiceName")
o:depends({ [_n("transport")] = "grpc" }) o:depends({ transport = "grpc" })
-- [[ Mux ]]-- -- [[ Mux ]]--
o = s:option(Flag, _n("mux"), translate("Mux")) o = s:option(Flag, "mux", translate("Mux"))
o.rmempty = false o.rmempty = false
o:depends({ [_n("protocol")] = "vmess" }) o:depends({ protocol = "vmess" })
o:depends({ [_n("protocol")] = "vless", [_n("flow")] = "" }) o:depends({ protocol = "vless", flow = "" })
o:depends({ [_n("protocol")] = "shadowsocks" }) o:depends({ protocol = "shadowsocks" })
o:depends({ [_n("protocol")] = "trojan" }) o:depends({ protocol = "trojan" })
-- [[ TCP Brutal ]]-- -- [[ TCP Brutal ]]--
o = s:option(Flag, _n("tcpbrutal"), translate("TCP Brutal")) o = s:option(Flag, "tcpbrutal", translate("TCP Brutal"))
o.default = 0 o.default = 0
o:depends({ [_n("mux")] = true }) o:depends({ mux = true })
o = s:option(Value, _n("tcpbrutal_up_mbps"), translate("Max upload Mbps")) o = s:option(Value, "tcpbrutal_up_mbps", translate("Max upload Mbps"))
o.default = "10" o.default = "10"
o:depends({ [_n("tcpbrutal")] = true }) o:depends({ tcpbrutal = true })
o = s:option(Value, _n("tcpbrutal_down_mbps"), translate("Max download Mbps")) o = s:option(Value, "tcpbrutal_down_mbps", translate("Max download Mbps"))
o.default = "50" o.default = "50"
o:depends({ [_n("tcpbrutal")] = true }) o:depends({ tcpbrutal = true })
if singbox_tags:find("with_wireguard") then if singbox_tags:find("with_wireguard") then
o = s:option(Flag, _n("wireguard_system_interface"), translate("System interface")) o = s:option(Flag, "wireguard_system_interface", translate("System interface"))
o.default = 0 o.default = 0
o:depends({ [_n("protocol")] = "wireguard" }) o:depends({ protocol = "wireguard" })
o = s:option(Value, _n("wireguard_mtu"), "MTU") o = s:option(Value, "wireguard_mtu", "MTU")
o.default = "1408" o.default = "1408"
o:depends({ [_n("protocol")] = "wireguard" }) o:depends({ protocol = "wireguard" })
o = s:option(DynamicList, _n("wireguard_local_address"), translate("Local Address")) o = s:option(DynamicList, "wireguard_local_address", translate("Local Address"))
o:depends({ [_n("protocol")] = "wireguard" }) o:depends({ protocol = "wireguard" })
o = s:option(Value, _n("wireguard_private_key"), translate("Private Key")) o = s:option(Value, "wireguard_private_key", translate("Private Key"))
o.datatype = "base64" o.datatype = "base64"
o:depends({ [_n("protocol")] = "wireguard" }) o:depends({ protocol = "wireguard" })
o = s:option(Value, _n("wireguard_public_key"), translate("Public Key")) o = s:option(Value, "wireguard_public_key", translate("Public Key"))
o.datatype = "base64" o.datatype = "base64"
o:depends({ [_n("protocol")] = "wireguard" }) o:depends({ protocol = "wireguard" })
o = s:option(DummyValue, _n("gen_wireguard_key")) o = s:option(DummyValue, "gen_wireguard_key")
o.prefix = option_prefix o.prefix = option_prefix
o.template = m:template_path("/server/server_wireguard") o.template = m:template_path("/server/server_wireguard")
o:depends({ [_n("protocol")] = "wireguard" }) o:depends({ protocol = "wireguard" })
end end
o = s:option(Flag, _n("bind_local"), translate("Bind Local"), translate("When selected, it can only be accessed localhost.")) o = s:option(Flag, "bind_local", translate("Bind Local"), translate("When selected, it can only be accessed localhost."))
o.default = "0" o.default = "0"
o:depends({ [_n("custom")] = false }) o:depends({ custom = false })
o = s:option(Flag, _n("accept_lan"), translate("Accept LAN Access"), translate("When selected, it can accessed lan , this will not be safe!")) o = s:option(Flag, "accept_lan", translate("Accept LAN Access"), translate("When selected, it can accessed lan , this will not be safe!"))
o.default = "0" o.default = "0"
o:depends({ [_n("custom")] = false }) o:depends({ custom = false })
local nodes_table = {} local nodes_table = {}
for k, e in ipairs(api.get_valid_nodes()) do for k, e in ipairs(api.get_valid_nodes()) do
@@ -449,7 +444,7 @@ for k, e in ipairs(api.get_valid_nodes()) do
end end
end end
o = s:option(ListValue, _n("outbound_node"), translate("outbound node")) o = s:option(ListValue, "outbound_node", translate("outbound node"))
o:value("", translate("Close")) o:value("", translate("Close"))
o:value("_socks", translate("Custom Socks")) o:value("_socks", translate("Custom Socks"))
o:value("_http", translate("Custom HTTP")) o:value("_http", translate("Custom HTTP"))
@@ -460,37 +455,37 @@ for k, v in pairs(nodes_table) do
o:value(v.id, v.remarks) o:value(v.id, v.remarks)
o.group[#o.group+1] = (v.group and v.group ~= "") and v.group or translate("default") o.group[#o.group+1] = (v.group and v.group ~= "") and v.group or translate("default")
end end
o:depends({ [_n("custom")] = false }) o:depends({ custom = false })
o = s:option(Value, _n("outbound_node_address"), translate("Address (Support Domain Name)")) o = s:option(Value, "outbound_node_address", translate("Address (Support Domain Name)"))
o:depends({ [_n("outbound_node")] = "_socks" }) o:depends({ outbound_node = "_socks" })
o:depends({ [_n("outbound_node")] = "_http" }) o:depends({ outbound_node = "_http" })
o = s:option(Value, _n("outbound_node_port"), translate("Port")) o = s:option(Value, "outbound_node_port", translate("Port"))
o.datatype = "port" o.datatype = "port"
o:depends({ [_n("outbound_node")] = "_socks" }) o:depends({ outbound_node = "_socks" })
o:depends({ [_n("outbound_node")] = "_http" }) o:depends({ outbound_node = "_http" })
o = s:option(Value, _n("outbound_node_username"), translate("Username")) o = s:option(Value, "outbound_node_username", translate("Username"))
o:depends({ [_n("outbound_node")] = "_socks" }) o:depends({ outbound_node = "_socks" })
o:depends({ [_n("outbound_node")] = "_http" }) o:depends({ outbound_node = "_http" })
o = s:option(Value, _n("outbound_node_password"), translate("Password")) o = s:option(Value, "outbound_node_password", translate("Password"))
o.password = true o.password = true
o:depends({ [_n("outbound_node")] = "_socks" }) o:depends({ outbound_node = "_socks" })
o:depends({ [_n("outbound_node")] = "_http" }) o:depends({ outbound_node = "_http" })
o = s:option(Value, _n("outbound_node_iface"), translate("Interface")) o = s:option(Value, "outbound_node_iface", translate("Interface"))
o:depends({ [_n("outbound_node")] = "_iface" }) o:depends({ outbound_node = "_iface" })
local netdev_list = api.get_network_devices() local netdev_list = api.get_network_devices()
for _, d in ipairs(netdev_list) do for _, d in ipairs(netdev_list) do
o:value(d.name, d.label) o:value(d.name, d.label)
end end
o = s:option(TextValue, _n("custom_config"), translate("Custom Config") .. " (JSON)") o = s:option(TextValue, "custom_config", translate("Custom Config") .. " (JSON)")
o.rows = 10 o.rows = 10
o.wrap = "off" o.wrap = "off"
o:depends({ [_n("custom")] = true }) o:depends({ custom = true })
o.datatype = "json" o.datatype = "json"
local o_validate = o.validate local o_validate = o.validate
o.validate = function(self, value) o.validate = function(self, value)
@@ -508,16 +503,16 @@ o.custom_write = function(self, section, value)
m:set(section, "config_str", api.base64Encode(value) or "") m:set(section, "config_str", api.base64Encode(value) or "")
end end
o = s:option(Flag, _n("log"), translate("Log")) o = s:option(Flag, "log", translate("Log"))
o.default = "1" o.default = "1"
o.rmempty = false o.rmempty = false
o = s:option(ListValue, _n("loglevel"), translate("Log Level")) o = s:option(ListValue, "loglevel", translate("Log Level"))
o.default = "info" o.default = "info"
o:value("debug") o:value("debug")
o:value("info") o:value("info")
o:value("warn") o:value("warn")
o:value("error") o:value("error")
o:depends({ [_n("log")] = true }) o:depends({ log = true })
api.luci_types(arg[1], m, s, type_name, option_prefix) api.luci_types(s1, s)
@@ -1,24 +1,20 @@
local m, s = ...
if not api.is_finded("ssserver") then if not api.is_finded("ssserver") then
return return
end end
-- [[ Shadowsocks Rust ]]
local m, s1 = ...
local type_name = "SS-Rust" local type_name = "SS-Rust"
-- [[ Shadowsocks Rust ]] s1.fields["type"]:value(type_name, translate("Shadowsocks Rust"))
s.fields["type"]:value(type_name, translate("Shadowsocks Rust")) if s1.val["type"] and s1.val["type"] ~= type_name then
if s.val["type"] and s.val["type"] ~= type_name then
return return
end end
local option_prefix = "ssrust_" local s = NamedSection(m, arg[1], "server")
s.type_name = type_name
local function _n(name) s.option_prefix = "ssrust_"
return option_prefix .. name
end
local ssrust_encrypt_method_list = { local ssrust_encrypt_method_list = {
"plain", "none", "plain", "none",
@@ -26,35 +22,12 @@ local ssrust_encrypt_method_list = {
"2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha8-poly1305", "2022-blake3-chacha20-poly1305" "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha8-poly1305", "2022-blake3-chacha20-poly1305"
} }
o = s:option(Flag, _n("custom"), translate("Use Custom Config")) o = s:option(Flag, "custom", translate("Use Custom Config"))
o = s:option(Value, _n("port"), translate("Listen Port")) o = s:option(TextValue, "custom_config", translate("Custom Config") .. " (JSON)")
o.datatype = "port"
o:depends({ [_n("custom")] = false })
o = s:option(ListValue, _n("user"), translate("User"))
for i, v in ipairs(user_list) do
o:value(v[".name"], v.username)
end
o:depends({ [_n("custom")] = false })
o = s:option(ListValue, _n("method"), translate("Encrypt Method"))
for a, t in ipairs(ssrust_encrypt_method_list) do o:value(t) end
o:depends({ [_n("custom")] = false })
o = s:option(Value, _n("timeout"), translate("Connection Timeout"))
o.datatype = "uinteger"
o.default = 300
o:depends({ [_n("custom")] = false })
o = s:option(Flag, _n("tcp_fast_open"), "TCP " .. translate("Fast Open"))
o.default = "0"
o:depends({ [_n("custom")] = false })
o = s:option(TextValue, _n("custom_config"), translate("Custom Config") .. " (JSON)")
o.rows = 10 o.rows = 10
o.wrap = "off" o.wrap = "off"
o:depends({ [_n("custom")] = true }) o:depends({ custom = true })
o.datatype = "json" o.datatype = "json"
local o_validate = o.validate local o_validate = o.validate
o.validate = function(self, value) o.validate = function(self, value)
@@ -72,8 +45,31 @@ o.custom_write = function(self, section, value)
m:set(section, "config_str", api.base64Encode(value) or "") m:set(section, "config_str", api.base64Encode(value) or "")
end end
o = s:option(Flag, _n("log"), translate("Log")) o = s:option(Value, "port", translate("Listen Port"))
o.datatype = "port"
o:depends({ custom = false })
o = s:option(ListValue, "user", translate("User"))
for i, v in ipairs(user_list) do
o:value(v[".name"], v.username)
end
o:depends({ custom = false })
o = s:option(ListValue, "method", translate("Encrypt Method"))
for a, t in ipairs(ssrust_encrypt_method_list) do o:value(t) end
o:depends({ custom = false })
o = s:option(Value, "timeout", translate("Connection Timeout"))
o.datatype = "uinteger"
o.default = 300
o:depends({ custom = false })
o = s:option(Flag, "tcp_fast_open", "TCP " .. translate("Fast Open"))
o.default = "0"
o:depends({ custom = false })
o = s:option(Flag, "log", translate("Log"))
o.default = "1" o.default = "1"
o.rmempty = false o.rmempty = false
api.luci_types(arg[1], m, s, type_name, option_prefix) api.luci_types(s1, s)
@@ -1,24 +1,20 @@
local m, s = ...
if not api.is_finded("ssr-server") then if not api.is_finded("ssr-server") then
return return
end end
-- [[ ShadowsocksR ]]
local m, s1 = ...
local type_name = "SSR" local type_name = "SSR"
-- [[ ShadowsocksR ]] s1.fields["type"]:value(type_name, translate("ShadowsocksR"))
s.fields["type"]:value(type_name, translate("ShadowsocksR")) if s1.val["type"] and s1.val["type"] ~= type_name then
if s.val["type"] and s.val["type"] ~= type_name then
return return
end end
local option_prefix = "ssr_" local s = NamedSection(m, arg[1], "server")
s.type_name = type_name
local function _n(name) s.option_prefix = "ssr_"
return option_prefix .. name
end
local ssr_encrypt_method_list = { local ssr_encrypt_method_list = {
"none", "table", "rc2-cfb", "rc4", "rc4-md5", "rc4-md5-6", "aes-128-cfb", "none", "table", "rc2-cfb", "rc4", "rc4-md5", "rc4-md5-6", "aes-128-cfb",
@@ -39,49 +35,12 @@ local ssr_obfs_list = {
"tls1.0_session_auth", "tls1.2_ticket_auth" "tls1.0_session_auth", "tls1.2_ticket_auth"
} }
o = s:option(Flag, _n("custom"), translate("Use Custom Config")) o = s:option(Flag, "custom", translate("Use Custom Config"))
o = s:option(Value, _n("port"), translate("Listen Port")) o = s:option(TextValue, "custom_config", translate("Custom Config") .. " (JSON)")
o.datatype = "port"
o:depends({ [_n("custom")] = false })
o = s:option(ListValue, _n("user"), translate("User"))
for i, v in ipairs(user_list) do
o:value(v[".name"], v.username)
end
o:depends({ [_n("custom")] = false })
o = s:option(ListValue, _n("method"), translate("Encrypt Method"))
for a, t in ipairs(ssr_encrypt_method_list) do o:value(t) end
o:depends({ [_n("custom")] = false })
o = s:option(ListValue, _n("protocol"), translate("Protocol"))
for a, t in ipairs(ssr_protocol_list) do o:value(t) end
o:depends({ [_n("custom")] = false })
o = s:option(Value, _n("protocol_param"), translate("Protocol_param"))
o:depends({ [_n("custom")] = false })
o = s:option(ListValue, _n("obfs"), translate("Obfs"))
for a, t in ipairs(ssr_obfs_list) do o:value(t) end
o:depends({ [_n("custom")] = false })
o = s:option(Value, _n("obfs_param"), translate("Obfs_param"))
o:depends({ [_n("custom")] = false })
o = s:option(Value, _n("timeout"), translate("Connection Timeout"))
o.datatype = "uinteger"
o.default = 300
o:depends({ [_n("custom")] = false })
o = s:option(Flag, _n("tcp_fast_open"), "TCP " .. translate("Fast Open"))
o.default = "0"
o:depends({ [_n("custom")] = false })
o = s:option(TextValue, _n("custom_config"), translate("Custom Config") .. " (JSON)")
o.rows = 10 o.rows = 10
o.wrap = "off" o.wrap = "off"
o:depends({ [_n("custom")] = true }) o:depends({ custom = true })
o.datatype = "json" o.datatype = "json"
local o_validate = o.validate local o_validate = o.validate
o.validate = function(self, value) o.validate = function(self, value)
@@ -99,12 +58,49 @@ o.custom_write = function(self, section, value)
m:set(section, "config_str", api.base64Encode(value) or "") m:set(section, "config_str", api.base64Encode(value) or "")
end end
o = s:option(Flag, _n("udp_forward"), translate("UDP Forward")) o = s:option(Value, "port", translate("Listen Port"))
o.datatype = "port"
o:depends({ custom = false })
o = s:option(ListValue, "user", translate("User"))
for i, v in ipairs(user_list) do
o:value(v[".name"], v.username)
end
o:depends({ custom = false })
o = s:option(ListValue, "method", translate("Encrypt Method"))
for a, t in ipairs(ssr_encrypt_method_list) do o:value(t) end
o:depends({ custom = false })
o = s:option(ListValue, "protocol", translate("Protocol"))
for a, t in ipairs(ssr_protocol_list) do o:value(t) end
o:depends({ custom = false })
o = s:option(Value, "protocol_param", translate("Protocol_param"))
o:depends({ custom = false })
o = s:option(ListValue, "obfs", translate("Obfs"))
for a, t in ipairs(ssr_obfs_list) do o:value(t) end
o:depends({ custom = false })
o = s:option(Value, "obfs_param", translate("Obfs_param"))
o:depends({ custom = false })
o = s:option(Value, "timeout", translate("Connection Timeout"))
o.datatype = "uinteger"
o.default = 300
o:depends({ custom = false })
o = s:option(Flag, "tcp_fast_open", "TCP " .. translate("Fast Open"))
o.default = "0"
o:depends({ custom = false })
o = s:option(Flag, "udp_forward", translate("UDP Forward"))
o.default = "1" o.default = "1"
o.rmempty = false o.rmempty = false
o = s:option(Flag, _n("log"), translate("Log")) o = s:option(Flag, "log", translate("Log"))
o.default = "1" o.default = "1"
o.rmempty = false o.rmempty = false
api.luci_types(arg[1], m, s, type_name, option_prefix) api.luci_types(s1, s)
+114 -107
View File
@@ -1,3 +1,6 @@
-- Copyright (C) 2022-2025 xiaorouji
-- Copyright (C) 2026 Openwrt-Passwall Organization
module("luci.passwall2.api", package.seeall) module("luci.passwall2.api", package.seeall)
appname = "passwall2" appname = "passwall2"
c_config = "passwall2" c_config = "passwall2"
@@ -17,10 +20,11 @@ command_timeout = 300
OPENWRT_ARCH = nil OPENWRT_ARCH = nil
DISTRIB_ARCH = nil DISTRIB_ARCH = nil
LOCK_PREFIX = "/tmp/lock/passwall2" LOCK_PREFIX = "/tmp/lock/" .. c_config
LOG_FILE = "/tmp/log/passwall2.log" LOG_FILE = "/tmp/log/" .. c_config .. ".log"
TMP_PATH = "/tmp/etc/passwall2" TMP_PATH = "/tmp/etc/" .. c_config
CACHE_PATH = TMP_PATH .. "_tmp" CACHE_PATH = TMP_PATH .. "_tmp"
S_TMP_PATH = "/tmp/etc/" .. s_config
TMP_IFACE_PATH = TMP_PATH .. "/iface" TMP_IFACE_PATH = TMP_PATH .. "/iface"
local lang = uci:get("luci", "main", "lang") or "auto" local lang = uci:get("luci", "main", "lang") or "auto"
@@ -814,16 +818,16 @@ function clone(org)
end end
local function get_bin_version_cache(file, cmd) local function get_bin_version_cache(file, cmd)
sys.call("mkdir -p /tmp/etc/passwall2_tmp") sys.call("mkdir -p " .. CACHE_PATH)
if fs.access(file) then if fs.access(file) then
chmod_755(file) chmod_755(file)
local md5 = sys.exec("echo -n $(md5sum " .. file .. " | awk '{print $1}')") local md5 = sys.exec("echo -n $(md5sum " .. file .. " | awk '{print $1}')")
if fs.access("/tmp/etc/passwall2_tmp/" .. md5) then if fs.access(CACHE_PATH .. "/" .. md5) then
return sys.exec("echo -n $(cat /tmp/etc/passwall2_tmp/%s)" % md5) return sys.exec("echo -n $(cat %s)" % { CACHE_PATH .. "/" .. md5 })
else else
local version = sys.exec(string.format("echo -n $(%s %s)", file, cmd)) local version = sys.exec(string.format("echo -n $(%s %s)", file, cmd))
if version and version ~= "" then if version and version ~= "" then
sys.call("echo '" .. version .. "' > " .. "/tmp/etc/passwall2_tmp/" .. md5) sys.call("echo '%s' > %s" % { version, CACHE_PATH .. "/" .. md5})
return version return version
end end
end end
@@ -1415,12 +1419,24 @@ function set_default_cbi()
if true then if true then
--Map --Map
local Map = cbi.Map local Map = cbi.Map
local original_init = Map.__init__ local default_init = Map.__init__
function Map.__init__(self, config, ...) function Map.__init__(self, config, ...)
if not config then config = c_config end if not config then config = c_config end
original_init(self, config, ...) default_init(self, config, ...)
self.api = require "luci.passwall2.api" self.api = require "luci.passwall2.api"
end end
if is_js_luci() == true then
local default_parse = Map.parse
function Map.parse(self, ...)
apply_redirect(self)
local old = self.on_after_save
self.on_after_save = function(self)
if old then old(self) end
self:set("@global[0]", "timestamp", os.time())
end
return default_parse(self, ...)
end
end
function Map.foreach(self, stype, func) function Map.foreach(self, stype, func)
self.uci:foreach(self.config, stype, func) self.uci:foreach(self.config, stype, func)
end end
@@ -1459,25 +1475,6 @@ function set_default_cbi()
sh_uci_set(c_config, "@global[0]", "auto_lang", lang, true) sh_uci_set(c_config, "@global[0]", "auto_lang", lang, true)
end end
end end
if is_js_luci() == true then
local hide_popup_box = nil
if hide_popup_box == true then
Map.apply_on_parse = false
Map.on_after_apply = function(self)
if self.redirect then
os.execute("sleep 1")
luci.http.redirect(self.redirect)
end
end
else
apply_redirect(Map)
local old = Map.on_after_save
Map.on_after_save = function(self)
if old then old(self) end
self:set("@global[0]", "timestamp", os.time())
end
end
end
end end
if true then if true then
--AbstractSection --AbstractSection
@@ -1498,9 +1495,9 @@ function set_default_cbi()
if true then if true then
--TextValue --TextValue
local TextValue = cbi.TextValue local TextValue = cbi.TextValue
local original_init = TextValue.__init__ local default_init = TextValue.__init__
function TextValue.__init__(self, ...) function TextValue.__init__(self, ...)
original_init(self, ...) default_init(self, ...)
self.template = appname .. "/cbi/tvalue" self.template = appname .. "/cbi/tvalue"
end end
end end
@@ -1543,102 +1540,112 @@ function return_map(map)
return map return map
end end
function luci_types(id, m, s, type_name, option_prefix) function luci_types(s, s2)
local cbi = require "luci.cbi" local cbi = require "luci.cbi"
local m = s.map
local id = s2.section
local type_name = s2.type_name
local option_prefix = s2.option_prefix
local fv_type local fv_type
local field_type = s.fields["type"] local field_type = s.fields["type"]
if field_type then if field_type then
fv_type = field_type:formvalue(id) fv_type = field_type:formvalue(id)
end end
local rewrite_option_table = {} for i, v in ipairs(s2.children) do
for key, value in pairs(s.fields) do local o = s2.children[i]
if key:find(option_prefix) == 1 then o.config_option = o.option
if not s.fields[key].not_rewrite then o.option = option_prefix .. o.option
if s.fields[key].rewrite_option then if not o.not_rewrite then
if not rewrite_option_table[s.fields[key].rewrite_option] then o.cfgvalue = function(self, section)
rewrite_option_table[s.fields[key].rewrite_option] = 1 -- Add a custom `custom_cfgvalue` attribute. If a custom `custom_cfgvalue` function exists, the custom `cfgvalue` logic will be used.
if self.custom_cfgvalue then
return self:custom_cfgvalue(section)
else
if self.rewrite_option then
return m:get(section, self.rewrite_option)
else else
rewrite_option_table[s.fields[key].rewrite_option] = rewrite_option_table[s.fields[key].rewrite_option] + 1 return m:get(section, self.config_option)
end end
end end
end
s.fields[key].cfgvalue = function(self, section) o.write = function(self, section, value)
-- Add a custom `custom_cfgvalue` attribute. If a custom `custom_cfgvalue` function exists, the custom `cfgvalue` logic will be used. if s.fields["type"]:formvalue(id) == type_name then
if self.custom_cfgvalue then -- Add a custom `custom_write` attribute; if a custom `custom_write` function exists, then use the custom write logic.
return self:custom_cfgvalue(section) if self.custom_write then
self:custom_write(section, value)
else
local new_val = value
if util.instanceof(self, cbi.DynamicList) then
local new_t = {}
if type(value) == "table" then
new_t = table_remove_duplicates(value)
else
new_t = { value }
end
if self.cast == "string" then
new_val = table.concat(new_t, " ")
else
new_val = new_t
end
end
if self.rewrite_option then
m:set(section, self.rewrite_option, new_val)
else
m:set(section, self.config_option, new_val)
end
end
end
end
o.remove = function(self, section)
if s.fields["type"]:formvalue(id) == type_name then
-- Add a custom `custom_remove` attribute; if a custom `custom_remove` function exists, use the custom remove logic.
if self.custom_remove then
self:custom_remove(section)
else else
if self.rewrite_option then if self.rewrite_option then
return m:get(section, self.rewrite_option) m:del(section, self.rewrite_option)
else else
if self.option:find(option_prefix) == 1 then m:del(section, self.config_option)
return m:get(section, self.option:sub(1 + #option_prefix))
end
end end
end end
end end
s.fields[key].write = function(self, section, value)
if s.fields["type"]:formvalue(id) == type_name then
-- Add a custom `custom_write` attribute; if a custom `custom_write` function exists, then use the custom write logic.
if self.custom_write then
self:custom_write(section, value)
else
local new_val = value
if util.instanceof(self, cbi.DynamicList) then
local new_t = {}
if type(value) == "table" then
new_t = table_remove_duplicates(value)
else
new_t = { value }
end
if self.cast == "string" then
new_val = table.concat(new_t, " ")
else
new_val = new_t
end
end
if self.rewrite_option then
m:set(section, self.rewrite_option, new_val)
else
if self.option:find(option_prefix) == 1 then
m:set(section, self.option:sub(1 + #option_prefix), new_val)
end
end
end
end
end
s.fields[key].remove = function(self, section)
if s.fields["type"]:formvalue(id) == type_name then
-- Add a custom `custom_remove` attribute; if a custom `custom_remove` function exists, use the custom remove logic.
if self.custom_remove then
self:custom_remove(section)
else
if self.rewrite_option and rewrite_option_table[self.rewrite_option] == 1 then
m:del(section, self.rewrite_option)
else
if self.option:find(option_prefix) == 1 then
m:del(section, self.option:sub(1 + #option_prefix))
end
end
end
end
end
end
local deps = s.fields[key].deps
if #deps > 0 then
for index, value in ipairs(deps) do
deps[index]["type"] = type_name
end
else
s.fields[key]:depends({ type = type_name })
end
if fv_type and fv_type ~= type_name then
s.fields[key].rmempty = true
end end
end end
local deps = o.deps
if #deps > 0 then
local function process_deps(dep)
local rewrite_deps = {}
for k, v in pairs(dep) do
if k:find("!") then
rewrite_deps[k] = v
else
rewrite_deps[option_prefix .. k] = v
end
end
if not rewrite_deps['!reverse'] then
rewrite_deps["type"] = type_name
end
return rewrite_deps
end
for index, value in ipairs(deps) do
local rewrite_deps = process_deps(value)
if rewrite_deps then
deps[index] = rewrite_deps
end
end
else
o:depends({ type = type_name })
end
if fv_type and fv_type ~= type_name then
o.rmempty = true
end
s:append(o)
end end
end end
function format_go_time(input, default) function format_go_time(input, default)
input = input and trim(input) input = input and trim(input)
local N = 0 local N = 0
@@ -7,7 +7,7 @@ local uci = api.uci
local jsonc = api.jsonc local jsonc = api.jsonc
local CONFIG = api.s_config local CONFIG = api.s_config
local CONFIG_PATH = "/tmp/etc/" .. CONFIG local CONFIG_PATH = api.S_TMP_PATH
local NFT_INCLUDE_FILE = CONFIG_PATH .. "/" .. CONFIG .. ".nft" local NFT_INCLUDE_FILE = CONFIG_PATH .. "/" .. CONFIG .. ".nft"
local LOG_APP_FILE = "/tmp/log/" .. CONFIG .. ".log" local LOG_APP_FILE = "/tmp/log/" .. CONFIG .. ".log"
local TMP_BIN_PATH = CONFIG_PATH .. "/bin" local TMP_BIN_PATH = CONFIG_PATH .. "/bin"
@@ -53,7 +53,7 @@ local function ln_run(s, d, command, output)
end end
local function gen_include() local function gen_include()
cmd(string.format("echo '#!/bin/sh' > /tmp/etc/%s.include", CONFIG)) cmd(string.format("echo '#!/bin/sh' > %s.include", CONFIG_PATH))
local function extract_rules(n, a) local function extract_rules(n, a)
local _ipt = ipt_bin local _ipt = ipt_bin
if n == "6" then if n == "6" then
@@ -64,7 +64,7 @@ local function gen_include()
result = result .. "COMMIT" result = result .. "COMMIT"
return result return result
end end
local f, err = io.open("/tmp/etc/" .. CONFIG .. ".include", "a") local f, err = io.open(CONFIG_PATH .. ".include", "a")
if f and err == nil then if f and err == nil then
if nft_flag == "0" then if nft_flag == "0" then
f:write(ipt_bin .. '-save -c | grep -v "PSW2-SERVER" | ' .. ipt_bin .. '-restore -c' .. "\n") f:write(ipt_bin .. '-save -c | grep -v "PSW2-SERVER" | ' .. ipt_bin .. '-restore -c' .. "\n")
@@ -232,7 +232,7 @@ local function stop()
cmd("nft flush chain inet fw4 PSW2-SERVER 2>/dev/null") cmd("nft flush chain inet fw4 PSW2-SERVER 2>/dev/null")
cmd("nft delete chain inet fw4 PSW2-SERVER 2>/dev/null") cmd("nft delete chain inet fw4 PSW2-SERVER 2>/dev/null")
end end
cmd(string.format("rm -rf %s %s /tmp/etc/%s.include", CONFIG_PATH, LOG_APP_FILE, CONFIG)) cmd(string.format("rm -rf %s %s %s", CONFIG_PATH, LOG_APP_FILE, CONFIG_PATH .. ".include"))
end end
if action then if action then
@@ -18,8 +18,8 @@ function gen_config_server(node)
end end
local config = { local config = {
listen = (function() listen = (function()
if node.hysteria2_realms and node.hysteria2_realm_url then if node.realms and node.realm_url then
local url = node.hysteria2_realm_url:gsub("/+$", "") local url = node.realm_url:gsub("/+$", "")
if node.port then if node.port then
url = url .. (url:find("?") and "&lport=" or "?lport=") .. node.port url = url .. (url:find("?") and "&lport=" or "?lport=") .. node.port
end end
@@ -31,30 +31,30 @@ function gen_config_server(node)
cert = node.tls_certificateFile, cert = node.tls_certificateFile,
key = node.tls_keyFile, key = node.tls_keyFile,
}, },
obfs = (node.hysteria2_obfs_type and node.hysteria2_obfs_password) and { obfs = (node.obfs_type and node.obfs_password) and {
type = node.hysteria2_obfs_type, type = node.obfs_type,
[node.hysteria2_obfs_type] = { [node.obfs_type] = {
password = node.hysteria2_obfs_password password = node.obfs_password
} }
} or nil, } or nil,
auth = users and { auth = users and {
type = "userpass", type = "userpass",
userpass = users userpass = users
} or nil, } or nil,
bandwidth = (node.hysteria2_up_mbps or node.hysteria2_down_mbps) and { bandwidth = (node.up_mbps or node.down_mbps) and {
up = node.hysteria2_up_mbps and node.hysteria2_up_mbps .. " mbps" or nil, up = node.up_mbps and node.up_mbps .. " mbps" or nil,
down = node.hysteria2_down_mbps and node.hysteria2_down_mbps .. " mbps" or nil down = node.down_mbps and node.down_mbps .. " mbps" or nil
} or nil, } or nil,
ignoreClientBandwidth = (node.hysteria2_ignoreClientBandwidth == "1") and true or false, ignoreClientBandwidth = (node.ignoreClientBandwidth == "1") and true or false,
disableUDP = (node.hysteria2_udp == "0") and true or false, disableUDP = (node.udp == "0") and true or false,
realm = (node.hysteria2_realms and node.hysteria2_realm_stun) and { realm = (node.realms and node.realm_stun) and {
stunServers = node.hysteria2_realm_stun stunServers = node.realm_stun
} or nil } or nil
} }
if config.obfs and config.obfs.gecko then if config.obfs and config.obfs.gecko then
local min = tonumber(node.hysteria2_obfs_MinPacketSize) or 512 local min = tonumber(node.obfs_MinPacketSize) or 512
local max = tonumber(node.hysteria2_obfs_MaxPacketSize) or 1200 local max = tonumber(node.obfs_MaxPacketSize) or 1200
if min <= 0 or min > max or max > 2048 then if min <= 0 or min > max or max > 2048 then
min = 512 min = 512
max = 1200 max = 1200
@@ -402,7 +402,7 @@ function gen_outbound(flag, node, tag, proxy_table)
if node.protocol == "shadowsocks" then if node.protocol == "shadowsocks" then
protocol_table = { protocol_table = {
method = node.method or nil, method = node.ss_method or nil,
password = node.password or "", password = node.password or "",
plugin = (node.plugin_enabled and node.plugin) or nil, plugin = (node.plugin_enabled and node.plugin) or nil,
plugin_opts = (node.plugin_enabled and node.plugin_opts) or nil, plugin_opts = (node.plugin_enabled and node.plugin_opts) or nil,
@@ -416,7 +416,7 @@ function gen_outbound(flag, node, tag, proxy_table)
if node.protocol == "shadowsocksr" then if node.protocol == "shadowsocksr" then
protocol_table = { protocol_table = {
method = node.method or nil, method = node.ssr_method or nil,
password = node.password or "", password = node.password or "",
obfs = node.ssr_obfs, obfs = node.ssr_obfs,
obfs_param = node.ssr_obfs_param, obfs_param = node.ssr_obfs_param,
@@ -865,7 +865,7 @@ function gen_config_server(node)
if node.protocol == "shadowsocks" then if node.protocol == "shadowsocks" then
protocol_table = { protocol_table = {
method = node.method, method = node.ss_method,
password = node.ss_password, password = node.ss_password,
users = users, users = users,
multiplex = mux, multiplex = mux,
@@ -388,8 +388,8 @@ function gen_outbound(flag, node, tag, proxy_table)
user = (node.protocol == "socks" or node.protocol == "http") and node.username or nil, user = (node.protocol == "socks" or node.protocol == "http") and node.username or nil,
pass = (node.protocol == "socks" or node.protocol == "http") and node.password or nil, pass = (node.protocol == "socks" or node.protocol == "http") and node.password or nil,
password = (node.protocol == "shadowsocks" or node.protocol == "trojan") and node.password or nil, password = (node.protocol == "shadowsocks" or node.protocol == "trojan") and node.password or nil,
method = (node.protocol == "shadowsocks") and ((node.method == "chacha20-ietf-poly1305" and "chacha20-poly1305") or method = (node.protocol == "shadowsocks") and ((node.ss_method == "chacha20-ietf-poly1305" and "chacha20-poly1305") or
(node.method == "xchacha20-ietf-poly1305" and "xchacha20-poly1305") or (node.method ~= "" and node.method) or nil) or nil, (node.ss_method == "xchacha20-ietf-poly1305" and "xchacha20-poly1305") or (node.ss_method ~= "" and node.ss_method) or nil) or nil,
secretKey = (node.protocol == "wireguard") and node.wireguard_secret_key or nil, secretKey = (node.protocol == "wireguard") and node.wireguard_secret_key or nil,
peers = (node.protocol == "wireguard") and { peers = (node.protocol == "wireguard") and {
{ {
@@ -523,6 +523,12 @@ function gen_config_server(node)
u.email = user.username u.email = user.username
u.auth = user.password u.auth = user.password
end end
if node.protocol == "wireguard" then
u.publicKey = user.wireguard_public_key
u.preSharedKey = user.wireguard_pre_shared_key
u.keepAlive = 0
u.allowedIPs = user.allowed_ips
end
users[#users + 1] = u users[#users + 1] = u
end end
end end
@@ -553,7 +559,7 @@ function gen_config_server(node)
node.tcp_guise = "none" node.tcp_guise = "none"
elseif node.protocol == "shadowsocks" then elseif node.protocol == "shadowsocks" then
settings = { settings = {
method = node.method, method = node.ss_method,
password = node.ss_password, password = node.ss_password,
users = users, users = users,
network = node.ss_network or "tcp,udp" network = node.ss_network or "tcp,udp"
@@ -575,6 +581,14 @@ function gen_config_server(node)
address = node.d_address, address = node.d_address,
port = tonumber(node.d_port) port = tonumber(node.d_port)
} }
elseif node.protocol == "wireguard" then
settings = {
secretKey = node.wireguard_private_key,
--address = node.wireguard_local_address,
--noKernelTun = node.wireguard_system_interface ~= "1" and true or false,
mtu = tonumber(node.wireguard_mtu or 1420),
peers = users
}
end end
if node.fallback and node.fallback == "1" then if node.fallback and node.fallback == "1" then
@@ -1120,7 +1120,7 @@ acl_app() {
} }
start() { start() {
busybox pgrep -f /tmp/etc/passwall2/bin > /dev/null 2>&1 && { busybox pgrep -f ${TMP_PATH}/bin > /dev/null 2>&1 && {
logger -t PW2-RESTART "Upgrade or overload residue is detected, and the subprocess is being called to perform complete cleaning..." logger -t PW2-RESTART "Upgrade or overload residue is detected, and the subprocess is being called to perform complete cleaning..."
(stop) (stop)
sleep 2 sleep 2
@@ -81,10 +81,10 @@ test_node() {
local curlx="socks5h://127.0.0.1:${_tmp_port}" local curlx="socks5h://127.0.0.1:${_tmp_port}"
local _proxy_status=$(test_url "${probe_url}" ${retry_num} ${connect_timeout} "-x $curlx" 1) local _proxy_status=$(test_url "${probe_url}" ${retry_num} ${connect_timeout} "-x $curlx" 1)
# Kill the SS plugin process # Kill the SS plugin process
local pid_file="/tmp/etc/${CONFIG}/test_node_${node_id}_plugin.pid" local pid_file="${TMP_PATH}/test_node_${node_id}_plugin.pid"
[ -s "$pid_file" ] && kill -9 "$(head -n 1 "$pid_file")" >/dev/null 2>&1 [ -s "$pid_file" ] && kill -9 "$(head -n 1 "$pid_file")" >/dev/null 2>&1
busybox pgrep -af "test_node_${node_id}" | awk '! /socks_auto_switch\.sh/{print $1}' | xargs kill -9 >/dev/null 2>&1 busybox pgrep -af "test_node_${node_id}" | awk '! /socks_auto_switch\.sh/{print $1}' | xargs kill -9 >/dev/null 2>&1
rm -rf /tmp/etc/${CONFIG}/test_node_${node_id}*.* rm -rf ${TMP_PATH}/test_node_${node_id}*.*
if [ "${_proxy_status}" -eq 200 ]; then if [ "${_proxy_status}" -eq 200 ]; then
return 0 return 0
fi fi
@@ -53,10 +53,10 @@ url_test_node() {
local probeUrl=$(config_t_get global_other url_test_url https://www.google.com/generate_204) local probeUrl=$(config_t_get global_other url_test_url https://www.google.com/generate_204)
result=$(curl --connect-timeout 3 --max-time 5 -o /dev/null -I -skL -w "%{http_code}:%{time_pretransfer}" -x ${curlx} "${probeUrl}") result=$(curl --connect-timeout 3 --max-time 5 -o /dev/null -I -skL -w "%{http_code}:%{time_pretransfer}" -x ${curlx} "${probeUrl}")
# End the SS plugin process # End the SS plugin process
local pid_file="/tmp/etc/${CONFIG}/url_test_${node_id}_plugin.pid" local pid_file="${TMP_PATH}/url_test_${node_id}_plugin.pid"
[ -s "$pid_file" ] && kill -9 "$(head -n 1 "$pid_file")" >/dev/null 2>&1 [ -s "$pid_file" ] && kill -9 "$(head -n 1 "$pid_file")" >/dev/null 2>&1
busybox pgrep -af "url_test_${node_id}" | awk '! /test\.sh/{print $1}' | xargs kill -9 >/dev/null 2>&1 busybox pgrep -af "url_test_${node_id}" | awk '! /test\.sh/{print $1}' | xargs kill -9 >/dev/null 2>&1
rm -rf /tmp/etc/${CONFIG}/*url_test_${node_id}*.* rm -rf ${TMP_PATH}/*url_test_${node_id}*.*
} }
echo $result echo $result
} }