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

+7 -38
View File
@@ -30,10 +30,7 @@ func (s *Service) Available() (bool, string) {
}
func (s *Service) Validate(cfg map[string]interface{}) (map[string]interface{}, error) {
out := map[string]interface{}{}
for k, v := range cfg {
out[k] = v
}
out := service.CloneConfig(cfg)
if service.Str(out, "address_pool") == "" {
out["address_pool"] = "10.12.0.0/24"
}
@@ -41,7 +38,7 @@ func (s *Service) Validate(cfg map[string]interface{}) (map[string]interface{},
return nil, fmt.Errorf("address_pool 必须是 CIDR,例如 10.12.0.0/24")
}
if service.Str(out, "dns") == "" {
out["dns"] = "223.5.5.5"
out["dns"] = service.DefaultDNS()
}
if service.Str(out, "local_id") == "" {
out["local_id"] = "@opsx-ikev2"
@@ -85,7 +82,7 @@ func (s *Service) Create(t *model.Tunnel) error {
secrets["password"] = pw
}
username := service.StrOr(cfg, "username", "vpnuser")
dialUsers, err := loadDialUsers(t.ID)
dialUsers, err := service.LoadDialUsers(t.ID)
if err != nil {
return err
}
@@ -115,7 +112,7 @@ func (s *Service) Create(t *model.Tunnel) error {
// RefreshAuth rewrites EAP secrets after dial-user changes.
func (s *Service) RefreshAuth(t *model.Tunnel) error { return s.Create(t) }
func (s *Service) swanctlConf(id uint, cfg map[string]interface{}, psk, defaultUser, defaultPass string, users []dialUser) string {
func (s *Service) swanctlConf(id uint, cfg map[string]interface{}, psk, defaultUser, defaultPass string, users []service.DialUser) string {
var secrets strings.Builder
fmt.Fprintf(&secrets, `secrets {
ike-rw-%d {
@@ -186,7 +183,7 @@ pools {
service.Str(cfg, "esp_proposal"),
id,
service.Str(cfg, "address_pool"),
service.StrOr(cfg, "dns", "223.5.5.5"),
service.StrOr(cfg, "dns", service.DefaultDNS()),
secrets.String(),
)
}
@@ -221,7 +218,7 @@ DNS : %s
username,
service.Str(secrets, "password"),
service.Str(cfg, "address_pool"),
service.StrOr(cfg, "dns", "223.5.5.5"),
service.StrOr(cfg, "dns", service.DefaultDNS()),
)
}
@@ -311,38 +308,10 @@ func (s *Service) Status(t *model.Tunnel) (core.Status, error) {
func (s *Service) Delete(t *model.Tunnel) error { return s.Stop(t) }
func (s *Service) unload(bin, confPath string) error {
_, _ = service.Run(bin, "--unload-conns", "--file", confPath)
_, _ = service.Run(bin, "--unload-creds", "--file", confPath)
service.SwanctlUnload(bin, confPath)
return nil
}
type dialUser struct {
Username string
Password string
}
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
}
func (s *Service) installFirewall(t *model.Tunnel, cfg map[string]interface{}) error {
iptables, ok := service.LookBin("iptables")
if !ok {