kevin 50e1661c1b feat: 实现每用户流量统计与定时落库
流量统计从全站按日聚合升级为 per-user 维度,并引入 60 秒定时
落库机制,服务崩溃最多丢失 60 秒数据。

后端:
- model/vpn.go: 新增 UserTrafficStat 模型(user_id + date 联合唯一索引)
- db/db.go: AutoMigrate 注册新模型
- vpn/tunnel.go: tunnelConn 增加 flushedRx/flushedTx 快照字段 +
  flushDelta() 增量计算;recordTraffic 改为 per-user 双写
  (user_traffic_stats + traffic_stats);连接断开 defer 改增量落库
- vpn/service.go: 新增 flushDone 字段 + trafficFlusher(60s 定时
  落库)+ flushAllTraffic;Stop() 先停 flusher 再最终 flush 全部
  在线连接增量;TotalLiveTraffic 改返回未落库增量避免与 DB 重复;
  新增 UserLiveTraffic(userID);ClientInfo 增加 RxBytes/TxBytes
- handler/traffic.go: 新建 5 个 handler(admin 3 个 + user 2 个)
- router.go: 注册 5 条新路由

新增 API:
- GET /api/admin/traffic/today  所有用户今日流量排行
- GET /api/admin/traffic/history?days=N  全站近 N 天流量历史
- GET /api/admin/traffic/users/:id?days=N  指定用户近 N 天流量
- GET /api/me/traffic/today  自己的今日流量
- GET /api/me/traffic?days=N  自己的近 N 天流量

前端:
- 安装 chart.js + vue-chartjs
- 新建 TrafficChart.vue 可复用柱状图组件(上行/下行双柱)
- AdminView.vue: 在线客户端表格增加 RX/TX 列 + 全站 7 天流量
  图表 + 用户今日流量排行表
- ProfileView.vue: 新增流量统计卡片(今日上行/下行/合计)+ 7 天
  流量柱状图
- zh.ts/en.ts: 新增 traffic 相关国际化
2026-07-10 14:18:06 +08:00
2026-07-02 20:45:53 +08:00
2026-07-02 20:45:53 +08:00
2026-07-09 14:53:12 +08:00
2026-07-09 14:53:12 +08:00

LMVPN

基于 WebSocket 隧道与 TUN 虚拟网卡的轻量级三层 VPN 系统,采用 Go + Vue 3 构建。服务端通过 WebSocket 与客户端建立控制通道,TUN 网卡与 WebSocket 之间双向搬运原始 IP 数据包,实现点对点与点对站点网络连接。

平台说明:服务端部署目前仅在 Linux 下测试功能正常systemd + nftables/iptables NAT,自动兼容 UFW)。配套客户端见 lmvpn_client,客户端协议规范见 docs/client-development.md


目录


环境要求

  • 操作系统:Linux(已测试),需 root 或 sudo 权限
  • Go:≥ 1.26.4
  • Node.js:≥ 22.18(用于构建前端)
  • 内核:支持 TUN 设备(/dev/net/tun),开启 ip_forward
  • 防火墙工具nftables(推荐)或 iptables,兼容 UFW
  • 网络:公网 IP,开放 Web 端口(或经反向代理)

一键部署(Linux,推荐)

仓库自带 install_linux.sh,一条命令完成构建、部署、内核转发配置与 systemd 服务安装。NAT / 转发 / UFW 规则由服务端程序在启动时根据后台子网配置自动管理,无需手动设置。

git clone <仓库地址> lmvpn_server
cd lmvpn_server
sudo bash install_linux.sh

