luci-app-v2ray-server: fix bug (#3481)

This commit is contained in:
Lienol 2020-03-02 21:57:15 +08:00 committed by GitHub
parent e8987b91ec
commit f178a4f6d7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 521 additions and 1 deletions

View File

@ -12,6 +12,10 @@ function index()
entry({"admin", "vpn", "v2ray_server", "users_status"},
call("v2ray_users_status")).leaf = true
entry({"admin", "vpn", "v2ray_server", "check"}, call("v2ray_check")).leaf =
true
entry({"admin", "vpn", "v2ray_server", "update"}, call("v2ray_update")).leaf =
true
entry({"admin", "vpn", "v2ray_server", "get_log"}, call("get_log")).leaf =
true
entry({"admin", "vpn", "v2ray_server", "clear_log"}, call("clear_log")).leaf =
@ -32,6 +36,26 @@ function v2ray_users_status()
http_write_json(e)
end
function v2ray_check()
local json = v2ray.to_check("")
http_write_json(json)
end
function v2ray_update()
local json = nil
local task = http.formvalue("task")
if task == "extract" then
json =
v2ray.to_extract(http.formvalue("file"), http.formvalue("subfix"))
elseif task == "move" then
json = v2ray.to_move(http.formvalue("file"))
else
json = v2ray.to_download(http.formvalue("url"))
end
http_write_json(json)
end
function get_log()
luci.http.write(luci.sys.exec(
"[ -f '/var/log/v2ray_server/app.log' ] && cat /var/log/v2ray_server/app.log"))

View File

@ -0,0 +1,328 @@
module("luci.model.cbi.v2ray_server.api.v2ray", package.seeall)
local fs = require "nixio.fs"
local sys = require "luci.sys"
local uci = require"luci.model.uci".cursor()
local util = require "luci.util"
local i18n = require "luci.i18n"
local ipkg = require "luci.model.ipkg"
local appname = "v2ray_server"
local v2ray_api =
"https://api.github.com/repos/v2ray/v2ray-core/releases/latest"
local wget = "/usr/bin/wget"
local wget_args = {
"--no-check-certificate", "--quiet", "--timeout=100", "--tries=3"
}
local command_timeout = 300
local LEDE_BOARD = nil
local DISTRIB_TARGET = nil
local is_armv7 = false
local function _unpack(t, i)
i = i or 1
if t[i] ~= nil then return t[i], _unpack(t, i + 1) end
end
local function exec(cmd, args, writer, timeout)
local os = require "os"
local nixio = require "nixio"
local fdi, fdo = nixio.pipe()
local pid = nixio.fork()
if pid > 0 then
fdo:close()
if writer or timeout then
local starttime = os.time()
while true do
if timeout and os.difftime(os.time(), starttime) >= timeout then
nixio.kill(pid, nixio.const.SIGTERM)
return 1
end
if writer then
local buffer = fdi:read(2048)
if buffer and #buffer > 0 then
writer(buffer)
end
end
local wpid, stat, code = nixio.waitpid(pid, "nohang")
if wpid and stat == "exited" then return code end
if not writer and timeout then nixio.nanosleep(1) end
end
else
local wpid, stat, code = nixio.waitpid(pid)
return wpid and stat == "exited" and code
end
elseif pid == 0 then
nixio.dup(fdo, nixio.stdout)
fdi:close()
fdo:close()
nixio.exece(cmd, args, nil)
nixio.stdout:close()
os.exit(1)
end
end
local function compare_versions(ver1, comp, ver2)
local table = table
local av1 = util.split(ver1, "[%.%-]", nil, true)
local av2 = util.split(ver2, "[%.%-]", nil, true)
local max = table.getn(av1)
local n2 = table.getn(av2)
if (max < n2) then max = n2 end
for i = 1, max, 1 do
local s1 = av1[i] or ""
local s2 = av2[i] or ""
if comp == "~=" and (s1 ~= s2) then return true end
if (comp == "<" or comp == "<=") and (s1 < s2) then return true end
if (comp == ">" or comp == ">=") and (s1 > s2) then return true end
if (s1 ~= s2) then return false end
end
return not (comp == "<" or comp == ">")
end
local function auto_get_arch()
local arch = nixio.uname().machine or ""
if fs.access("/usr/lib/os-release") then
LEDE_BOARD = sys.exec(
"echo -n `grep 'LEDE_BOARD' /usr/lib/os-release | awk -F '[\\042\\047]' '{print $2}'`")
end
if fs.access("/etc/openwrt_release") then
DISTRIB_TARGET = sys.exec(
"echo -n `grep 'DISTRIB_TARGET' /etc/openwrt_release | awk -F '[\\042\\047]' '{print $2}'`")
end
if arch == "mips" then
if LEDE_BOARD and LEDE_BOARD ~= "" then
if string.match(LEDE_BOARD, "ramips") == "ramips" then
arch = "ramips"
else
arch = sys.exec("echo '" .. LEDE_BOARD ..
"' | grep -oE 'ramips|ar71xx'")
end
elseif DISTRIB_TARGET and DISTRIB_TARGET ~= "" then
if string.match(DISTRIB_TARGET, "ramips") == "ramips" then
arch = "ramips"
else
arch = sys.exec("echo '" .. DISTRIB_TARGET ..
"' | grep -oE 'ramips|ar71xx'")
end
end
end
return util.trim(arch)
end
local function get_file_info(arch)
local file_tree = ""
local sub_version = ""
if arch == "x86_64" then
file_tree = "64"
elseif arch == "aarch64" then
file_tree = "arm64"
elseif arch == "ramips" then
file_tree = "mipsle"
elseif arch == "ar71xx" then
file_tree = "mips"
elseif arch:match("^i[%d]86$") then
file_tree = "32"
elseif arch:match("^armv[5-8]") then
file_tree = "arm"
sub_version = arch:match("[5-8]")
if LEDE_BOARD and string.match(LEDE_BOARD, "bcm53xx") == "bcm53xx" then
sub_version = "5"
elseif DISTRIB_TARGET and string.match(DISTRIB_TARGET, "bcm53xx") ==
"bcm53xx" then
sub_version = "5"
end
sub_version = "5"
if sub_version == "7" then is_armv7 = true end
end
return file_tree, sub_version
end
local function get_api_json(url)
local jsonc = require "luci.jsonc"
local output = {}
-- exec(wget, { "-O-", url, _unpack(wget_args) },
-- function(chunk) output[#output + 1] = chunk end)
-- local json_content = util.trim(table.concat(output))
local json_content = luci.sys.exec(wget ..
" --no-check-certificate --timeout=10 -t 1 -O- " ..
url)
if json_content == "" then return {} end
return jsonc.parse(json_content) or {}
end
function get_v2ray_file_path() return "/usr/bin/v2ray" end
function get_v2ray_version()
if get_v2ray_file_path() and get_v2ray_file_path() ~= "" then
if fs.access(get_v2ray_file_path() .. "/v2ray") then
return luci.sys.exec("echo -n `" .. get_v2ray_file_path() ..
"/v2ray -version | awk '{print $2}' | sed -n 1P" ..
"`")
end
end
return ""
end
function to_check(arch)
if not arch or arch == "" then arch = auto_get_arch() end
local file_tree, sub_version = get_file_info(arch)
if file_tree == "" then
return {
code = 1,
error = i18n.translate(
"Can't determine ARCH, or ARCH not supported.")
}
end
local json = get_api_json(v2ray_api)
if json.tag_name == nil then
return {
code = 1,
error = i18n.translate("Get remote version info failed.")
}
end
local remote_version = json.tag_name:match("[^v]+")
local needs_update = compare_versions(get_v2ray_version(), "<",
remote_version)
local html_url, download_url
if needs_update then
html_url = json.html_url
for _, v in ipairs(json.assets) do
if v.name and v.name:match("linux%-" .. file_tree) then
download_url = v.browser_download_url
break
end
end
end
if needs_update and not download_url then
return {
code = 1,
now_version = get_v2ray_version(),
version = remote_version,
html_url = html_url,
error = i18n.translate(
"New version found, but failed to get new version download url.")
}
end
return {
code = 0,
update = needs_update,
now_version = get_v2ray_version(),
version = remote_version,
url = {html = html_url, download = download_url}
}
end
function to_download(url)
if not url or url == "" then
return {code = 1, error = i18n.translate("Download url is required.")}
end
sys.call("/bin/rm -f /tmp/v2ray_download.*")
local tmp_file = util.trim(util.exec("mktemp -u -t v2ray_download.XXXXXX"))
local result = exec(wget, {"-O", tmp_file, url, _unpack(wget_args)}, nil,
command_timeout) == 0
if not result then
exec("/bin/rm", {"-f", tmp_file})
return {
code = 1,
error = i18n.translatef("File download failed or timed out: %s", url)
}
end
return {code = 0, file = tmp_file}
end
function to_extract(file, subfix)
local isinstall_unzip = ipkg.installed("unzip")
if isinstall_unzip == nil then
ipkg.update()
ipkg.install("unzip")
end
if not file or file == "" or not fs.access(file) then
return {code = 1, error = i18n.translate("File path required.")}
end
sys.call("/bin/rm -rf /tmp/v2ray_extract.*")
local tmp_dir = util.trim(util.exec("mktemp -d -t v2ray_extract.XXXXXX"))
local output = {}
exec("/usr/bin/unzip", {"-o", file, "-d", tmp_dir},
function(chunk) output[#output + 1] = chunk end)
local files = util.split(table.concat(output))
exec("/bin/rm", {"-f", file})
return {code = 0, file = tmp_dir}
end
function to_move(file)
if not file or file == "" then
sys.call("/bin/rm -rf /tmp/v2ray_extract.*")
return {code = 1, error = i18n.translate("Client file is required.")}
end
local client_file = get_v2ray_file_path()
sys.call("mkdir -p " .. client_file)
if not arch or arch == "" then arch = auto_get_arch() end
local file_tree, sub_version = get_file_info(arch)
local result = nil
if is_armv7 and is_armv7 == true then
result = exec("/bin/mv", {
"-f", file .. "/v2ray_armv7", file .. "/v2ctl_armv7", client_file
}, nil, command_timeout) == 0
else
result = exec("/bin/mv",
{"-f", file .. "/v2ray", file .. "/v2ctl", client_file},
nil, command_timeout) == 0
end
if not result or not fs.access(client_file) then
sys.call("/bin/rm -rf /tmp/v2ray_extract.*")
return {
code = 1,
error = i18n.translatef("Can't move new file to path: %s",
client_file)
}
end
exec("/bin/chmod", {"-R", "755", client_file})
sys.call("/bin/rm -rf /tmp/v2ray_extract.*")
return {code = 0}
end

View File

@ -1,7 +1,173 @@
<%
local v2ray_version=luci.sys.exec("[ -f '/usr/bin/v2ray/v2ray' ] && /usr/bin/v2ray/v2ray -version | awk '{print $2}' | sed -n 1P")
local dsp = require "luci.dispatcher"
-%>
<script type="text/javascript">
//<![CDATA[
var v2rayInfo;
var tokenStr = '<%=token%>';
var noUpdateText = '<%:已是最新版本%>';
var updateSuccessText = '<%:更新成功.%>';
var clickToUpdateText = '<%:点击更新%>';
var inProgressText = '<%:正在更新...%>';
var unexpectedErrorText = '<%:意外错误.%>';
var updateInProgressNotice = '<%:正在更新,你确认要关闭吗?%>';
window.onload = function() {
var v2rayCheckBtn = document.getElementById('_v2ray-check_btn');
var v2rayDetailElm = document.getElementById('_v2ray-check_btn-detail');
};
function addPageNotice_v2ray() {
window.onbeforeunload = function(e) {
e.returnValue = updateInProgressNotice;
return updateInProgressNotice;
};
}
function removePageNotice_v2ray() {
window.onbeforeunload = undefined;
}
function onUpdateSuccess_v2ray(btn) {
alert(updateSuccessText);
if(btn) {
btn.value = updateSuccessText;
btn.placeholder = updateSuccessText;
btn.disabled = true;
}
window.setTimeout(function() {
window.location.reload();
}, 1000);
}
function onRequestError_v2ray(btn, errorMessage) {
btn.disabled = false;
btn.value = btn.placeholder;
if(errorMessage) {
alert(errorMessage);
}
}
function doAjaxGet(url, data, onResult) {
new XHR().get(url, data, function(_, json) {
var resultJson = json || {
'code': 1,
'error': unexpectedErrorText
};
if(typeof onResult === 'function') {
onResult(resultJson);
}
})
}
function onBtnClick_v2ray(btn) {
if(v2rayInfo === undefined) {
checkUpdate_v2ray(btn);
} else {
doUpdate_v2ray(btn);
}
}
function checkUpdate_v2ray(btn) {
btn.disabled = true;
btn.value = inProgressText;
addPageNotice_v2ray();
var ckeckDetailElm = document.getElementById(btn.id + '-detail');
doAjaxGet('<%=dsp.build_url("admin/vpn/v2ray_server/check")%>', {
token: tokenStr,
arch: ''
}, function(json) {
removePageNotice_v2ray();
if(json.code) {
v2rayInfo = undefined;
onRequestError_v2ray(btn, json.error);
} else {
if(json.update) {
v2rayInfo = json;
btn.disabled = false;
btn.value = clickToUpdateText;
btn.placeholder = clickToUpdateText;
if(ckeckDetailElm) {
var urlNode = '';
if(json.version) {
urlNode = '<em style="color:red;">最新版本号:' + json.version + '</em>';
if(json.url && json.url.html) {
urlNode = '<a href="' + json.url.html + '" target="_blank">' + urlNode + '</a>';
}
}
ckeckDetailElm.innerHTML = urlNode;
}
} else {
btn.disabled = true;
btn.value = noUpdateText;
}
}
});
}
function doUpdate_v2ray(btn) {
btn.disabled = true;
btn.value = '<%:下载中...%>';
addPageNotice_v2ray();
var v2rayUpdateUrl = '<%=dsp.build_url("admin/vpn/v2ray_server/update")%>';
// Download file
doAjaxGet(v2rayUpdateUrl, {
token: tokenStr,
url: v2rayInfo ? v2rayInfo.url.download : ''
}, function(json) {
if(json.code) {
removePageNotice_v2ray();
onRequestError_v2ray(btn, json.error);
} else {
btn.value = '<%:解压中...%>';
// Extract file
doAjaxGet(v2rayUpdateUrl, {
token: tokenStr,
task: 'extract',
file: json.file,
subfix: v2rayInfo ? v2rayInfo.type : ''
}, function(json) {
if(json.code) {
removePageNotice_v2ray();
onRequestError_v2ray(btn, json.error);
} else {
btn.value = '<%:移动中...%>';
// Move file to target dir
doAjaxGet(v2rayUpdateUrl, {
token: tokenStr,
task: 'move',
file: json.file
}, function(json) {
removePageNotice_v2ray();
if(json.code) {
onRequestError_v2ray(btn, json.error);
} else {
onUpdateSuccess_v2ray(btn);
}
})
}
})
}
})
}
//]]>
</script>
<div class="cbi-value">
<label class="cbi-value-title">V2ray
<%:Version%>
@ -9,6 +175,8 @@ local v2ray_version=luci.sys.exec("[ -f '/usr/bin/v2ray/v2ray' ] && /usr/bin/v2r
<div class="cbi-value-field">
<div class="cbi-value-description">
<span><%=v2ray_version%>】</span>
<input class="cbi-button cbi-input-apply" type="submit" id="_v2ray-check_btn" onclick="onBtnClick_v2ray(this);" value="<%:Manually update%>">
<span id="_v2ray-check_btn-detail"></span>
</div>
</div>
</div>

View File

@ -17,5 +17,5 @@ EOF
chmod a+x /usr/share/v2ray_server/* >/dev/null 2>&1
rm -f /tmp/luci-indexcache
rm -rf /tmp/luci-*
exit 0