mirror of
https://github.com/hanwckf/immortalwrt-mt798x.git
synced 2025-01-10 19:12:33 +08:00
luci-app-ssr-plus-Jo: sync with upstream source
This commit is contained in:
parent
eb76dc2347
commit
1fb7b958aa
@ -1,8 +1,8 @@
|
|||||||
include $(TOPDIR)/rules.mk
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
PKG_NAME:=luci-app-ssr-plus-Jo
|
PKG_NAME:=luci-app-ssr-plus-Jo
|
||||||
PKG_VERSION:=150
|
PKG_VERSION:=151
|
||||||
PKG_RELEASE:=20200301-5
|
PKG_RELEASE:=20200304-5
|
||||||
PKG_CONFIG_DEPENDS:= CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Shadowsocks \
|
PKG_CONFIG_DEPENDS:= CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Shadowsocks \
|
||||||
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_V2ray \
|
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_V2ray \
|
||||||
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Trojan \
|
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Trojan \
|
||||||
|
@ -28,8 +28,8 @@ entry({"admin", "vpn", "shadowsocksr", "appointlist"},form("shadowsocksr/appoint
|
|||||||
entry({"admin", "vpn", "shadowsocksr", "server"},arcombine(cbi("shadowsocksr/server"), cbi("shadowsocksr/server-config")),_("SSR Server"),22).leaf = true
|
entry({"admin", "vpn", "shadowsocksr", "server"},arcombine(cbi("shadowsocksr/server"), cbi("shadowsocksr/server-config")),_("SSR Server"),22).leaf = true
|
||||||
end
|
end
|
||||||
entry({"admin", "vpn", "shadowsocksr", "status"},form("shadowsocksr/status"),_("Status"), 23).leaf = true
|
entry({"admin", "vpn", "shadowsocksr", "status"},form("shadowsocksr/status"),_("Status"), 23).leaf = true
|
||||||
entry({"admin", "vpn", "shadowsocksr", "log"}, cbi("shadowsocksr/log"), _("Log"), 30).leaf = true
|
entry({"admin", "vpn", "shadowsocksr", "logview"}, cbi("vssr/logview", {hideapplybtn=true, hidesavebtn=true, hideresetbtn=true}), _("Log") ,30).leaf=true
|
||||||
|
entry({"admin", "vpn", "shadowsocksr", "fileread"}, call("act_read"), nil).leaf=true
|
||||||
entry({"admin", "vpn", "shadowsocksr", "refresh"}, call("refresh_data"))
|
entry({"admin", "vpn", "shadowsocksr", "refresh"}, call("refresh_data"))
|
||||||
entry({"admin", "vpn", "shadowsocksr", "checkport"}, call("check_port"))
|
entry({"admin", "vpn", "shadowsocksr", "checkport"}, call("check_port"))
|
||||||
entry({"admin", "vpn", "shadowsocksr", "checkports"}, call("check_ports"))
|
entry({"admin", "vpn", "shadowsocksr", "checkports"}, call("check_ports"))
|
||||||
@ -65,7 +65,7 @@ function get_subscribe()
|
|||||||
end
|
end
|
||||||
luci.sys.call('uci commit shadowsocksr')
|
luci.sys.call('uci commit shadowsocksr')
|
||||||
luci.sys.call(
|
luci.sys.call(
|
||||||
"nohup /usr/share/shadowsocksr/subscribe.sh >/www/check_update.htm 2>/dev/null &")
|
"nohup /usr/bin/lua /usr/share/shadowsocksr/subscribe.lua >/www/check_update.htm 2>/dev/null &")
|
||||||
|
|
||||||
e.error = 0
|
e.error = 0
|
||||||
else
|
else
|
||||||
@ -372,5 +372,16 @@ function check_port()
|
|||||||
luci.http.write_json({ret = retstring , used = math.floor(tt*1000 + 0.5)})
|
luci.http.write_json({ret = retstring , used = math.floor(tt*1000 + 0.5)})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function act_read(lfile)
|
||||||
|
local NXFS = require "nixio.fs"
|
||||||
|
local HTTP = require "luci.http"
|
||||||
|
local lfile = HTTP.formvalue("lfile")
|
||||||
|
local ldata={}
|
||||||
|
ldata[#ldata+1] = NXFS.readfile(lfile) or "_nofile_"
|
||||||
|
if ldata[1] == "" then
|
||||||
|
ldata[1] = "_nodata_"
|
||||||
|
end
|
||||||
|
HTTP.prepare_content("application/json")
|
||||||
|
HTTP.write_json(ldata)
|
||||||
|
end
|
||||||
|
|
||||||
|
@ -0,0 +1,48 @@
|
|||||||
|
-- Copyright 2008 Steven Barth <steven@midlink.org>
|
||||||
|
-- Licensed to the public under the Apache License 2.0.
|
||||||
|
|
||||||
|
local NXFS = require "nixio.fs"
|
||||||
|
local DISP = require "luci.dispatcher"
|
||||||
|
local HTTP = require "luci.http"
|
||||||
|
local UCI = luci.model.uci.cursor()
|
||||||
|
|
||||||
|
m = Map("shadowsocksr")
|
||||||
|
|
||||||
|
-- log directory
|
||||||
|
log_dir = UCI:get_first(m.config, "global", "log_dir") or "/tmp"
|
||||||
|
run_dir = UCI:get_first(m.config, "global", "run_dir") or "/var/etc"
|
||||||
|
local logfile_list = {}
|
||||||
|
|
||||||
|
for path in (NXFS.glob("%s/ssr*" % log_dir) or function() end) do
|
||||||
|
logfile_list[#logfile_list+1] = path
|
||||||
|
end
|
||||||
|
for path in (NXFS.glob("%s/*.*" % run_dir) or function() end) do
|
||||||
|
logfile_list[#logfile_list+1] = path
|
||||||
|
end
|
||||||
|
|
||||||
|
ns = m:section(TypedSection, "_dummy", translate("File Viewer"))
|
||||||
|
ns.addremove = false
|
||||||
|
ns.anonymous = true
|
||||||
|
function ns.cfgsections()
|
||||||
|
return{"_exrules"}
|
||||||
|
end
|
||||||
|
|
||||||
|
lv = ns:option(DynamicList, "logfiles")
|
||||||
|
lv.template = "shadowsocksr/logsview"
|
||||||
|
lv.inputtitle = translate("Read / Reread log file")
|
||||||
|
lv.rows = 25
|
||||||
|
lv.default = ""
|
||||||
|
for _, lfile in ipairs(logfile_list) do lv:value(lfile, lfile) end
|
||||||
|
function lv.cfgvalue(self, section)
|
||||||
|
if logfile_list[1] then
|
||||||
|
local lfile=logfile_list[1]
|
||||||
|
if NXFS.access(lfile) then
|
||||||
|
return lfile .. "\n" .. translate("Please press [Read] button")
|
||||||
|
end
|
||||||
|
return lfile .. "\n" .. translate("File not found or empty")
|
||||||
|
else
|
||||||
|
return log_dir .. "\/\n" .. translate("No files found")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return m
|
@ -1,7 +1,7 @@
|
|||||||
-- Copyright (C) 2017 yushi studio <ywb94@qq.com>
|
-- Copyright (C) 2017 yushi studio <ywb94@qq.com>
|
||||||
-- Licensed to the public under the GNU General Public License v3.
|
-- Licensed to the public under the GNU General Public License v3.
|
||||||
|
|
||||||
local IPK_Version="20200301.150"
|
local IPK_Version="20200304.151"
|
||||||
local m, s, o
|
local m, s, o
|
||||||
local redir_run=0
|
local redir_run=0
|
||||||
local reudp_run=0
|
local reudp_run=0
|
||||||
@ -96,6 +96,88 @@ if nixio.fs.access("/etc/china_ssr.txt") then
|
|||||||
ip_count = sys.exec("cat /etc/china_ssr.txt | wc -l")
|
ip_count = sys.exec("cat /etc/china_ssr.txt | wc -l")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function processlist()
|
||||||
|
local data = {}
|
||||||
|
local netf = {}
|
||||||
|
local k
|
||||||
|
local ps = luci.util.execi("/bin/busybox top -bn1 | egrep -v dnsmasq")
|
||||||
|
local nets = luci.util.execi("netstat -netupl | egrep -v dnsmasq | awk '{print $1,$4,_,$6,$7}'")
|
||||||
|
|
||||||
|
if not ps or not nets then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
for line in nets do
|
||||||
|
-- tcp 0 0 127.0.0.1:1234 0.0.0.0:* LISTEN 5103/v2ray
|
||||||
|
-- udp 0 0 127.0.0.1:1234 0.0.0.0:* 5147/v2ray
|
||||||
|
-- local proto, ip, port, nid = line:match("([^%s]+) +.* +([^ ]*):(%d+) +.* +(%d+)\/.*")
|
||||||
|
local proto, ip, port, nid = line:match("([^%s]+) (.*):(%d+)[^%d]+(%d+)\/.*")
|
||||||
|
local idx = tonumber(nid)
|
||||||
|
if idx and ip then
|
||||||
|
local newstr = "://" .. ip .. ":" .. port
|
||||||
|
local isnew = (netf[idx] and netf[idx]['listen']) and netf[idx]['listen']:match(proto .. newstr) or false
|
||||||
|
netf[idx] = {
|
||||||
|
['listen'] = ((netf[idx] and netf[idx]['listen']) and (not isnew) and (netf[idx]['listen'] .. "\n" .. proto) or proto) .. newstr,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- 5103 1 root S 661m 543% 0% /usr/bin/v2ray/v2ray -config /var/etc/shadowsocksr.json
|
||||||
|
for line in ps do
|
||||||
|
local pid, ppid, user, stat, vsz, mem, cpu, cmd = line:match(
|
||||||
|
"^ *(%d+) +(%d+) +(%S.-%S) +([RSDZTW][W ][<N ]) +(%d+.?) +(%d+%%) +(%d+%%) +(.+)"
|
||||||
|
)
|
||||||
|
if cmd then
|
||||||
|
local idx = tonumber(pid)
|
||||||
|
local bin, param, cfg = cmd:match("^.*\/([^ ]*) *([^ ]*) *\/var\/etc\/([^ ]*).*")
|
||||||
|
if idx and cfg then
|
||||||
|
local listen = "NONE"
|
||||||
|
if netf[idx] and netf[idx]['listen'] then
|
||||||
|
listen = netf[idx]['listen']
|
||||||
|
end
|
||||||
|
data[idx] = {
|
||||||
|
['PID'] = pid,
|
||||||
|
['COMMAND'] = bin,
|
||||||
|
['LISTEN'] = listen,
|
||||||
|
['CONFIG'] = cfg,
|
||||||
|
['%MEM'] = mem,
|
||||||
|
['%CPU'] = cpu,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return data
|
||||||
|
end
|
||||||
|
|
||||||
|
function printstat(status, form, name)
|
||||||
|
local tabs = {
|
||||||
|
["Global Client"] = "shadowsocksr.json",
|
||||||
|
["Game Mode UDP Relay"] = "shadowsocksr_u.json",
|
||||||
|
["PDNSD"] = "pdnsd.conf",
|
||||||
|
["DNS Forward"] = "shadowsocksr_d.json",
|
||||||
|
["SOCKS5 Proxy"] = "shadowsocksr_s.json",
|
||||||
|
["Global SSR Server"] = "shadowsocksr_0.json",
|
||||||
|
|
||||||
|
}
|
||||||
|
local stat = translate("Unknown")
|
||||||
|
local sname = stat
|
||||||
|
if tabs[name] and status then
|
||||||
|
stat = translate("Not Running")
|
||||||
|
for idx, cfg in pairs(status) do
|
||||||
|
if status[idx]['CONFIG'] and status[idx]['CONFIG'] == tabs[name] then
|
||||||
|
stat = font_blue .. bold_on .. translate("Running") .. bold_off .. " > " .. status[idx]['COMMAND'] .. " -c " .. status[idx]['CONFIG'] .. font_off
|
||||||
|
sname = translate(status[idx]['COMMAND'])
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local section = form:field(DummyValue,name,translate(name) .. ": " .. sname)
|
||||||
|
section.rawhtml = true
|
||||||
|
section.value = stat
|
||||||
|
return section
|
||||||
|
end
|
||||||
|
|
||||||
|
procs=processlist()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -187,7 +269,12 @@ m = SimpleForm("Version")
|
|||||||
m.reset = false
|
m.reset = false
|
||||||
m.submit = false
|
m.submit = false
|
||||||
|
|
||||||
|
t = m:section(Table, procs, translate("Running Details: ") .. "(/var/etc)")
|
||||||
|
t:option(DummyValue, "PID", translate("PID"))
|
||||||
|
t:option(DummyValue, "COMMAND", translate("CMD"))
|
||||||
|
t:option(DummyValue, "LISTEN", translate("LISTEN"))
|
||||||
|
t:option(DummyValue, "%CPU", translate("CPU"))
|
||||||
|
t:option(DummyValue, "%MEM", translate("MEM"))
|
||||||
|
|
||||||
s=m:field(DummyValue,"redir_run",translate("Global Client"))
|
s=m:field(DummyValue,"redir_run",translate("Global Client"))
|
||||||
s.rawhtml = true
|
s.rawhtml = true
|
||||||
|
@ -58,6 +58,12 @@ o = s:option(DummyValue, "", "")
|
|||||||
o.rawhtml = true
|
o.rawhtml = true
|
||||||
o.template = "shadowsocksr/update_subscribe"
|
o.template = "shadowsocksr/update_subscribe"
|
||||||
|
|
||||||
|
o = s:option(Button,"update",translate("Update All Subscribe Severs"),translate("No special needs, please click here to subscribe to update"))
|
||||||
|
o.inputstyle = "reload"
|
||||||
|
o.write = function()
|
||||||
|
luci.sys.call("bash /usr/share/shadowsocksr/subscribe.sh >>/tmp/vssr.log 2>&1")
|
||||||
|
luci.http.redirect(luci.dispatcher.build_url("admin", "vpn", "shadowsocksr", "servers"))
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
o = s:option(Button,"delete",translate("Delete All Subscribe Severs"))
|
o = s:option(Button,"delete",translate("Delete All Subscribe Severs"))
|
||||||
|
@ -0,0 +1,109 @@
|
|||||||
|
<!-- ++ BEGIN ++ Auto Repeater ++ logsview.htm ++ -->
|
||||||
|
<%-
|
||||||
|
local values = self:formvalue(section)
|
||||||
|
if not values then
|
||||||
|
values = self:cfgvalue(section) or {self.default}
|
||||||
|
end
|
||||||
|
|
||||||
|
local function serialize_json(x, cb)
|
||||||
|
local rv, push = nil, cb
|
||||||
|
if not push then
|
||||||
|
rv = { }
|
||||||
|
push = function(tok) rv[#rv+1] = tok end
|
||||||
|
end
|
||||||
|
|
||||||
|
if x == nil then
|
||||||
|
push("null")
|
||||||
|
elseif type(x) == "table" then
|
||||||
|
push("[")
|
||||||
|
for k = 1, #x do
|
||||||
|
if k > 1 then
|
||||||
|
push(",")
|
||||||
|
end
|
||||||
|
serialize_json(x[k], push)
|
||||||
|
end
|
||||||
|
push("]")
|
||||||
|
else
|
||||||
|
push('"%s"' % tostring(x):gsub('["%z\1-\31\\]',
|
||||||
|
function(c) return '\\u%04x' % c:byte(1) end))
|
||||||
|
end
|
||||||
|
|
||||||
|
if not cb then
|
||||||
|
return table.concat(rv, "")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-%>
|
||||||
|
<%+cbi/valueheader%>
|
||||||
|
<select class="cbi-input-select" onchange="onclick_logview(this, false)"
|
||||||
|
<%=
|
||||||
|
attr("name", cbid) .. attr("id", cbid) .. attr("value", self.default) .. ifattr(self.size, "size")
|
||||||
|
%>
|
||||||
|
></select>
|
||||||
|
<script type="text/javascript">//<![CDATA[
|
||||||
|
(function() {
|
||||||
|
//var values = <%=serialize_json(values)%>;
|
||||||
|
var keylist = <%=serialize_json(self.keylist)%>;
|
||||||
|
var vallist = <%=serialize_json(self.vallist)%>;
|
||||||
|
var parent = document.getElementById("<%=cbid%>");
|
||||||
|
for (var j = 0; j < keylist.length; j++) {
|
||||||
|
var opt = document.createElement("option");
|
||||||
|
opt.value = keylist[j];
|
||||||
|
if (j == 0) {
|
||||||
|
opt.selected = "selected";
|
||||||
|
}
|
||||||
|
opt.appendChild(document.createTextNode(vallist[j]));
|
||||||
|
parent.appendChild(opt);
|
||||||
|
}
|
||||||
|
}());
|
||||||
|
//]]></script>
|
||||||
|
<script type="text/javascript">//<![CDATA[
|
||||||
|
function onclick_logview(id, bottom) {
|
||||||
|
// get elements
|
||||||
|
var txt = document.getElementById("<%=cbid%>.txt"); // TextArea
|
||||||
|
if ( !txt ) { return; } // security check
|
||||||
|
txt.value= "Reading: " + document.getElementById("<%=cbid%>").value
|
||||||
|
XHR.get('<%=luci.dispatcher.build_url("admin", "vpn", "shadowsocksr", "fileread")%>', {lfile: document.getElementById("<%=cbid%>").value} ,
|
||||||
|
function(x, ifc) {
|
||||||
|
if (! ifc) {txt.value = "XHR.get(<%=luci.dispatcher.build_url("admin", "vpn", "shadowsocksr", "fileread")%>) Failed!"; return;}
|
||||||
|
txt.value = ifc[0];
|
||||||
|
if (bottom)
|
||||||
|
txt.scrollTop = txt.scrollHeight;
|
||||||
|
else
|
||||||
|
txt.scrollTop = 0;
|
||||||
|
txt.scrollLeft = 0;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
//]]></script>
|
||||||
|
|
||||||
|
<%
|
||||||
|
-- one button on top, one at the buttom
|
||||||
|
%>
|
||||||
|
<br /><br />
|
||||||
|
<input class="cbi-button cbi-input-button" style="align: center; width: 100%" type="button" onclick='onclick_logview(this, false)'
|
||||||
|
<%=
|
||||||
|
attr("name", section) .. attr("id", cbid .. ".btn1") .. attr("value", self.inputtitle)
|
||||||
|
%> />
|
||||||
|
|
||||||
|
<br /><br />
|
||||||
|
|
||||||
|
<%
|
||||||
|
-- set a readable style taken from openwrt theme for textarea#syslog
|
||||||
|
-- in openwrt theme there are problems with a width of 100 so we check for theme and set to lower value
|
||||||
|
%>
|
||||||
|
<textarea style="width: <%if media == "/luci-static/openwrt.org" then%>98.7%<%else%>100%<%end%> ; min-height: 200px; border: 3px solid #cccccc; padding: 5px; font-family: monospace; resize: none;" wrap="off" readonly="readonly"
|
||||||
|
<%=
|
||||||
|
attr("name", cbid .. ".txt") .. attr("id", cbid .. ".txt") .. ifattr(self.rows, "rows")
|
||||||
|
%> >
|
||||||
|
<%-=pcdata(self:cfgvalue(section))-%>
|
||||||
|
</textarea>
|
||||||
|
<br /><br />
|
||||||
|
|
||||||
|
<%
|
||||||
|
-- one button on top, one at the buttom
|
||||||
|
%>
|
||||||
|
<input class="cbi-button cbi-input-button" style="align: center; width: 100%" type="button" onclick='onclick_logview(this, true)'
|
||||||
|
<%= attr("name", section) .. attr("id", cbid .. ".btn2") .. attr("value", self.inputtitle) %> />
|
||||||
|
<br /><br />
|
||||||
|
<%+cbi/valuefooter%>
|
||||||
|
<!-- ++ END ++ Auto Repeater ++ logsview.htm ++ -->
|
@ -1,123 +1,109 @@
|
|||||||
<%+cbi/valueheader%>
|
<%+cbi/valueheader%>
|
||||||
<link rel="stylesheet" href="/luci-static/shadowsocksr/css/shadowsocksr.css?v=72883">
|
<link rel="stylesheet" href="/luci-static/shadowsocksr/css/shadowsocksr.css?v=72883">
|
||||||
<label class="cbi-value-title"><%= translate("Update") %></label>
|
<label class="cbi-value-title"><%= translate("Before subscribing please click below to delete all servers in the subscription") %></label>
|
||||||
<div class="cbi-value-field">
|
<div class="cbi-value-field">
|
||||||
<input class="cbi-button cbi-button-reload" id="update_subscribe" type="button"
|
<input class="cbi-button cbi-button-reload" id="update_subscribe" type="button"
|
||||||
size="0" value="<%= translate("Save And Start Subscribe") %>">
|
size="0" value="<%= translate("Save And Start Subscribe") %> ">
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
//<![CDATA[
|
|
||||||
var _responseLen;
|
|
||||||
var noChange = 0;
|
|
||||||
var x = 5;
|
|
||||||
|
|
||||||
var modal = '<div class="modals-bg">' +
|
|
||||||
'<div class="modals">' +
|
|
||||||
'<h2><%:Subscription%></h2>'+
|
|
||||||
'<h3 style="margin-left:0;"><%:Subscribing,Please do not refresh!%></h3>'+
|
|
||||||
'<textarea cols="63" rows="28" wrap="on" readonly="readonly" id="log_content3" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"></textarea>'+
|
|
||||||
'</div>'+
|
|
||||||
'</div>';
|
|
||||||
|
|
||||||
|
|
||||||
function update_subscribe() {
|
|
||||||
$("body").append(modal);
|
|
||||||
$(".modals-bg").show();
|
|
||||||
|
|
||||||
setTimeout("get_realtime_log();", 500);
|
const SAVE_SUBSCRIBE_URL = '<%=luci.dispatcher.build_url("admin", "vpn", "shadowsocksr","subscribe")%>';
|
||||||
}
|
const SERVERS_URL = '<%=luci.dispatcher.build_url("admin", "vpn", "shadowsocksr","servers")%>';
|
||||||
|
|
||||||
function submit_url(){
|
var _responseLen;
|
||||||
prefix_array = $("#cbi-shadowsocksr-server_subscribe .cbi-section-node").attr("id").split("-");
|
var noChange = 0;
|
||||||
|
var modal = '<div class="modals-bg">' +
|
||||||
|
'<div class="modals">' +
|
||||||
|
'<h2><%:Subscription%></h2>' +
|
||||||
|
'<h3 style="margin-left:0;"><%:Subscribing,Please do not refresh!%></h3>' +
|
||||||
|
'<textarea cols="63" rows="28" wrap="on" readonly="readonly" id="log_content3" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"></textarea>' +
|
||||||
|
'</div>' +
|
||||||
|
'</div>';
|
||||||
|
|
||||||
|
//显示并开始刷新订阅
|
||||||
|
function update_subscribe() {
|
||||||
|
$("body").append(modal);
|
||||||
|
$(".modals-bg").show();
|
||||||
|
setTimeout("get_realtime_log();", 500);
|
||||||
|
}
|
||||||
|
//保存订阅按钮
|
||||||
|
$("#update_subscribe").click(function () {
|
||||||
|
prefix_array = $("#cbi-vssr-server_subscribe .cbi-section-node").attr("id").split("-");
|
||||||
prefix_array[0] = "cbid";
|
prefix_array[0] = "cbid";
|
||||||
prefix = prefix_array.join(".");
|
prefix = prefix_array.join(".");
|
||||||
if($("[name='"+prefix+".auto_update']").is(":checked")){
|
if ($("[name='" + prefix + ".auto_update']").is(":checked")) {
|
||||||
var auto_update = "1";
|
var auto_update = "1";
|
||||||
}else{
|
} else {
|
||||||
var auto_update = "0";
|
var auto_update = "0";
|
||||||
}
|
}
|
||||||
var auto_update_time = $("[name='"+prefix+".auto_update_time']").val();
|
var auto_update_time = $("[name='" + prefix + ".auto_update_time']").val();
|
||||||
var subscribe_url = [];
|
var subscribe_url = [];
|
||||||
$("[name='"+prefix+".subscribe_url']").each(function(){
|
$("[name='" + prefix + ".subscribe_url']").each(function () {
|
||||||
if($(this).val() != ""){
|
if ($(this).val() != "") {
|
||||||
subscribe_url.push($(this).val());
|
subscribe_url.push($(this).val());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if($("[name='"+prefix+".proxy']").is(":checked")){
|
if ($("[name='" + prefix + ".proxy']").is(":checked")) {
|
||||||
var proxy = "1";
|
var proxy = "1";
|
||||||
}else{
|
} else {
|
||||||
var proxy = "0";
|
var proxy = "0";
|
||||||
}
|
}
|
||||||
|
|
||||||
var data = {
|
var data = {
|
||||||
auto_update:auto_update,
|
auto_update: auto_update,
|
||||||
auto_update_time : auto_update_time,
|
auto_update_time: auto_update_time,
|
||||||
subscribe_url: JSON.stringify(subscribe_url),
|
subscribe_url: JSON.stringify(subscribe_url),
|
||||||
proxy: proxy
|
proxy: proxy
|
||||||
}
|
}
|
||||||
//console.log(data);
|
//console.log(data);
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: "post",
|
type: "post",
|
||||||
url: "<%=luci.dispatcher.build_url('admin', 'vpn', 'shadowsocksr','subscribe')%>",
|
url: SAVE_SUBSCRIBE_URL,
|
||||||
dataType : "json",
|
dataType: "json",
|
||||||
data: data,
|
data: data,
|
||||||
success: function (d) {
|
success: function (d) {
|
||||||
if(d.error == 0){
|
if (d.error == 0) {
|
||||||
//console.log("开始订阅");
|
update_subscribe();
|
||||||
update_subscribe();
|
} else {
|
||||||
}else{
|
alert("请至少填写一个订阅链接");
|
||||||
alert("请至少填写一个订阅链接");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
|
|
||||||
$("#update_subscribe").click(function(){
|
|
||||||
//console.log("提交数据");
|
|
||||||
submit_url();
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
function refresh_page(){
|
|
||||||
location.reload();
|
function get_realtime_log() {
|
||||||
return false;
|
$.ajax({
|
||||||
|
url: '/check_update.htm?v=' + parseInt(Math.random() * 100000000),
|
||||||
|
dataType: 'html',
|
||||||
|
error: function (xhr) {
|
||||||
|
setTimeout("get_realtime_log();", 1000);
|
||||||
|
},
|
||||||
|
success: function (response) {
|
||||||
|
var retArea = document.getElementById("log_content3");
|
||||||
|
if (response.search("END SUBSCRIBE") != -1) {
|
||||||
|
noChange++;
|
||||||
|
}
|
||||||
|
console.log(noChange);
|
||||||
|
if (noChange > 10) {
|
||||||
|
window.location.href = SERVERS_URL;
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
setTimeout("get_realtime_log();", 250);
|
||||||
|
}
|
||||||
|
retArea.value = response;
|
||||||
|
retArea.scrollTop = retArea.scrollHeight;
|
||||||
|
_responseLen = response.length;
|
||||||
|
},
|
||||||
|
error: function () {
|
||||||
|
setTimeout("get_realtime_log();", 500);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function get_realtime_log() {
|
|
||||||
$.ajax({
|
|
||||||
url: '/check_update.htm?v='+parseInt(Math.random() * 100000000),
|
|
||||||
dataType: 'html',
|
|
||||||
error: function (xhr) {
|
|
||||||
setTimeout("get_realtime_log();", 1000);
|
|
||||||
},
|
|
||||||
success: function (response) {
|
|
||||||
var retArea = document.getElementById("log_content3");
|
|
||||||
if (response.search("END SUBSCRIBE") != -1) {
|
|
||||||
noChange++;
|
|
||||||
}
|
|
||||||
console.log(noChange);
|
|
||||||
if (noChange > 10) {
|
|
||||||
window.location.href="<%=luci.dispatcher.build_url('admin', 'vpn', 'shadowsocksr','servers')%>"
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
setTimeout("get_realtime_log();", 250);
|
|
||||||
}
|
|
||||||
retArea.value = response;
|
|
||||||
retArea.scrollTop = retArea.scrollHeight;
|
|
||||||
_responseLen = response.length;
|
|
||||||
},
|
|
||||||
error: function () {
|
|
||||||
setTimeout("get_realtime_log();", 500);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//]]>
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<%+cbi/valuefooter%>
|
<%+cbi/valuefooter%>
|
@ -853,4 +853,14 @@ msgstr "SS/SSR/V2RAY 服务端"
|
|||||||
msgid "Delete All Subscribe Severs"
|
msgid "Delete All Subscribe Severs"
|
||||||
msgstr "删除所有订阅服务器节点"
|
msgstr "删除所有订阅服务器节点"
|
||||||
|
|
||||||
|
msgid "Update All Subscribe Severs"
|
||||||
|
msgstr "更新所有订阅服务器节点"
|
||||||
|
|
||||||
|
msgid "No special needs, please click here to subscribe to update"
|
||||||
|
msgstr "没有特殊需要,请点击这里订阅更新"
|
||||||
|
|
||||||
|
msgid "Running Details:"
|
||||||
|
msgstr "进程详情:"
|
||||||
|
|
||||||
|
msgid "File Viewer"
|
||||||
|
msgstr "文件查看器"
|
||||||
|
@ -217,7 +217,7 @@ start_rules() {
|
|||||||
|
|
||||||
local local_port=$(uci_get_by_name $GLOBAL_SERVER local_port)
|
local local_port=$(uci_get_by_name $GLOBAL_SERVER local_port)
|
||||||
local lan_ac_ips=$(uci_get_by_type access_control lan_ac_ips)
|
local lan_ac_ips=$(uci_get_by_type access_control lan_ac_ips)
|
||||||
local lan_ac_mode="b"
|
local lan_ac_mode=$(uci_get_by_type access_control lan_ac_mode)
|
||||||
local router_proxy=$(uci_get_by_type access_control router_proxy)
|
local router_proxy=$(uci_get_by_type access_control router_proxy)
|
||||||
if [ "$GLOBAL_SERVER" = "$UDP_RELAY_SERVER" -a $kcp_flag = 0 ]; then
|
if [ "$GLOBAL_SERVER" = "$UDP_RELAY_SERVER" -a $kcp_flag = 0 ]; then
|
||||||
ARG_UDP="-u"
|
ARG_UDP="-u"
|
||||||
@ -263,6 +263,7 @@ start_rules() {
|
|||||||
-i "$(uci_get_by_type access_control wan_bp_list)" \
|
-i "$(uci_get_by_type access_control wan_bp_list)" \
|
||||||
-b "$(uci_get_by_type access_control wan_bp_ips)" \
|
-b "$(uci_get_by_type access_control wan_bp_ips)" \
|
||||||
-w "$(uci_get_by_type access_control wan_fw_ips)" \
|
-w "$(uci_get_by_type access_control wan_fw_ips)" \
|
||||||
|
-B "$(uci_get_by_type access_control lan_bp_ips)" \
|
||||||
-p "$(uci_get_by_type access_control lan_fp_ips)" \
|
-p "$(uci_get_by_type access_control lan_fp_ips)" \
|
||||||
-G "$(uci_get_by_type access_control lan_gm_ips)" \
|
-G "$(uci_get_by_type access_control lan_gm_ips)" \
|
||||||
-D "$proxyport" \
|
-D "$proxyport" \
|
||||||
@ -679,11 +680,13 @@ start_redir() {
|
|||||||
for i in $(seq 1 $threads); do
|
for i in $(seq 1 $threads); do
|
||||||
$sscmd --config /var/etc/trojan-ssr-retcp.json >/dev/null 2>&1 &
|
$sscmd --config /var/etc/trojan-ssr-retcp.json >/dev/null 2>&1 &
|
||||||
done
|
done
|
||||||
echo "$(date "+%Y-%m-%d %H:%M:%S") $($sscmd --version 2>&1 | head -1) Started!" >> /tmp/ssrplus.log
|
echo "$(date "+%Y-%m-%d %H:%M:%S") $($sscmd --version 2>&1 | head -1) $threads Threads Started!" >> /tmp/ssrplus.log
|
||||||
|
|
||||||
elif [ "$stype" == "v2ray" ] ;then
|
elif [ "$stype" == "v2ray" ] ;then
|
||||||
|
for i in $(seq 1 $threads); do
|
||||||
$sscmd -config /var/etc/v2-ssr-retcp.json >/dev/null 2>&1 &
|
$sscmd -config /var/etc/v2-ssr-retcp.json >/dev/null 2>&1 &
|
||||||
echo "$(date "+%Y-%m-%d %H:%M:%S") $($sscmd -version | head -1) Started!" >> /tmp/ssrplus.log
|
done
|
||||||
|
echo "$(date "+%Y-%m-%d %H:%M:%S") $($sscmd -version | head -1) $threads Threads Started!" >> /tmp/ssrplus.log
|
||||||
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
@ -26,6 +26,7 @@ usage() {
|
|||||||
define access control mode
|
define access control mode
|
||||||
-b <wan_ips> wan ip of will be bypassed
|
-b <wan_ips> wan ip of will be bypassed
|
||||||
-w <wan_ips> wan ip of will be forwarded
|
-w <wan_ips> wan ip of will be forwarded
|
||||||
|
-B <bp_lan_ips> lan ip of will be bypassed proxy
|
||||||
-p <fp_lan_ips> lan ip of will be global proxy
|
-p <fp_lan_ips> lan ip of will be global proxy
|
||||||
-G <gm_lan_ips> lan ip of will be game mode proxy
|
-G <gm_lan_ips> lan ip of will be game mode proxy
|
||||||
-D <proxy_ports> proxy ports
|
-D <proxy_ports> proxy ports
|
||||||
@ -88,7 +89,7 @@ ipset_r() {
|
|||||||
EOF
|
EOF
|
||||||
ipset -N gfwlist hash:net 2>/dev/null
|
ipset -N gfwlist hash:net 2>/dev/null
|
||||||
$IPT -N SS_SPEC_WAN_AC
|
$IPT -N SS_SPEC_WAN_AC
|
||||||
$IPT -I SS_SPEC_WAN_AC -d $server -j RETURN
|
$IPT -I SS_SPEC_WAN_AC -p tcp ! --dport 53 -d $server -j RETURN
|
||||||
$IPT -A SS_SPEC_WAN_AC -m set --match-set ss_spec_wan_ac dst -j RETURN
|
$IPT -A SS_SPEC_WAN_AC -m set --match-set ss_spec_wan_ac dst -j RETURN
|
||||||
$IPT -A SS_SPEC_WAN_AC -j SS_SPEC_WAN_FW
|
$IPT -A SS_SPEC_WAN_AC -j SS_SPEC_WAN_FW
|
||||||
|
|
||||||
@ -99,7 +100,7 @@ EOF
|
|||||||
$IPT -A SS_SPEC_WAN_AC -m set --match-set china dst -j RETURN
|
$IPT -A SS_SPEC_WAN_AC -m set --match-set china dst -j RETURN
|
||||||
$IPT -A SS_SPEC_WAN_AC -m set --match-set gfwlist dst -j SS_SPEC_WAN_FW
|
$IPT -A SS_SPEC_WAN_AC -m set --match-set gfwlist dst -j SS_SPEC_WAN_FW
|
||||||
$IPT -A SS_SPEC_WAN_AC -m set --match-set gmlan src -m set ! --match-set china dst -j SS_SPEC_WAN_FW
|
$IPT -A SS_SPEC_WAN_AC -m set --match-set gmlan src -m set ! --match-set china dst -j SS_SPEC_WAN_FW
|
||||||
$IPT -I SS_SPEC_WAN_AC -d $server -j RETURN
|
$IPT -I SS_SPEC_WAN_AC -p tcp ! --dport 53 -d $server -j RETURN
|
||||||
|
|
||||||
elif [ "$RUNMODE" = "oversea" ] ;then
|
elif [ "$RUNMODE" = "oversea" ] ;then
|
||||||
ipset -N oversea hash:net 2>/dev/null
|
ipset -N oversea hash:net 2>/dev/null
|
||||||
@ -110,7 +111,7 @@ EOF
|
|||||||
|
|
||||||
for ip in $LAN_GM_IP; do ipset -! add gmlan $ip ; done
|
for ip in $LAN_GM_IP; do ipset -! add gmlan $ip ; done
|
||||||
$IPT -A SS_SPEC_WAN_AC -m set --match-set gmlan src -m set --match-set china dst -j SS_SPEC_WAN_FW
|
$IPT -A SS_SPEC_WAN_AC -m set --match-set gmlan src -m set --match-set china dst -j SS_SPEC_WAN_FW
|
||||||
$IPT -I SS_SPEC_WAN_AC -d $server -j RETURN
|
$IPT -I SS_SPEC_WAN_AC -p tcp ! --dport 53 -d $server -j RETURN
|
||||||
|
|
||||||
elif [ "$RUNMODE" = "routers" ] ;then
|
elif [ "$RUNMODE" = "routers" ] ;then
|
||||||
ipset -! -R <<-EOF || return 1
|
ipset -! -R <<-EOF || return 1
|
||||||
@ -127,14 +128,16 @@ EOF
|
|||||||
elif [ "$RUNMODE" = "all" ] ;then
|
elif [ "$RUNMODE" = "all" ] ;then
|
||||||
$IPT -N SS_SPEC_WAN_AC
|
$IPT -N SS_SPEC_WAN_AC
|
||||||
$IPT -A SS_SPEC_WAN_AC -j SS_SPEC_WAN_FW
|
$IPT -A SS_SPEC_WAN_AC -j SS_SPEC_WAN_FW
|
||||||
$IPT -I SS_SPEC_WAN_AC -d $server -j RETURN
|
$IPT -I SS_SPEC_WAN_AC -p tcp ! --dport 53 -d $server -j RETURN
|
||||||
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
ipset -N fplan hash:net 2>/dev/null
|
ipset -N fplan hash:net 2>/dev/null
|
||||||
for ip in $LAN_FP_IP; do ipset -! add fplan $ip ; done
|
for ip in $LAN_FP_IP; do ipset -! add fplan $ip ; done
|
||||||
$IPT -I SS_SPEC_WAN_AC -m set --match-set fplan src -j SS_SPEC_WAN_FW
|
$IPT -I SS_SPEC_WAN_AC -m set --match-set fplan src -j SS_SPEC_WAN_FW
|
||||||
|
ipset -N bplan hash:net 2>/dev/null
|
||||||
|
for ip in $LAN_BP_IP; do ipset -! add bplan $ip; done
|
||||||
|
$IPT -I SS_SPEC_WAN_AC -m set --match-set bplan src -j RETURN
|
||||||
ipset -N whitelist hash:net 2>/dev/null
|
ipset -N whitelist hash:net 2>/dev/null
|
||||||
ipset -N blacklist hash:net 2>/dev/null
|
ipset -N blacklist hash:net 2>/dev/null
|
||||||
$IPT -I SS_SPEC_WAN_AC -m set --match-set blacklist src -j SS_SPEC_WAN_FW
|
$IPT -I SS_SPEC_WAN_AC -m set --match-set blacklist src -j SS_SPEC_WAN_FW
|
||||||
@ -157,6 +160,7 @@ fw_rule() {
|
|||||||
$IPT -A SS_SPEC_WAN_FW -d 224.0.0.0/4 -j RETURN
|
$IPT -A SS_SPEC_WAN_FW -d 224.0.0.0/4 -j RETURN
|
||||||
$IPT -A SS_SPEC_WAN_FW -d 240.0.0.0/4 -j RETURN
|
$IPT -A SS_SPEC_WAN_FW -d 240.0.0.0/4 -j RETURN
|
||||||
$IPT -A SS_SPEC_WAN_FW -p tcp $PROXY_PORTS \
|
$IPT -A SS_SPEC_WAN_FW -p tcp $PROXY_PORTS \
|
||||||
|
$IPT -A SS_SPEC_WAN_FW -p tcp $PROXY_PORTS \
|
||||||
-j REDIRECT --to-ports $local_port 2>/dev/null || {
|
-j REDIRECT --to-ports $local_port 2>/dev/null || {
|
||||||
loger 3 "Can't redirect, please check the iptables."
|
loger 3 "Can't redirect, please check the iptables."
|
||||||
exit 1
|
exit 1
|
||||||
@ -217,8 +221,8 @@ tp_rule() {
|
|||||||
$ipt -A SS_SPEC_TPROXY -p udp -d 192.168.0.0/16 -j RETURN
|
$ipt -A SS_SPEC_TPROXY -p udp -d 192.168.0.0/16 -j RETURN
|
||||||
$ipt -A SS_SPEC_TPROXY -p udp -d 224.0.0.0/4 -j RETURN
|
$ipt -A SS_SPEC_TPROXY -p udp -d 224.0.0.0/4 -j RETURN
|
||||||
$ipt -A SS_SPEC_TPROXY -p udp -d 240.0.0.0/4 -j RETURN
|
$ipt -A SS_SPEC_TPROXY -p udp -d 240.0.0.0/4 -j RETURN
|
||||||
$ipt -A SS_SPEC_TPROXY -p udp -d $SERVER -j RETURN
|
$ipt -A SS_SPEC_TPROXY -p udp ! --dport 53 -d $server -j RETURN
|
||||||
|
$ipt -A SS_SPEC_TPROXY -p udp -m set --match-set bplan src -j RETURN
|
||||||
$ipt -A SS_SPEC_TPROXY -p udp $PROXY_PORTS -m set --match-set fplan src \
|
$ipt -A SS_SPEC_TPROXY -p udp $PROXY_PORTS -m set --match-set fplan src \
|
||||||
-j TPROXY --on-port "$LOCAL_PORT" --tproxy-mark 0x01/0x01
|
-j TPROXY --on-port "$LOCAL_PORT" --tproxy-mark 0x01/0x01
|
||||||
|
|
||||||
@ -326,7 +330,7 @@ EOF
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
while getopts ":s:l:S:L:i:e:a:b:w:p:G:D:k:oOuUfgrRczh" arg; do
|
while getopts ":s:l:S:L:i:e:a:B:b:w:p:G:D:k:oOuUfgrRczh" arg; do
|
||||||
case "$arg" in
|
case "$arg" in
|
||||||
s)
|
s)
|
||||||
server=$OPTARG
|
server=$OPTARG
|
||||||
@ -349,6 +353,11 @@ while getopts ":s:l:S:L:i:e:a:b:w:p:G:D:k:oOuUfgrRczh" arg; do
|
|||||||
a)
|
a)
|
||||||
LAN_AC_IP=$OPTARG
|
LAN_AC_IP=$OPTARG
|
||||||
;;
|
;;
|
||||||
|
|
||||||
|
B)
|
||||||
|
LAN_BP_IP=$OPTARG
|
||||||
|
;;
|
||||||
|
|
||||||
b)
|
b)
|
||||||
WAN_BP_IP=$(for ip in $OPTARG; do echo $ip; done)
|
WAN_BP_IP=$(for ip in $OPTARG; do echo $ip; done)
|
||||||
;;
|
;;
|
||||||
|
@ -0,0 +1,440 @@
|
|||||||
|
#!/usr/bin/lua
|
||||||
|
------------------------------------------------
|
||||||
|
-- This file is part of the luci-app-ssr-plus subscribe.lua
|
||||||
|
-- @author William Chan <root@williamchan.me>
|
||||||
|
------------------------------------------------
|
||||||
|
require 'nixio'
|
||||||
|
require 'luci.util'
|
||||||
|
require 'luci.jsonc'
|
||||||
|
require 'luci.sys'
|
||||||
|
|
||||||
|
-- these global functions are accessed all the time by the event handler
|
||||||
|
-- so caching them is worth the effort
|
||||||
|
local luci = luci
|
||||||
|
local tinsert = table.insert
|
||||||
|
local ssub, slen, schar, sbyte, sformat, sgsub = string.sub, string.len, string.char, string.byte, string.format, string.gsub
|
||||||
|
local jsonParse, jsonStringify = luci.jsonc.parse, luci.jsonc.stringify
|
||||||
|
local b64decode = nixio.bin.b64decode
|
||||||
|
local cache = {}
|
||||||
|
local nodeResult = setmetatable({}, { __index = cache }) -- update result
|
||||||
|
local name = 'shadowsocksr'
|
||||||
|
local uciType = 'servers'
|
||||||
|
local ucic = luci.model.uci.cursor()
|
||||||
|
local proxy = ucic:get_first(name, 'server_subscribe', 'proxy', '0')
|
||||||
|
local switch = ucic:get_first(name, 'server_subscribe', 'switch', '1')
|
||||||
|
local subscribe_url = ucic:get_first(name, 'server_subscribe', 'subscribe_url', {})
|
||||||
|
|
||||||
|
local log = function(...)
|
||||||
|
print(os.date("%Y-%m-%d %H:%M:%S ") .. table.concat({ ... }, " "))
|
||||||
|
end
|
||||||
|
-- 分割字符串
|
||||||
|
local function split(full, sep)
|
||||||
|
full = full:gsub("%z", "") -- 这里不是很清楚 有时候结尾带个\0
|
||||||
|
local off, result = 1, {}
|
||||||
|
while true do
|
||||||
|
local nStart, nEnd = full:find(sep, off)
|
||||||
|
if not nEnd then
|
||||||
|
local res = ssub(full, off, slen(full))
|
||||||
|
if #res > 0 then -- 过滤掉 \0
|
||||||
|
tinsert(result, res)
|
||||||
|
end
|
||||||
|
break
|
||||||
|
else
|
||||||
|
tinsert(result, ssub(full, off, nStart - 1))
|
||||||
|
off = nEnd + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
-- urlencode
|
||||||
|
local function get_urlencode(c)
|
||||||
|
return sformat("%%%02X", sbyte(c))
|
||||||
|
end
|
||||||
|
|
||||||
|
local function urlEncode(szText)
|
||||||
|
local str = szText:gsub("([^0-9a-zA-Z ])", get_urlencode)
|
||||||
|
str = str:gsub(" ", "+")
|
||||||
|
return str
|
||||||
|
end
|
||||||
|
|
||||||
|
local function get_urldecode(h)
|
||||||
|
return schar(tonumber(h, 16))
|
||||||
|
end
|
||||||
|
local function UrlDecode(szText)
|
||||||
|
return szText:gsub("+", " "):gsub("%%(%x%x)", get_urldecode)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- trim
|
||||||
|
local function trim(text)
|
||||||
|
if not text or text == "" then
|
||||||
|
return ""
|
||||||
|
end
|
||||||
|
return (sgsub(text, "^%s*(.-)%s*$", "%1"))
|
||||||
|
end
|
||||||
|
-- md5
|
||||||
|
local function md5(content)
|
||||||
|
local stdout = luci.sys.exec('echo \"' .. urlEncode(content) .. '\" | md5sum | cut -d \" \" -f1')
|
||||||
|
-- assert(nixio.errno() == 0)
|
||||||
|
return trim(stdout)
|
||||||
|
end
|
||||||
|
-- base64
|
||||||
|
local function base64Decode(text)
|
||||||
|
local raw = text
|
||||||
|
if not text then return '' end
|
||||||
|
text = text:gsub("%z", "")
|
||||||
|
text = text:gsub("_", "/")
|
||||||
|
text = text:gsub("-", "+")
|
||||||
|
local mod4 = #text % 4
|
||||||
|
text = text .. string.sub('====', mod4 + 1)
|
||||||
|
local result = b64decode(text)
|
||||||
|
if result then
|
||||||
|
return result:gsub("%z", "")
|
||||||
|
else
|
||||||
|
return raw
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- 处理数据
|
||||||
|
local function processData(szType, content)
|
||||||
|
local result = {
|
||||||
|
type = szType,
|
||||||
|
local_port = 1234,
|
||||||
|
kcp_param = '--nocomp'
|
||||||
|
}
|
||||||
|
if szType == 'ssr' then
|
||||||
|
local dat = split(content, "/%?")
|
||||||
|
local hostInfo = split(dat[1], ':')
|
||||||
|
result.server = hostInfo[1]
|
||||||
|
result.server_port = hostInfo[2]
|
||||||
|
result.protocol = hostInfo[3]
|
||||||
|
result.encrypt_method = hostInfo[4]
|
||||||
|
result.obfs = hostInfo[5]
|
||||||
|
result.password = base64Decode(hostInfo[6])
|
||||||
|
local params = {}
|
||||||
|
for _, v in pairs(split(dat[2], '&')) do
|
||||||
|
local t = split(v, '=')
|
||||||
|
params[t[1]] = t[2]
|
||||||
|
end
|
||||||
|
result.obfs_param = base64Decode(params.obfsparam)
|
||||||
|
result.protocol_param = base64Decode(params.protoparam)
|
||||||
|
local group = base64Decode(params.group)
|
||||||
|
if group then
|
||||||
|
result.alias = "[" .. group .. "] "
|
||||||
|
end
|
||||||
|
result.alias = result.alias .. base64Decode(params.remarks)
|
||||||
|
elseif szType == 'vmess' then
|
||||||
|
local info = jsonParse(content)
|
||||||
|
result.type = 'v2ray'
|
||||||
|
result.server = info.add
|
||||||
|
result.server_port = info.port
|
||||||
|
result.transport = info.net
|
||||||
|
result.alter_id = info.aid
|
||||||
|
result.vmess_id = info.id
|
||||||
|
result.alias = info.ps
|
||||||
|
result.insecure = 1
|
||||||
|
-- result.mux = 1
|
||||||
|
-- result.concurrency = 8
|
||||||
|
if info.net == 'ws' then
|
||||||
|
result.ws_host = info.host
|
||||||
|
result.ws_path = info.path
|
||||||
|
end
|
||||||
|
if info.net == 'h2' then
|
||||||
|
result.h2_host = info.host
|
||||||
|
result.h2_path = info.path
|
||||||
|
end
|
||||||
|
if info.net == 'tcp' then
|
||||||
|
result.tcp_guise = info.type
|
||||||
|
result.http_host = info.host
|
||||||
|
result.http_path = info.path
|
||||||
|
end
|
||||||
|
if info.net == 'kcp' then
|
||||||
|
result.kcp_guise = info.type
|
||||||
|
result.mtu = 1350
|
||||||
|
result.tti = 50
|
||||||
|
result.uplink_capacity = 5
|
||||||
|
result.downlink_capacity = 20
|
||||||
|
result.read_buffer_size = 2
|
||||||
|
result.write_buffer_size = 2
|
||||||
|
end
|
||||||
|
if info.net == 'quic' then
|
||||||
|
result.quic_guise = info.type
|
||||||
|
result.quic_key = info.key
|
||||||
|
result.quic_security = info.securty
|
||||||
|
end
|
||||||
|
if info.security then
|
||||||
|
result.security = info.security
|
||||||
|
end
|
||||||
|
if info.tls == "tls" or info.tls == "1" then
|
||||||
|
result.tls = "1"
|
||||||
|
result.tls_host = info.host
|
||||||
|
else
|
||||||
|
result.tls = "0"
|
||||||
|
end
|
||||||
|
elseif szType == "ss" then
|
||||||
|
local idx_sp = 0
|
||||||
|
local alias = ""
|
||||||
|
if content:find("#") then
|
||||||
|
idx_sp = content:find("#")
|
||||||
|
alias = content:sub(idx_sp + 1, -1)
|
||||||
|
end
|
||||||
|
local info = content:sub(1, idx_sp - 1)
|
||||||
|
local hostInfo = split(base64Decode(info), "@")
|
||||||
|
local host = split(hostInfo[2], ":")
|
||||||
|
local userinfo = base64Decode(hostInfo[1])
|
||||||
|
local method = userinfo:sub(1, userinfo:find(":") - 1)
|
||||||
|
local password = userinfo:sub(userinfo:find(":") + 1, #userinfo)
|
||||||
|
result.alias = UrlDecode(alias)
|
||||||
|
result.type = "ss"
|
||||||
|
result.server = host[1]
|
||||||
|
if host[2]:find("/%?") then
|
||||||
|
local query = split(host[2], "/%?")
|
||||||
|
result.server_port = query[1]
|
||||||
|
local params = {}
|
||||||
|
for _, v in pairs(split(query[2], '&')) do
|
||||||
|
local t = split(v, '=')
|
||||||
|
params[t[1]] = t[2]
|
||||||
|
end
|
||||||
|
if params.plugin then
|
||||||
|
local plugin_info = UrlDecode(params.plugin)
|
||||||
|
local idx_pn = plugin_info:find(";")
|
||||||
|
if idx_pn then
|
||||||
|
result.plugin = plugin_info:sub(1, idx_pn - 1)
|
||||||
|
result.plugin_opts = plugin_info:sub(idx_pn + 1, #plugin_info)
|
||||||
|
else
|
||||||
|
result.plugin = plugin_info
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
result.server_port = host[2]
|
||||||
|
end
|
||||||
|
result.encrypt_method_ss = method
|
||||||
|
result.password = password
|
||||||
|
elseif szType == "ssd" then
|
||||||
|
result.type = "ss"
|
||||||
|
result.server = content.server
|
||||||
|
result.server_port = content.port
|
||||||
|
result.password = content.password
|
||||||
|
result.encrypt_method_ss = content.encryption
|
||||||
|
result.plugin = content.plugin
|
||||||
|
result.plugin_opts = content.plugin_options
|
||||||
|
result.alias = "[" .. content.airport .. "] " .. content.remarks
|
||||||
|
elseif szType == "trojan" then
|
||||||
|
local idx_sp = 0
|
||||||
|
local alias = ""
|
||||||
|
if content:find("#") then
|
||||||
|
idx_sp = content:find("#")
|
||||||
|
alias = content:sub(idx_sp + 1, -1)
|
||||||
|
end
|
||||||
|
local info = content:sub(1, idx_sp - 1)
|
||||||
|
local hostInfo = split(info, "@")
|
||||||
|
local host = split(hostInfo[2], ":")
|
||||||
|
local userinfo = hostInfo[1]
|
||||||
|
local password = userinfo
|
||||||
|
result.alias = UrlDecode(alias)
|
||||||
|
result.type = "trojan"
|
||||||
|
result.server = host[1]
|
||||||
|
-- 按照官方的建议 默认验证ssl证书
|
||||||
|
result.insecure = "0"
|
||||||
|
result.tls = "1"
|
||||||
|
if host[2]:find("?") then
|
||||||
|
local query = split(host[2], "?")
|
||||||
|
result.server_port = query[1]
|
||||||
|
local params = {}
|
||||||
|
for _, v in pairs(split(query[2], '&')) do
|
||||||
|
local t = split(v, '=')
|
||||||
|
params[t[1]] = t[2]
|
||||||
|
end
|
||||||
|
|
||||||
|
if params.peer then
|
||||||
|
-- 未指定peer(sni)默认使用remote addr
|
||||||
|
result.tls_host = params.peer
|
||||||
|
end
|
||||||
|
|
||||||
|
if params.allowInsecure == "1" then
|
||||||
|
result.insecure = "1"
|
||||||
|
else
|
||||||
|
result.insecure = "0"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
result.server_port = host[2]
|
||||||
|
end
|
||||||
|
result.password = password
|
||||||
|
end
|
||||||
|
if not result.alias then
|
||||||
|
result.alias = result.server .. ':' .. result.server_port
|
||||||
|
end
|
||||||
|
|
||||||
|
-- alias 不参与 hashkey 计算
|
||||||
|
local alias = result.alias
|
||||||
|
result.alias = nil
|
||||||
|
local switch_enable = result.switch_enable
|
||||||
|
result.switch_enable = nil
|
||||||
|
result.hashkey = md5(jsonStringify(result))
|
||||||
|
result.alias = alias
|
||||||
|
result.switch_enable = switch_enable
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
-- wget
|
||||||
|
local function wget(url)
|
||||||
|
local stdout = luci.sys.exec('wget-ssl --user-agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36" --no-check-certificate -t 3 -T 10 -O- "' .. url .. '"')
|
||||||
|
return trim(stdout)
|
||||||
|
end
|
||||||
|
|
||||||
|
local execute = function()
|
||||||
|
-- exec
|
||||||
|
do
|
||||||
|
if proxy == '0' then -- 不使用代理更新的话先暂停
|
||||||
|
log('服务正在暂停')
|
||||||
|
luci.sys.init.stop(name)
|
||||||
|
end
|
||||||
|
for k, url in ipairs(subscribe_url) do
|
||||||
|
local raw = wget(url)
|
||||||
|
if #raw > 0 then
|
||||||
|
local nodes, szType
|
||||||
|
local groupHash = md5(url)
|
||||||
|
cache[groupHash] = {}
|
||||||
|
tinsert(nodeResult, {})
|
||||||
|
local index = #nodeResult
|
||||||
|
-- SSD 似乎是这种格式 ssd:// 开头的
|
||||||
|
if raw:find('ssd://') then
|
||||||
|
szType = 'ssd'
|
||||||
|
local nEnd = select(2, raw:find('ssd://'))
|
||||||
|
nodes = base64Decode(raw:sub(nEnd + 1, #raw))
|
||||||
|
nodes = jsonParse(nodes)
|
||||||
|
local extra = {
|
||||||
|
airport = nodes.airport,
|
||||||
|
port = nodes.port,
|
||||||
|
encryption = nodes.encryption,
|
||||||
|
password = nodes.password
|
||||||
|
}
|
||||||
|
local servers = {}
|
||||||
|
-- SS里面包着 干脆直接这样
|
||||||
|
for _, server in ipairs(nodes.servers) do
|
||||||
|
tinsert(servers, setmetatable(server, { __index = extra }))
|
||||||
|
end
|
||||||
|
nodes = servers
|
||||||
|
else
|
||||||
|
-- ssd 外的格式
|
||||||
|
nodes = split(base64Decode(raw):gsub(" ", "\n"), "\n")
|
||||||
|
end
|
||||||
|
for _, v in ipairs(nodes) do
|
||||||
|
if v then
|
||||||
|
local result
|
||||||
|
if szType == 'ssd' then
|
||||||
|
result = processData(szType, v)
|
||||||
|
elseif not szType then
|
||||||
|
local node = trim(v)
|
||||||
|
local dat = split(node, "://")
|
||||||
|
if dat and dat[1] and dat[2] then
|
||||||
|
if dat[1] == 'ss' or dat[1] == 'trojan' then
|
||||||
|
result = processData(dat[1], dat[2])
|
||||||
|
else
|
||||||
|
result = processData(dat[1], base64Decode(dat[2]))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
log('跳过未知类型: ' .. szType)
|
||||||
|
end
|
||||||
|
-- log(result)
|
||||||
|
if result then
|
||||||
|
if result.alias:find("过期时间") or
|
||||||
|
result.alias:find("剩余流量") or
|
||||||
|
result.alias:find("QQ群") or
|
||||||
|
result.alias:find("官网") or
|
||||||
|
result.alias:find("防失联地址") or
|
||||||
|
not result.server or
|
||||||
|
result.server:match("[^0-9a-zA-Z%-%.%s]") -- 中文做地址的 也没有人拿中文域名搞,就算中文域也有Puny Code SB 机场
|
||||||
|
then
|
||||||
|
log('丢弃无效节点: ' .. result.type ..' 节点, ' .. result.alias)
|
||||||
|
else
|
||||||
|
log('成功解析: ' .. result.type ..' 节点, ' .. result.alias)
|
||||||
|
result.grouphashkey = groupHash
|
||||||
|
tinsert(nodeResult[index], result)
|
||||||
|
cache[groupHash][result.hashkey] = nodeResult[index][#nodeResult[index]]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
log('成功解析节点数量: ' ..#nodes)
|
||||||
|
else
|
||||||
|
log(url .. ': 获取内容为空')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- diff
|
||||||
|
do
|
||||||
|
if next(nodeResult) == nil then
|
||||||
|
log("更新失败,没有可用的节点信息")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local add, del = 0, 0
|
||||||
|
ucic:foreach(name, uciType, function(old)
|
||||||
|
if old.grouphashkey or old.hashkey then -- 没有 hash 的不参与删除
|
||||||
|
if not nodeResult[old.grouphashkey] or not nodeResult[old.grouphashkey][old.hashkey] then
|
||||||
|
ucic:delete(name, old['.name'])
|
||||||
|
del = del + 1
|
||||||
|
else
|
||||||
|
local dat = nodeResult[old.grouphashkey][old.hashkey]
|
||||||
|
ucic:tset(name, old['.name'], dat)
|
||||||
|
-- 标记一下
|
||||||
|
setmetatable(nodeResult[old.grouphashkey][old.hashkey], { __index = { _ignore = true } })
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if not old.alias then
|
||||||
|
old.alias = old.server .. ':' .. old.server_port
|
||||||
|
end
|
||||||
|
log('忽略手动添加的节点: ' .. old.alias)
|
||||||
|
end
|
||||||
|
|
||||||
|
end)
|
||||||
|
for k, v in ipairs(nodeResult) do
|
||||||
|
for kk, vv in ipairs(v) do
|
||||||
|
if not vv._ignore then
|
||||||
|
local section = ucic:add(name, uciType)
|
||||||
|
ucic:tset(name, section, vv)
|
||||||
|
ucic:set(name, section, "switch_enable", switch)
|
||||||
|
add = add + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
ucic:commit(name)
|
||||||
|
-- 如果原有服务器节点已经不见了就尝试换为第一个节点
|
||||||
|
local globalServer = ucic:get_first(name, 'global', 'global_server', '')
|
||||||
|
local firstServer = ucic:get_first(name, uciType)
|
||||||
|
if firstServer then
|
||||||
|
if not ucic:get(name, globalServer) then
|
||||||
|
luci.sys.call("/etc/init.d/" .. name .. " stop > /dev/null 2>&1 &")
|
||||||
|
ucic:commit(name)
|
||||||
|
ucic:set(name, ucic:get_first(name, 'global'), 'global_server', ucic:get_first(name, uciType))
|
||||||
|
ucic:commit(name)
|
||||||
|
log('当前主服务器节点已被删除,正在自动更换为第一个节点。')
|
||||||
|
luci.sys.call("/etc/init.d/" .. name .. " start > /dev/null 2>&1 &")
|
||||||
|
else
|
||||||
|
log('维持当前主服务器节点。')
|
||||||
|
luci.sys.call("/etc/init.d/" .. name .." restart > /dev/null 2>&1 &")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
log('没有服务器节点了,停止服务')
|
||||||
|
luci.sys.call("/etc/init.d/" .. name .. " stop > /dev/null 2>&1 &")
|
||||||
|
end
|
||||||
|
log('新增节点数量: ' ..add, '删除节点数量: ' .. del)
|
||||||
|
log("END SUBSCRIBE")
|
||||||
|
log('订阅更新成功')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if subscribe_url and #subscribe_url > 0 then
|
||||||
|
xpcall(execute, function(e)
|
||||||
|
log(e)
|
||||||
|
log(debug.traceback())
|
||||||
|
log("END SUBSCRIBE")
|
||||||
|
log('发生错误, 正在恢复服务')
|
||||||
|
local firstServer = ucic:get_first(name, uciType)
|
||||||
|
if firstServer then
|
||||||
|
luci.sys.call("/etc/init.d/" .. name .." restart > /dev/null 2>&1 &") -- 不加&的话日志会出现的更早
|
||||||
|
log('重启服务成功')
|
||||||
|
else
|
||||||
|
luci.sys.call("/etc/init.d/" .. name .." stop > /dev/null 2>&1 &") -- 不加&的话日志会出现的更早
|
||||||
|
log('停止服务成功')
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
Loading…
x
Reference in New Issue
Block a user