luci-app-vpnbypass: client-side rendering 4943/head
authorStan Grishin <stangri@melmac.net>
Thu, 25 Mar 2021 05:26:22 +0000 (05:26 +0000)
committerStan Grishin <stangri@melmac.net>
Thu, 25 Mar 2021 05:26:22 +0000 (05:26 +0000)
Signed-off-by: Stan Grishin <stangri@melmac.net>
15 files changed:
applications/luci-app-vpnbypass/Makefile
applications/luci-app-vpnbypass/htdocs/luci-static/resources/view/vpnbypass/overview.js [new file with mode: 0644]
applications/luci-app-vpnbypass/htdocs/luci-static/resources/vpnbypass/widgets.js [new file with mode: 0644]
applications/luci-app-vpnbypass/luasrc/controller/vpnbypass.lua [deleted file]
applications/luci-app-vpnbypass/luasrc/model/cbi/vpnbypass.lua [deleted file]
applications/luci-app-vpnbypass/luasrc/view/vpnbypass/buttons.htm [deleted file]
applications/luci-app-vpnbypass/luasrc/view/vpnbypass/css.htm [deleted file]
applications/luci-app-vpnbypass/luasrc/view/vpnbypass/js.htm [deleted file]
applications/luci-app-vpnbypass/luasrc/view/vpnbypass/status-textarea.htm [deleted file]
applications/luci-app-vpnbypass/luasrc/view/vpnbypass/status.htm [deleted file]
applications/luci-app-vpnbypass/po/templates/vpnbypass.pot
applications/luci-app-vpnbypass/root/etc/uci-defaults/40_luci-vpnbypass
applications/luci-app-vpnbypass/root/usr/libexec/rpcd/luci.vpnbypass [new file with mode: 0755]
applications/luci-app-vpnbypass/root/usr/share/luci/menu.d/vpnbypass.json [new file with mode: 0644]
applications/luci-app-vpnbypass/root/usr/share/rpcd/acl.d/luci-app-vpnbypass.json

index da2c2af42dcfe32bcfe85daebd17ba531ca67f42..abab837c324ff1cb2286784784296b05f252c191 100644 (file)
@@ -5,10 +5,11 @@ include $(TOPDIR)/rules.mk
 
 PKG_LICENSE:=GPL-3.0-or-later
 PKG_MAINTAINER:=Stan Grishin <stangri@melmac.net>
+PKG_VERSION:=1.3.1-9
 
 LUCI_TITLE:=VPN Bypass Web UI
 LUCI_DESCRIPTION:=Provides Web UI for VPNBypass service.
-LUCI_DEPENDS:=+luci-compat +luci-mod-admin-full +vpnbypass
+LUCI_DEPENDS:=+luci-mod-admin-full +vpnbypass
 LUCI_PKGARCH:=all
 
 include ../../luci.mk
