feat: 后端限速实现 + 前端i18n全面国际化 + 安全与代码质量修复

后端:
- trafficmgr: 实现 tc htb 限速(ApplyRateLimit/RemoveRateLimit),自动推导接口名
- 各协议服务: 代码质量改进与错误处理增强

前端:
- protocols.js: 170个硬编码中文字符串全部转为 i18n key,中英文双语翻译
- 提取 fmtBytes() 到 utils/format.js 公共模块(消除 4 处重复)
- router.js: 添加路由级权限守卫,非管理员无法直接 URL 访问管理页
- Tunnels.vue: SSE 日志流改用 fetch+ReadableStream,token 放 Authorization header
- VPNUsers.vue: 修复 catch 块中文硬编码 fallback
- 新增 TaskProgress 组件、taskmgr 后端任务管理
This commit is contained in:
opsx-tools committed 2026-07-23 12:57:46 +08:00
1 parent 021d012478
commit 91974fc30f
26 files changed
+1977 -377

No files matched your search

+50 -7
View File
@@ -4,7 +4,9 @@ package trafficmgr
import (
"fmt"
"log"
"os/exec"
"strconv"
"strings"
"time"
@@ -106,25 +108,66 @@ func HandleExceed(tunnelID uint, action string, tunnelName string) {
}
}
// ApplyRateLimit 使用 tc 设置隧道接口限速(需 root + tc 命令)
// tunnelIface returns the network interface name for a tunnel, derived from
// its protocol and ID. Returns "" if the protocol does not use a tc-able interface.
func tunnelIface(tunnelID uint) string {
var t model.Tunnel
if store.DB == nil || store.DB.First(&t, tunnelID).Error != nil {
return ""
}
switch t.Protocol {
case "wireguard":
return "wg" + strconv.FormatUint(uint64(t.ID), 36)
case "openvpn":
return "tun0"
default:
return ""
}
}
// ApplyRateLimit 使用 tc htb 对隧道网络接口限速(需 root + tc 命令)
func ApplyRateLimit(tunnelID uint, mbps int) {
if mbps <= 0 {
RemoveRateLimit(tunnelID)
return
}
// tc 规则基于网络接口名,这里仅做记录;实际接口名需从隧道配置中获取
// 完整实现需在 tunnel start 时获取 iface name 并传入
if _, err := exec.LookPath("tc"); err != nil {
tc, err := exec.LookPath("tc")
if err != nil {
return // tc 不存在则跳过
}
// 限速规则模板(需要接口名,此处占位)
iface := tunnelIface(tunnelID)
if iface == "" {
return // 协议不支持 tc 限速
}
// 先清除旧规则再重建
RemoveRateLimit(tunnelID)
// tc qdisc add dev <iface> root htb default 10
if out, err := exec.Command(tc, "qdisc", "add", "dev", iface, "root", "htb", "default", "10").CombinedOutput(); err != nil {
log.Printf("[trafficmgr] tc qdisc add 失败 (iface=%s): %v: %s", iface, err, string(out))
return
}
// tc class add dev <iface> parent root classid 1:10 htb rate <mbps>mbit
if out, err := exec.Command(tc, "class", "add", "dev", iface, "parent", "root", "classid", "1:10", "htb", "rate", fmt.Sprintf("%dmbit", mbps)).CombinedOutput(); err != nil {
log.Printf("[trafficmgr] tc class add 失败 (iface=%s): %v: %s", iface, err, string(out))
return
}
log.Printf("[trafficmgr] 已对 %s 限速至 %d Mbps", iface, mbps)
}
// RemoveRateLimit 清除限速规则
// RemoveRateLimit 清除隧道接口上的 tc 限速规则
func RemoveRateLimit(tunnelID uint) {
if _, err := exec.LookPath("tc"); err != nil {
tc, err := exec.LookPath("tc")
if err != nil {
return
}
iface := tunnelIface(tunnelID)
if iface == "" {
return
}
if out, err := exec.Command(tc, "qdisc", "del", "dev", iface, "root").CombinedOutput(); err != nil {
// qdisc del 在无规则时返回非零,不算真正错误
_ = out
}
}
// GetPolicy 获取隧道的流量策略(不存在则返回默认空策略)