luci-app-v2ray-server: bump to 13-1 version (#5894)

This commit is contained in:
Mattraks 2020-12-09 15:04:55 +08:00 committed by GitHub
parent a5217b2901
commit c06f6662d4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 785 additions and 650 deletions

View File

@ -1,18 +1,17 @@
# Copyright (C) 2018-2019 Lienol
# Copyright (C) 2018-2020 Lienol <lawlienol@gmail.com>
#
# This is free software, licensed under the GNU General Public License v3.
#
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-v2ray-server
LUCI_TITLE:=LuCI support for V2ray Server
LUCI_DEPENDS:=+v2ray
LUCI_DEPENDS:=+unzip
LUCI_PKGARCH:=all
PKG_VERSION:=1.1
PKG_RELEASE:=5
PKG_VERSION:=13
PKG_RELEASE:=1
include $(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot signature

View File

@ -1,25 +1,17 @@
-- 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
entry({"admin", "services", "v2ray_server"}, cbi("v2ray_server/index"), _("V2ray Server"), 3).dependent = true
entry({"admin", "services", "v2ray_server", "config"}, cbi("v2ray_server/user")).leaf = true
entry({"admin", "services", "v2ray_server", "users_status"}, call("users_status")).leaf = true
entry({"admin", "services", "v2ray_server", "check"}, call("v2ray_check")).leaf = true
entry({"admin", "services", "v2ray_server", "update"}, call("v2ray_update")).leaf = true
entry({"admin", "services", "v2ray_server", "get_log"}, call("get_log")).leaf = true
entry({"admin", "services", "v2ray_server", "clear_log"}, call("clear_log")).leaf = true
end
local function http_write_json(content)
@ -27,12 +19,18 @@ local function http_write_json(content)
http.write_json(content or {code = 1})
end
function v2ray_users_status()
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
function 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
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
@ -56,10 +54,3 @@ function v2ray_update()
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

@ -0,0 +1,109 @@
#!/usr/bin/lua
local action = arg[1]
local sys = require 'luci.sys'
local jsonc = require "luci.jsonc"
local ucic = require"luci.model.uci".cursor()
local CONFIG = "v2ray_server"
local CONFIG_PATH = "/var/etc/" .. CONFIG
local LOG_PATH = "/var/log/" .. CONFIG
local LOG_APP_FILE = LOG_PATH .. "/app.log"
local BIN_PATH = "/var/bin/"
local BIN_PATH_FILE = BIN_PATH .. CONFIG
local function log(...)
local f, err = io.open(LOG_APP_FILE, "a")
if f and err == nil then
local str = os.date("%Y-%m-%d %H:%M:%S: ") .. table.concat({...}, " ")
f:write(str .. "\n")
f:close()
end
end
local function cmd(cmd)
sys.call(cmd)
end
local function gen_include()
cmd(string.format("echo '#!/bin/sh' > /var/etc/%s.include", CONFIG))
local function extract_rules(a)
local result = "*" .. a
result = result .. "\n" .. sys.exec('iptables-save -t ' .. a .. ' | grep "V2RAY-SERVER" | sed -e "s/^-A \\(INPUT\\)/-I \\1 1/"')
result = result .. "COMMIT"
return result
end
local f, err = io.open("/var/etc/" .. CONFIG .. ".include", "a")
if f and err == nil then
f:write('iptables-save -c | grep -v "V2RAY-SERVER" | iptables-restore -c' .. "\n")
f:write('iptables-restore -n <<-EOT' .. "\n")
f:write(extract_rules("filter") .. "\n")
f:write("EOT" .. "\n")
f:close()
end
end
local function start()
local enabled = tonumber(ucic:get(CONFIG, "@global[0]", "enable") or 0)
if enabled == nil or enabled == 0 then
return
end
cmd(string.format("mkdir -p %s %s", CONFIG_PATH, LOG_PATH))
cmd(string.format("touch %s", LOG_APP_FILE))
cmd("iptables -N V2RAY-SERVER")
cmd("iptables -I INPUT -j V2RAY-SERVER")
ucic:foreach(CONFIG, "user", function(user)
local id = user[".name"]
local enable = user.enable
if enable and tonumber(enable) == 1 then
local remarks = user.remarks
local port = tonumber(user.port)
if nixio.fs.access("/usr/bin/xray") and not nixio.fs.access(BIN_PATH_FILE) then
cmd(string.format("mkdir -p %s", BIN_PATH))
cmd(string.format("cp -a /usr/bin/xray %s", BIN_PATH_FILE))
end
local bin = BIN_PATH_FILE
local config = {}
local config_file = CONFIG_PATH .. "/" .. id .. ".json"
config = require("luci.model.cbi.v2ray_server.api.gen_config").gen_config(user)
bin = bin .. " -config " .. config_file
if next(config) then
local f, err = io.open(config_file, "w")
if f and err == nil then
f:write(jsonc.stringify(config, 1))
f:close()
end
log(string.format("%s %s 生成配置文件并运行 - %s", remarks, port, config_file))
end
if bin then
cmd(bin .. ">/dev/null 2>&1 &")
end
local bind_local = user.bind_local or 0
if bind_local and tonumber(bind_local) ~= 1 then
cmd(string.format('iptables -A V2RAY-SERVER -p tcp --dport %s -m comment --comment "%s" -j ACCEPT', port, remarks))
cmd(string.format('iptables -A V2RAY-SERVER -p udp --dport %s -m comment --comment "%s" -j ACCEPT', port, remarks))
end
end
end)
gen_include()
end
local function stop()
cmd(string.format("ps -w | grep -v 'grep' | grep '%s/' | awk '{print $1}' | xargs kill -9 >/dev/null 2>&1 &", CONFIG_PATH))
cmd("iptables -D INPUT -j V2RAY-SERVER 2>/dev/null")
cmd("iptables -F V2RAY-SERVER 2>/dev/null")
cmd("iptables -X V2RAY-SERVER 2>/dev/null")
cmd(string.format("rm -rf %s %s /var/etc/%s.include", CONFIG_PATH, LOG_APP_FILE, CONFIG))
end
if action then
if action == "start" then
start()
elseif action == "stop" then
stop()
end
end

View File

@ -0,0 +1,184 @@
module("luci.model.cbi.v2ray_server.api.gen_config", package.seeall)
function gen_config(user)
local settings = nil
local routing = nil
local outbounds = {
{protocol = "freedom", tag = "direct"}, {protocol = "blackhole", tag = "blocked"}
}
if user.protocol == "vmess" or user.protocol == "vless" then
if user.uuid then
local clients = {}
for i = 1, #user.uuid do
clients[i] = {
id = user.uuid[i],
flow = (user.xtls and user.xtls == "1") and user.flow or nil,
level = tonumber(user.level),
alterId = tonumber(user.alter_id)
}
end
settings = {
clients = clients,
decryption = user.decryption or "none"
}
end
elseif user.protocol == "socks" then
settings = {
auth = (user.auth and user.auth == "1") and "password" or "noauth",
accounts = (user.auth and user.auth == "1") and {
{
user = user.username,
pass = user.password
}
}
}
elseif user.protocol == "http" then
settings = {
allowTransparent = false,
accounts = (user.auth and user.auth == "1") and {
{
user = user.username,
pass = user.password
}
}
}
user.transport = "tcp"
user.tcp_guise = "none"
elseif user.protocol == "shadowsocks" then
settings = {
method = user.method,
password = user.password,
level = tonumber(user.level) or 1,
network = user.ss_network or "TCP,UDP"
}
elseif user.protocol == "trojan" then
if user.uuid then
local clients = {}
for i = 1, #user.uuid do
clients[i] = {
password = user.uuid[i],
level = tonumber(user.level)
}
end
settings = {
clients = clients
}
end
elseif user.protocol == "mtproto" then
settings = {
users = {
{
level = tonumber(user.level) or 1,
secret = (user.password == nil) and "" or user.password
}
}
}
end
routing = {
domainStrategy = "IPOnDemand",
rules = {
{
type = "field",
ip = {"10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"},
outboundTag = (user.accept_lan == nil or user.accept_lan == "0") and "blocked" or "direct"
}
}
}
local config = {
log = {
-- error = "/var/etc/v2ray_server/log/" .. user[".name"] .. ".log",
loglevel = "warning"
},
-- 传入连接
inbounds = {
{
listen = (user.bind_local == "1") and "127.0.0.1" or nil,
port = tonumber(user.port),
protocol = user.protocol,
settings = settings,
streamSettings = {
network = user.transport,
security = "none",
xtlsSettings = (user.tls and user.tls == "1" and user.xtls and user.xtls == "1") and {
--alpn = {"http/1.1"},
disableSystemRoot = false,
certificates = {
{
certificateFile = user.tls_certificateFile,
keyFile = user.tls_keyFile
}
}
} or nil,
tlsSettings = (user.tls and user.tls == "1") and {
disableSystemRoot = false,
certificates = {
{
certificateFile = user.tls_certificateFile,
keyFile = user.tls_keyFile
}
}
} or nil,
tcpSettings = (user.transport == "tcp") and {
header = {
type = user.tcp_guise,
request = (user.tcp_guise == "http") and {
path = {user.tcp_guise_http_path} or {"/"},
headers = {
Host = {user.tcp_guise_http_host} or {}
}
} or nil
}
} or nil,
kcpSettings = (user.transport == "mkcp") and {
mtu = tonumber(user.mkcp_mtu),
tti = tonumber(user.mkcp_tti),
uplinkCapacity = tonumber(user.mkcp_uplinkCapacity),
downlinkCapacity = tonumber(user.mkcp_downlinkCapacity),
congestion = (user.mkcp_congestion == "1") and true or false,
readBufferSize = tonumber(user.mkcp_readBufferSize),
writeBufferSize = tonumber(user.mkcp_writeBufferSize),
seed = (user.mkcp_seed and user.mkcp_seed ~= "") and user.mkcp_seed or nil,
header = {type = user.mkcp_guise}
} or nil,
wsSettings = (user.transport == "ws") and {
acceptProxyProtocol = false,
headers = (user.ws_host) and {Host = user.ws_host} or nil,
path = user.ws_path
} or nil,
httpSettings = (user.transport == "h2") and {
path = user.h2_path, host = user.h2_host
} or nil,
dsSettings = (user.transport == "ds") and {
path = user.ds_path
} or nil,
quicSettings = (user.transport == "quic") and {
security = user.quic_security,
key = user.quic_key,
header = {type = user.quic_guise}
} or nil
}
}
},
-- 传出连接
outbounds = outbounds,
routing = routing
}
if user.tls and user.tls == "1" then
config.inbounds[1].streamSettings.security = "tls"
if user.xtls and user.xtls == "1" then
config.inbounds[1].streamSettings.security = "xtls"
config.inbounds[1].streamSettings.tlsSettings = nil
end
end
if user.transport == "mkcp" or user.transport == "quic" then
config.inbounds[1].streamSettings.security = "none"
config.inbounds[1].streamSettings.tlsSettings = nil
end
return config
end

View File

@ -1,135 +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
local routing = 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 = (server.socks_username == nil and server.socks_password == nil) and
"noauth" or "password",
accounts = {
{
user = (server.socks_username == nil) and "" or
server.socks_username,
pass = (server.socks_password == nil) and "" or
server.socks_password
}
}
}
elseif server.protocol == "http" then
settings = {
allowTransparent = false,
accounts = {
{
user = (server.http_username == nil) and "" or
server.http_username,
pass = (server.http_password == nil) and "" or
server.http_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
if server.accept_lan == nil or server.accept_lan == "0" then
routing = {
domainStrategy = "IPOnDemand",
rules = {
{
type = "field",
ip = {"10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"},
outboundTag = "blocked"
}
}
}
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,
tcpSettings = (server.transport == "tcp") and {
header = {
type = server.tcp_guise,
request = {
path = server.tcp_guise_http_path,
headers = {
Host = server.tcp_guise_http_host
}
}
}
} 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"}},
routing = routing
}
print(json.stringify(v2ray, 1))

View File

@ -7,12 +7,9 @@ local i18n = require "luci.i18n"
local ipkg = require "luci.model.ipkg"
local appname = "v2ray_server"
local v2ray_api =
"https://api.github.com/repos/v2fly/v2ray-core/releases/latest"
local v2ray_api ="https://api.github.com/repos/XTLS/Xray-core/releases/latest"
local wget = "/usr/bin/wget"
local wget_args = {
"--no-check-certificate", "--quiet", "--timeout=100", "--tries=3"
}
local wget_args = {"--no-check-certificate", "--quiet", "--timeout=100", "--tries=3"}
local command_timeout = 300
local LEDE_BOARD = nil
@ -95,12 +92,10 @@ 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}'`")
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}'`")
DISTRIB_TARGET = sys.exec("echo -n `grep 'DISTRIB_TARGET' /etc/openwrt_release | awk -F '[\\042\\047]' '{print $2}'`")
end
if arch == "mips" then
@ -108,15 +103,13 @@ local function auto_get_arch()
if string.match(LEDE_BOARD, "ramips") == "ramips" then
arch = "ramips"
else
arch = sys.exec("echo '" .. LEDE_BOARD ..
"' | grep -oE 'ramips|ar71xx'")
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'")
arch = sys.exec("echo '" .. DISTRIB_TARGET .. "' | grep -oE 'ramips|ar71xx'")
end
end
end
@ -162,23 +155,19 @@ local function get_api_json(url)
-- 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)
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_file_path() return "/usr/bin" 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" ..
"`")
if fs.access(get_v2ray_file_path() .. "/xray") then
return luci.sys.exec("echo -n `" .. get_v2ray_file_path() .. "/xray -version | awk '{print $2}' | sed -n 1P" .. "`")
end
end
return ""
@ -192,8 +181,7 @@ function to_check(arch)
if file_tree == "" then
return {
code = 1,
error = i18n.translate(
"Can't determine ARCH, or ARCH not supported.")
error = i18n.translate("Can't determine ARCH, or ARCH not supported.")
}
end
@ -207,8 +195,7 @@ function to_check(arch)
end
local remote_version = json.tag_name:match("[^v]+")
local needs_update = compare_versions(get_v2ray_version(), "<",
remote_version)
local needs_update = compare_versions(get_v2ray_version(), "<",remote_version)
local html_url, download_url
if needs_update then
@ -227,8 +214,7 @@ function to_check(arch)
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.")
error = i18n.translate("New version found, but failed to get new version download url.")
}
end
@ -250,8 +236,7 @@ function to_download(url)
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
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})
@ -302,25 +287,16 @@ function to_move(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
result = exec("/bin/mv", {"-f", file .. "/xray", client_file}, nil, command_timeout) == 0
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)
error = i18n.translatef("Can't move new file to path: %s", client_file)
}
end
exec("/bin/chmod", {"-R", "755", client_file})
exec("/bin/chmod", {"-R", "755", client_file .. "/xray"})
sys.call("/bin/rm -rf /tmp/v2ray_extract.*")