脚本执行流程

  1. 拉取最新代码(git fetch origin && git reset --hard origin/main
  2. 构建前端:cd frontend && npm install && npm run build(产物输出到 ../dist
  3. 编译后端:go build -o lmvpn .
  4. 创建无 shell 的系统用户 lmvpn
  5. 停止旧服务,部署到 /opt/lmvpn(二进制 + dist/),属主改为 lmvpn
  6. 开启内核 IP 转发(IPv4 / IPv6),写入 /etc/sysctl.d/99-lmvpn.conf
  7. 安装 systemd 服务 /etc/systemd/system/lmvpn.service,以 lmvpn 用户运行并授予 CAP_NET_ADMIN / CAP_NET_RAW
  8. 启动服务(NAT / 转发 / UFW 规则由程序在启动时根据后台子网自动配置)

⚠️ 脚本中的 git reset --hard origin/main丢弃所有本地未提交改动。部署前请确保工作区干净,或先提交/暂存。

防火墙规则自动管理

NAT masquerade、forward 放行、UFW 转发规则由服务端程序在 ApplySettings() 时根据当前后台 VPN 子网动态配置:

  • 自动检测出口网卡(ip route show default
  • 配置 nft lmvpn_nat 表的 postrouting masquerade 和 forward accept 规则
  • 检测 UFW 是否启用(存在 ufw-user-forward 链),若启用则自动创建 lmvpn-fwd / lmvpn6-fwd 链并注入 jump
  • 在后台修改子网后保存即可,程序自动更新规则,无需重新执行脚本

首次登录与初始化

  1. 获取初始密码:首次启动时自动创建管理员 admin,密码为随机 16 位字符串,会

    • 打印到 stdout(仅一次)
    • 写入 /opt/lmvpn/data/.initial_admin_password(权限 0600
    sudo cat /opt/lmvpn/data/.initial_admin_password
    
  2. 登录:浏览器访问 http://<服务器IP>:8080,使用 admin + 初始密码登录。

  3. 立即修改密码,并删除初始密码文件:

    sudo rm /opt/lmvpn/data/.initial_admin_password
    
  4. 启用 VPN:进入「管理后台 → VPN 管理」,确认子网(默认 192.168.77.0/24 + IPv6 fd00:dead:beef::/112),打开「启用」并保存。

  5. 诊断检查:VPN 管理页的「系统环境检测」面板(对应 GET /api/admin/vpn/diag)会检测 ip_forward、NAT、UFW 转发规则、TUN 等,客户端无法上网时优先查看此处。


手动部署

如需自行控制部署细节,可参照以下步骤。

1. 构建产物

# 前端
cd frontend
npm install
npm run build        # 产物输出到 ../dist
cd ..

# 后端
go build -o lmvpn .

2. 部署文件

sudo useradd -r -s /bin/false lmvpn
sudo mkdir -p /opt/lmvpn
sudo cp lmvpn /opt/lmvpn/
sudo cp -r dist /opt/lmvpn/
sudo chown -R lmvpn:lmvpn /opt/lmvpn

3. 开启内核转发

sudo sysctl -w net.ipv4.ip_forward=1
sudo sysctl -w net.ipv6.conf.all.forwarding=1   # IPv6 双栈时需要

cat <<'EOF' | sudo tee /etc/sysctl.d/99-lmvpn.conf
net.ipv4.ip_forward = 1
net.ipv6.conf.all.forwarding = 1
EOF
sudo sysctl -p /etc/sysctl.d/99-lmvpn.conf

4. 安装 systemd 服务

NAT / 转发 / UFW 规则由服务端程序在启动时自动配置,无需手动设置。以下命令仅在程序无法自动配置防火墙时作为参考。

手动配置 NAT(点击展开,通常不需要)

WAN_IFACE 替换为出口网卡,VPN_SUBNET 替换为 VPN 子网:

WAN_IFACE=eth0
VPN_SUBNET=192.168.77.0/24
VPN_SUBNET6=fd00:dead:beef::/112

sudo nft add table inet lmvpn_nat
sudo nft 'add chain inet lmvpn_nat postrouting { type nat hook postrouting priority 100 ; }'
sudo nft add rule inet lmvpn_nat postrouting oifname "$WAN_IFACE" ip saddr "$VPN_SUBNET" masquerade
sudo nft add rule inet lmvpn_nat postrouting oifname "$WAN_IFACE" ip6 saddr "$VPN_SUBNET6" masquerade
sudo nft 'add chain inet lmvpn_nat forward { type filter hook forward priority 0 ; policy accept ; }'
sudo nft add rule inet lmvpn_nat forward ip saddr "$VPN_SUBNET" accept
sudo nft add rule inet lmvpn_nat forward ip daddr "$VPN_SUBNET" accept

若启用了 UFW,还需放行 VPN 子网转发:

sudo ufw route allow from "$VPN_SUBNET"
sudo ufw route allow to "$VPN_SUBNET"
sudo ufw route allow from "$VPN_SUBNET6"
sudo ufw route allow to "$VPN_SUBNET6"

5. 安装 systemd 服务

sudo tee /etc/systemd/system/lmvpn.service >/dev/null <<'EOF'
[Unit]
Description=LMVPN Server
After=network.target

[Service]
Type=simple
User=lmvpn
WorkingDirectory=/opt/lmvpn
ExecStart=/opt/lmvpn/lmvpn
Restart=on-failure
RestartSec=5
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_RAW
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_RAW

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable --now lmvpn

配置说明

配置文件位于 data/config.yml(相对工作目录,即 /opt/lmvpn/data/config.yml),首次启动时自动生成。

web:
  port: 8080                      # TCP 监听端口,0 则不监听 TCP
  sock: "/run/lmvpnweb.sock"      # Unix Socket 路径,空则不监听 socket
  sock_mode: "0666"               # socket 文件权限
  sock_group: ""                  # socket 文件属组,空=不修改
  sock_dir_mode: "0755"           # socket 目录权限
  jwt_secret: ""                  # JWT 密钥,留空则按下面规则生成
database:
  type: sqlite                    # sqlite 或 mysql
  path: data/lmvpn.db             # sqlite 文件路径
  dsn: ""                         # mysql DSNtype=mysql 时必填)

web.portweb.sock 至少配置一个,两者都配则同时监听。生产环境建议仅留一个,由反向代理统一接入。

JWT 密钥

按以下优先级加载:

  1. 环境变量 LMVPN_JWT_SECRET
  2. 配置文件 web.jwt_secret
  3. 首次启动自动生成 32 字节随机密钥并写入配置文件

生产环境建议通过环境变量注入,避免密钥落盘:

# /etc/systemd/system/lmvpn.service 的 [Service] 段追加
Environment=LMVPN_JWT_SECRET=<你的密钥>

Unix Socket 权限收紧

多租户/高安全场景建议收紧 socket 权限(将 lmvpn 进程用户加入反代用户组):

web:
  sock: "/run/lmvpnweb.sock"
  sock_mode: "0660"
  sock_group: "caddy"
  sock_dir_mode: "0750"

若服务以非 root 用户运行且 socket 目录不可写,可将 sock 指向用户可写目录(如 /opt/lmvpn/run/lmvpnweb.sock),或设 sock: "" 仅用 TCP。


反向代理(HTTPS/WSS

生产环境必须通过反向代理提供 HTTPS/WSS。服务端默认监听 HTTP :8080,由反代终结 TLS。

Caddy(自动 HTTPS

lmvpn.example.com {
    reverse_proxy 127.0.0.1:8080
}

Caddy 的 reverse_proxy 自动处理 WebSocket 升级,无需额外配置。

若使用 Unix Socket 接入:

lmvpn.example.com {
    reverse_proxy unix//run/lmvpnweb.sock
}

Nginx

server {
    listen 443 ssl http2;
    server_name lmvpn.example.com;

    ssl_certificate     /etc/ssl/certs/lmvpn.pem;
    ssl_certificate_key /etc/ssl/private/lmvpn.key;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host              $host;
        proxy_set_header X-Real-IP         $remote_addr;
        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    location /ws {
        proxy_pass http://127.0.0.1:8080;
        proxy_http_version 1.1;
        proxy_set_header Upgrade           $http_upgrade;
        proxy_set_header Connection        "upgrade";
        proxy_set_header Host              $host;
        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;
    }
}

部署反代后,客户端应使用 wss://lmvpn.example.com/ws 连接。/ws 的长连接超时建议调大(如 3600s),避免空闲断连。


服务管理

sudo systemctl start lmvpn       # 启动
sudo systemctl stop lmvpn        # 停止
sudo systemctl restart lmvpn     # 重启
sudo systemctl status lmvpn      # 状态
sudo systemctl enable lmvpn      # 开机自启

查看实时日志:

sudo journalctl -u lmvpn -f

升级

最简方式:重新执行一键脚本,它会自动拉取最新代码、重新构建并重启服务。

cd lmvpn_server
git pull
sudo bash install_linux.sh

脚本会 git reset --hard origin/main,本地改动会被覆盖。/opt/lmvpn/data/ 下的配置与数据库不受影响。


目录说明

  • data/ — 运行时数据(config.ymllmvpn.db、初始密码文件),gitignore
  • dist/ — 前端构建产物,由 Go 服务端托管,gitignore
  • frontend/ — 前端工程(Vue 3 + TypeScript + Vite
  • internal/ — Go 后端源码
    • config/ — 配置加载
    • db/ — 数据库初始化
    • handler/ — HTTP 请求处理
    • middleware/ — 中间件(认证、限流)
    • model/ — 数据模型
    • router/ — 路由
    • vpn/ - VPN 核心(认证、隧道、TUN、包转发、防火墙自动配置)
  • docs/ — 文档
  • pytest/ — Python 测试脚本
  • install_linux.sh — Linux 一键部署脚本
  • main.go — 服务端入口

常见问题排查

现象 可能原因 排查建议
服务启动失败 socket 目录不可写 / 端口占用 journalctl -u lmvpn;设 sock: "" 或换可写目录
客户端连上但无法上网 未开 ip_forward / NAT 未生效 / UFW 拦截 管理后台诊断面板查看 UFW 状态和 NAT 检测结果
客户端连上但下行极慢(几十 bps UFW FORWARD 默认 DROP 拦截 TCP 包 诊断面板检查 UFW 转发规则;重启服务使程序自动配置 UFW
登录提示「请求过于频繁」 触发限流(/api/login 5 次/分钟·IP 等待 1 分钟后重试
修改子网后客户端异常 旧防火墙规则残留 后台重新保存设置,程序自动更新 NAT / UFW 规则
忘记管理员密码 删除 data/lmvpn.db 重新初始化(会丢失所有数据),或直接改库

相关文档


安全提示

  • 配置文件 data/config.yml 权限为 0600,仅限运行用户读写
  • /api/login 与 WebSocket 密码认证均限制每 IP 5 次/分钟
  • WebSocket /ws 支持 JWT?token=xxx)与用户名/密码两种认证
  • 生产环境务必经反向代理启用 HTTPS/WSS

许可证

本项目基于 MIT License 开源。

Copyright (c) 2026 wuwenfengmi1998

S
Description
No description provided
Readme MIT
699 KiB
Languages
Go 46.2%
Vue 40.2%
TypeScript 9.5%
Python 2.8%
Shell 1.1%
Other 0.2%