diff --git a/applications/luci-app-vpnbypass/htdocs/luci-static/resources/view/vpnbypass/overview.js b/applications/luci-app-vpnbypass/htdocs/luci-static/resources/view/vpnbypass/overview.js
new file mode 100644 (file)
index 0000000..b2d5d1f
--- /dev/null
@@ -0,0 +1,62 @@
+// Copyright 2021 Stan Grishin (stangri@melmac.net)
+// Many thanks to [@vsviridov](https://github.com/vsviridov) for help with transition to JS
+
+'use strict';
+'require form';
+'require uci';
+'require view';
+'require vpnbypass.widgets as widgets';
+
+var pkg = {
+       get Name() { return 'vpnbypass'; },
+       get URL() { return 'https://docs.openwrt.melmac.net/' + pkg.Name + '/'; }
+};
+
+return view.extend({
+       load: function () {
+               return Promise.all([
+                       uci.load(pkg.Name),
+                       uci.load('dhcp')
+               ]);
+       },
+
+       render: function (data) {
+
+               var m, d, s, o;
+
+               m = new form.Map(pkg.Name, _('VPN Bypass'));
+
+               s = m.section(form.NamedSection, 'config', pkg.Name);
+
+               o = s.option(widgets.Status, '', _('Service Status'));
+
+               o = s.option(widgets.Buttons, '', _('Service Control'));
+
+               o = s.option(form.DynamicList, 'localport', _('Local Ports to Bypass'), _('Local ports to trigger VPN Bypass.'));
+               o.datatype = 'portrange';
+               o.addremove = false;
+               o.optional = false;
+
+               o = s.option(form.DynamicList, 'remoteport', _('Remote Ports to Bypass'), _('Remote ports to trigger VPN Bypass.'));
+               o.datatype = 'portrange';
+               o.addremove = false;
+               o.optional = false;
+
+               o = s.option(form.DynamicList, 'localsubnet', _('Local IP Addresses to Bypass'), _('Local IP addresses or subnets with direct internet access.'));
+               o.datatype = 'ip4addr';
+               o.addremove = false;
+               o.optional = false;
+
+               o = s.option(form.DynamicList, 'remotesubnet', _('Remote IP Addresses to Bypass'), _('Remote IP addresses or subnets which will be accessed directly.'));
+               o.datatype = 'ip4addr';
+               o.addremove = false;
+               o.optional = false;
+
+               d = new form.Map('dhcp');
+               s = d.section(form.TypedSection, 'dnsmasq');
+               s.anonymous = true;
+               o = s.option(form.DynamicList, 'ipset', _('Domains to Bypass'), _('Domains to be accessed directly, see %sREADME%s for syntax.').format('<a href="' + pkg.URL + '#bypass-domains-formatsyntax" target="_blank" rel="noreferrer noopener">', '</a>'));
+
+               return Promise.all([m.render(), d.render()]);
+       }
+});
diff --git a/applications/luci-app-vpnbypass/htdocs/luci-static/resources/vpnbypass/widgets.js b/applications/luci-app-vpnbypass/htdocs/luci-static/resources/vpnbypass/widgets.js
new file mode 100644 (file)
index 0000000..f14dd21
--- /dev/null
@@ -0,0 +1,192 @@
+// Thsis file wouldn't have been possible without help from [@vsviridov](https://github.com/vsviridov)
+
+'require ui';
+'require rpc';
+'require form';
+
+var pkg = {
+       get Name() { return 'vpnbypass'; }
+};
+
+var _getInitList = rpc.declare({
+       object: 'luci.' + pkg.Name,
+       method: 'getInitList',
+       params: ['name']
+});
+
+var _setInitAction = rpc.declare({
+       object: 'luci.' + pkg.Name,
+       method: 'setInitAction',
+       params: ['name', 'action'],
+       expect: { result: false }
+});
+
+var _getInitStatus = rpc.declare({
+       object: 'luci.' + pkg.Name,
+       method: 'getInitStatus',
+       params: ['name']
+});
+
+var RPC = {
+       listeners: [],
+       on: function on(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 emit(event, data) {
+               this.listeners.forEach(function (listener) {
+                       if (listener.event === event) {
+                               listener.callback(data);
+                       }
+               });
+       },
+       getInitList: function getInitList(name) {
+               _getInitList(name).then(function (result) {
+                       this.emit('getInitList', result);
+               }.bind(this));
+
+       },
+       getInitStatus: function getInitStatus(name) {
+               _getInitStatus(name).then(function (result) {
+                       this.emit('getInitStatus', result);
+               }.bind(this));
+       },
+       setInitAction: function setInitAction(name, action) {
+               _setInitAction(name, action).then(function (result) {
+                       this.emit('setInitAction', result);
+               }.bind(this));
+       }
+}
+
+var statusCBI = form.DummyValue.extend({
+       renderWidget: function (section) {
+               var status = E('span', {}, _("Quering") + "...");
+               RPC.on('getInitStatus', function (reply) {
+                       if (reply[pkg.Name].version) {
+                               if (reply[pkg.Name].running) {
+                                       status.innerText = _("Running (version: %s)").format(reply[pkg.Name].version);
+                               }
+                               else {
+                                       if (reply[pkg.Name].enabled) {
+                                               status.innerText = _("Stopped (version: %s)").format(reply[pkg.Name].version);
+                                       }
+                                       else {
+                                               status.innerText = _("Stopped (Disabled)");
+                                       }
+                               }
+                       }
+                       else {
+                               status.innerText = _("Not installed or not found")
+                       }
+               });
+               return E('div', {}, [status]);
+       }
+});
+
+var buttonsCBI = form.DummyValue.extend({
+       renderWidget: function (section) {
+
+               var btn_gap = E('span', {}, '&nbsp;&nbsp;');
+               var btn_gap_long = E('span', {}, '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;');
+
+               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'));
+
+               RPC.on('getInitStatus', function (reply) {
+                       if (reply[pkg.Name].version) {
+                               if (reply[pkg.Name].enabled) {
+                                       btn_enable.disabled = true;
+                                       btn_disable.disabled = false;
+                                       if (reply[pkg.Name].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;
+                               }
+                       }
+               });
+
+               RPC.getInitStatus(pkg.Name);
+               
+               return E('div', {}, [btn_start, btn_gap, btn_action, btn_gap, btn_stop, btn_gap_long, btn_enable, btn_gap, btn_disable]);
+       }
+});
+
+RPC.on('setInitAction', function (reply) {
+       ui.hideModal();
+       RPC.getInitStatus(pkg.Name);
+});
+
+return L.Class.extend({
+       Status: statusCBI,
+       Buttons: buttonsCBI
+});
diff --git a/applications/luci-app-vpnbypass/luasrc/controller/vpnbypass.lua b/applications/luci-app-vpnbypass/luasrc/controller/vpnbypass.lua
deleted file mode 100644 (file)
index 057aee7..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-module("luci.controller.vpnbypass", package.seeall)
-function index()
-       if nixio.fs.access("/etc/config/vpnbypass") then
-               local e = entry({"admin", "vpn"}, firstchild(), _("VPN"), 60)
-               e.dependent = false
-               e.acl_depends = { "luci-app-vpnbypass" }
-               entry({"admin", "vpn", "vpnbypass"}, cbi("vpnbypass"), _("VPN Bypass"))
-               entry({"admin", "vpn", "vpnbypass", "action"}, call("vpnbypass_action"), nil).leaf = true
-       end
-end
-
-function vpnbypass_action(name)
-       local packageName = "vpnbypass"
-       local http = require "luci.http"
-       local sys = require "luci.sys"
-       local uci = require "luci.model.uci".cursor()
-       local util = require "luci.util"
-       if name == "start" then
-               sys.init.start(packageName)
-       elseif name == "action" then
-               util.exec("/etc/init.d/" .. packageName .. " restart >/dev/null 2>&1")
-               util.exec("/etc/init.d/dnsmasq restart >/dev/null 2>&1")
-       elseif name == "stop" then
-               sys.init.stop(packageName)
-       elseif name == "enable" then
-               uci:set(packageName, "config", "enabled", "1")
-               uci:commit(packageName)
-       elseif name == "disable" then
-               uci:set(packageName, "config", "enabled", "0")
-               uci:commit(packageName)
-       end
-       http.prepare_content("text/plain")
-       http.write("0")
-end
diff --git a/applications/luci-app-vpnbypass/luasrc/model/cbi/vpnbypass.lua b/applications/luci-app-vpnbypass/luasrc/model/cbi/vpnbypass.lua
deleted file mode 100644 (file)
index d0f6afc..0000000
+++ /dev/null
@@ -1,95 +0,0 @@
-local uci = require "luci.model.uci".cursor()
-local sys = require "luci.sys"
-local util = require "luci.util"
-local packageName = "vpnbypass"
-local readmeURL = "https://docs.openwrt.melmac.net/" .. packageName .. "/"
-
-function getPackageVersion()
-       local opkgFile = "/usr/lib/opkg/status"
-       local line
-       local flag = false
-       for line in io.lines(opkgFile) do
-               if flag then
-                       return line:match('[%d%.$-]+') or ""
-               elseif line:find("Package: " .. packageName:gsub("%-", "%%%-")) then
-                       flag = true
-               end
-       end
-       return ""
-end
-
-local packageVersion = getPackageVersion()
-local statusText = nil 
-if packageVersion == "" then
-       statusText = translatef("%s is not installed or not found", packageName)
-end
-
-local serviceRunning, serviceEnabled = false, false
-if uci:get(packageName, "config", "enabled") == "1" then
-       serviceEnabled = true
-end
-if sys.call("iptables -t mangle -L | grep -q " .. packageName:upper()) == 0 then
-       serviceRunning = true
-end
-
-if serviceRunning then
-       statusText = translate("Running")
-else
-       statusText = translate("Stopped")
-       if not serviceEnabled then
-               statusText = translatef("%s (disabled)", statusText)
-       end
-end
-
-m = Map("vpnbypass", translate("VPN Bypass Settings"))
-
-h = m:section(NamedSection, "config", packageName, translatef("Service Status [%s %s]", packageName, packageVersion))
-ss = h:option(DummyValue, "_dummy", translate("Service Status"))
-ss.template = packageName .. "/status"
-ss.value = statusText
-if packageVersion ~= "" then
-       buttons = h:option(DummyValue, "_dummy")
-       buttons.template = packageName .. "/buttons"
-end
-
-s = m:section(NamedSection, "config", "vpnbypass", translate("VPN Bypass Rules"))
--- Local Ports
-p1 = s:option(DynamicList, "localport", translate("Local Ports to Bypass"), translate("Local ports to trigger VPN Bypass"))
-p1.datatype    = "portrange"
--- p1.placeholder = "0-65535"
-p1.addremove = false
-p1.optional = false
-
--- Remote Ports
-p2 = s:option(DynamicList, "remoteport", translate("Remote Ports to Bypass"), translate("Remote ports to trigger VPN Bypass"))
-p2.datatype    = "portrange"
--- p2.placeholder = "0-65535"
-p2.addremove = false
-p2.optional = false
-
--- Local Subnets
-r1 = s:option(DynamicList, "localsubnet", translate("Local IP Addresses to Bypass"), translate("Local IP addresses or subnets with direct internet access (outside of the VPN tunnel)"))
-r1.datatype    = "ip4addr"
--- r1.placeholder = ip.new(m.uci:get("network", "lan", "ipaddr"), m.uci:get("network", "lan", "netmask"))
-r1.addremove = false
-r1.optional = false
-
--- Remote Subnets
-r2 = s:option(DynamicList, "remotesubnet", translate("Remote IP Addresses to Bypass"), translate("Remote IP addresses or subnets which will be accessed directly (outside of the VPN tunnel)"))
-r2.datatype    = "ip4addr"
--- r2.placeholder = "0.0.0.0/0"
-r2.addremove = false
-r2.optional = false
-
--- Domains
-d = Map("dhcp")
-s4 = d:section(TypedSection, "dnsmasq")
-s4.anonymous = true
-di = s4:option(DynamicList, "ipset", translate("Domains to Bypass"),
-               translatef("Domains to be accessed directly (outside of the VPN tunnel), see %sREADME%s for syntax", 
-               "<a href=\"" .. readmeURL   .. "#bypass-domains-formatsyntax" .. "\" target=\"_blank\">", "</a>"))
-function d.on_after_commit(map)
-       util.exec("/etc/init.d/dnsmasq restart >/dev/null 2>&1")
-end
-
-return m, d
diff --git a/applications/luci-app-vpnbypass/luasrc/view/vpnbypass/buttons.htm b/applications/luci-app-vpnbypass/luasrc/view/vpnbypass/buttons.htm
deleted file mode 100644 (file)
index 9261c05..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-<%# Copyright 2020 Stan Grishin <stangri@melmac.net> -%>
-
-<%+vpnbypass/css%>
-<%+vpnbypass/js%>
-
-<%-
-       local packageName = "vpnbypass"
-       local serviceRunning, serviceEnabled = false, false;
-       if luci.sys.call("iptables -t mangle -L | grep -q " .. packageName:upper()) == 0 then
-               serviceRunning = true
-       end
-       if luci.model.uci.cursor():get(packageName, "config", "enabled") == "1" then
-               serviceEnabled = true
-       end
-
-       if serviceEnabled then
-               btn_start_status = true
-               btn_action_status = true
-               btn_stop_status = true
-               btn_enable_status = false
-               btn_disable_status = true
-       else
-               btn_start_status = false
-               btn_action_status = false
-               btn_stop_status = false
-               btn_enable_status = true
-               btn_disable_status = false
-       end
-       if serviceRunning then
-               btn_start_status = false
-               btn_action_status = true
-               btn_stop_status = true
-       else
-               btn_action_status = false
-               btn_stop_status = false
-       end
--%>
-
-<div class="cbi-value"><label class="cbi-value-title">Service Control</label>
-       <div class="cbi-value-field">
-               <input type="button" class="btn cbi-button cbi-button-apply" id="btn_start" name="start" value="<%:Start%>"
-                       onclick="button_action(this)" />
-               <span id="btn_start_spinner" class="btn_spinner"></span>
-               <input type="button" class="btn cbi-button cbi-button-apply" id="btn_action" name="action" value="<%:Restart%>"
-                       onclick="button_action(this)" />
-               <span id="btn_action_spinner" class="btn_spinner"></span>
-               <input type="button" class="btn cbi-button cbi-button-reset" id="btn_stop" name="stop" value="<%:Stop%>"
-                       onclick="button_action(this)" />
-               <span id="btn_stop_spinner" class="btn_spinner"></span>
-               &nbsp;
-               &nbsp;
-               &nbsp;
-               &nbsp;
-               <input type="button" class="btn cbi-button cbi-button-apply" id="btn_enable" name="enable" value="<%:Enable%>"
-                       onclick="button_action(this)" />
-               <span id="btn_enable_spinner" class="btn_spinner"></span>
-               <input type="button" class="btn cbi-button cbi-button-reset" id="btn_disable" name="disable" value="<%:Disable%>"
-                       onclick="button_action(this)" />
-               <span id="btn_disable_spinner" class="btn_spinner"></span>
-       </div>
-</div>
-
-<%-if not btn_start_status then%>
-<script type="text/javascript">document.getElementById("btn_start").disabled = true;</script>
-<%-end%>
-<%-if not btn_action_status then%>
-<script type="text/javascript">document.getElementById("btn_action").disabled = true;</script>
-<%-end%>
-<%-if not btn_stop_status then%>
-<script type="text/javascript">document.getElementById("btn_stop").disabled = true;</script>
-<%-end%>
-<%-if not btn_enable_status then%>
-<script type="text/javascript">document.getElementById("btn_enable").disabled = true;</script>
-<%-end%>
-<%-if not btn_disable_status then%>
-<script type="text/javascript">document.getElementById("btn_disable").disabled = true;</script>
-<%-end%>
\ No newline at end of file
diff --git a/applications/luci-app-vpnbypass/luasrc/view/vpnbypass/css.htm b/applications/luci-app-vpnbypass/luasrc/view/vpnbypass/css.htm
deleted file mode 100644 (file)
index 6fb3d51..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-<style type="text/css">
-       .btn_spinner
-       {
-               display: inline-block;
-               width: 0px;
-               height: 16px;
-               margin: 0 0px;
-       }
-</style>
diff --git a/applications/luci-app-vpnbypass/luasrc/view/vpnbypass/js.htm b/applications/luci-app-vpnbypass/luasrc/view/vpnbypass/js.htm
deleted file mode 100644 (file)
index e8c076f..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-<script type="text/javascript">
-//<![CDATA[
- function button_action(action) {
-       var xhr = new XHR(false);
-       var btn_start = document.getElementById("btn_start");
-       var btn_action = document.getElementById("btn_action");
-       var btn_stop = document.getElementById("btn_stop");
-       var btn_enable = document.getElementById("btn_enable");
-       var btn_disable = document.getElementById("btn_disable");
-       var btn_spinner;
-       switch (action.name) {
-               case "start":
-                       btn_spinner = document.getElementById("btn_start_spinner");
-                       break;
-               case "action":
-                       btn_spinner = document.getElementById("btn_action_spinner");
-                       break;
-               case "stop":
-                       btn_spinner = document.getElementById("btn_stop_spinner");
-                       break;
-               case "enable":
-                       btn_spinner = document.getElementById("btn_enable_spinner");
-                       break;
-               case "disable":
-                       btn_spinner = document.getElementById("btn_disable_spinner");
-                       break;
-       }
-       btn_start.disabled = true;
-       btn_action.disabled = true;
-       btn_stop.disabled = true;
-       btn_enable.disabled = true;
-       btn_disable.disabled = true;
-       spinner(btn_spinner, 1);
-       xhr.get('<%=luci.dispatcher.build_url("admin", "vpn", "vpnbypass", "action")%>/' + action.name, null,
-               function (x) {
-                       if (!x) {
-                               return;
-                       }
-                       btn_start.disabled = false;
-                       btn_action.disabled = false;
-                       btn_stop.disabled = false;
-                       btn_enable.disabled = false;
-                       btn_disable.disabled = false;
-                       spinner(btn_spinner, 0);
-                       location.reload();
-                });
-}
-function spinner(element, state) {
-       if (state === 1) {
-               element.style.width = "16px";
-               element.innerHTML = '<img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" width="16" height="16" style="vertical-align:middle" />';
-       }
-       else {
-               element.style.width = "0px";
-               element.innerHTML = '';
-       }
-}
-//]]>
-</script>
diff --git a/applications/luci-app-vpnbypass/luasrc/view/vpnbypass/status-textarea.htm b/applications/luci-app-vpnbypass/luasrc/view/vpnbypass/status-textarea.htm
deleted file mode 100644 (file)
index 4ab2e11..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-<%#
-Copyright 2017-2019 Stan Grishin (stangri@melmac.net)
-This is free software, licensed under the Apache License, Version 2.0
--%>
-
-<%+cbi/valueheader%>
-
-<textarea rows="<%=select(2, self:cfgvalue(section):gsub('\n', '\n'))%>"
-       style="border:none;box-shadow:none;background:transparent;font-weight:bold;line-height:20px;width:50em;padding:none;margin:6px;resize:none;overflow:hidden;"
-       disabled="disabled"><%=self:cfgvalue(section):gsub('\n', '\n')%>
-</textarea>
-
-<%+cbi/valuefooter%>
diff --git a/applications/luci-app-vpnbypass/luasrc/view/vpnbypass/status.htm b/applications/luci-app-vpnbypass/luasrc/view/vpnbypass/status.htm
deleted file mode 100644 (file)
index c453428..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-<%#
-Copyright 2017-2018 Dirk Brenken (dev@brenken.org)
-This is free software, licensed under the Apache License, Version 2.0
--%>
-
-<%+cbi/valueheader%>
-
-<input name="status" id="status" type="text" class="cbi-input-text" style="outline:none;border:none;box-shadow:none;background:transparent;font-weight:bold;line-height:30px;height:30px;width:50em;" value="<%=self:cfgvalue(section)%>" disabled="disabled" />
-
-<%+cbi/valuefooter%>
index abbe19822964b6a962bc74d8ef32344b7d6f9bea..443ba8b9c0bb81e13e027c7b74f9d12e069a9fe8 100644 (file)
 msgid ""
 msgstr "Content-Type: text/plain; charset=UTF-8"
 
-#: applications/luci-app-vpnbypass/luasrc/model/cbi/vpnbypass.lua:40
-msgid "%s (disabled)"
-msgstr ""
-
-#: applications/luci-app-vpnbypass/luasrc/model/cbi/vpnbypass.lua:24
-msgid "%s is not installed or not found"
+#: applications/luci-app-vpnbypass/htdocs/luci-static/resources/vpnbypass/widgets.js:150
+msgid "Disable"
 msgstr ""
 
-#: applications/luci-app-vpnbypass/luasrc/view/vpnbypass/buttons.htm:57
-msgid "Disable"
+#: applications/luci-app-vpnbypass/htdocs/luci-static/resources/vpnbypass/widgets.js:146
+msgid "Disabling %s service"
 msgstr ""
 
-#: applications/luci-app-vpnbypass/luasrc/model/cbi/vpnbypass.lua:88
+#: applications/luci-app-vpnbypass/htdocs/luci-static/resources/view/vpnbypass/overview.js:58
 msgid "Domains to Bypass"
 msgstr ""
 
-#: applications/luci-app-vpnbypass/luasrc/model/cbi/vpnbypass.lua:89
-msgid ""
-"Domains to be accessed directly (outside of the VPN tunnel), see %sREADME%s "
-"for syntax"
+#: applications/luci-app-vpnbypass/htdocs/luci-static/resources/view/vpnbypass/overview.js:58
+msgid "Domains to be accessed directly, see %sREADME%s for syntax."
 msgstr ""
 
-#: applications/luci-app-vpnbypass/luasrc/view/vpnbypass/buttons.htm:54
+#: applications/luci-app-vpnbypass/htdocs/luci-static/resources/vpnbypass/widgets.js:139
 msgid "Enable"
 msgstr ""
 
-#: applications/luci-app-vpnbypass/root/usr/share/rpcd/acl.d/luci-app-vpnbypass.json:3
-msgid "Grant UCI and file access for luci-app-vpnbypass"
+#: applications/luci-app-vpnbypass/htdocs/luci-static/resources/vpnbypass/widgets.js:135
+msgid "Enabling %s service"
 msgstr ""
 
-#: applications/luci-app-vpnbypass/luasrc/view/vpnbypass/js.htm:51
-msgid "Loading"
+#: applications/luci-app-vpnbypass/root/usr/share/rpcd/acl.d/luci-app-vpnbypass.json:3
+msgid "Grant UCI and file access for luci-app-vpnbypass"
 msgstr ""
 
-#: applications/luci-app-vpnbypass/luasrc/model/cbi/vpnbypass.lua:71
+#: applications/luci-app-vpnbypass/htdocs/luci-static/resources/view/vpnbypass/overview.js:45
 msgid "Local IP Addresses to Bypass"
 msgstr ""
 
-#: applications/luci-app-vpnbypass/luasrc/model/cbi/vpnbypass.lua:71
-msgid ""
-"Local IP addresses or subnets with direct internet access (outside of the "
-"VPN tunnel)"
+#: applications/luci-app-vpnbypass/htdocs/luci-static/resources/view/vpnbypass/overview.js:45
+msgid "Local IP addresses or subnets with direct internet access."
 msgstr ""
 
-#: applications/luci-app-vpnbypass/luasrc/model/cbi/vpnbypass.lua:57
+#: applications/luci-app-vpnbypass/htdocs/luci-static/resources/view/vpnbypass/overview.js:35
 msgid "Local Ports to Bypass"
 msgstr ""
 
-#: applications/luci-app-vpnbypass/luasrc/model/cbi/vpnbypass.lua:57
-msgid "Local ports to trigger VPN Bypass"
+#: applications/luci-app-vpnbypass/htdocs/luci-static/resources/view/vpnbypass/overview.js:35
+msgid "Local ports to trigger VPN Bypass."
+msgstr ""
+
+#: applications/luci-app-vpnbypass/htdocs/luci-static/resources/vpnbypass/widgets.js:84
+msgid "Not installed or not found"
 msgstr ""
 
-#: applications/luci-app-vpnbypass/luasrc/model/cbi/vpnbypass.lua:78
+#: applications/luci-app-vpnbypass/htdocs/luci-static/resources/vpnbypass/widgets.js:68
+msgid "Quering"
+msgstr ""
+
+#: applications/luci-app-vpnbypass/htdocs/luci-static/resources/view/vpnbypass/overview.js:50
 msgid "Remote IP Addresses to Bypass"
 msgstr ""
 
-#: applications/luci-app-vpnbypass/luasrc/model/cbi/vpnbypass.lua:78
-msgid ""
-"Remote IP addresses or subnets which will be accessed directly (outside of "
-"the VPN tunnel)"
+#: applications/luci-app-vpnbypass/htdocs/luci-static/resources/view/vpnbypass/overview.js:50
+msgid "Remote IP addresses or subnets which will be accessed directly."
 msgstr ""
 
-#: applications/luci-app-vpnbypass/luasrc/model/cbi/vpnbypass.lua:64
+#: applications/luci-app-vpnbypass/htdocs/luci-static/resources/view/vpnbypass/overview.js:40
 msgid "Remote Ports to Bypass"
 msgstr ""
 
-#: applications/luci-app-vpnbypass/luasrc/model/cbi/vpnbypass.lua:64
-msgid "Remote ports to trigger VPN Bypass"
+#: applications/luci-app-vpnbypass/htdocs/luci-static/resources/view/vpnbypass/overview.js:40
+msgid "Remote ports to trigger VPN Bypass."
 msgstr ""
 
-#: applications/luci-app-vpnbypass/luasrc/view/vpnbypass/buttons.htm:44
+#: applications/luci-app-vpnbypass/htdocs/luci-static/resources/vpnbypass/widgets.js:117
 msgid "Restart"
 msgstr ""
 
-#: applications/luci-app-vpnbypass/luasrc/model/cbi/vpnbypass.lua:36
-msgid "Running"
+#: applications/luci-app-vpnbypass/htdocs/luci-static/resources/vpnbypass/widgets.js:113
+msgid "Restarting %s service"
 msgstr ""
 
-#: applications/luci-app-vpnbypass/luasrc/model/cbi/vpnbypass.lua:47
-msgid "Service Status"
+#: applications/luci-app-vpnbypass/htdocs/luci-static/resources/vpnbypass/widgets.js:72
+msgid "Running (version: %s)"
 msgstr ""
 
-#: applications/luci-app-vpnbypass/luasrc/model/cbi/vpnbypass.lua:46
-msgid "Service Status [%s %s]"
+#: applications/luci-app-vpnbypass/htdocs/luci-static/resources/view/vpnbypass/overview.js:33
+msgid "Service Control"
 msgstr ""
 
-#: applications/luci-app-vpnbypass/luasrc/view/vpnbypass/buttons.htm:41
+#: applications/luci-app-vpnbypass/htdocs/luci-static/resources/view/vpnbypass/overview.js:31
+msgid "Service Status"
+msgstr ""
+
+#: applications/luci-app-vpnbypass/htdocs/luci-static/resources/vpnbypass/widgets.js:106
 msgid "Start"
 msgstr ""
 
-#: applications/luci-app-vpnbypass/luasrc/view/vpnbypass/buttons.htm:47
-msgid "Stop"
+#: applications/luci-app-vpnbypass/htdocs/luci-static/resources/vpnbypass/widgets.js:102
+msgid "Starting %s service"
 msgstr ""
 
-#: applications/luci-app-vpnbypass/luasrc/model/cbi/vpnbypass.lua:38
-msgid "Stopped"
+#: applications/luci-app-vpnbypass/htdocs/luci-static/resources/vpnbypass/widgets.js:128
+msgid "Stop"
 msgstr ""
 
-#: applications/luci-app-vpnbypass/luasrc/controller/vpnbypass.lua:4
-msgid "VPN"
+#: applications/luci-app-vpnbypass/htdocs/luci-static/resources/vpnbypass/widgets.js:79
+msgid "Stopped (Disabled)"
 msgstr ""
 
-#: applications/luci-app-vpnbypass/luasrc/controller/vpnbypass.lua:7
-msgid "VPN Bypass"
+#: applications/luci-app-vpnbypass/htdocs/luci-static/resources/vpnbypass/widgets.js:76
+msgid "Stopped (version: %s)"
 msgstr ""
 
-#: applications/luci-app-vpnbypass/luasrc/model/cbi/vpnbypass.lua:55
-msgid "VPN Bypass Rules"
+#: applications/luci-app-vpnbypass/htdocs/luci-static/resources/vpnbypass/widgets.js:124
+msgid "Stopping %s service"
 msgstr ""
 
-#: applications/luci-app-vpnbypass/luasrc/model/cbi/vpnbypass.lua:44
-msgid "VPN Bypass Settings"
+#: applications/luci-app-vpnbypass/htdocs/luci-static/resources/view/vpnbypass/overview.js:27
+#: applications/luci-app-vpnbypass/root/usr/share/luci/menu.d/vpnbypass.json:3
+msgid "VPN Bypass"
 msgstr ""
index 6df7810334efdcdc9f577291e4eb08d4a9a265b3..8e457adce8e89e4b5304d2e05d48db237f05c6c8 100644 (file)
@@ -1,4 +1,5 @@
 #!/bin/sh
+
+/etc/init.d/rpcd reload
 rm -rf /var/luci-modulecache/; rm -f /var/luci-indexcache;
 exit 0
-
diff --git a/applications/luci-app-vpnbypass/root/usr/libexec/rpcd/luci.vpnbypass b/applications/luci-app-vpnbypass/root/usr/libexec/rpcd/luci.vpnbypass
new file mode 100755 (executable)
index 0000000..e5eb7c2
--- /dev/null
@@ -0,0 +1,105 @@
+#!/bin/sh
+# Copyright 2021 Stan Grishin (stangri@melmac.net)
+# shellcheck disable=SC1091,SC2039
+
+# TechRef: https://openwrt.org/docs/techref/rpcd
+
+. /lib/functions.sh
+. /usr/share/libubox/jshn.sh
+
+pkgName="vpnbypass"
+
+is_enabled() { uci -q get "${1}.config.enabled"; }
+is_running() { iptables -t mangle -L | grep -q VPNBYPASS && echo '1' || echo '0'; }
+get_version() { grep -A2 -w "Package: $1$" /usr/lib/opkg/status | sed -n 's/Version: //p'; }
+print_json_bool() { json_init; json_add_boolean "$1" "$2"; json_dump; json_cleanup; }
+print_json_string() { json_init; json_add_string "$1" "$2"; json_dump; json_cleanup; }
+logger() { /usr/bin/logger -t "$pkgName" "$@"; }
+
+get_init_list() {
+       local name="$1"
+       json_init
+       json_add_object "$name"
+       json_add_boolean 'enabled' "$(is_enabled "$name")"
+       json_add_boolean 'running' "$(is_running "$name")"
+       json_close_object
+       json_dump
+       json_cleanup
+}
+
+set_init_action() {
+       local name="$1" action="$2" cmd
+       if [ ! -f "/etc/init.d/$name" ]; then
+               print_json_string 'error' 'Init script not found!'
+               return
+       fi
+       case $action in
+               enable)
+                       cmd="uci -q set ${name}.config.enabled=1 && uci commit $name";;
+               disable)
+                       cmd="uci -q set ${name}.config.enabled=0 && uci commit $name";;
+               start|stop|reload|restart)
+                       cmd="/etc/init.d/${name} ${action}";;
+       esac
+       if [ -n "$cmd" ] && eval "${cmd}" 1>/dev/null 2>&1; then
+               print_json_bool "result" '1'
+       else
+               print_json_bool "result" '0'
+       fi
+}
+
+get_init_status() {
+       local name="$1"
+       json_init
+       json_add_object "$name"
+       json_add_boolean 'enabled' "$(is_enabled "$name")"
+       json_add_boolean 'running' "$(is_running "$name")"
+       json_add_string 'version' "$(get_version "$name")"
+       json_close_object
+       json_dump
+       json_cleanup
+}
+
+case "$1" in
+       list)
+               json_init
+               json_add_object "getInitList"
+                       json_add_string 'name' 'name'
+               json_close_object
+               json_add_object "setInitAction"
+                       json_add_string 'name' 'name'
+                       json_add_string 'action' 'action'
+               json_close_object
+               json_add_object "getInitStatus"
+                       json_add_string 'name' 'name'
+               json_close_object
+               json_dump
+               json_cleanup
+               ;;
+       call)
+               case "$2" in
+                       getInitList)
+                               read -r input
+                               json_load "$input"
+                               json_get_var name 'name'
+                               json_cleanup
+                               get_init_list "$name"
+                               ;;
+                       getInitStatus)
+                               read -r input
+                               json_load "$input"
+                               json_get_var name 'name'
+                               json_cleanup
+                               get_init_status "$name"
+                               ;;
+                       setInitAction)
+                               read -r input
+                               json_load "$input"
+                               json_get_var name 'name'
+                               json_get_var action 'action'
+                               json_cleanup
+                               set_init_action "$name" "$action"
+                               ;;
+               esac
+       ;;
+esac
diff --git a/applications/luci-app-vpnbypass/root/usr/share/luci/menu.d/vpnbypass.json b/applications/luci-app-vpnbypass/root/usr/share/luci/menu.d/vpnbypass.json
new file mode 100644 (file)
index 0000000..0a56f90
--- /dev/null
@@ -0,0 +1,15 @@
+{
+       "admin/vpn/vpnbypass": {
+               "title": "VPN Bypass",
+               "order": 90,
+               "action": {
+                       "type": "view",
+                       "path": "vpnbypass/overview"
+               },
+               "depends": {
+                       "acl": [
+                               "luci-app-vpnbypass"
+                       ]
+               }
+       }
+}
index 6dc1cb6a3f8c292703fda4b105f36696dfa2a9f4..3e3e06745c6b75adf9e1cdcc0e0119a6f54d6513 100644 (file)
@@ -2,39 +2,23 @@
        "luci-app-vpnbypass": {
                "description": "Grant UCI and file access for luci-app-vpnbypass",
                "read": {
-                       "cgi-io": [
-                               "exec"
-                       ],
-                       "file": {
-                               "/usr/lib/opkg/status": [
-                                       "read"
-                               ],
-                               "/etc/init.d/vpnbypass *": [
-                                       "exec"
-                               ],
-                               "/etc/init.d/dnsmasq restart *": [
-                                       "exec"
-                               ],
-                               "/usr/bin/grep *": [
-                                       "exec"
-                               ],
-                               "/usr/sbin/grep *": [
-                                       "exec"
-                               ],
-                               "/usr/sbin/iptables *": [
-                                       "exec"
+                       "ubus": {
+                               "luci.vpnbypass": [
+                                       "getInitList",
+                                       "getInitStatus"
                                ]
                        },
                        "uci": [
-                               "dhcp",
-                               "vpnbypass"
+                               "vpnbypass",
+                               "dnsmasq"
                        ]
                },
                "write": {
-                       "uci": [
-                               "dhcp",
-                               "vpnbypass"
-                       ]
+                       "ubus": {
+                               "luci.vpnbypass": [
+                                       "setInitAction"
+                               ]
+                       }
                }
        }
 }
\ No newline at end of file