View File

@ -1,238 +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("http",translate("Http"))
protocol:value("shadowsocks", translate("Shadowsocks"))
socks_username = t:option(Value, "socks_username", translate("User name"))
socks_username.rmempty = true
socks_username:depends("protocol", "socks")
socks_password = t:option(Value, "socks_password", translate("Password"))
socks_password.rmempty = true
socks_password.password = true
socks_password:depends("protocol", "socks")
http_username = t:option(Value, "http_username", translate("User name"))
http_username.rmempty = true
http_username:depends("protocol", "http")
http_password = t:option(Value, "http_password", translate("Password"))
http_password.rmempty = true
http_password.password = true
http_password:depends("protocol", "http")
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)
accept_lan = t:option(Flag, "accept_lan", translate("Accept LAN Access"),
translate(
"When selected, it can accessed lan , this will not be safe!"))
accept_lan.default = "0"
accept_lan.rmempty = false
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,7 +1,4 @@
local i = require "luci.dispatcher"
local e = require "nixio.fs"
local e = require "luci.sys"
local e = luci.model.uci.cursor()
local ds = require "luci.dispatcher"
local o = "v2ray_server"
m = Map(o, translate("V2ray Server"))
@ -17,88 +14,41 @@ 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")
t.extedit = ds.build_url("admin", "services", 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))
local uuid = luci.sys.exec("echo -n $(cat /proc/sys/kernel/random/uuid)") or ""
uuid = string.gsub(uuid, "-", "")
local e = TypedSection.create(t, uuid)
luci.http.redirect(ds.build_url("admin", "services", o, "config", uuid))
end
function t.remove(t, a)
t.map.proceed = true
t.map:del(a)
luci.http.redirect(i.build_url("admin", "vpn", o))
luci.http.redirect(ds.build_url("admin", "services", 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.rawhtml = true
e.cfgvalue = function(t, n)
return string.format('<font class="users_status" hint="%s">%s</font>', n, translate("Collecting data..."))
end
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"
elseif protocol == "http" then
e = "http_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"))

