update 2023-12-20 23:35:16
This commit is contained in:
parent
91a8a5df89
commit
224d2d725a
|
@ -4,21 +4,19 @@ Focus on making the most of Xray (HTTP/HTTPS/Socks/TProxy inbounds, multiple pro
|
|||
|
||||
## Warnings
|
||||
|
||||
* Version 3.0.0 involves a lot of breaking changes and is completely not compatible with older versions. Configurations needs to be filled in again.
|
||||
* Since the last OpenWrt version with `firewall3` as default firewall implementation (which is OpenWrt 21.02.7) is now EoL, the `fw3` variant of this project is dropped.
|
||||
* Check out [tag v2.1.2](https://github.com/yichya/luci-app-xray/tree/v2.1.2) and compile fw3 variant yourself if you really need that.
|
||||
* About experimental REALITY support
|
||||
* may change quite frequently so keep in mind about following warnings
|
||||
* server role support **involves breaking changes if you use HTTPS server**: certificate settings are now bound to stream security, so previously uploaded certificate and key files will disappear in LuCI, but this won't prevent Xray from using them. Your previously uploaded file are still there, just select them again in LuCI. If Xray fails to start up and complains about missing certificate files, also try picking them again.
|
||||
* legacy XTLS support has already been removed in version 1.8.0 and is also removed by this project since version 2.0.0.
|
||||
* If you see `WARNING: at least one of asset files (geoip.dat, geosite.dat) is not found under /usr/share/xray. Xray may not work properly` and don't know what to do:
|
||||
* try `opkg update && opkg install xray-geodata` (at least OpenWrt 21.02 releases)
|
||||
* if that doesn't work or you are using OpenWrt 19.07 releases, see [#52](https://github.com/yichya/luci-app-xray/issues/52#issuecomment-856059905)
|
||||
* Since version 3.2.0 sniffing and global custom settings are deprecated.
|
||||
* These features are moved out of main luci app. Select "Preview or Deprecated" in "Extra Settings" tab and reboot to let those settings show again in preview app.
|
||||
* These likely to be removed in version 4.0.0. Use FakeDNS instead of sniffing and use "Custom Configuration Hook" for global custom settings.
|
||||
* This project **DOES NOT SUPPORT** the following versions of OpenWrt because of the requirements of firewall4 and cilent-side rendering LuCI:
|
||||
* LEDE / OpenWrt prior to 22.03
|
||||
* [Lean's OpenWrt Source](https://github.com/coolsnowwolf/lede) (which uses a variant of LuCI shipped with OpenWrt 18.06)
|
||||
|
||||
If this is your case, use Passwall or similar projects instead (you could find links in [XTLS/Xray-core](https://github.com/XTLS/Xray-core/)).
|
||||
* About experimental REALITY support
|
||||
* it may change quite frequently (before the release of official documents about the protocol). Keep in mind for (maybe) breaking changes.
|
||||
* If you see `WARNING: at least one of asset files (geoip.dat, geosite.dat) is not found under /usr/share/xray. Xray may not work properly` and don't know what to do:
|
||||
* try `opkg update && opkg install v2ray-geoip v2ray-geosite`
|
||||
* if that doesn't work, see [#52](https://github.com/yichya/luci-app-xray/issues/52#issuecomment-856059905)
|
||||
* This project may change its code structure, configuration files format, user interface or dependencies quite frequently since it is still in its very early stage.
|
||||
|
||||
## Installation (Fw4 only)
|
||||
|
@ -34,6 +32,10 @@ Choose one below:
|
|||
|
||||
Then find `luci-app-xray` under `Extra Packages`.
|
||||
|
||||
## Changelog since 3.2.0
|
||||
|
||||
* 2023-12-20 chore: bump version
|
||||
|
||||
## Changelog since 3.1.0
|
||||
|
||||
* 2023-10-24 chore: bump version
|
||||
|
@ -43,6 +45,9 @@ Then find `luci-app-xray` under `Extra Packages`.
|
|||
* 2023-10-31 chore: bump version to 3.1.1
|
||||
* 2023-11-01 feat: custom configuration hook
|
||||
* 2023-11-02 feat: specify DNS to resolve outbound server name
|
||||
* 2023-11-30 fix: dialer proxy tag
|
||||
* 2023-12-14 fix: default gateway
|
||||
* 2023-12-20 chore: deprecate sniffing; move some preview features to main app; add custom configuration hook; refactor web files
|
||||
|
||||
## Changelog since 3.0.0
|
||||
|
||||
|
|
|
@ -98,6 +98,9 @@ endif
|
|||
$(INSTALL_DIR) $(1)/www/luci-static/resources/view/xray
|
||||
$(INSTALL_DATA) ./root/www/luci-static/resources/view/xray/core.js $(1)/www/luci-static/resources/view/xray/core.js
|
||||
$(INSTALL_DATA) ./root/www/luci-static/resources/view/xray/preview.js $(1)/www/luci-static/resources/view/xray/preview.js
|
||||
$(INSTALL_DATA) ./root/www/luci-static/resources/view/xray/protocol.js $(1)/www/luci-static/resources/view/xray/protocol.js
|
||||
$(INSTALL_DATA) ./root/www/luci-static/resources/view/xray/shared.js $(1)/www/luci-static/resources/view/xray/shared.js
|
||||
$(INSTALL_DATA) ./root/www/luci-static/resources/view/xray/transport.js $(1)/www/luci-static/resources/view/xray/transport.js
|
||||
$(INSTALL_DIR) $(1)/usr/libexec/rpcd
|
||||
$(INSTALL_BIN) ./root/usr/libexec/rpcd/xray $(1)/usr/libexec/rpcd/xray
|
||||
$(INSTALL_DIR) $(1)/usr/share/luci/menu.d
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"admin/services/xray_core": {
|
||||
"title": "Xray (core)",
|
||||
"title": "Xray",
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "xray/core"
|
||||
|
@ -25,7 +25,11 @@
|
|||
"luci-app-xray"
|
||||
],
|
||||
"uci": {
|
||||
"xray_core": true
|
||||
"xray_core": {
|
||||
"@general[0]": {
|
||||
"preview_or_deprecated": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
"use strict";
|
||||
|
||||
import { lsdir } from "fs";
|
||||
import { balancer } from "./system.mjs";
|
||||
import { fake_dns_domains } from "./fake_dns.mjs";
|
||||
import { balancer } from "./system.mjs";
|
||||
|
||||
const fallback_fast_dns = "223.5.5.5:53";
|
||||
const fallback_secure_dns = "8.8.8.8:53";
|
||||
|
@ -118,14 +118,14 @@ export function dns_conf(proxy, config, manual_tproxy, fakedns) {
|
|||
const default_dns_object = split_ipv4_host_port(proxy["default_dns"] || fallback_default_dns, 53);
|
||||
const upstream_domain_options = upstream_domain_names(proxy, config);
|
||||
let servers = [
|
||||
default_dns_object,
|
||||
...fake_dns_domains(fakedns),
|
||||
...map(keys(upstream_domain_options[1]), function (k) {
|
||||
const i = split_ipv4_host_port(upstream_domain_options[1][k]);
|
||||
i["domains"] = [k];
|
||||
i["domains"] = [`domain:${k}`];
|
||||
i["skipFallback"] = true;
|
||||
return i;
|
||||
}),
|
||||
default_dns_object,
|
||||
{
|
||||
address: fast_dns_object["address"],
|
||||
port: fast_dns_object["port"],
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
"use strict";
|
||||
|
||||
import { dokodemo_inbound, http_inbound, socks_inbound } from "./inbound.mjs";
|
||||
import { balancer } from "./system.mjs";
|
||||
import { socks_inbound, http_inbound, dokodemo_inbound } from "./inbound.mjs";
|
||||
|
||||
export function extra_inbounds(proxy, extra_inbound) {
|
||||
let result = [];
|
||||
|
@ -36,7 +36,7 @@ export function extra_inbound_rules(extra_inbound) {
|
|||
return result;
|
||||
};
|
||||
|
||||
export function extra_inbound_balancers(extra_inbound, balancer_strategy) {
|
||||
export function extra_inbound_balancers(extra_inbound) {
|
||||
let result = [];
|
||||
for (let e in extra_inbound) {
|
||||
if (e["specify_outbound"] == "1") {
|
||||
|
@ -44,7 +44,7 @@ export function extra_inbound_balancers(extra_inbound, balancer_strategy) {
|
|||
"tag": `extra_inbound_outbound:${e[".name"]}`,
|
||||
"selector": balancer(e, "destination", `extra_inbound:${e[".name"]}`),
|
||||
"strategy": {
|
||||
"type": balancer_strategy
|
||||
"type": e["balancer_strategy"] || "random"
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -37,20 +37,20 @@ export function fake_dns_rules(fakedns) {
|
|||
return result;
|
||||
};
|
||||
|
||||
export function fake_dns_balancers(fakedns, balancer_strategy) {
|
||||
export function fake_dns_balancers(fakedns) {
|
||||
let result = [];
|
||||
for (let f in fakedns) {
|
||||
push(result, {
|
||||
"tag": `fake_dns_balancer:${f[".name"]}@tcp_balancer`,
|
||||
"selector": balancer(f, "fake_dns_forward_server_tcp", `fake_dns_tcp:${f[".name"]}`),
|
||||
"strategy": {
|
||||
"type": balancer_strategy
|
||||
"type": f["fake_dns_balancer_strategy"] || "random"
|
||||
}
|
||||
}, {
|
||||
"tag": `fake_dns_balancer:${f[".name"]}@udp_balancer`,
|
||||
"selector": balancer(f, "fake_dns_forward_server_udp", `fake_dns_udp:${f[".name"]}`),
|
||||
"strategy": {
|
||||
"type": balancer_strategy
|
||||
"type": f["fake_dns_balancer_strategy"] || "random"
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -3,14 +3,14 @@
|
|||
|
||||
import { lsdir } from "fs";
|
||||
import { load_config } from "./common/config.mjs";
|
||||
import { balancer, api_conf, metrics_conf, logging, policy, system_route_rules } from "./feature/system.mjs";
|
||||
import { blocked_domain_rules, fast_domain_rules, secure_domain_rules, dns_server_tags, dns_server_inbounds, dns_server_outbound, dns_conf } from "./feature/dns.mjs";
|
||||
import { socks_inbound, http_inbound, https_inbound, dokodemo_inbound } from "./feature/inbound.mjs";
|
||||
import { blackhole_outbound, direct_outbound, server_outbound } from "./feature/outbound.mjs";
|
||||
import { bridges, bridge_outbounds, bridge_rules } from "./feature/bridge.mjs";
|
||||
import { extra_inbounds, extra_inbound_rules, extra_inbound_global_tcp_tags, extra_inbound_global_udp_tags, extra_inbound_balancers } from "./feature/extra_inbound.mjs";
|
||||
import { manual_tproxy_outbounds, manual_tproxy_outbound_tags, manual_tproxy_rules } from "./feature/manual_tproxy.mjs";
|
||||
import { bridge_outbounds, bridge_rules, bridges } from "./feature/bridge.mjs";
|
||||
import { blocked_domain_rules, dns_conf, dns_server_inbounds, dns_server_outbound, dns_server_tags, fast_domain_rules, secure_domain_rules } from "./feature/dns.mjs";
|
||||
import { extra_inbound_balancers, extra_inbound_global_tcp_tags, extra_inbound_global_udp_tags, extra_inbound_rules, extra_inbounds } from "./feature/extra_inbound.mjs";
|
||||
import { fake_dns_balancers, fake_dns_conf, fake_dns_rules } from "./feature/fake_dns.mjs";
|
||||
import { dokodemo_inbound, http_inbound, https_inbound, socks_inbound } from "./feature/inbound.mjs";
|
||||
import { manual_tproxy_outbound_tags, manual_tproxy_outbounds, manual_tproxy_rules } from "./feature/manual_tproxy.mjs";
|
||||
import { blackhole_outbound, direct_outbound, server_outbound } from "./feature/outbound.mjs";
|
||||
import { api_conf, balancer, logging, metrics_conf, policy, system_route_rules } from "./feature/system.mjs";
|
||||
|
||||
function inbounds(proxy, config, extra_inbound) {
|
||||
let i = [
|
||||
|
@ -22,10 +22,10 @@ function inbounds(proxy, config, extra_inbound) {
|
|||
dokodemo_inbound("0.0.0.0", proxy["tproxy_port_udp_v6"] || 1085, "tproxy_udp_inbound_v6", proxy["tproxy_sniffing"], proxy["route_only"], ["quic"], "0", "udp", "tproxy", proxy["conn_idle"]),
|
||||
...extra_inbounds(proxy, extra_inbound),
|
||||
...dns_server_inbounds(proxy),
|
||||
dokodemo_inbound("0.0.0.0", proxy["tproxy_port_tcp_f4"] || 1086, "tproxy_tcp_inbound_f4", "1", "0", ["fakedns"], "1", "tcp", "tproxy", proxy["fake_dns_timeout"]),
|
||||
dokodemo_inbound("0.0.0.0", proxy["tproxy_port_tcp_f6"] || 1087, "tproxy_tcp_inbound_f6", "1", "0", ["fakedns"], "1", "tcp", "tproxy", proxy["fake_dns_timeout"]),
|
||||
dokodemo_inbound("0.0.0.0", proxy["tproxy_port_udp_f4"] || 1088, "tproxy_udp_inbound_f4", "1", "0", ["fakedns"], "1", "udp", "tproxy", proxy["fake_dns_timeout"]),
|
||||
dokodemo_inbound("0.0.0.0", proxy["tproxy_port_udp_f6"] || 1089, "tproxy_udp_inbound_f6", "1", "0", ["fakedns"], "1", "udp", "tproxy", proxy["fake_dns_timeout"]),
|
||||
dokodemo_inbound("0.0.0.0", proxy["tproxy_port_tcp_f4"] || 1086, "tproxy_tcp_inbound_f4", "1", "0", ["fakedns"], "1", "tcp", "tproxy", proxy["conn_idle"]),
|
||||
dokodemo_inbound("0.0.0.0", proxy["tproxy_port_tcp_f6"] || 1087, "tproxy_tcp_inbound_f6", "1", "0", ["fakedns"], "1", "tcp", "tproxy", proxy["conn_idle"]),
|
||||
dokodemo_inbound("0.0.0.0", proxy["tproxy_port_udp_f4"] || 1088, "tproxy_udp_inbound_f4", "1", "0", ["fakedns"], "1", "udp", "tproxy", proxy["conn_idle"]),
|
||||
dokodemo_inbound("0.0.0.0", proxy["tproxy_port_udp_f6"] || 1089, "tproxy_udp_inbound_f6", "1", "0", ["fakedns"], "1", "udp", "tproxy", proxy["conn_idle"]),
|
||||
];
|
||||
if (proxy["web_server_enable"] == "1") {
|
||||
push(i, https_inbound(proxy, config));
|
||||
|
@ -220,38 +220,39 @@ function rules(geoip_existence, proxy, bridge, manual_tproxy, extra_inbound, fak
|
|||
return result;
|
||||
}
|
||||
|
||||
function balancers(proxy, extra_inbound, fakedns, balancer_strategy) {
|
||||
function balancers(proxy, extra_inbound, fakedns) {
|
||||
const general_balancer_strategy = proxy["general_balancer_strategy"] || "random";
|
||||
let result = [
|
||||
{
|
||||
"tag": "tcp_outbound_v4",
|
||||
"selector": balancer(proxy, "tcp_balancer_v4", "tcp_balancer_v4"),
|
||||
"strategy": {
|
||||
"type": balancer_strategy
|
||||
"type": general_balancer_strategy
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "udp_outbound_v4",
|
||||
"selector": balancer(proxy, "udp_balancer_v4", "udp_balancer_v4"),
|
||||
"strategy": {
|
||||
"type": balancer_strategy
|
||||
"type": general_balancer_strategy
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "tcp_outbound_v6",
|
||||
"selector": balancer(proxy, "tcp_balancer_v6", "tcp_balancer_v6"),
|
||||
"strategy": {
|
||||
"type": balancer_strategy
|
||||
"type": general_balancer_strategy
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "udp_outbound_v6",
|
||||
"selector": balancer(proxy, "udp_balancer_v6", "udp_balancer_v6"),
|
||||
"strategy": {
|
||||
"type": balancer_strategy
|
||||
"type": general_balancer_strategy
|
||||
}
|
||||
},
|
||||
...extra_inbound_balancers(extra_inbound, balancer_strategy),
|
||||
...fake_dns_balancers(fakedns, balancer_strategy),
|
||||
...extra_inbound_balancers(extra_inbound),
|
||||
...fake_dns_balancers(fakedns),
|
||||
];
|
||||
|
||||
return result;
|
||||
|
@ -269,8 +270,6 @@ function observatory(proxy, manual_tproxy) {
|
|||
}
|
||||
|
||||
function gen_config() {
|
||||
const balancer_strategy = "random";
|
||||
|
||||
const share_dir = lsdir("/usr/share/xray");
|
||||
const geoip_existence = index(share_dir, "geoip.dat") > 0;
|
||||
|
||||
|
@ -301,7 +300,7 @@ function gen_config() {
|
|||
routing: {
|
||||
domainStrategy: general["routing_domain_strategy"] || "AsIs",
|
||||
rules: rules(geoip_existence, general, bridge, manual_tproxy, extra_inbound, fakedns),
|
||||
balancers: balancers(general, extra_inbound, fakedns, balancer_strategy)
|
||||
balancers: balancers(general, extra_inbound, fakedns)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,191 +1,67 @@
|
|||
'use strict';
|
||||
'require form';
|
||||
'require fs';
|
||||
'require network';
|
||||
'require tools.widgets as widgets';
|
||||
'require uci';
|
||||
'require view';
|
||||
'require view.xray.protocol as protocol';
|
||||
'require view.xray.shared as shared';
|
||||
'require view.xray.transport as transport';
|
||||
|
||||
const variant = "xray_core";
|
||||
|
||||
function validate_object(id, a) {
|
||||
if (a == "") {
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
const t = JSON.parse(a);
|
||||
if (Array.isArray(t)) {
|
||||
return "TypeError: Requires an object here, got an array";
|
||||
function list_folded_format(config_data, k, n) {
|
||||
return function (s) {
|
||||
const records = uci.get(config_data, s, k) || [];
|
||||
switch (records.length) {
|
||||
case 0: {
|
||||
return "-";
|
||||
}
|
||||
case 1: {
|
||||
return records[0];
|
||||
}
|
||||
}
|
||||
if (t instanceof Object) {
|
||||
return true;
|
||||
}
|
||||
return "TypeError: Requires an object here, got a " + typeof t;
|
||||
} catch (e) {
|
||||
return e;
|
||||
}
|
||||
return E([], [
|
||||
records[0],
|
||||
", ... ",
|
||||
shared.badge(`+<strong>${records.length - 1}</strong>`, `${records.length} ${n}\n${records.join("\n")}`)
|
||||
]);
|
||||
};
|
||||
}
|
||||
|
||||
function fingerprints(o) {
|
||||
o.value("chrome", "chrome");
|
||||
o.value("firefox", "firefox");
|
||||
o.value("safari", "safari");
|
||||
o.value("ios", "ios");
|
||||
o.value("android", "android");
|
||||
o.value("edge", "edge");
|
||||
o.value("360", "360");
|
||||
o.value("qq", "qq");
|
||||
o.value("random", "random");
|
||||
o.value("randomized", "randomized");
|
||||
function destination_format(config_data, k) {
|
||||
return function (s) {
|
||||
const dest = uci.get(config_data, s, k) || [];
|
||||
if (dest.length == 0) {
|
||||
return "<i>direct</i>";
|
||||
}
|
||||
return dest.map(v => uci.get(config_data, v, "alias")).join(", ");
|
||||
};
|
||||
}
|
||||
|
||||
function add_flow_and_stream_security_conf(s, tab_name, depends_field_name, protocol_name, have_tls_flow, client_side) {
|
||||
let o = s.taboption(tab_name, form.ListValue, `${protocol_name}_tls`, _(`[${protocol_name}] Stream Security`));
|
||||
let odep = {};
|
||||
odep[depends_field_name] = protocol_name;
|
||||
if (client_side) {
|
||||
o.depends(depends_field_name, protocol_name);
|
||||
o.value("none", "None");
|
||||
} else {
|
||||
odep["web_server_enable"] = "1";
|
||||
function extra_outbound_format(config_data, s, with_desc) {
|
||||
const inbound_addr = uci.get(config_data, s, "inbound_addr") || "";
|
||||
const inbound_port = uci.get(config_data, s, "inbound_port") || "";
|
||||
if (inbound_addr == "" && inbound_port == "") {
|
||||
return "-";
|
||||
}
|
||||
o.value("tls", "TLS");
|
||||
if (have_tls_flow) {
|
||||
o.value("reality", "REALITY (Experimental)");
|
||||
if (with_desc) {
|
||||
return `${inbound_addr}:${inbound_port} (${destination_format(config_data, "destination")(s)})`;
|
||||
}
|
||||
o.depends(odep);
|
||||
o.rmempty = false;
|
||||
o.modalonly = true;
|
||||
return `${inbound_addr}:${inbound_port}`;
|
||||
}
|
||||
|
||||
if (have_tls_flow) {
|
||||
let flow_tls = s.taboption(tab_name, form.ListValue, `${protocol_name}_flow_tls`, _(`[${protocol_name}][tls] Flow`));
|
||||
let flow_tls_dep = {};
|
||||
flow_tls_dep[depends_field_name] = protocol_name;
|
||||
flow_tls_dep[`${protocol_name}_tls`] = "tls";
|
||||
flow_tls.value("none", "none");
|
||||
flow_tls.value("xtls-rprx-vision", "xtls-rprx-vision");
|
||||
flow_tls.value("xtls-rprx-vision-udp443", "xtls-rprx-vision-udp443");
|
||||
if (client_side) {
|
||||
// wait for some other things
|
||||
} else {
|
||||
flow_tls_dep["web_server_enable"] = "1";
|
||||
function access_control_format(config_data, s, t) {
|
||||
return function (v) {
|
||||
switch (uci.get(config_data, v, s)) {
|
||||
case "tproxy": {
|
||||
return _("Enable tproxy");
|
||||
}
|
||||
case "bypass": {
|
||||
return _("Disable tproxy");
|
||||
}
|
||||
}
|
||||
flow_tls.depends(flow_tls_dep);
|
||||
flow_tls.rmempty = false;
|
||||
flow_tls.modalonly = true;
|
||||
|
||||
let flow_reality = s.taboption(tab_name, form.ListValue, `${protocol_name}_flow_reality`, _(`[${protocol_name}][reality] Flow`));
|
||||
let flow_reality_dep = {};
|
||||
flow_reality_dep[depends_field_name] = protocol_name;
|
||||
flow_reality_dep[`${protocol_name}_tls`] = "reality";
|
||||
flow_reality.value("none", "none");
|
||||
flow_reality.value("xtls-rprx-vision", "xtls-rprx-vision");
|
||||
flow_reality.value("xtls-rprx-vision-udp443", "xtls-rprx-vision-udp443");
|
||||
if (client_side) {
|
||||
// wait for some other things
|
||||
} else {
|
||||
flow_reality_dep["web_server_enable"] = "1";
|
||||
}
|
||||
flow_reality.depends(flow_reality_dep);
|
||||
flow_reality.rmempty = false;
|
||||
flow_reality.modalonly = true;
|
||||
|
||||
o = s.taboption(tab_name, form.Flag, `${protocol_name}_reality_show`, _(`[${protocol_name}][reality] Show`));
|
||||
o.depends(`${protocol_name}_tls`, "reality");
|
||||
o.modalonly = true;
|
||||
}
|
||||
|
||||
if (client_side) {
|
||||
o = s.taboption(tab_name, form.Value, `${protocol_name}_tls_host`, _(`[${protocol_name}][tls] Server Name`));
|
||||
o.depends(`${protocol_name}_tls`, "tls");
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.taboption(tab_name, form.Flag, `${protocol_name}_tls_insecure`, _(`[${protocol_name}][tls] Allow Insecure`));
|
||||
o.depends(`${protocol_name}_tls`, "tls");
|
||||
o.rmempty = false;
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.taboption(tab_name, form.Value, `${protocol_name}_tls_fingerprint`, _(`[${protocol_name}][tls] Fingerprint`));
|
||||
o.depends(`${protocol_name}_tls`, "tls");
|
||||
o.value("", "(not set)");
|
||||
fingerprints(o);
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.taboption(tab_name, form.DynamicList, `${protocol_name}_tls_alpn`, _(`[${protocol_name}][tls] ALPN`));
|
||||
o.depends(`${protocol_name}_tls`, "tls");
|
||||
o.value("h2", "h2");
|
||||
o.value("http/1.1", "http/1.1");
|
||||
o.modalonly = true;
|
||||
|
||||
if (have_tls_flow) {
|
||||
o = s.taboption(tab_name, form.Value, `${protocol_name}_reality_fingerprint`, _(`[${protocol_name}][reality] Fingerprint`));
|
||||
o.depends(`${protocol_name}_tls`, "reality");
|
||||
fingerprints(o);
|
||||
o.rmempty = false;
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.taboption(tab_name, form.Value, `${protocol_name}_reality_server_name`, _(`[${protocol_name}][reality] Server Name`));
|
||||
o.depends(`${protocol_name}_tls`, "reality");
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.taboption(tab_name, form.Value, `${protocol_name}_reality_public_key`, _(`[${protocol_name}][reality] Public Key`));
|
||||
o.depends(`${protocol_name}_tls`, "reality");
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.taboption(tab_name, form.Value, `${protocol_name}_reality_short_id`, _(`[${protocol_name}][reality] Short Id`));
|
||||
o.depends(`${protocol_name}_tls`, "reality");
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.taboption(tab_name, form.Value, `${protocol_name}_spider_x`, _(`[${protocol_name}][reality] SpiderX`));
|
||||
o.depends(`${protocol_name}_tls`, "reality");
|
||||
o.modalonly = true;
|
||||
}
|
||||
} else {
|
||||
let tls_cert_key_dep = { "web_server_enable": "1" };
|
||||
tls_cert_key_dep[`${protocol_name}_tls`] = "tls";
|
||||
o = s.taboption(tab_name, form.FileUpload, `${protocol_name}_tls_cert_file`, _(`[${protocol_name}][tls] Certificate File`));
|
||||
o.root_directory = "/etc/luci-uploads/xray";
|
||||
o.depends(tls_cert_key_dep);
|
||||
|
||||
o = s.taboption(tab_name, form.FileUpload, `${protocol_name}_tls_key_file`, _(`[${protocol_name}][tls] Private Key File`));
|
||||
o.root_directory = "/etc/luci-uploads/xray";
|
||||
o.depends(tls_cert_key_dep);
|
||||
|
||||
if (have_tls_flow) {
|
||||
o = s.taboption(tab_name, form.Value, `${protocol_name}_reality_dest`, _(`[${protocol_name}][reality] Dest`));
|
||||
o.depends(`${protocol_name}_tls`, "reality");
|
||||
o.datatype = "hostport";
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.taboption(tab_name, form.Value, `${protocol_name}_reality_xver`, _(`[${protocol_name}][reality] Xver`));
|
||||
o.depends(`${protocol_name}_tls`, "reality");
|
||||
o.datatype = "integer";
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.taboption(tab_name, form.DynamicList, `${protocol_name}_reality_server_names`, _(`[${protocol_name}][reality] Server Names`));
|
||||
o.depends(`${protocol_name}_tls`, "reality");
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.taboption(tab_name, form.Value, `${protocol_name}_reality_private_key`, _(`[${protocol_name}][reality] Private Key`));
|
||||
o.depends(`${protocol_name}_tls`, "reality");
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.taboption(tab_name, form.Value, `${protocol_name}_reality_min_client_ver`, _(`[${protocol_name}][reality] Min Client Ver`));
|
||||
o.depends(`${protocol_name}_tls`, "reality");
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.taboption(tab_name, form.Value, `${protocol_name}_reality_max_client_ver`, _(`[${protocol_name}][reality] Max Client Ver`));
|
||||
o.depends(`${protocol_name}_tls`, "reality");
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.taboption(tab_name, form.Value, `${protocol_name}_reality_max_time_diff`, _(`[${protocol_name}][reality] Max Time Diff`));
|
||||
o.depends(`${protocol_name}_tls`, "reality");
|
||||
o.datatype = "integer";
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.taboption(tab_name, form.DynamicList, `${protocol_name}_reality_short_ids`, _(`[${protocol_name}][reality] Short Ids`));
|
||||
o.depends(`${protocol_name}_tls`, "reality");
|
||||
o.modalonly = true;
|
||||
}
|
||||
}
|
||||
return extra_outbound_format(config_data, uci.get(config_data, v, t));
|
||||
};
|
||||
}
|
||||
|
||||
function check_resource_files(load_result) {
|
||||
|
@ -224,8 +100,9 @@ function check_resource_files(load_result) {
|
|||
return view.extend({
|
||||
load: function () {
|
||||
return Promise.all([
|
||||
uci.load(variant),
|
||||
fs.list("/usr/share/xray")
|
||||
uci.load(shared.variant),
|
||||
fs.list("/usr/share/xray"),
|
||||
network.getHostHints()
|
||||
]);
|
||||
},
|
||||
|
||||
|
@ -233,6 +110,7 @@ return view.extend({
|
|||
const config_data = load_result[0];
|
||||
const { geoip_existence, geoip_size, geosite_existence, geosite_size, xray_bin_default, xray_running } = check_resource_files(load_result[1]);
|
||||
const status_text = xray_running ? _("[Xray is running]") : _("[Xray is stopped]");
|
||||
const hosts = load_result[2].hosts;
|
||||
|
||||
let asset_file_status = _('WARNING: at least one of asset files (geoip.dat, geosite.dat) is not found under /usr/share/xray. Xray may not work properly. See <a href="https://github.com/yichya/luci-app-xray">here</a> for help.');
|
||||
if (geoip_existence) {
|
||||
|
@ -241,7 +119,7 @@ return view.extend({
|
|||
}
|
||||
}
|
||||
|
||||
const m = new form.Map(variant, _('Xray (core)'), status_text + " " + asset_file_status);
|
||||
const m = new form.Map(shared.variant, _('Xray'), status_text + " " + asset_file_status);
|
||||
|
||||
let s, o, ss;
|
||||
|
||||
|
@ -251,11 +129,7 @@ return view.extend({
|
|||
|
||||
s.tab('general', _('General Settings'));
|
||||
|
||||
o = s.taboption('general', form.Value, 'xray_bin', _('Xray Executable Path'));
|
||||
o.rmempty = false;
|
||||
if (xray_bin_default) {
|
||||
o.value("/usr/bin/xray", _("/usr/bin/xray (default, exist)"));
|
||||
}
|
||||
o = s.taboption('general', form.Flag, 'transparent_proxy_enable', _('Enable Xray Service'), _('Uncheck this to disable the entire Xray service.'));
|
||||
|
||||
let tcp_balancer_v4 = s.taboption('general', form.MultiValue, 'tcp_balancer_v4', _('TCP Server (IPv4)'), _("Select multiple outbound servers to enable load balancing. Select none to disable TCP Outbound."));
|
||||
tcp_balancer_v4.datatype = "uciname";
|
||||
|
@ -269,36 +143,11 @@ return view.extend({
|
|||
let udp_balancer_v6 = s.taboption('general', form.MultiValue, 'udp_balancer_v6', _('UDP Server (IPv6)'), _("Select multiple outbound servers to enable load balancing. Select none to disable UDP Outbound."));
|
||||
udp_balancer_v6.datatype = "uciname";
|
||||
|
||||
const servers = uci.sections(config_data, "servers");
|
||||
if (servers.length == 0) {
|
||||
tcp_balancer_v4.value("direct", _("No server configured"));
|
||||
udp_balancer_v4.value("direct", _("No server configured"));
|
||||
tcp_balancer_v6.value("direct", _("No server configured"));
|
||||
udp_balancer_v6.value("direct", _("No server configured"));
|
||||
|
||||
tcp_balancer_v4.readonly = true;
|
||||
udp_balancer_v4.readonly = true;
|
||||
tcp_balancer_v6.readonly = true;
|
||||
udp_balancer_v6.readonly = true;
|
||||
} else {
|
||||
for (const v of servers) {
|
||||
tcp_balancer_v4.value(v[".name"], v.alias || v.server + ":" + v.server_port);
|
||||
udp_balancer_v4.value(v[".name"], v.alias || v.server + ":" + v.server_port);
|
||||
tcp_balancer_v6.value(v[".name"], v.alias || v.server + ":" + v.server_port);
|
||||
udp_balancer_v6.value(v[".name"], v.alias || v.server + ":" + v.server_port);
|
||||
}
|
||||
}
|
||||
|
||||
o = s.taboption('general', form.Flag, 'transparent_proxy_enable', _('Enable Transparent Proxy'), _('This enables DNS query forwarding and TProxy for both TCP and UDP connections.'));
|
||||
|
||||
o = s.taboption('general', form.Flag, 'tproxy_sniffing', _('Enable Sniffing'), _('If sniffing is enabled, requests will be routed according to domain settings in "DNS Settings" tab.'));
|
||||
o.depends("transparent_proxy_enable", "1");
|
||||
|
||||
o = s.taboption('general', form.Flag, 'route_only', _('Route Only'), _('Use sniffed domain for routing only but still access through IP. Reduces unnecessary DNS requests. See <a href="https://github.com/XTLS/Xray-core/commit/a3023e43ef55d4498b1afbc9a7fe7b385138bb1a">here</a> for help.'));
|
||||
o.depends({ "transparent_proxy_enable": "1", "tproxy_sniffing": "1" });
|
||||
|
||||
o = s.taboption('general', form.Flag, 'direct_bittorrent', _('Bittorrent Direct'), _("If enabled, no bittorrent request will be forwarded through Xray."));
|
||||
o.depends({ "transparent_proxy_enable": "1", "tproxy_sniffing": "1" });
|
||||
let general_balancer_strategy = s.taboption('general', form.Value, 'general_balancer_strategy', _('Balancer Strategy'), _('Strategy <code>leastPing</code> requires observatory (see "Extra Options" tab) to be enabled.'));
|
||||
general_balancer_strategy.value("random");
|
||||
general_balancer_strategy.value("leastPing");
|
||||
general_balancer_strategy.default = "random";
|
||||
general_balancer_strategy.rmempty = false;
|
||||
|
||||
o = s.taboption('general', form.SectionValue, "xray_servers", form.GridSection, 'servers', _('Xray Servers'), _("Servers are referenced by index (order in the following list). Deleting servers may result in changes of upstream servers actually used by proxy and bridge."));
|
||||
ss = o.subsection;
|
||||
|
@ -337,232 +186,15 @@ return view.extend({
|
|||
ss.tab('protocol', _('Protocol Settings'));
|
||||
|
||||
o = ss.taboption('protocol', form.ListValue, "protocol", _("Protocol"));
|
||||
o.value("vmess", "VMess");
|
||||
o.value("vless", "VLESS");
|
||||
o.value("trojan", "Trojan");
|
||||
o.value("shadowsocks", "Shadowsocks");
|
||||
protocol.add_client_protocol(o, ss, 'protocol');
|
||||
o.rmempty = false;
|
||||
|
||||
add_flow_and_stream_security_conf(ss, "protocol", "protocol", "trojan", false, true);
|
||||
|
||||
o = ss.taboption('protocol', form.ListValue, "shadowsocks_security", _("[shadowsocks] Encrypt Method"));
|
||||
o.depends("protocol", "shadowsocks");
|
||||
o.value("none", "none");
|
||||
o.value("aes-256-gcm", "aes-256-gcm");
|
||||
o.value("aes-128-gcm", "aes-128-gcm");
|
||||
o.value("chacha20-poly1305", "chacha20-poly1305");
|
||||
o.value("2022-blake3-aes-128-gcm", "2022-blake3-aes-128-gcm");
|
||||
o.value("2022-blake3-aes-256-gcm", "2022-blake3-aes-256-gcm");
|
||||
o.value("2022-blake3-chacha20-poly1305", "2022-blake3-chacha20-poly1305");
|
||||
o.rmempty = false;
|
||||
o.modalonly = true;
|
||||
|
||||
o = ss.taboption('protocol', form.Flag, 'shadowsocks_udp_over_tcp', _('[shadowsocks] UDP over TCP'), _('Only available for shadowsocks-2022 ciphers (2022-*)'));
|
||||
o.depends("shadowsocks_security", /2022/);
|
||||
o.rmempty = false;
|
||||
o.modalonly = true;
|
||||
|
||||
add_flow_and_stream_security_conf(ss, "protocol", "protocol", "shadowsocks", false, true);
|
||||
|
||||
o = ss.taboption('protocol', form.ListValue, "vmess_security", _("[vmess] Encrypt Method"));
|
||||
o.depends("protocol", "vmess");
|
||||
o.value("none", "none");
|
||||
o.value("auto", "auto");
|
||||
o.value("aes-128-gcm", "aes-128-gcm");
|
||||
o.value("chacha20-poly1305", "chacha20-poly1305");
|
||||
o.rmempty = false;
|
||||
o.modalonly = true;
|
||||
|
||||
o = ss.taboption('protocol', form.ListValue, "vmess_alter_id", _("[vmess] AlterId"), _("Deprecated. Make sure you always use VMessAEAD."));
|
||||
o.depends("protocol", "vmess");
|
||||
o.value(0, "0 (this enables VMessAEAD)");
|
||||
o.value(1, "1");
|
||||
o.value(4, "4");
|
||||
o.value(16, "16");
|
||||
o.value(64, "64");
|
||||
o.value(256, "256");
|
||||
o.rmempty = false;
|
||||
o.modalonly = true;
|
||||
|
||||
add_flow_and_stream_security_conf(ss, "protocol", "protocol", "vmess", false, true);
|
||||
|
||||
o = ss.taboption('protocol', form.ListValue, "vless_encryption", _("[vless] Encrypt Method"));
|
||||
o.depends("protocol", "vless");
|
||||
o.value("none", "none");
|
||||
o.rmempty = false;
|
||||
o.modalonly = true;
|
||||
|
||||
add_flow_and_stream_security_conf(ss, "protocol", "protocol", "vless", true, true);
|
||||
|
||||
ss.tab('transport', _('Transport Settings'));
|
||||
|
||||
o = ss.taboption('transport', form.ListValue, 'transport', _('Transport'));
|
||||
o.value("tcp", "TCP");
|
||||
o.value("mkcp", "mKCP");
|
||||
o.value("ws", "WebSocket");
|
||||
o.value("h2", "HTTP/2");
|
||||
o.value("quic", "QUIC");
|
||||
o.value("grpc", "gRPC");
|
||||
transport.init(o, ss, 'transport');
|
||||
o.rmempty = false;
|
||||
|
||||
o = ss.taboption('transport', form.ListValue, "tcp_guise", _("[tcp] Fake Header Type"));
|
||||
o.depends("transport", "tcp");
|
||||
o.value("none", _("None"));
|
||||
o.value("http", "HTTP");
|
||||
o.modalonly = true;
|
||||
|
||||
o = ss.taboption('transport', form.DynamicList, "http_host", _("[tcp][fake_http] Host"));
|
||||
o.depends("tcp_guise", "http");
|
||||
o.rmempty = false;
|
||||
o.modalonly = true;
|
||||
|
||||
o = ss.taboption('transport', form.DynamicList, "http_path", _("[tcp][fake_http] Path"));
|
||||
o.depends("tcp_guise", "http");
|
||||
o.modalonly = true;
|
||||
|
||||
o = ss.taboption('transport', form.ListValue, "mkcp_guise", _("[mkcp] Fake Header Type"));
|
||||
o.depends("transport", "mkcp");
|
||||
o.value("none", _("None"));
|
||||
o.value("srtp", _("VideoCall (SRTP)"));
|
||||
o.value("utp", _("BitTorrent (uTP)"));
|
||||
o.value("wechat-video", _("WechatVideo"));
|
||||
o.value("dtls", "DTLS 1.2");
|
||||
o.value("wireguard", "WireGuard");
|
||||
o.modalonly = true;
|
||||
|
||||
o = ss.taboption('transport', form.Value, "mkcp_mtu", _("[mkcp] Maximum Transmission Unit"));
|
||||
o.datatype = "uinteger";
|
||||
o.depends("transport", "mkcp");
|
||||
o.placeholder = 1350;
|
||||
o.modalonly = true;
|
||||
|
||||
o = ss.taboption('transport', form.Value, "mkcp_tti", _("[mkcp] Transmission Time Interval"));
|
||||
o.datatype = "uinteger";
|
||||
o.depends("transport", "mkcp");
|
||||
o.placeholder = 50;
|
||||
o.modalonly = true;
|
||||
|
||||
o = ss.taboption('transport', form.Value, "mkcp_uplink_capacity", _("[mkcp] Uplink Capacity"));
|
||||
o.datatype = "uinteger";
|
||||
o.depends("transport", "mkcp");
|
||||
o.placeholder = 5;
|
||||
o.modalonly = true;
|
||||
|
||||
o = ss.taboption('transport', form.Value, "mkcp_downlink_capacity", _("[mkcp] Downlink Capacity"));
|
||||
o.datatype = "uinteger";
|
||||
o.depends("transport", "mkcp");
|
||||
o.placeholder = 20;
|
||||
o.modalonly = true;
|
||||
|
||||
o = ss.taboption('transport', form.Value, "mkcp_read_buffer_size", _("[mkcp] Read Buffer Size"));
|
||||
o.datatype = "uinteger";
|
||||
o.depends("transport", "mkcp");
|
||||
o.placeholder = 2;
|
||||
o.modalonly = true;
|
||||
|
||||
o = ss.taboption('transport', form.Value, "mkcp_write_buffer_size", _("[mkcp] Write Buffer Size"));
|
||||
o.datatype = "uinteger";
|
||||
o.depends("transport", "mkcp");
|
||||
o.placeholder = 2;
|
||||
o.modalonly = true;
|
||||
|
||||
o = ss.taboption('transport', form.Flag, "mkcp_congestion", _("[mkcp] Congestion Control"));
|
||||
o.depends("transport", "mkcp");
|
||||
o.modalonly = true;
|
||||
|
||||
o = ss.taboption('transport', form.Value, "mkcp_seed", _("[mkcp] Seed"));
|
||||
o.depends("transport", "mkcp");
|
||||
o.modalonly = true;
|
||||
|
||||
o = ss.taboption('transport', form.ListValue, "quic_security", _("[quic] Security"));
|
||||
o.depends("transport", "quic");
|
||||
o.value("none", "none");
|
||||
o.value("aes-128-gcm", "aes-128-gcm");
|
||||
o.value("chacha20-poly1305", "chacha20-poly1305");
|
||||
o.rmempty = false;
|
||||
o.modalonly = true;
|
||||
|
||||
o = ss.taboption('transport', form.Value, "quic_key", _("[quic] Key"));
|
||||
o.depends("transport", "quic");
|
||||
o.modalonly = true;
|
||||
|
||||
o = ss.taboption('transport', form.ListValue, "quic_guise", _("[quic] Fake Header Type"));
|
||||
o.depends("transport", "quic");
|
||||
o.value("none", _("None"));
|
||||
o.value("srtp", _("VideoCall (SRTP)"));
|
||||
o.value("utp", _("BitTorrent (uTP)"));
|
||||
o.value("wechat-video", _("WechatVideo"));
|
||||
o.value("dtls", "DTLS 1.2");
|
||||
o.value("wireguard", "WireGuard");
|
||||
o.default = "none";
|
||||
o.modalonly = true;
|
||||
|
||||
o = ss.taboption('transport', form.DynamicList, "h2_host", _("[http2] Host"));
|
||||
o.depends("transport", "h2");
|
||||
o.modalonly = true;
|
||||
|
||||
o = ss.taboption('transport', form.Value, "h2_path", _("[http2] Path"));
|
||||
o.depends("transport", "h2");
|
||||
o.modalonly = true;
|
||||
|
||||
o = ss.taboption('transport', form.Flag, "h2_health_check", _("[h2] Health Check"));
|
||||
o.depends("transport", "h2");
|
||||
o.modalonly = true;
|
||||
|
||||
o = ss.taboption('transport', form.Value, "h2_read_idle_timeout", _("[h2] Read Idle Timeout"));
|
||||
o.depends({ "transport": "h2", "h2_health_check": "1" });
|
||||
o.modalonly = true;
|
||||
o.placeholder = 10;
|
||||
o.datatype = 'integer';
|
||||
|
||||
o = ss.taboption('transport', form.Value, "h2_health_check_timeout", _("[h2] Health Check Timeout"));
|
||||
o.depends({ "transport": "h2", "h2_health_check": "1" });
|
||||
o.modalonly = true;
|
||||
o.placeholder = 20;
|
||||
o.datatype = 'integer';
|
||||
|
||||
o = ss.taboption('transport', form.Value, "grpc_service_name", _("[grpc] Service Name"));
|
||||
o.depends("transport", "grpc");
|
||||
o.modalonly = true;
|
||||
|
||||
o = ss.taboption('transport', form.Flag, "grpc_multi_mode", _("[grpc] Multi Mode"));
|
||||
o.depends("transport", "grpc");
|
||||
o.modalonly = true;
|
||||
|
||||
o = ss.taboption('transport', form.Flag, "grpc_health_check", _("[grpc] Health Check"));
|
||||
o.depends("transport", "grpc");
|
||||
o.modalonly = true;
|
||||
|
||||
o = ss.taboption('transport', form.Value, "grpc_idle_timeout", _("[grpc] Idle Timeout"));
|
||||
o.depends({ "transport": "grpc", "grpc_health_check": "1" });
|
||||
o.modalonly = true;
|
||||
o.placeholder = 10;
|
||||
o.datatype = 'integer';
|
||||
|
||||
o = ss.taboption('transport', form.Value, "grpc_health_check_timeout", _("[grpc] Health Check Timeout"));
|
||||
o.depends({ "transport": "grpc", "grpc_health_check": "1" });
|
||||
o.modalonly = true;
|
||||
o.placeholder = 20;
|
||||
o.datatype = 'integer';
|
||||
|
||||
o = ss.taboption('transport', form.Flag, "grpc_permit_without_stream", _("[grpc] Permit Without Stream"));
|
||||
o.depends({ "transport": "grpc", "grpc_health_check": "1" });
|
||||
o.modalonly = true;
|
||||
|
||||
o = ss.taboption('transport', form.Value, "grpc_initial_windows_size", _("[grpc] Initial Windows Size"), _("Set to 524288 to avoid Cloudflare sending ENHANCE_YOUR_CALM."));
|
||||
o.depends("transport", "grpc");
|
||||
o.modalonly = true;
|
||||
o.placeholder = 0;
|
||||
o.datatype = 'integer';
|
||||
|
||||
o = ss.taboption('transport', form.Value, "ws_host", _("[websocket] Host"));
|
||||
o.depends("transport", "ws");
|
||||
o.modalonly = true;
|
||||
|
||||
o = ss.taboption('transport', form.Value, "ws_path", _("[websocket] Path"));
|
||||
o.depends("transport", "ws");
|
||||
o.modalonly = true;
|
||||
|
||||
o = ss.taboption('transport', form.ListValue, 'dialer_proxy', _('Dialer Proxy'), _('Similar to <a href="https://xtls.github.io/config/outbound.html#proxysettingsobject">ProxySettings.Tag</a>'));
|
||||
o.datatype = "uciname";
|
||||
o.value("disabled", _("Disabled"));
|
||||
|
@ -577,45 +209,154 @@ return view.extend({
|
|||
o.modalonly = true;
|
||||
o.monospace = true;
|
||||
o.rows = 10;
|
||||
o.validate = validate_object;
|
||||
o.validate = shared.validate_object;
|
||||
|
||||
s.tab('proxy', _('Proxy Settings'));
|
||||
s.tab('inbounds', _('Inbounds'));
|
||||
|
||||
o = s.taboption('proxy', form.Value, 'socks_port', _('Socks5 proxy port'));
|
||||
o = s.taboption('inbounds', form.Value, 'socks_port', _('Socks5 proxy port'));
|
||||
o.datatype = 'port';
|
||||
o.placeholder = 1080;
|
||||
|
||||
o = s.taboption('proxy', form.Value, 'http_port', _('HTTP proxy port'));
|
||||
o = s.taboption('inbounds', form.Value, 'http_port', _('HTTP proxy port'));
|
||||
o.datatype = 'port';
|
||||
o.placeholder = 1081;
|
||||
|
||||
o = s.taboption('proxy', form.Value, 'tproxy_port_tcp_v4', _('Transparent proxy port (TCP4)'));
|
||||
o = s.taboption('inbounds', form.Value, 'tproxy_port_tcp_v4', _('Transparent proxy port (TCP4)'));
|
||||
o.datatype = 'port';
|
||||
o.placeholder = 1082;
|
||||
|
||||
o = s.taboption('proxy', form.Value, 'tproxy_port_tcp_v6', _('Transparent proxy port (TCP6)'));
|
||||
o = s.taboption('inbounds', form.Value, 'tproxy_port_tcp_v6', _('Transparent proxy port (TCP6)'));
|
||||
o.datatype = 'port';
|
||||
o.placeholder = 1083;
|
||||
|
||||
o = s.taboption('proxy', form.Value, 'tproxy_port_udp_v4', _('Transparent proxy port (UDP4)'));
|
||||
o = s.taboption('inbounds', form.Value, 'tproxy_port_udp_v4', _('Transparent proxy port (UDP4)'));
|
||||
o.datatype = 'port';
|
||||
o.placeholder = 1084;
|
||||
|
||||
o = s.taboption('proxy', form.Value, 'tproxy_port_udp_v6', _('Transparent proxy port (UDP6)'));
|
||||
o = s.taboption('inbounds', form.Value, 'tproxy_port_udp_v6', _('Transparent proxy port (UDP6)'));
|
||||
o.datatype = 'port';
|
||||
o.placeholder = 1085;
|
||||
|
||||
o = s.taboption('proxy', form.DynamicList, 'uids_direct', _('Bypass tproxy for uids'), _("Processes started by users with these uids won't be forwarded through Xray."));
|
||||
o = s.taboption('inbounds', form.DynamicList, 'uids_direct', _('Bypass tproxy for uids'), _("Processes started by users with these uids won't be forwarded through Xray."));
|
||||
o.datatype = "integer";
|
||||
|
||||
o = s.taboption('proxy', form.DynamicList, 'gids_direct', _('Bypass tproxy for gids'), _("Processes started by users in groups with these gids won't be forwarded through Xray."));
|
||||
o = s.taboption('inbounds', form.DynamicList, 'gids_direct', _('Bypass tproxy for gids'), _("Processes started by users in groups with these gids won't be forwarded through Xray."));
|
||||
o.datatype = "integer";
|
||||
|
||||
o = s.taboption('proxy', form.Value, 'firewall_priority', _('Priority for firewall rules'), _('See firewall status page for rules Xray used and <a href="https://wiki.nftables.org/wiki-nftables/index.php/Netfilter_hooks#Priority_within_hook">Netfilter Internal Priority</a> for reference.'));
|
||||
o.datatype = 'range(-49, 49)';
|
||||
o.placeholder = 10;
|
||||
let extra_inbounds = s.taboption('inbounds', form.SectionValue, "extra_inbound_section", form.GridSection, 'extra_inbound', _('Extra Inbounds'), _("Add more socks5 / http inbounds and redirect to other outbounds.")).subsection;
|
||||
extra_inbounds.sortable = false;
|
||||
extra_inbounds.anonymous = true;
|
||||
extra_inbounds.addremove = true;
|
||||
extra_inbounds.nodescriptions = true;
|
||||
|
||||
s.tab('dns', _('DNS Settings'));
|
||||
let inbound_addr = extra_inbounds.option(form.Value, "inbound_addr", _("Listen Address"));
|
||||
inbound_addr.datatype = "ip4addr";
|
||||
|
||||
let inbound_port = extra_inbounds.option(form.Value, "inbound_port", _("Listen Port"));
|
||||
inbound_port.datatype = "port";
|
||||
|
||||
let inbound_type = extra_inbounds.option(form.ListValue, "inbound_type", _("Inbound Type"));
|
||||
inbound_type.value("socks5", _("Socks5 Proxy"));
|
||||
inbound_type.value("http", _("HTTP Proxy"));
|
||||
inbound_type.value("tproxy_tcp", _("Transparent Proxy (TCP)"));
|
||||
inbound_type.value("tproxy_udp", _("Transparent Proxy (UDP)"));
|
||||
inbound_type.rmempty = false;
|
||||
|
||||
let specify_outbound = extra_inbounds.option(form.Flag, 'specify_outbound', _('Specify Outbound'), _('If not selected, this inbound will use global settings (including sniffing settings). '));
|
||||
specify_outbound.modalonly = true;
|
||||
|
||||
let destination = extra_inbounds.option(form.MultiValue, 'destination', _('Destination'), _("Select multiple outbounds for load balancing. If none selected, requests will be sent via direct outbound."));
|
||||
destination.depends("specify_outbound", "1");
|
||||
destination.datatype = "uciname";
|
||||
destination.textvalue = destination_format(config_data, "destination");
|
||||
|
||||
let balancer_strategy = extra_inbounds.option(form.Value, 'balancer_strategy', _('Balancer Strategy'), _('Strategy <code>leastPing</code> requires observatory (see "Extra Options" tab) to be enabled.'));
|
||||
balancer_strategy.value("random");
|
||||
balancer_strategy.value("leastPing");
|
||||
balancer_strategy.default = "random";
|
||||
balancer_strategy.rmempty = false;
|
||||
balancer_strategy.modalonly = true;
|
||||
|
||||
s.tab("lan_hosts_access_control", _("LAN Hosts Access Control"));
|
||||
|
||||
let tproxy_ifaces_v4 = s.taboption('lan_hosts_access_control', widgets.DeviceSelect, 'tproxy_ifaces_v4', _("Devices to enable IPv4 tproxy"), _("Enable IPv4 transparent proxy on these interfaces / network devices."));
|
||||
tproxy_ifaces_v4.noaliases = true;
|
||||
tproxy_ifaces_v4.nocreate = true;
|
||||
tproxy_ifaces_v4.multiple = true;
|
||||
|
||||
let tproxy_ifaces_v6 = s.taboption('lan_hosts_access_control', widgets.DeviceSelect, 'tproxy_ifaces_v6', _("Devices to enable IPv6 tproxy"), _("Enable IPv6 transparent proxy on these interfaces / network devices."));
|
||||
tproxy_ifaces_v6.noaliases = true;
|
||||
tproxy_ifaces_v6.nocreate = true;
|
||||
tproxy_ifaces_v6.multiple = true;
|
||||
|
||||
let bypass_ifaces_v4 = s.taboption('lan_hosts_access_control', widgets.DeviceSelect, 'bypass_ifaces_v4', _("Devices to disable IPv4 tproxy"), _("This overrides per-device settings below. FakeDNS and manual transparent proxy won't be affected by this option."));
|
||||
bypass_ifaces_v4.noaliases = true;
|
||||
bypass_ifaces_v4.nocreate = true;
|
||||
bypass_ifaces_v4.multiple = true;
|
||||
|
||||
let bypass_ifaces_v6 = s.taboption('lan_hosts_access_control', widgets.DeviceSelect, 'bypass_ifaces_v6', _("Devices to disable IPv6 tproxy"), _("This overrides per-device settings below. FakeDNS and manual transparent proxy won't be affected by this option."));
|
||||
bypass_ifaces_v6.noaliases = true;
|
||||
bypass_ifaces_v6.nocreate = true;
|
||||
bypass_ifaces_v6.multiple = true;
|
||||
|
||||
let lan_hosts = s.taboption('lan_hosts_access_control', form.SectionValue, "lan_hosts_section", form.GridSection, 'lan_hosts', _('LAN Hosts Access Control'), _("Per-device settings here override per-interface enabling settings above. FakeDNS and manual transparent proxy won't be affected by these options.")).subsection;
|
||||
lan_hosts.sortable = false;
|
||||
lan_hosts.anonymous = true;
|
||||
lan_hosts.addremove = true;
|
||||
|
||||
let macaddr = lan_hosts.option(form.Value, "macaddr", _("MAC Address"));
|
||||
macaddr.datatype = "macaddr";
|
||||
macaddr.rmempty = false;
|
||||
L.sortedKeys(hosts).forEach(function (mac) {
|
||||
macaddr.value(mac, E([], [mac, ' (', E('strong', [hosts[mac].name || L.toArray(hosts[mac].ipaddrs || hosts[mac].ipv4)[0] || L.toArray(hosts[mac].ip6addrs || hosts[mac].ipv6)[0] || '?']), ')']));
|
||||
});
|
||||
|
||||
let access_control_strategy_v4 = lan_hosts.option(form.ListValue, "access_control_strategy_v4", _("Access Control Strategy (IPv4)"));
|
||||
access_control_strategy_v4.value("tproxy", _("Enable transparent proxy"));
|
||||
access_control_strategy_v4.value("forward", _("Forward via extra inbound"));
|
||||
access_control_strategy_v4.value("bypass", _("Disable transparent proxy"));
|
||||
access_control_strategy_v4.modalonly = true;
|
||||
access_control_strategy_v4.rmempty = false;
|
||||
|
||||
let access_control_forward_tcp_v4 = lan_hosts.option(form.ListValue, "access_control_forward_tcp_v4", _("Extra inbound (TCP4)"));
|
||||
access_control_forward_tcp_v4.depends("access_control_strategy_v4", "forward");
|
||||
access_control_forward_tcp_v4.textvalue = access_control_format(config_data, "access_control_strategy_v4", "access_control_forward_tcp_v4");
|
||||
|
||||
let access_control_forward_udp_v4 = lan_hosts.option(form.ListValue, "access_control_forward_udp_v4", _("Extra inbound (UDP4)"));
|
||||
access_control_forward_udp_v4.depends("access_control_strategy_v4", "forward");
|
||||
access_control_forward_udp_v4.textvalue = access_control_format(config_data, "access_control_strategy_v4", "access_control_forward_udp_v4");
|
||||
|
||||
let access_control_strategy_v6 = lan_hosts.option(form.ListValue, "access_control_strategy_v6", _("Access Control Strategy (IPv6)"));
|
||||
access_control_strategy_v6.value("tproxy", _("Enable transparent proxy"));
|
||||
access_control_strategy_v6.value("forward", _("Forward via extra inbound"));
|
||||
access_control_strategy_v6.value("bypass", _("Disable transparent proxy"));
|
||||
access_control_strategy_v6.modalonly = true;
|
||||
access_control_strategy_v6.rmempty = false;
|
||||
|
||||
let access_control_forward_tcp_v6 = lan_hosts.option(form.ListValue, "access_control_forward_tcp_v6", _("Extra inbound (TCP6)"));
|
||||
access_control_forward_tcp_v6.depends("access_control_strategy_v6", "forward");
|
||||
access_control_forward_tcp_v6.textvalue = access_control_format(config_data, "access_control_strategy_v6", "access_control_forward_tcp_v6");
|
||||
|
||||
let access_control_forward_udp_v6 = lan_hosts.option(form.ListValue, "access_control_forward_udp_v6", _("Extra inbound (UDP6)"));
|
||||
access_control_forward_udp_v6.depends("access_control_strategy_v6", "forward");
|
||||
access_control_forward_udp_v6.textvalue = access_control_format(config_data, "access_control_strategy_v6", "access_control_forward_udp_v6");
|
||||
|
||||
for (const v of uci.sections(config_data, "extra_inbound")) {
|
||||
switch (v["inbound_type"]) {
|
||||
case "tproxy_tcp": {
|
||||
access_control_forward_tcp_v4.value(v[".name"], extra_outbound_format(config_data, v[".name"], true));
|
||||
access_control_forward_tcp_v6.value(v[".name"], extra_outbound_format(config_data, v[".name"], true));
|
||||
break;
|
||||
}
|
||||
case "tproxy_udp": {
|
||||
access_control_forward_udp_v4.value(v[".name"], extra_outbound_format(config_data, v[".name"], true));
|
||||
access_control_forward_udp_v6.value(v[".name"], extra_outbound_format(config_data, v[".name"], true));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s.tab('dns', _('DNS'));
|
||||
|
||||
o = s.taboption('dns', form.Value, 'fast_dns', _('Fast DNS'), _("DNS for resolving outbound domains and following bypassed domains"));
|
||||
o.datatype = 'or(ip4addr, ip4addrport)';
|
||||
|
@ -665,60 +406,118 @@ return view.extend({
|
|||
o.default = "AsIs";
|
||||
o.rmempty = false;
|
||||
|
||||
s.tab('transparent_proxy_rules', _('Transparent Proxy Rules'));
|
||||
s.tab('fake_dns', _('FakeDNS'));
|
||||
|
||||
let tproxy_port_tcp_f4 = s.taboption('fake_dns', form.Value, 'tproxy_port_tcp_f4', _('Transparent proxy port (TCP4)'));
|
||||
tproxy_port_tcp_f4.datatype = 'port';
|
||||
tproxy_port_tcp_f4.placeholder = 1086;
|
||||
|
||||
let tproxy_port_tcp_f6 = s.taboption('fake_dns', form.Value, 'tproxy_port_tcp_f6', _('Transparent proxy port (TCP6)'));
|
||||
tproxy_port_tcp_f6.datatype = 'port';
|
||||
tproxy_port_tcp_f6.placeholder = 1087;
|
||||
|
||||
let tproxy_port_udp_f4 = s.taboption('fake_dns', form.Value, 'tproxy_port_udp_f4', _('Transparent proxy port (UDP4)'));
|
||||
tproxy_port_udp_f4.datatype = 'port';
|
||||
tproxy_port_udp_f4.placeholder = 1088;
|
||||
|
||||
let tproxy_port_udp_f6 = s.taboption('fake_dns', form.Value, 'tproxy_port_udp_f6', _('Transparent proxy port (UDP6)'));
|
||||
tproxy_port_udp_f6.datatype = 'port';
|
||||
tproxy_port_udp_f6.placeholder = 1089;
|
||||
|
||||
let pool_v4 = s.taboption('fake_dns', form.Value, 'pool_v4', _('Address Pool (IPv4)'));
|
||||
pool_v4.datatype = 'ip4addr';
|
||||
pool_v4.placeholder = "198.18.0.0/15";
|
||||
|
||||
let pool_v4_size = s.taboption('fake_dns', form.Value, 'pool_v4_size', _('Address Pool Size (IPv4)'));
|
||||
pool_v4_size.datatype = 'integer';
|
||||
pool_v4_size.placeholder = 65535;
|
||||
|
||||
let pool_v6 = s.taboption('fake_dns', form.Value, 'pool_v6', _('Address Pool (IPv6)'));
|
||||
pool_v6.datatype = 'ip6addr';
|
||||
pool_v6.placeholder = "fc00::/18";
|
||||
|
||||
let pool_v6_size = s.taboption('fake_dns', form.Value, 'pool_v6_size', _('Address Pool Size (IPv6)'));
|
||||
pool_v6_size.datatype = 'integer';
|
||||
pool_v6_size.placeholder = 65535;
|
||||
|
||||
let fs = s.taboption('fake_dns', form.SectionValue, "fake_dns_section", form.GridSection, 'fakedns', _('FakeDNS Routing'), _('See <a href="https://github.com/v2ray/v2ray-core/issues/2233">FakeDNS</a> for details.')).subsection;
|
||||
fs.sortable = false;
|
||||
fs.anonymous = true;
|
||||
fs.addremove = true;
|
||||
|
||||
let fake_dns_domain_names = fs.option(form.DynamicList, "fake_dns_domain_names", _("Domain names"));
|
||||
fake_dns_domain_names.rmempty = false;
|
||||
fake_dns_domain_names.textvalue = list_folded_format(config_data, "fake_dns_domain_names", "domains");
|
||||
|
||||
let fake_dns_forward_server_tcp = fs.option(form.MultiValue, 'fake_dns_forward_server_tcp', _('Force Forward server (TCP)'));
|
||||
fake_dns_forward_server_tcp.datatype = "uciname";
|
||||
fake_dns_forward_server_tcp.textvalue = destination_format(config_data, "fake_dns_forward_server_tcp");
|
||||
|
||||
let fake_dns_forward_server_udp = fs.option(form.MultiValue, 'fake_dns_forward_server_udp', _('Force Forward server (UDP)'));
|
||||
fake_dns_forward_server_udp.datatype = "uciname";
|
||||
fake_dns_forward_server_udp.textvalue = destination_format(config_data, "fake_dns_forward_server_udp");
|
||||
|
||||
let fake_dns_balancer_strategy = fs.option(form.Value, 'fake_dns_balancer_strategy', _('Balancer Strategy'), _('Strategy <code>leastPing</code> requires observatory (see "Extra Options" tab) to be enabled.'));
|
||||
fake_dns_balancer_strategy.value("random");
|
||||
fake_dns_balancer_strategy.value("leastPing");
|
||||
fake_dns_balancer_strategy.default = "random";
|
||||
fake_dns_balancer_strategy.rmempty = false;
|
||||
fake_dns_balancer_strategy.modalonly = true;
|
||||
|
||||
s.tab('outbound_routing', _('Outbound Routing'));
|
||||
|
||||
if (geoip_existence) {
|
||||
let geoip_direct_code_list = s.taboption('transparent_proxy_rules', form.DynamicList, 'geoip_direct_code_list', _('GeoIP Direct Code List (IPv4)'), _("Hosts in these GeoIP sets will not be forwarded through Xray. Remove all items to forward all non-private hosts."));
|
||||
let geoip_direct_code_list = s.taboption('outbound_routing', form.DynamicList, 'geoip_direct_code_list', _('GeoIP Direct Code List (IPv4)'), _("Hosts in these GeoIP sets will not be forwarded through Xray. Remove all items to forward all non-private hosts."));
|
||||
geoip_direct_code_list.datatype = "string";
|
||||
geoip_direct_code_list.value("cn", "cn");
|
||||
geoip_direct_code_list.value("telegram", "telegram");
|
||||
|
||||
let geoip_direct_code_list_v6 = s.taboption('transparent_proxy_rules', form.DynamicList, 'geoip_direct_code_list_v6', _('GeoIP Direct Code List (IPv6)'), _("Hosts in these GeoIP sets will not be forwarded through Xray. Remove all items to forward all non-private hosts."));
|
||||
let geoip_direct_code_list_v6 = s.taboption('outbound_routing', form.DynamicList, 'geoip_direct_code_list_v6', _('GeoIP Direct Code List (IPv6)'), _("Hosts in these GeoIP sets will not be forwarded through Xray. Remove all items to forward all non-private hosts."));
|
||||
geoip_direct_code_list_v6.datatype = "string";
|
||||
geoip_direct_code_list_v6.value("cn", "cn");
|
||||
geoip_direct_code_list_v6.value("telegram", "telegram");
|
||||
} else {
|
||||
let geoip_direct_code_list = s.taboption('transparent_proxy_rules', form.DynamicList, 'geoip_direct_code_list', _('GeoIP Direct Code List (IPv4)'), _("Resource file /usr/share/xray/geoip.dat not exist. All network traffic will be forwarded. <br/> Compile your firmware again with data files to use this feature, or<br/><a href=\"https://github.com/v2fly/geoip\">download one</a> (maybe disable transparent proxy first) and upload it to your router."));
|
||||
let geoip_direct_code_list = s.taboption('outbound_routing', form.DynamicList, 'geoip_direct_code_list', _('GeoIP Direct Code List (IPv4)'), _("Resource file /usr/share/xray/geoip.dat not exist. All network traffic will be forwarded. <br/> Compile your firmware again with data files to use this feature, or<br/><a href=\"https://github.com/v2fly/geoip\">download one</a> (maybe disable transparent proxy first) and upload it to your router."));
|
||||
geoip_direct_code_list.readonly = true;
|
||||
geoip_direct_code_list.datatype = "string";
|
||||
|
||||
let geoip_direct_code_list_v6 = s.taboption('transparent_proxy_rules', form.DynamicList, 'geoip_direct_code_list_v6', _('GeoIP Direct Code List (IPv6)'), _("Resource file /usr/share/xray/geoip.dat not exist. All network traffic will be forwarded. <br/> Compile your firmware again with data files to use this feature, or<br/><a href=\"https://github.com/v2fly/geoip\">download one</a> (maybe disable transparent proxy first) and upload it to your router."));
|
||||
let geoip_direct_code_list_v6 = s.taboption('outbound_routing', form.DynamicList, 'geoip_direct_code_list_v6', _('GeoIP Direct Code List (IPv6)'), _("Resource file /usr/share/xray/geoip.dat not exist. All network traffic will be forwarded. <br/> Compile your firmware again with data files to use this feature, or<br/><a href=\"https://github.com/v2fly/geoip\">download one</a> (maybe disable transparent proxy first) and upload it to your router."));
|
||||
geoip_direct_code_list_v6.readonly = true;
|
||||
geoip_direct_code_list_v6.datatype = "string";
|
||||
}
|
||||
|
||||
o = s.taboption('transparent_proxy_rules', form.DynamicList, "wan_bp_ips", _("Bypassed IP"), _("Requests to these IPs won't be forwarded through Xray."));
|
||||
o = s.taboption('outbound_routing', form.DynamicList, "wan_bp_ips", _("Bypassed IP"), _("Requests to these IPs won't be forwarded through Xray."));
|
||||
o.datatype = "ipaddr";
|
||||
|
||||
o = s.taboption('transparent_proxy_rules', form.DynamicList, "wan_fw_ips", _("Forwarded IP"), _("Requests to these IPs will always be handled by Xray (but still might be bypassed by Xray itself, like private addresses).<br/>Useful for some really strange network. If you really need to forward private addresses, try Manual Transparent Proxy below."));
|
||||
o = s.taboption('outbound_routing', form.DynamicList, "wan_fw_ips", _("Forwarded IP"), _("Requests to these IPs will always be handled by Xray (but still might be bypassed by Xray itself, like private addresses).<br/>Useful for some really strange network. If you really need to forward private addresses, try Manual Transparent Proxy below."));
|
||||
o.datatype = "ipaddr";
|
||||
|
||||
o = s.taboption('transparent_proxy_rules', form.ListValue, 'transparent_default_port_policy', _('Default Ports Policy'));
|
||||
o = s.taboption('outbound_routing', form.ListValue, 'transparent_default_port_policy', _('Default Ports Policy'));
|
||||
o.value("forwarded", _("Forwarded"));
|
||||
o.value("bypassed", _("Bypassed"));
|
||||
o.default = "forwarded";
|
||||
|
||||
o = s.taboption('transparent_proxy_rules', form.DynamicList, "wan_fw_tcp_ports", _("Forwarded TCP Ports"), _("Requests to these TCP Ports will be forwarded through Xray. Recommended ports: 80, 443, 853"));
|
||||
o = s.taboption('outbound_routing', form.DynamicList, "wan_fw_tcp_ports", _("Forwarded TCP Ports"), _("Requests to these TCP Ports will be forwarded through Xray. Recommended ports: 80, 443, 853"));
|
||||
o.depends("transparent_default_port_policy", "bypassed");
|
||||
o.datatype = "portrange";
|
||||
|
||||
o = s.taboption('transparent_proxy_rules', form.DynamicList, "wan_fw_udp_ports", _("Forwarded UDP Ports"), _("Requests to these UDP Ports will be forwarded through Xray. Recommended ports: 53, 443"));
|
||||
o = s.taboption('outbound_routing', form.DynamicList, "wan_fw_udp_ports", _("Forwarded UDP Ports"), _("Requests to these UDP Ports will be forwarded through Xray. Recommended ports: 53, 443"));
|
||||
o.depends("transparent_default_port_policy", "bypassed");
|
||||
o.datatype = "portrange";
|
||||
|
||||
o = s.taboption('transparent_proxy_rules', form.DynamicList, "wan_bp_tcp_ports", _("Bypassed TCP Ports"), _("Requests to these TCP Ports won't be forwarded through Xray."));
|
||||
o = s.taboption('outbound_routing', form.DynamicList, "wan_bp_tcp_ports", _("Bypassed TCP Ports"), _("Requests to these TCP Ports won't be forwarded through Xray."));
|
||||
o.depends("transparent_default_port_policy", "forwarded");
|
||||
o.datatype = "portrange";
|
||||
|
||||
o = s.taboption('transparent_proxy_rules', form.DynamicList, "wan_bp_udp_ports", _("Bypassed UDP Ports"), _("Requests to these UDP Ports won't be forwarded through Xray."));
|
||||
o = s.taboption('outbound_routing', form.DynamicList, "wan_bp_udp_ports", _("Bypassed UDP Ports"), _("Requests to these UDP Ports won't be forwarded through Xray."));
|
||||
o.depends("transparent_default_port_policy", "forwarded");
|
||||
o.datatype = "portrange";
|
||||
|
||||
o = s.taboption('transparent_proxy_rules', form.Value, 'mark', _('Socket Mark Number'), _('Avoid proxy loopback problems with local (gateway) traffic'));
|
||||
o = s.taboption('outbound_routing', form.Value, 'mark', _('Socket Mark Number'), _('Avoid proxy loopback problems with local (gateway) traffic'));
|
||||
o.datatype = 'range(1, 255)';
|
||||
o.placeholder = 255;
|
||||
|
||||
o = s.taboption('transparent_proxy_rules', form.SectionValue, "access_control_manual_tproxy", form.GridSection, 'manual_tproxy', _('Manual Transparent Proxy'), _('Compared to iptables REDIRECT, Xray could do NAT46 / NAT64 (for example accessing IPv6 only sites). See <a href="https://github.com/v2ray/v2ray-core/issues/2233">FakeDNS</a> for details.'));
|
||||
o = s.taboption('outbound_routing', form.SectionValue, "access_control_manual_tproxy", form.GridSection, 'manual_tproxy', _('Manual Transparent Proxy'), _('Compared to iptables REDIRECT, Xray could do NAT46 / NAT64 (for example accessing IPv6 only sites). See <a href="https://github.com/v2ray/v2ray-core/issues/2233">FakeDNS</a> for details.'));
|
||||
|
||||
ss = o.subsection;
|
||||
ss.sortable = false;
|
||||
|
@ -737,6 +536,7 @@ return view.extend({
|
|||
o.datatype = "port";
|
||||
|
||||
o = ss.option(form.DynamicList, "domain_names", _("Domain names to associate"));
|
||||
o.textvalue = list_folded_format(config_data, "domain_names", "domains");
|
||||
|
||||
o = ss.option(form.Flag, 'rebind_domain_ok', _('Exempt rebind protection'), _('Avoid dnsmasq filtering RFC1918 IP addresses (and some TESTNET addresses as well) from result.<br/>Must be enabled for TESTNET addresses (<code>192.0.2.0/24</code>, <code>198.51.100.0/24</code>, <code>203.0.113.0/24</code>). Addresses like <a href="https://www.as112.net/">AS112 Project</a> (<code>192.31.196.0/24</code>, <code>192.175.48.0/24</code>) or <a href="https://www.nyiix.net/technical/rtbh/">NYIIX RTBH</a> (<code>198.32.160.7</code>) can avoid that.'));
|
||||
o.modalonly = true;
|
||||
|
@ -773,15 +573,10 @@ return view.extend({
|
|||
o.depends("web_server_enable", "1");
|
||||
|
||||
o = s.taboption('xray_server', form.ListValue, "web_server_protocol", _("Protocol"), _("Only protocols which support fallback are available. Note that REALITY does not support fallback right now."));
|
||||
o.value("vless", "VLESS");
|
||||
o.value("trojan", "Trojan");
|
||||
protocol.add_server_protocol(o, s, 'xray_server');
|
||||
o.rmempty = false;
|
||||
o.depends("web_server_enable", "1");
|
||||
|
||||
add_flow_and_stream_security_conf(s, "xray_server", "web_server_protocol", "vless", true, false);
|
||||
|
||||
add_flow_and_stream_security_conf(s, "xray_server", "web_server_protocol", "trojan", false, false);
|
||||
|
||||
o = s.taboption('xray_server', form.DynamicList, 'web_server_password', _('UserId / Password'), _('Fill user_id for vmess / VLESS, or password for shadowsocks / trojan (also supports <a href="https://github.com/XTLS/Xray-core/issues/158">Xray UUID Mapping</a>)'));
|
||||
o.depends("web_server_enable", "1");
|
||||
|
||||
|
@ -813,6 +608,12 @@ return view.extend({
|
|||
|
||||
s.tab('extra_options', _('Extra Options'));
|
||||
|
||||
o = s.taboption('extra_options', form.Value, 'xray_bin', _('Xray Executable Path'));
|
||||
o.rmempty = false;
|
||||
if (xray_bin_default) {
|
||||
o.value("/usr/bin/xray", _("/usr/bin/xray (default, exist)"));
|
||||
}
|
||||
|
||||
o = s.taboption('extra_options', form.ListValue, 'loglevel', _('Log Level'), _('Read Xray log in "System Log" or use <code>logread</code> command.'));
|
||||
o.value("debug");
|
||||
o.value("info");
|
||||
|
@ -829,7 +630,7 @@ return view.extend({
|
|||
|
||||
o = s.taboption('extra_options', form.Flag, 'stats', _('Enable Statistics'), _('Enable statistics of inbounds / outbounds data. Use Xray API to query values.'));
|
||||
|
||||
o = s.taboption('extra_options', form.Flag, 'observatory', _('Enable Observatory'), _('Enable latency measurement for TCP and UDP outbounds. Support for balancers and strategy will be added later.'));
|
||||
o = s.taboption('extra_options', form.Flag, 'observatory', _('Enable Observatory'), _('Enable latency measurement for TCP and UDP outbounds.'));
|
||||
|
||||
o = s.taboption('extra_options', form.Flag, 'fw4_counter', _('Enable firewall4 counters'), _('Add <a href="/cgi-bin/luci/admin/status/nftables">counters to firewall4</a> for transparent proxy rules. (Not supported in all OpenWrt versions. )'));
|
||||
|
||||
|
@ -860,6 +661,12 @@ return view.extend({
|
|||
o.datatype = 'uinteger';
|
||||
o.placeholder = 512;
|
||||
|
||||
o = s.taboption('extra_options', form.Value, 'firewall_priority', _('Priority for Firewall Rules'), _('See firewall status page for rules Xray used and <a href="https://wiki.nftables.org/wiki-nftables/index.php/Netfilter_hooks#Priority_within_hook">Netfilter Internal Priority</a> for reference.'));
|
||||
o.datatype = 'range(-49, 49)';
|
||||
o.placeholder = 10;
|
||||
|
||||
o = s.taboption('extra_options', form.Flag, 'preview_or_deprecated', _('Preview or Deprecated'), _("Show preview or deprecated features (requires reboot to take effect)."));
|
||||
|
||||
o = s.taboption('extra_options', form.SectionValue, "xray_bridge", form.TableSection, 'bridge', _('Bridge'), _('Reverse proxy tool. Currently only client role (bridge) is supported. See <a href="https://xtls.github.io/config/reverse.html#bridgeobject">here</a> for help.'));
|
||||
|
||||
ss = o.subsection;
|
||||
|
@ -881,10 +688,39 @@ return view.extend({
|
|||
o.rmempty = false;
|
||||
|
||||
s.tab('custom_options', _('Custom Options'));
|
||||
o = s.taboption('custom_options', form.TextValue, 'custom_config', _('Custom Configurations'), _('Check <code>/var/etc/xray/config.json</code> for tags of generated inbounds and outbounds. See <a href="https://xtls.github.io/config/features/multiple.html">here</a> for help'));
|
||||
o.monospace = true;
|
||||
o.rows = 20;
|
||||
o.validate = validate_object;
|
||||
let custom_configuration_hook = s.taboption('custom_options', form.TextValue, 'custom_configuration_hook', _('Custom Configuration Hook'), _('Read <a href="https://ucode.mein.io/">ucode Documentation</a> for the language used. Code filled here may need to change after upgrading luci-app-xray.'));
|
||||
custom_configuration_hook.placeholder = "return function(config) {\n return config;\n};";
|
||||
custom_configuration_hook.monospace = true;
|
||||
custom_configuration_hook.rows = 20;
|
||||
|
||||
const servers = uci.sections(config_data, "servers");
|
||||
if (servers.length == 0) {
|
||||
destination.value("direct", _("No server configured"));
|
||||
tcp_balancer_v4.value("direct", _("No server configured"));
|
||||
udp_balancer_v4.value("direct", _("No server configured"));
|
||||
tcp_balancer_v6.value("direct", _("No server configured"));
|
||||
udp_balancer_v6.value("direct", _("No server configured"));
|
||||
fake_dns_forward_server_tcp.value("direct", _("No server configured"));
|
||||
fake_dns_forward_server_udp.value("direct", _("No server configured"));
|
||||
|
||||
destination.readonly = true;
|
||||
tcp_balancer_v4.readonly = true;
|
||||
udp_balancer_v4.readonly = true;
|
||||
tcp_balancer_v6.readonly = true;
|
||||
udp_balancer_v6.readonly = true;
|
||||
fake_dns_forward_server_tcp.readonly = true;
|
||||
fake_dns_forward_server_udp.readonly = true;
|
||||
} else {
|
||||
for (const v of servers) {
|
||||
destination.value(v[".name"], v.alias || v.server + ":" + v.server_port);
|
||||
tcp_balancer_v4.value(v[".name"], v.alias || v.server + ":" + v.server_port);
|
||||
udp_balancer_v4.value(v[".name"], v.alias || v.server + ":" + v.server_port);
|
||||
tcp_balancer_v6.value(v[".name"], v.alias || v.server + ":" + v.server_port);
|
||||
udp_balancer_v6.value(v[".name"], v.alias || v.server + ":" + v.server_port);
|
||||
fake_dns_forward_server_tcp.value(v[".name"], v.alias || v.server + ":" + v.server_port);
|
||||
fake_dns_forward_server_udp.value(v[".name"], v.alias || v.server + ":" + v.server_port);
|
||||
}
|
||||
}
|
||||
|
||||
return m.render();
|
||||
}
|
||||
|
|
|
@ -1,243 +1,36 @@
|
|||
'use strict';
|
||||
'require form';
|
||||
'require network';
|
||||
'require tools.widgets as widgets';
|
||||
'require uci';
|
||||
'require view';
|
||||
|
||||
const variant = "xray_core";
|
||||
|
||||
function destination_format(k) {
|
||||
return function (s) {
|
||||
const dest = uci.get(variant, s, k) || [];
|
||||
if (dest.length == 0) {
|
||||
return "<i>direct</i>";
|
||||
}
|
||||
return dest.map(v => uci.get(variant, v, "alias")).join(", ");
|
||||
};
|
||||
}
|
||||
|
||||
function extra_outbound_format(config_data, s, with_desc) {
|
||||
const inbound_addr = uci.get(config_data, s, "inbound_addr") || "";
|
||||
const inbound_port = uci.get(config_data, s, "inbound_port") || "";
|
||||
if (inbound_addr == "" && inbound_port == "") {
|
||||
return "-";
|
||||
}
|
||||
if (with_desc) {
|
||||
return `${inbound_addr}:${inbound_port} (${destination_format("destination")(s)})`;
|
||||
}
|
||||
return `${inbound_addr}:${inbound_port}`;
|
||||
}
|
||||
|
||||
function access_control_format(config_data, s, t) {
|
||||
return function (v) {
|
||||
switch (uci.get(config_data, v, s)) {
|
||||
case "tproxy": {
|
||||
return _("Enable tproxy");
|
||||
}
|
||||
case "bypass": {
|
||||
return _("Disable tproxy");
|
||||
}
|
||||
}
|
||||
return extra_outbound_format(config_data, uci.get(config_data, v, t));
|
||||
};
|
||||
}
|
||||
'require view.xray.shared as shared';
|
||||
|
||||
return view.extend({
|
||||
load: function () {
|
||||
return Promise.all([
|
||||
uci.load(variant),
|
||||
network.getHostHints()
|
||||
]);
|
||||
},
|
||||
|
||||
render: function (load_result) {
|
||||
const m = new form.Map(variant, _('Xray (preview)'), _("WARNING: These features are experimental, may cause a lot of problems and are not guaranteed to be compatible across minor versions."));
|
||||
const config_data = load_result[0];
|
||||
const hosts = load_result[1].hosts;
|
||||
render: function () {
|
||||
const m = new form.Map(shared.variant, _('Xray (preview)'), _("WARNING: These features are experimental, may cause a lot of problems and are not guaranteed to be compatible across minor versions."));
|
||||
|
||||
let s = m.section(form.TypedSection, 'general');
|
||||
s.addremove = false;
|
||||
s.anonymous = true;
|
||||
|
||||
s.tab('fake_dns', _('FakeDNS'));
|
||||
s.tab("ttl_override", _("TTL Override"));
|
||||
|
||||
let tproxy_port_tcp_f4 = s.taboption('fake_dns', form.Value, 'tproxy_port_tcp_f4', _('Transparent proxy port (TCP4)'));
|
||||
tproxy_port_tcp_f4.datatype = 'port';
|
||||
tproxy_port_tcp_f4.placeholder = 1086;
|
||||
let ttl_override = s.taboption('ttl_override', form.Value, 'ttl_override', _('Override IPv4 TTL'), _("Strongly not recommended. Only used for some network environments with specific restrictions."));
|
||||
ttl_override.datatype = 'uinteger';
|
||||
|
||||
let tproxy_port_tcp_f6 = s.taboption('fake_dns', form.Value, 'tproxy_port_tcp_f6', _('Transparent proxy port (TCP6)'));
|
||||
tproxy_port_tcp_f6.datatype = 'port';
|
||||
tproxy_port_tcp_f6.placeholder = 1087;
|
||||
let hop_limit_override = s.taboption('ttl_override', form.Value, 'hop_limit_override', _('Override IPv6 Hop Limit'), _("Strongly not recommended. Only used for some network environments with specific restrictions."));
|
||||
hop_limit_override.datatype = 'uinteger';
|
||||
|
||||
let tproxy_port_udp_f4 = s.taboption('fake_dns', form.Value, 'tproxy_port_udp_f4', _('Transparent proxy port (UDP4)'));
|
||||
tproxy_port_udp_f4.datatype = 'port';
|
||||
tproxy_port_udp_f4.placeholder = 1088;
|
||||
let ttl_hop_limit_match = s.taboption('ttl_override', form.Value, 'ttl_hop_limit_match', _('TTL / Hop Limit Match'), _("Only override TTL / hop limit for packets with specific TTL / hop limit."));
|
||||
ttl_hop_limit_match.datatype = 'uinteger';
|
||||
|
||||
let tproxy_port_udp_f6 = s.taboption('fake_dns', form.Value, 'tproxy_port_udp_f6', _('Transparent proxy port (UDP6)'));
|
||||
tproxy_port_udp_f6.datatype = 'port';
|
||||
tproxy_port_udp_f6.placeholder = 1089;
|
||||
s.tab("sniffing", _("Sniffing"));
|
||||
|
||||
let pool_v4 = s.taboption('fake_dns', form.Value, 'pool_v4', _('Address Pool (IPv4)'));
|
||||
pool_v4.datatype = 'ip4addr';
|
||||
pool_v4.placeholder = "198.18.0.0/15";
|
||||
s.taboption('sniffing', form.Flag, 'tproxy_sniffing', _('Enable Sniffing'), _('Route requests according to domain settings in "DNS Settings" tab in core settings. Deprecated; use FakeDNS instead.'));
|
||||
|
||||
let pool_v4_size = s.taboption('fake_dns', form.Value, 'pool_v4_size', _('Address Pool Size (IPv4)'));
|
||||
pool_v4_size.datatype = 'integer';
|
||||
pool_v4_size.placeholder = 65535;
|
||||
let route_only = s.taboption('sniffing', form.Flag, 'route_only', _('Route Only'), _('Use sniffed domain for routing only but still access through IP. Reduces unnecessary DNS requests. See <a href="https://github.com/XTLS/Xray-core/commit/a3023e43ef55d4498b1afbc9a7fe7b385138bb1a">here</a> for help.'));
|
||||
route_only.depends("tproxy_sniffing", "1");
|
||||
|
||||
let pool_v6 = s.taboption('fake_dns', form.Value, 'pool_v6', _('Address Pool (IPv6)'));
|
||||
pool_v6.datatype = 'ip6addr';
|
||||
pool_v6.placeholder = "fc00::/18";
|
||||
|
||||
let pool_v6_size = s.taboption('fake_dns', form.Value, 'pool_v6_size', _('Address Pool Size (IPv6)'));
|
||||
pool_v6_size.datatype = 'integer';
|
||||
pool_v6_size.placeholder = 65535;
|
||||
|
||||
let fake_dns_timeout = s.taboption('fake_dns', form.Value, 'fake_dns_timeout', _('Connection Idle Timeout'), _('Policy: Close connection if no data is transferred within given timeout. See <a href="https://xtls.github.io/config/policy.html#levelpolicyobject">here</a> for help.'));
|
||||
fake_dns_timeout.datatype = 'uinteger';
|
||||
fake_dns_timeout.placeholder = 300;
|
||||
|
||||
let fs = s.taboption('fake_dns', form.SectionValue, "fake_dns_section", form.GridSection, 'fakedns', _('FakeDNS Routing'), _('See <a href="https://github.com/v2ray/v2ray-core/issues/2233">FakeDNS</a> for details.')).subsection;
|
||||
fs.sortable = false;
|
||||
fs.anonymous = true;
|
||||
fs.addremove = true;
|
||||
|
||||
let fake_dns_domain_names = fs.option(form.DynamicList, "fake_dns_domain_names", _("Domain names to associate"));
|
||||
fake_dns_domain_names.rmempty = true;
|
||||
|
||||
let fake_dns_forward_server_tcp = fs.option(form.MultiValue, 'fake_dns_forward_server_tcp', _('Force Forward server (TCP)'));
|
||||
fake_dns_forward_server_tcp.datatype = "uciname";
|
||||
fake_dns_forward_server_tcp.textvalue = destination_format("fake_dns_forward_server_tcp");
|
||||
|
||||
let fake_dns_forward_server_udp = fs.option(form.MultiValue, 'fake_dns_forward_server_udp', _('Force Forward server (UDP)'));
|
||||
fake_dns_forward_server_udp.datatype = "uciname";
|
||||
fake_dns_forward_server_udp.textvalue = destination_format("fake_dns_forward_server_udp");
|
||||
|
||||
s.tab("extra_inbounds", "Extra Inbounds");
|
||||
|
||||
let extra_inbounds = s.taboption('extra_inbounds', form.SectionValue, "extra_inbound_section", form.GridSection, 'extra_inbound', _('Extra Inbounds'), _("Add more socks5 / http inbounds and redirect to other outbounds.")).subsection;
|
||||
extra_inbounds.sortable = false;
|
||||
extra_inbounds.anonymous = true;
|
||||
extra_inbounds.addremove = true;
|
||||
extra_inbounds.nodescriptions = true;
|
||||
|
||||
let inbound_addr = extra_inbounds.option(form.Value, "inbound_addr", _("Listen Address"));
|
||||
inbound_addr.datatype = "ip4addr";
|
||||
|
||||
let inbound_port = extra_inbounds.option(form.Value, "inbound_port", _("Listen Port"));
|
||||
inbound_port.datatype = "port";
|
||||
|
||||
let inbound_type = extra_inbounds.option(form.ListValue, "inbound_type", _("Inbound Type"));
|
||||
inbound_type.value("socks5", _("Socks5 Proxy"));
|
||||
inbound_type.value("http", _("HTTP Proxy"));
|
||||
inbound_type.value("tproxy_tcp", _("Transparent Proxy (TCP)"));
|
||||
inbound_type.value("tproxy_udp", _("Transparent Proxy (UDP)"));
|
||||
inbound_type.rmempty = false;
|
||||
|
||||
let specify_outbound = extra_inbounds.option(form.Flag, 'specify_outbound', _('Specify Outbound'), _('If not selected, this inbound will use global settings (including sniffing settings). '));
|
||||
specify_outbound.modalonly = true;
|
||||
|
||||
let destination = extra_inbounds.option(form.MultiValue, 'destination', _('Destination'), _("Select multiple outbounds for load balancing. If none selected, requests will be sent via direct outbound."));
|
||||
destination.depends("specify_outbound", "1");
|
||||
destination.datatype = "uciname";
|
||||
destination.textvalue = destination_format("destination");
|
||||
|
||||
const servers = uci.sections(config_data, "servers");
|
||||
if (servers.length == 0) {
|
||||
destination.value("direct", _("No server configured"));
|
||||
fake_dns_forward_server_tcp.value("direct", _("No server configured"));
|
||||
fake_dns_forward_server_udp.value("direct", _("No server configured"));
|
||||
|
||||
destination.readonly = true;
|
||||
fake_dns_forward_server_tcp.readonly = true;
|
||||
fake_dns_forward_server_udp.readonly = true;
|
||||
} else {
|
||||
for (const v of uci.sections(config_data, "servers")) {
|
||||
destination.value(v[".name"], v.alias || v.server + ":" + v.server_port);
|
||||
fake_dns_forward_server_tcp.value(v[".name"], v.alias || v.server + ":" + v.server_port);
|
||||
fake_dns_forward_server_udp.value(v[".name"], v.alias || v.server + ":" + v.server_port);
|
||||
}
|
||||
}
|
||||
|
||||
s.tab("lan_hosts_access_control", _("LAN Hosts Access Control"));
|
||||
|
||||
let tproxy_ifaces_v4 = s.taboption('lan_hosts_access_control', widgets.DeviceSelect, 'tproxy_ifaces_v4', _("Devices to enable IPv4 tproxy"), _("Enable IPv4 transparent proxy on these interfaces / network devices."));
|
||||
tproxy_ifaces_v4.noaliases = true;
|
||||
tproxy_ifaces_v4.nocreate = true;
|
||||
tproxy_ifaces_v4.multiple = true;
|
||||
|
||||
let tproxy_ifaces_v6 = s.taboption('lan_hosts_access_control', widgets.DeviceSelect, 'tproxy_ifaces_v6', _("Devices to enable IPv6 tproxy"), _("Enable IPv6 transparent proxy on these interfaces / network devices."));
|
||||
tproxy_ifaces_v6.noaliases = true;
|
||||
tproxy_ifaces_v6.nocreate = true;
|
||||
tproxy_ifaces_v6.multiple = true;
|
||||
|
||||
let bypass_ifaces_v4 = s.taboption('lan_hosts_access_control', widgets.DeviceSelect, 'bypass_ifaces_v4', _("Devices to disable IPv4 tproxy"), _("This overrides per-device settings below. FakeDNS and manual transparent proxy won't be affected by this option."));
|
||||
bypass_ifaces_v4.noaliases = true;
|
||||
bypass_ifaces_v4.nocreate = true;
|
||||
bypass_ifaces_v4.multiple = true;
|
||||
|
||||
let bypass_ifaces_v6 = s.taboption('lan_hosts_access_control', widgets.DeviceSelect, 'bypass_ifaces_v6', _("Devices to disable IPv6 tproxy"), _("This overrides per-device settings below. FakeDNS and manual transparent proxy won't be affected by this option."));
|
||||
bypass_ifaces_v6.noaliases = true;
|
||||
bypass_ifaces_v6.nocreate = true;
|
||||
bypass_ifaces_v6.multiple = true;
|
||||
|
||||
let lan_hosts = s.taboption('lan_hosts_access_control', form.SectionValue, "lan_hosts_section", form.GridSection, 'lan_hosts', _('LAN Hosts Access Control'), _("Per-device settings here override per-interface enabling settings above. FakeDNS and manual transparent proxy won't be affected by these options.")).subsection;
|
||||
lan_hosts.sortable = false;
|
||||
lan_hosts.anonymous = true;
|
||||
lan_hosts.addremove = true;
|
||||
|
||||
let macaddr = lan_hosts.option(form.Value, "macaddr", _("MAC Address"));
|
||||
macaddr.datatype = "macaddr";
|
||||
macaddr.rmempty = false;
|
||||
L.sortedKeys(hosts).forEach(function (mac) {
|
||||
macaddr.value(mac, E([], [mac, ' (', E('strong', [hosts[mac].name || L.toArray(hosts[mac].ipaddrs || hosts[mac].ipv4)[0] || L.toArray(hosts[mac].ip6addrs || hosts[mac].ipv6)[0] || '?']), ')']));
|
||||
});
|
||||
|
||||
let access_control_strategy_v4 = lan_hosts.option(form.ListValue, "access_control_strategy_v4", _("Access Control Strategy (IPv4)"));
|
||||
access_control_strategy_v4.value("tproxy", _("Enable transparent proxy"));
|
||||
access_control_strategy_v4.value("forward", _("Forward via extra inbound"));
|
||||
access_control_strategy_v4.value("bypass", _("Disable transparent proxy"));
|
||||
access_control_strategy_v4.modalonly = true;
|
||||
access_control_strategy_v4.rmempty = false;
|
||||
|
||||
let access_control_forward_tcp_v4 = lan_hosts.option(form.ListValue, "access_control_forward_tcp_v4", _("Extra inbound (TCP4)"));
|
||||
access_control_forward_tcp_v4.depends("access_control_strategy_v4", "forward");
|
||||
access_control_forward_tcp_v4.textvalue = access_control_format(config_data, "access_control_strategy_v4", "access_control_forward_tcp_v4");
|
||||
|
||||
let access_control_forward_udp_v4 = lan_hosts.option(form.ListValue, "access_control_forward_udp_v4", _("Extra inbound (UDP4)"));
|
||||
access_control_forward_udp_v4.depends("access_control_strategy_v4", "forward");
|
||||
access_control_forward_udp_v4.textvalue = access_control_format(config_data, "access_control_strategy_v4", "access_control_forward_udp_v4");
|
||||
|
||||
let access_control_strategy_v6 = lan_hosts.option(form.ListValue, "access_control_strategy_v6", _("Access Control Strategy (IPv6)"));
|
||||
access_control_strategy_v6.value("tproxy", _("Enable transparent proxy"));
|
||||
access_control_strategy_v6.value("forward", _("Forward via extra inbound"));
|
||||
access_control_strategy_v6.value("bypass", _("Disable transparent proxy"));
|
||||
access_control_strategy_v6.modalonly = true;
|
||||
access_control_strategy_v6.rmempty = false;
|
||||
|
||||
let access_control_forward_tcp_v6 = lan_hosts.option(form.ListValue, "access_control_forward_tcp_v6", _("Extra inbound (TCP6)"));
|
||||
access_control_forward_tcp_v6.depends("access_control_strategy_v6", "forward");
|
||||
access_control_forward_tcp_v6.textvalue = access_control_format(config_data, "access_control_strategy_v6", "access_control_forward_tcp_v6");
|
||||
|
||||
let access_control_forward_udp_v6 = lan_hosts.option(form.ListValue, "access_control_forward_udp_v6", _("Extra inbound (UDP6)"));
|
||||
access_control_forward_udp_v6.depends("access_control_strategy_v6", "forward");
|
||||
access_control_forward_udp_v6.textvalue = access_control_format(config_data, "access_control_strategy_v6", "access_control_forward_udp_v6");
|
||||
|
||||
for (const v of uci.sections(config_data, "extra_inbound")) {
|
||||
switch (v["inbound_type"]) {
|
||||
case "tproxy_tcp": {
|
||||
access_control_forward_tcp_v4.value(v[".name"], `${extra_outbound_format(config_data, v[".name"], true)}`);
|
||||
access_control_forward_tcp_v6.value(v[".name"], `${extra_outbound_format(config_data, v[".name"], true)}`);
|
||||
break;
|
||||
}
|
||||
case "tproxy_udp": {
|
||||
access_control_forward_udp_v4.value(v[".name"], `${extra_outbound_format(config_data, v[".name"], true)}`);
|
||||
access_control_forward_udp_v6.value(v[".name"], `${extra_outbound_format(config_data, v[".name"], true)}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
let direct_bittorrent = s.taboption('sniffing', form.Flag, 'direct_bittorrent', _('Bittorrent Direct'), _("If enabled, no bittorrent request will be forwarded through Xray."));
|
||||
direct_bittorrent.depends("tproxy_sniffing", "1");
|
||||
|
||||
s.tab('dynamic_direct', _('Dynamic Direct'));
|
||||
|
||||
|
@ -250,20 +43,11 @@ return view.extend({
|
|||
dynamic_direct_timeout.datatype = 'uinteger';
|
||||
dynamic_direct_timeout.placeholder = 300;
|
||||
|
||||
let ttl_override = s.taboption('dynamic_direct', form.Value, 'ttl_override', _('Override IPv4 TTL'), _("Strongly not recommended. Only used for some network environments with specific restrictions."));
|
||||
ttl_override.datatype = 'uinteger';
|
||||
|
||||
let hop_limit_override = s.taboption('dynamic_direct', form.Value, 'hop_limit_override', _('Override IPv6 Hop Limit'), _("Strongly not recommended. Only used for some network environments with specific restrictions."));
|
||||
hop_limit_override.datatype = 'uinteger';
|
||||
|
||||
let ttl_hop_limit_match = s.taboption('dynamic_direct', form.Value, 'ttl_hop_limit_match', _('TTL / Hop Limit Match'), _("Only override TTL / hop limit for packets with specific TTL / hop limit."));
|
||||
ttl_hop_limit_match.datatype = 'uinteger';
|
||||
|
||||
s.tab('custom_options', _('Custom Options'));
|
||||
let custom_configuration_hook = s.taboption('custom_options', form.TextValue, 'custom_configuration_hook', _('Custom Configuration Hook'), _('Read <a href="https://ucode.mein.io/">ucode Documentation</a> for the language used. Code filled here may need to change after upgrading luci-app-xray.'));
|
||||
custom_configuration_hook.placeholder = "return function(config) {\n return config;\n};";
|
||||
custom_configuration_hook.monospace = true;
|
||||
custom_configuration_hook.rows = 20;
|
||||
let custom_config = s.taboption('custom_options', form.TextValue, 'custom_config', _('Custom Configurations'), _('Check <code>/var/etc/xray/config.json</code> for tags of generated inbounds and outbounds. See <a href="https://xtls.github.io/config/features/multiple.html">here</a> for help'));
|
||||
custom_config.monospace = true;
|
||||
custom_config.rows = 20;
|
||||
custom_config.validate = shared.validate_object;
|
||||
|
||||
return m.render();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,252 @@
|
|||
'use strict';
|
||||
'require baseclass';
|
||||
'require form';
|
||||
|
||||
function fingerprints(o) {
|
||||
o.value("chrome", "chrome");
|
||||
o.value("firefox", "firefox");
|
||||
o.value("safari", "safari");
|
||||
o.value("ios", "ios");
|
||||
o.value("android", "android");
|
||||
o.value("edge", "edge");
|
||||
o.value("360", "360");
|
||||
o.value("qq", "qq");
|
||||
o.value("random", "random");
|
||||
o.value("randomized", "randomized");
|
||||
}
|
||||
|
||||
function add_flow_and_stream_security_conf(s, tab_name, depends_field_name, protocol_name, have_tls_flow, server_side) {
|
||||
let o = s.taboption(tab_name, form.ListValue, `${protocol_name}_tls`, _(`[${protocol_name}] Stream Security`));
|
||||
let odep = {};
|
||||
odep[depends_field_name] = protocol_name;
|
||||
if (server_side) {
|
||||
odep["web_server_enable"] = "1";
|
||||
} else {
|
||||
o.depends(depends_field_name, protocol_name);
|
||||
o.value("none", "None");
|
||||
}
|
||||
o.value("tls", "TLS");
|
||||
if (have_tls_flow) {
|
||||
o.value("reality", "REALITY (Experimental)");
|
||||
}
|
||||
o.depends(odep);
|
||||
o.rmempty = false;
|
||||
o.modalonly = true;
|
||||
|
||||
if (have_tls_flow) {
|
||||
let flow_tls = s.taboption(tab_name, form.ListValue, `${protocol_name}_flow_tls`, _(`[${protocol_name}][tls] Flow`));
|
||||
let flow_tls_dep = {};
|
||||
flow_tls_dep[depends_field_name] = protocol_name;
|
||||
flow_tls_dep[`${protocol_name}_tls`] = "tls";
|
||||
flow_tls.value("none", "none");
|
||||
flow_tls.value("xtls-rprx-vision", "xtls-rprx-vision");
|
||||
flow_tls.value("xtls-rprx-vision-udp443", "xtls-rprx-vision-udp443");
|
||||
if (server_side) {
|
||||
flow_tls_dep["web_server_enable"] = "1";
|
||||
}
|
||||
flow_tls.depends(flow_tls_dep);
|
||||
flow_tls.rmempty = false;
|
||||
flow_tls.modalonly = true;
|
||||
|
||||
let flow_reality = s.taboption(tab_name, form.ListValue, `${protocol_name}_flow_reality`, _(`[${protocol_name}][reality] Flow`));
|
||||
let flow_reality_dep = {};
|
||||
flow_reality_dep[depends_field_name] = protocol_name;
|
||||
flow_reality_dep[`${protocol_name}_tls`] = "reality";
|
||||
flow_reality.value("none", "none");
|
||||
flow_reality.value("xtls-rprx-vision", "xtls-rprx-vision");
|
||||
flow_reality.value("xtls-rprx-vision-udp443", "xtls-rprx-vision-udp443");
|
||||
if (server_side) {
|
||||
flow_reality_dep["web_server_enable"] = "1";
|
||||
}
|
||||
flow_reality.depends(flow_reality_dep);
|
||||
flow_reality.rmempty = false;
|
||||
flow_reality.modalonly = true;
|
||||
|
||||
o = s.taboption(tab_name, form.Flag, `${protocol_name}_reality_show`, _(`[${protocol_name}][reality] Show`));
|
||||
o.depends(`${protocol_name}_tls`, "reality");
|
||||
o.modalonly = true;
|
||||
}
|
||||
|
||||
if (server_side) {
|
||||
let tls_cert_key_dep = { "web_server_enable": "1" };
|
||||
tls_cert_key_dep[`${protocol_name}_tls`] = "tls";
|
||||
o = s.taboption(tab_name, form.FileUpload, `${protocol_name}_tls_cert_file`, _(`[${protocol_name}][tls] Certificate File`));
|
||||
o.root_directory = "/etc/luci-uploads/xray";
|
||||
o.depends(tls_cert_key_dep);
|
||||
|
||||
o = s.taboption(tab_name, form.FileUpload, `${protocol_name}_tls_key_file`, _(`[${protocol_name}][tls] Private Key File`));
|
||||
o.root_directory = "/etc/luci-uploads/xray";
|
||||
o.depends(tls_cert_key_dep);
|
||||
|
||||
if (have_tls_flow) {
|
||||
o = s.taboption(tab_name, form.Value, `${protocol_name}_reality_dest`, _(`[${protocol_name}][reality] Dest`));
|
||||
o.depends(`${protocol_name}_tls`, "reality");
|
||||
o.datatype = "hostport";
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.taboption(tab_name, form.Value, `${protocol_name}_reality_xver`, _(`[${protocol_name}][reality] Xver`));
|
||||
o.depends(`${protocol_name}_tls`, "reality");
|
||||
o.datatype = "integer";
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.taboption(tab_name, form.DynamicList, `${protocol_name}_reality_server_names`, _(`[${protocol_name}][reality] Server Names`));
|
||||
o.depends(`${protocol_name}_tls`, "reality");
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.taboption(tab_name, form.Value, `${protocol_name}_reality_private_key`, _(`[${protocol_name}][reality] Private Key`));
|
||||
o.depends(`${protocol_name}_tls`, "reality");
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.taboption(tab_name, form.Value, `${protocol_name}_reality_min_client_ver`, _(`[${protocol_name}][reality] Min Client Ver`));
|
||||
o.depends(`${protocol_name}_tls`, "reality");
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.taboption(tab_name, form.Value, `${protocol_name}_reality_max_client_ver`, _(`[${protocol_name}][reality] Max Client Ver`));
|
||||
o.depends(`${protocol_name}_tls`, "reality");
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.taboption(tab_name, form.Value, `${protocol_name}_reality_max_time_diff`, _(`[${protocol_name}][reality] Max Time Diff`));
|
||||
o.depends(`${protocol_name}_tls`, "reality");
|
||||
o.datatype = "integer";
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.taboption(tab_name, form.DynamicList, `${protocol_name}_reality_short_ids`, _(`[${protocol_name}][reality] Short Ids`));
|
||||
o.depends(`${protocol_name}_tls`, "reality");
|
||||
o.modalonly = true;
|
||||
}
|
||||
} else {
|
||||
o = s.taboption(tab_name, form.Value, `${protocol_name}_tls_host`, _(`[${protocol_name}][tls] Server Name`));
|
||||
o.depends(`${protocol_name}_tls`, "tls");
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.taboption(tab_name, form.Flag, `${protocol_name}_tls_insecure`, _(`[${protocol_name}][tls] Allow Insecure`));
|
||||
o.depends(`${protocol_name}_tls`, "tls");
|
||||
o.rmempty = false;
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.taboption(tab_name, form.Value, `${protocol_name}_tls_fingerprint`, _(`[${protocol_name}][tls] Fingerprint`));
|
||||
o.depends(`${protocol_name}_tls`, "tls");
|
||||
o.value("", "(not set)");
|
||||
fingerprints(o);
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.taboption(tab_name, form.DynamicList, `${protocol_name}_tls_alpn`, _(`[${protocol_name}][tls] ALPN`));
|
||||
o.depends(`${protocol_name}_tls`, "tls");
|
||||
o.value("h2", "h2");
|
||||
o.value("http/1.1", "http/1.1");
|
||||
o.modalonly = true;
|
||||
|
||||
if (have_tls_flow) {
|
||||
o = s.taboption(tab_name, form.Value, `${protocol_name}_reality_fingerprint`, _(`[${protocol_name}][reality] Fingerprint`));
|
||||
o.depends(`${protocol_name}_tls`, "reality");
|
||||
fingerprints(o);
|
||||
o.rmempty = false;
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.taboption(tab_name, form.Value, `${protocol_name}_reality_server_name`, _(`[${protocol_name}][reality] Server Name`));
|
||||
o.depends(`${protocol_name}_tls`, "reality");
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.taboption(tab_name, form.Value, `${protocol_name}_reality_public_key`, _(`[${protocol_name}][reality] Public Key`));
|
||||
o.depends(`${protocol_name}_tls`, "reality");
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.taboption(tab_name, form.Value, `${protocol_name}_reality_short_id`, _(`[${protocol_name}][reality] Short Id`));
|
||||
o.depends(`${protocol_name}_tls`, "reality");
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.taboption(tab_name, form.Value, `${protocol_name}_spider_x`, _(`[${protocol_name}][reality] SpiderX`));
|
||||
o.depends(`${protocol_name}_tls`, "reality");
|
||||
o.modalonly = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function shadowsocks_client(protocol, sub_section, tab_name) {
|
||||
protocol.value("shadowsocks", "Shadowsocks");
|
||||
|
||||
let shadowsocks_security = sub_section.taboption(tab_name, form.ListValue, "shadowsocks_security", _("[shadowsocks] Encrypt Method"));
|
||||
shadowsocks_security.depends("protocol", "shadowsocks");
|
||||
shadowsocks_security.value("none", "none");
|
||||
shadowsocks_security.value("aes-256-gcm", "aes-256-gcm");
|
||||
shadowsocks_security.value("aes-128-gcm", "aes-128-gcm");
|
||||
shadowsocks_security.value("chacha20-poly1305", "chacha20-poly1305");
|
||||
shadowsocks_security.value("2022-blake3-aes-128-gcm", "2022-blake3-aes-128-gcm");
|
||||
shadowsocks_security.value("2022-blake3-aes-256-gcm", "2022-blake3-aes-256-gcm");
|
||||
shadowsocks_security.value("2022-blake3-chacha20-poly1305", "2022-blake3-chacha20-poly1305");
|
||||
shadowsocks_security.rmempty = false;
|
||||
shadowsocks_security.modalonly = true;
|
||||
|
||||
let shadowsocks_udp_over_tcp = sub_section.taboption(tab_name, form.Flag, 'shadowsocks_udp_over_tcp', _('[shadowsocks] UDP over TCP'), _('Only available for shadowsocks-2022 ciphers (2022-*)'));
|
||||
shadowsocks_udp_over_tcp.depends("shadowsocks_security", /2022/);
|
||||
shadowsocks_udp_over_tcp.rmempty = false;
|
||||
shadowsocks_udp_over_tcp.modalonly = true;
|
||||
|
||||
add_flow_and_stream_security_conf(sub_section, tab_name, "protocol", "shadowsocks", false, false);
|
||||
}
|
||||
|
||||
function vmess_client(protocol, sub_section, tab_name) {
|
||||
protocol.value("vmess", "VMess");
|
||||
|
||||
let vmess_security = sub_section.taboption(tab_name, form.ListValue, "vmess_security", _("[vmess] Encrypt Method"));
|
||||
vmess_security.depends("protocol", "vmess");
|
||||
vmess_security.value("none", "none");
|
||||
vmess_security.value("auto", "auto");
|
||||
vmess_security.value("aes-128-gcm", "aes-128-gcm");
|
||||
vmess_security.value("chacha20-poly1305", "chacha20-poly1305");
|
||||
vmess_security.rmempty = false;
|
||||
vmess_security.modalonly = true;
|
||||
|
||||
let vmess_alter_id = sub_section.taboption(tab_name, form.ListValue, "vmess_alter_id", _("[vmess] AlterId"), _("Deprecated. Make sure you always use VMessAEAD."));
|
||||
vmess_alter_id.depends("protocol", "vmess");
|
||||
vmess_alter_id.value(0, "0 (this enables VMessAEAD)");
|
||||
vmess_alter_id.value(1, "1");
|
||||
vmess_alter_id.value(4, "4");
|
||||
vmess_alter_id.value(16, "16");
|
||||
vmess_alter_id.value(64, "64");
|
||||
vmess_alter_id.value(256, "256");
|
||||
vmess_alter_id.rmempty = false;
|
||||
vmess_alter_id.modalonly = true;
|
||||
|
||||
add_flow_and_stream_security_conf(sub_section, tab_name, "protocol", "vmess", false, false);
|
||||
}
|
||||
|
||||
function vless_client(protocol, sub_section, tab_name) {
|
||||
protocol.value("vless", "VLESS");
|
||||
|
||||
let vless_encryption = sub_section.taboption(tab_name, form.ListValue, "vless_encryption", _("[vless] Encrypt Method"));
|
||||
vless_encryption.depends("protocol", "vless");
|
||||
vless_encryption.value("none", "none");
|
||||
vless_encryption.rmempty = false;
|
||||
vless_encryption.modalonly = true;
|
||||
|
||||
add_flow_and_stream_security_conf(sub_section, tab_name, "protocol", "vless", true, false);
|
||||
}
|
||||
|
||||
function vless_server(protocol, section, tab_name) {
|
||||
protocol.value("vless", "VLESS");
|
||||
add_flow_and_stream_security_conf(section, tab_name, "web_server_protocol", "vless", true, true);
|
||||
}
|
||||
|
||||
function trojan_client(protocol, sub_section, tab_name) {
|
||||
protocol.value("trojan", "Trojan");
|
||||
add_flow_and_stream_security_conf(sub_section, tab_name, "protocol", "trojan", false, false);
|
||||
}
|
||||
|
||||
function trojan_server(protocol, section, tab_name) {
|
||||
protocol.value("trojan", "Trojan");
|
||||
add_flow_and_stream_security_conf(section, tab_name, "web_server_protocol", "trojan", false, true);
|
||||
}
|
||||
|
||||
return baseclass.extend({
|
||||
add_client_protocol: function (protocol, sub_section, tab_name) {
|
||||
vmess_client(protocol, sub_section, tab_name);
|
||||
vless_client(protocol, sub_section, tab_name);
|
||||
trojan_client(protocol, sub_section, tab_name);
|
||||
shadowsocks_client(protocol, sub_section, tab_name);
|
||||
},
|
||||
add_server_protocol: function (protocol, section, tab_name) {
|
||||
vless_server(protocol, section, tab_name);
|
||||
trojan_server(protocol, section, tab_name);
|
||||
},
|
||||
});
|
|
@ -0,0 +1,35 @@
|
|||
'use strict';
|
||||
'require baseclass';
|
||||
'require uci';
|
||||
|
||||
const variant = "xray_core";
|
||||
|
||||
function badge(text, tooltip) {
|
||||
let options = { 'class': 'ifacebadge' };
|
||||
if (tooltip) {
|
||||
options["data-tooltip"] = tooltip;
|
||||
}
|
||||
return E('span', options, text);
|
||||
}
|
||||
|
||||
return baseclass.extend({
|
||||
badge: badge,
|
||||
validate_object: function (id, a) {
|
||||
if (a == "") {
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
const t = JSON.parse(a);
|
||||
if (Array.isArray(t)) {
|
||||
return "TypeError: Requires an object here, got an array";
|
||||
}
|
||||
if (t instanceof Object) {
|
||||
return true;
|
||||
}
|
||||
return "TypeError: Requires an object here, got a " + typeof t;
|
||||
} catch (e) {
|
||||
return e;
|
||||
}
|
||||
},
|
||||
variant: variant
|
||||
});
|
|
@ -0,0 +1,196 @@
|
|||
'use strict';
|
||||
'require baseclass';
|
||||
'require form';
|
||||
|
||||
function transport_tcp(transport, sub_section, tab_name) {
|
||||
transport.value("tcp", "TCP");
|
||||
|
||||
let tcp_guise = sub_section.taboption(tab_name, form.ListValue, "tcp_guise", _("[tcp] Fake Header Type"));
|
||||
tcp_guise.depends("transport", "tcp");
|
||||
tcp_guise.value("none", _("None"));
|
||||
tcp_guise.value("http", "HTTP");
|
||||
tcp_guise.modalonly = true;
|
||||
|
||||
let http_host = sub_section.taboption(tab_name, form.DynamicList, "http_host", _("[tcp][fake_http] Host"));
|
||||
http_host.depends("tcp_guise", "http");
|
||||
http_host.rmempty = false;
|
||||
http_host.modalonly = true;
|
||||
|
||||
let http_path = sub_section.taboption(tab_name, form.DynamicList, "http_path", _("[tcp][fake_http] Path"));
|
||||
http_path.depends("tcp_guise", "http");
|
||||
http_path.modalonly = true;
|
||||
}
|
||||
|
||||
function transport_mkcp(transport, sub_section, tab_name) {
|
||||
transport.value("mkcp", "mKCP");
|
||||
|
||||
let mkcp_guise = sub_section.taboption(tab_name, form.ListValue, "mkcp_guise", _("[mkcp] Fake Header Type"));
|
||||
mkcp_guise.depends("transport", "mkcp");
|
||||
mkcp_guise.value("none", _("None"));
|
||||
mkcp_guise.value("srtp", _("VideoCall (SRTP)"));
|
||||
mkcp_guise.value("utp", _("BitTorrent (uTP)"));
|
||||
mkcp_guise.value("wechat-video", _("WechatVideo"));
|
||||
mkcp_guise.value("dtls", "DTLS 1.2");
|
||||
mkcp_guise.value("wireguard", "WireGuard");
|
||||
mkcp_guise.modalonly = true;
|
||||
|
||||
let mkcp_mtu = sub_section.taboption(tab_name, form.Value, "mkcp_mtu", _("[mkcp] Maximum Transmission Unit"));
|
||||
mkcp_mtu.datatype = "uinteger";
|
||||
mkcp_mtu.depends("transport", "mkcp");
|
||||
mkcp_mtu.placeholder = 1350;
|
||||
mkcp_mtu.modalonly = true;
|
||||
|
||||
let mkcp_tti = sub_section.taboption(tab_name, form.Value, "mkcp_tti", _("[mkcp] Transmission Time Interval"));
|
||||
mkcp_tti.datatype = "uinteger";
|
||||
mkcp_tti.depends("transport", "mkcp");
|
||||
mkcp_tti.placeholder = 50;
|
||||
mkcp_tti.modalonly = true;
|
||||
|
||||
let mkcp_uplink_capacity = sub_section.taboption(tab_name, form.Value, "mkcp_uplink_capacity", _("[mkcp] Uplink Capacity"));
|
||||
mkcp_uplink_capacity.datatype = "uinteger";
|
||||
mkcp_uplink_capacity.depends("transport", "mkcp");
|
||||
mkcp_uplink_capacity.placeholder = 5;
|
||||
mkcp_uplink_capacity.modalonly = true;
|
||||
|
||||
let mkcp_downlink_capacity = sub_section.taboption(tab_name, form.Value, "mkcp_downlink_capacity", _("[mkcp] Downlink Capacity"));
|
||||
mkcp_downlink_capacity.datatype = "uinteger";
|
||||
mkcp_downlink_capacity.depends("transport", "mkcp");
|
||||
mkcp_downlink_capacity.placeholder = 20;
|
||||
mkcp_downlink_capacity.modalonly = true;
|
||||
|
||||
let mkcp_read_buffer_size = sub_section.taboption(tab_name, form.Value, "mkcp_read_buffer_size", _("[mkcp] Read Buffer Size"));
|
||||
mkcp_read_buffer_size.datatype = "uinteger";
|
||||
mkcp_read_buffer_size.depends("transport", "mkcp");
|
||||
mkcp_read_buffer_size.placeholder = 2;
|
||||
mkcp_read_buffer_size.modalonly = true;
|
||||
|
||||
let mkcp_write_buffer_size = sub_section.taboption(tab_name, form.Value, "mkcp_write_buffer_size", _("[mkcp] Write Buffer Size"));
|
||||
mkcp_write_buffer_size.datatype = "uinteger";
|
||||
mkcp_write_buffer_size.depends("transport", "mkcp");
|
||||
mkcp_write_buffer_size.placeholder = 2;
|
||||
mkcp_write_buffer_size.modalonly = true;
|
||||
|
||||
let mkcp_congestion = sub_section.taboption(tab_name, form.Flag, "mkcp_congestion", _("[mkcp] Congestion Control"));
|
||||
mkcp_congestion.depends("transport", "mkcp");
|
||||
mkcp_congestion.modalonly = true;
|
||||
|
||||
let mkcp_seed = sub_section.taboption(tab_name, form.Value, "mkcp_seed", _("[mkcp] Seed"));
|
||||
mkcp_seed.depends("transport", "mkcp");
|
||||
mkcp_seed.modalonly = true;
|
||||
}
|
||||
|
||||
function transport_ws(transport, sub_section, tab_name) {
|
||||
transport.value("ws", "WebSocket");
|
||||
|
||||
let ws_host = sub_section.taboption(tab_name, form.Value, "ws_host", _("[websocket] Host"));
|
||||
ws_host.depends("transport", "ws");
|
||||
ws_host.modalonly = true;
|
||||
|
||||
let ws_path = sub_section.taboption(tab_name, form.Value, "ws_path", _("[websocket] Path"));
|
||||
ws_path.depends("transport", "ws");
|
||||
ws_path.modalonly = true;
|
||||
}
|
||||
|
||||
function transport_h2(transport, sub_section, tab_name) {
|
||||
transport.value("h2", "HTTP/2");
|
||||
|
||||
let h2_host = sub_section.taboption(tab_name, form.DynamicList, "h2_host", _("[http2] Host"));
|
||||
h2_host.depends("transport", "h2");
|
||||
h2_host.modalonly = true;
|
||||
|
||||
let h2_path = sub_section.taboption(tab_name, form.Value, "h2_path", _("[http2] Path"));
|
||||
h2_path.depends("transport", "h2");
|
||||
h2_path.modalonly = true;
|
||||
|
||||
let h2_health_check = sub_section.taboption(tab_name, form.Flag, "h2_health_check", _("[h2] Health Check"));
|
||||
h2_health_check.depends("transport", "h2");
|
||||
h2_health_check.modalonly = true;
|
||||
|
||||
let h2_read_idle_timeout = sub_section.taboption(tab_name, form.Value, "h2_read_idle_timeout", _("[h2] Read Idle Timeout"));
|
||||
h2_read_idle_timeout.depends({ "transport": "h2", "h2_health_check": "1" });
|
||||
h2_read_idle_timeout.modalonly = true;
|
||||
h2_read_idle_timeout.placeholder = 10;
|
||||
h2_read_idle_timeout.datatype = 'integer';
|
||||
|
||||
let h2_health_check_timeout = sub_section.taboption(tab_name, form.Value, "h2_health_check_timeout", _("[h2] Health Check Timeout"));
|
||||
h2_health_check_timeout.depends({ "transport": "h2", "h2_health_check": "1" });
|
||||
h2_health_check_timeout.modalonly = true;
|
||||
h2_health_check_timeout.placeholder = 20;
|
||||
h2_health_check_timeout.datatype = 'integer';
|
||||
}
|
||||
|
||||
function transport_quic(transport, sub_section, tab_name) {
|
||||
transport.value("quic", "QUIC");
|
||||
|
||||
let quic_security = sub_section.taboption(tab_name, form.ListValue, "quic_security", _("[quic] Security"));
|
||||
quic_security.depends("transport", "quic");
|
||||
quic_security.value("none", "none");
|
||||
quic_security.value("aes-128-gcm", "aes-128-gcm");
|
||||
quic_security.value("chacha20-poly1305", "chacha20-poly1305");
|
||||
quic_security.rmempty = false;
|
||||
quic_security.modalonly = true;
|
||||
|
||||
let quic_key = sub_section.taboption(tab_name, form.Value, "quic_key", _("[quic] Key"));
|
||||
quic_key.depends("transport", "quic");
|
||||
quic_key.modalonly = true;
|
||||
|
||||
let quic_guise = sub_section.taboption(tab_name, form.ListValue, "quic_guise", _("[quic] Fake Header Type"));
|
||||
quic_guise.depends("transport", "quic");
|
||||
quic_guise.value("none", _("None"));
|
||||
quic_guise.value("srtp", _("VideoCall (SRTP)"));
|
||||
quic_guise.value("utp", _("BitTorrent (uTP)"));
|
||||
quic_guise.value("wechat-video", _("WechatVideo"));
|
||||
quic_guise.value("dtls", "DTLS 1.2");
|
||||
quic_guise.value("wireguard", "WireGuard");
|
||||
quic_guise.default = "none";
|
||||
quic_guise.modalonly = true;
|
||||
}
|
||||
|
||||
function transport_grpc(transport, sub_section, tab_name) {
|
||||
transport.value("grpc", "gRPC");
|
||||
|
||||
let grpc_service_name = sub_section.taboption(tab_name, form.Value, "grpc_service_name", _("[grpc] Service Name"));
|
||||
grpc_service_name.depends("transport", "grpc");
|
||||
grpc_service_name.modalonly = true;
|
||||
|
||||
let grpc_multi_mode = sub_section.taboption(tab_name, form.Flag, "grpc_multi_mode", _("[grpc] Multi Mode"));
|
||||
grpc_multi_mode.depends("transport", "grpc");
|
||||
grpc_multi_mode.modalonly = true;
|
||||
|
||||
let grpc_health_check = sub_section.taboption(tab_name, form.Flag, "grpc_health_check", _("[grpc] Health Check"));
|
||||
grpc_health_check.depends("transport", "grpc");
|
||||
grpc_health_check.modalonly = true;
|
||||
|
||||
let grpc_idle_timeout = sub_section.taboption(tab_name, form.Value, "grpc_idle_timeout", _("[grpc] Idle Timeout"));
|
||||
grpc_idle_timeout.depends({ "transport": "grpc", "grpc_health_check": "1" });
|
||||
grpc_idle_timeout.modalonly = true;
|
||||
grpc_idle_timeout.placeholder = 10;
|
||||
grpc_idle_timeout.datatype = 'integer';
|
||||
|
||||
let grpc_health_check_timeout = sub_section.taboption(tab_name, form.Value, "grpc_health_check_timeout", _("[grpc] Health Check Timeout"));
|
||||
grpc_health_check_timeout.depends({ "transport": "grpc", "grpc_health_check": "1" });
|
||||
grpc_health_check_timeout.modalonly = true;
|
||||
grpc_health_check_timeout.placeholder = 20;
|
||||
grpc_health_check_timeout.datatype = 'integer';
|
||||
|
||||
let grpc_permit_without_stream = sub_section.taboption(tab_name, form.Flag, "grpc_permit_without_stream", _("[grpc] Permit Without Stream"));
|
||||
grpc_permit_without_stream.depends({ "transport": "grpc", "grpc_health_check": "1" });
|
||||
grpc_permit_without_stream.modalonly = true;
|
||||
|
||||
let grpc_initial_windows_size = sub_section.taboption(tab_name, form.Value, "grpc_initial_windows_size", _("[grpc] Initial Windows Size"), _("Set to 524288 to avoid Cloudflare sending ENHANCE_YOUR_CALM."));
|
||||
grpc_initial_windows_size.depends("transport", "grpc");
|
||||
grpc_initial_windows_size.modalonly = true;
|
||||
grpc_initial_windows_size.placeholder = 0;
|
||||
grpc_initial_windows_size.datatype = 'integer';
|
||||
}
|
||||
|
||||
return baseclass.extend({
|
||||
init: function (transport, sub_section, tab_name) {
|
||||
transport_tcp(transport, sub_section, tab_name);
|
||||
transport_mkcp(transport, sub_section, tab_name);
|
||||
transport_ws(transport, sub_section, tab_name);
|
||||
transport_h2(transport, sub_section, tab_name);
|
||||
transport_quic(transport, sub_section, tab_name);
|
||||
transport_grpc(transport, sub_section, tab_name);
|
||||
}
|
||||
});
|
|
@ -5,8 +5,7 @@
|
|||
'require uci';
|
||||
'require ui';
|
||||
'require view';
|
||||
|
||||
const variant = "xray_core";
|
||||
'require view.xray.shared as shared';
|
||||
|
||||
function greater_than_zero(n) {
|
||||
if (n < 0) {
|
||||
|
@ -19,46 +18,46 @@ function get_inbound_uci_description(config, key) {
|
|||
const ks = key.split(":");
|
||||
switch (ks[0]) {
|
||||
case "https_inbound": {
|
||||
return E([], [key, " ", E('span', { 'class': 'ifacebadge' }, `{ listen: <strong>https://0.0.0.0:443</strong> }`)]);
|
||||
return E([], [key, " ", shared.badge(`{ listen: <strong>https://0.0.0.0:443</strong> }`)]);
|
||||
}
|
||||
case "http_inbound": {
|
||||
return E([], [key, " ", E('span', { 'class': 'ifacebadge' }, `{ listen: <strong>http://0.0.0.0:${uci.get_first(config, "general", "http_port") || 1081}</strong> }`)]);
|
||||
return E([], [key, " ", shared.badge(`{ listen: <strong>http://0.0.0.0:${uci.get_first(config, "general", "http_port") || 1081}</strong> }`)]);
|
||||
}
|
||||
case "socks_inbound": {
|
||||
return E([], [key, " ", E('span', { 'class': 'ifacebadge' }, `{ listen: <strong>socks5://0.0.0.0:${uci.get_first(config, "general", "socks_port") || 1080}</strong> }`)]);
|
||||
return E([], [key, " ", shared.badge(`{ listen: <strong>socks5://0.0.0.0:${uci.get_first(config, "general", "socks_port") || 1080}</strong> }`)]);
|
||||
}
|
||||
case "tproxy_tcp_inbound_v4": {
|
||||
return E([], [key, " ", E('span', { 'class': 'ifacebadge' }, `{ listen: <strong>tproxy_tcp://0.0.0.0:${uci.get_first(config, "general", "tproxy_port_tcp_v4") || 1082}</strong> }`)]);
|
||||
return E([], [key, " ", shared.badge(`{ listen: <strong>tproxy_tcp://0.0.0.0:${uci.get_first(config, "general", "tproxy_port_tcp_v4") || 1082}</strong> }`)]);
|
||||
}
|
||||
case "tproxy_udp_inbound_v4": {
|
||||
return E([], [key, " ", E('span', { 'class': 'ifacebadge' }, `{ listen: <strong>tproxy_udp://0.0.0.0:${uci.get_first(config, "general", "tproxy_port_udp_v4") || 1084}</strong> }`)]);
|
||||
return E([], [key, " ", shared.badge(`{ listen: <strong>tproxy_udp://0.0.0.0:${uci.get_first(config, "general", "tproxy_port_udp_v4") || 1084}</strong> }`)]);
|
||||
}
|
||||
case "tproxy_tcp_inbound_v6": {
|
||||
return E([], [key, " ", E('span', { 'class': 'ifacebadge' }, `{ listen: <strong>tproxy_tcp://[::]:${uci.get_first(config, "general", "tproxy_port_tcp_v6") || 1083}</strong> }`)]);
|
||||
return E([], [key, " ", shared.badge(`{ listen: <strong>tproxy_tcp://[::]:${uci.get_first(config, "general", "tproxy_port_tcp_v6") || 1083}</strong> }`)]);
|
||||
}
|
||||
case "tproxy_udp_inbound_v6": {
|
||||
return E([], [key, " ", E('span', { 'class': 'ifacebadge' }, `{ listen: <strong>tproxy_udp://[::]:${uci.get_first(config, "general", "tproxy_port_udp_v6") || 1085}</strong> }`)]);
|
||||
return E([], [key, " ", shared.badge(`{ listen: <strong>tproxy_udp://[::]:${uci.get_first(config, "general", "tproxy_port_udp_v6") || 1085}</strong> }`)]);
|
||||
}
|
||||
case "tproxy_tcp_inbound_f4": {
|
||||
return E([], [key, " ", E('span', { 'class': 'ifacebadge' }, `{ listen: <strong>tproxy_tcp://0.0.0.0:${uci.get_first(config, "general", "tproxy_port_tcp_f4") || 1086}</strong> }`)]);
|
||||
return E([], [key, " ", shared.badge(`{ listen: <strong>tproxy_tcp://0.0.0.0:${uci.get_first(config, "general", "tproxy_port_tcp_f4") || 1086}</strong> }`)]);
|
||||
}
|
||||
case "tproxy_udp_inbound_f4": {
|
||||
return E([], [key, " ", E('span', { 'class': 'ifacebadge' }, `{ listen: <strong>tproxy_udp://0.0.0.0:${uci.get_first(config, "general", "tproxy_port_udp_f4") || 1088}</strong> }`)]);
|
||||
return E([], [key, " ", shared.badge(`{ listen: <strong>tproxy_udp://0.0.0.0:${uci.get_first(config, "general", "tproxy_port_udp_f4") || 1088}</strong> }`)]);
|
||||
}
|
||||
case "tproxy_tcp_inbound_f6": {
|
||||
return E([], [key, " ", E('span', { 'class': 'ifacebadge' }, `{ listen: <strong>tproxy_tcp://[::]:${uci.get_first(config, "general", "tproxy_port_tcp_f6") || 1087}</strong> }`)]);
|
||||
return E([], [key, " ", shared.badge(`{ listen: <strong>tproxy_tcp://[::]:${uci.get_first(config, "general", "tproxy_port_tcp_f6") || 1087}</strong> }`)]);
|
||||
}
|
||||
case "tproxy_udp_inbound_f6": {
|
||||
return E([], [key, " ", E('span', { 'class': 'ifacebadge' }, `{ listen: <strong>tproxy_udp://[::]:${uci.get_first(config, "general", "tproxy_port_udp_f6") || 1089}</strong> }`)]);
|
||||
return E([], [key, " ", shared.badge(`{ listen: <strong>tproxy_udp://[::]:${uci.get_first(config, "general", "tproxy_port_udp_f6") || 1089}</strong> }`)]);
|
||||
}
|
||||
case "metrics": {
|
||||
return E([], [key, " ", E('span', { 'class': 'ifacebadge' }, `{ listen: <strong>http://0.0.0.0:${uci.get_first(config, "general", "metrics_server_port") || 18888}</strong> }`)]);
|
||||
return E([], [key, " ", shared.badge(`{ listen: <strong>http://0.0.0.0:${uci.get_first(config, "general", "metrics_server_port") || 18888}</strong> }`)]);
|
||||
}
|
||||
case "api": {
|
||||
return E([], [key, " ", E('span', { 'class': 'ifacebadge' }, `{ listen: <strong>grpc://127.0.0.1:8080</strong> }`)]);
|
||||
return E([], [key, " ", shared.badge(`{ listen: <strong>grpc://127.0.0.1:8080</strong> }`)]);
|
||||
}
|
||||
case "dns_server_inbound": {
|
||||
return E([], [key, " ", E('span', { 'class': 'ifacebadge' }, `{ listen: <strong>dns://0.0.0.0:${ks[1]}</strong> }`)]);
|
||||
return E([], [key, " ", shared.badge(`{ listen: <strong>dns://0.0.0.0:${ks[1]}</strong> }`)]);
|
||||
}
|
||||
}
|
||||
const uci_key = key.slice(-9);
|
||||
|
@ -68,7 +67,7 @@ function get_inbound_uci_description(config, key) {
|
|||
}
|
||||
switch (uci_item[".type"]) {
|
||||
case "extra_inbound": {
|
||||
return E([], [key, " ", E('span', { 'class': 'ifacebadge' }, `{ listen: <strong>${uci_item["inbound_type"]}://${uci_item["inbound_addr"]}:${uci_item["inbound_port"]}</strong> }`)]);
|
||||
return E([], [key, " ", shared.badge(`{ listen: <strong>${uci_item["inbound_type"]}://${uci_item["inbound_addr"]}:${uci_item["inbound_port"]}</strong> }`)]);
|
||||
}
|
||||
}
|
||||
return key;
|
||||
|
@ -122,57 +121,37 @@ function outbound_first_tag_format(tag_split, first_uci_description) {
|
|||
switch (first_tag[0]) {
|
||||
case "extra_inbound": {
|
||||
if (tag_split.length < 3) {
|
||||
result.push(" ", E('span', {
|
||||
'class': 'ifacebadge',
|
||||
'data-tooltip': `${first_uci_description}`,
|
||||
}, `{ listen: <strong>${first_uci_description}</strong> }`));
|
||||
result.push(" ", shared.badge(`{ listen: <strong>${first_uci_description}</strong> }`));
|
||||
} else {
|
||||
result.push(" ", E('span', {
|
||||
'class': 'ifacebadge',
|
||||
'data-tooltip': `${first_uci_description}`,
|
||||
}, `{ listen <strong>...</strong> }`));
|
||||
result.push(" ", shared.badge(`{ listen <strong>...</strong> }`, first_uci_description));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "force_forward": {
|
||||
result.push(" ", E('span', {
|
||||
'class': 'ifacebadge',
|
||||
'data-tooltip': `${first_uci_description}`,
|
||||
}, `{ force_forward <strong>...</strong> }`));
|
||||
result.push(" ", shared.badge(`{ force_forward <strong>...</strong> }`, first_uci_description));
|
||||
break;
|
||||
}
|
||||
case "balancer_outbound": {
|
||||
if (tag_split.length < 4) {
|
||||
result.push(" ", E('span', {
|
||||
'class': 'ifacebadge',
|
||||
'data-tooltip': `${first_uci_description}`,
|
||||
}, `{ balancer_outbound <strong>...</strong> }`));
|
||||
result.push(" ", shared.badge(`{ balancer_outbound <strong>...</strong> }`, first_uci_description));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "fake_dns_tcp":
|
||||
case "fake_dns_udp": {
|
||||
result.push(" ", E('span', {
|
||||
'class': 'ifacebadge',
|
||||
'data-tooltip': `${first_uci_description}`,
|
||||
}, `{ fake_dns <strong>...</strong> }`));
|
||||
result.push(" ", shared.badge(`{ fake_dns <strong>...</strong> }`, first_uci_description));
|
||||
break;
|
||||
}
|
||||
case "manual_tproxy": {
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
result.push(" ", E('span', {
|
||||
'class': 'ifacebadge',
|
||||
'data-tooltip': `${first_uci_description}`,
|
||||
}, `{ <strong>...</strong> }`));
|
||||
result.push(" ", shared.badge(`{ <strong>...</strong> }`, first_uci_description));
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
result.push(" ", E('span', {
|
||||
'class': 'ifacebadge',
|
||||
'data-tooltip': first_tag[0],
|
||||
}, `{ <strong>${first_uci_description}</strong> }`));
|
||||
result.push(" ", shared.badge(`{ <strong>${first_uci_description}</strong> }`, first_tag[0]));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -181,79 +160,45 @@ function outbound_middle_tag_format(tag_split, first_uci_description, current_ta
|
|||
switch (current_tag[0]) {
|
||||
case "extra_inbound": {
|
||||
if (tag_split.length < 3) {
|
||||
return E('span', {
|
||||
'class': 'ifacebadge',
|
||||
'data-tooltip': `${current_tag[0]}: ${current_uci_description} (${current_tag[1]})`,
|
||||
}, `{ listen: <strong>${current_uci_description}</strong> }`);
|
||||
return shared.badge(`{ listen: <strong>${current_uci_description}</strong> }`, `${current_tag[0]}: ${current_uci_description} (${current_tag[1]})`);
|
||||
}
|
||||
return E('span', {
|
||||
'class': 'ifacebadge',
|
||||
'data-tooltip': `${current_tag[0]}: ${current_uci_description} (${current_tag[1]})`,
|
||||
}, `{ listen <strong>...</strong> }`);
|
||||
return shared.badge(`{ listen <strong>...</strong> }`, `${current_tag[0]}: ${current_uci_description} (${current_tag[1]})`);
|
||||
}
|
||||
case "force_forward": {
|
||||
return E('span', {
|
||||
'class': 'ifacebadge',
|
||||
'data-tooltip': `${current_tag[0]}: ${current_uci_description} (${current_tag[1]})`,
|
||||
}, `{ force_forward <strong>...</strong> }`);
|
||||
return shared.badge(`{ force_forward <strong>...</strong> }`, `${current_tag[0]}: ${current_uci_description} (${current_tag[1]})`);
|
||||
}
|
||||
case "balancer_outbound": {
|
||||
if (tag_split.length < 4) {
|
||||
return E('span', {
|
||||
'class': 'ifacebadge',
|
||||
'data-tooltip': `${current_tag[0]}: ${current_uci_description} (${current_tag[1]})`,
|
||||
}, `{ balancer_outbound <strong>...</strong> }`);
|
||||
return shared.badge(`{ balancer_outbound <strong>...</strong> }`, `${current_tag[0]}: ${current_uci_description} (${current_tag[1]})`);
|
||||
}
|
||||
}
|
||||
case "tcp_outbound": {
|
||||
if (tag_split.length < 4) {
|
||||
return E('span', {
|
||||
'class': 'ifacebadge',
|
||||
'data-tooltip': `${current_tag[0]}`,
|
||||
}, `{ tcp: <strong>${first_uci_description}</strong> }`);
|
||||
return shared.badge(`{ tcp: <strong>${first_uci_description}</strong> }`, current_tag[0]);
|
||||
}
|
||||
return E('span', {
|
||||
'class': 'ifacebadge',
|
||||
'data-tooltip': `tcp: ${first_uci_description}`,
|
||||
}, `{ tcp <strong>...</strong> }`);
|
||||
return shared.badge(`{ tcp <strong>...</strong> }`, `tcp: ${first_uci_description}`);
|
||||
}
|
||||
case "udp_outbound": {
|
||||
if (tag_split.length < 4) {
|
||||
return E('span', {
|
||||
'class': 'ifacebadge',
|
||||
'data-tooltip': `${current_tag[0]}`,
|
||||
}, `{ udp: <strong>${first_uci_description}</strong> }`);
|
||||
return shared.badge(`{ udp: <strong>${first_uci_description}</strong> }`, current_tag[0]);
|
||||
}
|
||||
return E('span', {
|
||||
'class': 'ifacebadge',
|
||||
'data-tooltip': `udp: ${first_uci_description}`,
|
||||
}, `{ udp <strong>...</strong> }`);
|
||||
return shared.badge(`{ udp <strong>...</strong> }`, `udp: ${first_uci_description}`);
|
||||
}
|
||||
case "fake_dns_tcp":
|
||||
case "fake_dns_udp": {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return E('span', {
|
||||
'class': 'ifacebadge',
|
||||
'data-tooltip': `${current_uci_description}`,
|
||||
}, `{ <strong>...</strong> }`);
|
||||
return shared.badge(`{ <strong>...</strong> }`, current_uci_description);
|
||||
}
|
||||
|
||||
function outbound_last_tag_format(first_uci_description, last_tag, last_uci_description) {
|
||||
if (last_tag[0] == "tcp_outbound") {
|
||||
return E('span', {
|
||||
'class': 'ifacebadge',
|
||||
}, `{ tcp: <strong>${first_uci_description}</strong> }`);
|
||||
return shared.badge(`{ tcp: <strong>${first_uci_description}</strong> }`);
|
||||
} else if (last_tag[0] == "udp_outbound") {
|
||||
return E('span', {
|
||||
'class': 'ifacebadge',
|
||||
}, `{ udp: <strong>${first_uci_description}</strong> }`);
|
||||
return shared.badge(`{ udp: <strong>${first_uci_description}</strong> }`);
|
||||
}
|
||||
return E('span', {
|
||||
'class': 'ifacebadge',
|
||||
'data-tooltip': `${last_tag[1]}`,
|
||||
}, `{ ${last_tag[0]}: <strong>${last_uci_description}</strong> }`);
|
||||
return shared.badge(`{ ${last_tag[0]}: <strong>${last_uci_description}</strong> }`, last_tag[1]);
|
||||
}
|
||||
|
||||
function get_outbound_description(config, tag) {
|
||||
|
@ -352,7 +297,7 @@ function inbound_stats(vars, config) {
|
|||
|
||||
return view.extend({
|
||||
load: function () {
|
||||
return uci.load(variant);
|
||||
return uci.load(shared.variant);
|
||||
},
|
||||
|
||||
render: function (config) {
|
||||
|
|
|
@ -10,11 +10,11 @@ include $(TOPDIR)/rules.mk
|
|||
PKG_ARCH_quickstart:=$(ARCH)
|
||||
|
||||
PKG_NAME:=quickstart
|
||||
PKG_VERSION:=0.8.0
|
||||
PKG_VERSION:=0.8.1
|
||||
PKG_RELEASE:=1
|
||||
PKG_SOURCE:=$(PKG_NAME)-binary-$(PKG_VERSION).tar.gz
|
||||
PKG_SOURCE_URL:=https://github.com/linkease/istore-packages/releases/download/prebuilt/
|
||||
PKG_HASH:=13febad75727047e053be38cdf18e4196e3117c7fafb887f2f006191daac104c
|
||||
PKG_HASH:=c18d33b5ffb4b3d4175a98d93751fd7bc69c362ca17342085726e6c802db9e70
|
||||
|
||||
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-binary-$(PKG_VERSION)
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ USE_PROCD=1
|
|||
|
||||
start_service() {
|
||||
procd_open_instance
|
||||
procd_set_param command /usr/sbin/quickstart serve
|
||||
procd_set_param command /usr/sbin/quickstart serve --unix /var/run/quickstart/local.sock
|
||||
procd_set_param stderr 1
|
||||
procd_set_param respawn
|
||||
procd_close_instance
|
||||
|
|
Loading…
Reference in New Issue