diff options
Diffstat (limited to 'applications/luci-app-https-dns-proxy/htdocs/luci-static/resources')
3 files changed, 1014 insertions, 0 deletions
diff --git a/applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/https-dns-proxy/status.js b/applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/https-dns-proxy/status.js new file mode 100644 index 0000000000..0ae128b923 --- /dev/null +++ b/applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/https-dns-proxy/status.js @@ -0,0 +1,434 @@ +// Copyright MOSSDeF, 2023 Stan Grishin <stangri@melmac.ca> +// This code wouldn't have been possible without help from: +// - [@stokito](https://github.com/stokito) +// - [@vsviridov](https://github.com/vsviridov) +// noinspection JSAnnotator + +"require ui"; +"require rpc"; +"require form"; +"require baseclass"; + +var pkg = { + get Name() { + return "https-dns-proxy"; + }, + get URL() { + return "https://docs.openwrt.melmac.net/" + pkg.Name + "/"; + }, + templateToRegexp: function (template) { + return RegExp( + "^" + + template + .split(/(\{\w+\})/g) + .map((part) => { + let placeholder = part.match(/^\{(\w+)\}$/); + if (placeholder) return `(?<${placeholder[1]}>.*?)`; + else return part.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); + }) + .join("") + + "$" + ); + }, +}; + +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 getPlatformSupport = rpc.declare({ + object: "luci." + pkg.Name, + method: "getPlatformSupport", + params: ["name"], +}); + +var getProviders = rpc.declare({ + object: "luci." + pkg.Name, + method: "getProviders", + params: ["name"], +}); + +var getRuntime = rpc.declare({ + object: "luci." + pkg.Name, + method: "getRuntime", + 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) + ); + }, + getPlatformSupport: function (name) { + getPlatformSupport(name).then( + function (result) { + this.emit("getPlatformSupport", result); + }.bind(this) + ); + }, + getProviders: function (name) { + getProviders(name).then( + function (result) { + this.emit("getProviders", result); + }.bind(this) + ); + }, + getRuntime: function (name) { + getRuntime(name).then( + function (result) { + this.emit("getRuntime", 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(getProviders(pkg.Name), {}), + L.resolveDefault(getRuntime(pkg.Name), {}), + ]).then(function (data) { + var text; + var reply = { + status: (data[0] && data[0][pkg.Name]) || { + enabled: null, + running: null, + force_dns_active: null, + version: null, + }, + providers: (data[1] && data[1][pkg.Name]) || { providers: [] }, + runtime: (data[2] && data[2][pkg.Name]) || { instances: [] }, + }; + reply.providers.sort(function (a, b) { + return _(a.title).localeCompare(_(b.title)); + }); + reply.providers.push({ + title: "Custom", + template: "{option}", + params: { option: { type: "text" } }, + }); + + var header = E("h2", {}, _("HTTPS DNS Proxy - Status")); + var statusTitle = E( + "label", + { class: "cbi-value-title" }, + _("Service Status") + ); + if (reply.status.version) { + if (reply.status.running) { + text = _("Version %s - Running.").format(reply.status.version); + if (reply.status.force_dns_active) { + text += "<br />" + _("Force DNS ports:"); + reply.status.force_dns_ports.forEach((element) => { + text += " " + element; + }); + text += "."; + } + } else { + if (reply.status.enabled) { + text = _("Version %s - Stopped.").format(reply.status.version); + } else { + text = _("Version %s - Stopped (Disabled).").format( + reply.status.version + ); + } + } + } 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 instancesDiv = []; + if (reply.runtime.instances) { + var instancesTitle = E( + "label", + { class: "cbi-value-title" }, + _("Service Instances") + ); + text = _("See the %sREADME%s for details.").format( + '<a href="' + + pkg.URL + + '#a-word-about-default-routing " target="_blank">', + "</a>" + ); + var instancesDescr = E("div", { class: "cbi-value-description" }, ""); + + text = ""; + Object.values(reply.runtime.instances).forEach((element) => { + var resolver; + var address; + var port; + var name; + var option; + var found; + element.command.forEach((param, index, arr) => { + if (param === "-r") resolver = arr[index + 1]; + if (param === "-a") address = arr[index + 1]; + if (param === "-p") port = arr[index + 1]; + }); + resolver = resolver || "Unknown"; + address = address || "127.0.0.1"; + port = port || "Unknown"; + reply.providers.forEach((prov) => { + let regexp = pkg.templateToRegexp(prov.template); + if (!found && regexp.test(resolver)) { + found = true; + name = _(prov.title); + let match = resolver.match(regexp); + if (match[1] != null) { + if ( + prov.params && + prov.params.option && + prov.params.option.options + ) { + prov.params.option.options.forEach((opt) => { + if (opt.value === match[1]) option = _(opt.description); + }); + name += " (" + option + ")"; + } else { + if (match[1] !== "") name += " (" + match[1] + ")"; + } + } + } + }); + if (address === "127.0.0.1") + text += _("%s%s%s proxy on port %s.%s").format( + "<strong>", + name, + "</strong>", + port, + "<br />" + ); + else + text += _("%s%s%s proxy at %s on port %s.%s").format( + "<strong>", + name, + "</strong>", + address, + port, + "<br />" + ); + }); + var instancesText = E("div", {}, text); + var instancesField = E("div", { class: "cbi-value-field" }, [ + instancesText, + instancesDescr, + ]); + instancesDiv = E("div", { class: "cbi-value" }, [ + instancesTitle, + instancesField, + ]); + } + + 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.status.enabled) { + btn_enable.disabled = true; + btn_disable.disabled = false; + if (reply.status.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.status.version + ? E("div", { class: "cbi-value" }, [buttonsTitle, buttonsField]) + : ""; + return E("div", {}, [header, statusDiv, instancesDiv, buttonsDiv]); + }); + }, +}); + +RPC.on("setInitAction", function (reply) { + ui.hideModal(); + location.reload(); +}); + +return L.Class.extend({ + status: status, + getPlatformSupport: getPlatformSupport, + getProviders: getProviders, + getRuntime: getRuntime, +}); diff --git a/applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js b/applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js new file mode 100644 index 0000000000..86cafd1030 --- /dev/null +++ b/applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/https-dns-proxy/overview.js @@ -0,0 +1,427 @@ +// Copyright 2023 MOSSDeF, Stan Grishin <stangri@melmac.ca> +// This code wouldn't have been possible without help from: +// - [@jow-](https://github.com/jow-) +// - [@stokito](https://github.com/stokito) +// - [@vsviridov](https://github.com/vsviridov) +// noinspection JSAnnotator + +"use strict"; +"require form"; +"require rpc"; +"require view"; +"require https-dns-proxy.status as hdp"; + +var pkg = { + get Name() { + return "https-dns-proxy"; + }, + + get URL() { + return "https://docs.openwrt.melmac.net/" + pkg.Name + "/"; + }, + + templateToRegexp: function (template) { + return RegExp( + "^" + + template + .split(/(\{\w+\})/g) + .map((part) => { + let placeholder = part.match(/^\{(\w+)\}$/); + if (placeholder) return `(?<${placeholder[1]}>.*?)`; + else return part.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); + }) + .join("") + + "$" + ); + }, + + templateToResolver: function (template, args) { + return template.replace(/{(\w+)}/g, (_, v) => args[v]); + }, +}; + +return view.extend({ + load: function () { + return Promise.all([ + L.resolveDefault(hdp.getPlatformSupport(pkg.Name), {}), + L.resolveDefault(hdp.getProviders(pkg.Name), {}), + L.resolveDefault(L.uci.load(pkg.Name), {}), + L.resolveDefault(L.uci.load("dhcp"), {}), + ]); + }, + + render: function (data) { + var reply = { + platform: (data[0] && data[0][pkg.Name]) || { + http2_support: null, + http3_support: null, + }, + providers: (data[1] && data[1][pkg.Name]) || { providers: [] }, + }; + reply.providers.sort(function (a, b) { + return _(a.title).localeCompare(_(b.title)); + }); + reply.providers.push({ + title: "Custom", + template: "{option}", + params: { option: { type: "text" } }, + }); + + var status, m, s, o, p; + var text; + + status = new hdp.status(); + + m = new form.Map(pkg.Name, _("HTTPS DNS Proxy - Configuration")); + + s = m.section(form.NamedSection, "config", pkg.Name); + + o = s.option( + form.ListValue, + "dnsmasq_config_update_option", + _("Update DNSMASQ Config on Start/Stop"), + _( + "If update option is selected, the %s'DNS Forwards' section of DHCP and DNS%s will be automatically updated to use selected DoH providers (%smore information%s)." + ).format( + '<a href="' + L.url("admin", "network", "dhcp") + '">', + "</a>", + '<a href="' + pkg.URL + "#default-settings" + '" target="_blank">', + "</a>" + ) + ); + o.value("*", _("Update all configs")); + o.value("+", _("Update select configs")); + o.value("-", _("Do not update configs")); + o.default = "*"; + o.retain = true; + o.cfgvalue = function (section_id) { + let val = this.map.data.get( + this.map.config, + section_id, + "dnsmasq_config_update" + ); + if (val && val[0]) { + switch (val[0]) { + case "*": + case "-": + return val[0]; + default: + return "+"; + } + } else return "*"; + }; + o.write = function (section_id, formvalue) { + L.uci.set(pkg.Name, section_id, "dnsmasq_config_update", formvalue); + }; + + o = s.option( + form.MultiValue, + "dnsmasq_config_update", + _("Select the DNSMASQ Configs to update") + ); + Object.values(L.uci.sections("dhcp", "dnsmasq")).forEach(function ( + element + ) { + var description; + var key; + if (element[".name"] === L.uci.resolveSID("dhcp", element[".name"])) { + key = element[".index"]; + description = "dnsmasq[" + element[".index"] + "]"; + } else { + key = element[".name"]; + description = element[".name"]; + } + o.value(key, description); + }); + o.depends("dnsmasq_config_update_option", "+"); + o.retain = true; + + o = s.option( + form.ListValue, + "force_dns", + _("Force Router DNS"), + _("Forces Router DNS use on local devices, also known as DNS Hijacking.") + ); + o.value("0", _("Let local devices use their own DNS servers if set")); + o.value("1", _("Force Router DNS server to all local devices")); + o.default = "1"; + + o = s.option( + form.ListValue, + "canary_domains_icloud", + _("Canary Domains iCloud"), + _( + "Blocks access to iCloud Private Relay resolvers, forcing local devices to use router for DNS resolution (%smore information%s)." + ).format( + '<a href="' + pkg.URL + "#canary_domains_icloud" + '" target="_blank">', + "</a>" + ) + ); + o.value("0", _("Let local devices use iCloud Private Relay")); + o.value("1", _("Force Router DNS server to all local devices")); + o.depends("force_dns", "1"); + o.default = "1"; + + o = s.option( + form.ListValue, + "canary_domains_mozilla", + _("Canary Domains Mozilla"), + _( + "Blocks access to Mozilla Encrypted resolvers, forcing local devices to use router for DNS resolution (%smore information%s)." + ).format( + '<a href="' + + pkg.URL + + "#canary_domains_mozilla" + + '" target="_blank">', + "</a>" + ) + ); + o.value("0", _("Let local devices use Mozilla Private Relay")); + o.value("1", _("Force Router DNS server to all local devices")); + o.depends("force_dns", "1"); + o.default = "1"; + + text = ""; + if (!reply.platform.http2_support) + text += + _( + "Please note that %s is not supported on this system (%smore information%s)." + ).format( + "<i>HTTP/2</i>", + '<a href="' + pkg.URL + "#http2-support" + '" target="_blank">', + "</a>" + ) + "<br />"; + if (!reply.platform.http3_support) + text += + _( + "Please note that %s is not supported on this system (%smore information%s)." + ).format( + "<i>HTTP/3 (QUIC)</i>", + '<a href="' + pkg.URL + "#http3-quic-support" + '" target="_blank">', + "</a>" + ) + "<br />"; + + s = m.section( + form.GridSection, + "https-dns-proxy", + _("HTTPS DNS Proxy - Instances"), + text + ); + s.rowcolors = true; + s.sortable = true; + s.anonymous = true; + s.addremove = true; + + s.sectiontitle = (section_id) => { + var provText; + var found; + reply.providers.forEach((prov) => { + var option; + let regexp = pkg.templateToRegexp(prov.template); + let resolver = L.uci.get(pkg.Name, section_id, "resolver_url"); + resolver = resolver === undefined ? null : resolver; + if (!found && resolver && regexp.test(resolver)) { + found = true; + provText = _(prov.title); + let match = resolver.match(regexp); + if (match[1] != null) { + if ( + prov.params && + prov.params.option && + prov.params.option.options + ) { + prov.params.option.options.forEach((opt) => { + if (opt.value === match[1]) { + option = _(opt.description); + } + }); + provText += " (" + option + ")"; + } else { + if (match[1] !== "") provText += " (" + match[1] + ")"; + } + } + } + }); + return provText || _("Unknown"); + }; + + var _provider; + _provider = s.option(form.ListValue, "_provider", _("Provider")); + _provider.modalonly = true; + _provider.cfgvalue = function (section_id) { + let resolver = this.map.data.get( + this.map.config, + section_id, + "resolver_url" + ); + if (resolver === undefined || resolver === null) return null; + let found; + let ret; + reply.providers.forEach((prov, i) => { + let regexp = pkg.templateToRegexp(prov.template); + if (!found && regexp.test(resolver)) { + found = true; + ret = prov.template; + } + }); + return ret || ""; + }; + _provider.write = function (section_id, formvalue) { + L.uci.set(pkg.Name, section_id, "resolver_url", formvalue); + }; + + reply.providers.forEach((prov, i) => { + if (prov.http2_only && !reply.platform.http2_support) return; + if (prov.http3_only && !reply.platform.http3_support) return; + _provider.value(prov.template, _(prov.title)); + if ( + prov.params && + prov.params.option && + prov.params.option.type && + prov.params.option.type === "select" + ) { + let optName = prov.params.option.description || _("Parameter"); + var _paramList = s.option(form.ListValue, "_paramList_" + i, optName); + _paramList.template = prov.template; + _paramList.modalonly = true; + if (prov.params.option.default) { + _paramList.default = prov.params.option.default; + } + prov.params.option.options.forEach((opt) => { + let val = opt.value || ""; + let descr = opt.description || ""; + _paramList.value(val, descr); + }); + _paramList.depends("_provider", prov.template); + _paramList.write = function (section_id, formvalue) { + let template = this.map.data.get( + this.map.config, + section_id, + "resolver_url" + ); + if (!formvalue && _paramList.template !== template) return 0; + let resolver = pkg.templateToResolver(_paramList.template, { + option: formvalue || "", + }); + L.uci.set(pkg.Name, section_id, "resolver_url", resolver); + }; + _paramList.remove = _paramList.write; + } else if ( + prov.params && + prov.params.option && + prov.params.option.type && + prov.params.option.type === "text" + ) { + let optName = prov.params.option.description || _("Parameter"); + var _paramText = s.option(form.Value, "_paramText_" + i, optName); + _paramText.template = prov.template; + _paramText.modalonly = true; + _paramText.depends("_provider", prov.template); + _paramText.optional = !( + prov.params.option.default && prov.params.option.default !== "" + ); + _paramText.cfgvalue = function (section_id) { + let resolver = this.map.data.get( + this.map.config, + section_id, + "resolver_url" + ); + if (resolver === undefined || resolver === null) return null; + let regexp = pkg.templateToRegexp(prov.template); + let match = resolver.match(regexp); + return (match && match[1]) || null; + }; + _paramText.write = function (section_id, formvalue) { + let template = this.map.data.get( + this.map.config, + section_id, + "resolver_url" + ); + if (!formvalue && _paramText.template !== template) return 0; + let resolver = pkg.templateToResolver(_paramText.template, { + option: formvalue || "", + }); + L.uci.set(pkg.Name, section_id, "resolver_url", resolver); + }; + _paramText.remove = _paramText.write; + } + }); + + o = s.option(form.Value, "bootstrap_dns", _("Bootstrap DNS")); + o.default = ""; + o.modalonly = true; + o.optional = true; + + o = s.option(form.Value, "listen_addr", _("Listen Address")); + o.datatype = "ipaddr"; + o.default = ""; + o.optional = true; + o.placeholder = "127.0.0.1"; + + o = s.option(form.Value, "listen_port", _("Listen Port")); + o.datatype = "port"; + o.default = ""; + o.optional = true; + o.placeholder = "5053"; + + o = s.option(form.Value, "user", _("Run As User")); + o.default = ""; + o.modalonly = true; + o.optional = true; + + o = s.option(form.Value, "group", _("Run As Group")); + o.default = ""; + o.modalonly = true; + o.optional = true; + + o = s.option(form.Value, "dscp_codepoint", _("DSCP Codepoint")); + o.datatype = "and(uinteger, range(0,63))"; + o.default = ""; + o.modalonly = true; + o.optional = true; + + o = s.option(form.Value, "verbosity", _("Logging Verbosity")); + o.datatype = "and(uinteger, range(0,4))"; + o.default = ""; + o.modalonly = true; + o.optional = true; + + o = s.option(form.Value, "logfile", _("Logging File Path")); + o.default = ""; + o.modalonly = true; + o.optional = true; + + o = s.option(form.Value, "polling_interval", _("Polling Interval")); + o.datatype = "and(uinteger, range(5,3600))"; + o.default = ""; + o.modalonly = true; + o.optional = true; + + o = s.option(form.Value, "proxy_server", _("Proxy Server")); + o.default = ""; + o.modalonly = true; + o.optional = true; + + o = s.option(form.ListValue, "use_http1", _("Use HTTP/1")); + o.modalonly = true; + o.optional = true; + o.rmempty = true; + o.value("", _("Use negotiated HTTP version")); + o.value("1", _("Force use of HTTP/1")); + o.default = ""; + + o = s.option( + form.ListValue, + "use_ipv6_resolvers_only", + _("Use IPv6 resolvers") + ); + o.modalonly = true; + o.optional = true; + o.rmempty = true; + o.value("", _("Use any family DNS resolvers")); + o.value("1", _("Force use of IPv6 DNS resolvers")); + o.default = ""; + + return Promise.all([status.render(), m.render()]); + }, +}); diff --git a/applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/status/include/71_https-dns-proxy.js b/applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/status/include/71_https-dns-proxy.js new file mode 100644 index 0000000000..8723b6e653 --- /dev/null +++ b/applications/luci-app-https-dns-proxy/htdocs/luci-static/resources/view/status/include/71_https-dns-proxy.js @@ -0,0 +1,153 @@ +"require ui"; +"require rpc"; +"require uci"; +"require form"; +"require baseclass"; + +var pkg = { + get Name() { + return "https-dns-proxy"; + }, + get URL() { + return "https://docs.openwrt.melmac.net/" + pkg.Name + "/"; + }, + templateToRegexp: function (template) { + return RegExp( + "^" + + template + .split(/(\{\w+\})/g) + .map((part) => { + let placeholder = part.match(/^\{(\w+)\}$/); + if (placeholder) return `(?<${placeholder[1]}>.*?)`; + else return part.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); + }) + .join("") + + "$" + ); + }, +}; + +var getInitStatus = rpc.declare({ + object: "luci." + pkg.Name, + method: "getInitStatus", + params: ["name"], +}); + +var getPlatformSupport = rpc.declare({ + object: "luci." + pkg.Name, + method: "getPlatformSupport", + params: ["name"], +}); + +var getProviders = rpc.declare({ + object: "luci." + pkg.Name, + method: "getProviders", + params: ["name"], +}); + +var getRuntime = rpc.declare({ + object: "luci." + pkg.Name, + method: "getRuntime", + params: ["name"], +}); + +return baseclass.extend({ + title: _("HTTPS DNS Proxy Instances"), + + load: function () { + return Promise.all([ + getInitStatus(pkg.Name), + getProviders(pkg.Name), + getRuntime(pkg.Name), + ]); + }, + + render: function (data) { + var reply = { + status: (data[0] && data[0][pkg.Name]) || { + enabled: null, + running: null, + force_dns_active: null, + version: null, + }, + providers: (data[1] && data[1][pkg.Name]) || { providers: [] }, + runtime: (data[2] && data[2][pkg.Name]) || { instances: [] }, + }; + reply.providers.sort(function (a, b) { + return _(a.title).localeCompare(_(b.title)); + }); + reply.providers.push({ + title: "Custom", + template: "{option}", + params: { option: { type: "text" } }, + }); + + var forceDnsText = ""; + if (reply.status.force_dns_active) { + reply.status.force_dns_ports.forEach((element) => { + forceDnsText += element + " "; + }); + } else { + forceDnsText = "-"; + } + + var table = E( + "table", + { class: "table", id: "https-dns-proxy_status_table" }, + [ + E("tr", { class: "tr table-titles" }, [ + E("th", { class: "th" }, _("Name / Type")), + E("th", { class: "th" }, _("Listen Address")), + E("th", { class: "th" }, _("Listen Port")), + E("th", { class: "th" }, _("Force DNS Ports")), + ]), + ] + ); + + var rows = []; + if (reply.runtime.instances) { + Object.values(reply.runtime.instances).forEach((element) => { + var resolver; + var address; + var port; + var name; + var option; + var found; + element.command.forEach((param, index, arr) => { + if (param === "-r") resolver = arr[index + 1]; + if (param === "-a") address = arr[index + 1]; + if (param === "-p") port = arr[index + 1]; + }); + resolver = resolver || "Unknown"; + address = address || "127.0.0.1"; + port = port || "Unknown"; + reply.providers.forEach((prov) => { + let regexp = pkg.templateToRegexp(prov.template); + if (!found && regexp.test(resolver)) { + found = true; + name = _(prov.title); + let match = resolver.match(regexp); + if (match[1] != null) { + if ( + prov.params && + prov.params.option && + prov.params.option.options + ) { + prov.params.option.options.forEach((opt) => { + if (opt.value === match[1]) option = _(opt.description); + }); + name += " (" + option + ")"; + } else { + if (match[1] !== "") name += " (" + match[1] + ")"; + } + } + } + }); + rows.push([name, address, port, forceDnsText]); + }); + } + cbi_update_table(table, rows, E("em", _("There are no active instances."))); + + return table; + }, +}); |