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
--- /dev/null
+// 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()]);
+ }
+});
--- /dev/null
+// 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', {}, ' ');
+ 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'));
+
+ 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
+});
+++ /dev/null
-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
+++ /dev/null
-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
+++ /dev/null
-<%# 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>
-
-
-
-
- <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
+++ /dev/null
-<style type="text/css">
- .btn_spinner
- {
- display: inline-block;
- width: 0px;
- height: 16px;
- margin: 0 0px;
- }
-</style>
+++ /dev/null
-<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>
+++ /dev/null
-<%#
-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%>
+++ /dev/null
-<%#
-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%>
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 ""
#!/bin/sh
+
+/etc/init.d/rpcd reload
rm -rf /var/luci-modulecache/; rm -f /var/luci-indexcache;
exit 0
-
--- /dev/null
+#!/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
--- /dev/null
+{
+ "admin/vpn/vpnbypass": {
+ "title": "VPN Bypass",
+ "order": 90,
+ "action": {
+ "type": "view",
+ "path": "vpnbypass/overview"
+ },
+ "depends": {
+ "acl": [
+ "luci-app-vpnbypass"
+ ]
+ }
+ }
+}
"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