123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635 |
- // 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.7-39";
- },
- 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"
- );
- },
- };
- const getGateways = rpc.declare({
- object: "luci." + pkg.Name,
- method: "getGateways",
- params: ["name"],
- });
- const getInitList = rpc.declare({
- object: "luci." + pkg.Name,
- method: "getInitList",
- params: ["name"],
- });
- const getInitStatus = rpc.declare({
- object: "luci." + pkg.Name,
- method: "getInitStatus",
- params: ["name"],
- });
- const getInterfaces = rpc.declare({
- object: "luci." + pkg.Name,
- method: "getInterfaces",
- params: ["name"],
- });
- const getPlatformSupport = rpc.declare({
- object: "luci." + pkg.Name,
- method: "getPlatformSupport",
- params: ["name"],
- });
- const _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", {}, "  ");
- var btn_gap_long = E(
- "span",
- {},
- "        "
- );
- 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,
- });
|