Merge pull request #4943 from stangri/21.02-luci-app-vpnbypass
[21.02] luci-app-vpnbypass: client-side rendering
This commit is contained in:
commit
9cf8f15fae
15 changed files with 450 additions and 384 deletions
|
@ -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
|
||||
|
|
|
@ -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()]);
|
||||
}
|
||||
});
|
|
@ -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', {}, ' ');
|
||||
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
|
||||
});
|
|
@ -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
|
|
@ -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
|
|
@ -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>
|
||||
|
||||
|
||||
|
||||
|
||||
<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%>
|
|
@ -1,9 +0,0 @@
|
|||
<style type="text/css">
|
||||
.btn_spinner
|
||||
{
|
||||
display: inline-block;
|
||||
width: 0px;
|
||||
height: 16px;
|
||||
margin: 0 0px;
|
||||
}
|
||||
</style>
|
|
@ -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>
|
|
@ -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%>
|
|
@ -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%>
|
|
@ -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 ""
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#!/bin/sh
|
||||
|
||||
/etc/init.d/rpcd reload
|
||||
rm -rf /var/luci-modulecache/; rm -f /var/luci-indexcache;
|
||||
exit 0
|
||||
|
||||
|
|
105
applications/luci-app-vpnbypass/root/usr/libexec/rpcd/luci.vpnbypass
Executable file
105
applications/luci-app-vpnbypass/root/usr/libexec/rpcd/luci.vpnbypass
Executable 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
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"admin/vpn/vpnbypass": {
|
||||
"title": "VPN Bypass",
|
||||
"order": 90,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "vpnbypass/overview"
|
||||
},
|
||||
"depends": {
|
||||
"acl": [
|
||||
"luci-app-vpnbypass"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue