3x-ui 式共享节点改造:nodes 状态机 + nodeClients 各自凭据

- 移除 VMess 协议
- VLESS/SS2022 按用户流量统计(AnyTLS 日志解析,SS 节点级)
- sing-box v2ray_api stats 支持
- 内存优化:面板 128M,agent 64M,sing-box 64M
This commit is contained in:
Muse
2026-10-19 22:23:40 +08:00
parent 1678f766ac
commit 084afeb8f4
35 changed files with 2142 additions and 1749 deletions
+109 -29
View File
@@ -25,52 +25,107 @@ while [[ $# -gt 0 ]]; do
done
[[ "${EUID}" -eq 0 ]] || die "请使用 root 运行"
[[ -f /etc/debian_version ]] || die "控制面一键安装器当前支持 Debian 12 / Ubuntu 22.04+"
[[ ! -e /opt/nexusgate ]] || die "检测到已有安装,请运行 nexusgate update"
IS_OPENWRT=false
if [[ -f /etc/openwrt_release ]]; then
IS_OPENWRT=true
elif [[ ! -f /etc/debian_version ]]; then
die "控制面一键安装器当前支持 Debian 12 / Ubuntu 22.04+ 和 OpenWrt"
fi
if [[ -z "$DOMAIN" ]]; then DOMAIN="$(ask '请输入面板域名(如 gate.example.com):')"; fi
[[ "$DOMAIN" =~ ^[A-Za-z0-9.-]+$ ]] || die "域名格式不正确"
if [[ -z "$DOMAIN" ]]; then
if $IS_OPENWRT; then
DOMAIN="$(ask '面板域名(OpenWrt 上可留空,留空则仅内网访问、不配置 Caddy):')"
else
DOMAIN="$(ask '请输入面板域名(如 gate.example.com):')"
fi
fi
if [[ -n "$DOMAIN" ]]; then
[[ "$DOMAIN" =~ ^[A-Za-z0-9.-]+$ ]] || die "域名格式不正确"
elif ! $IS_OPENWRT; then
die "域名不能为空"
fi
if [[ -z "$EMAIL" ]]; then EMAIL="$(ask '证书邮箱(可留空):')"; fi
info "安装系统依赖"
export DEBIAN_FRONTEND=noninteractive
apt-get update -qq
apt-get install -y -qq ca-certificates curl tar openssl nodejs caddy
if $IS_OPENWRT; then
# 注意:OpenWrt 上需预先装好 bash(本脚本依赖 bash):opkg update && opkg install bash
opkg update
# OpenWrt 上 openssl 命令行工具在 openssl-util 包里;部分精简固件没有 install 命令,需 coreutils-install
opkg install node caddy curl ca-certificates openssl-util tar coreutils-install
else
export DEBIAN_FRONTEND=noninteractive
apt-get update -qq
apt-get install -y -qq ca-certificates curl tar openssl nodejs caddy
fi
node_major="$(node -p 'process.versions.node.split(`.`)[0]')"
[[ "$node_major" -ge 18 ]] || die "需要 Node.js 18 或更高版本"
tmp_dir="$(mktemp -d /tmp/nexusgate-install.XXXXXX)"
trap 'rm -rf -- "$tmp_dir"' EXIT
info "下载 ${REPO}@${BRANCH}"
curl -fL --retry 3 "https://github.com/${REPO}/archive/refs/heads/${BRANCH}.tar.gz" -o "$tmp_dir/source.tgz"
mkdir -p "$tmp_dir/source"
tar -xzf "$tmp_dir/source.tgz" -C "$tmp_dir/source" --strip-components=1
[[ -f "$tmp_dir/source/server.js" ]] || die "安装包不完整"
# NG_LOCAL_SRC 指向本地源码目录时跳过 GitHub 下载(本地改动尚未 push 时的全新安装用这个)
src_dir=""
if [[ -n "${NG_LOCAL_SRC:-}" ]]; then
[[ -f "${NG_LOCAL_SRC}/server.js" ]] || die "NG_LOCAL_SRC 目录不完整(缺少 server.js)"
info "使用本地源码 ${NG_LOCAL_SRC}"
src_dir="${NG_LOCAL_SRC}"
else
tmp_dir="$(mktemp -d /tmp/nexusgate-install.XXXXXX)"
trap 'rm -rf -- "$tmp_dir"' EXIT
info "下载 ${REPO}@${BRANCH}"
curl -fL --retry 3 "https://github.com/${REPO}/archive/refs/heads/${BRANCH}.tar.gz" -o "$tmp_dir/source.tgz"
mkdir -p "$tmp_dir/source"
tar -xzf "$tmp_dir/source.tgz" -C "$tmp_dir/source"
# GitHub tarball 解包后只有一层顶层目录;不用 --strip-components(BusyBox tar 不支持)
src_dir="$(echo "$tmp_dir/source"/*/)"
[[ -f "${src_dir}server.js" ]] || die "安装包不完整"
fi
getent group nexusgate >/dev/null || groupadd --system nexusgate
id nexusgate >/dev/null 2>&1 || useradd --system --gid nexusgate --home-dir /var/lib/nexusgate --shell /usr/sbin/nologin nexusgate
install -d -o root -g root -m 0755 /opt/nexusgate
cp -a "$tmp_dir/source/." /opt/nexusgate/
install -d -o nexusgate -g nexusgate -m 0700 /var/lib/nexusgate
if $IS_OPENWRT; then
# OpenWrt 是单用户系统,不建专用账号;/var 是内存盘,数据必须放闪存
DATA_DIR=/etc/nexusgate/data
install -d -m 0755 /opt/nexusgate
cp -a "${src_dir}/." /opt/nexusgate/
install -d -m 0700 "$DATA_DIR"
else
DATA_DIR=/var/lib/nexusgate
getent group nexusgate >/dev/null || groupadd --system nexusgate
id nexusgate >/dev/null 2>&1 || useradd --system --gid nexusgate --home-dir /var/lib/nexusgate --shell /usr/sbin/nologin nexusgate
install -d -o root -g root -m 0755 /opt/nexusgate
cp -a "${src_dir}/." /opt/nexusgate/
install -d -o nexusgate -g nexusgate -m 0700 "$DATA_DIR"
fi
if $IS_OPENWRT && [[ -z "$DOMAIN" ]]; then
# 无域名:直接监听内网(OpenWrt 防火墙默认拦截 WAN 入站),纯 HTTP
NG_HOST=0.0.0.0
NG_COOKIE_SECURE=false
else
NG_HOST=127.0.0.1
NG_COOKIE_SECURE=true
fi
admin_password="$(openssl rand -base64 24 | tr -d '\n')"
session_secret="$(openssl rand -hex 32)"
cat > /etc/nexusgate.env <<EOF
NODE_ENV=production
NG_HOST=127.0.0.1
NG_HOST=${NG_HOST}
NG_PORT=8787
NG_DATA_FILE=/var/lib/nexusgate/nexusgate.json
NG_COOKIE_SECURE=true
NG_DATA_FILE=${DATA_DIR}/nexusgate.json
NG_COOKIE_SECURE=${NG_COOKIE_SECURE}
NG_ADMIN_USERNAME=admin
NG_ADMIN_PASSWORD=${admin_password}
NG_SESSION_SECRET=${session_secret}
EOF
chmod 0600 /etc/nexusgate.env
install -m 0644 /opt/nexusgate/systemd/nexusgate.service /etc/systemd/system/nexusgate.service
install -d -m 0755 /usr/local/sbin
install -m 0755 /opt/nexusgate/scripts/nexusgate.sh /usr/local/sbin/nexusgate
ln -sf /usr/local/sbin/nexusgate /usr/local/sbin/ng
if $IS_OPENWRT; then
install -m 0755 /opt/nexusgate/openrc/nexusgate /etc/init.d/nexusgate
else
install -m 0644 /opt/nexusgate/systemd/nexusgate.service /etc/systemd/system/nexusgate.service
fi
if [[ -n "$DOMAIN" ]]; then
install -d -m 0755 /etc/caddy/Caddyfile.d
cat > /etc/caddy/Caddyfile.d/nexusgate.caddy <<EOF
${DOMAIN} {
@@ -96,23 +151,48 @@ fi
caddy fmt --overwrite /etc/caddy/Caddyfile
caddy fmt --overwrite /etc/caddy/Caddyfile.d/nexusgate.caddy
caddy validate --config /etc/caddy/Caddyfile
fi
systemctl daemon-reload
systemctl enable --now nexusgate.service
if $IS_OPENWRT; then
/etc/init.d/nexusgate enable
/etc/init.d/nexusgate start
else
systemctl daemon-reload
systemctl enable --now nexusgate.service
fi
healthy=false
for _ in {1..30}; do
if curl -fsS http://127.0.0.1:8787/healthz >/dev/null 2>&1; then healthy=true; break; fi
sleep 1
done
if [[ "$healthy" != true ]]; then
systemctl status nexusgate --no-pager || true
journalctl -u nexusgate -n 100 --no-pager || true
if $IS_OPENWRT; then
logread 2>/dev/null | grep -a nexusgate | tail -n 100 || true
else
systemctl status nexusgate --no-pager || true
journalctl -u nexusgate -n 100 --no-pager || true
fi
die "控制面启动失败,诊断信息已输出"
fi
if $IS_OPENWRT && [[ "$NG_HOST" == "0.0.0.0" ]] && ! netstat -tln 2>/dev/null | grep -q "0.0.0.0:8787"; then
die "控制面未监听 0.0.0.0:8787(环境变量未正确传递),请检查 /etc/init.d/nexusgate"
fi
sed -i '/^NG_ADMIN_PASSWORD=/d' /etc/nexusgate.env
systemctl enable --now caddy
systemctl reload caddy
if [[ -n "$DOMAIN" ]]; then
if $IS_OPENWRT; then
/etc/init.d/caddy enable
/etc/init.d/caddy restart
else
systemctl enable --now caddy
systemctl reload caddy
fi
fi
printf '\n\033[1;32m安装完成\033[0m\n'
printf '地址:https://%s\n账号:admin\n密码:%s\n' "$DOMAIN" "$admin_password"
if [[ -n "$DOMAIN" ]]; then
printf '地址:https://%s\n' "$DOMAIN"
else
printf '地址:http://<路由器 LAN IP>:8787(内网访问)\n'
fi
printf '账号:admin\n密码:%s\n' "$admin_password"
printf '管理命令:nexusgate 或 ng\n请立即登录并妥善保存密码。\n'