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

+107
View File
@@ -323,3 +323,110 @@ func SaveTunnelConfig(t *model.Tunnel, public, secrets map[string]interface{}) e
}
return store.DB.Save(t).Error
}
// CloneConfig 深拷贝配置 map(避免修改原始数据)
func CloneConfig(in map[string]interface{}) map[string]interface{} {
out := make(map[string]interface{}, len(in))
for k, v := range in {
out[k] = v
}
return out
}
// SwanctlUnload 卸载 strongSwan 连接与凭据
func SwanctlUnload(bin, confPath string) {
_, _ = Run(bin, "--unload-conns", "--file", confPath)
_, _ = Run(bin, "--unload-creds", "--file", confPath)
}
// DialUser 拨号用户凭证
type DialUser struct {
Username string
Password string
}
// LoadDialUsers 从数据库加载指定隧道的本地拨号用户
func LoadDialUsers(tunnelID uint) ([]DialUser, error) {
if store.DB == nil {
return nil, nil
}
var users []model.VPNUser
if err := store.DB.Where("tunnel_id = ? AND enabled = ? AND source = ?", tunnelID, true, "local").Find(&users).Error; err != nil {
return nil, err
}
out := make([]DialUser, 0, len(users))
for _, u := range users {
if u.DialSecret == "" {
continue
}
pw, err := store.DecryptSecret(u.DialSecret)
if err != nil || pw == "" {
continue
}
out = append(out, DialUser{Username: u.Username, Password: pw})
}
return out, nil
}
// FirewallRule 描述一条 iptables 规则
type FirewallRule struct {
Table string // "filter" or "nat"
Chain string // "INPUT", "POSTROUTING", etc.
Args []string
}
// ApplyFirewallRules 检查并添加 iptables 规则(已存在则跳过)
func ApplyFirewallRules(rules []FirewallRule) error {
iptables, ok := LookBin("iptables")
if !ok {
return fmt.Errorf("未检测到 iptables")
}
for _, rule := range rules {
prefix := []string{}
if rule.Table != "filter" {
prefix = append(prefix, "-t", rule.Table)
}
check := append(append([]string{}, prefix...), "-C", rule.Chain)
check = append(check, rule.Args...)
if _, err := Run(iptables, check...); err == nil {
continue
}
add := append(append([]string{}, prefix...), "-A", rule.Chain)
add = append(add, rule.Args...)
if _, err := Run(iptables, add...); err != nil {
RemoveFirewallRules(rules)
return fmt.Errorf("无法添加防火墙规则: %w", err)
}
}
return nil
}
// DefaultDNS 返回默认 DNS 服务器地址(可通过 VPN_DNS_SERVER 环境变量覆盖)
func DefaultDNS() string {
if v := os.Getenv("VPN_DNS_SERVER"); v != "" {
return v
}
return "223.5.5.5"
}
// RemoveFirewallRules 检查并删除 iptables 规则
func RemoveFirewallRules(rules []FirewallRule) {
iptables, ok := LookBin("iptables")
if !ok {
return
}
for _, rule := range rules {
prefix := []string{}
if rule.Table != "filter" {
prefix = append(prefix, "-t", rule.Table)
}
check := append(append([]string{}, prefix...), "-C", rule.Chain)
check = append(check, rule.Args...)
if _, err := Run(iptables, check...); err != nil {
continue
}
del := append(append([]string{}, prefix...), "-D", rule.Chain)
del = append(del, rule.Args...)
_, _ = Run(iptables, del...)
}
}