diff --git a/README.md b/README.md index 7e3b01f..36a48f7 100644 --- a/README.md +++ b/README.md @@ -72,8 +72,74 @@ rm -rf internal/httpapi/dist && cp -r web/dist internal/httpapi/dist cd web && npm run build && rm -rf internal/httpapi/dist && cp -r web/dist internal/httpapi/dist && go build -o webui4frpc ./cmd/webui4frpc ``` +## 安装 + +从 [Release 页](https://gitcode.com/JianFeeeee/webui4frpc/releases/latest) 下载对应平台的安装包。 + +### Linux (Debian / Ubuntu) + +```bash +sudo dpkg -i webui4frpc-0.1.0-linux-amd64.deb # 或 linux-arm64.deb +sudo nano /etc/webui4frpc/webui4frpc.env # 改掉默认密码 +sudo systemctl restart webui4frpc +``` + +### Linux (RHEL / Fedora / openEuler / 默认 / 龙蜥) + +```bash +sudo rpm -i webui4frpc-0.1.0-linux-x86_64.rpm +sudo vi /etc/webui4frpc/webui4frpc.env +sudo systemctl enable --now webui4frpc +``` + +安装后自动建 `webui4frpc` 系统账号、注册 systemd 单元,数据目录在 `/var/lib/webui4frpc`。 +服务以非 root 运行,并启用 `ProtectSystem=full` / `NoNewPrivileges` 等加固项。 + +### Windows + +双击 `webui4frpc-0.1.0-windows-amd64-setup.exe`(ARM 设备用 `windows-arm64-setup.exe`)。 +安装向导会询问监听地址与管理员凭据,完成后: + +- 程序装到 `C:\Program Files\webui4frpc` +- 开始菜单生成启动快捷方式与卸载项 +- 自动添加防火墙入站规则 + +### macOS + +```bash +sudo installer -pkg webui4frpc-0.1.0-darwin-arm64.pkg -target / # Apple Silicon +sudo installer -pkg webui4frpc-0.1.0-darwin-amd64.pkg -target / # Intel +``` + +或直接双击 `.pkg` 走图形安装器。安装后 launchd 开机自启,配置在 +`/usr/local/etc/webui4frpc/webui4frpc.env`,改完执行: + +```bash +sudo launchctl kickstart -k system/com.jianfeeeee.webui4frpc +``` + +> 该 `.pkg` 未经 Apple 公证(notarization)。首次打开若被 Gatekeeper 拦下, +> 在「系统设置 → 隐私与安全」选择仍要打开。 + +### 免安装(tar.gz / zip) + +不想装服务只想跑一下,用对应的 `.tar.gz` / `.zip`,解压即用: + +```bash +tar xzf webui4frpc-0.1.0-linux-amd64.tar.gz +./webui4frpc -addr 127.0.0.1:7500 -user admin -password 你的密码 -workdir ./data +``` + +下载后可用 `SHA256SUMS` 校验完整性: + +```bash +sha256sum -c SHA256SUMS --ignore-missing +``` + ## 运行 +从源码构建后直接跑: + ```bash ./webui4frpc -addr 127.0.0.1:7500 -user admin -password admin123 -workdir ./data ``` diff --git a/packaging/common/LICENSE.txt b/packaging/common/LICENSE.txt new file mode 100644 index 0000000..c59abe4 --- /dev/null +++ b/packaging/common/LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 JianFeeeee + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packaging/common/webui4frpc.env b/packaging/common/webui4frpc.env new file mode 100644 index 0000000..2a74d9c --- /dev/null +++ b/packaging/common/webui4frpc.env @@ -0,0 +1,8 @@ +# webui4frpc runtime configuration +# 默认监听全部接口的 7500 端口;请修改密码后再投入生产。 +W4F_ADDR=0.0.0.0:7500 +W4F_USER=admin +W4F_PASSWORD=admin + +# 可选:首次提供的 frpc 二进制路径(留空则从 Web UI 手动/自动安装) +# W4F_FRPC=/usr/local/bin/frpc diff --git a/packaging/common/webui4frpc.service b/packaging/common/webui4frpc.service new file mode 100644 index 0000000..a1654e8 --- /dev/null +++ b/packaging/common/webui4frpc.service @@ -0,0 +1,25 @@ +[Unit] +Description=webui4frpc - visual frpc controller (cluster node) +Documentation=https://gitcode.com/JianFeeeee/webui4frpc +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple +User=webui4frpc +Group=webui4frpc +EnvironmentFile=/etc/webui4frpc/webui4frpc.env +ExecStart=/usr/bin/webui4frpc -addr ${W4F_ADDR} -user ${W4F_USER} -password ${W4F_PASSWORD} -workdir /var/lib/webui4frpc +Restart=always +RestartSec=5 +LimitNOFILE=65535 + +# hardening +NoNewPrivileges=true +PrivateTmp=true +ProtectSystem=full +ProtectHome=true +ReadWritePaths=/var/lib/webui4frpc + +[Install] +WantedBy=multi-user.target diff --git a/packaging/deb/postinst b/packaging/deb/postinst new file mode 100755 index 0000000..7998640 --- /dev/null +++ b/packaging/deb/postinst @@ -0,0 +1,42 @@ +#!/bin/sh +# webui4frpc deb/rpm 安装后脚本:建用户、建目录、装服务 +set -e + +if ! getent group webui4frpc >/dev/null 2>&1; then + groupadd --system webui4frpc +fi +if ! getent passwd webui4frpc >/dev/null 2>&1; then + useradd --system --gid webui4frpc \ + --home-dir /var/lib/webui4frpc --no-create-home \ + --shell /usr/sbin/nologin \ + --comment "webui4frpc service account" webui4frpc +fi + +mkdir -p /var/lib/webui4frpc +chown -R webui4frpc:webui4frpc /var/lib/webui4frpc +chmod 750 /var/lib/webui4frpc +chown root:webui4frpc /etc/webui4frpc/webui4frpc.env 2>/dev/null || true +chmod 640 /etc/webui4frpc/webui4frpc.env 2>/dev/null || true + +if command -v systemctl >/dev/null 2>&1; then + systemctl daemon-reload || true + systemctl enable webui4frpc.service || true + if [ -z "${DPKG_ROOT:-}" ]; then + systemctl restart webui4frpc.service || true + fi +fi + +cat <<'EOF' + +webui4frpc 已安装。 + + 配置: /etc/webui4frpc/webui4frpc.env (请立即修改 W4F_USER / W4F_PASSWORD) + 数据: /var/lib/webui4frpc + 服务: systemctl status webui4frpc + 界面: http://<本机地址>:7500 + +改完配置后执行: systemctl restart webui4frpc + +EOF + +exit 0 diff --git a/packaging/deb/postrm b/packaging/deb/postrm new file mode 100755 index 0000000..b955881 --- /dev/null +++ b/packaging/deb/postrm @@ -0,0 +1,20 @@ +#!/bin/sh +# webui4frpc deb 卸载后脚本:purge 时清数据与账号 +set -e + +if command -v systemctl >/dev/null 2>&1; then + systemctl daemon-reload || true +fi + +if [ "$1" = "purge" ]; then + rm -rf /var/lib/webui4frpc + rm -rf /etc/webui4frpc + if getent passwd webui4frpc >/dev/null 2>&1; then + userdel webui4frpc || true + fi + if getent group webui4frpc >/dev/null 2>&1; then + groupdel webui4frpc || true + fi +fi + +exit 0 diff --git a/packaging/deb/prerm b/packaging/deb/prerm new file mode 100755 index 0000000..d63e2df --- /dev/null +++ b/packaging/deb/prerm @@ -0,0 +1,10 @@ +#!/bin/sh +# webui4frpc deb 卸载前脚本:停服务、撤 enable +set -e + +if command -v systemctl >/dev/null 2>&1; then + systemctl stop webui4frpc.service || true + systemctl disable webui4frpc.service || true +fi + +exit 0 diff --git a/packaging/macos/build_pkg.sh b/packaging/macos/build_pkg.sh new file mode 100755 index 0000000..ef1fa1a --- /dev/null +++ b/packaging/macos/build_pkg.sh @@ -0,0 +1,187 @@ +#!/usr/bin/env bash +# +# 在 Linux 上手工组装 macOS .pkg (flat package)。 +# macOS 的 pkgbuild/productbuild 不可用,因此用 xar + mkbom 手搓 flat package: +# +# archive.xar +# ├── Distribution 安装器界面描述 (choices / title) +# └── webui4frpc.pkg/ +# ├── PackageInfo 组件元数据 + postinstall 声明 +# ├── Bom 文件清单 (mkbom 生成) +# ├── Payload 根文件系统 cpio.gz +# └── Scripts 安装脚本 cpio.gz +# +# 用法: build_pkg.sh +set -euo pipefail + +VERSION="$1"; ARCH="$2" +# xar 与 cpio 都在临时目录里执行,输入输出必须先转成绝对路径 +BINARY="$(cd "$(dirname "$3")" && pwd)/$(basename "$3")" +mkdir -p "$(dirname "$4")" +OUTFILE="$(cd "$(dirname "$4")" && pwd)/$(basename "$4")" + +case "$ARCH" in + amd64) PKG_ARCH="x86_64" ;; + arm64) PKG_ARCH="arm64" ;; + *) echo "未知架构: $ARCH" >&2; exit 1 ;; +esac + +WORK="$(mktemp -d)" +trap 'rm -rf "$WORK"' EXIT + +ROOT="$WORK/root" +SCRIPTS="$WORK/scripts" +FLAT="$WORK/flat" +PKGDIR="$FLAT/webui4frpc.pkg" +mkdir -p "$ROOT/usr/local/bin" "$ROOT/usr/local/etc/webui4frpc" "$SCRIPTS" "$PKGDIR" + +# ---- payload 根文件系统 ---- +install -m 0755 "$BINARY" "$ROOT/usr/local/bin/webui4frpc" + +cat > "$ROOT/usr/local/etc/webui4frpc/webui4frpc.env" <<'ENVEOF' +# webui4frpc runtime configuration (macOS) +# 请修改密码后再投入使用;改完执行: +# sudo launchctl kickstart -k system/com.jianfeeeee.webui4frpc +W4F_ADDR=0.0.0.0:7500 +W4F_USER=admin +W4F_PASSWORD=admin +ENVEOF +chmod 0644 "$ROOT/usr/local/etc/webui4frpc/webui4frpc.env" + +# ---- launchd plist ---- +mkdir -p "$ROOT/Library/LaunchDaemons" +cat > "$ROOT/Library/LaunchDaemons/com.jianfeeeee.webui4frpc.plist" <<'PLISTEOF' + + + + + Label + com.jianfeeeee.webui4frpc + ProgramArguments + + /usr/local/bin/webui4frpc-launch + + RunAtLoad + + KeepAlive + + StandardOutPath + /usr/local/var/log/webui4frpc.log + StandardErrorPath + /usr/local/var/log/webui4frpc.err.log + WorkingDirectory + /usr/local/var/lib/webui4frpc + + +PLISTEOF +chmod 0644 "$ROOT/Library/LaunchDaemons/com.jianfeeeee.webui4frpc.plist" + +# launchd 不读 EnvironmentFile,用小包装脚本 source 配置再 exec +cat > "$ROOT/usr/local/bin/webui4frpc-launch" <<'LAUNCHEOF' +#!/bin/sh +# 读取 /usr/local/etc/webui4frpc/webui4frpc.env 后启动 webui4frpc +set -e +ENV_FILE=/usr/local/etc/webui4frpc/webui4frpc.env +W4F_ADDR=0.0.0.0:7500 +W4F_USER=admin +W4F_PASSWORD=admin +[ -f "$ENV_FILE" ] && . "$ENV_FILE" +exec /usr/local/bin/webui4frpc \ + -addr "$W4F_ADDR" \ + -user "$W4F_USER" \ + -password "$W4F_PASSWORD" \ + -workdir /usr/local/var/lib/webui4frpc +LAUNCHEOF +chmod 0755 "$ROOT/usr/local/bin/webui4frpc-launch" + +# ---- postinstall ---- +cat > "$SCRIPTS/postinstall" <<'POSTEOF' +#!/bin/sh +set -e +mkdir -p /usr/local/var/lib/webui4frpc /usr/local/var/log +chmod 750 /usr/local/var/lib/webui4frpc + +PLIST=/Library/LaunchDaemons/com.jianfeeeee.webui4frpc.plist +chown root:wheel "$PLIST" +chmod 644 "$PLIST" + +# 覆盖安装时先卸载旧的 +launchctl bootout system "$PLIST" 2>/dev/null || true +launchctl bootstrap system "$PLIST" 2>/dev/null || \ + launchctl load -w "$PLIST" 2>/dev/null || true + +exit 0 +POSTEOF +chmod 0755 "$SCRIPTS/postinstall" + +# ---- Payload / Scripts (cpio.gz) ---- +( cd "$ROOT" && find . -print | cpio -o --format odc --owner 0:0 2>/dev/null ) \ + | gzip -9 > "$PKGDIR/Payload" +( cd "$SCRIPTS" && find . -print | cpio -o --format odc --owner 0:0 2>/dev/null ) \ + | gzip -9 > "$PKGDIR/Scripts" + +# ---- Bom ---- +mkbom -u 0 -g 80 "$ROOT" "$PKGDIR/Bom" + +NFILES=$(find "$ROOT" | wc -l) +INSTALL_KB=$(du -sk "$ROOT" | cut -f1) + +# ---- PackageInfo ---- +cat > "$PKGDIR/PackageInfo" < + + + + + + + +INFOEOF + +# ---- Distribution ---- +cat > "$FLAT/Distribution" < + + webui4frpc $VERSION + com.jianfeeeee + + + + + + webui4frpc — 可视化 frpc 控制器 + +安装内容: + /usr/local/bin/webui4frpc 主程序(内嵌 Web 界面) + /usr/local/etc/webui4frpc/webui4frpc.env 配置文件 + /Library/LaunchDaemons/…webui4frpc.plist 开机自启服务 + +安装完成后服务自动启动,访问 http://127.0.0.1:7500 +默认凭据 admin/admin —— 请立即修改 webui4frpc.env 中的密码。 + + + + + + + + + + #webui4frpc.pkg + +DISTEOF + +# ---- 打成 xar (Distribution 必须是第一个成员) ---- +rm -f "$OUTFILE" +( cd "$FLAT" && xar --compression none -cf "$OUTFILE" Distribution webui4frpc.pkg ) + +echo "已生成: $OUTFILE ($(du -h "$OUTFILE" | cut -f1))" diff --git a/packaging/rpm/webui4frpc.spec b/packaging/rpm/webui4frpc.spec new file mode 100644 index 0000000..b48b3c2 --- /dev/null +++ b/packaging/rpm/webui4frpc.spec @@ -0,0 +1,72 @@ +Name: webui4frpc +Version: %{?_w4f_version}%{!?_w4f_version:0.1.0} +Release: 1%{?dist} +Summary: Visual frpc controller with token-ring cluster +License: MIT +URL: https://gitcode.com/JianFeeeee/webui4frpc +BuildArch: %{?_w4f_arch}%{!?_w4f_arch:x86_64} + +Requires(pre): shadow-utils +%{?systemd_requires} +BuildRequires: systemd-rpm-macros + +%description +A web UI to manage FRP forwards across a cluster of nodes. Ships as a single +statically-linked binary with an embedded Vue frontend. Includes a token-ring +based cluster for distributed forward management, NIC network load sampling, +and audit-log CSV export. + +%install +install -D -m 0755 %{_w4f_bin} %{buildroot}%{_bindir}/webui4frpc +install -D -m 0644 %{_w4f_service} %{buildroot}%{_unitdir}/webui4frpc.service +install -D -m 0640 %{_w4f_env} %{buildroot}%{_sysconfdir}/webui4frpc/webui4frpc.env +install -D -m 0644 %{_w4f_readme} %{buildroot}%{_docdir}/webui4frpc/README.md +install -d -m 0750 %{buildroot}%{_sharedstatedir}/webui4frpc + +%pre +getent group webui4frpc >/dev/null || groupadd --system webui4frpc +getent passwd webui4frpc >/dev/null || \ + useradd --system --gid webui4frpc \ + --home-dir %{_sharedstatedir}/webui4frpc --no-create-home \ + --shell /sbin/nologin \ + --comment "webui4frpc service account" webui4frpc +exit 0 + +%post +%systemd_post webui4frpc.service +chown -R webui4frpc:webui4frpc %{_sharedstatedir}/webui4frpc || : +chown root:webui4frpc %{_sysconfdir}/webui4frpc/webui4frpc.env || : +cat <<'EOF' + +webui4frpc 已安装。 + + 配置: /etc/webui4frpc/webui4frpc.env (请立即修改 W4F_USER / W4F_PASSWORD) + 数据: /var/lib/webui4frpc + 服务: systemctl enable --now webui4frpc + 界面: http://<本机地址>:7500 + +EOF + +%preun +%systemd_preun webui4frpc.service + +%postun +%systemd_postun_with_restart webui4frpc.service +if [ $1 -eq 0 ]; then + rm -rf %{_sharedstatedir}/webui4frpc + getent passwd webui4frpc >/dev/null && userdel webui4frpc || : + getent group webui4frpc >/dev/null && groupdel webui4frpc || : +fi + +%files +%{_bindir}/webui4frpc +%{_unitdir}/webui4frpc.service +%dir %{_sysconfdir}/webui4frpc +%config(noreplace) %attr(0640,root,webui4frpc) %{_sysconfdir}/webui4frpc/webui4frpc.env +%dir %attr(0750,webui4frpc,webui4frpc) %{_sharedstatedir}/webui4frpc +%doc %{_docdir}/webui4frpc/README.md + +%changelog +* Fri Aug 28 2026 JianFeeeee - 0.1.0-1 +- 首次发布:per-forward worker 模型、NIC 负载采样、审计 CSV 导出、 + session 登录与三级角色。 diff --git a/packaging/windows/LICENSE.txt b/packaging/windows/LICENSE.txt new file mode 100644 index 0000000..c59abe4 --- /dev/null +++ b/packaging/windows/LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 JianFeeeee + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packaging/windows/README.md b/packaging/windows/README.md new file mode 100644 index 0000000..7e3b01f --- /dev/null +++ b/packaging/windows/README.md @@ -0,0 +1,202 @@ +# webui4frpc + +> **AI-assisted**: 本项目使用 AI 辅助编程(代码与文档由 AI 协作完成,经人工复核)。 +一个**独立于 frp 源码**的可视化 frpc 控制器。用画布方式把「本地转发项」连到「远程服务器」,自动为每台远程服务器生成 frpc 配置并拉起独立 worker 进程,免去运维反复手写 frpc 配置的麻烦。支持多节点组成令牌环集群,协同分发转发任务。 + +> 本仓库**不捆绑、不包含任何 frp 源码**。frpc 可执行文件由用户一键从官方 GitHub Releases 下载,或手动指定路径。 + +## 特性 + +### 画布配置 + +- **Scratch 风格画布**:本地转发项(local)与远程服务器(remote)可视化连线,一个 local 可连多个 remote,一个 remote 可连多个 local +- **曲线连线 + 端口标签**:每条连线独立曲线,标签为远程端口,可拖拽调整曲线位置,端口可点开编辑 +- **画布冲突检查**:保存时自动检测远程端口/域名重复,避免配置冲突 +- **边开关与分组**:画布边上可一键禁用/启用单条转发,可打分组标签(状态页按分组管理、一键启停整组) +- **Del 键/× 按钮删除**:选中连线按 Delete 键或点击端口标签红 × 删除,确认后标记脏 +- **一键生成配置**:保存画布即渲染每台 remote 的 frpc JSON 配置,自动拉起/重启对应 worker +- **画布导入导出**:支持 JSON 信封备份/恢复(含 provenance 元数据) + +### frpc 能力 + +- **代理类型**:tcp(完整)、http/https(customDomains / subdomain / locations / basicAuth / headerRewrite) +- **传输参数**:useEncryption / useCompression / bandwidthLimit / poolCount / transport.protocol(tcp/quic/kcp/websocket)/ TLS +- **负载均衡与健康检查**:lbGroup + healthCheck 渲染输出;状态页通过 frpc admin API 拉取 per-proxy 真实状态(running / check failed / wait start …) +- **worker 自愈**:崩溃自动重启(指数退避),随 webui 启停 +- **frpc 一键安装**:从官方 Releases 下载任意版本 frpc,或手动指定二进制路径 + +### 集群模式(令牌环) + +- **令牌环协议**:若干 webui4frpc 节点组成协作网络;令牌按固定顺序传递,每节点持完整集群信息,单轮即全网收敛 +- **负载最低者摘取**:新转发任务附加在令牌中,由负载最低的节点自行摘取并创建 frpc worker + - 主负载信号:当前节点拥有的转发数(权重 ×100) + - 次级信号:**NIC 网络接口利用率**(从 `/proc/net/dev` 增量 + `/sys/class/net/*/speed` 计算,排除了 lo/docker/veth 等虚拟接口) + - 同转发数节点间由内存占用 + 网络饱和度打破平局 +- **per-forward 独立进程**:每条转发拥有独立的 frpc 配置文件和 frpc 进程(worker 键 = `local~remote~port`),单条转发故障不影响兄弟,消除旧模型的多节点 `proxy already exists` 冲突 +- **容错自愈**:leader / 非 leader 节点宕机 → 自动检测 → 环跳过死亡节点、任务重挂;leader 宕机 → 上邻居晋升为新 leader +- **宕机自动重联**:节点宕机重启后通过缓存的集群拓扑(对端地址 + 准入密钥)自动重联,显式脱离集群的节点不自动重联 +- **准入密钥**:每节点有 nodeKey,新节点加入需提供对端密钥;密钥随令牌环交换,全节点互通 +- **集群二进制分发**:节点间优先互传 frpc 二进制,外部 URL 兜底 + +### 认证与权限 + +- **Session-cookie 登录 + Basic Auth**:UI 使用 session-cookie(支持登出),API 保持 Basic Auth + Bearer API Key 兼容 +- **三级角色**:superadmin(账号管理)、admin(全功能除账号)、viewer(只读状态/集群/审计导出) +- **用户管理**:superadmin 角色创建/管理用户,bcrypt 存储,flag 内置账号为 system 只读 +- **API Key**:read / write / admin 三级 scope,`w4f_` 前缀,sha256 存储,创建时明文仅展示一次 +- **审计导出**:read 级 view 即可导出 CSV(账号登录记录 / API Key 使用记录 / 集群操作日志 / worker 运行日志) + +## 构建 + +### 后端(Go 1.25+) + +```bash +go build -o webui4frpc ./cmd/webui4frpc +``` + +### 前端(Node 20+) + +```bash +cd web && npm install && npm run build +``` + +前端产物在 `web/dist`,需拷贝到 `internal/httpapi/dist` 以便 Go `embed` 打包进单一二进制: + +```bash +rm -rf internal/httpapi/dist && cp -r web/dist internal/httpapi/dist +``` + +一键全量构建(dist 不进入 git,编译前必须构建前端): + +```bash +cd web && npm run build && rm -rf internal/httpapi/dist && cp -r web/dist internal/httpapi/dist && go build -o webui4frpc ./cmd/webui4frpc +``` + +## 运行 + +```bash +./webui4frpc -addr 127.0.0.1:7500 -user admin -password admin123 -workdir ./data +``` + +打开 ,输入账号密码进入画布。首次使用可到「设置」页一键安装 frpc(或手动指定路径),然后回到画布创建 local / remote 并连线、保存。 + +### 参数 + +| 参数 | 默认 | 说明 | +| --- | --- | --- | +| `-addr` | `127.0.0.1:7500` | 监听地址 | +| `-user` / `-password` | `admin` / `admin` | Basic 认证(自动同步为 system 管理员账号) | +| `-workdir` | `./webui-frpc` | 数据目录(db / 配置 / 日志 / bin) | +| `-bin` | 空 | 初始 frpc 二进制路径(可选,首次启动写入 settings) | +| `-frpc` | 空 | worker 默认 frpc 路径(settings 无值时的回退) | +| `-peer` | 空 | 集群对端地址 `host:port`(可重复,仅首次引导加入用) | +| `-join-key` | `$W4F_JOIN_KEY` | 集群准入密钥 = 对端节点的 nodeKey(与 `-peer` 配合使用) | + +环境变量 `W4F_HOST` 可覆盖节点对外可达的主机名(Docker DNS / 通配监听场景用)。 + +### 启动行为 + +节点启动时的集群引导优先级: + +1. **缓存拓扑优先**:读取持久化的 `ClusterPeers`(宕机前最后令牌周期保存的对端列表),逐个尝试 rejoin +2. **`-peer` 引导**:无缓存拓扑时,用 `-peer` + `-join-key` 加入指定对端(10s 重试、5min 超时) +3. **新建集群**:无缓存无 `-peer` → `CreateCluster` 成为独立 leader + +显式脱离集群(detachAsStandalone)会清除 `ClusterPeers` → 重启后不自动重联。 + +## 数据目录 + +``` + +├── manager.db # SQLite(locals / remotes / links / settings / users / api_keys) +├── configs/~~.json # 每条转发的独立 frpc 配置(per-forward 模型) +├── logs/~~.log # 每条转发的独立 worker 日志(按大小轮转) +└── bin/frpc-/frpc # 一键安装的 frpc +``` + +## 架构 + +``` +webui4frpc(单二进制,无 frp 依赖,前端 go:embed) +├── cmd/webui4frpc 入口:flag 解析 → store/process/cluster 装配 → HTTP 服务 + 生命周期 +├── internal/ +│ ├── store SQLite 持久化(locals / remotes / links / settings / users / api_keys / sessions) +│ ├── canvas 画布模型(local / remote / link 多对多) +│ ├── render 渲染 frpc JSON 配置(per-forward 单 proxy + 高级参数 + LB/健康检查) +│ ├── process worker 进程管理(spawn/stop/restart/自愈/日志轮转,worker 键 = local~remote~port) +│ ├── install 一键下载官方 frpc(GitHub Releases,含解压安全防护) +│ ├── cluster 令牌环协议(ring engine / leader 选举 / 容错 / 宕机重联 / 二进制分发 / NIC 负载采样) +│ └── httpapi REST API + 静态资源(auth/session / canvas / forwards / cluster / users / audit 导出) +└── web/ Vue 3 SPA(Element Plus + VueFlow 画布 + Pinia) + └── src/views/ 状态总览 / 连接配置(画布)/ 集群 / 设置 / 账号与密钥 +``` + +## API + +所有接口前缀 `/api/manager`。UI 使用 session-cookie 登录(`/login` `/logout`),API 兼容 Basic Auth 或 Bearer API Key。权限分 read / write / admin 三级,角色 superadmin / admin / viewer 映射到不同 UI 可见性与操作能力。 + +| 类别 | 方法 | 路径 | 权限 | 说明 | +| --- | --- | --- | --- | --- | +| **状态** | GET | `/status` | read | 总览(版本/服务/节点/转发/profiles) | +| **画布** | GET/PUT | `/canvas` | read/write | 读写完整画布 | +| | GET | `/canvas/export` | read | 导出画布备份 | +| | POST | `/canvas/import` | write | 导入画布 | +| **转发** | POST | `/forwards/start\|stop` | write | 启停单条转发 | +| | POST | `/forwards/group/start\|stop` | write | 启停整组 | +| | POST | `/forwards/assign` | write | 修改转发分组 | +| | POST | `/forwards/group/delete` | write | 删除分组 | +| **单资源** | PUT/DELETE | `/locals[/{name}]` | write | 增删 local | +| | PUT/DELETE | `/remotes[/{name}]` | write | 增删 remote | +| | POST/DELETE | `/links[/{id}]` | write | 增删 link | +| **Profile** | POST | `/profiles/{name}/start\|stop\|restart` | write | worker 启停重启 | +| | GET | `/profiles/{name}/config\|logs` | read | 配置/日志 | +| **设置** | GET/PUT | `/settings` | read/write | 运行策略 | +| | GET | `/binary/status` | read | frpc 路径 | +| | POST | `/binary/install` | write | 安装 frpc | +| **集群** | GET | `/cluster/ring` | read | 令牌环快照 | +| | POST | `/cluster/create` | write | 创建独立集群 | +| | POST | `/cluster/join-ring` | write | 加入集群(本节点发起) | +| | POST | `/cluster/join` | write | 接收新节点加入 | +| | POST | `/cluster/task` | write | 提交转发任务到环 | +| | POST | `/cluster/node-remove` | write | 移除节点 | +| | POST | `/cluster/token` | write | 令牌中继 + 心跳 | +| | GET | `/cluster/nodes` | read | 集群节点 + 缓存 | +| | GET/POST | `/cluster/cache` | read/write | 二进制缓存管理 | +| | GET | `/node/logs` | read | 本节点 worker 日志 | +| | GET | `/cluster/logs/export` | read | 导出全节点 worker 日志(JSON bundle) | +| **审计导出** | GET | `/audit/users.csv` | read | 账号清单 CSV(含最后登录) | +| | GET | `/audit/apikeys.csv` | read | API Key 清单 CSV(含最后使用/过期) | +| | GET | `/audit/cluster-log.csv` | read | 集群操作日志 CSV(时间线 + 原始载荷) | +| | GET | `/audit/worker-logs.csv` | read | 全节点 worker 运行日志 CSV(逐行展开) | +| | GET | `/frpc/{version}` | read | 节点间 frpc 二进制分发 | +| **认证** | POST | `/login` | 无 | 登录(表单凭证 → session cookie) | +| | POST | `/logout` | 无 | 登出(清除 session) | +| | GET | `/me` | read | 当前身份 | +| | GET/POST | `/users` | admin | 用户列表/创建 | +| | PUT/DELETE | `/users/{name}` | admin | 修改/删除用户 | +| | GET/POST | `/apikeys` | admin | API Key 列表/创建 | +| | DELETE | `/apikeys/{id}` | admin | 吊销 API Key | + +> 集群管理 API 的详细说明(请求体、响应格式、错误码、示例)见 [docs/cluster-api.md](docs/cluster-api.md)。 + +## 集群模式 + +多个 webui4frpc 节点可组成令牌环集群协同工作: + +- **创建集群**:首个节点 `CreateCluster` 成为 leader,生成准入密钥(nodeKey) +- **加入集群**:在集群页复制对端的 nodeKey,填入「加入集群」对话框(对端地址 + 密钥) +- **任务分发**:在任意节点画布上配置 `localOnly=false` 的转发并保存 → 画布差异判断生成命令 → 令牌携带 → 负载最低节点摘取 → 拉起 frpc worker +- **容错**:节点宕机 → 自动检测 → 环自愈(跳过死亡节点 / leader failover);重启 → 自动重联 +- **退出集群**:在集群页「退出集群」→ 本节点脱离为 standalone,不自动重联 + +集群页展示:环拓扑(self/leader 标记)、令牌轮次、节点负载、待办任务、活跃转发拓扑(含 owner 归属)、增量事件日志。 + +## 测试 + +```bash +# 后端 +go test ./... + +# 前端类型检查 +cd web && npm run type-check +``` diff --git a/packaging/windows/webui4frpc.nsi b/packaging/windows/webui4frpc.nsi new file mode 100644 index 0000000..59df78c --- /dev/null +++ b/packaging/windows/webui4frpc.nsi @@ -0,0 +1,162 @@ +; webui4frpc Windows 安装程序 (NSIS) +; +; 由 scripts/build_installers.sh 调用,需在命令行传入: +; -DVERSION=0.1.0 -DARCH=amd64 -DBINSRC= -DOUTFILE=<输出 exe> + +!include "MUI2.nsh" +!include "LogicLib.nsh" + +!ifndef VERSION + !define VERSION "0.0.0" +!endif +!ifndef ARCH + !define ARCH "amd64" +!endif + +Name "webui4frpc ${VERSION}" +OutFile "${OUTFILE}" +Unicode true +RequestExecutionLevel admin +InstallDir "$PROGRAMFILES64\webui4frpc" +InstallDirRegKey HKLM "Software\webui4frpc" "InstallDir" + +VIProductVersion "${VERSION}.0" +VIAddVersionKey "ProductName" "webui4frpc" +VIAddVersionKey "FileDescription" "Visual frpc controller with token-ring cluster" +VIAddVersionKey "FileVersion" "${VERSION}" +VIAddVersionKey "ProductVersion" "${VERSION}" +VIAddVersionKey "LegalCopyright" "JianFeeeee" + +!define MUI_ABORTWARNING +!insertmacro MUI_PAGE_LICENSE "LICENSE.txt" +!insertmacro MUI_PAGE_DIRECTORY +Page custom ConfigPage ConfigPageLeave +!insertmacro MUI_PAGE_INSTFILES +!insertmacro MUI_PAGE_FINISH +!insertmacro MUI_UNPAGE_CONFIRM +!insertmacro MUI_UNPAGE_INSTFILES +!insertmacro MUI_LANGUAGE "SimpChinese" +!insertmacro MUI_LANGUAGE "English" + +Var Dialog +Var LblAddr +Var TxtAddr +Var LblUser +Var TxtUser +Var LblPass +Var TxtPass +Var CfgAddr +Var CfgUser +Var CfgPass + +Function .onInit + StrCpy $CfgAddr "0.0.0.0:7500" + StrCpy $CfgUser "admin" + StrCpy $CfgPass "admin" +FunctionEnd + +Function ConfigPage + !insertmacro MUI_HEADER_TEXT "服务配置" "设置监听地址与登录凭据" + nsDialogs::Create 1018 + Pop $Dialog + ${If} $Dialog == error + Abort + ${EndIf} + + ${NSD_CreateLabel} 0 0 100% 12u "监听地址 (host:port):" + Pop $LblAddr + ${NSD_CreateText} 0 14u 100% 12u "$CfgAddr" + Pop $TxtAddr + + ${NSD_CreateLabel} 0 34u 100% 12u "管理员用户名:" + Pop $LblUser + ${NSD_CreateText} 0 48u 100% 12u "$CfgUser" + Pop $TxtUser + + ${NSD_CreateLabel} 0 68u 100% 12u "管理员密码(请勿沿用默认值):" + Pop $LblPass + ${NSD_CreatePassword} 0 82u 100% 12u "$CfgPass" + Pop $TxtPass + + nsDialogs::Show +FunctionEnd + +Function ConfigPageLeave + ${NSD_GetText} $TxtAddr $CfgAddr + ${NSD_GetText} $TxtUser $CfgUser + ${NSD_GetText} $TxtPass $CfgPass +FunctionEnd + +Section "webui4frpc" SecMain + SetOutPath "$INSTDIR" + File "/oname=webui4frpc.exe" "${BINSRC}" + File "README.md" + File "LICENSE.txt" + + CreateDirectory "$INSTDIR\data" + + ; 启动脚本,承载安装时选择的配置 + FileOpen $0 "$INSTDIR\start-webui4frpc.cmd" w + FileWrite $0 "@echo off$\r$\n" + FileWrite $0 "rem webui4frpc 启动脚本(由安装程序生成)$\r$\n" + FileWrite $0 "cd /d %~dp0$\r$\n" + FileWrite $0 'webui4frpc.exe -addr $CfgAddr -user $CfgUser -password $CfgPass -workdir "%~dp0data"$\r$\n' + FileClose $0 + + WriteRegStr HKLM "Software\webui4frpc" "InstallDir" "$INSTDIR" + WriteRegStr HKLM "Software\webui4frpc" "Version" "${VERSION}" + + ; 添加/删除程序 + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\webui4frpc" \ + "DisplayName" "webui4frpc ${VERSION}" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\webui4frpc" \ + "UninstallString" "$\"$INSTDIR\uninstall.exe$\"" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\webui4frpc" \ + "DisplayVersion" "${VERSION}" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\webui4frpc" \ + "Publisher" "JianFeeeee" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\webui4frpc" \ + "URLInfoAbout" "https://gitcode.com/JianFeeeee/webui4frpc" + WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\webui4frpc" \ + "NoModify" 1 + WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\webui4frpc" \ + "NoRepair" 1 + + WriteUninstaller "$INSTDIR\uninstall.exe" + + ; 开始菜单 + CreateDirectory "$SMPROGRAMS\webui4frpc" + CreateShortcut "$SMPROGRAMS\webui4frpc\webui4frpc.lnk" \ + "$INSTDIR\start-webui4frpc.cmd" "" "$INSTDIR\webui4frpc.exe" 0 + CreateShortcut "$SMPROGRAMS\webui4frpc\打开管理界面.lnk" \ + "http://127.0.0.1:7500" + CreateShortcut "$SMPROGRAMS\webui4frpc\卸载 webui4frpc.lnk" "$INSTDIR\uninstall.exe" + + ; 防火墙放行(失败不阻断安装) + nsExec::ExecToLog 'netsh advfirewall firewall add rule name="webui4frpc" \ + dir=in action=allow program="$INSTDIR\webui4frpc.exe" enable=yes profile=any' +SectionEnd + +Section "Uninstall" + nsExec::ExecToLog 'netsh advfirewall firewall delete rule name="webui4frpc"' + + Delete "$INSTDIR\webui4frpc.exe" + Delete "$INSTDIR\start-webui4frpc.cmd" + Delete "$INSTDIR\README.md" + Delete "$INSTDIR\LICENSE.txt" + Delete "$INSTDIR\uninstall.exe" + + ; data 目录保留(含数据库与日志),由用户手动删除 + MessageBox MB_YESNO|MB_ICONQUESTION \ + "是否同时删除数据目录 $INSTDIR\data(数据库、转发配置、日志)?" \ + IDNO KeepData + RMDir /r "$INSTDIR\data" +KeepData: + RMDir "$INSTDIR" + + Delete "$SMPROGRAMS\webui4frpc\*.lnk" + RMDir "$SMPROGRAMS\webui4frpc" + + DeleteRegKey HKLM "Software\webui4frpc" + DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\webui4frpc" +SectionEnd diff --git a/scripts/build_installers.sh b/scripts/build_installers.sh new file mode 100755 index 0000000..733420e --- /dev/null +++ b/scripts/build_installers.sh @@ -0,0 +1,131 @@ +#!/usr/bin/env bash +# +# 从 release.sh 的交叉编译产物构建原生安装包: +# - linux amd64/arm64 → .deb +# - linux amd64/arm64 → .rpm +# - windows amd64/arm64 → NSIS .exe 安装器 +# - darwin amd64/arm64 → macOS .pkg +# +# 用法: build_installers.sh [arch...] +# arch 默认: linux amd64 arm64 +# +# 在 release.sh 流程内被调用;依赖 dist/artifacts//bin/-/ 下的二进制。 +set -euo pipefail + +VERSION="${1:-0.1.0}" +VERSION="${VERSION#v}" +ROOT="$(cd "$(dirname "$0")/.." && pwd)" +OUT="$ROOT/dist/artifacts/$VERSION" +PKG="$ROOT/packaging" + +# 工具检测(缺工具则给出明确提示后跳过该平台) +have() { command -v "$1" >/dev/null 2>&1; } + +build_deb() { + local arch="$1" key="$2" bindir + bindir="$OUT/bin/linux-$arch" + [ -x "$bindir/webui4frpc" ] || { echo " ! 缺二进制 linux-$arch,跳过 deb"; return; } + local deb=/tmp/w4f-deb-build; rm -rf "$deb" + mkdir -p "$deb/DEBIAN" "$deb/usr/bin" "$deb/etc/webui4frpc" \ + "$deb/lib/systemd/system" "$deb/usr/share/doc/webui4frpc" + # control + cat > "$deb/DEBIAN/control" < +Description: visual frpc controller with token-ring cluster + A web UI to manage FRP forwards across a cluster of nodes. Single + statically-linked binary with an embedded Vue frontend. Includes a + token-ring based cluster for distributed forward management, NIC network + load sampling, and audit-log CSV export. +Homepage: https://gitcode.com/JianFeeeee/webui4frpc +EOF + install -m 0755 "$bindir/webui4frpc" "$deb/usr/bin/webui4frpc" + install -m 0644 "$PKG/common/webui4frpc.service" "$deb/lib/systemd/system/webui4frpc.service" + install -m 0640 "$PKG/common/webui4frpc.env" "$deb/etc/webui4frpc/webui4frpc.env" + install -m 0644 "$ROOT/README.md" "$deb/usr/share/doc/webui4frpc/README.md" + cat > "$deb/usr/share/doc/webui4frpc/copyright" <<'EOF' +webui4frpc - visual frpc controller +Copyright (C) 2026 JianFeeeee +SPDX-License-Identifier: MIT +EOF + install -m 0755 "$PKG/deb/postinst" "$deb/DEBIAN/postinst" + install -m 0755 "$PKG/deb/prerm" "$deb/DEBIAN/prerm" + install -m 0755 "$PKG/deb/postrm" "$deb/DEBIAN/postrm" + echo "/etc/webui4frpc/webui4frpc.env" > "$deb/DEBIAN/conffiles" + ( cd "$deb" && find usr etc lib -type f -exec md5sum {} \; > DEBIAN/md5sums ) + dpkg-deb -b --root-owner-group "$deb" "$OUT/webui4frpc-${VERSION}-linux-${arch}.deb" >/dev/null + echo " ✔ .deb linux/$arch" +} + +build_rpm() { + local arch="$1" rarch key="$2" bindir + case "$arch" in amd64) rarch=x86_64;; arm64) rarch=aarch64;; *) return;; esac + bindir="$OUT/bin/linux-$arch" + [ -x "$bindir/webui4frpc" ] || { echo " ! 缺二进制 linux-$arch,跳过 rpm"; return; } + local R=/tmp/w4f-rpmbuild; rm -rf "$R"; mkdir -p "$R"/{BUILD,RPMS,SPECS,SOURCES} + rpmbuild -bb "$PKG/rpm/webui4frpc.spec" --nodeps --target "$rarch" \ + --define "_topdir $R" \ + --define "_w4f_version $VERSION" \ + --define "_w4f_arch $rarch" \ + --define "_w4f_bin $bindir/webui4frpc" \ + --define "_w4f_service $PKG/common/webui4frpc.service" \ + --define "_w4f_env $PKG/common/webui4frpc.env" \ + --define "_w4f_readme $ROOT/README.md" \ + --define "_unitdir /usr/lib/systemd/system" \ + --define "_sharedstatedir /var/lib" >/tmp/w4f-rpm.log 2>&1 || true + cp "$R"/RPMS/$rarch/*.rpm "$OUT/webui4frpc-${VERSION}-linux-${rarch}.rpm" 2>/dev/null \ + && echo " ✔ .rpm linux/$rarch" || { echo " ! rpm 失败: $(tail -3 /tmp/w4f-rpm.log)"; } +} + +build_nsis() { + local arch="$1" key="$2" bindir + bindir="$OUT/bin/windows-$arch" + [ -f "$bindir/webui4frpc.exe" ] || { echo " ! 缺二进制 windows-$arch,跳过 NSIS"; return; } + [ -f "$PKG/windows/LICENSE.txt" ] || { echo " ! 缺 $PKG/windows/LICENSE.txt"; return; } + cp -f "$ROOT/README.md" "$PKG/windows/README.md" + makensis "-DVERSION=$VERSION" "-DARCH=$arch" \ + "-DBINSRC=$bindir/webui4frpc.exe" \ + "-DOUTFILE=$OUT/webui4frpc-${VERSION}-windows-${arch}-setup.exe" \ + "$PKG/windows/webui4frpc.nsi" >/tmp/w4f-nsis.log 2>&1 \ + && echo " ✔ NSIS setup.exe windows/$arch" \ + || { echo " ! NSIS 失败: $(tail -4 /tmp/w4f-nsis.log)"; } +} + +build_pkg() { + local arch="$1" key="$2" bindir + bindir="$OUT/bin/darwin-$arch" + [ -x "$bindir/webui4frpc" ] || { echo " ! 缺二进制 darwin-$arch,跳过 pkg"; return; } + "$PKG/macos/build_pkg.sh" "$VERSION" "$arch" \ + "$bindir/webui4frpc" \ + "$OUT/webui4frpc-${VERSION}-darwin-${arch}.pkg" >/dev/null \ + && echo " ✔ .pkg darwin/$arch" \ + || echo " ! macOS pkg 失败 darwin/$arch" +} + +ARCHES=("${@:2}") +[ ${#ARCHES[@]} -eq 0 ] && ARCHES=(amd64 arm64) + +echo "==> 原生安装包 (v$VERSION):" +for arch in "${ARCHES[@]}"; do + key="linux-$arch" + if have dpkg-deb; then build_deb "$arch" "$key"; else echo " . dpkg-deb 不可用,跳过 deb"; fi + if have rpmbuild; then build_rpm "$arch" "$key"; else echo " . rpmbuild 不可用,跳过 rpm"; fi +done +if have makensis; then + for arch in "${ARCHES[@]}"; do build_nsis "$arch" "windows-$arch"; done +else + echo " . makensis 不可用,跳过 windows setup.exe" +fi +if have xar && have mkbom && have cpio; then + for arch in "${ARCHES[@]}"; do build_pkg "$arch" "darwin-$arch"; done +else + echo " . xar/mkbom 不可用,跳过 macOS pkg" +fi + +echo "" +echo "==> 安装包产物:" +ls -lh "$OUT"/*.deb "$OUT"/*.rpm "$OUT"/*-setup.exe "$OUT"/*.pkg 2>/dev/null || true \ No newline at end of file diff --git a/scripts/release.sh b/scripts/release.sh index 77db1d5..8de8445 100755 --- a/scripts/release.sh +++ b/scripts/release.sh @@ -129,6 +129,13 @@ rm -rf "$OUT/.stage" echo "==> 生成 SHA256SUMS" ( cd "$OUT" && sha256sum *.tar.gz *.zip 2>/dev/null | tee SHA256SUMS ) +# ---- 4.5 原生安装包 (deb/rpm/setup.exe/pkg) ---- +if [ "${NO_INSTALLERS:-0}" != "1" ]; then + echo "" +echo "==> 构建原生安装包" + "$ROOT/scripts/build_installers.sh" "$VERSION" +fi + # ---- 5. 汇总 ---- echo "" echo "==> 发布产物位于: $OUT"