lienol/luci-app-v2ray-server: drop dupicate package

This commit is contained in:
CN_SZTL 2020-12-06 12:51:47 +08:00
parent 499c3dffe1
commit e872cb9336
No known key found for this signature in database
GPG Key ID: 6850B6345C862176
16 changed files with 0 additions and 1370 deletions

View File

@ -1,19 +0,0 @@
# Copyright (C) 2018-2020 Lienol
#
# This is free software, licensed under the Apache License, Version 2.0 .
#
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-v2ray-server
LUCI_TITLE:=LuCI support for V2ray Server
LUCI_DEPENDS:=+libsodium +luci-lib-jsonc +unzip +v2ray
LUCI_PKGARCH:=all
PKG_VERSION:=1.1
PKG_RELEASE:=1-20200101
include $(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot signature

View File

@ -1,66 +0,0 @@
-- Copyright 2018-2019 Lienol <lawlienol@gmail.com>
module("luci.controller.v2ray_server", package.seeall)
local http = require "luci.http"
local v2ray = require "luci.model.cbi.v2ray_server.api.v2ray"
function index()
if not nixio.fs.access("/etc/config/v2ray_server") then return end
entry({"admin", "vpn"}, firstchild(), "VPN", 45).dependent = false
entry({"admin", "vpn", "v2ray_server"}, cbi("v2ray_server/index"),
_("V2ray Server"), 3).dependent = true
entry({"admin", "vpn", "v2ray_server", "config"}, cbi("v2ray_server/config")).leaf =
true
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 =
true
end
local function http_write_json(content)
http.prepare_content("application/json")
http.write_json(content or {code = 1})
end
function v2ray_users_status()
local e = {}
e.index = luci.http.formvalue("index")
e.status = luci.sys.call(
"ps -w| grep -v grep | grep '/var/etc/v2ray_server/" ..
luci.http.formvalue("id") .. "' >/dev/null") == 0
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"))
end
function clear_log() luci.sys.call("echo '' > /var/log/v2ray_server/app.log") end

View File

@ -1,91 +0,0 @@
local ucursor = require"luci.model.uci".cursor()
local json = require "luci.jsonc"
local server_section = arg[1]
local server = ucursor:get_all("v2ray_server", server_section)
local settings = nil
if server.protocol == "vmess" then
if server.VMess_id then
local clients = {}
for i = 1, #server.VMess_id do
clients[i] = {
id = server.VMess_id[i],
level = tonumber(server.VMess_level),
alterId = tonumber(server.VMess_alterId)
}
end
settings = {clients = clients}
end
elseif server.protocol == "socks" then
settings = {
auth = "password",
accounts = {
{user = server.socks_username, pass = server.socks_password}
}
}
elseif server.protocol == "shadowsocks" then
settings = {
method = server.ss_method,
password = server.ss_password,
level = tonumber(server.ss_level),
network = server.ss_network,
ota = (server.ss_ota == '1') and true or false
}
end
local v2ray = {
log = {
-- error = "/var/log/v2ray.log",
loglevel = "warning"
},
-- 传入连接
inbound = {
listen = (server.bind_local == "1") and "127.0.0.1" or nil,
port = tonumber(server.port),
protocol = server.protocol,
-- 底层传输配置
settings = settings,
streamSettings = (server.protocol == "vmess") and {
network = server.transport,
security = (server.tls_enable == '1') and "tls" or "none",
tlsSettings = (server.tls_enable == '1') and {
-- serverName = (server.tls_serverName),
allowInsecure = false,
disableSystemRoot = false,
certificates = {
{
certificateFile = server.tls_certificateFile,
keyFile = server.tls_keyFile
}
}
} or nil,
kcpSettings = (server.transport == "mkcp") and {
mtu = tonumber(server.mkcp_mtu),
tti = tonumber(server.mkcp_tti),
uplinkCapacity = tonumber(server.mkcp_uplinkCapacity),
downlinkCapacity = tonumber(server.mkcp_downlinkCapacity),
congestion = (server.mkcp_congestion == "1") and true or false,
readBufferSize = tonumber(server.mkcp_readBufferSize),
writeBufferSize = tonumber(server.mkcp_writeBufferSize),
header = {type = server.mkcp_guise}
} or nil,
wsSettings = (server.transport == "ws") and {
headers = (server.ws_host) and {Host = server.ws_host} or nil,
path = server.ws_path
} or nil,
httpSettings = (server.transport == "h2") and
{path = server.h2_path, host = server.h2_host} or nil,
quicSettings = (server.transport == "quic") and {
security = server.quic_security,
key = server.quic_key,
header = {type = server.quic_guise}
} or nil
} or nil
},
-- 传出连接
outbound = {protocol = "freedom"},
-- 额外传出连接
outboundDetour = {{protocol = "blackhole", tag = "blocked"}}
}
print(json.stringify(v2ray, 1))

View File

@ -1,328 +0,0 @@
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,220 +0,0 @@
local app_name = "v2ray_server"
local d = require "luci.dispatcher"
local header_type = {"none", "srtp", "utp", "wechat-video", "dtls", "wireguard"}
map = Map(app_name, "V2ray " .. translate("Server Config"))
map.redirect = d.build_url("admin", "vpn", "v2ray_server")
t = map:section(NamedSection, arg[1], "user", "")
t.addremove = false
t.dynamic = false
enable = t:option(Flag, "enable", translate("Enable"))
enable.default = "1"
enable.rmempty = false
remarks = t:option(Value, "remarks", translate("Remarks"))
remarks.default = translate("Remarks")
remarks.rmempty = false
bind_local = t:option(Flag, "bind_local", translate("Bind Local"), translate(
"When selected, it can only be accessed locally,It is recommended to turn on when using reverse proxies."))
bind_local.default = "0"
bind_local.rmempty = false
port = t:option(Value, "port", translate("Port"))
port.datatype = "port"
port.rmempty = false
protocol = t:option(ListValue, "protocol", translate("Protocol"))
protocol:value("vmess", translate("Vmess"))
protocol:value("socks", translate("Socks"))
protocol:value("shadowsocks", translate("Shadowsocks"))
socks_username = t:option(Value, "socks_username", translate("User name"))
socks_username:depends("protocol", "socks")
socks_password = t:option(Value, "socks_password", translate("Password"))
socks_password.password = true
socks_password:depends("protocol", "socks")
ss_method = t:option(ListValue, "ss_method", translate("Encrypt Method"))
ss_method:value("aes-128-cfb")
ss_method:value("aes-256-cfb")
ss_method:value("aes-128-gcm")
ss_method:value("aes-256-gcm")
ss_method:value("chacha20")
ss_method:value("chacha20-ietf")
ss_method:value("chacha20-poly1305")
ss_method:value("chacha20-ietf-poly1305")
ss_method:depends("protocol", "shadowsocks")
ss_password = t:option(Value, "ss_password", translate("Password"))
ss_password:depends("protocol", "shadowsocks")
ss_level = t:option(Value, "ss_level", translate("User Level"))
ss_level.default = 1
ss_level:depends("protocol", "shadowsocks")
ss_network = t:option(ListValue, "ss_network", translate("Transport"))
ss_network.default = "tcp,udp"
ss_network:value("tcp", "TCP")
ss_network:value("udp", "UDP")
ss_network:value("tcp,udp", "TCP,UDP")
ss_network:depends("protocol", "shadowsocks")
ss_ota = t:option(Flag, "ss_ota", translate("OTA"), translate(
"When OTA is enabled, V2Ray will reject connections that are not OTA enabled. This option is invalid when using AEAD encryption."))
ss_ota.default = "0"
ss_ota:depends("protocol", "shadowsocks")
VMess_id = t:option(DynamicList, "VMess_id", translate("ID"))
for i = 1, 3 do
local uuid = luci.sys.exec("cat /proc/sys/kernel/random/uuid")
VMess_id:value(uuid)
end
VMess_id:depends("protocol", "vmess")
VMess_alterId = t:option(Value, "VMess_alterId", translate("Alter ID"))
VMess_alterId.default = 16
VMess_alterId:depends("protocol", "vmess")
VMess_level = t:option(Value, "VMess_level", translate("User Level"))
VMess_level.default = 1
VMess_level:depends("protocol", "vmess")
transport = t:option(ListValue, "transport", translate("Transport"))
transport:value("tcp", "TCP")
transport:value("mkcp", "mKCP")
transport:value("ws", "WebSocket")
transport:value("h2", "HTTP/2")
transport:value("quic", "QUIC")
transport:depends("protocol", "vmess")
-- [[ TCP部分 ]]--
-- TCP伪装
tcp_guise = t:option(ListValue, "tcp_guise", translate("Camouflage Type"))
tcp_guise:depends("transport", "tcp")
tcp_guise:value("none", "none")
tcp_guise:value("http", "http")
-- HTTP域名
tcp_guise_http_host = t:option(DynamicList, "tcp_guise_http_host",
translate("HTTP Host"))
tcp_guise_http_host:depends("tcp_guise", "http")
-- HTTP路径
tcp_guise_http_path = t:option(DynamicList, "tcp_guise_http_path",
translate("HTTP Path"))
tcp_guise_http_path:depends("tcp_guise", "http")
-- [[ mKCP部分 ]]--
mkcp_guise = t:option(ListValue, "mkcp_guise", translate("Camouflage Type"),
translate(
'<br>none: default, no masquerade, data sent is packets with no characteristics.<br>srtp: disguised as an SRTP packet, it will be recognized as video call data (such as FaceTime).<br>utp: packets disguised as uTP will be recognized as bittorrent downloaded data.<br>wechat-video: packets disguised as WeChat video calls.<br>dtls: disguised as DTLS 1.2 packet.<br>wireguard: disguised as a WireGuard packet. (not really WireGuard protocol)'))
for a, t in ipairs(header_type) do mkcp_guise:value(t) end
mkcp_guise:depends("transport", "mkcp")
mkcp_mtu = t:option(Value, "mkcp_mtu", translate("KCP MTU"))
mkcp_mtu:depends("transport", "mkcp")
mkcp_tti = t:option(Value, "mkcp_tti", translate("KCP TTI"))
mkcp_tti:depends("transport", "mkcp")
mkcp_uplinkCapacity = t:option(Value, "mkcp_uplinkCapacity",
translate("KCP uplinkCapacity"))
mkcp_uplinkCapacity:depends("transport", "mkcp")
mkcp_downlinkCapacity = t:option(Value, "mkcp_downlinkCapacity",
translate("KCP downlinkCapacity"))
mkcp_downlinkCapacity:depends("transport", "mkcp")
mkcp_congestion = t:option(Flag, "mkcp_congestion", translate("KCP Congestion"))
mkcp_congestion:depends("transport", "mkcp")
mkcp_readBufferSize = t:option(Value, "mkcp_readBufferSize",
translate("KCP readBufferSize"))
mkcp_readBufferSize:depends("transport", "mkcp")
mkcp_writeBufferSize = t:option(Value, "mkcp_writeBufferSize",
translate("KCP writeBufferSize"))
mkcp_writeBufferSize:depends("transport", "mkcp")
-- [[ WebSocket部分 ]]--
ws_path = t:option(Value, "ws_path", translate("WebSocket Path"))
ws_path:depends("transport", "ws")
ws_host = t:option(Value, "ws_host", translate("WebSocket Host"))
ws_host:depends("transport", "ws")
-- [[ HTTP/2部分 ]]--
h2_path = t:option(Value, "h2_path", translate("HTTP/2 Path"))
h2_path:depends("transport", "h2")
h2_host = t:option(DynamicList, "h2_host", translate("HTTP/2 Host"),
translate("Camouflage Domain,you can not fill in"))
h2_host:depends("transport", "h2")
-- [[ QUIC部分 ]]--
quic_security =
t:option(ListValue, "quic_security", translate("Encrypt Method"))
quic_security:value("none")
quic_security:value("aes-128-gcm")
quic_security:value("chacha20-poly1305")
quic_security:depends("transport", "quic")
quic_key = t:option(Value, "quic_key",
translate("Encrypt Method") .. translate("Key"))
quic_key:depends("transport", "quic")
quic_guise = t:option(ListValue, "quic_guise", translate("Camouflage Type"))
for a, t in ipairs(header_type) do quic_guise:value(t) end
quic_guise:depends("transport", "quic")
-- [[ TLS部分 ]] --
tls_enable = t:option(Flag, "tls_enable", translate("Use HTTPS"))
tls_enable:depends("transport", "ws")
tls_enable:depends("transport", "h2")
tls_enable.default = "1"
tls_enable.rmempty = false
-- tls_serverName = t:option(Value, "tls_serverName", translate("Domain"))
-- tls_serverName:depends("transport", "ws")
-- tls_serverName:depends("transport", "h2")
tls_certificateFile = t:option(Value, "tls_certificateFile",
translate("Public key absolute path"),
translate("as:") .. "/etc/ssl/fullchain.pem")
tls_certificateFile:depends("tls_enable", 1)
tls_keyFile = t:option(Value, "tls_keyFile",
translate("Private key absolute path"),
translate("as:") .. "/etc/ssl/private.key")
tls_keyFile:depends("tls_enable", 1)
function rmempty_restore()
VMess_id.rmempty = true
VMess_alterId.rmempty = true
socks_username.rmempty = true
socks_password.rmempty = true
ss_password.rmempty = true
ss_ota.rmempty = true
end
protocol.validate = function(self, value)
rmempty_restore()
if value == "vmess" then
VMess_id.rmempty = false
VMess_alterId.rmempty = false
elseif value == "socks" then
socks_username.rmempty = true
socks_password.rmempty = true
elseif value == "shadowsocks" then
ss_password.rmempty = false
ss_ota.rmempty = false
end
return value
end
return map

View File

@ -1,105 +0,0 @@
local i = require "luci.dispatcher"
local e = require "nixio.fs"
local e = require "luci.sys"
local e = luci.model.uci.cursor()
local o = "v2ray_server"
m = Map(o, translate("V2ray Server"))
t = m:section(TypedSection, "global", translate("Global Settings"))
t.anonymous = true
t.addremove = false
e = t:option(Flag, "enable", translate("Enable"))
e.rmempty = false
t:append(Template("v2ray_server/v2ray"))
t = m:section(TypedSection, "user", translate("Users Manager"))
t.anonymous = true
t.addremove = true
t.template = "cbi/tblsection"
t.extedit = i.build_url("admin", "vpn", o, "config", "%s")
function t.create(t, e)
local e = TypedSection.create(t, e)
luci.http.redirect(i.build_url("admin", "vpn", o, "config", e))
end
function t.remove(t, a)
t.map.proceed = true
t.map:del(a)
luci.http.redirect(i.build_url("admin", "vpn", o))
end
e = t:option(Flag, "enable", translate("Enable"))
e.width = "5%"
e.rmempty = false
e = t:option(DummyValue, "status", translate("Status"))
e.template = "v2ray_server/users_status"
e.value = translate("Collecting data...")
e = t:option(DummyValue, "remarks", translate("Remarks"))
e.width = "15%"
e = t:option(DummyValue, "port", translate("Port"))
e.width = "10%"
e = t:option(DummyValue, "protocol", translate("Protocol"))
e.width = "15%"
e.cfgvalue = function(self, section)
local str = "未知"
local protocol = m:get(section, "protocol") or ""
if protocol ~= "" then str = (protocol:gsub("^%l", string.upper)) end
return str
end
e = t:option(DummyValue, "transport", translate("Transport"))
e.width = "10%"
e.cfgvalue = function(self, section)
local t = "未知"
local b = ""
local protocol = m:get(section, "protocol") or ""
if protocol == "vmess" then
b = "transport"
elseif protocol == "shadowsocks" then
b = "ss_network"
end
local a = m:get(section, b) or ""
if a == "tcp" then
t = "TCP"
elseif a == "udp" then
t = "UDP"
elseif a == "tcp,udp" then
t = "TCP,UDP"
elseif a == "mkcp" then
t = "mKCP"
elseif a == "ws" then
t = "WebSocket"
elseif a == "h2" then
t = "HTTP/2"
elseif a == "quic" then
t = "QUIC"
else
t = "TCP,UDP"
end
return t
end
e = t:option(DummyValue, "password", translate("Password"))
e.width = "30%"
e.cfgvalue = function(self, section)
local e = ""
local protocol = m:get(section, "protocol") or ""
if protocol == "vmess" then
e = "VMess_id"
elseif protocol == "shadowsocks" then
e = "ss_password"
elseif protocol == "socks" then
e = "socks_password"
end
local e = m:get(section, e) or ""
local t = ""
if type(e) == "table" then
for a = 1, #e do t = t .. e[a] end
else
t = e
end
return t
end
m:append(Template("v2ray_server/log"))
m:append(Template("v2ray_server/users_list_status"))
return m

View File

@ -1,31 +0,0 @@
<script type="text/javascript">
//<![CDATA[
function clear_log(btn) {
XHR.get('<%=url([[admin]], [[vpn]], [[v2ray_server]], [[clear_log]])%>', null,
function(x, data) {
if(x && x.status == 200) {
var log_textarea = document.getElementById('log_textarea');
log_textarea.innerHTML = "";
log_textarea.scrollTop = log_textarea.scrollHeight;
}
}
);
}
XHR.poll(3, '<%=url([[admin]], [[vpn]], [[v2ray_server]], [[get_log]])%>', null,
function(x, data) {
if(x && x.status == 200) {
var log_textarea = document.getElementById('log_textarea');
log_textarea.innerHTML = x.responseText;
log_textarea.scrollTop = log_textarea.scrollHeight;
}
}
);
//]]>
</script>
<fieldset class="cbi-section" id="_log_fieldset">
<legend>
<%:Logs%>
</legend>
<input class="cbi-button cbi-input-remove" type="button" onclick="clear_log()" value="<%:Clear logs%>">
<textarea id="log_textarea" class="cbi-input-textarea" style="width: 100%;margin-top: 10px;" data-update="change" rows="20" wrap="off" readonly="readonly"></textarea>
</fieldset>

View File

@ -1,23 +0,0 @@
<%
local dsp = require "luci.dispatcher"
-%>
<script type="text/javascript">
//<![CDATA[
var v2ray_users_status = document.getElementsByClassName('v2ray_users_status');
for(var i = 0; i < v2ray_users_status.length; i++) {
var id = v2ray_users_status[i].parentElement.parentElement.parentElement.id;
id = id.substr(id.lastIndexOf("-") + 1);
XHR.poll(1,'<%=dsp.build_url("admin/vpn/v2ray_server/users_status")%>', {
index: i,
id: id
},
function(x, result) {
v2ray_users_status[result.index].setAttribute("style","font-weight:bold;");
v2ray_users_status[result.index].setAttribute("color",result.status ? "green":"red");
v2ray_users_status[result.index].innerHTML = (result.status ? '✓' : 'X');
}
);
}
//]]>
</script>

View File

@ -1,3 +0,0 @@
<%+cbi/valueheader%>
<font class="v2ray_users_status" hint="<%=self:cfgvalue(section)%>">--</font>
<%+cbi/valuefooter%>

View File

@ -1,182 +0,0 @@
<%
local v2ray_version=luci.sys.exec("/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%>
</label>
<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

@ -1,137 +0,0 @@
msgid "V2ray Server"
msgstr "V2ray 服务器"
msgid "Global Settings"
msgstr "全局设置"
msgid "Caddy path"
msgstr "Caddy 路径"
msgid "if you want to run from memory, change the path, such as /tmp/caddy, Then save the application and update it manually."
msgstr "如果你希望从内存中运行,请更改路径,例如/tmp/caddy然后保存应用后再手动更新。"
msgid "Server Config"
msgstr "服务器配置"
msgid "Users Manager"
msgstr "用户管理"
msgid "Remarks"
msgstr "备注"
msgid "Bind Local"
msgstr "本机监听"
msgid "When selected, it can only be accessed locally,It is recommended to turn on when using reverse proxies."
msgstr "当勾选时,只能由本机访问此端口,当开启反向代理时建议勾选此项。"
msgid "Port"
msgstr "端口"
msgid "Password"
msgstr "密码"
msgid "Protocol"
msgstr "协议"
msgid "Null"
msgstr "无"
msgid "Alter ID"
msgstr "额外ID(AlterID)"
msgid "User Level"
msgstr "用户等级(Level)"
msgid "When OTA is enabled, V2Ray will reject connections that are not OTA enabled. This option is invalid when using AEAD encryption."
msgstr "开启 OTA 后V2Ray 会拒绝未启用 OTA 的连接。当使用 AEAD 加密时,该选项无效。"
msgid "Transport"
msgstr "传输方式"
msgid "Camouflage Type"
msgstr "伪装类型"
msgid "Camouflage Domain,you can not fill in"
msgstr "伪装域名,也可以不填写"
msgid "Reverse Proxy"
msgstr "反向代理"
msgid "Reverse Proxy Type"
msgstr "反向代理类型"
msgid "can not has conflict"
msgstr "请不要冲突"
msgid "Use HTTPS"
msgstr "使用HTTPS"
msgid "TLS Settings"
msgstr "TLS配置"
msgid "Nginx does not support HTTP/2 reverse proxies"
msgstr "Nginx 不支持HTTP/2反向代理"
msgid "as:"
msgstr "如:"
msgid "Public key absolute path"
msgstr "公钥文件绝对路径"
msgid "Private key absolute path"
msgstr "私钥文件绝对路径"
msgid "<br>none: default, no masquerade, data sent is packets with no characteristics.<br>srtp: disguised as an SRTP packet, it will be recognized as video call data (such as FaceTime).<br>utp: packets disguised as uTP will be recognized as bittorrent downloaded data.<br>wechat-video: packets disguised as WeChat video calls.<br>dtls: disguised as DTLS 1.2 packet.<br>wireguard: disguised as a WireGuard packet. (not really WireGuard protocol)"
msgstr "<br>none默认值不进行伪装发送的数据是没有特征的数据包。<br>srtp伪装成 SRTP 数据包,会被识别为视频通话数据(如 FaceTime。<br>utp伪装成 uTP 数据包,会被识别为 BT 下载数据。<br>wechat-video伪装成微信视频通话的数据包。<br>dtls伪装成 DTLS 1.2 数据包。<br>wireguard伪装成 WireGuard 数据包。(并不是真正的 WireGuard 协议)"
msgid "Logs"
msgstr "日志"
msgid "Clear logs"
msgstr "清空日志"
msgid "Can't determine ARCH, or ARCH not supported."
msgstr "无法确认ARCH架构或是不支持。"
msgid "Get remote version info failed."
msgstr "获取远程版本信息失败。"
msgid "New version found, but failed to get new version download url."
msgstr "发现新版本,但未能获得新版本的下载地址。"
msgid "Download url is required."
msgstr "请指定下载地址。"
msgid "File download failed or timed out: %s"
msgstr "文件下载失败或超时:%s"
msgid "File path required."
msgstr "请指定文件路径。"
msgid "Can't find client in file: %s"
msgstr "无法在文件中找到客户端:%s"
msgid "Client file is required."
msgstr "请指定客户端文件。"
msgid "The client file is not suitable for current device."
msgstr "客户端文件不适合当前设备。"
msgid "Can't move new file to path: %s"
msgstr "无法移动新文件到:%s"
msgid "Enabled"
msgstr "启用"
msgid "Status"
msgstr "状态"
msgid "Current Condition"
msgstr "当前状态"
msgid "NOT RUNNING"
msgstr "未运行"
msgid "RUNNING"
msgstr "运行中"

View File

@ -1,45 +0,0 @@
config global
option enable '0'
config user
option enable '1'
option remarks '备注222'
option bind_local '0'
option protocol 'vmess'
list VMess_id 'fd00927a-b0c2-4629-aef7-d9ff15a9d722'
option VMess_alterId '16'
option VMess_level '1'
option transport 'tcp'
option tcp_guise 'none'
option port '12366'
config user
option enable '1'
option remarks 'WebsocketDemo'
option bind_local '0'
option protocol 'vmess'
option VMess_alterId '16'
option VMess_level '1'
list VMess_id 'fd00927a-b0c2-4629-aef7-d9ff15a9d722'
option transport 'ws'
option ws_path '/websocket'
option tls_enable '1'
option tls_certificateFile '/etc/config/ssl/fullchain.pem'
option tls_keyFile '/etc/config/ssl/private.key'
option port '30010'
config user
option enable '1'
option remarks 'H2Demo'
option bind_local '0'
option protocol 'vmess'
option VMess_alterId '16'
option VMess_level '1'
list VMess_id 'fd00927a-b0c2-4629-aef7-d9ff15a9d722'
option tls_enable '1'
option tls_certificateFile '/etc/config/ssl/fullchain.pem'
option tls_keyFile '/etc/config/ssl/private.key'
option transport 'h2'
option h2_path '/h2'
option port '30011'

View File

@ -1,59 +0,0 @@
#!/bin/sh /etc/rc.common
# Copyright (C) 2018-2020 Lienol <lawlienol@gmail.com>
START=99
CONFIG=v2ray_server
CONFIG_PATH=/var/etc/$CONFIG
LOG_PATH=/var/log/$CONFIG
LOG_APP_FILE=$LOG_PATH/app.log
echolog() {
echo -e "$(date "+%Y-%m-%d %H:%M:%S"): $1" >> $LOG_APP_FILE
}
gen_v2ray_config_file() {
config_get enable $1 enable
[ "$enable" = "0" ] && return 0
config_get remarks $1 remarks
config_get port $1 port
config_get transport $1 transport
lua /usr/lib/lua/luci/model/cbi/v2ray_server/api/genv2rayconfig.lua $1 > $CONFIG_PATH/$1.json
echolog "$remarks $port 生成并运行 V2ray 配置文件 - $CONFIG_PATH/$1.json"
/usr/bin/v2ray/v2ray -config $CONFIG_PATH/$1.json >/dev/null 2>&1 &
}
start_v2ray_server() {
mkdir -p $CONFIG_PATH $LOG_PATH
touch $LOG_APP_FILE
caddy_file=$(uci get $CONFIG.@global[0].caddy_file)
config_foreach gen_v2ray_config_file "user"
fw3 reload >/dev/null 2>&1 &
}
stop_v2ray_server() {
fw3 reload >/dev/null 2>&1 &
ps -w | grep "$CONFIG_PATH/" | grep -v "grep" | awk '{print $1}' | xargs kill -9 >/dev/null 2>&1 &
rm -rf $CONFIG_PATH
rm -rf $LOG_PATH
}
start() {
config_load $CONFIG
enable=$(uci get $CONFIG.@global[0].enable)
if [ "$enable" = "0" ];then
stop_v2ray_server
else
start_v2ray_server
fi
}
stop() {
stop_v2ray_server
}
restart() {
stop
sleep 1
start
}

View File

@ -1,21 +0,0 @@
#!/bin/sh
uci -q batch <<-EOF >/dev/null
delete firewall.v2ray_server
set firewall.v2ray_server=include
set firewall.v2ray_server.type=script
set firewall.v2ray_server.path=/usr/share/v2ray_server/firewall.include
set firewall.v2ray_server.reload=1
EOF
uci -q batch <<-EOF >/dev/null
delete ucitrack.@v2ray_server[-1]
add ucitrack v2ray_server
set ucitrack.@v2ray_server[-1].init=v2ray_server
commit ucitrack
EOF
chmod a+x /usr/share/v2ray_server/* >/dev/null 2>&1
rm -f /tmp/luci-indexcache
exit 0

View File

@ -1,11 +0,0 @@
{
"luci-app-v2ray-server": {
"description": "Grant UCI access for luci-app-v2ray-server",
"read": {
"uci": [ "v2ray_server" ]
},
"write": {
"uci": [ "v2ray_server" ]
}
}
}

View File

@ -1,29 +0,0 @@
#!/bin/sh
. $IPKG_INSTROOT/lib/functions.sh
. $IPKG_INSTROOT/lib/functions/service.sh
gen_user_iptables() {
config_get enable $1 enable
[ "$enable" = "0" ] && return 0
config_get remarks $1 remarks
config_get bind_local $1 bind_local
config_get port $1 port
dport=$port
[ "$bind_local" != "1" ] && {
iptables -A V2RAY-SERVER -p tcp --dport $dport -m comment --comment "$remarks" -j ACCEPT
iptables -A V2RAY-SERVER -p udp --dport $dport -m comment --comment "$remarks" -j ACCEPT
}
}
iptables -F V2RAY-SERVER 2>/dev/null
iptables -D INPUT -j V2RAY-SERVER 2>/dev/null
iptables -X V2RAY-SERVER 2>/dev/null
enable=$(uci get v2ray_server.@global[0].enable)
if [ $enable -eq 1 ]; then
iptables -N V2RAY-SERVER
iptables -I INPUT -j V2RAY-SERVER
config_load v2ray_server
config_foreach gen_user_iptables "user"
fi