View File

@ -0,0 +1,315 @@
local d = require "luci.dispatcher"
local appname = "v2ray_server"
local v_ss_encrypt_method_list = {
"aes-128-cfb", "aes-256-cfb", "aes-128-gcm", "aes-256-gcm", "chacha20", "chacha20-ietf", "chacha20-poly1305", "chacha20-ietf-poly1305"
}
local header_type_list = {
"none", "srtp", "utp", "wechat-video", "dtls", "wireguard"
}
m = Map(appname, translate("Server Config"))
m.redirect = d.build_url("admin", "services", appname)
s = m:section(NamedSection, arg[1], "user", "")
s.addremove = false
s.dynamic = false
enable = s:option(Flag, "enable", translate("Enable"))
enable.default = "1"
enable.rmempty = false
remarks = s:option(Value, "remarks", translate("Remarks"))
remarks.default = translate("Remarks")
remarks.rmempty = false
protocol = s:option(ListValue, "protocol", translate("Protocol"))
protocol:value("vmess", "Vmess")
protocol:value("vless", "VLESS")
protocol:value("http", "HTTP")
protocol:value("socks", "Socks")
protocol:value("shadowsocks", "Shadowsocks")
protocol:value("trojan", "Trojan")
protocol:value("mtproto", "MTProto")
port = s:option(Value, "port", translate("Port"))
port.datatype = "port"
port.rmempty = false
auth = s:option(Flag, "auth", translate("Auth"))
auth.validate = function(self, value, t)
if value and value == "1" then
local user_v = username:formvalue(t) or ""
local pass_v = password:formvalue(t) or ""
if user_v == "" or pass_v == "" then
return nil, translate("Username and Password must be used together!")
end
end
return value
end
auth:depends({ protocol = "socks" })
auth:depends({ protocol = "http" })
username = s:option(Value, "username", translate("Username"))
username:depends("auth", "1")
password = s:option(Value, "password", translate("Password"))
password.password = true
password:depends("auth", "1")
password:depends({ protocol = "shadowsocks" })
mtproto_password = s:option(Value, "mtproto_password", translate("Password"), translate("The MTProto protocol must be 32 characters and can only contain characters from 0 to 9 and a to f."))
mtproto_password:depends({ protocol = "mtproto" })
mtproto_password.default = arg[1]
function mtproto_password.cfgvalue(self, section)
return m:get(section, "password")
end
function mtproto_password.write(self, section, value)
m:set(section, "password", value)
end
decryption = s:option(Value, "decryption", translate("Encrypt Method"))
decryption.default = "none"
decryption:depends({ protocol = "vless" })
v_ss_encrypt_method = s:option(ListValue, "v_ss_encrypt_method", translate("Encrypt Method"))
for a, t in ipairs(v_ss_encrypt_method_list) do v_ss_encrypt_method:value(t) end
v_ss_encrypt_method:depends({ protocol = "shadowsocks" })
function v_ss_encrypt_method.cfgvalue(self, section)
return m:get(section, "method")
end
function v_ss_encrypt_method.write(self, section, value)
m:set(section, "method", value)
end
ss_network = s: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" })
uuid = s:option(DynamicList, "uuid", translate("ID") .. "/" .. translate("Password"))
for i = 1, 3 do
uuid:value(luci.sys.exec("echo -n $(cat /proc/sys/kernel/random/uuid)"))
end
uuid:depends({ protocol = "vmess" })
uuid:depends({ protocol = "vless" })
uuid:depends({ protocol = "trojan" })
alter_id = s:option(Value, "alter_id", translate("Alter ID"))
alter_id.default = 16
alter_id:depends({ protocol = "vmess" })
level = s:option(Value, "level", translate("User Level"))
level.default = 1
level:depends({ protocol = "vmess" })
level:depends({ protocol = "vless" })
level:depends({ protocol = "shadowsocks" })
level:depends({ protocol = "trojan" })
level:depends({ protocol = "mtproto" })
tls = s:option(Flag, "tls", translate("TLS"))
tls.default = 0
tls.validate = function(self, value, t)
if value then
if value == "1" then
local ca = tls_certificateFile:formvalue(t) or ""
local key = tls_keyFile:formvalue(t) or ""
if ca == "" or key == "" then
return nil, translate("Public key and Private key path can not be empty!")
end
end
return value
end
end
tls:depends({ protocol = "vmess" })
tls:depends({ protocol = "vless" })
tls:depends({ protocol = "socks" })
tls:depends({ protocol = "shadowsocks" })
xtls = s:option(Flag, "xtls", translate("XTLS"))
xtls.default = 0
xtls:depends({ protocol = "vless", tls = "1" })
flow = s:option(Value, "flow", translate("flow"))
flow.default = "xtls-rprx-direct"
flow:value("xtls-rprx-origin")
flow:value("xtls-rprx-origin-udp443")
flow:value("xtls-rprx-direct")
flow:value("xtls-rprx-direct-udp443")
flow:value("xtls-rprx-splice")
flow:value("xtls-rprx-splice-udp443")
flow:depends("xtls", "1")
-- [[ TLS部分 ]] --
tls_serverName = s:option(Value, "tls_serverName", translate("Domain"))
tls_serverName:depends("tls", "1")
tls_certificateFile = s:option(Value, "tls_certificateFile", translate("Public key absolute path"), translate("as:") .. "/etc/ssl/fullchain.pem")
tls_certificateFile.validate = function(self, value, t)
if value and value ~= "" then
if not nixio.fs.access(value) then
return nil, translate("Can't find this file!")
else
return value
end
end
return nil
end
tls_certificateFile:depends("tls", "1")
tls_keyFile = s:option(Value, "tls_keyFile", translate("Private key absolute path"), translate("as:") .. "/etc/ssl/private.key")
tls_keyFile.validate = function(self, value, t)
if value and value ~= "" then
if not nixio.fs.access(value) then
return nil, translate("Can't find this file!")
else
return value
end
end
return nil
end
tls_keyFile:depends("tls", "1")
transport = s:option(ListValue, "transport", translate("Transport"))
transport:value("tcp", "TCP")
transport:value("mkcp", "mKCP")
transport:value("ws", "WebSocket")
transport:value("h2", "HTTP/2")
transport:value("ds", "DomainSocket")
transport:value("quic", "QUIC")
transport:depends({ protocol = "vmess" })
transport:depends({ protocol = "vless" })
transport:depends({ protocol = "socks" })
transport:depends({ protocol = "shadowsocks" })
transport:depends({ protocol = "trojan" })
-- [[ WebSocket部分 ]]--
ws_host = s:option(Value, "ws_host", translate("WebSocket Host"))
ws_host:depends("transport", "ws")
ws_host:depends("ss_transport", "ws")
ws_host:depends("trojan_transport", "h2+ws")
ws_host:depends("trojan_transport", "ws")
ws_path = s:option(Value, "ws_path", translate("WebSocket Path"))
ws_path:depends("transport", "ws")
ws_path:depends("ss_transport", "ws")
ws_path:depends("trojan_transport", "h2+ws")
ws_path:depends("trojan_transport", "ws")
-- [[ HTTP/2部分 ]]--
h2_host = s:option(Value, "h2_host", translate("HTTP/2 Host"))
h2_host:depends("transport", "h2")
h2_host:depends("ss_transport", "h2")
h2_host:depends("trojan_transport", "h2+ws")
h2_host:depends("trojan_transport", "h2")
h2_path = s:option(Value, "h2_path", translate("HTTP/2 Path"))
h2_path:depends("transport", "h2")
h2_path:depends("ss_transport", "h2")
h2_path:depends("trojan_transport", "h2+ws")
h2_path:depends("trojan_transport", "h2")
-- [[ TCP部分 ]]--
-- TCP伪装
tcp_guise = s:option(ListValue, "tcp_guise", translate("Camouflage Type"))
tcp_guise:value("none", "none")
tcp_guise:value("http", "http")
tcp_guise:depends("transport", "tcp")
-- HTTP域名
tcp_guise_http_host = s:option(DynamicList, "tcp_guise_http_host", translate("HTTP Host"))
tcp_guise_http_host:depends("tcp_guise", "http")
-- HTTP路径
tcp_guise_http_path = s:option(DynamicList, "tcp_guise_http_path", translate("HTTP Path"))
tcp_guise_http_path:depends("tcp_guise", "http")
-- [[ mKCP部分 ]]--
mkcp_guise = s: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_list) do mkcp_guise:value(t) end
mkcp_guise:depends("transport", "mkcp")
mkcp_mtu = s:option(Value, "mkcp_mtu", translate("KCP MTU"))
mkcp_mtu.default = "1350"
mkcp_mtu:depends("transport", "mkcp")
mkcp_tti = s:option(Value, "mkcp_tti", translate("KCP TTI"))
mkcp_tti.default = "20"
mkcp_tti:depends("transport", "mkcp")
mkcp_uplinkCapacity = s:option(Value, "mkcp_uplinkCapacity", translate("KCP uplinkCapacity"))
mkcp_uplinkCapacity.default = "5"
mkcp_uplinkCapacity:depends("transport", "mkcp")
mkcp_downlinkCapacity = s:option(Value, "mkcp_downlinkCapacity", translate("KCP downlinkCapacity"))
mkcp_downlinkCapacity.default = "20"
mkcp_downlinkCapacity:depends("transport", "mkcp")
mkcp_congestion = s:option(Flag, "mkcp_congestion", translate("KCP Congestion"))
mkcp_congestion:depends("transport", "mkcp")
mkcp_readBufferSize = s:option(Value, "mkcp_readBufferSize", translate("KCP readBufferSize"))
mkcp_readBufferSize.default = "1"
mkcp_readBufferSize:depends("transport", "mkcp")
mkcp_writeBufferSize = s:option(Value, "mkcp_writeBufferSize", translate("KCP writeBufferSize"))
mkcp_writeBufferSize.default = "1"
mkcp_writeBufferSize:depends("transport", "mkcp")
mkcp_seed = s:option(Value, "mkcp_seed", translate("KCP Seed"))
mkcp_seed:depends("transport", "mkcp")
-- [[ DomainSocket部分 ]]--
ds_path = s:option(Value, "ds_path", "Path", translate("A legal file path. This file must not exist before running V2Ray."))
ds_path:depends("transport", "ds")
-- [[ QUIC部分 ]]--
quic_security = s: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 = s:option(Value, "quic_key", translate("Encrypt Method") .. translate("Key"))
quic_key:depends("transport", "quic")
quic_guise = s:option(ListValue, "quic_guise", translate("Camouflage Type"))
for a, t in ipairs(header_type_list) do quic_guise:value(t) end
quic_guise:depends("transport", "quic")
-- [[ VLESS Fallback部分 ]]--
--[[
fallback = s:option(Flag, "fallback", translate("Fallback"))
fallback:depends({ protocol = "vless", transport = "tcp", tls = "1" })
fallback_alpn = s:option(Value, "fallback_alpn", "Fallback alpn")
fallback_alpn:depends("fallback", "1")
fallback_path = s:option(Value, "fallback_path", "Fallback path")
fallback_path:depends("fallback", "1")
fallback_dest = s:option(Value, "fallback_dest", "Fallback dest")
fallback_dest:depends("fallback", "1")
fallback_xver = s:option(Value, "fallback_xver", "Fallback xver")
fallback_xver.default = 0
fallback_xver:depends("fallback", "1")
]]--
bind_local = s: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"
accept_lan = s:option(Flag, "accept_lan", translate("Accept LAN Access"), translate("When selected, it can accessed lan , this will not be safe!"))
accept_lan.default = "0"
accept_lan.rmempty = false
return m

