luci-app-vpnbypass: client-side rendering

Signed-off-by: Stan Grishin <stangri@melmac.net>
This commit is contained in:
Stan Grishin 2021-03-25 05:26:22 +00:00
parent 73b420b09c
commit 5db241b2b5
15 changed files with 450 additions and 384 deletions

View 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

View file

@ -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()]);
}
});

View file

@ -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
});

View file

@ -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

View file

@ -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

View file

@ -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%>

View file

@ -1,9 +0,0 @@
<style type="text/css">
.btn_spinner
{
display: inline-block;
width: 0px;
height: 16px;
margin: 0 0px;
}
</style>

View file

@ -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>

View file

@ -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%>

View file

@ -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%>

View file

@ -1,116 +1,119 @@
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"
msgstr ""
#: applications/luci-app-vpnbypass/luasrc/view/vpnbypass/buttons.htm:57
#: applications/luci-app-vpnbypass/htdocs/luci-static/resources/vpnbypass/widgets.js:150
msgid "Disable"
msgstr ""
#: applications/luci-app-vpnbypass/luasrc/model/cbi/vpnbypass.lua:88
#: applications/luci-app-vpnbypass/htdocs/luci-static/resources/vpnbypass/widgets.js:146
msgid "Disabling %s service"
msgstr ""
#: 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/htdocs/luci-static/resources/vpnbypass/widgets.js:135
msgid "Enabling %s service"
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"
msgstr ""
#: applications/luci-app-vpnbypass/luasrc/view/vpnbypass/js.htm:51
msgid "Loading"
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/luasrc/model/cbi/vpnbypass.lua:78
#: applications/luci-app-vpnbypass/htdocs/luci-static/resources/vpnbypass/widgets.js:84
msgid "Not installed or not found"
msgstr ""
#: 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
#: applications/luci-app-vpnbypass/htdocs/luci-static/resources/vpnbypass/widgets.js:72
msgid "Running (version: %s)"
msgstr ""
#: applications/luci-app-vpnbypass/htdocs/luci-static/resources/view/vpnbypass/overview.js:33
msgid "Service Control"
msgstr ""
#: applications/luci-app-vpnbypass/htdocs/luci-static/resources/view/vpnbypass/overview.js:31
msgid "Service Status"
msgstr ""
#: applications/luci-app-vpnbypass/luasrc/model/cbi/vpnbypass.lua:46
msgid "Service Status [%s %s]"
msgstr ""
#: applications/luci-app-vpnbypass/luasrc/view/vpnbypass/buttons.htm:41
#: 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
#: applications/luci-app-vpnbypass/htdocs/luci-static/resources/vpnbypass/widgets.js:102
msgid "Starting %s service"
msgstr ""
#: applications/luci-app-vpnbypass/htdocs/luci-static/resources/vpnbypass/widgets.js:128
msgid "Stop"
msgstr ""
#: applications/luci-app-vpnbypass/luasrc/model/cbi/vpnbypass.lua:38
msgid "Stopped"
#: applications/luci-app-vpnbypass/htdocs/luci-static/resources/vpnbypass/widgets.js:79
msgid "Stopped (Disabled)"
msgstr ""
#: applications/luci-app-vpnbypass/luasrc/controller/vpnbypass.lua:4
msgid "VPN"
#: applications/luci-app-vpnbypass/htdocs/luci-static/resources/vpnbypass/widgets.js:76
msgid "Stopped (version: %s)"
msgstr ""
#: applications/luci-app-vpnbypass/luasrc/controller/vpnbypass.lua:7
#: applications/luci-app-vpnbypass/htdocs/luci-static/resources/vpnbypass/widgets.js:124
msgid "Stopping %s service"
msgstr ""
#: 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 ""
#: applications/luci-app-vpnbypass/luasrc/model/cbi/vpnbypass.lua:55
msgid "VPN Bypass Rules"
msgstr ""
#: applications/luci-app-vpnbypass/luasrc/model/cbi/vpnbypass.lua:44
msgid "VPN Bypass Settings"
msgstr ""

View file

@ -1,4 +1,5 @@
#!/bin/sh
/etc/init.d/rpcd reload
rm -rf /var/luci-modulecache/; rm -f /var/luci-indexcache;
exit 0

View file

@ -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

View file

@ -0,0 +1,15 @@
{
"admin/vpn/vpnbypass": {
"title": "VPN Bypass",
"order": 90,
"action": {
"type": "view",
"path": "vpnbypass/overview"
},
"depends": {
"acl": [
"luci-app-vpnbypass"
]
}
}
}

View 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"
]
}
}
}
}