0
0
mirror of https://github.com/openwrt/luci.git synced 2025-02-12 03:28:11 +00:00
Stan Grishin f41478a9b8 luci-app-pbr: update to 1.1.8
* match version with principal package
* update README version
* use var instead of const for RPCD objects

Signed-off-by: Stan Grishin <stangri@melmac.ca>
2024-12-18 05:18:49 +00:00

636 lines
17 KiB
JavaScript

// Copyright 2022 Stan Grishin <stangri@melmac.ca>
// This code wouldn't have been possible without help from [@vsviridov](https://github.com/vsviridov)
"require ui";
"require rpc";
"require form";
"require baseclass";
var pkg = {
get Name() {
return "pbr";
},
get ReadmeCompat() {
return "1.1.8";
},
get URL() {
return (
"https://docs.openwrt.melmac.net/" +
pkg.Name +
"/" +
(pkg.ReadmeCompat ? pkg.ReadmeCompat + "/" : "")
);
},
get DonateURL() {
return (
"https://docs.openwrt.melmac.net/" +
pkg.Name +
"/" +
(pkg.ReadmeCompat ? pkg.ReadmeCompat + "/" : "") +
"#Donate"
);
},
};
var getGateways = rpc.declare({
object: "luci." + pkg.Name,
method: "getGateways",
params: ["name"],
});
var getInitList = rpc.declare({
object: "luci." + pkg.Name,
method: "getInitList",
params: ["name"],
});
var getInitStatus = rpc.declare({
object: "luci." + pkg.Name,
method: "getInitStatus",
params: ["name"],
});
var getInterfaces = rpc.declare({
object: "luci." + pkg.Name,
method: "getInterfaces",
params: ["name"],
});
var getPlatformSupport = rpc.declare({
object: "luci." + pkg.Name,
method: "getPlatformSupport",
params: ["name"],
});
var _setInitAction = rpc.declare({
object: "luci." + pkg.Name,
method: "setInitAction",
params: ["name", "action"],
expect: { result: false },
});
var RPC = {
listeners: [],
on: function (event, callback) {
var pair = { event: event, callback: callback };
this.listeners.push(pair);
return function unsubscribe() {
this.listeners = this.listeners.filter(function (listener) {
return listener !== pair;
});
}.bind(this);
},
emit: function (event, data) {
this.listeners.forEach(function (listener) {
if (listener.event === event) {
listener.callback(data);
}
});
},
getInitList: function (name) {
getInitList(name).then(
function (result) {
this.emit("getInitList", result);
}.bind(this)
);
},
getInitStatus: function (name) {
getInitStatus(name).then(
function (result) {
this.emit("getInitStatus", result);
}.bind(this)
);
},
getGateways: function (name) {
getGateways(name).then(
function (result) {
this.emit("getGateways", result);
}.bind(this)
);
},
getPlatformSupport: function (name) {
getPlatformSupport(name).then(
function (result) {
this.emit("getPlatformSupport", result);
}.bind(this)
);
},
getInterfaces: function (name) {
getInterfaces(name).then(
function (result) {
this.emit("getInterfaces", result);
}.bind(this)
);
},
setInitAction: function (name, action) {
_setInitAction(name, action).then(
function (result) {
this.emit("setInitAction", result);
}.bind(this)
);
},
};
var status = baseclass.extend({
render: function () {
return Promise.all([
L.resolveDefault(getInitStatus(pkg.Name), {}),
// L.resolveDefault(getGateways(pkg.Name), {}),
]).then(function (data) {
// var replyStatus = data[0];
// var replyGateways = data[1];
var reply;
var text;
if (data[0] && data[0][pkg.Name]) {
reply = data[0][pkg.Name];
} else {
reply = {
enabled: null,
running: null,
running_iptables: null,
running_nft: null,
running_nft_file: null,
version: null,
gateways: null,
errors: [],
warnings: [],
};
}
var header = E("h2", {}, _("Policy Based Routing - Status"));
var statusTitle = E(
"label",
{ class: "cbi-value-title" },
_("Service Status")
);
if (reply.version) {
text = _("Version %s").format(reply.version) + " - ";
if (reply.running) {
text += _("Running");
if (reply.running_iptables) {
text += " (" + _("iptables mode") + ").";
} else if (reply.running_nft_file) {
text += " (" + _("fw4 nft file mode") + ").";
} else if (reply.running_nft) {
text += " (" + _("nft mode") + ").";
} else {
text += ".";
}
} else {
if (reply.enabled) {
text += _("Stopped.");
} else {
text += _("Stopped (Disabled).");
}
}
} else {
text = _("Not installed or not found");
}
var statusText = E("div", {}, text);
var statusField = E("div", { class: "cbi-value-field" }, statusText);
var statusDiv = E("div", { class: "cbi-value" }, [
statusTitle,
statusField,
]);
var gatewaysDiv = [];
if (reply.gateways) {
var gatewaysTitle = E(
"label",
{ class: "cbi-value-title" },
_("Service Gateways")
);
text =
_(
"The %s indicates default gateway. See the %sREADME%s for details."
).format(
"<strong>✓</strong>",
'<a href="' +
pkg.URL +
'#AWordAboutDefaultRouting" target="_blank">',
"</a>"
) +
"<br />" +
_("Please %sdonate%s to support development of this project.").format(
"<a href='" + pkg.DonateURL + "' target='_blank'>",
"</a>"
);
var gatewaysDescr = E("div", { class: "cbi-value-description" }, text);
var gatewaysText = E("div", {}, reply.gateways);
var gatewaysField = E("div", { class: "cbi-value-field" }, [
gatewaysText,
gatewaysDescr,
]);
gatewaysDiv = E("div", { class: "cbi-value" }, [
gatewaysTitle,
gatewaysField,
]);
}
var warningsDiv = [];
if (reply.warnings && reply.warnings.length) {
var textLabelsTable = {
warningResolverNotSupported: _(
"Resolver set (%s) is not supported on this system."
).format(L.uci.get(pkg.Name, "config", "resolver_set")),
warningAGHVersionTooLow: _(
"Installed AdGuardHome (%s) doesn't support 'ipset_file' option."
),
warningPolicyProcessCMD: _("%s"),
warningTorUnsetParams: _(
"Please unset 'src_addr', 'src_port' and 'dest_port' for policy '%s'"
),
warningTorUnsetProto: _(
"Please unset 'proto' or set 'proto' to 'all' for policy '%s'"
),
warningTorUnsetChainIpt: _(
"Please unset 'chain' or set 'chain' to 'PREROUTING' for policy '%s'"
),
warningTorUnsetChainNft: _(
"Please unset 'chain' or set 'chain' to 'prerouting' for policy '%s'"
),
warningInvalidOVPNConfig: _(
"Invalid OpenVPN config for %s interface"
),
warningOutdatedLuciPackage: _(
"The WebUI application (luci-app-pbr) is outdated, please update it"
),
warningOutdatedPrincipalPackage: _(
"The principal package (pbr) is outdated, please update it"
),
warningBadNftCallsInUserFile: _(
"Incompatible nft calls detected in user include file, disabling fw4 nft file support"
),
warningDnsmasqInstanceNoConfdir: _(
"Dnsmasq instance (%s) targeted in settings, but it doesn't have its own confdir"
),
warningDhcpLanForce: _(
_(
"Please set 'dhcp.%%s.force=1' to speed up service start-up %s(more info)%s"
).format(
"<a href='" +
pkg.URL +
"#Warning:Pleasesetdhcp.lan.force1" +
"' target='_blank'>",
"</a>"
)
),
};
var warningsTitle = E(
"label",
{ class: "cbi-value-title" },
_("Service Warnings")
);
var text = "";
reply.warnings.forEach((element) => {
if (element.id && textLabelsTable[element.id]) {
if (element.id !== "warningPolicyProcessCMD") {
text +=
(textLabelsTable[element.id] + ".").format(
element.extra || " "
) + "<br />";
}
} else {
text += _("Unknown warning") + "<br />";
}
});
var warningsText = E("div", { class: "cbi-value-description" }, text);
var warningsField = E(
"div",
{ class: "cbi-value-field" },
warningsText
);
warningsDiv = E("div", { class: "cbi-value" }, [
warningsTitle,
warningsField,
]);
}
var errorsDiv = [];
if (reply.errors && reply.errors.length) {
var textLabelsTable = {
errorConfigValidation: _("Config (%s) validation failure").format(
"/etc/config/" + pkg.Name
),
errorNoIptables: _("%s binary cannot be found").format("iptables"),
errorNoIpset: _(
"Resolver set support (%s) requires ipset, but ipset binary cannot be found"
).format(L.uci.get(pkg.Name, "config", "resolver_set")),
errorNoNft: _(
"Resolver set support (%s) requires nftables, but nft binary cannot be found"
).format(L.uci.get(pkg.Name, "config", "resolver_set")),
errorResolverNotSupported: _(
"Resolver set (%s) is not supported on this system"
).format(L.uci.get(pkg.Name, "config", "resolver_set")),
errorServiceDisabled: _(
"The %s service is currently disabled"
).format(pkg.Name),
errorNoWanGateway: _(
"The %s service failed to discover WAN gateway"
).format(pkg.Name),
errorNoWanInterface: _(
"The %s interface not found, you need to set the 'pbr.config.procd_wan_interface' option"
),
errorNoWanInterfaceHint: _(
"Refer to https://docs.openwrt.melmac.net/pbr/#procd_wan_interface"
),
errorIpsetNameTooLong: _(
"The ipset name '%s' is longer than allowed 31 characters"
),
errorNftsetNameTooLong: _(
"The nft set name '%s' is longer than allowed 255 characters"
),
errorUnexpectedExit: _(
"Unexpected exit or service termination: '%s'"
),
errorPolicyNoSrcDest: _(
"Policy '%s' has no source/destination parameters"
),
errorPolicyNoInterface: _("Policy '%s' has no assigned interface"),
errorPolicyNoDns: _("Policy '%s' has no assigned DNS"),
errorPolicyProcessNoInterfaceDns: _(
"Interface '%s' has no assigned DNS"
),
errorPolicyUnknownInterface: _(
"Policy '%s' has an unknown interface"
),
errorPolicyProcessCMD: _("%s"),
errorFailedSetup: _("Failed to set up '%s'"),
errorFailedReload: _("Failed to reload '%s'"),
errorUserFileNotFound: _("Custom user file '%s' not found or empty"),
errorUserFileSyntax: _("Syntax error in custom user file '%s'"),
errorUserFileRunning: _("Error running custom user file '%s'"),
errorUserFileNoCurl: _(
"Use of 'curl' is detected in custom user file '%s', but 'curl' isn't installed"
),
errorNoGateways: _("Failed to set up any gateway"),
errorResolver: _("Resolver '%s'"),
errorPolicyProcessNoIpv6: _(
"Skipping IPv6 policy '%s' as IPv6 support is disabled"
),
errorPolicyProcessUnknownFwmark: _(
"Unknown packet mark for interface '%s'"
),
errorPolicyProcessMismatchFamily: _(
"Mismatched IP family between in policy '%s'"
),
errorPolicyProcessUnknownProtocol: _(
"Unknown protocol in policy '%s'"
),
errorPolicyProcessInsertionFailed: _(
"Insertion failed for both IPv4 and IPv6 for policy '%s'"
),
errorPolicyProcessInsertionFailedIpv4: _(
"Insertion failed for IPv4 for policy '%s'"
),
errorInterfaceRoutingEmptyValues: _(
"Received empty tid/mark or interface name when setting up routing"
),
errorFailedToResolve: _("Failed to resolve '%s'"),
errorInvalidOVPNConfig: _(
"Invalid OpenVPN config for '%s' interface"
),
errorNftFileInstall: _("Failed to install fw4 nft file '%s'"),
errorNoDownloadWithSecureReload: _(
"Policy '%s' refers to URL which can't be downloaded in 'secure_reload' mode"
),
errorDownloadUrlNoHttps: _(
"Failed to download '%s', HTTPS is not supported"
),
errorDownloadUrl: _("Failed to download '%s'"),
errorFileSchemaRequiresCurl: _(
"The file:// schema requires curl, but it's not detected on this system"
),
errorTryFailed: _("Command failed: '%s'"),
errorIncompatibleUserFile: _(
"Incompatible custom user file detected '%s'"
),
errorDefaultFw4TableMissing: _("Default fw4 table '%s' is missing"),
errorDefaultFw4ChainMissing: _("Default fw4 chain '%s' is missing"),
errorRequiredBinaryMissing: _("Required binary '%s' is missing"),
errorInterfaceRoutingUnknownDevType: _(
"Unknown IPv6 Link type for device '%s'"
),
};
var errorsTitle = E(
"label",
{ class: "cbi-value-title" },
_("Service Errors")
);
var text = "";
reply.errors.forEach((element) => {
if (element.id && textLabelsTable[element.id]) {
if (element.id !== "errorPolicyProcessCMD") {
text +=
(textLabelsTable[element.id] + "!").format(
element.extra || " "
) + "<br />";
}
} else {
text += _("Unknown error!") + "<br />";
}
});
text += _("Errors encountered, please check the %sREADME%s").format(
'<a href="' + pkg.URL + '" target="_blank">',
"</a>!<br />"
);
var errorsText = E("div", { class: "cbi-value-description" }, text);
var errorsField = E("div", { class: "cbi-value-field" }, errorsText);
errorsDiv = E("div", { class: "cbi-value" }, [
errorsTitle,
errorsField,
]);
}
var btn_gap = E("span", {}, "&#160;&#160;");
var btn_gap_long = E(
"span",
{},
"&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;"
);
var btn_start = E(
"button",
{
class: "btn cbi-button cbi-button-apply",
disabled: true,
click: function (ev) {
ui.showModal(null, [
E(
"p",
{ class: "spinning" },
_("Starting %s service").format(pkg.Name)
),
]);
return RPC.setInitAction(pkg.Name, "start");
},
},
_("Start")
);
var btn_action = E(
"button",
{
class: "btn cbi-button cbi-button-apply",
disabled: true,
click: function (ev) {
ui.showModal(null, [
E(
"p",
{ class: "spinning" },
_("Restarting %s service").format(pkg.Name)
),
]);
return RPC.setInitAction(pkg.Name, "restart");
},
},
_("Restart")
);
var btn_stop = E(
"button",
{
class: "btn cbi-button cbi-button-reset",
disabled: true,
click: function (ev) {
ui.showModal(null, [
E(
"p",
{ class: "spinning" },
_("Stopping %s service").format(pkg.Name)
),
]);
return RPC.setInitAction(pkg.Name, "stop");
},
},
_("Stop")
);
var btn_enable = E(
"button",
{
class: "btn cbi-button cbi-button-apply",
disabled: true,
click: function (ev) {
ui.showModal(null, [
E(
"p",
{ class: "spinning" },
_("Enabling %s service").format(pkg.Name)
),
]);
return RPC.setInitAction(pkg.Name, "enable");
},
},
_("Enable")
);
var btn_disable = E(
"button",
{
class: "btn cbi-button cbi-button-reset",
disabled: true,
click: function (ev) {
ui.showModal(null, [
E(
"p",
{ class: "spinning" },
_("Disabling %s service").format(pkg.Name)
),
]);
return RPC.setInitAction(pkg.Name, "disable");
},
},
_("Disable")
);
if (reply.enabled) {
btn_enable.disabled = true;
btn_disable.disabled = false;
if (reply.running) {
btn_start.disabled = true;
btn_action.disabled = false;
btn_stop.disabled = false;
} else {
btn_start.disabled = false;
btn_action.disabled = true;
btn_stop.disabled = true;
}
} else {
btn_start.disabled = true;
btn_action.disabled = true;
btn_stop.disabled = true;
btn_enable.disabled = false;
btn_disable.disabled = true;
}
var buttonsTitle = E(
"label",
{ class: "cbi-value-title" },
_("Service Control")
);
var buttonsText = E("div", {}, [
btn_start,
btn_gap,
btn_action,
btn_gap,
btn_stop,
btn_gap_long,
btn_enable,
btn_gap,
btn_disable,
]);
var buttonsField = E("div", { class: "cbi-value-field" }, buttonsText);
var buttonsDiv = reply.version
? E("div", { class: "cbi-value" }, [buttonsTitle, buttonsField])
: "";
var donateTitle = E(
"label",
{ class: "cbi-value-title" },
_("Donate to the Project")
);
var donateText = E(
"div",
{ class: "cbi-value-field" },
E(
"div",
{ class: "cbi-value-description" },
_("Please %sdonate%s to support development of this project.").format(
"<a href='" + pkg.DonateURL + "' target='_blank'>",
"</a>"
)
)
);
var donateDiv = reply.version
? E("div", { class: "cbi-value" }, [donateTitle, donateText])
: "";
return E("div", {}, [
header,
statusDiv,
gatewaysDiv,
warningsDiv,
errorsDiv,
buttonsDiv,
// donateDiv,
]);
});
},
});
RPC.on("setInitAction", function (reply) {
ui.hideModal();
location.reload();
});
return L.Class.extend({
status: status,
pkg: pkg,
getInitStatus: getInitStatus,
getInterfaces: getInterfaces,
getPlatformSupport: getPlatformSupport,
});