View File

@ -1,7 +1,7 @@
<script type="text/javascript">
//<![CDATA[
function clear_log(btn) {
XHR.get('<%=url([[admin]], [[vpn]], [[v2ray_server]], [[clear_log]])%>', null,
XHR.get('<%=url([[admin]], [[services]], [[v2ray_server]], [[clear_log]])%>', null,
function(x, data) {
if(x && x.status == 200) {
var log_textarea = document.getElementById('log_textarea');
@ -11,7 +11,7 @@
}
);
}
XHR.poll(3, '<%=url([[admin]], [[vpn]], [[v2ray_server]], [[get_log]])%>', null,
XHR.poll(3, '<%=url([[admin]], [[services]], [[v2ray_server]], [[get_log]])%>', null,
function(x, data) {
if(x && x.status == 200) {
var log_textarea = document.getElementById('log_textarea');

View File

@ -1,21 +1,17 @@
<%
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;
var users_status = document.getElementsByClassName('users_status');
for(var i = 0; i < users_status.length; i++) {
var id = 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")%>', {
XHR.poll(1, '<%=url([[admin]], [[services]], [[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');
users_status[result.index].setAttribute("style","font-weight:bold;");
users_status[result.index].setAttribute("color",result.status ? "green":"red");
users_status[result.index].innerHTML = (result.status ? '✓' : 'X');
}
);
}

View File

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

View File

@ -1,18 +1,20 @@
<%
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"
local v2ray_version=luci.sys.exec("/usr/bin/xray -version | awk '{print $2}' | sed -n 1P")
-%>
<script type="text/javascript">
//<![CDATA[
var v2rayInfo;
var tokenStr = '<%=token%>';
var noUpdateText = '<%:已是最新版本%>';
var updateSuccessText = '<%:更新成功.%>';
var clickToUpdateText = '<%:点击更新%>';
var inProgressText = '<%:正在更新...%>';
var unexpectedErrorText = '<%:意外错误.%>';
var updateInProgressNotice = '<%:正在更新,你确认要关闭吗?%>';
var noUpdateText = '<%:It is the latest version%>';
var updateSuccessText = '<%:Update successful%>';
var clickToUpdateText = '<%:Click to update%>';
var inProgressText = '<%:Updating...%>';
var unexpectedErrorText = '<%:Unexpected error%>';
var updateInProgressNotice = '<%:Updating, are you sure to close?%>';
var downloadingText = '<%:Downloading...%>';
var decompressioningText = '<%:Unpacking...%>';
var movingText = '<%:Moving...%>';
window.onload = function() {
var v2rayCheckBtn = document.getElementById('_v2ray-check_btn');
@ -82,7 +84,7 @@ local dsp = require "luci.dispatcher"
var ckeckDetailElm = document.getElementById(btn.id + '-detail');
doAjaxGet('<%=dsp.build_url("admin/vpn/v2ray_server/check")%>', {
doAjaxGet('<%=url([[admin]], [[services]], [[v2ray_server]], [[check]])%>', {
token: tokenStr,
arch: ''
}, function(json) {
@ -118,11 +120,11 @@ local dsp = require "luci.dispatcher"
function doUpdate_v2ray(btn) {
btn.disabled = true;
btn.value = '<%:下载中...%>';
btn.value = downloadingText;
addPageNotice_v2ray();
var v2rayUpdateUrl = '<%=dsp.build_url("admin/vpn/v2ray_server/update")%>';
var v2rayUpdateUrl = '<%=url([[admin]], [[services]], [[v2ray_server]], [[update]])%>';
// Download file
doAjaxGet(v2rayUpdateUrl, {
token: tokenStr,
@ -132,7 +134,7 @@ local dsp = require "luci.dispatcher"
removePageNotice_v2ray();
onRequestError_v2ray(btn, json.error);
} else {
btn.value = '<%:解压中...%>';
btn.value = decompressioningText;
// Extract file
doAjaxGet(v2rayUpdateUrl, {
@ -145,7 +147,7 @@ local dsp = require "luci.dispatcher"
removePageNotice_v2ray();
onRequestError_v2ray(btn, json.error);
} else {
btn.value = '<%:移动中...%>';
btn.value = movingText;
// Move file to target dir
doAjaxGet(v2rayUpdateUrl, {

View File

@ -4,6 +4,12 @@ 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 "服务器配置"
@ -52,6 +58,12 @@ 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 "请不要冲突"
@ -61,6 +73,9 @@ msgstr "使用HTTPS"
msgid "TLS Settings"
msgstr "TLS配置"
msgid "Nginx does not support HTTP/2 reverse proxies"
msgstr "Nginx 不支持HTTP/2反向代理"
msgid "as:"
msgstr "如:"
@ -73,6 +88,12 @@ 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 "transit node"
msgstr "中转到此节点"
msgid "This is the node inside passwall, which you can't use if you don't have it installed."
msgstr "这里显示的是passwall里的节点如果没有安装将无法使用此功能。"
msgid "Accept LAN Access"
msgstr "接受局域网访问"
@ -85,6 +106,66 @@ 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 "Update..."
msgstr "更新中"
msgid "It is the latest version"
msgstr "已是最新版本"
msgid "Update successful"
msgstr "更新成功"
msgid "Click to update"
msgstr "点击更新"
msgid "Updating..."
msgstr "更新中"
msgid "Unexpected error"
msgstr "意外错误"
msgid "Updating, are you sure to close?"
msgstr "正在更新,你确认要关闭吗?"
msgid "Downloading..."
msgstr "下载中"
msgid "Unpacking..."
msgstr "解压中"
msgid "Moving..."
msgstr "移动中"
msgid "Enabled"
msgstr "启用"

View File

@ -2,28 +2,3 @@
config global
option enable '0'
config user
option enable '1'
option remarks 'tcp'
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 'ws'
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 '0'
option port '30010'

View File

@ -1,64 +1,16 @@
#!/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"
if [ ! -f /var/v2server ]; then
local ret="/usr/bin/xray"
[ ! -f "$ret" ] && ret="/usr/bin/v2ray/v2ray"
[ ! -f "$ret" ] && ret="/usr/bin/v2ray"
cp -a $ret /var/v2server
fi
/var/v2server -config $CONFIG_PATH/$1.json >/dev/null 2>&1 &
}
start_v2ray_server() {
mkdir -p $CONFIG_PATH $LOG_PATH
touch $LOG_APP_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
/usr/lib/lua/luci/model/cbi/v2ray_server/api/app.lua start
}
stop() {
stop_v2ray_server
/usr/lib/lua/luci/model/cbi/v2ray_server/api/app.lua stop
}
restart() {
stop
sleep 1
start
}

View File

@ -4,8 +4,9 @@ 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.path=/var/etc/v2ray_server.include
set firewall.v2ray_server.reload=1
commit firewall
EOF
uci -q batch <<-EOF >/dev/null
@ -15,7 +16,5 @@ uci -q batch <<-EOF >/dev/null
commit ucitrack
EOF
chmod a+x /usr/share/v2ray_server/* >/dev/null 2>&1
rm -rf /tmp/luci-*
rm -rf /tmp/luci-*cache
exit 0

View File

@ -0,0 +1,11 @@
{
"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