luci-app-vssr: bump to v1.22

Signed-off-by: CN_SZTL <cnsztl@project-openwrt.eu.org>
This commit is contained in:
jerrykuku 2021-01-19 19:20:44 +08:00 committed by CN_SZTL
parent 77f6cfb52d
commit 296e65d890
No known key found for this signature in database
GPG Key ID: 6850B6345C862176
26 changed files with 1099 additions and 853 deletions

View File

@ -1,8 +1,8 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-vssr
PKG_VERSION:=1.21
PKG_RELEASE:=20210114
PKG_VERSION:=1.22
PKG_RELEASE:=20210119
include $(INCLUDE_DIR)/package.mk
@ -46,7 +46,7 @@ define Package/luci-app-vssr
SUBMENU:=3. Applications
TITLE:=A New SS/SSR/V2Ray/Trojan LuCI interface
PKGARCH:=all
DEPENDS:=+shadowsocksr-libev-alt +ipset +ip-full +iptables-mod-tproxy +dnsmasq-full +coreutils +coreutils-base64 +bash +pdnsd-alt +wget-ssl +luasocket +lua-maxminddb \
DEPENDS:=+shadowsocksr-libev-alt +ipset +ip-full +iptables-mod-tproxy +dnsmasq-full +coreutils +coreutils-base64 +bash +pdnsd-alt +wget-ssl +lua +luasocket +lua-maxminddb \
+shadowsocks-libev-ss-local +shadowsocksr-libev-ssr-local +shadowsocks-libev-ss-redir +simple-obfs \
+PACKAGE_$(PKG_NAME)_INCLUDE_V2ray_plugin:v2ray-plugin \
+PACKAGE_$(PKG_NAME)_INCLUDE_V2ray:v2ray \

View File

@ -12,12 +12,13 @@ function index()
entry({'admin', 'services', 'vssr', 'client'}, cbi('vssr/client'), _('SSR Client'), 10).leaf = true -- 基本设置
entry({'admin', 'services', 'vssr', 'servers'}, cbi('vssr/servers'), _('Severs Nodes'), 11).leaf = true -- 服务器节点
entry({'admin', 'services', 'vssr', 'servers'}, arcombine(cbi('vssr/servers'), cbi('vssr/client-config')), _('Severs Nodes'), 11).leaf = true -- 编辑节点
entry({'admin', 'services', 'vssr', 'control'}, cbi('vssr/control'), _('Access Control'), 12).leaf = true -- 访问控制
entry({'admin', 'services', 'vssr', 'router'}, cbi('vssr/router'), _('Router Config'), 13).leaf = true -- 访问控制
entry({'admin', 'services', 'vssr', 'subscribe_config'}, cbi('vssr/subscribe-config', {hideapplybtn = true, hidesavebtn = true, hideresetbtn = true}), _('Subscribe'), 12).leaf = true -- 订阅设置
entry({'admin', 'services', 'vssr', 'control'}, cbi('vssr/control'), _('Access Control'), 13).leaf = true -- 访问控制
entry({'admin', 'services', 'vssr', 'router'}, cbi('vssr/router'), _('Router Config'), 14).leaf = true -- 访问控制
if nixio.fs.access('/usr/bin/v2ray/v2ray') or nixio.fs.access('/usr/bin/v2ray') or nixio.fs.access('/usr/bin/xray') or nixio.fs.access('/usr/bin/xray/xray') then
entry({'admin', 'services', 'vssr', 'socks5'}, cbi('vssr/socks5'), _('Socks5'), 14).leaf = true -- Socks5代理
entry({'admin', 'services', 'vssr', 'socks5'}, cbi('vssr/socks5'), _('Local Proxy'), 15).leaf = true -- Socks5代理
end
entry({'admin', 'services', 'vssr', 'advanced'}, cbi('vssr/advanced'), _('Advanced Settings'), 15).leaf = true -- 高级设置
entry({'admin', 'services', 'vssr', 'advanced'}, cbi('vssr/advanced'), _('Advanced Settings'), 16).leaf = true -- 高级设置
elseif nixio.fs.access('/usr/bin/ssr-server') then
entry({'admin', 'services', 'vssr'}, alias('admin', 'services', 'vssr', 'server'), _('vssr'), 10).dependent = true
else
@ -240,11 +241,7 @@ function refresh_data()
local icount = 0
if set == 'gfw_data' then
if nixio.fs.access('/usr/bin/wget-ssl') then
refresh_cmd = 'wget-ssl --no-check-certificate https://cdn.jsdelivr.net/gh/gfwlist/gfwlist/gfwlist.txt -O /tmp/gfw.b64'
else
refresh_cmd = 'wget -O /tmp/gfw.b64 http://iytc.net/tools/list.b64'
end
refresh_cmd = 'wget-ssl --no-check-certificate https://cdn.jsdelivr.net/gh/gfwlist/gfwlist/gfwlist.txt -O /tmp/gfw.b64'
sret = luci.sys.call(refresh_cmd .. ' 2>/dev/null')
if sret == 0 then
luci.sys.call('/usr/bin/vssr-gfw')
@ -265,7 +262,7 @@ function refresh_data()
retstring = '-1'
end
elseif set == 'ip_data' then
refresh_cmd ="wget -O- 'https://ispip.clang.cn/all_cn.txt' > /tmp/china_ssr.txt 2>/dev/null"
refresh_cmd = "wget-ssl -O- 'https://ispip.clang.cn/all_cn.txt' > /tmp/china_ssr.txt 2>/dev/null"
sret = luci.sys.call(refresh_cmd)
icount = luci.sys.exec('cat /tmp/china_ssr.txt | wc -l')
if sret == 0 and tonumber(icount) > 1000 then
@ -282,12 +279,8 @@ function refresh_data()
luci.sys.exec('rm -f /tmp/china_ssr.txt ')
else
local need_process = 0
if nixio.fs.access('/usr/bin/wget-ssl') then
refresh_cmd = 'wget-ssl --no-check-certificate -O - https://easylist-downloads.adblockplus.org/easylistchina+easylist.txt > /tmp/adnew.conf'
need_process = 1
else
refresh_cmd = 'wget -O /tmp/ad.conf http://iytc.net/tools/ad.conf'
end
refresh_cmd = 'wget-ssl --no-check-certificate -O - https://easylist-downloads.adblockplus.org/easylistchina+easylist.txt > /tmp/adnew.conf'
need_process = 1
sret = luci.sys.call(refresh_cmd .. ' 2>/dev/null')
if sret == 0 then
if need_process == 1 then
@ -319,4 +312,4 @@ function refresh_data()
end
luci.http.prepare_content('application/json')
luci.http.write_json({ret = retstring, retcount = icount})
end
end

View File

@ -61,59 +61,6 @@ o.datatype = 'uinteger'
o:depends('enable_switch', '1')
o.default = 3
-- [[ 节点订阅 ]]--
s = m:section(TypedSection, 'server_subscribe', translate('Servers subscription and manage'))
s.anonymous = true
o = s:option(Flag, 'auto_update', translate('Auto Update'))
o.rmempty = false
o.description = translate('Auto Update Server subscription, GFW list and CHN route')
o = s:option(ListValue, 'auto_update_time', translate('Update time (every day)'))
for t = 0, 23 do
o:value(t, t .. ':00')
end
o.default = 2
o.rmempty = false
o = s:option(DynamicList, 'subscribe_url', translate('Subscribe URL'))
o.rmempty = true
o.description = translate('You can manually add group names in front of the URL, splited by ,')
o = s:option(Value, "filter_words", translate("Subscribe Filter Words"))
o.rmempty = true
o.description = translate("Filter Words splited by /")
o = s:option(Flag, 'proxy', translate('Through proxy update'))
o.rmempty = false
o.description = translate('Through proxy update list, Not Recommended ')
o = s:option(DummyValue, '', '')
o.rawhtml = true
o.template = 'vssr/update_subscribe'
o = s:option(Button, 'delete', translate('Delete All Subscribe Severs'))
o.inputstyle = 'reset'
o.description = string.format(translate('Server Count') .. ': %d', server_count)
o.write = function()
uci:delete_all(
'vssr',
'servers',
function(s)
if s.hashkey or s.isSubscribe then
return true
else
return false
end
end
)
uci:save('vssr')
uci:commit('vssr')
luci.sys.exec('/etc/init.d/vssr restart')
luci.http.redirect(luci.dispatcher.build_url('admin', 'services', 'vssr', 'servers'))
return
end
-- [[ adblock ]]--
s = m:section(TypedSection, 'global', translate('adblock settings'))

View File

@ -93,15 +93,7 @@ for i, v in pairs(route_name) do
o = s:option(ListValue, v, translate(route_label[i]))
o:value('nil', translate('Same as Main Server'))
for _, key in pairs(server_table) do
if(v == 'tw_video_server' and key.flag == "tw") then
o:value(key.name, key.gname)
end
if(v == 'tvb_server' and key.flag == "hk") then
o:value(key.name, key.gname)
end
if(v ~= 'tvb_server' and v ~= 'tw_video_server') then
o:value(key.name, key.gname)
end
o:value(key.name, key.gname)
end
o:depends('v2ray_flow', '1')
o.default = 'nil'

View File

@ -2,7 +2,8 @@ local fs = require 'nixio.fs'
local conffile = '/tmp/ssrpro.log'
f = SimpleForm('logview')
f.submit = false
f.reset = false
t = f:field(TextValue, 'conf')
t.rmempty = true
t.rows = 20

View File

