From 4915d6b18dabaace15a7557682f93bf7607a2ecf Mon Sep 17 00:00:00 2001 From: MHSanaei Date: Wed, 17 Jun 2026 17:24:16 +0200 Subject: [PATCH] refactor(frontend): move form-item hints from extra to tooltip Switch reality target, node options, and WARP auto-update-IP hints from inline extra text to label tooltips for a cleaner form layout. --- .../pages/inbounds/form/security/reality.tsx | 2 +- frontend/src/pages/nodes/NodeFormModal.tsx | 14 +- .../src/pages/xray/overrides/WarpModal.tsx | 311 +++++++++--------- internal/database/migrate_data.go | 2 +- internal/logger/logger_test.go | 2 +- internal/mtproto/manager.go | 8 +- internal/sub/clash_external.go | 4 +- internal/sub/controller.go | 8 +- internal/util/link/outbound.go | 6 +- internal/web/controller/xray_setting.go | 2 +- internal/web/middleware/validate.go | 5 +- .../web/service/api_scale_postgres_test.go | 6 +- internal/web/service/email/email.go | 2 +- internal/web/service/email/subscriber.go | 2 +- internal/web/service/integration/warp.go | 4 +- internal/web/service/metric_history.go | 5 +- internal/web/service/node.go | 7 +- .../web/service/sync_scale_postgres_test.go | 8 +- internal/web/service/tgbot/tgbot_event.go | 2 +- internal/web/service/xray_setting.go | 26 +- internal/web/web.go | 2 +- internal/xray/mutation_audit_test.go | 8 +- tools/openapigen/emit_examples.go | 5 +- 23 files changed, 218 insertions(+), 223 deletions(-) diff --git a/frontend/src/pages/inbounds/form/security/reality.tsx b/frontend/src/pages/inbounds/form/security/reality.tsx index fd5006229..d9ea750e1 100644 --- a/frontend/src/pages/inbounds/form/security/reality.tsx +++ b/frontend/src/pages/inbounds/form/security/reality.tsx @@ -47,7 +47,7 @@ export default function RealityForm({ @@ -331,7 +331,7 @@ export default function NodeFormModal({ {messageContextHolder} - {!hasWarp ? ( - - ) : ( - <> - - - - - - - - - - - - - - - - - - - -
{t('pages.xray.warp.accessToken')}{warpData?.access_token}
{t('pages.xray.warp.deviceId')}{warpData?.device_id}
{t('pages.xray.warp.licenseKey')}{warpData?.license_key}
{t('pages.xray.warp.privateKey')}{warpData?.private_key}
- - + ) : ( + <> + + + + + + + + + + + + + + + + + + + +
{t('pages.xray.warp.accessToken')}{warpData?.access_token}
{t('pages.xray.warp.deviceId')}{warpData?.device_id}
{t('pages.xray.warp.licenseKey')}{warpData?.license_key}
{t('pages.xray.warp.privateKey')}{warpData?.private_key}
- {t('pages.xray.warp.settings')} + - - - { - setWarpPlus(e.target.value); - setLicenseError(''); - }} - /> -
- + {licenseError && ( + + )} +
+
+ + ), + }, + { + key: '2', + label: t('pages.xray.warp.autoUpdateIp', 'Auto Update IP Address'), + children: ( +
+ + setUpdateInterval(Number(e.target.value))} + /> + - {licenseError && ( - - )} - - -
- ), - }, - { - key: '2', - label: t('pages.xray.warp.autoUpdateIp', 'Auto Update IP Address'), - children: ( -
- - setUpdateInterval(Number(e.target.value))} - /> - - -
- ), - }, - ]} - /> +
+ + ), + }, + ]} + /> - {t('pages.xray.warp.accountInfo')} -
- - -
+ {t('pages.xray.warp.accountInfo')} +
+ + +
- {hasConfig && ( - <> - - - - - - - - - - - - - - - {warpConfig?.account && ( - <> - - - - - - - - - - - - - - - - - {warpConfig.account.usage != null && ( + {hasConfig && ( + <> +
{t('pages.xray.warp.deviceName')}{warpConfig?.name}
{t('pages.xray.warp.deviceModel')}{warpConfig?.model}
{t('pages.xray.warp.deviceEnabled')}{String(warpConfig?.enabled)}
{t('pages.xray.warp.accountType')}{warpConfig.account.account_type}
{t('pages.xray.warp.role')}{warpConfig.account.role}
{t('pages.xray.warp.warpPlusData')}{SizeFormatter.sizeFormat(warpConfig.account.premium_data)}
{t('pages.xray.warp.quota')}{SizeFormatter.sizeFormat(warpConfig.account.quota)}
+ + + + + + + + + + + + + + {warpConfig?.account && ( + <> - - + + - )} - - )} - -
{t('pages.xray.warp.deviceName')}{warpConfig?.name}
{t('pages.xray.warp.deviceModel')}{warpConfig?.model}
{t('pages.xray.warp.deviceEnabled')}{String(warpConfig?.enabled)}
{t('pages.xray.warp.usage')}{SizeFormatter.sizeFormat(warpConfig.account.usage)}{t('pages.xray.warp.accountType')}{warpConfig.account.account_type}
+ + {t('pages.xray.warp.role')} + {warpConfig.account.role} + + + {t('pages.xray.warp.warpPlusData')} + {SizeFormatter.sizeFormat(warpConfig.account.premium_data)} + + + {t('pages.xray.warp.quota')} + {SizeFormatter.sizeFormat(warpConfig.account.quota)} + + {warpConfig.account.usage != null && ( + + {t('pages.xray.warp.usage')} + {SizeFormatter.sizeFormat(warpConfig.account.usage)} + + )} + + )} + + - {t('pages.xray.outbound.outboundStatus')} - {warpOutboundIndex >= 0 ? ( - <> - {t('enabled')} - - - ) : ( - <> - {t('disabled')} - - - )} - - )} - - )} + {t('pages.xray.outbound.outboundStatus')} + {warpOutboundIndex >= 0 ? ( + <> + {t('enabled')} + + + ) : ( + <> + {t('disabled')} + + + )} + + )} + + )} ); diff --git a/internal/database/migrate_data.go b/internal/database/migrate_data.go index e0382232e..6bf8d7e60 100644 --- a/internal/database/migrate_data.go +++ b/internal/database/migrate_data.go @@ -223,7 +223,7 @@ func copyTable(src, dst *gorm.DB, mdl any) (int, error) { } rows := make([]map[string]any, n) - for i := 0; i < n; i++ { + for i := range n { rv := reflect.Indirect(slice.Index(i)) row := make(map[string]any, len(columns)) for _, name := range columns { diff --git a/internal/logger/logger_test.go b/internal/logger/logger_test.go index b989517ee..3836650a5 100644 --- a/internal/logger/logger_test.go +++ b/internal/logger/logger_test.go @@ -12,7 +12,7 @@ func TestGetLogs_ReturnsAtMostC(t *testing.T) { logBufferMu.Lock() logBuffer = nil logBufferMu.Unlock() - for i := 0; i < 5; i++ { + for i := range 5 { addToBuffer("ERROR", fmt.Sprintf("m%d", i)) } diff --git a/internal/mtproto/manager.go b/internal/mtproto/manager.go index ab25d332c..bbe7a4559 100644 --- a/internal/mtproto/manager.go +++ b/internal/mtproto/manager.go @@ -425,12 +425,12 @@ func parseMetricLine(line string) (name string, labels map[string]string, value if end < brace { return "", nil, 0, fmt.Errorf("malformed metric line") } - for _, kv := range strings.Split(line[brace+1:end], ",") { - eq := strings.IndexByte(kv, '=') - if eq < 0 { + for kv := range strings.SplitSeq(line[brace+1:end], ",") { + before, after, ok := strings.Cut(kv, "=") + if !ok { continue } - labels[strings.TrimSpace(kv[:eq])] = strings.Trim(strings.TrimSpace(kv[eq+1:]), `"`) + labels[strings.TrimSpace(before)] = strings.Trim(strings.TrimSpace(after), `"`) } rest = strings.TrimSpace(line[end+1:]) } else { diff --git a/internal/sub/clash_external.go b/internal/sub/clash_external.go index f0ded7505..f84791b74 100644 --- a/internal/sub/clash_external.go +++ b/internal/sub/clash_external.go @@ -220,8 +220,8 @@ func clashStringList(v any) []string { } func stripCIDR(addr string) string { - if i := strings.IndexByte(addr, '/'); i >= 0 { - return addr[:i] + if before, _, ok := strings.Cut(addr, "/"); ok { + return before } return addr } diff --git a/internal/sub/controller.go b/internal/sub/controller.go index 97572fbde..8e2e3a7f0 100644 --- a/internal/sub/controller.go +++ b/internal/sub/controller.go @@ -147,9 +147,9 @@ func (a *SUBController) subs(c *gin.Context) { if err != nil || len(subs) == 0 { writeSubError(c, err) } else { - result := "" + var result strings.Builder for _, sub := range subs { - result += sub + "\n" + result.WriteString(sub + "\n") } // If the request expects HTML (e.g., browser) or explicitly asked (?html=1 or ?view=html), render the info page here @@ -180,9 +180,9 @@ func (a *SUBController) subs(c *gin.Context) { a.ApplyCommonHeaders(c, header, a.updateInterval, a.subTitle, a.subSupportUrl, profileUrl, a.subAnnounce, a.subEnableRouting, a.subRoutingRules) if a.subEncrypt { - c.String(200, base64.StdEncoding.EncodeToString([]byte(result))) + c.String(200, base64.StdEncoding.EncodeToString([]byte(result.String()))) } else { - c.String(200, result) + c.String(200, result.String()) } } } diff --git a/internal/util/link/outbound.go b/internal/util/link/outbound.go index 8ba193396..b8100e946 100644 --- a/internal/util/link/outbound.go +++ b/internal/util/link/outbound.go @@ -397,11 +397,11 @@ func parseShadowsocks(link string) (*ParseResult, error) { } func splitMethodPass(userInfo string) (string, string) { - colon := strings.Index(userInfo, ":") - if colon < 0 { + before, after, ok := strings.Cut(userInfo, ":") + if !ok { return "2022-blake3-aes-128-gcm", userInfo // guess } - return userInfo[:colon], userInfo[colon+1:] + return before, after } // --- hysteria2 --- diff --git a/internal/web/controller/xray_setting.go b/internal/web/controller/xray_setting.go index c3a5c6cc7..253008291 100644 --- a/internal/web/controller/xray_setting.go +++ b/internal/web/controller/xray_setting.go @@ -325,7 +325,7 @@ func (a *XraySettingController) testOutbounds(c *gin.Context) { func (a *XraySettingController) balancerStatus(c *gin.Context) { raw := c.PostForm("tags") var tags []string - for _, tag := range strings.Split(raw, ",") { + for tag := range strings.SplitSeq(raw, ",") { if tag = strings.TrimSpace(tag); tag != "" { tags = append(tags, tag) } diff --git a/internal/web/middleware/validate.go b/internal/web/middleware/validate.go index 3b6f4beca..628fe347f 100644 --- a/internal/web/middleware/validate.go +++ b/internal/web/middleware/validate.go @@ -80,8 +80,7 @@ type ValidationPayload struct { func writeBindFailure(c *gin.Context, err error) { payload := ValidationPayload{Issues: []FieldIssue{}, Message: err.Error()} - var ve validator.ValidationErrors - if errors.As(err, &ve) { + if ve, ok := errors.AsType[validator.ValidationErrors](err); ok { payload.Issues = make([]FieldIssue, 0, len(ve)) for _, fe := range ve { payload.Issues = append(payload.Issues, FieldIssue{ @@ -102,7 +101,7 @@ func writeBindFailure(c *gin.Context, err error) { func init() { validate.RegisterTagNameFunc(func(fld reflect.StructField) string { - name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0] + name, _, _ := strings.Cut(fld.Tag.Get("json"), ",") if name == "-" || name == "" { return fld.Name } diff --git a/internal/web/service/api_scale_postgres_test.go b/internal/web/service/api_scale_postgres_test.go index 00f5488dd..37a791e06 100644 --- a/internal/web/service/api_scale_postgres_test.go +++ b/internal/web/service/api_scale_postgres_test.go @@ -92,7 +92,7 @@ func TestAllAPIsPostgresScale(t *testing.T) { db.Exec("ANALYZE") emails := make([]string, n) - for i := 0; i < n; i++ { + for i := range n { emails[i] = clients[i].Email } emailsM := emails[:m] @@ -196,7 +196,7 @@ func TestGetClientTrafficByEmailABScale(t *testing.T) { targets := []string{clients[0].Email, clients[n/2].Email, clients[n-1].Email} start := time.Now() - for i := 0; i < reps; i++ { + for i := range reps { if _, err := inboundSvc.GetClientTrafficByEmail(targets[i%len(targets)]); err != nil { t.Fatalf("new GetClientTrafficByEmail: %v", err) } @@ -204,7 +204,7 @@ func TestGetClientTrafficByEmailABScale(t *testing.T) { newDur := time.Since(start) / reps start = time.Now() - for i := 0; i < reps; i++ { + for i := range reps { if err := oldImpl(targets[i%len(targets)]); err != nil { t.Fatalf("old GetClientTrafficByEmail: %v", err) } diff --git a/internal/web/service/email/email.go b/internal/web/service/email/email.go index 835d8730e..db28cea89 100644 --- a/internal/web/service/email/email.go +++ b/internal/web/service/email/email.go @@ -270,7 +270,7 @@ func parseRecipients(toStr string) []string { return nil } var out []string - for _, s := range strings.Split(toStr, ",") { + for s := range strings.SplitSeq(toStr, ",") { s = strings.TrimSpace(s) if s != "" { out = append(out, s) diff --git a/internal/web/service/email/subscriber.go b/internal/web/service/email/subscriber.go index 463a1e537..f9fdd0989 100644 --- a/internal/web/service/email/subscriber.go +++ b/internal/web/service/email/subscriber.go @@ -53,7 +53,7 @@ func (s *Subscriber) isEventEnabled(t eventbus.EventType) bool { if err != nil || events == "" { return false } - for _, e := range strings.Split(events, ",") { + for e := range strings.SplitSeq(events, ",") { if strings.TrimSpace(e) == string(t) { return true } diff --git a/internal/web/service/integration/warp.go b/internal/web/service/integration/warp.go index 275225eca..5e22a4986 100644 --- a/internal/web/service/integration/warp.go +++ b/internal/web/service/integration/warp.go @@ -190,8 +190,8 @@ func (s *WarpService) ChangeWarpIP() (string, error) { } var parsed struct { - Data map[string]string `json:"data"` - Config map[string]interface{} `json:"config"` + Data map[string]string `json:"data"` + Config map[string]any `json:"config"` } if err := json.Unmarshal([]byte(result), &parsed); err != nil { return "", err diff --git a/internal/web/service/metric_history.go b/internal/web/service/metric_history.go index b6d9a9f53..8823b4507 100644 --- a/internal/web/service/metric_history.go +++ b/internal/web/service/metric_history.go @@ -4,6 +4,7 @@ import ( "encoding/gob" "os" "path/filepath" + "slices" "sync" "time" @@ -106,8 +107,8 @@ func (h *metricHistory) aggregate(metric string, bucketSeconds int, maxPoints in h.mu.Lock() hist := h.metrics[metric] startIdx := 0 - for i := len(hist) - 1; i >= 0; i-- { - if hist[i].T < cutoff { + for i, h := range slices.Backward(hist) { + if h.T < cutoff { startIdx = i + 1 break } diff --git a/internal/web/service/node.go b/internal/web/service/node.go index 59d393054..bafccd23c 100644 --- a/internal/web/service/node.go +++ b/internal/web/service/node.go @@ -11,6 +11,7 @@ import ( "net" "net/http" "net/url" + "slices" "strconv" "strings" "time" @@ -405,10 +406,8 @@ func (s *NodeService) EnsureInboundTagAllowed(nodeID int, tag string) error { if node.InboundSyncMode != "selected" { return nil } - for _, t := range node.InboundTags { - if t == tag { - return nil - } + if slices.Contains(node.InboundTags, tag) { + return nil } buf, err := json.Marshal(append(node.InboundTags, tag)) if err != nil { diff --git a/internal/web/service/sync_scale_postgres_test.go b/internal/web/service/sync_scale_postgres_test.go index bbb812ec4..3df6bc28a 100644 --- a/internal/web/service/sync_scale_postgres_test.go +++ b/internal/web/service/sync_scale_postgres_test.go @@ -70,7 +70,7 @@ func syncInboundOld(tx *gorm.DB, inboundId int, clients []model.Client) error { func makeScaleClients(n int) []model.Client { out := make([]model.Client, n) - for i := 0; i < n; i++ { + for i := range n { out[i] = model.Client{ ID: uuid.NewString(), Email: fmt.Sprintf("user-%07d@scale", i), @@ -260,7 +260,7 @@ func TestGroupAndListPostgresScale(t *testing.T) { } db.Exec("ANALYZE") emails := make([]string, n) - for i := 0; i < n; i++ { + for i := range n { emails[i] = clients[i].Email } @@ -382,7 +382,7 @@ func TestBulkOpsPostgresScale(t *testing.T) { } emailsM := make([]string, m) - for i := 0; i < m; i++ { + for i := range m { emailsM[i] = clients[i].Email } @@ -405,7 +405,7 @@ func TestBulkOpsPostgresScale(t *testing.T) { detachDur := time.Since(t0) payloads := make([]ClientCreatePayload, m) - for i := 0; i < m; i++ { + for i := range m { payloads[i] = ClientCreatePayload{ Client: model.Client{ID: uuid.NewString(), Email: fmt.Sprintf("bulknew-%07d@scale", i), SubID: fmt.Sprintf("bnsub-%07d", i), Enable: true}, InboundIds: []int{ib.Id}, diff --git a/internal/web/service/tgbot/tgbot_event.go b/internal/web/service/tgbot/tgbot_event.go index f4849756d..d66784cd6 100644 --- a/internal/web/service/tgbot/tgbot_event.go +++ b/internal/web/service/tgbot/tgbot_event.go @@ -49,7 +49,7 @@ func (t *Tgbot) isEventEnabled(eventType eventbus.EventType) bool { if err != nil || events == "" { return false } - for _, e := range strings.Split(events, ",") { + for e := range strings.SplitSeq(events, ",") { if strings.TrimSpace(e) == string(eventType) { return true } diff --git a/internal/web/service/xray_setting.go b/internal/web/service/xray_setting.go index a73534f5e..1f2db1c07 100644 --- a/internal/web/service/xray_setting.go +++ b/internal/web/service/xray_setting.go @@ -41,39 +41,39 @@ func (s *XraySettingService) CheckXrayConfig(XrayTemplateConfig string) error { return nil } -func (s *XraySettingService) UpdateWarpXraySetting(warpData map[string]string, warpConfig map[string]interface{}) error { +func (s *XraySettingService) UpdateWarpXraySetting(warpData map[string]string, warpConfig map[string]any) error { template, err := s.GetXrayConfigTemplate() if err != nil { return err } - var cfg map[string]interface{} + var cfg map[string]any if err := json.Unmarshal([]byte(template), &cfg); err != nil { return err } - outbounds, ok := cfg["outbounds"].([]interface{}) + outbounds, ok := cfg["outbounds"].([]any) if !ok { return nil } updated := false for _, outIface := range outbounds { - out, ok := outIface.(map[string]interface{}) + out, ok := outIface.(map[string]any) if !ok { continue } if tag, ok := out["tag"].(string); ok && tag == "warp" { - settings, ok := out["settings"].(map[string]interface{}) + settings, ok := out["settings"].(map[string]any) if !ok { continue } settings["secretKey"] = warpData["private_key"] - if conf, ok := warpConfig["config"].(map[string]interface{}); ok { - if iface, ok := conf["interface"].(map[string]interface{}); ok { - if addrs, ok := iface["addresses"].(map[string]interface{}); ok { + if conf, ok := warpConfig["config"].(map[string]any); ok { + if iface, ok := conf["interface"].(map[string]any); ok { + if addrs, ok := iface["addresses"].(map[string]any); ok { var addrList []string if v4, ok := addrs["v4"].(string); ok && v4 != "" { addrList = append(addrList, v4+"/32") @@ -100,12 +100,12 @@ func (s *XraySettingService) UpdateWarpXraySetting(warpData map[string]string, w settings["reserved"] = res } - if peers, ok := conf["peers"].([]interface{}); ok && len(peers) > 0 { - if peer, ok := peers[0].(map[string]interface{}); ok { - if pSettings, ok := settings["peers"].([]interface{}); ok && len(pSettings) > 0 { - if pSet, ok := pSettings[0].(map[string]interface{}); ok { + if peers, ok := conf["peers"].([]any); ok && len(peers) > 0 { + if peer, ok := peers[0].(map[string]any); ok { + if pSettings, ok := settings["peers"].([]any); ok && len(pSettings) > 0 { + if pSet, ok := pSettings[0].(map[string]any); ok { pSet["publicKey"] = peer["public_key"] - if endpoint, ok := peer["endpoint"].(map[string]interface{}); ok { + if endpoint, ok := peer["endpoint"].(map[string]any); ok { pSet["endpoint"] = endpoint["host"] } } diff --git a/internal/web/web.go b/internal/web/web.go index 185f3ff2c..6ecc972a0 100644 --- a/internal/web/web.go +++ b/internal/web/web.go @@ -388,7 +388,7 @@ func (s *Server) cpuAlarmWanted() bool { if threshold <= 0 { return false } - for _, e := range strings.Split(events, ",") { + for e := range strings.SplitSeq(events, ",") { if strings.TrimSpace(e) == string(eventbus.EventCPUHigh) { return true } diff --git a/internal/xray/mutation_audit_test.go b/internal/xray/mutation_audit_test.go index fdc748507..734798c3a 100644 --- a/internal/xray/mutation_audit_test.go +++ b/internal/xray/mutation_audit_test.go @@ -4,6 +4,7 @@ import ( "os" "os/exec" "path/filepath" + "slices" "testing" "time" @@ -248,12 +249,7 @@ func TestRefreshLocalOnline_GraceBoundaryInbounds(t *testing.T) { } func containsString(s []string, v string) bool { - for _, x := range s { - if x == v { - return true - } - } - return false + return slices.Contains(s, v) } // --------------------------------------------------------------------------- diff --git a/tools/openapigen/emit_examples.go b/tools/openapigen/emit_examples.go index fb03b5c4d..a0ecbcdff 100644 --- a/tools/openapigen/emit_examples.go +++ b/tools/openapigen/emit_examples.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "io" + "maps" "strconv" "strings" ) @@ -116,9 +117,7 @@ func isVisitedRef(t TypeRef, visited map[string]bool) bool { func cloneVisited(in map[string]bool) map[string]bool { out := make(map[string]bool, len(in)+1) - for k, v := range in { - out[k] = v - } + maps.Copy(out, in) return out }