Prechádzať zdrojové kódy

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.
MHSanaei 1 deň pred
rodič
commit
4915d6b18d

+ 1 - 1
frontend/src/pages/inbounds/form/security/reality.tsx

@@ -47,7 +47,7 @@ export default function RealityForm({
       </Form.Item>
       <Form.Item
         label={t('pages.inbounds.form.target')}
-        extra={t('pages.inbounds.form.realityTargetHint')}
+        tooltip={t('pages.inbounds.form.realityTargetHint')}
       >
         <Space.Compact block>
           <Form.Item

+ 7 - 7
frontend/src/pages/nodes/NodeFormModal.tsx

@@ -323,7 +323,7 @@ export default function NodeFormModal({
             label={t('pages.nodes.allowPrivateAddress')}
             name="allowPrivateAddress"
             valuePropName="checked"
-            extra={t('pages.nodes.allowPrivateAddressHint')}
+            tooltip={t('pages.nodes.allowPrivateAddressHint')}
           >
             <Switch />
           </Form.Item>
@@ -331,7 +331,7 @@ export default function NodeFormModal({
           <Form.Item
             label={t('pages.nodes.tlsVerifyMode')}
             name="tlsVerifyMode"
-            extra={t('pages.nodes.tlsVerifyModeHint')}
+            tooltip={t('pages.nodes.tlsVerifyModeHint')}
           >
             <Select
               disabled={scheme === 'http'}
@@ -366,7 +366,7 @@ export default function NodeFormModal({
             <Form.Item
               label={t('pages.nodes.pinnedCert')}
               name="pinnedCertSha256"
-              extra={t('pages.nodes.pinnedCertHint')}
+              tooltip={t('pages.nodes.pinnedCertHint')}
             >
               <Input.Search
                 placeholder={t('pages.nodes.pinnedCertPlaceholder')}
@@ -381,7 +381,7 @@ export default function NodeFormModal({
             label={t('pages.nodes.apiToken')}
             name="apiToken"
             rules={[antdRule(NodeFormSchema.shape.apiToken, t)]}
-            extra={t('pages.nodes.apiTokenHint')}
+            tooltip={t('pages.nodes.apiTokenHint')}
           >
             <Input.Password placeholder={t('pages.nodes.apiTokenPlaceholder')} />
           </Form.Item>
@@ -389,7 +389,7 @@ export default function NodeFormModal({
           <Form.Item
             label={t('pages.nodes.outboundTag')}
             name="outboundTag"
-            extra={t('pages.nodes.outboundTagHint')}
+            tooltip={t('pages.nodes.outboundTagHint')}
             getValueProps={(v) => ({ value: (v as string) || undefined })}
           >
             <Select
@@ -403,7 +403,7 @@ export default function NodeFormModal({
           <Form.Item
             label={t('pages.nodes.inboundSyncMode')}
             name="inboundSyncMode"
-            extra={t('pages.nodes.inboundSyncModeHint')}
+            tooltip={t('pages.nodes.inboundSyncModeHint')}
           >
             <Select
               options={[
@@ -417,7 +417,7 @@ export default function NodeFormModal({
             <Form.Item
               label={t('pages.nodes.inboundTags')}
               name="inboundTags"
-              extra={t('pages.nodes.inboundTagsHint')}
+              tooltip={t('pages.nodes.inboundTagsHint')}
             >
               <Select
                 mode="multiple"

+ 156 - 155
frontend/src/pages/xray/overrides/WarpModal.tsx

@@ -266,170 +266,171 @@ export default function WarpModal({
     <>
       {messageContextHolder}
       <Modal open={open} title="Cloudflare WARP" footer={null} onCancel={onClose}>
-      {!hasWarp ? (
-        <Button type="primary" loading={loading} icon={<ApiOutlined />} onClick={register}>
-          {t('pages.xray.warp.createAccount')}
-        </Button>
-      ) : (
-        <>
-          <table className="warp-data-table">
-            <tbody>
-              <tr className="row-odd">
-                <td>{t('pages.xray.warp.accessToken')}</td>
-                <td>{warpData?.access_token}</td>
-              </tr>
-              <tr>
-                <td>{t('pages.xray.warp.deviceId')}</td>
-                <td>{warpData?.device_id}</td>
-              </tr>
-              <tr className="row-odd">
-                <td>{t('pages.xray.warp.licenseKey')}</td>
-                <td>{warpData?.license_key}</td>
-              </tr>
-              <tr>
-                <td>{t('pages.xray.warp.privateKey')}</td>
-                <td>{warpData?.private_key}</td>
-              </tr>
-            </tbody>
-          </table>
-
-          <Button loading={loading} type="primary" danger className="mt-8" icon={<DeleteOutlined />} onClick={delConfig}>
-            {t('pages.xray.warp.deleteAccount')}
+        {!hasWarp ? (
+          <Button type="primary" loading={loading} icon={<ApiOutlined />} onClick={register}>
+            {t('pages.xray.warp.createAccount')}
           </Button>
+        ) : (
+          <>
+            <table className="warp-data-table">
+              <tbody>
+                <tr className="row-odd">
+                  <td>{t('pages.xray.warp.accessToken')}</td>
+                  <td>{warpData?.access_token}</td>
+                </tr>
+                <tr>
+                  <td>{t('pages.xray.warp.deviceId')}</td>
+                  <td>{warpData?.device_id}</td>
+                </tr>
+                <tr className="row-odd">
+                  <td>{t('pages.xray.warp.licenseKey')}</td>
+                  <td>{warpData?.license_key}</td>
+                </tr>
+                <tr>
+                  <td>{t('pages.xray.warp.privateKey')}</td>
+                  <td>{warpData?.private_key}</td>
+                </tr>
+              </tbody>
+            </table>
+
+            <Button loading={loading} type="primary" danger className="mt-8" icon={<DeleteOutlined />} onClick={delConfig}>
+              {t('pages.xray.warp.deleteAccount')}
+            </Button>
 
-          <Divider className="zero-margin">{t('pages.xray.warp.settings')}</Divider>
+            <Divider className="zero-margin">{t('pages.xray.warp.settings')}</Divider>
 
-          <Collapse
-            className="my-10"
-            items={[
-              {
-                key: '1',
-                label: t('pages.xray.warp.licenseKeyLabel'),
-                children: (
-                  <Form colon={false} labelCol={{ md: { span: 6 } }} wrapperCol={{ md: { span: 14 } }}>
-                    <Form.Item label={t('pages.xray.warp.key')}>
-                      <Input
-                        value={warpPlus}
-                        placeholder={t('pages.xray.warp.keyPlaceholder')}
-                        onChange={(e) => {
-                          setWarpPlus(e.target.value);
-                          setLicenseError('');
-                        }}
-                      />
-                      <div className="license-actions mt-8">
-                        <Button
-                          type="primary"
-                          disabled={warpPlus.length < 26}
-                          loading={loading}
-                          onClick={updateLicense}
-                        >
-                          {t('update')}
+            <Collapse
+              className="my-10"
+              items={[
+                {
+                  key: '1',
+                  label: t('pages.xray.warp.licenseKeyLabel'),
+                  children: (
+                    <Form colon={false} labelCol={{ md: { span: 6 } }} wrapperCol={{ md: { span: 14 } }}>
+                      <Form.Item label={t('pages.xray.warp.key')}>
+                        <Input
+                          value={warpPlus}
+                          placeholder={t('pages.xray.warp.keyPlaceholder')}
+                          onChange={(e) => {
+                            setWarpPlus(e.target.value);
+                            setLicenseError('');
+                          }}
+                        />
+                        <div className="license-actions mt-8">
+                          <Button
+                            type="primary"
+                            disabled={warpPlus.length < 26}
+                            loading={loading}
+                            onClick={updateLicense}
+                          >
+                            {t('update')}
+                          </Button>
+                          {licenseError && (
+                            <Alert title={licenseError} type="error" showIcon className="license-error" />
+                          )}
+                        </div>
+                      </Form.Item>
+                    </Form>
+                  ),
+                },
+                {
+                  key: '2',
+                  label: t('pages.xray.warp.autoUpdateIp', 'Auto Update IP Address'),
+                  children: (
+                    <Form colon={false} labelCol={{ md: { span: 8 } }} wrapperCol={{ md: { span: 12 } }}>
+                      <Form.Item label={t('pages.xray.warp.intervalDays', 'Interval (Days)')}
+                        tooltip={t('pages.xray.warp.intervalDesc', '0 to disable. Changes IP address automatically.')}>
+                        <Input
+                          type="number"
+                          min={0}
+                          value={updateInterval}
+                          onChange={(e) => setUpdateInterval(Number(e.target.value))}
+                        />
+                        <Button className="mt-8" type="primary" loading={loading} onClick={saveInterval}>
+                          {t('save', 'Save')}
                         </Button>
-                        {licenseError && (
-                          <Alert title={licenseError} type="error" showIcon className="license-error" />
-                        )}
-                      </div>
-                    </Form.Item>
-                  </Form>
-                ),
-              },
-              {
-                key: '2',
-                label: t('pages.xray.warp.autoUpdateIp', 'Auto Update IP Address'),
-                children: (
-                  <Form colon={false} labelCol={{ md: { span: 8 } }} wrapperCol={{ md: { span: 12 } }}>
-                    <Form.Item label={t('pages.xray.warp.intervalDays', 'Interval (Days)')} extra={t('pages.xray.warp.intervalDesc', '0 to disable. Changes IP address automatically.')}>
-                      <Input
-                        type="number"
-                        min={0}
-                        value={updateInterval}
-                        onChange={(e) => setUpdateInterval(Number(e.target.value))}
-                      />
-                      <Button className="mt-8" type="primary" loading={loading} onClick={saveInterval}>
-                        {t('save', 'Save')}
-                      </Button>
-                    </Form.Item>
-                  </Form>
-                ),
-              },
-            ]}
-          />
+                      </Form.Item>
+                    </Form>
+                  ),
+                },
+              ]}
+            />
 
-          <Divider className="zero-margin">{t('pages.xray.warp.accountInfo')}</Divider>
-          <div className="my-8">
-            <Button loading={loading} type="primary" icon={<SyncOutlined />} onClick={getConfig}>
-              {t('refresh')}
-            </Button>
-            <Button loading={loading} type="primary" className="ml-8" icon={<SyncOutlined />} onClick={changeIp}>
-              {t('pages.xray.warp.changeIp', 'Change IP')}
-            </Button>
-          </div>
+            <Divider className="zero-margin">{t('pages.xray.warp.accountInfo')}</Divider>
+            <div className="my-8">
+              <Button loading={loading} type="primary" icon={<SyncOutlined />} onClick={getConfig}>
+                {t('refresh')}
+              </Button>
+              <Button loading={loading} type="primary" className="ml-8" icon={<SyncOutlined />} onClick={changeIp}>
+                {t('pages.xray.warp.changeIp', 'Change IP')}
+              </Button>
+            </div>
 
-          {hasConfig && (
-            <>
-              <table className="warp-data-table">
-                <tbody>
-                  <tr className="row-odd">
-                    <td>{t('pages.xray.warp.deviceName')}</td>
-                    <td>{warpConfig?.name}</td>
-                  </tr>
-                  <tr>
-                    <td>{t('pages.xray.warp.deviceModel')}</td>
-                    <td>{warpConfig?.model}</td>
-                  </tr>
-                  <tr className="row-odd">
-                    <td>{t('pages.xray.warp.deviceEnabled')}</td>
-                    <td>{String(warpConfig?.enabled)}</td>
-                  </tr>
-                  {warpConfig?.account && (
-                    <>
-                      <tr>
-                        <td>{t('pages.xray.warp.accountType')}</td>
-                        <td>{warpConfig.account.account_type}</td>
-                      </tr>
-                      <tr className="row-odd">
-                        <td>{t('pages.xray.warp.role')}</td>
-                        <td>{warpConfig.account.role}</td>
-                      </tr>
-                      <tr>
-                        <td>{t('pages.xray.warp.warpPlusData')}</td>
-                        <td>{SizeFormatter.sizeFormat(warpConfig.account.premium_data)}</td>
-                      </tr>
-                      <tr className="row-odd">
-                        <td>{t('pages.xray.warp.quota')}</td>
-                        <td>{SizeFormatter.sizeFormat(warpConfig.account.quota)}</td>
-                      </tr>
-                      {warpConfig.account.usage != null && (
+            {hasConfig && (
+              <>
+                <table className="warp-data-table">
+                  <tbody>
+                    <tr className="row-odd">
+                      <td>{t('pages.xray.warp.deviceName')}</td>
+                      <td>{warpConfig?.name}</td>
+                    </tr>
+                    <tr>
+                      <td>{t('pages.xray.warp.deviceModel')}</td>
+                      <td>{warpConfig?.model}</td>
+                    </tr>
+                    <tr className="row-odd">
+                      <td>{t('pages.xray.warp.deviceEnabled')}</td>
+                      <td>{String(warpConfig?.enabled)}</td>
+                    </tr>
+                    {warpConfig?.account && (
+                      <>
                         <tr>
-                          <td>{t('pages.xray.warp.usage')}</td>
-                          <td>{SizeFormatter.sizeFormat(warpConfig.account.usage)}</td>
+                          <td>{t('pages.xray.warp.accountType')}</td>
+                          <td>{warpConfig.account.account_type}</td>
                         </tr>
-                      )}
-                    </>
-                  )}
-                </tbody>
-              </table>
+                        <tr className="row-odd">
+                          <td>{t('pages.xray.warp.role')}</td>
+                          <td>{warpConfig.account.role}</td>
+                        </tr>
+                        <tr>
+                          <td>{t('pages.xray.warp.warpPlusData')}</td>
+                          <td>{SizeFormatter.sizeFormat(warpConfig.account.premium_data)}</td>
+                        </tr>
+                        <tr className="row-odd">
+                          <td>{t('pages.xray.warp.quota')}</td>
+                          <td>{SizeFormatter.sizeFormat(warpConfig.account.quota)}</td>
+                        </tr>
+                        {warpConfig.account.usage != null && (
+                          <tr>
+                            <td>{t('pages.xray.warp.usage')}</td>
+                            <td>{SizeFormatter.sizeFormat(warpConfig.account.usage)}</td>
+                          </tr>
+                        )}
+                      </>
+                    )}
+                  </tbody>
+                </table>
 
-              <Divider className="my-10">{t('pages.xray.outbound.outboundStatus')}</Divider>
-              {warpOutboundIndex >= 0 ? (
-                <>
-                  <Tag color="green">{t('enabled')}</Tag>
-                  <Button type="primary" danger loading={loading} className="ml-8" onClick={resetOutbound}>
-                    {t('reset')}
-                  </Button>
-                </>
-              ) : (
-                <>
-                  <Tag color="orange">{t('disabled')}</Tag>
-                  <Button type="primary" loading={loading} className="ml-8" icon={<PlusOutlined />} onClick={addOutbound}>
-                    {t('pages.xray.warp.addOutbound')}
-                  </Button>
-                </>
-              )}
-            </>
-          )}
-        </>
-      )}
+                <Divider className="my-10">{t('pages.xray.outbound.outboundStatus')}</Divider>
+                {warpOutboundIndex >= 0 ? (
+                  <>
+                    <Tag color="green">{t('enabled')}</Tag>
+                    <Button type="primary" danger loading={loading} className="ml-8" onClick={resetOutbound}>
+                      {t('reset')}
+                    </Button>
+                  </>
+                ) : (
+                  <>
+                    <Tag color="orange">{t('disabled')}</Tag>
+                    <Button type="primary" loading={loading} className="ml-8" icon={<PlusOutlined />} onClick={addOutbound}>
+                      {t('pages.xray.warp.addOutbound')}
+                    </Button>
+                  </>
+                )}
+              </>
+            )}
+          </>
+        )}
       </Modal>
     </>
   );

+ 1 - 1
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 {

+ 1 - 1
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))
 	}
 

+ 4 - 4
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 {

+ 2 - 2
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
 }

+ 4 - 4
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())
 		}
 	}
 }

+ 3 - 3
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 ---

+ 1 - 1
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)
 		}

+ 2 - 3
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
 		}

+ 3 - 3
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)
 				}

+ 1 - 1
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)

+ 1 - 1
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
 		}

+ 2 - 2
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

+ 3 - 2
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
 		}

+ 3 - 4
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 {

+ 4 - 4
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},

+ 1 - 1
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
 		}

+ 13 - 13
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"]
 								}
 							}

+ 1 - 1
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
 			}

+ 2 - 6
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)
 }
 
 // ---------------------------------------------------------------------------

+ 2 - 3
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
 }