@ -1,31 +1,31 @@
local m, s, o
local NXFS = require 'nixio.fs'
local router_table = {
yotube = {
a1 = {
name = 'youtube',
des = 'Youtube Domain'
},
tw_video = {
a2 = {
name = 'tw_video',
des = 'Tw Video Domain'
},
netflix = {
a3 = {
name = 'netflix',
des = 'Netflix Domain'
},
disney = {
a4 = {
name = 'disney',
des = 'Disney+ Domain'
},
prime = {
a5 = {
name = 'prime',
des = 'Prime Video Domain'
},
tvb = {
a6 = {
name = 'tvb',
des = 'TVB Domain'
},
custom = {
a7 = {
name = 'custom',
des = 'Custom Domain'
}
@ -39,6 +39,7 @@ for _, v in pairs(router_table) do
local conf = '/etc/vssr/'.. v.name ..'_domain.list'
o = s:taboption(v.name, TextValue, v.name ..'conf')
o.rows = 13
o.description = translate('↑ Put your domain list here')
o.wrap = 'off'
o.rmempty = true
o.cfgvalue = function(self, section)
@ -50,6 +51,23 @@ for _, v in pairs(router_table) do
o.remove = function(self, section, value)
NXFS.writefile(conf, '')
end
local conf1 = '/etc/vssr/'.. v.name ..'_ip.list'
o = s:taboption(v.name, TextValue, v.name ..'conf1')
o.rows = 13
o.wrap = 'off'
o.description = translate('↑ Put your IP list here')
o.rmempty = true
o.cfgvalue = function(self, section)
return NXFS.readfile(conf1) or ' '
end
o.write = function(self, section, value)
NXFS.writefile(conf1, value:gsub('\r\n', '\n'))
end
o.remove = function(self, section, value)
NXFS.writefile(conf1, '')
end
end
return m

View File

@ -0,0 +1,77 @@
local vssr = 'vssr'
local uci = luci.model.uci.cursor()
local server_table = {}
local gfwmode = 0
local gfw_count = 0
local ip_count = 0
local ad_count = 0
local server_count = 0
local sys = require 'luci.sys'
uci:foreach(
'vssr',
'servers',
function(s)
server_count = server_count + 1
end
)
m = Map(vssr)
-- [[ 节点订阅 ]]--
s = m:section(TypedSection, 'server_subscribe', translate('Servers subscription and manage'))
s.anonymous = true
o = s:option(Flag, 'auto_update', translate('Auto Update'))
o.rmempty = false
o.description = translate('Auto Update Server subscription, GFW list and CHN route')
o = s:option(ListValue, 'auto_update_time', translate('Update time (every day)'))
for t = 0, 23 do
o:value(t, t .. ':00')
end
o.default = 2
o.rmempty = false
o = s:option(DynamicList, 'subscribe_url', translate('Subscribe URL'))
o.rmempty = true
o.description = translate('You can manually add group names in front of the URL, splited by ,')
o = s:option(Value, 'filter_words', translate('Subscribe Filter Words'))
o.rmempty = true
o.description = translate('Filter Words splited by /')
o = s:option(Flag, 'proxy', translate('Through proxy update'))
o.rmempty = false
o.description = translate('Through proxy update list, Not Recommended ')
o = s:option(DummyValue, '', '')
o.rawhtml = true
o.template = 'vssr/update_subscribe'
o = s:option(Button, 'delete', translate('Delete All Subscribe Severs'))
o.inputstyle = 'reset'
o.description = string.format(translate('Server Count') .. ': %d', server_count)
o.write = function()
uci:delete_all(
'vssr',
'servers',
function(s)
if s.hashkey or s.isSubscribe then
return true
else
return false
end
end
)
uci:save('vssr')
uci:commit('vssr')
luci.sys.exec('/etc/init.d/vssr restart')
luci.http.redirect(luci.dispatcher.build_url('admin', 'services', 'vssr', 'servers'))
return
end
return m

View File

@ -2,304 +2,305 @@
<script type="text/javascript">//<![CDATA[
const GET_FLAG_URL = '<%=luci.dispatcher.build_url("admin", "services", "vssr","flag")%>';
var sid;
const GET_FLAG_URL = '<%=luci.dispatcher.build_url("admin", "services", "vssr","flag")%>';
var sid;
function getFlag(remark, hosts, sid) {
XHR.get(GET_FLAG_URL,
{ host: hosts, remark: remark },
function (x, rv) {
el('.flag').value = rv.flag.replace(/[\r\n]/g, "");
function getFlag(remark, hosts, sid) {
XHR.get(GET_FLAG_URL,
{ host: hosts, remark: remark },
function (x, rv) {
el('.flag').value = rv.flag.replace(/[\r\n]/g, "");
}
);
}
function padright(str, cnt, pad) {
return str + Array(cnt + 1).join(pad);
}
function b64EncodeUnicode(str) {
return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function (match, p1) {
return String.fromCharCode('0x' + p1);
}));
}
function b64encutf8safe(str) {
return b64EncodeUnicode(str).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, '');
}
function b64DecodeUnicode(str) {
return decodeURIComponent(Array.prototype.map.call(atob(str), function (c) {
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
}).join(''));
}
function b64decutf8safe(str) {
var l;
str = str.replace(/-/g, "+").replace(/_/g, "/");
l = str.length;
l = (4 - l % 4) % 4;
if (l)
str = padright(str, l, "=");
return b64DecodeUnicode(str);
}
function b64encsafe(str) {
return btoa(str).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, '')
}
function b64decsafe(str) {
var l;
str = str.replace(/-/g, "+").replace(/_/g, "/");
l = str.length;
l = (4 - l % 4) % 4;
if (l)
str = padright(str, l, "=");
try {
return atob(str);
} catch (err) {
return str;
}
);
}
function padright(str, cnt, pad) {
return str + Array(cnt + 1).join(pad);
}
function b64EncodeUnicode(str) {
return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function (match, p1) {
return String.fromCharCode('0x' + p1);
}));
}
function b64encutf8safe(str) {
return b64EncodeUnicode(str).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, '');
}
function b64DecodeUnicode(str) {
return decodeURIComponent(Array.prototype.map.call(atob(str), function (c) {
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
}).join(''));
}
function b64decutf8safe(str) {
var l;
str = str.replace(/-/g, "+").replace(/_/g, "/");
l = str.length;
l = (4 - l % 4) % 4;
if (l)
str = padright(str, l, "=");
return b64DecodeUnicode(str);
}
function b64encsafe(str) {
return btoa(str).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, '')
}
function b64decsafe(str) {
var l;
str = str.replace(/-/g, "+").replace(/_/g, "/");
l = str.length;
l = (4 - l % 4) % 4;
if (l)
str = padright(str, l, "=");
try {
return atob(str);
} catch (err) {
return str;
}
}
function dictvalue(d, key) {
var v = d[key];
if (typeof (v) == 'undefined' || v == '')
return '';
return b64decsafe(v);
}
function el(id) {
return document.getElementById('cbid.vssr.' + sid + id);
}
function import_ssr_url(btn, urlname, sids) {
sid = sids;
var s = document.getElementById(urlname + '-status');
if (!s)
return false;
var ssrurl = prompt("<%:Paste Node Link Here%> ssr:// | ss:// | vmess:// | trojan://", "");
if (ssrurl == null || ssrurl == "") {
s.innerHTML = "<font color='red'><%:User Cancel%></font>";
return false;
}
s.innerHTML = "";
//var ssu = ssrurl.match(/ssr:\/\/([A-Za-z0-9_-]+)/i);
var ssu = ssrurl.split('://');
console.log(ssu.length);
if ((ssu[0] != "ssr" && ssu[0] != "ss" && ssu[0] != "vmess" && ssu[0] != "trojan") || ssu[1] == "") {
s.innerHTML = "<font color='red'><%:Invalid Format%></font>";
return false;
function dictvalue(d, key) {
var v = d[key];
if (typeof (v) == 'undefined' || v == '')
return '';
return b64decsafe(v);
}
function el(id) {
return document.getElementById('cbid.vssr.' + sid + id);
}
var event = document.createEvent("HTMLEvents");
event.initEvent("change", true, true);
var rema = "";
if (ssu[0] == "ssr") {
var sstr = b64decsafe(ssu[1]);
var ploc = sstr.indexOf("/?");
el('.type').value = "ssr";
el('.type').dispatchEvent(event);
var url0, param = "";
if (ploc > 0) {
url0 = sstr.substr(0, ploc);
param = sstr.substr(ploc + 2);
}
var ssm = url0.match(/^(.+):([^:]+):([^:]*):([^:]+):([^:]*):([^:]+)/);
if (!ssm || ssm.length < 7)
function import_ssr_url(btn, urlname, sids) {
sid = sids;
var s = document.getElementById(urlname + '-status');
if (!s)
return false;
var ssrurl = prompt("<%:Paste Node Link Here%> ssr:// | ss:// | vmess:// | trojan://", "");
if (ssrurl == null || ssrurl == "") {
s.innerHTML = "<font color='red'><%:User Cancel%></font>";
return false;
var pdict = {};
if (param.length > 2) {
var a = param.split('&');
for (var i = 0; i < a.length; i++) {
var b = a[i].split('=');
pdict[decodeURIComponent(b[0])] = decodeURIComponent(b[1] || '');
}
}
el('.server').value = ssm[1];
el('.server_port').value = ssm[2];
el('.protocol').value = ssm[3];
el('.encrypt_method').value = ssm[4];
el('.obfs').value = ssm[5];
el('.password').value = b64decsafe(ssm[6]);
el('.obfs_param').value = dictvalue(pdict, 'obfsparam');
el('.protocol_param').value = dictvalue(pdict, 'protoparam');
s.innerHTML = "";
//var ssu = ssrurl.match(/ssr:\/\/([A-Za-z0-9_-]+)/i);
var ssu = ssrurl.split('://');
console.log(ssu.length);
var rem = pdict['remarks'];
if (typeof (rem) != 'undefined' && rem != '' && rem.length > 0)
rema = b64decutf8safe(rem);
el('.alias').value = b64decutf8safe(rem);
getFlag(rema, ssm[1], sid); //get flag iso code
s.innerHTML = "<font color='green'><%:Import%>ShadowsocksR<%:Configuration Succeeded%></font>";
return false;
} else if (ssu[0] == "ss") {
var url0, param = "";
var sipIndex = ssu[1].indexOf("@");
var ploc = ssu[1].indexOf("#");
if (ploc > 0) {
url0 = ssu[1].substr(0, ploc);
param = ssu[1].substr(ploc + 1);
} else {
url0 = ssu[1];
}
var event = document.createEvent("HTMLEvents");
event.initEvent("change", true, true);
var rema = "";
switch (ssu[0]) {
case "ssr":
var sstr = b64decsafe(ssu[1]);
var ploc = sstr.indexOf("/?");
el('.type').value = "ssr";
el('.type').dispatchEvent(event);
var url0, param = "";
if (ploc > 0) {
url0 = sstr.substr(0, ploc);
param = sstr.substr(ploc + 2);
}
var ssm = url0.match(/^(.+):([^:]+):([^:]*):([^:]+):([^:]*):([^:]+)/);
if (!ssm || ssm.length < 7)
return false;
var pdict = {};
if (param.length > 2) {
var a = param.split('&');
for (var i = 0; i < a.length; i++) {
var b = a[i].split('=');
pdict[decodeURIComponent(b[0])] = decodeURIComponent(b[1] || '');
}
}
el('.server').value = ssm[1];
el('.server_port').value = ssm[2];
el('.protocol').value = ssm[3];
el('.encrypt_method').value = ssm[4];
el('.obfs').value = ssm[5];
el('.password').value = b64decsafe(ssm[6]);
el('.obfs_param').value = dictvalue(pdict, 'obfsparam');
el('.protocol_param').value = dictvalue(pdict, 'protoparam');
if (sipIndex != -1) {
// SIP002
var userInfo = b64decsafe(url0.substr(0, sipIndex));
var temp = url0.substr(sipIndex + 1).split("/?");
var serverInfo = temp[0].split(":");
var server = serverInfo[0];
var port = serverInfo[1];
var method, password, plugin, pluginOpts;
if (temp[1]) {
var pluginInfo = decodeURIComponent(temp[1]);
var pluginIndex = pluginInfo.indexOf(";");
var pluginNameInfo = pluginInfo.substr(0, pluginIndex);
plugin = pluginNameInfo.substr(pluginNameInfo.indexOf("=") + 1)
pluginOpts = pluginInfo.substr(pluginIndex + 1);
}
var rem = pdict['remarks'];
if (typeof (rem) != 'undefined' && rem != '' && rem.length > 0)
rema = b64decutf8safe(rem);
el('.alias').value = b64decutf8safe(rem);
getFlag(rema, ssm[1], sid); //get flag iso code
s.innerHTML = "<font color='green'><%:Import%>ShadowsocksR<%:Configuration Succeeded%></font>";
break;
case "ss":
var url0, param = "";
var sipIndex = ssu[1].indexOf("@");
var ploc = ssu[1].indexOf("#");
if (ploc > 0) {
url0 = ssu[1].substr(0, ploc);
param = ssu[1].substr(ploc + 1);
} else {
url0 = ssu[1];
}
var userInfoSplitIndex = userInfo.indexOf(":");
if (userInfoSplitIndex != -1) {
method = userInfo.substr(0, userInfoSplitIndex);
password = userInfo.substr(userInfoSplitIndex + 1);
}
el('.type').value = "ss";
el('.type').dispatchEvent(event);
el('.server').value = server;
el('.server_port').value = port;
el('.password').value = password || "";
el('.encrypt_method_ss').value = method || "";
el('.plugin').value = plugin || "";
el('.plugin_opts').value = pluginOpts || "";
} else {
var sstr = b64decsafe(url0);
el('.type').value = "ss";
el('.type').dispatchEvent(event);
var team = sstr.split('@');
console.log(param);
var part1 = team[0].split(':');
var part2 = team[1].split(':');
el('.server').value = part2[0];
el('.server_port').value = part2[1];
el('.password').value = part1[1];
el('.encrypt_method_ss').value = part1[0];
el('.plugin').value = "";
el('.plugin_opts').value = "";
}
if (sipIndex != -1) {
// SIP002
var userInfo = b64decsafe(url0.substr(0, sipIndex));
var temp = url0.substr(sipIndex + 1).split("/?");
if(temp.length == 1){
temp = url0.substr(sipIndex + 1).split("?");
}
var serverInfo = temp[0].split(":");
var server = serverInfo[0];
var port = serverInfo[1];
var method, password, plugin, pluginOpts;
if (temp[1]) {
var pluginInfo = decodeURIComponent(temp[1]);
var pluginIndex = pluginInfo.indexOf(";");
var pluginNameInfo = pluginInfo.substr(0, pluginIndex);
plugin = pluginNameInfo.substr(pluginNameInfo.indexOf("=") + 1)
pluginOpts = pluginInfo.substr(pluginIndex + 1);
}
if (param != undefined) {
rema = decodeURI(param)
el('.alias').value = decodeURI(param);
}
getFlag(rema, server, sid); //get flag iso code
s.innerHTML = "<font color='green'><%:Import%>Shadowsocks<%:Configuration Succeeded%></font>";
return false;
} else if (ssu[0] == "trojan") {
var ploc = ssu[1].indexOf("#");
if (ploc > 0) {
url0 = ssu[1].substr(0, ploc);
param = ssu[1].substr(ploc + 1);
} else {
url0 = ssu[1]
}
var sstr = b64decsafe(url0);
el('.type').value = "trojan";
el('.type').dispatchEvent(event);
var team = sstr.split('@');
console.log(team);
var part1 = team[0].split(':');
var part2 = team[1].split(':');
var others = part2[1].split('?');
var queryParam = {}
if (others.length > 1) {
var queryParams = others[1]
var queryArray = queryParams.split('&')
for (i = 0; i < queryArray.length; i++) {
var params = queryArray[i].split('=');
queryParam[decodeURIComponent(params[0])] = decodeURIComponent(params[1] || '');
}
}
el('.server').value = part2[0];
el('.server_port').value = others[0];
el('.password').value = part1[0];
if (queryParam.peer || queryParam.sni) {
el('.tls').checked = true;
el('.peer').value = queryParam.peer || queryParam.sni;
}
var userInfoSplitIndex = userInfo.indexOf(":");
if (userInfoSplitIndex != -1) {
method = userInfo.substr(0, userInfoSplitIndex);
password = userInfo.substr(userInfoSplitIndex + 1);
}
el('.type').value = "ss";
el('.type').dispatchEvent(event);
el('.server').value = server;
el('.server_port').value = port;
el('.password').value = password || "";
el('.encrypt_method_ss').value = method || "";
el('.plugin').value = plugin || "";
el('.plugin_opts').value = pluginOpts || "";
} else {
var sstr = b64decsafe(url0);
el('.type').value = "ss";
el('.type').dispatchEvent(event);
var team = sstr.split('@');
console.log(param);
var part1 = team[0].split(':');
var part2 = team[1].split(':');
el('.server').value = part2[0];
el('.server_port').value = part2[1];
el('.password').value = part1[1];
el('.encrypt_method_ss').value = part1[0];
el('.plugin').value = "";
el('.plugin_opts').value = "";
}
if (param != undefined) {
rema = decodeURI(param)
el('.alias').value = decodeURI(param);
}
getFlag(rema, part2[0], sid); //get flag iso code
if (param != undefined) {
rema = decodeURI(param)
el('.alias').value = decodeURI(param);
}
getFlag(rema, server, sid); //get flag iso code
s.innerHTML = "<font color='green'><%:Import%>Shadowsocks<%:Configuration Succeeded%></font>";
break;
case "trojan":
var ploc = ssu[1].indexOf("#");
if (ploc > 0) {
url0 = ssu[1].substr(0, ploc);
param = ssu[1].substr(ploc + 1);
} else {
url0 = ssu[1]
}
var sstr = b64decsafe(url0);
el('.type').value = "trojan";
el('.type').dispatchEvent(event);
var team = sstr.split('@');
console.log(team);
var part1 = team[0].split(':');
var part2 = team[1].split(':');
var others = part2[1].split('?');
var queryParam = {}
if (others.length > 1) {
var queryParams = others[1]
var queryArray = queryParams.split('&')
for (i = 0; i < queryArray.length; i++) {
var params = queryArray[i].split('=');
queryParam[decodeURIComponent(params[0])] = decodeURIComponent(params[1] || '');
}
}
el('.server').value = part2[0];
el('.server_port').value = others[0];
el('.password').value = part1[0];
if (queryParam.peer || queryParam.sni) {
el('.tls').checked = true;
el('.peer').value = queryParam.peer || queryParam.sni;
}
s.innerHTML = "<font color='green'>导入Trojan<%:Configuration Succeeded%></font>";
return false;
} else if (ssu[0] == "vmess") {
var sstr = b64DecodeUnicode(ssu[1]);
var ploc = sstr.indexOf("/?");
el('.type').value = "v2ray";
el('.type').dispatchEvent(event);
var url0, param = "";
if (ploc > 0) {
url0 = sstr.substr(0, ploc);
param = sstr.substr(ploc + 2);
}
var ssm = JSON.parse(sstr);
el('.alias').value = ssm.ps;
el('.server').value = ssm.add;
el('.server_port').value = ssm.port;
el('.alter_id').value = ssm.aid;
el('.vmess_id').value = ssm.id;
el('.transport').value = ssm.net;
el('.transport').dispatchEvent(event);
if (ssm.net == "tcp") {
try {
el('.http_host').value = ssm.host;
el('.http_path').value = ssm.path;
} catch (err) { }
if (param != undefined) {
rema = decodeURI(param)
el('.alias').value = decodeURI(param);
}
getFlag(rema, part2[0], sid); //get flag iso code
s.innerHTML = "<font color='green'>导入Trojan<%:Configuration Succeeded%></font>";
break;
case "vmess":
var sstr = b64DecodeUnicode(ssu[1]);
var ploc = sstr.indexOf("/?");
el('.type').value = "v2ray";
el('.type').dispatchEvent(event);
var url0, param = "";
if (ploc > 0) {
url0 = sstr.substr(0, ploc);
param = sstr.substr(ploc + 2);
}
var ssm = JSON.parse(sstr);
el('.alias').value = ssm.ps;
el('.server').value = ssm.add;
el('.server_port').value = ssm.port;
el('.alter_id').value = ssm.aid;
el('.vmess_id').value = ssm.id;
el('.transport').value = ssm.net;
el('.transport').dispatchEvent(event);
if (ssm.net == "tcp") {
try {
el('.http_host').value = ssm.host;
el('.http_path').value = ssm.path;
} catch (err) { }
}
if (ssm.net == "ws") {
try {
el('.ws_host').value = ssm.host;
el('.ws_path').value = ssm.path;
} catch (err) { }
}
if (ssm.net == "h2") {
try {
el('.h2_host').value = ssm.host;
el('.h2_path').value = ssm.path;
} catch (err) { }
}
if (ssm.net == "quic") {
try {
el('.quic_security').value = ssm.securty;
el('.quic_key').value = ssm.key;
} catch (err) { }
}
if (ssm.net == "kcp") {
try {
el('.kcp_guise').value = ssm.type;
} catch (err) { }
try {
el('.seed').value = ssm.seed;
} catch (err) { }
}
if (ssm.tls == "tls") {
try {
el('.tls').checked = true;
el('.tls').dispatchEvent(event);
el('.tls_host').value = ssm.host;
} catch (err) { }
}
el('.mux').checked = true;
el('.mux').dispatchEvent(event);
getFlag(ssm.ps, ssm.add, sid); //get flag iso code
s.innerHTML = "<font color='green'><%:Import%>V2ray<%:Configuration Succeeded%></font>";
break;
default:
s.innerHTML = "<font color='red'><%:Invalid Format%></font>";
}
if (ssm.net == "ws") {
try {
el('.ws_host').value = ssm.host;
el('.ws_path').value = ssm.path;
} catch (err) { }
}
if (ssm.net == "h2") {
try {
el('.h2_host').value = ssm.host;
el('.h2_path').value = ssm.path;
} catch (err) { }
}
if (ssm.net == "quic") {
try {
el('.quic_security').value = ssm.securty;
el('.quic_key').value = ssm.key;
} catch (err) { }
}
if (ssm.net == "kcp") {
try {
el('.kcp_guise').value = ssm.type;
} catch (err) { }
try {
el('.seed').value = ssm.seed;
} catch (err) { }
}
if (ssm.tls == "tls") {
try {
el('.tls').checked = true;
el('.tls').dispatchEvent(event);
el('.tls_host').value = ssm.host;
} catch (err) { }
}
el('.mux').checked = true;
el('.mux').dispatchEvent(event);
getFlag(ssm.ps, ssm.add, sid); //get flag iso code
s.innerHTML = "<font color='green'><%:Import%>V2ray<%:Configuration Succeeded%></font>";
return false;
}
}
//]]></script>
<input type="button" class="cbi-button cbi-button-apply" value="<%:Import Configuration%>"

View File

@ -26,6 +26,9 @@
function resize() {
wW = $(window).width();
lw = $(".main-left").width()
if(lw == undefined){
lw = 0;
}
$(".status-bar").width(wW - lw);
$(".status-bar .flag").width($(".status-bar .flag").height() / 3 * 4);

View File

@ -1,6 +1,6 @@
<% math.randomseed(os.time()) %>
<link rel="stylesheet" href="/luci-static/vssr/css/vssr.css?v=<%=math.random(1,100000)%>">
<script src="<%=media%>/js/jquery.min.js"></script>
<script src="/luci-static/vssr/jquery.min.js"></script>
<div class="pure-g status">
<div class="pure-u-1-4">

View File

@ -3,87 +3,113 @@
<script type="text/javascript" src="/luci-static/vssr/polyfill.js?v=1.16"></script>
<!-- tblsection -->
<fieldset class="cbi-section" id="cbi-<%=self.config%>-<%=self.sectiontype%>">
<button class="cbi-button cbi-button-check "><%:Ping All Servers%></button><span class="panel-title">总计
<%- print(self.des)-%>个节点</span>
<div class="ssr-title">
<button class="cbi-button cbi-button-check ">
<%:Ping All Servers%>
</button><span class="panel-title">总计
<%- print(self.des)-%>个节点
</span>
<div style="clear:both;"></div>
</div>
<div class="cbi-section-node">
<%- local count = 0 -%>
<div class="cbi-section-table pure-g p-in5">
<%- local count=0 -%>
<div class="cbi-section-table pure-g p-in5">
<%- local isempty=true for i, k in ipairs(self.serverTable) do section=k isempty=false local
fc=(section.name==self.current) and "fast" or "" local flag_icon=(section.flag ~=nil)
and "flag-icon-" .. section.flag or "flag-icon-un" local switch_enable=(section.switch_enable=="1" )
and "" or "hidden" scope={ valueheader="cbi/cell_valueheader" , valuefooter="cbi/cell_valuefooter" }
-%>
<%- local isempty = true
for i, k in ipairs(self.serverTable) do
section = k
isempty = false
local fc = (section.name == self.current) and "fast" or ""
local flag_icon = (section.flag ~= nil) and "flag-icon-" .. section.flag or "flag-icon-un"
local switch_enable = (section.switch_enable == "1") and "" or "hidden"
scope = { valueheader = "cbi/cell_valueheader", valuefooter = "cbi/cell_valuefooter" }
-%>
<div class="pure-u-1-5">
<div class="cbi-section-table-row <%=fc %> " id="cbi-<%=self.config%>-<%=section.name%>" data-flag="<%=section.flag%>" server="<%=section.server%>" server_port="<%=section.server_port%>">
<div class="loadings hide"></div>
<div class="incon type flag-icon <%=flag_icon %>" data-setction="<%=section.name%>">
<img class="pure-imgw <%=switch_enable %>" src="/luci-static/vssr/img/switch.png">
<div class="tp"><%=section.type%></div>
</div>
<div class="incon alias"><%=section.alias%></div>
<%- if self.extedit or self.addremove then -%>
<div class="cbi-section-table-cell">
<div class="host_con"></div>
<a class="cbi-button ssr-button" type="button" value="" onclick="apply_node('<%=section.name%>')"
alt="<%:Apply%>" title="<%:Apply%>"><span class="icon-use"></span> <%:Apply%></a>
<%- if self.extedit then -%>
<a class="cbi-button ssr-button " type="button" value="" <%- if type(self.extedit) == "string" then
%> onclick="location.href='<%=self.extedit:format(section.name)%>'" <%- elseif type(self.extedit) == "function" then
%> onclick="location.href='<%=self:extedit(section.name)%>'" <%- end
%> alt="<%:Edit%>" title="<%:Edit%>"><span class="icon-edit"></span> <%:Edit%></a>
<%- end; if self.addremove then %>
<a class="cbi-button ssr-button" type="button" value="" onclick="delete_node('<%=section.name%>')"
alt="<%:Delete%>" title="<%:Delete%>" /><span
class="icon-delete"></span> <%:Delete%></a>
<%- end -%>
<div class="pure-u-1-5">
<div class="cbi-section-table-row <%=fc %> " id="cbi-<%=self.config%>-<%=section.name%>"
data-flag="<%=section.flag%>" server="<%=section.server%>"
server_port="<%=section.server_port%>">
<div class="loadings hide"></div>
<div class="incon type flag-icon <%=flag_icon %>" data-setction="<%=section.name%>">
<img class="pure-imgw <%=switch_enable %>" src="/luci-static/vssr/img/switch.png">
<div class="tp">
<%=section.type%>
</div>
</div>
<div class=" alias">
<%=section.alias%>
</div>
<%- if self.extedit or self.addremove then -%>
<div class="cbi-section-table-cell">
<div class="host_con"></div>
<a class="cbi-button ssr-button" type="button" value=""
onclick="apply_node('<%=section.name%>')" alt="<%:Apply%>"
title="<%:Apply%>"><span class="icon-use"></span>
<%:Apply%>
</a>
<%- if self.extedit then -%>
<a class="cbi-button ssr-button " type="button" value="" <%- if type(self.extedit) == "string" then
%> onclick="location.href='<%=self.extedit:format(section.name)%>'" <%- elseif type(self.extedit) == "function" then
%> onclick="location.href='<%=self:extedit(section.name)%>'" <%- end
%> alt="<%:Edit%>" title="<%:Edit%>"><span class="icon-edit"></span> <%:Edit%></a>
<%- end; if self.addremove then %>
<a class="cbi-button ssr-button" type="button" value=""
onclick="delete_node('<%=section.name%>')" alt="<%:Delete%>"
title="<%:Delete%>" /><span class="icon-delete"></span>
<%:Delete%></a>
<%- end -%>
</div>
<%- end -%>
</div>
</div>
<%- end -%>
<%- if isempty then -%>
<div class="cbi-section-table-row">
<div colspan="<%=count%>"><em><br />
<%:This section contains no values yet%>
</em></div>
</div>
<%- end -%>
</div>
<% if self.error then %>
<div class="cbi-section-error">
<ul>
<% for _, c in pairs(self.error) do for _, e in ipairs(c) do -%>
<li>
<%=pcdata(e):gsub("\n","<br />")%>
</li>
<%- end end %>
</ul>
</div>
</div>
<%- end -%>
<% end %>
<%- if isempty then -%>
<div class="cbi-section-table-row">
<div colspan="<%=count%>"><em><br /><%:This section contains no values yet%></em></div>
</div>
<%- end -%>
</div>
<% if self.error then %>
<div class="cbi-section-error">
<ul><% for _, c in pairs(self.error) do for _, e in ipairs(c) do -%>
<li><%=pcdata(e):gsub("\n","<br />")%></li>
<%- end end %></ul>
</div>
<% end %>
<%- if self.addremove then -%>
<% if self.template_addremove then include(self.template_addremove) else -%>
<div class="cbi-section-create cbi-tblsection-create">
<% if self.anonymous then %>
<input class="cbi-button cbi-button-add" type="submit" value="<%:Add%>"
name="cbi.cts.<%=self.config%>.<%=self.sectiontype%>.<%=section%>" title="<%:Add%>" />
<% else %>
<% if self.invalid_cts then -%><div class="cbi-section-error"><% end %>
<input type="text" class="cbi-section-create-name"
id="cbi.cts.<%=self.config%>.<%=self.sectiontype%>.<%=section%>"
name="cbi.cts.<%=self.config%>.<%=self.sectiontype%>.<%=section%>" data-type="uciname"
data-optional="true" />
<input class="cbi-button cbi-button-add" type="submit"
onclick="this.form.cbi_state = 'add-section'; return true" value="<%:Add%>" title="<%:Add%>" />
<% if self.invalid_cts then -%>
<br /><%:Invalid%></div>
<%- end %>
<% end %>
</div>
<%- end %>
<%- end -%>
<%- if self.addremove then -%>
<% if self.template_addremove then include(self.template_addremove) else -%>
<div class="cbi-section-create cbi-tblsection-create">
<% if self.anonymous then %>
<input class="cbi-button cbi-button-add" type="submit" value="<%:Add%>"
name="cbi.cts.<%=self.config%>.<%=self.sectiontype%>.<%=section%>"
title="<%:Add%>" />
<% else %>
<% if self.invalid_cts then -%>
<div class="cbi-section-error">
<% end %>
<input type="text" class="cbi-section-create-name"
id="cbi.cts.<%=self.config%>.<%=self.sectiontype%>.<%=section%>"
name="cbi.cts.<%=self.config%>.<%=self.sectiontype%>.<%=section%>"
data-type="uciname" data-optional="true" />
<input class="cbi-button cbi-button-add" type="submit"
onclick="this.form.cbi_state = 'add-section'; return true"
value="<%:Add%>" title="<%:Add%>" />
<% if self.invalid_cts then -%>
<br />
<%:Invalid%>
</div>
<%- end %>
<% end %>
</div>
<%- end %>
<%- end -%>
</div>
</fieldset>
<!-- /tblsection -->
@ -120,15 +146,15 @@
$html += '<div class="pure-u-1-2"><div class="gap"><button class="cbi-button cbi-button-apply change-node" data-server="global" data-node="' + node + '"> 主服务器 </button></div></div>';
$html += '<div class="pure-u-1-2"><div class="gap"><button class="cbi-button cbi-button-apply change-node" data-server="udp_relay" data-node="' + node + '"> UDP游戏模式 </button></div></div>';
$html += '<div class="pure-u-1-2"><div class="gap"><button class="cbi-button cbi-button-apply change-node" data-server="youtube" data-node="' + node + '"> Youtube </button></div></div>';
if ($('#cbi-' + CONFIG + '-' + node).attr("data-flag") == "tw") {
//if ($('#cbi-' + CONFIG + '-' + node).attr("data-flag") == "tw") {
$html += '<div class="pure-u-1-2"><div class="gap"><button class="cbi-button cbi-button-apply change-node" data-server="tw_video" data-node="' + node + '"> TW Video </button></div></div>';
}
//}
$html += '<div class="pure-u-1-2"><div class="gap"><button class="cbi-button cbi-button-apply change-node" data-server="netflix" data-node="' + node + '"> Netflix </button></div></div>';
$html += '<div class="pure-u-1-2"><div class="gap"><button class="cbi-button cbi-button-apply change-node" data-server="disney" data-node="' + node + '"> Diseny+ </button></div></div>';
$html += '<div class="pure-u-1-2"><div class="gap"><button class="cbi-button cbi-button-apply change-node" data-server="prime" data-node="' + node + '"> Prime Video </button></div></div>';
if ($('#cbi-' + CONFIG + '-' + node).attr("data-flag") == "hk") {
//if ($('#cbi-' + CONFIG + '-' + node).attr("data-flag") == "hk") {
$html += '<div class="pure-u-1-2"><div class="gap"><button class="cbi-button cbi-button-apply change-node" data-server="tvb" data-node="' + node + '"> TVB Video </button></div></div>';
}
//}
$html += '<div class="pure-u-1-2"><div class="gap"><button class="cbi-button cbi-button-apply change-node" data-server="custom" data-node="' + node + '"> 自定义 </button></div></div>';
$html += '</div>';
Swal.fire({
@ -165,9 +191,9 @@
}
})
}
//删除节点
function delete_node(node){
function delete_node(node) {
XHR.halt();
$.each(ajaxArray, function (n, value) { value.abort(); }) //中断所有的ajax请求
$.get(DELETE_NODE_URL, { node: node },
@ -223,10 +249,7 @@
function (data, status) {
currentCheckCount++
var host_con = $(target).find(".host_con");
host_con.removeClass("fast");
host_con.removeClass("middle");
host_con.removeClass("slow");
host_con.removeClass("nopass");
host_con.removeClass("fast middle slow nopass");
if (data.ret == 1) {
if (data.used == "") {
host_con.addClass("nopass");

View File

@ -1,5 +1,6 @@
<%+cbi/valueheader%>
<link rel="stylesheet" href="/luci-static/vssr/css/vssr.css?v=<%=math.random(1,100000)%>">
<script src="/luci-static/vssr/jquery.min.js"></script>
<label class="cbi-value-title"><%= translate("Update") %></label>
<div class="cbi-value-field">
<input class="cbi-button cbi-button-reload" id="update_subscribe" type="button" size="0"
@ -7,7 +8,7 @@
</div>
<script type="text/javascript">
const SAVE_SUBSCRIBE_URL = '<%=luci.dispatcher.build_url("admin", "services", "vssr","subscribe")%>';
const SERVERS_URL = '<%=luci.dispatcher.build_url("admin", "services", "vssr","servers")%>';
@ -20,14 +21,14 @@
'<textarea cols="63" rows="28" wrap="on" readonly="readonly" id="log_content3" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"></textarea>' +
'</div>' +
'</div>';
$(document).ready(function () {
//显示并开始刷新订阅
function update_subscribe() {
$("body").append(modal);
$(".modals-bg").removeClass("hide");
setTimeout(function(){
$(".modals").removeClass("hide");
}, 200);
}, 100);
setTimeout("get_realtime_log();", 500);
}
//保存订阅按钮
@ -76,7 +77,7 @@
});
return false;
});
});
function get_realtime_log() {
$.ajax({
@ -106,7 +107,7 @@
}
});
}
</script>
<%+cbi/valuefooter%>

View File

@ -128,13 +128,13 @@ msgid "SSR Server"
msgstr "服务端"
msgid "vssr Server"
msgstr "vssr 服务端"
msgstr "Hello World 服务端"
msgid "vssr Server is running"
msgstr "vssr 服务端运行中"
msgstr "Hello World 服务端运行中"
msgid "vssr Server is not running"
msgstr "vssr 服务端未运行"
msgstr "Hello World 服务端未运行"
msgid "Enable Server"
msgstr "启动服务端"
@ -710,3 +710,15 @@ msgstr "TVB 域名"
msgid "Custom Domain"
msgstr "自定义域名"
msgid "Subscribe"
msgstr "订阅设置"
msgid "Local Proxy"
msgstr "本地代理"
msgid "↑ Put your IP list here"
msgstr "↑ 在上面放置你需要分流的IP列表"
msgid "↑ Put your domain list here"
msgstr "↑ 在上面放置你需要分流的域名列表"

View File

@ -609,6 +609,7 @@ EOF
/etc/init.d/dnsmasq restart >/dev/null 2>&1
start_server
start_local
scount=$(count_shunt)
if [ $scount != "0" ]; then
start_shunt
fi

View File

@ -1,6 +1,7 @@
local ucursor = require 'luci.model.uci'.cursor()
local name = 'vssr'
local json = require 'luci.jsonc'
local nixio = require 'nixio'
local server_section = arg[1]
local proto = arg[2]
local local_port = arg[3]
@ -8,6 +9,9 @@ local outbounds_table = {}
local rules_table = {}
function read_conf(file)
if not nixio.fs.access(file) then
return nil
end
local rfile = io.open(file, "r")
local ltable = {}
for line in rfile:lines() do
@ -18,15 +22,30 @@ function read_conf(file)
return rtable
end
local v2ray_flow = ucursor:get_first(name, 'global', 'v2ray_flow', '0')
local custom_domain = read_conf("/etc/vssr/custom_domain.list")
local custom_ip = read_conf("/etc/vssr/custom_ip.list")
local youtube_domain = read_conf("/etc/vssr/youtube_domain.list")
local youtube_ip = read_conf("/etc/vssr/youtube_ip.list")
local tw_video_domain = read_conf("/etc/vssr/tw_video_domain.list")
local tw_video_ip = read_conf("/etc/vssr/tw_video_ip.list")
local netflix_domain = read_conf("/etc/vssr/netflix_domain.list")
local netflix_ip = read_conf("/etc/vssr/netflix_ip.list")
local disney_domain = read_conf("/etc/vssr/disney_domain.list")
local disney_ip = read_conf("/etc/vssr/disney_ip.list")
local prime_domain = read_conf("/etc/vssr/prime_domain.list")
local prime_ip = read_conf("/etc/vssr/prime_ip.list")
local tvb_domain = read_conf("/etc/vssr/tvb_domain.list")
local tvb_ip = read_conf("/etc/vssr/tvb_ip.list")
local flow_table = {
yotube = {
@ -35,6 +54,7 @@ local flow_table = {
rules = {
type = 'field',
domain = youtube_domain,
ip = youtube_ip,
outboundTag = 'youtube'
}
},
@ -44,6 +64,7 @@ local flow_table = {
rules = {
type = 'field',
domain = tw_video_domain,
ip = tw_video_ip,
outboundTag = 'tw_video'
}
},
@ -53,6 +74,7 @@ local flow_table = {
rules = {
type = 'field',
domain = netflix_domain,
ip = netflix_ip,
outboundTag = 'netflix'
}
},
@ -62,6 +84,7 @@ local flow_table = {
rules = {
type = 'field',
domain = disney_domain,
ip = disney_ip,
outboundTag = 'disney'
}
},
@ -71,6 +94,7 @@ local flow_table = {
rules = {
type = 'field',
domain = prime_domain,
ip = prime_ip,
outboundTag = 'prime'
}
},
@ -80,6 +104,7 @@ local flow_table = {
rules = {
type = 'field',
domain = tvb_domain,
ip = tvb_ip,
outboundTag = 'tvb'
}
},
@ -89,6 +114,7 @@ local flow_table = {
rules = {
type = 'field',
domain = custom_domain,
ip = custom_ip,
outboundTag = 'custom'
}
}
@ -113,7 +139,7 @@ local bt_rules1 = {
'BitTorrent',
'announce_peer',
'announce.php?passkey='
},
}
}
function gen_outbound(server_node, tags, local_ports)
@ -210,12 +236,30 @@ function gen_bt_outbounds()
end
if v2ray_flow == '1' then
table.insert(outbounds_table, gen_outbound(server_section, 'global', 2080))
for _, v in pairs(flow_table) do
if(v.rules.domain ~= nil) then
if(v.rules.domain ~= nil or v.rules.ip ~= nil) then
local server = ucursor:get_first(name, 'global', v.name .. '_server')
table.insert(outbounds_table, gen_outbound(server, v.name, v.port))
table.insert(rules_table, (server ~= nil and server ~= 'nil' ) and v.rules or nil)
if(v.rules.domain ~= nil) then
domain_rules = {
type = 'field',
domain = v.rules.domain,
outboundTag = v.rules.outboundTag
}
table.insert(rules_table, (server ~= nil and server ~= 'nil' ) and domain_rules or nil)
end
if(v.rules.ip ~= nil) then
ip_rules = {
type = 'field',
ip = v.rules.ip,
outboundTag = v.rules.outboundTag
}
table.insert(rules_table, (server ~= nil and server ~= 'nil' ) and ip_rules or nil)
end
end
end
else
@ -246,6 +290,6 @@ local v2ray = {
},
-- 传出连接
outbounds = outbounds_table,
routing = {domainStrategy = 'AsIs', rules = rules_table}
routing = {domainStrategy = 'IPIfNonMatch', rules = rules_table}
}
print(json.stringify(v2ray, 1))

View File

@ -33,6 +33,40 @@ local switch = '0'
local subscribe_url = ucic:get_first(name, 'server_subscribe', 'subscribe_url', {})
local filter_words = ucic:get_first(name, 'server_subscribe', 'filter_words', '过期时间/剩余流量')
function print_r ( t )
local print_r_cache={}
local function sub_print_r(t,indent)
if (print_r_cache[tostring(t)]) then
print(indent.."*"..tostring(t))
else
print_r_cache[tostring(t)]=true
if (type(t)=="table") then
for pos,val in pairs(t) do
if (type(val)=="table") then
print(indent.."["..pos.."] => "..tostring(t).." {")
sub_print_r(val,indent..string.rep(" ",string.len(pos)+8))
print(indent..string.rep(" ",string.len(pos)+6).."}")
elseif (type(val)=="string") then
print(indent.."["..pos..'] => "'..val..'"')
else
print(indent.."["..pos.."] => "..tostring(val))
end
end
else
print(indent..tostring(t))
end
end
end
if (type(t)=="table") then
print(tostring(t).." {")
sub_print_r(t," ")
print("}")
else
sub_print_r(t," ")
end
print()
end
local log = function(...)
print(os.date('%Y-%m-%d %H:%M:%S ') .. table.concat({...}, ' '))
end
@ -55,6 +89,43 @@ local function split(full, sep)
end
return result
end
--table去重
local function clone( object )
local lookup_table = {}
local function copyObj( object )
if type( object ) ~= "table" then
return object
elseif lookup_table[object] then
return lookup_table[object]
end
local new_table = {}
lookup_table[object] = new_table
for key, value in pairs( object ) do
new_table[copyObj( key )] = copyObj( value )
end
return setmetatable( new_table, getmetatable( object ) )
end
return copyObj( object )
end
local function table_unique(list)
local temp1 = clone(list)
local temp2 = clone(list)
for k1, v1 in ipairs(temp1) do
for k2, v2 in ipairs(temp2) do
if v1.alias ~= v2.alias and v1.hashkey == v2.hashkey then
table.remove(temp1, k1)
table.remove(temp2, k1)
end
end
end
return temp1
end
-- urlencode
local function get_urlencode(c)
return sformat('%%%02X', sbyte(c))
@ -385,12 +456,21 @@ local execute = function()
end
end
log('成功解析节点数量: ' .. #nodes)
end
else
log(url .. ': 获取内容为空')
end
end
end
-- diff
do
assert(next(nodeResult), '获取不到节点信息,请检查路由器是否能够访问网络,或者订阅链接是否正确')
if next(nodeResult) == nil then
log("更新失败,没有可用的节点信息")
if proxy == '0' then
luci.sys.init.start(name)
log('订阅失败, 恢复服务')
end
return
end
local add, del = 0, 0
ucic:foreach(
name,
@ -407,17 +487,24 @@ local execute = function()
setmetatable(nodeResult[old.grouphashkey][old.hashkey], {__index = {_ignore = true}})
end
else
if (old.alias ~= nil) then
log('忽略手动添加的节点: ' .. old.alias)
if not old.alias then
if not old.server or old.server_port then
ucic:delete(name, old['.name'])
else
old.alias = old.server .. ':' .. old.server_port
log('忽略手动添加的节点: ' .. old.alias)
end
else
log('忽略手动添加的无效节点')
log('忽略手动添加的节点: ' .. old.alias)
end
end
end
)
for k, v in ipairs(nodeResult) do
for kk, vv in ipairs(v) do
-- 如果订阅节点中有相同的节点信息 需要先去重。
new_nodes = table_unique(v)
for kk, vv in ipairs(new_nodes) do
if not vv._ignore then
local section = ucic:add(name, uciType)
ucic:tset(name, section, vv)

View File

@ -16,11 +16,7 @@ local log = function(...)
end
log('正在更新【GFW列表】数据库')
if nixio.fs.access('/usr/bin/wget-ssl') then
refresh_cmd = 'wget-ssl --no-check-certificate https://cdn.jsdelivr.net/gh/gfwlist/gfwlist/gfwlist.txt -O /tmp/gfw.b64'
else
refresh_cmd = 'wget -O /tmp/gfw.b64 http://iytc.net/tools/list.b64'
end
refresh_cmd = 'wget-ssl --no-check-certificate https://cdn.jsdelivr.net/gh/gfwlist/gfwlist/gfwlist.txt -O /tmp/gfw.b64'
sret = luci.sys.call(refresh_cmd .. ' 2>/dev/null')
if sret == 0 then
luci.sys.call('/usr/bin/vssr-gfw')
@ -43,7 +39,7 @@ else
end
log('正在更新【国内IP段】数据库')
refresh_cmd ="wget -O- 'https://ispip.clang.cn/all_cn.txt' > /tmp/china_ssr.txt 2>/dev/null"
refresh_cmd ="wget-ssl -O- 'https://ispip.clang.cn/all_cn.txt' > /tmp/china_ssr.txt 2>/dev/null"
sret = luci.sys.call(refresh_cmd)
icount = luci.sys.exec('cat /tmp/china_ssr.txt | wc -l')
if sret == 0 then
@ -69,12 +65,8 @@ end
if ucic:get_first('vssr', 'global', 'adblock', '') == '1' then
log('正在更新【广告屏蔽】数据库')
local need_process = 0
if nixio.fs.access('/usr/bin/wget-ssl') then
refresh_cmd = 'wget-ssl --no-check-certificate -O - https://easylist-downloads.adblockplus.org/easylistchina+easylist.txt > /tmp/adnew.conf'
need_process = 1
else
refresh_cmd = 'wget -O /tmp/ad.conf http://iytc.net/tools/ad.conf'
end
refresh_cmd = 'wget-ssl --no-check-certificate -O - https://easylist-downloads.adblockplus.org/easylistchina+easylist.txt > /tmp/adnew.conf'
need_process = 1
sret = luci.sys.call(refresh_cmd .. ' 2>/dev/null')
if sret == 0 then
if need_process == 1 then

View File

@ -0,0 +1,232 @@
/*!
Pure v1.0.1
Copyright 2013 Yahoo!
Licensed under the BSD License.
https://github.com/pure-css/pure/blob/master/LICENSE.md
*/
.pure-g {
letter-spacing: -.31em;
text-rendering: optimizespeed;
font-family: FreeSans, Arimo, "Droid Sans", Helvetica, Arial, sans-serif;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-box-orient: horizontal;
-webkit-box-direction: normal;
-webkit-flex-flow: row wrap;
-ms-flex-flow: row wrap;
flex-flow: row wrap;
-webkit-align-content: flex-start;
-ms-flex-line-pack: start;
align-content: flex-start
}
@media all and (-ms-high-contrast:none),
(-ms-high-contrast:active) {
table .pure-g {
display: block
}
}
.opera-only :-o-prefocus,
.pure-g {
word-spacing: -.43em
}
.pure-u {
display: inline-block;
zoom: 1;
letter-spacing: normal;
word-spacing: normal;
vertical-align: top;
text-rendering: auto
}
.pure-g [class*=pure-u] {
font-family: sans-serif
}
.pure-u-1,
.pure-u-1-1,
.pure-u-1-12,
.pure-u-1-2,
.pure-u-1-24,
.pure-u-1-3,
.pure-u-1-4,
.pure-u-1-5,
.pure-u-1-6,
.pure-u-1-8,
.pure-u-10-24,
.pure-u-11-12,
.pure-u-11-24,
.pure-u-12-24,
.pure-u-13-24,
.pure-u-14-24,
.pure-u-15-24,
.pure-u-16-24,
.pure-u-17-24,
.pure-u-18-24,
.pure-u-19-24,
.pure-u-2-24,
.pure-u-2-3,
.pure-u-2-5,
.pure-u-20-24,
.pure-u-21-24,
.pure-u-22-24,
.pure-u-23-24,
.pure-u-24-24,
.pure-u-3-24,
.pure-u-3-4,
.pure-u-3-5,
.pure-u-3-8,
.pure-u-4-24,
.pure-u-4-5,
.pure-u-5-12,
.pure-u-5-24,
.pure-u-5-5,
.pure-u-5-6,
.pure-u-5-8,
.pure-u-6-24,
.pure-u-7-12,
.pure-u-7-24,
.pure-u-7-8,
.pure-u-8-24,
.pure-u-9-24 {
display: inline-block;
zoom: 1;
letter-spacing: normal;
word-spacing: normal;
vertical-align: top;
text-rendering: auto
}
.pure-u-1-24 {
width: 4.1667%
}
.pure-u-1-12,
.pure-u-2-24 {
width: 8.3333%
}
.pure-u-1-8,
.pure-u-3-24 {
width: 12.5%
}
.pure-u-1-6,
.pure-u-4-24 {
width: 16.6667%
}
.pure-u-1-5 {
width: 20%
}
.pure-u-5-24 {
width: 20.8333%
}
.pure-u-1-4,
.pure-u-6-24 {
width: 25%
}
.pure-u-7-24 {
width: 29.1667%
}
.pure-u-1-3,
.pure-u-8-24 {
width: 33.3333%
}
.pure-u-3-8,
.pure-u-9-24 {
width: 37.5%
}
.pure-u-2-5 {
width: 40%
}
.pure-u-10-24,
.pure-u-5-12 {
width: 41.6667%
}
.pure-u-11-24 {
width: 45.8333%
}
.pure-u-1-2,
.pure-u-12-24 {
width: 50%
}
.pure-u-13-24 {
width: 54.1667%
}
.pure-u-14-24,
.pure-u-7-12 {
width: 58.3333%
}
.pure-u-3-5 {
width: 60%
}
.pure-u-15-24,
.pure-u-5-8 {
width: 62.5%
}
.pure-u-16-24,
.pure-u-2-3 {
width: 66.6667%
}
.pure-u-17-24 {
width: 70.8333%
}
.pure-u-18-24,
.pure-u-3-4 {
width: 75%
}
.pure-u-19-24 {
width: 79.1667%
}
.pure-u-4-5 {
width: 80%
}
.pure-u-20-24,
.pure-u-5-6 {
width: 83.3333%
}
.pure-u-21-24,
.pure-u-7-8 {
width: 87.5%
}
.pure-u-11-12,
.pure-u-22-24 {
width: 91.6667%
}
.pure-u-23-24 {
width: 95.8333%
}
.pure-u-1,
.pure-u-1-1,
.pure-u-24-24,
.pure-u-5-5 {
width: 100%
}

View File

@ -1,245 +1,73 @@
@import url("flag-icon.min.css?v=1.16");
@import url("sweetalert2.css?v=1.16");
/*!
Pure v1.0.1
Copyright 2013 Yahoo!
Licensed under the BSD License.
https://github.com/pure-css/pure/blob/master/LICENSE.md
*/
@import url("pure.css?v=1.16");
.pure-g {
letter-spacing: -.31em;
text-rendering: optimizespeed;
font-family: FreeSans, Arimo, "Droid Sans", Helvetica, Arial, sans-serif;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-box-orient: horizontal;
-webkit-box-direction: normal;
-webkit-flex-flow: row wrap;
-ms-flex-flow: row wrap;
flex-flow: row wrap;
-webkit-align-content: flex-start;
-ms-flex-line-pack: start;
align-content: flex-start
/** Common **/
.pure-img {
max-height: 100%;
width: auto;
}
@media all and (-ms-high-contrast:none),
(-ms-high-contrast:active) {
table .pure-g {
display: block
}
.pure-imgw {
max-width: 100%;
height: auto;
}
.opera-only :-o-prefocus,
.pure-g {
word-spacing: -.43em
}
.pure-u {
display: inline-block;
zoom: 1;
letter-spacing: normal;
word-spacing: normal;
vertical-align: top;
text-rendering: auto
}
.pure-g [class*=pure-u] {
font-family: sans-serif
}
.pure-u-1,
.pure-u-1-1,
.pure-u-1-12,
.pure-u-1-2,
.pure-u-1-24,
.pure-u-1-3,
.pure-u-1-4,
.pure-u-1-5,
.pure-u-1-6,
.pure-u-1-8,
.pure-u-10-24,
.pure-u-11-12,
.pure-u-11-24,
.pure-u-12-24,
.pure-u-13-24,
.pure-u-14-24,
.pure-u-15-24,
.pure-u-16-24,
.pure-u-17-24,
.pure-u-18-24,
.pure-u-19-24,
.pure-u-2-24,
.pure-u-2-3,
.pure-u-2-5,
.pure-u-20-24,
.pure-u-21-24,
.pure-u-22-24,
.pure-u-23-24,
.pure-u-24-24,
.pure-u-3-24,
.pure-u-3-4,
.pure-u-3-5,
.pure-u-3-8,
.pure-u-4-24,
.pure-u-4-5,
.pure-u-5-12,
.pure-u-5-24,
.pure-u-5-5,
.pure-u-5-6,
.pure-u-5-8,
.pure-u-6-24,
.pure-u-7-12,
.pure-u-7-24,
.pure-u-7-8,
.pure-u-8-24,
.pure-u-9-24 {
display: inline-block;
zoom: 1;
letter-spacing: normal;
word-spacing: normal;
vertical-align: top;
text-rendering: auto
}
.pure-u-1-24 {
width: 4.1667%
}
.pure-u-1-12,
.pure-u-2-24 {
width: 8.3333%
}
.pure-u-1-8,
.pure-u-3-24 {
width: 12.5%
}
.pure-u-1-6,
.pure-u-4-24 {
width: 16.6667%
}
.pure-u-1-5 {
width: 20%
}
.pure-u-5-24 {
width: 20.8333%
}
.pure-u-1-4,
.pure-u-6-24 {
width: 25%
}
.pure-u-7-24 {
width: 29.1667%
}
.pure-u-1-3,
.pure-u-8-24 {
width: 33.3333%
}
.pure-u-3-8,
.pure-u-9-24 {
width: 37.5%
}
.pure-u-2-5 {
width: 40%
}
.pure-u-10-24,
.pure-u-5-12 {
width: 41.6667%
}
.pure-u-11-24 {
width: 45.8333%
}
.pure-u-1-2,
.pure-u-12-24 {
width: 50%
}
.pure-u-13-24 {
width: 54.1667%
}
.pure-u-14-24,
.pure-u-7-12 {
width: 58.3333%
}
.pure-u-3-5 {
width: 60%
}
.pure-u-15-24,
.pure-u-5-8 {
width: 62.5%
display: flex;
align-items: center;
}
.pure-u-16-24,
.pure-u-2-3 {
width: 66.6667%
.gap {
margin-right: 0.5rem;
}
.pure-u-17-24 {
width: 70.8333%
/** 复写theme **/
.cbi-section {
border: 0;
border-radius: .375rem;
overflow-x: auto;
overflow-y: hidden;
}
.pure-u-18-24,
.pure-u-3-4 {
width: 75%
.cbi-section-node {
border: 0;
border-radius: .375rem;
margin-bottom: 0px;
overflow-x: auto;
overflow-y: hidden;
padding: 0rem 0;
}
.pure-u-19-24 {
width: 79.1667%
#cbi-vssr {
overflow-x: hidden;
}
.pure-u-4-5 {
width: 80%
}
.pure-u-20-24,
.pure-u-5-6 {
width: 83.3333%
}
.pure-u-21-24,
.pure-u-7-8 {
width: 87.5%
}
.pure-u-11-12,
.pure-u-22-24 {
width: 91.6667%
}
.pure-u-23-24 {
width: 95.8333%
}
.pure-u-1,
.pure-u-1-1,
.pure-u-24-24,
.pure-u-5-5 {
width: 100%
#cbi-vssr .panel-title {
width: 100%;
display: block;
margin-bottom: 0;
padding: .8755rem 1.5rem;
margin-bottom: 0rem;
letter-spacing: .1rem;
font-weight: 600;
box-sizing: border-box;
}
/** Top StatusBar **/
.status {
margin: 1rem -0.5rem 1rem -0.5rem;
margin: 0.5rem -0.5rem 1rem -0.5rem;
}
.block {
.status .block {
margin: 0.5rem 0.5rem;
padding: 0;
font-weight: normal;
@ -255,56 +83,28 @@ https://github.com/pure-css/pure/blob/master/LICENSE.md
box-shadow: 0 0 2rem 0 rgba(136, 152, 170, .15);
}
.img-con {
.status .block .img-con {
margin: 1rem;
}
.pure-img {
max-height: 100%;
width: auto;
}
.pure-imgw {
max-width: 100%;
height: auto;
}
.green {
.status .block .green {
font-size: 1.25rem;
color: #2dce89;
}
.red {
.status .block .red {
font-size: 1.25rem;
color: #fb6340;
}
.sk-text-success {
color: #2dce89;
}
.sk-text-error {
color: #fb6340;
}
.gap {
margin-right: 0.5rem;
}
.block img {
.status .block img {
width: 48px;
height: auto;
float: right;
}
.pure-u-5-8 {
display: flex;
align-items: center;
}
.block h4 {
.status .block h4 {
font-size: .8125rem;
font-weight: 600;
margin: 1rem;
@ -312,12 +112,10 @@ https://github.com/pure-css/pure/blob/master/LICENSE.md
line-height: 1.8em;
}
.p-in5 {
padding: 8px;
}
/** Server List **/
.flag-icon:before {
position: absolute;
.p-in5 {
margin: 8px !important;
}
.cbi-section-table-row {
@ -346,6 +144,17 @@ https://github.com/pure-css/pure/blob/master/LICENSE.md
color: #fff;
}
.ssr-button:link,
.ssr-button:hover,
.ssr-button:focus,
.ssr-button:visited,
.ssr-button:active {
outline: 0;
text-decoration: none;
box-shadow: none;
}
.cbi-section-table-row.fast .ssr-button {
color: #fff;
}
@ -363,6 +172,15 @@ https://github.com/pure-css/pure/blob/master/LICENSE.md
color: #fff !important;
}
.host_con {
position: absolute;
left: 0;
width: 70px;
text-align: left;
height: 22px;
font-weight: bold;
}
.host_con.fast {
color: #6f9a37;
}
@ -379,7 +197,19 @@ https://github.com/pure-css/pure/blob/master/LICENSE.md
color: #fb6340;
}
.host_ok {
color: #fff;
background: #2dce89;
padding: 0.1rem 0.5rem;
border-radius: 0.2rem;
}
.host_error {
color: #fff;
background: #f5365c;
padding: 0.1rem 0.5rem;
border-radius: 0.2rem;
}
.loadings.hide {
opacity: 0;
@ -392,20 +222,20 @@ https://github.com/pure-css/pure/blob/master/LICENSE.md
height: 30px;
}
.flag-icon:before {
position: absolute;
}
.alias {
margin-bottom: 1px;
padding-left: 0px;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
.incon {
cursor: pointer;
}
.incon .pure-imgw {
transition: all 0.2s;
}
.incon:nth-child(2) {
position: absolute;
left: 0;
top: 0;
@ -414,10 +244,13 @@ https://github.com/pure-css/pure/blob/master/LICENSE.md
border: 0;
border-right: #d1dfed 1px solid;
background-position: top;
}
.incon:nth-child(2) .tp {
.incon .pure-imgw {
transition: all 0.2s;
}
.incon .tp {
position: absolute;
left: 0;
bottom: 0;
@ -430,34 +263,29 @@ https://github.com/pure-css/pure/blob/master/LICENSE.md
background: #525f7f;
}
.incon:nth-child(3) {
padding-left: 0px;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
.incon:nth-child(5),
.incon:nth-child(6) {
display: none;
}
.cbi-section-table-cell {
text-align: right;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
position: relative;
}
.ssr-button {
background: none;
border: none;
color: #525f7f;
padding: 0 0 0 0;
padding: 0 0.2rem 0 0;
height: auto;
line-height: 1.5em;
box-shadow: none;
}
.ssr-button:hover,
.ssr-button:focus,
.ssr-button:active {
color: #525f7f;
outline: 0;
text-decoration: none;
box-shadow: none;
@ -472,37 +300,18 @@ https://github.com/pure-css/pure/blob/master/LICENSE.md
color: #fff !important;
background-color: #337ab7 !important;
border-color: #2e6da4 !important;
background-image: none;
float: right;
margin-right: 18px;
margin-top: 1rem;
margin-top: .25rem !important;
outline: none;
}
.cbi-section-table-cell {
position: relative;
.cbi-button-check:focus {
outline: none;
}
.host_con {
position: absolute;
left: 0;
width: 70px;
text-align: left;
height: 22px;
font-weight: bold;
}
.host_ok {
color: #fff;
background: #2dce89;
padding: 0.1rem 0.5rem;
border-radius: 0.2rem;
}
.host_error {
color: #fff;
background: #f5365c;
padding: 0.1rem 0.5rem;
border-radius: 0.2rem;
}
/** Bottom StatusBar **/
footer.mobile-hide {
display: block;
@ -537,6 +346,7 @@ footer.mobile-hide {
.status-bar .inner .status-info {
font-weight: bold;
line-height: 1.5em;
}
.status-bar .icon-con {
@ -544,6 +354,8 @@ footer.mobile-hide {
text-align: right;
}
/** Node choose panel **/
.choose_node .gap {
margin: 0.5rem 1rem;
position: relative;
@ -553,66 +365,8 @@ footer.mobile-hide {
width: 100% !important;
}
@media screen and (max-width: 2000px) {
.pure-u-1-5 {
width: 25%;
}
}
@media screen and (max-width: 1600px) {
.pure-u-1-5 {
width: 33.33333333%;
}
}
@media screen and (max-width: 1200px) {
.pure-u-1-5 {
width: 50%;
}
.status .pure-u-1-5 {
width: 33.333%;
}
}
@media screen and (max-width: 700px) {
.pure-u-1-4 {
width: 50%;
}
.pure-u-1-2 {
width: 100%;
}
.pure-u-1-5 {
width: 100%;
font-size: 14px;
}
.status .pure-u-1-5 {
width: 50%;
}
.cbi-button-add {
position: fixed;
padding: 0.3rem 0.5rem;
z-index: 1000;
width: 50px !important;
height: 50px;
bottom: 90px;
right: 5px;
font-size: 16px;
border-radius: 50%;
display: block;
background-color: #fb6340 !important;
border-color: #fb6340 !important;
box-shadow: 0 0 1rem 0 rgba(136, 152, 170, .75);
}
.choose_node .pure-u-1-2 {
width: 50%;
}
}
/** Subscribe panel **/
.modals-bg {
position: fixed;
@ -625,7 +379,7 @@ footer.mobile-hide {
transition: all 0.2s;
}
.modals-bg.hide{
.modals-bg.hide {
visibility: hidden;
opacity: 0;
}
@ -651,7 +405,7 @@ footer.mobile-hide {
transform: scale(1);
}
.modals.hide{
.modals.hide {
visibility: hidden;
opacity: 0;
transform: scale(0.5);
@ -688,7 +442,27 @@ footer.mobile-hide {
overflow: hidden
}
@media screen and (max-width: 1024px) {
@media screen and (max-width: 2000px) {
.pure-u-1-5 {
width: 25%;
}
}
@media screen and (max-width: 1600px) {
.pure-u-1-5 {
width: 33.33333333%;
}
}
@media screen and (max-width: 1200px) {
.pure-u-1-5 {
width: 50%;
}
.status .pure-u-1-5 {
width: 33.333%;
}
.modals {
position: fixed;
z-index: 100;
@ -704,6 +478,45 @@ footer.mobile-hide {
}
@media screen and (max-width: 700px) {
.pure-u-1-4 {
width: 50%;
}
.pure-u-1-2 {
width: 100%;
}
.pure-u-1-5 {
width: 100%;
font-size: 14px;
}
.status .pure-u-1-5 {
width: 50%;
}
.cbi-button-add {
position: fixed;
padding: 0.3rem 0.5rem;
z-index: 1000;
width: 50px !important;
height: 50px !important;
bottom: 90px;
color: #fff;
right: 5px;
font-size: 16px;
border-radius: 50%;
display: block;
background-color: #fb6340 !important;
border-color: #fb6340 !important;
box-shadow: 0 0 1rem 0 rgba(136, 152, 170, .75);
background-image: none;
}
.choose_node .pure-u-1-2 {
width: 50%;
}
.modals-bg {
position: fixed;
z-index: 100000;
@ -732,6 +545,14 @@ footer.mobile-hide {
height: 2.5em;
text-align: right;
}
.p-in5 {
padding-bottom: 3rem;
}
.cbi-page-actions {
padding-bottom: 4rem !important;
}
}
@media (prefers-color-scheme: dark) {
@ -753,4 +574,5 @@ footer.mobile-hide {
.ssr-button {
color: #ccc;
}
}