Ver código fonte

style: adopt golangci-lint v2 and resolve all findings

Add .golangci.yml (v2): the standard linters plus bodyclose, errorlint, noctx, misspell, rowserrcheck, sqlclosecheck, unconvert, usestdlibvars, with gofumpt + goimports formatters. Enable the std-error-handling exclusion preset for idiomatic Close/Remove/Setenv ignores; scope-exclude SA1019 (parser.ParseDir in tools/openapigen) and ST1005 (intentional capitalized user-facing error copy that tests assert verbatim). No inline nolint directives were introduced.

Resolve all 217 findings behavior-preserving: gofumpt/goimports formatting, explicit blank assignment on intentionally ignored errors, errors.Is/errors.As and %w wrapping, context-aware stdlib calls (CommandContext/QueryContext/NewRequestWithContext/Dialer), staticcheck simplifications, removed redundant conversions, http.StatusOK and http.MethodGet, inlined the go:fix intPtr helper, and deferred sql rows Close. Add a golangci CI job mirroring the existing Go jobs.
MHSanaei 12 horas atrás
pai
commit
fa1a19c03c
81 arquivos alterados com 410 adições e 286 exclusões
  1. 15 0
      .github/workflows/ci.yml
  2. 53 0
      .golangci.yml
  3. 2 1
      internal/database/api_token_timestamp_test.go
  4. 6 6
      internal/database/db.go
  5. 8 11
      internal/database/dump_sqlite.go
  6. 2 2
      internal/database/migrate_data.go
  7. 2 1
      internal/eventbus/bus_test.go
  8. 2 1
      internal/logger/logger.go
  9. 11 6
      internal/mtproto/manager.go
  10. 2 1
      internal/mtproto/process.go
  11. 4 3
      internal/mtproto/process_windows.go
  12. 3 3
      internal/sub/clash_service.go
  13. 3 2
      internal/sub/controller.go
  14. 2 1
      internal/sub/external_subscription.go
  15. 2 1
      internal/sub/external_subscription_test.go
  16. 7 7
      internal/sub/json_service.go
  17. 9 9
      internal/sub/service.go
  18. 3 3
      internal/sub/sub.go
  19. 1 1
      internal/tunnelmonitor/monitor.go
  20. 2 1
      internal/tunnelmonitor/monitor_test.go
  21. 2 1
      internal/util/common/gorecover_test.go
  22. 1 1
      internal/util/random/random_test.go
  23. 2 1
      internal/util/sys/sys_windows.go
  24. 0 2
      internal/web/controller/api.go
  25. 0 1
      internal/web/controller/inbound.go
  26. 6 7
      internal/web/controller/server.go
  27. 8 7
      internal/web/job/check_client_ip_job.go
  28. 2 1
      internal/web/job/check_client_ip_job_integration_test.go
  29. 1 4
      internal/web/job/check_cpu_usage.go
  30. 1 4
      internal/web/job/check_memory_usage.go
  31. 4 4
      internal/web/job/clear_logs_job.go
  32. 2 2
      internal/web/job/clear_logs_job_test.go
  33. 1 1
      internal/web/job/xray_traffic_job.go
  34. 1 1
      internal/web/locale/locale.go
  35. 1 1
      internal/web/network/auto_https_conn.go
  36. 8 4
      internal/web/service/client_apply_field_test.go
  37. 1 0
      internal/web/service/client_bulk.go
  38. 4 2
      internal/web/service/client_bulk_flow_test.go
  39. 1 0
      internal/web/service/client_crud.go
  40. 1 0
      internal/web/service/client_portable.go
  41. 1 1
      internal/web/service/client_traffic.go
  42. 1 0
      internal/web/service/del_shared_email_runtime_test.go
  43. 6 5
      internal/web/service/email/email.go
  44. 2 1
      internal/web/service/fallback.go
  45. 10 9
      internal/web/service/inbound.go
  46. 1 0
      internal/web/service/inbound_clients.go
  47. 3 3
      internal/web/service/inbound_disable.go
  48. 6 5
      internal/web/service/inbound_migration.go
  49. 2 2
      internal/web/service/inbound_node.go
  50. 8 7
      internal/web/service/inbound_traffic.go
  51. 1 1
      internal/web/service/inbound_update_tag_test.go
  52. 16 7
      internal/web/service/integration/nord.go
  53. 9 8
      internal/web/service/integration/warp.go
  54. 1 1
      internal/web/service/node.go
  55. 3 0
      internal/web/service/node_bulk_dispatch_test.go
  56. 2 1
      internal/web/service/node_client_traffic_sum_test.go
  57. 1 0
      internal/web/service/node_mtls_test.go
  58. 2 1
      internal/web/service/outbound/outbound.go
  59. 8 8
      internal/web/service/outbound/probe_http.go
  60. 2 2
      internal/web/service/outbound_subscription.go
  61. 14 5
      internal/web/service/panel/panel.go
  62. 6 7
      internal/web/service/panel/user.go
  63. 4 4
      internal/web/service/panel/websocket.go
  64. 7 9
      internal/web/service/port_conflict_test.go
  65. 37 20
      internal/web/service/server.go
  66. 1 0
      internal/web/service/setting.go
  67. 1 0
      internal/web/service/sync_scale_postgres_test.go
  68. 2 2
      internal/web/service/tgbot/tgbot.go
  69. 9 10
      internal/web/service/tgbot/tgbot_client.go
  70. 1 5
      internal/web/service/tgbot/tgbot_inbound.go
  71. 2 2
      internal/web/service/tgbot/tgbot_report.go
  72. 8 11
      internal/web/service/tgbot/tgbot_router.go
  73. 3 3
      internal/web/service/xray.go
  74. 1 1
      internal/web/service/xray_setting.go
  75. 24 24
      internal/web/web.go
  76. 2 1
      internal/web/websocket/hub_test.go
  77. 5 4
      internal/xray/process.go
  78. 3 2
      internal/xray/process_test.go
  79. 4 3
      internal/xray/process_windows.go
  80. 4 4
      main.go
  81. 1 3
      tools/openapigen/walker.go

+ 15 - 0
.github/workflows/ci.yml

@@ -102,6 +102,21 @@ jobs:
           go test -run '^$' -fuzz 'FuzzParseLink$' -fuzztime=30s ./internal/util/link/
           go test -run '^$' -fuzz 'FuzzDecodeCertPin$' -fuzztime=30s ./internal/web/runtime/
 
+  golangci:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v7
+      - uses: actions/setup-go@v6
+        with:
+          go-version-file: go.mod
+          cache: true
+      - name: Stub internal/web/dist for go:embed
+        run: mkdir -p internal/web/dist && touch internal/web/dist/.gitkeep
+      - name: golangci-lint
+        uses: golangci/golangci-lint-action@v8
+        with:
+          version: latest
+
   frontend:
     runs-on: ubuntu-latest
     steps:

+ 53 - 0
.golangci.yml

@@ -0,0 +1,53 @@
+version: "2"
+
+run:
+  build-tags: []
+  timeout: 5m
+
+linters:
+  default: standard
+  enable:
+    - bodyclose
+    - errorlint
+    - noctx
+    - misspell
+    - rowserrcheck
+    - sqlclosecheck
+    - unconvert
+    - usestdlibvars
+  exclusions:
+    generated: lax
+    presets:
+      - std-error-handling
+    paths:
+      - frontend
+      - internal/web/dist
+    rules:
+      - path: _test\.go
+        linters:
+          - errcheck
+          - bodyclose
+          - noctx
+      # tools/openapigen relies on go/parser.ParseDir; migrating it to
+      # golang.org/x/tools/go/packages is a generator change, out of scope here.
+      - linters:
+          - staticcheck
+        text: "SA1019: parser.ParseDir"
+      # ST1005 (capitalized error strings) conflicts with intentional
+      # user-facing error copy that tests assert verbatim.
+      - linters:
+          - staticcheck
+        text: "ST1005:"
+
+formatters:
+  enable:
+    - gofumpt
+    - goimports
+  settings:
+    goimports:
+      local-prefixes:
+        - github.com/mhsanaei/3x-ui
+  exclusions:
+    paths:
+      - frontend
+      - internal/web/dist

+ 2 - 1
internal/database/api_token_timestamp_test.go

@@ -3,10 +3,11 @@ package database
 import (
 	"testing"
 
-	"github.com/mhsanaei/3x-ui/v3/internal/database/model"
 	"gorm.io/driver/sqlite"
 	"gorm.io/gorm"
 	"gorm.io/gorm/logger"
+
+	"github.com/mhsanaei/3x-ui/v3/internal/database/model"
 )
 
 func TestNormalizeApiTokenCreatedAtSeconds(t *testing.T) {

+ 6 - 6
internal/database/db.go

@@ -4,6 +4,7 @@ package database
 
 import (
 	"bytes"
+	"context"
 	"encoding/json"
 	"errors"
 	"fmt"
@@ -43,7 +44,7 @@ func IsPostgres() bool {
 	if db == nil {
 		return config.GetDBKind() == "postgres"
 	}
-	return db.Dialector.Name() == "postgres"
+	return db.Name() == "postgres"
 }
 
 // Dialect returns the active GORM dialect name, or "" if the DB is not open.
@@ -51,7 +52,7 @@ func Dialect() string {
 	if db == nil {
 		return ""
 	}
-	return db.Dialector.Name()
+	return db.Name()
 }
 
 const (
@@ -363,7 +364,6 @@ func initUser() error {
 	}
 	if empty {
 		hashedPassword, err := crypto.HashPasswordAsBcrypt(defaultPassword)
-
 		if err != nil {
 			log.Printf("Error hashing default password: %v", err)
 			return err
@@ -580,7 +580,7 @@ func fail2banCanEnforce() bool {
 	if runtime.GOOS == "windows" {
 		return false
 	}
-	return exec.Command("fail2ban-client", "-h").Run() == nil
+	return exec.CommandContext(context.Background(), "fail2ban-client", "-h").Run() == nil
 }
 
 // clearLegacyProxySettings drops the deprecated panelProxy/tgBotProxy rows so a
@@ -1038,7 +1038,7 @@ func InitDB(dbPath string) error {
 		}
 	default:
 		dir := path.Dir(dbPath)
-		if err = os.MkdirAll(dir, 0755); err != nil {
+		if err = os.MkdirAll(dir, 0o755); err != nil {
 			return err
 		}
 		// Keep journal_mode=DELETE so the DB stays a single file (no -wal/-shm
@@ -1065,7 +1065,7 @@ func InitDB(dbPath string) error {
 			"PRAGMA temp_store=MEMORY",
 		}
 		for _, p := range pragmas {
-			if _, err := sqlDB.Exec(p); err != nil {
+			if _, err := sqlDB.ExecContext(context.Background(), p); err != nil {
 				return err
 			}
 		}

+ 8 - 11
internal/database/dump_sqlite.go

@@ -1,6 +1,7 @@
 package database
 
 import (
+	"context"
 	"database/sql"
 	"fmt"
 	"os"
@@ -50,23 +51,21 @@ func DumpSQLiteToBytes(srcPath string) ([]byte, error) {
 	// Tables in creation order, each followed by its data.
 	type object struct{ name, ddl string }
 	var tables []object
-	rows, err := sqlDB.Query(`SELECT name, sql FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' AND sql IS NOT NULL ORDER BY rowid`)
+	rows, err := sqlDB.QueryContext(context.Background(), `SELECT name, sql FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' AND sql IS NOT NULL ORDER BY rowid`)
 	if err != nil {
 		return nil, err
 	}
+	defer rows.Close()
 	for rows.Next() {
 		var o object
 		if err := rows.Scan(&o.name, &o.ddl); err != nil {
-			rows.Close()
 			return nil, err
 		}
 		tables = append(tables, o)
 	}
 	if err := rows.Err(); err != nil {
-		rows.Close()
 		return nil, err
 	}
-	rows.Close()
 
 	for _, t := range tables {
 		b.WriteString(t.ddl)
@@ -85,24 +84,22 @@ func DumpSQLiteToBytes(srcPath string) ([]byte, error) {
 	}
 
 	// Indexes, triggers and views after the data is in place.
-	rows2, err := sqlDB.Query(`SELECT sql FROM sqlite_master WHERE type IN ('index','trigger','view') AND sql IS NOT NULL ORDER BY rowid`)
+	rows2, err := sqlDB.QueryContext(context.Background(), `SELECT sql FROM sqlite_master WHERE type IN ('index','trigger','view') AND sql IS NOT NULL ORDER BY rowid`)
 	if err != nil {
 		return nil, err
 	}
+	defer rows2.Close()
 	for rows2.Next() {
 		var ddl string
 		if err := rows2.Scan(&ddl); err != nil {
-			rows2.Close()
 			return nil, err
 		}
 		b.WriteString(ddl)
 		b.WriteString(";\n")
 	}
 	if err := rows2.Err(); err != nil {
-		rows2.Close()
 		return nil, err
 	}
-	rows2.Close()
 
 	b.WriteString("COMMIT;\n")
 
@@ -131,7 +128,7 @@ func RestoreSQLite(dumpPath, dstPath string) error {
 	}
 
 	// mattn/go-sqlite3 executes every statement in a multi-statement string.
-	if _, err := sqlDB.Exec(string(script)); err != nil {
+	if _, err := sqlDB.ExecContext(context.Background(), string(script)); err != nil {
 		sqlDB.Close()
 		os.Remove(dstPath)
 		return fmt.Errorf("restore failed: %w", err)
@@ -141,7 +138,7 @@ func RestoreSQLite(dumpPath, dstPath string) error {
 
 // dumpTableData appends one INSERT statement per row of table to b.
 func dumpTableData(db *sql.DB, table string, b *strings.Builder) error {
-	rows, err := db.Query(`SELECT * FROM "` + table + `"`)
+	rows, err := db.QueryContext(context.Background(), `SELECT * FROM "`+table+`"`)
 	if err != nil {
 		return err
 	}
@@ -213,6 +210,6 @@ func quoteSQLiteText(s string) string {
 
 func sqliteTableExists(db *sql.DB, name string) bool {
 	var found string
-	err := db.QueryRow(`SELECT name FROM sqlite_master WHERE type='table' AND name=?`, name).Scan(&found)
+	err := db.QueryRowContext(context.Background(), `SELECT name FROM sqlite_master WHERE type='table' AND name=?`, name).Scan(&found)
 	return err == nil
 }

+ 2 - 2
internal/database/migrate_data.go

@@ -67,7 +67,7 @@ func MigrateData(srcPath, dstDSN string) error {
 		return errors.New("destination DSN is required")
 	}
 
-	if err := os.MkdirAll(path.Dir(srcPath), 0755); err != nil {
+	if err := os.MkdirAll(path.Dir(srcPath), 0o755); err != nil {
 		return err
 	}
 
@@ -144,7 +144,7 @@ func ExportPostgresToSQLite(srcDSN, dstPath string) error {
 	if srcDSN == "" {
 		return errors.New("source DSN is required")
 	}
-	if err := os.MkdirAll(path.Dir(dstPath), 0755); err != nil {
+	if err := os.MkdirAll(path.Dir(dstPath), 0o755); err != nil {
 		return err
 	}
 	// Start from an empty file so AutoMigrate creates the canonical schema.

+ 2 - 1
internal/eventbus/bus_test.go

@@ -6,8 +6,9 @@ import (
 	"testing"
 	"time"
 
-	"github.com/mhsanaei/3x-ui/v3/internal/logger"
 	"github.com/op/go-logging"
+
+	"github.com/mhsanaei/3x-ui/v3/internal/logger"
 )
 
 func TestMain(m *testing.M) {

+ 2 - 1
internal/logger/logger.go

@@ -10,9 +10,10 @@ import (
 	"sync"
 	"time"
 
-	"github.com/mhsanaei/3x-ui/v3/internal/config"
 	"github.com/op/go-logging"
 
+	"github.com/mhsanaei/3x-ui/v3/internal/config"
+
 	"gopkg.in/natefinch/lumberjack.v2"
 )
 

+ 11 - 6
internal/mtproto/manager.go

@@ -2,6 +2,7 @@ package mtproto
 
 import (
 	"bufio"
+	"context"
 	"encoding/json"
 	"fmt"
 	"net"
@@ -181,7 +182,7 @@ func (m *Manager) ensureLocked(inst Instance) error {
 			cur.tag = inst.Tag
 			return nil
 		}
-		cur.proc.Stop()
+		_ = cur.proc.Stop()
 		delete(m.procs, inst.Id)
 	}
 	metricsPort, err := FreeLocalPort()
@@ -211,7 +212,7 @@ func (m *Manager) Remove(id int) {
 	m.mu.Lock()
 	defer m.mu.Unlock()
 	if cur, ok := m.procs[id]; ok {
-		cur.proc.Stop()
+		_ = cur.proc.Stop()
 		delete(m.procs, id)
 		_ = os.Remove(configPathForID(id))
 		logger.Infof("mtproto: stopped mtg for inbound %d", id)
@@ -231,7 +232,7 @@ func (m *Manager) Reconcile(desired []Instance) {
 	}
 	for id, cur := range m.procs {
 		if _, ok := want[id]; !ok {
-			cur.proc.Stop()
+			_ = cur.proc.Stop()
 			delete(m.procs, id)
 			_ = os.Remove(configPathForID(id))
 		}
@@ -323,7 +324,7 @@ func (m *Manager) CollectTraffic() []Traffic {
 // for mtg's metrics endpoint and to allocate the per-inbound SOCKS egress
 // bridge port persisted into mtproto inbound settings.
 func FreeLocalPort() (int, error) {
-	l, err := net.Listen("tcp", "127.0.0.1:0")
+	l, err := (&net.ListenConfig{}).Listen(context.Background(), "tcp", "127.0.0.1:0")
 	if err != nil {
 		return 0, err
 	}
@@ -383,7 +384,11 @@ func writeConfig(path string, inst Instance, metricsPort int) error {
 // Best-effort: an unreachable endpoint or unrecognised format yields ok=false.
 func scrapeTraffic(port int) (up int64, down int64, ok bool) {
 	client := http.Client{Timeout: 3 * time.Second}
-	resp, err := client.Get(fmt.Sprintf("http://127.0.0.1:%d/metrics", port))
+	req, reqErr := http.NewRequestWithContext(context.Background(), http.MethodGet, fmt.Sprintf("http://127.0.0.1:%d/metrics", port), nil)
+	if reqErr != nil {
+		return 0, 0, false
+	}
+	resp, err := client.Do(req)
 	if err != nil {
 		return 0, 0, false
 	}
@@ -418,7 +423,7 @@ func scrapeTraffic(port int) (up int64, down int64, ok bool) {
 
 func parseMetricLine(line string) (name string, labels map[string]string, value float64, err error) {
 	labels = map[string]string{}
-	rest := line
+	var rest string
 	if brace := strings.IndexByte(line, '{'); brace >= 0 {
 		name = line[:brace]
 		end := strings.IndexByte(line, '}')

+ 2 - 1
internal/mtproto/process.go

@@ -5,6 +5,7 @@
 package mtproto
 
 import (
+	"context"
 	"errors"
 	"fmt"
 	"os"
@@ -154,7 +155,7 @@ func (p *Process) Start() error {
 	if p.IsRunning() {
 		return errors.New("mtg is already running")
 	}
-	cmd := exec.Command(GetBinaryPath(), "run", p.configPath)
+	cmd := exec.CommandContext(context.Background(), GetBinaryPath(), "run", p.configPath)
 	cmd.Stdout = p.logWriter
 	cmd.Stderr = p.logWriter
 	p.cmd = cmd

+ 4 - 3
internal/mtproto/process_windows.go

@@ -7,8 +7,9 @@ import (
 	"sync"
 	"unsafe"
 
-	"github.com/mhsanaei/3x-ui/v3/internal/logger"
 	"golang.org/x/sys/windows"
+
+	"github.com/mhsanaei/3x-ui/v3/internal/logger"
 )
 
 var (
@@ -36,7 +37,7 @@ func ensureKillOnExitJob() (windows.Handle, error) {
 			uint32(unsafe.Sizeof(info)),
 		)
 		if err != nil {
-			windows.CloseHandle(h)
+			_ = windows.CloseHandle(h)
 			killOnExitJobErr = err
 			return
 		}
@@ -59,7 +60,7 @@ func attachChildLifetime(cmd *exec.Cmd) {
 		logger.Warningf("mtproto: OpenProcess for job attach failed: %v", err)
 		return
 	}
-	defer windows.CloseHandle(h)
+	defer func() { _ = windows.CloseHandle(h) }()
 	if err := windows.AssignProcessToJobObject(job, h); err != nil {
 		logger.Warningf("mtproto: AssignProcessToJobObject failed: %v", err)
 	}

+ 3 - 3
internal/sub/clash_service.go

@@ -241,7 +241,7 @@ func (s *SubClashService) buildProxy(subReq *SubService, inbound *model.Inbound,
 		proxy["type"] = "vless"
 		proxy["uuid"] = client.ID
 		var inboundSettings map[string]any
-		json.Unmarshal([]byte(inbound.Settings), &inboundSettings)
+		_ = json.Unmarshal([]byte(inbound.Settings), &inboundSettings)
 		streamSecurity, _ := stream["security"].(string)
 		if client.Flow != "" && vlessFlowAllowed(network, streamSecurity, inboundSettings) {
 			proxy["flow"] = client.Flow
@@ -259,7 +259,7 @@ func (s *SubClashService) buildProxy(subReq *SubService, inbound *model.Inbound,
 		proxy["type"] = "ss"
 		proxy["password"] = client.Password
 		var inboundSettings map[string]any
-		json.Unmarshal([]byte(inbound.Settings), &inboundSettings)
+		_ = json.Unmarshal([]byte(inbound.Settings), &inboundSettings)
 		method, _ := inboundSettings["method"].(string)
 		if method == "" {
 			return nil
@@ -655,7 +655,7 @@ func (s *SubClashService) applySecurity(proxy map[string]any, security string, s
 
 func (s *SubClashService) streamData(stream string) map[string]any {
 	var streamSettings map[string]any
-	json.Unmarshal([]byte(stream), &streamSettings)
+	_ = json.Unmarshal([]byte(stream), &streamSettings)
 	security, _ := streamSettings["security"].(string)
 	switch security {
 	case "tls":

+ 3 - 2
internal/sub/controller.go

@@ -16,6 +16,7 @@ import (
 	"time"
 
 	"github.com/gin-gonic/gin"
+
 	"github.com/mhsanaei/3x-ui/v3/internal/logger"
 	"github.com/mhsanaei/3x-ui/v3/internal/web/service"
 )
@@ -417,7 +418,7 @@ func (a *SUBController) ApplyCommonHeaders(
 	c.Writer.Header().Set("Subscription-Userinfo", header)
 	c.Writer.Header().Set("Profile-Update-Interval", updateInterval)
 
-	//Basics
+	// Basics
 	if profileTitle != "" {
 		c.Writer.Header().Set("Profile-Title", "base64:"+base64.StdEncoding.EncodeToString([]byte(profileTitle)))
 	}
@@ -431,7 +432,7 @@ func (a *SUBController) ApplyCommonHeaders(
 		c.Writer.Header().Set("Announce", "base64:"+base64.StdEncoding.EncodeToString([]byte(profileAnnounce)))
 	}
 
-	//Advanced (Happ)
+	// Advanced (Happ)
 	c.Writer.Header().Set("Routing-Enable", strconv.FormatBool(profileEnableRouting))
 	if profileRoutingRules != "" {
 		c.Writer.Header().Set("Routing", profileRoutingRules)

+ 2 - 1
internal/sub/external_subscription.go

@@ -1,6 +1,7 @@
 package sub
 
 import (
+	"context"
 	"encoding/base64"
 	"io"
 	"net/http"
@@ -64,7 +65,7 @@ func fetchSubscriptionLinks(rawURL string) []string {
 }
 
 func doFetchSubscriptionLinks(rawURL string) ([]string, error) {
-	req, err := http.NewRequest(http.MethodGet, rawURL, nil)
+	req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, rawURL, nil)
 	if err != nil {
 		return nil, err
 	}

+ 2 - 1
internal/sub/external_subscription_test.go

@@ -1,6 +1,7 @@
 package sub
 
 import (
+	"errors"
 	"net/http"
 	"net/http/httptest"
 	"strings"
@@ -14,7 +15,7 @@ func TestDoFetchSubscriptionLinks_RejectsOversizedBody(t *testing.T) {
 	defer srv.Close()
 
 	links, err := doFetchSubscriptionLinks(srv.URL)
-	if err != errSubscriptionBodyTooLarge {
+	if !errors.Is(err, errSubscriptionBodyTooLarge) {
 		t.Fatalf("err = %v, want errSubscriptionBodyTooLarge", err)
 	}
 	if links != nil {

+ 7 - 7
internal/sub/json_service.go

@@ -29,7 +29,7 @@ type SubJsonService struct {
 func NewSubJsonService(mux string, rules string, finalMask string, subService *SubService) *SubJsonService {
 	var configJson map[string]any
 	var defaultOutbounds []json_util.RawMessage
-	json.Unmarshal([]byte(defaultJson), &configJson)
+	_ = json.Unmarshal([]byte(defaultJson), &configJson)
 	if outboundSlices, ok := configJson["outbounds"].([]any); ok {
 		for _, defaultOutbound := range outboundSlices {
 			jsonBytes, _ := json.Marshal(defaultOutbound)
@@ -41,7 +41,7 @@ func NewSubJsonService(mux string, rules string, finalMask string, subService *S
 		var newRules []any
 		routing, _ := configJson["routing"].(map[string]any)
 		defaultRules, _ := routing["rules"].([]any)
-		json.Unmarshal([]byte(rules), &newRules)
+		_ = json.Unmarshal([]byte(rules), &newRules)
 		defaultRules = append(newRules, defaultRules...)
 		routing["rules"] = defaultRules
 		configJson["routing"] = routing
@@ -234,7 +234,7 @@ func (s *SubJsonService) getConfig(subReq *SubService, inbound *model.Inbound, c
 
 func (s *SubJsonService) streamData(stream string) map[string]any {
 	var streamSettings map[string]any
-	json.Unmarshal([]byte(stream), &streamSettings)
+	_ = json.Unmarshal([]byte(stream), &streamSettings)
 	security, _ := streamSettings["security"].(string)
 	switch security {
 	case "tls":
@@ -392,7 +392,7 @@ func (s *SubJsonService) genVless(inbound *model.Inbound, streamSettings json_ut
 
 	// Add encryption for VLESS outbound from inbound settings
 	var inboundSettings map[string]any
-	json.Unmarshal([]byte(inbound.Settings), &inboundSettings)
+	_ = json.Unmarshal([]byte(inbound.Settings), &inboundSettings)
 	encryption, _ := inboundSettings["encryption"].(string)
 
 	settings := map[string]any{
@@ -423,7 +423,7 @@ func (s *SubJsonService) genServer(inbound *model.Inbound, streamSettings json_u
 
 	if inbound.Protocol == model.Shadowsocks {
 		var inboundSettings map[string]any
-		json.Unmarshal([]byte(inbound.Settings), &inboundSettings)
+		_ = json.Unmarshal([]byte(inbound.Settings), &inboundSettings)
 		method, _ := inboundSettings["method"].(string)
 		serverData[0].Method = method
 
@@ -474,7 +474,7 @@ func (s *SubJsonService) genHy(inbound *model.Inbound, newStream map[string]any,
 	}
 
 	var settings, stream map[string]any
-	json.Unmarshal([]byte(inbound.Settings), &settings)
+	_ = json.Unmarshal([]byte(inbound.Settings), &settings)
 	version, _ := settings["version"].(float64)
 	outbound.Settings = map[string]any{
 		"version": int(version),
@@ -482,7 +482,7 @@ func (s *SubJsonService) genHy(inbound *model.Inbound, newStream map[string]any,
 		"port":    inbound.Port,
 	}
 
-	json.Unmarshal([]byte(inbound.StreamSettings), &stream)
+	_ = json.Unmarshal([]byte(inbound.StreamSettings), &stream)
 	hyStream := stream["hysteriaSettings"].(map[string]any)
 	outHyStream := map[string]any{
 		"version": int(version),

+ 9 - 9
internal/sub/service.go

@@ -453,12 +453,12 @@ func (s *SubService) projectThroughFallbackMaster(inbound *model.Inbound) bool {
 // + ws/grpc/etc. settings) stays the child's.
 func mergeStreamFromMaster(childStream, masterStream string) string {
 	var stream map[string]any
-	json.Unmarshal([]byte(childStream), &stream)
+	_ = json.Unmarshal([]byte(childStream), &stream)
 	if stream == nil {
 		stream = map[string]any{}
 	}
 	var mst map[string]any
-	json.Unmarshal([]byte(masterStream), &mst)
+	_ = json.Unmarshal([]byte(masterStream), &mst)
 	if mst == nil {
 		return childStream
 	}
@@ -511,7 +511,7 @@ func (s *SubService) genMtprotoLink(inbound *model.Inbound, _ string) string {
 		return ""
 	}
 	settings := map[string]any{}
-	json.Unmarshal([]byte(inbound.Settings), &settings)
+	_ = json.Unmarshal([]byte(inbound.Settings), &settings)
 	secret, _ := settings["secret"].(string)
 	if secret == "" {
 		if healed, ok := model.HealMtprotoSecret(inbound.Settings); ok {
@@ -617,7 +617,7 @@ func (s *SubService) genVlessLink(inbound *model.Inbound, email string) string {
 
 	// Add encryption parameter for VLESS from inbound settings
 	var settings map[string]any
-	json.Unmarshal([]byte(inbound.Settings), &settings)
+	_ = json.Unmarshal([]byte(inbound.Settings), &settings)
 	if encryption, ok := settings["encryption"].(string); ok {
 		params["encryption"] = encryption
 	}
@@ -739,7 +739,7 @@ func (s *SubService) genShadowsocksLink(inbound *model.Inbound, email string) st
 	clients, _ := s.inboundService.GetClients(inbound)
 
 	var settings map[string]any
-	json.Unmarshal([]byte(inbound.Settings), &settings)
+	_ = json.Unmarshal([]byte(inbound.Settings), &settings)
 	inboundPassword := settings["password"].(string)
 	method := settings["method"].(string)
 	clientIndex := findClientIndex(clients, email)
@@ -808,7 +808,7 @@ func (s *SubService) genHysteriaLink(inbound *model.Inbound, email string) strin
 		return ""
 	}
 	var stream map[string]any
-	json.Unmarshal([]byte(inbound.StreamSettings), &stream)
+	_ = json.Unmarshal([]byte(inbound.StreamSettings), &stream)
 	clients, _ := s.inboundService.GetClients(inbound)
 	clientIndex := -1
 	for i, client := range clients {
@@ -877,7 +877,7 @@ func (s *SubService) genHysteriaLink(inbound *model.Inbound, email string) strin
 	}
 
 	var settings map[string]any
-	json.Unmarshal([]byte(inbound.Settings), &settings)
+	_ = json.Unmarshal([]byte(inbound.Settings), &settings)
 	version, _ := settings["version"].(float64)
 	protocol := "hysteria2"
 	if int(version) == 1 {
@@ -1008,7 +1008,7 @@ func findClientIndex(clients []model.Client, email string) int {
 
 func unmarshalStreamSettings(streamSettings string) map[string]any {
 	var stream map[string]any
-	json.Unmarshal([]byte(streamSettings), &stream)
+	_ = json.Unmarshal([]byte(streamSettings), &stream)
 	return stream
 }
 
@@ -1316,7 +1316,7 @@ func buildVmessLink(obj map[string]any) string {
 func cloneVmessShareObj(baseObj map[string]any, newSecurity string) map[string]any {
 	newObj := map[string]any{}
 	for key, value := range baseObj {
-		if !(newSecurity == "none" && (key == "alpn" || key == "sni" || key == "fp" || key == "pcs")) {
+		if newSecurity != "none" || (key != "alpn" && key != "sni" && key != "fp" && key != "pcs") {
 			newObj[key] = value
 		}
 	}

+ 3 - 3
internal/sub/sub.go

@@ -253,7 +253,7 @@ func (s *Server) Start() (err error) {
 	// This is an anonymous function, no function name
 	defer func() {
 		if err != nil {
-			s.Stop()
+			_ = s.Stop()
 		}
 	}()
 
@@ -288,7 +288,7 @@ func (s *Server) Start() (err error) {
 	}
 
 	listenAddr := net.JoinHostPort(listen, strconv.Itoa(port))
-	listener, err := net.Listen("tcp", listenAddr)
+	listener, err := (&net.ListenConfig{}).Listen(context.Background(), "tcp", listenAddr)
 	if err != nil {
 		return err
 	}
@@ -323,7 +323,7 @@ func (s *Server) Start() (err error) {
 	}
 
 	go func() {
-		s.httpServer.Serve(listener)
+		_ = s.httpServer.Serve(listener)
 	}()
 
 	return nil

+ 1 - 1
internal/tunnelmonitor/monitor.go

@@ -189,7 +189,7 @@ func (m *Monitor) Step(ctx context.Context) (bool, error) {
 		}
 
 		if recErr := m.recover(ctx); recErr != nil {
-			return false, fmt.Errorf("recovery failed after probe error %v: %w", err, recErr)
+			return false, fmt.Errorf("recovery failed after probe error %w: %w", err, recErr)
 		}
 
 		m.lastRecovery = now

+ 2 - 1
internal/tunnelmonitor/monitor_test.go

@@ -9,8 +9,9 @@ import (
 	"testing"
 	"time"
 
-	"github.com/mhsanaei/3x-ui/v3/internal/logger"
 	"github.com/op/go-logging"
+
+	"github.com/mhsanaei/3x-ui/v3/internal/logger"
 )
 
 func TestMain(m *testing.M) {

+ 2 - 1
internal/util/common/gorecover_test.go

@@ -5,8 +5,9 @@ import (
 	"testing"
 	"time"
 
-	"github.com/mhsanaei/3x-ui/v3/internal/logger"
 	"github.com/op/go-logging"
+
+	"github.com/mhsanaei/3x-ui/v3/internal/logger"
 )
 
 func TestMain(m *testing.M) {

+ 1 - 1
internal/util/random/random_test.go

@@ -15,7 +15,7 @@ func TestSeq_LengthAndAlphabet(t *testing.T) {
 			isDigit := r >= '0' && r <= '9'
 			isLower := r >= 'a' && r <= 'z'
 			isUpper := r >= 'A' && r <= 'Z'
-			if !(isDigit || isLower || isUpper) {
+			if !isDigit && !isLower && !isUpper {
 				t.Fatalf("Seq(%d) byte %d = %q is not alphanumeric", n, i, r)
 			}
 		}

+ 2 - 1
internal/util/sys/sys_windows.go

@@ -66,7 +66,8 @@ func CPUPercentRaw() (float64, error) {
 		uintptr(unsafe.Pointer(&userFT)),
 	)
 	if r1 == 0 {
-		if errno, _ := e1.(syscall.Errno); errno != 0 {
+		var errno syscall.Errno
+		if errors.As(e1, &errno) && errno != 0 {
 			return 0, errno
 		}
 		return 0, errors.New("GetSystemTimes failed")

+ 0 - 2
internal/web/controller/api.go

@@ -5,7 +5,6 @@ import (
 	"strings"
 
 	"github.com/mhsanaei/3x-ui/v3/internal/web/middleware"
-	"github.com/mhsanaei/3x-ui/v3/internal/web/service"
 	"github.com/mhsanaei/3x-ui/v3/internal/web/service/panel"
 	"github.com/mhsanaei/3x-ui/v3/internal/web/service/tgbot"
 	"github.com/mhsanaei/3x-ui/v3/internal/web/session"
@@ -22,7 +21,6 @@ type APIController struct {
 	hostController        *HostController
 	settingController     *SettingController
 	xraySettingController *XraySettingController
-	settingService        service.SettingService
 	userService           panel.UserService
 	apiTokenService       panel.ApiTokenService
 	Tgbot                 tgbot.Tgbot

+ 0 - 1
internal/web/controller/inbound.go

@@ -61,7 +61,6 @@ func (a *InboundController) broadcastInboundsUpdate(userId int) {
 
 // initRouter initializes the routes for inbound-related operations.
 func (a *InboundController) initRouter(g *gin.RouterGroup) {
-
 	g.GET("/list", a.getInbounds)
 	g.GET("/list/slim", a.getInboundsSlim)
 	g.GET("/options", a.getInboundOptions)

+ 6 - 7
internal/web/controller/server.go

@@ -42,7 +42,6 @@ func NewServerController(g *gin.RouterGroup) *ServerController {
 
 // initRouter sets up the routes for server status, Xray management, and utility endpoints.
 func (a *ServerController) initRouter(g *gin.RouterGroup) {
-
 	g.GET("/status", a.status)
 	g.GET("/cpuHistory/:bucket", a.getCpuHistoryBucket)
 	g.GET("/history/:metric/:bucket", a.getMetricHistoryBucket)
@@ -89,7 +88,7 @@ func (a *ServerController) initRouter(g *gin.RouterGroup) {
 // the cross-service side effects (xrayMetrics sample + websocket broadcast).
 func (a *ServerController) startTask() {
 	c := global.GetWebServer().GetCron()
-	c.AddFunc("@every 2s", func() {
+	_, _ = c.AddFunc("@every 2s", func() {
 		status := a.serverService.RefreshStatus()
 		if status == nil {
 			return
@@ -97,7 +96,7 @@ func (a *ServerController) startTask() {
 		a.xrayMetricsService.Sample(time.Now())
 		websocket.BroadcastStatus(status)
 	})
-	c.AddFunc("@every 1m", func() {
+	_, _ = c.AddFunc("@every 1m", func() {
 		if err := service.PersistSystemMetrics(); err != nil {
 			logger.Warning("persist system metrics failed:", err)
 		}
@@ -327,13 +326,13 @@ func (a *ServerController) getDb(c *gin.Context) {
 
 	filename := a.serverService.BackupFilename(c.Request.Host)
 	if !filenameRegex.MatchString(filename) {
-		c.AbortWithError(http.StatusBadRequest, fmt.Errorf("invalid filename"))
+		_ = c.AbortWithError(http.StatusBadRequest, fmt.Errorf("invalid filename"))
 		return
 	}
 
 	c.Header("Content-Type", "application/octet-stream")
 	c.Header("Content-Disposition", "attachment; filename="+filename)
-	c.Writer.Write(db)
+	_, _ = c.Writer.Write(db)
 }
 
 // getMigration downloads a cross-engine migration file: a .dump on SQLite or a
@@ -345,13 +344,13 @@ func (a *ServerController) getMigration(c *gin.Context) {
 		return
 	}
 	if !filenameRegex.MatchString(filename) {
-		c.AbortWithError(http.StatusBadRequest, fmt.Errorf("invalid filename"))
+		_ = c.AbortWithError(http.StatusBadRequest, fmt.Errorf("invalid filename"))
 		return
 	}
 
 	c.Header("Content-Type", "application/octet-stream")
 	c.Header("Content-Disposition", "attachment; filename="+filename)
-	c.Writer.Write(data)
+	_, _ = c.Writer.Write(data)
 }
 
 // importDB imports a database file and restarts the Xray service.

+ 8 - 7
internal/web/job/check_client_ip_job.go

@@ -1,6 +1,7 @@
 package job
 
 import (
+	"context"
 	"encoding/json"
 	"errors"
 	"log"
@@ -127,7 +128,7 @@ func (j *CheckClientIpJob) hasLimitIp() bool {
 		}
 
 		settings := map[string][]model.Client{}
-		json.Unmarshal([]byte(inbound.Settings), &settings)
+		_ = json.Unmarshal([]byte(inbound.Settings), &settings)
 		clients := settings["clients"]
 
 		for _, client := range clients {
@@ -189,7 +190,7 @@ func (j *CheckClientIpJob) processObserved(observed map[string]map[string]int64,
 
 		clientIpsRecord, err := j.getInboundClientIps(email)
 		if err != nil {
-			j.addInboundClientIps(email, ipsWithTime)
+			_ = j.addInboundClientIps(email, ipsWithTime)
 			continue
 		}
 
@@ -277,7 +278,7 @@ func (j *CheckClientIpJob) checkFail2BanInstalled() bool {
 
 	cmd := "fail2ban-client"
 	args := []string{"-h"}
-	err := exec.Command(cmd, args...).Run()
+	err := exec.CommandContext(context.Background(), cmd, args...).Run()
 	return err == nil
 }
 
@@ -345,7 +346,7 @@ func (j *CheckClientIpJob) updateInboundClientIps(inboundClientIps *model.Inboun
 	}
 
 	settings := map[string][]model.Client{}
-	json.Unmarshal([]byte(inbound.Settings), &settings)
+	_ = json.Unmarshal([]byte(inbound.Settings), &settings)
 	clients := settings["clients"]
 
 	// Find the client's IP limit
@@ -372,7 +373,7 @@ func (j *CheckClientIpJob) updateInboundClientIps(inboundClientIps *model.Inboun
 	// Parse old IPs from database
 	var oldIpsWithTime []IPWithTimestamp
 	if inboundClientIps.Ips != "" {
-		json.Unmarshal([]byte(inboundClientIps.Ips), &oldIpsWithTime)
+		_ = json.Unmarshal([]byte(inboundClientIps.Ips), &oldIpsWithTime)
 	}
 
 	ipMap := mergeClientIps(oldIpsWithTime, newIpsWithTime, time.Now().Unix()-ipStaleAfterSeconds, observedAreLive)
@@ -393,7 +394,7 @@ func (j *CheckClientIpJob) updateInboundClientIps(inboundClientIps *model.Inboun
 	if len(bannedLive) > 0 {
 		shouldCleanLog = true
 
-		logIpFile, err := os.OpenFile(xray.GetIPLimitLogPath(), os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
+		logIpFile, err := os.OpenFile(xray.GetIPLimitLogPath(), os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0o644)
 		if err != nil {
 			logger.Errorf("failed to open IP limit log file: %s", err)
 			return false
@@ -455,7 +456,7 @@ func (j *CheckClientIpJob) disconnectClientTemporarily(inbound *model.Inbound, c
 		if client.Email == clientEmail {
 			// Convert client to map for API
 			clientBytes, _ := json.Marshal(client)
-			json.Unmarshal(clientBytes, &clientConfig)
+			_ = json.Unmarshal(clientBytes, &clientConfig)
 			break
 		}
 	}

+ 2 - 1
internal/web/job/check_client_ip_job_integration_test.go

@@ -9,10 +9,11 @@ import (
 	"testing"
 	"time"
 
+	"github.com/op/go-logging"
+
 	"github.com/mhsanaei/3x-ui/v3/internal/database"
 	"github.com/mhsanaei/3x-ui/v3/internal/database/model"
 	xuilogger "github.com/mhsanaei/3x-ui/v3/internal/logger"
-	"github.com/op/go-logging"
 )
 
 // 3x-ui logger must be initialised once before any code path that can

+ 1 - 4
internal/web/job/check_cpu_usage.go

@@ -4,15 +4,12 @@ import (
 	"time"
 
 	"github.com/mhsanaei/3x-ui/v3/internal/eventbus"
-	"github.com/mhsanaei/3x-ui/v3/internal/web/service"
 
 	"github.com/shirou/gopsutil/v4/cpu"
 )
 
 // CheckCpuJob monitors CPU usage and publishes events when threshold is exceeded.
-type CheckCpuJob struct {
-	settingService service.SettingService
-}
+type CheckCpuJob struct{}
 
 // NewCheckCpuJob creates a new CPU monitoring job instance.
 func NewCheckCpuJob() *CheckCpuJob {

+ 1 - 4
internal/web/job/check_memory_usage.go

@@ -2,15 +2,12 @@ package job
 
 import (
 	"github.com/mhsanaei/3x-ui/v3/internal/eventbus"
-	"github.com/mhsanaei/3x-ui/v3/internal/web/service"
 
 	"github.com/shirou/gopsutil/v4/mem"
 )
 
 // CheckMemJob monitors memory usage and publishes events when threshold is exceeded.
-type CheckMemJob struct {
-	settingService service.SettingService
-}
+type CheckMemJob struct{}
 
 // NewCheckMemJob creates a new memory monitoring job instance.
 func NewCheckMemJob() *CheckMemJob {

+ 4 - 4
internal/web/job/clear_logs_job.go

@@ -20,11 +20,11 @@ func NewClearLogsJob() *ClearLogsJob {
 // ensureFileExists creates the necessary directories and file if they don't exist
 func ensureFileExists(path string) error {
 	dir := filepath.Dir(path)
-	if err := os.MkdirAll(dir, 0755); err != nil {
+	if err := os.MkdirAll(dir, 0o755); err != nil {
 		return err
 	}
 
-	file, err := os.OpenFile(path, os.O_CREATE|os.O_RDWR, 0644)
+	file, err := os.OpenFile(path, os.O_CREATE|os.O_RDWR, 0o644)
 	if err != nil {
 		return err
 	}
@@ -48,13 +48,13 @@ func (j *ClearLogsJob) Run() {
 	for i := range len(logFiles) {
 		if i > 0 {
 			// Copy to previous logs
-			logFilePrev, err := os.OpenFile(logFilesPrev[i-1], os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
+			logFilePrev, err := os.OpenFile(logFilesPrev[i-1], os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0o644)
 			if err != nil {
 				logger.Warning("Failed to open previous log file for writing:", logFilesPrev[i-1], "-", err)
 				continue
 			}
 
-			logFile, err := os.OpenFile(logFiles[i], os.O_RDONLY, 0644)
+			logFile, err := os.OpenFile(logFiles[i], os.O_RDONLY, 0o644)
 			if err != nil {
 				logger.Warning("Failed to open current log file for reading:", logFiles[i], "-", err)
 				logFilePrev.Close()

+ 2 - 2
internal/web/job/clear_logs_job_test.go

@@ -19,14 +19,14 @@ func writeAccessLogConfig(t *testing.T, accessPath string) {
 	if err != nil {
 		t.Fatalf("marshal xray config: %v", err)
 	}
-	if err := os.WriteFile(filepath.Join(binDir, "config.json"), configData, 0644); err != nil {
+	if err := os.WriteFile(filepath.Join(binDir, "config.json"), configData, 0o644); err != nil {
 		t.Fatalf("write xray config: %v", err)
 	}
 }
 
 func TestWipeAccessLog_TruncatesEnabledLog(t *testing.T) {
 	accessLog := filepath.Join(t.TempDir(), "access.log")
-	if err := os.WriteFile(accessLog, []byte("2026/06/23 12:00:00 from tcp:203.0.113.10:443 accepted\n"), 0644); err != nil {
+	if err := os.WriteFile(accessLog, []byte("2026/06/23 12:00:00 from tcp:203.0.113.10:443 accepted\n"), 0o644); err != nil {
 		t.Fatalf("seed access log: %v", err)
 	}
 	writeAccessLogConfig(t, accessLog)

+ 1 - 1
internal/web/job/xray_traffic_job.go

@@ -178,7 +178,7 @@ func (j *XrayTrafficJob) informTrafficToExternalAPI(inboundTraffics []*xray.Traf
 	defer fasthttp.ReleaseRequest(request)
 	request.Header.SetMethod("POST")
 	request.Header.SetContentType("application/json; charset=UTF-8")
-	request.SetBody([]byte(requestBody))
+	request.SetBody(requestBody)
 	request.SetRequestURI(informURL)
 	response := fasthttp.AcquireResponse()
 	defer fasthttp.ReleaseResponse(response)

+ 1 - 1
internal/web/locale/locale.go

@@ -56,7 +56,7 @@ func InitLocalizer(i18nFS embed.FS, settingService SettingService) error {
 
 // createTemplateData creates a template data map from parameters with optional separator.
 func createTemplateData(params []string, separator ...string) map[string]any {
-	var sep string = "=="
+	sep := "=="
 	if len(separator) > 0 {
 		sep = separator[0]
 	}

+ 1 - 1
internal/web/network/auto_https_conn.go

@@ -50,7 +50,7 @@ func (c *AutoHttpsConn) readRequest() bool {
 	resp.StatusCode = http.StatusTemporaryRedirect
 	location := fmt.Sprintf("https://%v%v", request.Host, request.RequestURI)
 	resp.Header.Set("Location", location)
-	resp.Write(c.Conn)
+	_ = resp.Write(c.Conn)
 	c.Close()
 	c.firstBuf = nil
 	return true

+ 8 - 4
internal/web/service/client_apply_field_test.go

@@ -34,10 +34,14 @@ func TestResetClientExpiryTimeByEmail_MultiInbound(t *testing.T) {
 		return string(b)
 	}
 
-	first := &model.Inbound{Tag: "vless-a", Enable: true, Port: 50001, Protocol: model.VLESS,
-		StreamSettings: `{"network":"tcp","security":"reality"}`, Settings: clientJSON(oldExpiry)}
-	second := &model.Inbound{Tag: "vless-b", Enable: true, Port: 50002, Protocol: model.VLESS,
-		StreamSettings: `{"network":"ws","security":"tls"}`, Settings: clientJSON(oldExpiry)}
+	first := &model.Inbound{
+		Tag: "vless-a", Enable: true, Port: 50001, Protocol: model.VLESS,
+		StreamSettings: `{"network":"tcp","security":"reality"}`, Settings: clientJSON(oldExpiry),
+	}
+	second := &model.Inbound{
+		Tag: "vless-b", Enable: true, Port: 50002, Protocol: model.VLESS,
+		StreamSettings: `{"network":"ws","security":"tls"}`, Settings: clientJSON(oldExpiry),
+	}
 	for _, ib := range []*model.Inbound{first, second} {
 		if err := db.Create(ib).Error; err != nil {
 			t.Fatalf("create inbound %s: %v", ib.Tag, err)

+ 1 - 0
internal/web/service/client_bulk.go

@@ -8,6 +8,7 @@ import (
 	"time"
 
 	"github.com/google/uuid"
+
 	"github.com/mhsanaei/3x-ui/v3/internal/database"
 	"github.com/mhsanaei/3x-ui/v3/internal/database/model"
 	"github.com/mhsanaei/3x-ui/v3/internal/logger"

+ 4 - 2
internal/web/service/client_bulk_flow_test.go

@@ -40,8 +40,10 @@ func flowOf(t *testing.T, svc *ClientService, email string) string {
 	return rec.Flow
 }
 
-const realityStream = `{"network":"tcp","security":"reality"}`
-const wsStream = `{"network":"ws","security":"none"}`
+const (
+	realityStream = `{"network":"tcp","security":"reality"}`
+	wsStream      = `{"network":"ws","security":"none"}`
+)
 
 // TestBulkAdjust_FlowSetAndClear covers the happy path: a vision flow is applied
 // on an eligible VLESS inbound and later cleared with the "none" directive. Both

+ 1 - 0
internal/web/service/client_crud.go

@@ -9,6 +9,7 @@ import (
 	"time"
 
 	"github.com/google/uuid"
+
 	"github.com/mhsanaei/3x-ui/v3/internal/database"
 	"github.com/mhsanaei/3x-ui/v3/internal/database/model"
 	"github.com/mhsanaei/3x-ui/v3/internal/util/common"

+ 1 - 0
internal/web/service/client_portable.go

@@ -5,6 +5,7 @@ import (
 	"time"
 
 	"github.com/google/uuid"
+
 	"github.com/mhsanaei/3x-ui/v3/internal/database"
 	"github.com/mhsanaei/3x-ui/v3/internal/database/model"
 	"github.com/mhsanaei/3x-ui/v3/internal/xray"

+ 1 - 1
internal/web/service/client_traffic.go

@@ -84,7 +84,7 @@ func (s *ClientService) BulkResetTraffic(inboundSvc *InboundService, emails []st
 		if err == nil && !rec.Enable {
 			updated := rec.ToClient()
 			updated.Enable = true
-			s.Update(inboundSvc, rec.Id, *updated)
+			_, _ = s.Update(inboundSvc, rec.Id, *updated)
 		}
 	}
 

+ 1 - 0
internal/web/service/del_shared_email_runtime_test.go

@@ -4,6 +4,7 @@ import (
 	"testing"
 
 	"github.com/google/uuid"
+
 	"github.com/mhsanaei/3x-ui/v3/internal/database/model"
 )
 

+ 6 - 5
internal/web/service/email/email.go

@@ -1,6 +1,7 @@
 package email
 
 import (
+	"context"
 	"crypto/tls"
 	"fmt"
 	"net"
@@ -115,10 +116,10 @@ func (s *EmailService) TestConnection() SMTPTestResult {
 
 	switch encryptionType {
 	case "tls":
-		conn, err = tls.DialWithDialer(dialer, "tcp", addr, &tls.Config{
+		conn, err = (&tls.Dialer{NetDialer: dialer, Config: &tls.Config{
 			ServerName:         host,
 			InsecureSkipVerify: false,
-		})
+		}}).DialContext(context.Background(), "tcp", addr)
 	default:
 		conn, err = dialer.Dial("tcp", addr)
 	}
@@ -188,10 +189,10 @@ func (s *EmailService) TestConnection() SMTPTestResult {
 func (s *EmailService) sendWithTLS(addr string, auth smtp.Auth, from string, to []string, msg []byte, host string) error {
 	// Dial with explicit timeout
 	dialer := &net.Dialer{Timeout: 10 * time.Second}
-	conn, err := tls.DialWithDialer(dialer, "tcp", addr, &tls.Config{
+	conn, err := (&tls.Dialer{NetDialer: dialer, Config: &tls.Config{
 		ServerName:         host,
 		InsecureSkipVerify: false,
-	})
+	}}).DialContext(context.Background(), "tcp", addr)
 	if err != nil {
 		return err
 	}
@@ -289,7 +290,7 @@ func buildMessage(from string, to []string, subject, body string) []byte {
 	}
 	var msg strings.Builder
 	for k, v := range headers {
-		msg.WriteString(fmt.Sprintf("%s: %s\r\n", k, v))
+		fmt.Fprintf(&msg, "%s: %s\r\n", k, v)
 	}
 	msg.WriteString("\r\n")
 	msg.WriteString(body)

+ 2 - 1
internal/web/service/fallback.go

@@ -1,6 +1,7 @@
 package service
 
 import (
+	"errors"
 	"fmt"
 	"strings"
 
@@ -46,7 +47,7 @@ func (s *FallbackService) GetParentForChild(childId int) (*model.InboundFallback
 		Where("child_id = ?", childId).
 		Order("sort_order ASC, id ASC").
 		First(&row).Error
-	if err == gorm.ErrRecordNotFound {
+	if errors.Is(err, gorm.ErrRecordNotFound) {
 		return nil, nil
 	}
 	if err != nil {

+ 10 - 9
internal/web/service/inbound.go

@@ -5,6 +5,7 @@ package service
 import (
 	"context"
 	"encoding/json"
+	"errors"
 	"fmt"
 	"net"
 	"sort"
@@ -153,7 +154,7 @@ func (s *InboundService) GetInbounds(userId int) ([]*model.Inbound, error) {
 	db := database.GetDB()
 	var inbounds []*model.Inbound
 	err := db.Model(model.Inbound{}).Preload("ClientStats").Where("user_id = ?", userId).Order("id ASC").Find(&inbounds).Error
-	if err != nil && err != gorm.ErrRecordNotFound {
+	if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
 		return nil, err
 	}
 	s.enrichClientStats(db, inbounds)
@@ -196,7 +197,7 @@ func (s *InboundService) GetInboundsSlim(userId int) ([]*model.Inbound, error) {
 	db := database.GetDB()
 	var inbounds []*model.Inbound
 	err := db.Model(model.Inbound{}).Preload("ClientStats").Where("user_id = ?", userId).Order("id ASC").Find(&inbounds).Error
-	if err != nil && err != gorm.ErrRecordNotFound {
+	if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
 		return nil, err
 	}
 	s.annotateFallbackParents(db, inbounds)
@@ -319,7 +320,7 @@ func (s *InboundService) GetInboundOptions(userId int) ([]InboundOption, error)
 		Where("user_id = ?", userId).
 		Order("id ASC").
 		Scan(&rows).Error
-	if err != nil && err != gorm.ErrRecordNotFound {
+	if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
 		return nil, err
 	}
 	out := make([]InboundOption, 0, len(rows))
@@ -343,7 +344,7 @@ func (s *InboundService) GetAllInbounds() ([]*model.Inbound, error) {
 	db := database.GetDB()
 	var inbounds []*model.Inbound
 	err := db.Model(model.Inbound{}).Preload("ClientStats").Find(&inbounds).Error
-	if err != nil && err != gorm.ErrRecordNotFound {
+	if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
 		return nil, err
 	}
 	s.enrichClientStats(db, inbounds)
@@ -354,7 +355,7 @@ func (s *InboundService) GetInboundsByTrafficReset(period string) ([]*model.Inbo
 	db := database.GetDB()
 	var inbounds []*model.Inbound
 	err := db.Model(model.Inbound{}).Where("traffic_reset = ?", period).Find(&inbounds).Error
-	if err != nil && err != gorm.ErrRecordNotFound {
+	if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
 		return nil, err
 	}
 	return inbounds, nil
@@ -362,7 +363,7 @@ func (s *InboundService) GetInboundsByTrafficReset(period string) ([]*model.Inbo
 
 func (s *InboundService) GetClients(inbound *model.Inbound) ([]model.Client, error) {
 	settings := map[string][]model.Client{}
-	json.Unmarshal([]byte(inbound.Settings), &settings)
+	_ = json.Unmarshal([]byte(inbound.Settings), &settings)
 	if settings == nil {
 		return nil, fmt.Errorf("setting is null")
 	}
@@ -1348,7 +1349,7 @@ func (s *InboundService) GetInboundTags() (string, error) {
 	db := database.GetDB()
 	var inboundTags []string
 	err := db.Model(model.Inbound{}).Select("tag").Find(&inboundTags).Error
-	if err != nil && err != gorm.ErrRecordNotFound {
+	if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
 		return "", err
 	}
 	tags, _ := json.Marshal(inboundTags)
@@ -1359,7 +1360,7 @@ func (s *InboundService) GetClientReverseTags() (string, error) {
 	db := database.GetDB()
 	var inbounds []model.Inbound
 	err := db.Model(model.Inbound{}).Select("settings").Where("protocol = ?", "vless").Find(&inbounds).Error
-	if err != nil && err != gorm.ErrRecordNotFound {
+	if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
 		return "[]", err
 	}
 
@@ -1404,7 +1405,7 @@ func (s *InboundService) SearchInbounds(query string) ([]*model.Inbound, error)
 	db := database.GetDB()
 	var inbounds []*model.Inbound
 	err := db.Model(model.Inbound{}).Preload("ClientStats").Where("remark like ?", "%"+query+"%").Find(&inbounds).Error
-	if err != nil && err != gorm.ErrRecordNotFound {
+	if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
 		return nil, err
 	}
 	return inbounds, nil

+ 1 - 0
internal/web/service/inbound_clients.go

@@ -8,6 +8,7 @@ import (
 	"time"
 
 	"github.com/google/uuid"
+
 	"github.com/mhsanaei/3x-ui/v3/internal/database"
 	"github.com/mhsanaei/3x-ui/v3/internal/database/model"
 	"github.com/mhsanaei/3x-ui/v3/internal/logger"

+ 3 - 3
internal/web/service/inbound_disable.go

@@ -27,7 +27,7 @@ func (s *InboundService) disableInvalidInbounds(tx *gorm.DB) (bool, int64, error
 		if err != nil {
 			return false, 0, err
 		}
-		s.xrayApi.Init(p.GetAPIPort())
+		_ = s.xrayApi.Init(p.GetAPIPort())
 		for _, tag := range tags {
 			err1 := s.xrayApi.DelInbound(tag)
 			if err1 == nil {
@@ -141,7 +141,7 @@ func (s *InboundService) disableInvalidClients(tx *gorm.DB) (bool, int64, []int,
 	}
 
 	if p != nil && len(localTargets) > 0 {
-		s.xrayApi.Init(p.GetAPIPort())
+		_ = s.xrayApi.Init(p.GetAPIPort())
 		for _, t := range localTargets {
 			err1 := s.xrayApi.RemoveUser(t.Tag, t.Email)
 			if err1 == nil {
@@ -231,7 +231,7 @@ func (s *InboundService) markClientsDisabledInSettings(tx *gorm.DB, inboundID in
 		if _, hit := emails[email]; !hit {
 			continue
 		}
-		if cur, _ := entry["enable"].(bool); cur == false {
+		if cur, _ := entry["enable"].(bool); !cur {
 			continue
 		}
 		entry["enable"] = false

+ 6 - 5
internal/web/service/inbound_migration.go

@@ -2,6 +2,7 @@ package service
 
 import (
 	"encoding/json"
+	"errors"
 	"fmt"
 	"strconv"
 	"strings"
@@ -93,12 +94,12 @@ func (s *InboundService) MigrationRequirements() {
 	// Fix inbounds based problems
 	var inbounds []*model.Inbound
 	err = tx.Model(model.Inbound{}).Where("protocol IN (?)", []string{"vmess", "vless", "trojan", "shadowsocks", "hysteria"}).Find(&inbounds).Error
-	if err != nil && err != gorm.ErrRecordNotFound {
+	if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
 		return
 	}
 	for inbound_index := range inbounds {
 		settings := map[string]any{}
-		json.Unmarshal([]byte(inbounds[inbound_index].Settings), &settings)
+		_ = json.Unmarshal([]byte(inbounds[inbound_index].Settings), &settings)
 		if raw, exists := settings["clients"]; exists && raw == nil {
 			settings["clients"] = []any{}
 		}
@@ -117,7 +118,7 @@ func (s *InboundService) MigrationRequirements() {
 
 				// Convert string tgId to int64
 				if _, ok := c["tgId"]; ok {
-					var tgId any = c["tgId"]
+					tgId := c["tgId"]
 					if tgIdStr, ok2 := tgId.(string); ok2 {
 						tgIdInt64, err := strconv.ParseInt(strings.ReplaceAll(tgIdStr, " ", ""), 10, 64)
 						if err == nil {
@@ -170,7 +171,7 @@ func (s *InboundService) MigrationRequirements() {
 				var count int64
 				tx.Model(xray.ClientTraffic{}).Where("email = ?", modelClient.Email).Count(&count)
 				if count == 0 {
-					s.AddClientStat(tx, inbounds[inbound_index].Id, &modelClient)
+					_ = s.AddClientStat(tx, inbounds[inbound_index].Id, &modelClient)
 				}
 			}
 		}
@@ -212,7 +213,7 @@ func (s *InboundService) MigrationRequirements() {
 	for _, ep := range externalProxy {
 		var reverses any
 		var stream map[string]any
-		json.Unmarshal([]byte(ep.StreamSettings), &stream)
+		_ = json.Unmarshal([]byte(ep.StreamSettings), &stream)
 		if tlsSettings, ok := stream["tlsSettings"].(map[string]any); ok {
 			if settings, ok := tlsSettings["settings"].(map[string]any); ok {
 				if domains, ok := settings["domains"].([]any); ok {

+ 2 - 2
internal/web/service/inbound_node.go

@@ -999,7 +999,7 @@ func (s *InboundService) GetClientsLastOnline() (map[string]int64, error) {
 	db := database.GetDB()
 	var rows []xray.ClientTraffic
 	err := db.Model(&xray.ClientTraffic{}).Select("email, last_online").Find(&rows).Error
-	if err != nil && err != gorm.ErrRecordNotFound {
+	if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
 		return nil, err
 	}
 	result := make(map[string]int64, len(rows))
@@ -1029,7 +1029,7 @@ func (s *InboundService) FilterAndSortClientEmails(emails []string) ([]string, [
 	clients := make([]xray.ClientTraffic, 0, len(uniqEmails))
 	for _, batch := range chunkStrings(uniqEmails, sqliteMaxVars) {
 		var page []xray.ClientTraffic
-		if err := db.Where("email IN ?", batch).Find(&page).Error; err != nil && err != gorm.ErrRecordNotFound {
+		if err := db.Where("email IN ?", batch).Find(&page).Error; err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
 			return nil, nil, err
 		}
 		clients = append(clients, page...)

+ 8 - 7
internal/web/service/inbound_traffic.go

@@ -3,6 +3,7 @@ package service
 import (
 	"context"
 	"encoding/json"
+	"errors"
 	"fmt"
 	"strings"
 	"time"
@@ -224,7 +225,7 @@ func (s *InboundService) adjustTraffics(tx *gorm.DB, dbClientTraffics []*xray.Cl
 	}
 	for inbound_index := range inbounds {
 		settings := map[string]any{}
-		json.Unmarshal([]byte(inbounds[inbound_index].Settings), &settings)
+		_ = json.Unmarshal([]byte(inbounds[inbound_index].Settings), &settings)
 		clients, ok := settings["clients"].([]any)
 		if ok {
 			var newClients []any
@@ -357,7 +358,7 @@ func (s *InboundService) autoRenewClients(tx *gorm.DB) (bool, int64, error) {
 	}
 	for inbound_index := range inbounds {
 		settings := map[string]any{}
-		json.Unmarshal([]byte(inbounds[inbound_index].Settings), &settings)
+		_ = json.Unmarshal([]byte(inbounds[inbound_index].Settings), &settings)
 		clients, _ := settings["clients"].([]any)
 		if len(clients) == 0 {
 			continue
@@ -760,7 +761,7 @@ func (s *InboundService) DelDepletedClients(id int) (err error) {
 			continue
 		}
 		if len(newClients) == 0 {
-			s.DelInbound(inbound.Id)
+			_, _ = s.DelInbound(inbound.Id)
 			continue
 		}
 		settings["clients"] = newClients
@@ -827,7 +828,7 @@ func (s *InboundService) GetClientTrafficTgBot(tgId int64) ([]*xray.ClientTraffi
 
 	// Retrieve inbounds where settings contain the given tgId
 	err := db.Model(model.Inbound{}).Where("settings LIKE ?", fmt.Sprintf(`%%"tgId": %d%%`, tgId)).Find(&inbounds).Error
-	if err != nil && err != gorm.ErrRecordNotFound {
+	if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
 		logger.Errorf("Error retrieving inbounds with tgId %d: %v", tgId, err)
 		return nil, err
 	}
@@ -853,7 +854,7 @@ func (s *InboundService) GetClientTrafficTgBot(tgId int64) ([]*xray.ClientTraffi
 	for _, batch := range chunkStrings(uniqEmails, sqliteMaxVars) {
 		var page []*xray.ClientTraffic
 		if err = db.Model(xray.ClientTraffic{}).Where("email IN ?", batch).Find(&page).Error; err != nil {
-			if err == gorm.ErrRecordNotFound {
+			if errors.Is(err, gorm.ErrRecordNotFound) {
 				continue
 			}
 			logger.Errorf("Error retrieving ClientTraffic for emails %v: %v", batch, err)
@@ -1008,7 +1009,7 @@ func (s *InboundService) SearchClientTraffic(query string) (traffic *xray.Client
 	// Search for inbound settings that contain the query
 	err = db.Model(model.Inbound{}).Where("settings LIKE ?", "%\""+query+"\"%").First(inbound).Error
 	if err != nil {
-		if err == gorm.ErrRecordNotFound {
+		if errors.Is(err, gorm.ErrRecordNotFound) {
 			logger.Warningf("Inbound settings containing query %s not found: %v", query, err)
 			return nil, err
 		}
@@ -1041,7 +1042,7 @@ func (s *InboundService) SearchClientTraffic(query string) (traffic *xray.Client
 	// Retrieve ClientTraffic based on the found email
 	err = db.Model(xray.ClientTraffic{}).Where("email = ?", traffic.Email).First(traffic).Error
 	if err != nil {
-		if err == gorm.ErrRecordNotFound {
+		if errors.Is(err, gorm.ErrRecordNotFound) {
 			logger.Warningf("ClientTraffic for email %s not found: %v", traffic.Email, err)
 			return nil, err
 		}

+ 1 - 1
internal/web/service/inbound_update_tag_test.go

@@ -49,7 +49,7 @@ func TestUpdateInbound_RegeneratesAutoTagOnPortChange(t *testing.T) {
 // the returned object) which is what the save would use.
 func TestUpdateInbound_NodeTagKeepsPrefixWhenNodeIdOmitted(t *testing.T) {
 	setupConflictDB(t)
-	seedInboundConflictNode(t, "n1-in-443-tcp", "0.0.0.0", 443, model.VLESS, `{"network":"tcp"}`, `{"clients":[]}`, intPtr(1))
+	seedInboundConflictNode(t, "n1-in-443-tcp", "0.0.0.0", 443, model.VLESS, `{"network":"tcp"}`, `{"clients":[]}`, new(1))
 
 	var existing model.Inbound
 	if err := database.GetDB().Where("tag = ?", "n1-in-443-tcp").First(&existing).Error; err != nil {

+ 16 - 7
internal/web/service/integration/nord.go

@@ -1,6 +1,7 @@
 package integration
 
 import (
+	"context"
 	"encoding/json"
 	"fmt"
 	"io"
@@ -21,7 +22,11 @@ var nordHTTPClient = &http.Client{Timeout: 15 * time.Second}
 const maxResponseSize = 10 << 20
 
 func (s *NordService) GetCountries() (string, error) {
-	resp, err := nordHTTPClient.Get("https://api.nordvpn.com/v1/countries")
+	req, reqErr := http.NewRequestWithContext(context.Background(), http.MethodGet, "https://api.nordvpn.com/v1/countries", nil)
+	if reqErr != nil {
+		return "", reqErr
+	}
+	resp, err := nordHTTPClient.Do(req)
 	if err != nil {
 		return "", err
 	}
@@ -44,7 +49,11 @@ func (s *NordService) GetServers(countryId string) (string, error) {
 		}
 	}
 	url := fmt.Sprintf("https://api.nordvpn.com/v2/servers?limit=0&filters[servers_technologies][id]=35&filters[country_id]=%s", countryId)
-	resp, err := nordHTTPClient.Get(url)
+	req, reqErr := http.NewRequestWithContext(context.Background(), http.MethodGet, url, nil)
+	if reqErr != nil {
+		return "", reqErr
+	}
+	resp, err := nordHTTPClient.Do(req)
 	if err != nil {
 		return "", err
 	}
@@ -89,7 +98,7 @@ func (s *NordService) SetKey(privateKey string) (string, error) {
 		"token":       "",
 	}
 	data, _ := json.Marshal(nordData)
-	err := s.SettingService.SetNord(string(data))
+	err := s.SetNord(string(data))
 	if err != nil {
 		return "", err
 	}
@@ -98,7 +107,7 @@ func (s *NordService) SetKey(privateKey string) (string, error) {
 
 func (s *NordService) GetCredentials(token string) (string, error) {
 	url := "https://api.nordvpn.com/v1/users/services/credentials"
-	req, err := http.NewRequest("GET", url, nil)
+	req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, url, nil)
 	if err != nil {
 		return "", err
 	}
@@ -134,7 +143,7 @@ func (s *NordService) GetCredentials(token string) (string, error) {
 		"token":       token,
 	}
 	data, _ := json.Marshal(nordData)
-	err = s.SettingService.SetNord(string(data))
+	err = s.SetNord(string(data))
 	if err != nil {
 		return "", err
 	}
@@ -143,9 +152,9 @@ func (s *NordService) GetCredentials(token string) (string, error) {
 }
 
 func (s *NordService) GetNordData() (string, error) {
-	return s.SettingService.GetNord()
+	return s.GetNord()
 }
 
 func (s *NordService) DelNordData() error {
-	return s.SettingService.SetNord("")
+	return s.SetNord("")
 }

+ 9 - 8
internal/web/service/integration/warp.go

@@ -2,6 +2,7 @@ package integration
 
 import (
 	"bytes"
+	"context"
 	"encoding/json"
 	"fmt"
 	"io"
@@ -27,11 +28,11 @@ const (
 )
 
 func (s *WarpService) GetWarpData() (string, error) {
-	return s.SettingService.GetWarp()
+	return s.GetWarp()
 }
 
 func (s *WarpService) DelWarpData() error {
-	return s.SettingService.SetWarp("")
+	return s.SetWarp("")
 }
 
 func (s *WarpService) GetWarpConfig() (string, error) {
@@ -41,7 +42,7 @@ func (s *WarpService) GetWarpConfig() (string, error) {
 	}
 
 	url := fmt.Sprintf("%s/reg/%s", warpAPIBase, warpData["device_id"])
-	req, err := http.NewRequest(http.MethodGet, url, nil)
+	req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, url, nil)
 	if err != nil {
 		return "", err
 	}
@@ -67,7 +68,7 @@ func (s *WarpService) RegWarp(secretKey string, publicKey string) (string, error
 		return "", err
 	}
 
-	req, err := http.NewRequest(http.MethodPost, warpAPIBase+"/reg", bytes.NewReader(reqBody))
+	req, err := http.NewRequestWithContext(context.Background(), http.MethodPost, warpAPIBase+"/reg", bytes.NewReader(reqBody))
 	if err != nil {
 		return "", err
 	}
@@ -116,7 +117,7 @@ func (s *WarpService) RegWarp(secretKey string, publicKey string) (string, error
 	if err != nil {
 		return "", err
 	}
-	if err := s.SettingService.SetWarp(string(warpJSON)); err != nil {
+	if err := s.SetWarp(string(warpJSON)); err != nil {
 		return "", err
 	}
 
@@ -142,7 +143,7 @@ func (s *WarpService) SetWarpLicense(license string) (string, error) {
 		return "", err
 	}
 
-	req, err := http.NewRequest(http.MethodPut, url, bytes.NewReader(reqBody))
+	req, err := http.NewRequestWithContext(context.Background(), http.MethodPut, url, bytes.NewReader(reqBody))
 	if err != nil {
 		return "", err
 	}
@@ -167,7 +168,7 @@ func (s *WarpService) SetWarpLicense(license string) (string, error) {
 	if err != nil {
 		return "", err
 	}
-	if err := s.SettingService.SetWarp(string(newWarpData)); err != nil {
+	if err := s.SetWarp(string(newWarpData)); err != nil {
 		return "", err
 	}
 	return string(newWarpData), nil
@@ -213,7 +214,7 @@ func (s *WarpService) ChangeWarpIP() (string, error) {
 
 // loadWarpCreds reads the stored warp JSON and ensures access_token + device_id are set.
 func (s *WarpService) loadWarpCreds() (map[string]string, error) {
-	warp, err := s.SettingService.GetWarp()
+	warp, err := s.GetWarp()
 	if err != nil {
 		return nil, err
 	}

+ 1 - 1
internal/web/service/node.go

@@ -832,7 +832,7 @@ func (s *NodeService) withOutboundBridge(nodeID int, outboundTag string, fn func
 		return
 	}
 
-	listener, err := net.Listen("tcp", "127.0.0.1:0")
+	listener, err := (&net.ListenConfig{}).Listen(context.Background(), "tcp", "127.0.0.1:0")
 	if err != nil {
 		fn("")
 		return

+ 3 - 0
internal/web/service/node_bulk_dispatch_test.go

@@ -7,6 +7,7 @@ import (
 	"testing"
 
 	"github.com/google/uuid"
+
 	"github.com/mhsanaei/3x-ui/v3/internal/database"
 	"github.com/mhsanaei/3x-ui/v3/internal/database/model"
 	"github.com/mhsanaei/3x-ui/v3/internal/web/runtime"
@@ -33,10 +34,12 @@ func (f *fakeNodeRuntime) UpdateUser(context.Context, *model.Inbound, string, mo
 	f.updateUser.Add(1)
 	return nil
 }
+
 func (f *fakeNodeRuntime) DeleteUser(context.Context, *model.Inbound, string) error {
 	f.deleteUser.Add(1)
 	return nil
 }
+
 func (f *fakeNodeRuntime) AddClient(context.Context, *model.Inbound, model.Client) error {
 	f.addClient.Add(1)
 	return nil

+ 2 - 1
internal/web/service/node_client_traffic_sum_test.go

@@ -5,11 +5,12 @@ import (
 	"path/filepath"
 	"testing"
 
+	"gorm.io/gorm"
+
 	"github.com/mhsanaei/3x-ui/v3/internal/database"
 	"github.com/mhsanaei/3x-ui/v3/internal/database/model"
 	"github.com/mhsanaei/3x-ui/v3/internal/web/runtime"
 	"github.com/mhsanaei/3x-ui/v3/internal/xray"
-	"gorm.io/gorm"
 )
 
 func initTrafficTestDB(t *testing.T) *gorm.DB {

+ 1 - 0
internal/web/service/node_mtls_test.go

@@ -6,6 +6,7 @@ import (
 	"testing"
 
 	"github.com/go-playground/validator/v10"
+
 	"github.com/mhsanaei/3x-ui/v3/internal/database/model"
 )
 

+ 2 - 1
internal/web/service/outbound/outbound.go

@@ -1,6 +1,7 @@
 package outbound
 
 import (
+	"context"
 	"encoding/json"
 	"fmt"
 	"net"
@@ -196,7 +197,7 @@ func (s *OutboundService) testOutboundTCP(outboundJSON string) (*TestOutboundRes
 func probeTCPEndpoint(endpoint string, timeout time.Duration) TestEndpointResult {
 	r := TestEndpointResult{Address: endpoint}
 	start := time.Now()
-	conn, err := net.DialTimeout("tcp", endpoint, timeout)
+	conn, err := (&net.Dialer{Timeout: timeout}).DialContext(context.Background(), "tcp", endpoint)
 	r.Delay = time.Since(start).Milliseconds()
 	if err != nil {
 		r.Error = err.Error()

+ 8 - 8
internal/web/service/outbound/probe_http.go

@@ -101,7 +101,7 @@ func (s *OutboundService) TestOutbound(outboundJSON string, testURL string, allO
 func (s *OutboundService) TestOutbounds(outboundsJSON string, testURL string, allOutboundsJSON string, mode string) ([]*TestOutboundResult, error) {
 	var raw []json.RawMessage
 	if err := json.Unmarshal([]byte(outboundsJSON), &raw); err != nil {
-		return nil, fmt.Errorf("invalid outbounds JSON: %v", err)
+		return nil, fmt.Errorf("invalid outbounds JSON: %w", err)
 	}
 	if len(raw) > maxBatchItems {
 		return nil, fmt.Errorf("too many outbounds in one request (max %d)", maxBatchItems)
@@ -253,7 +253,7 @@ func (s *OutboundService) testOutboundsParsed(items []map[string]any, testURL st
 func runHTTPProbeBatch(items []*httpBatchItem, allOutbounds []any, testURL string) (retryPerItem bool, err error) {
 	ports, release, err := reserveLoopbackPorts(len(items))
 	if err != nil {
-		return false, fmt.Errorf("Failed to reserve test ports: %v", err)
+		return false, fmt.Errorf("Failed to reserve test ports: %w", err)
 	}
 	defer release()
 
@@ -261,14 +261,14 @@ func runHTTPProbeBatch(items []*httpBatchItem, allOutbounds []any, testURL strin
 
 	configPath, err := createTestConfigPath()
 	if err != nil {
-		return false, fmt.Errorf("Failed to create test config path: %v", err)
+		return false, fmt.Errorf("Failed to create test config path: %w", err)
 	}
 	defer os.Remove(configPath)
 
 	proc := newBatchProcess(cfg, configPath)
 	defer func() {
 		if proc.IsRunning() {
-			proc.Stop()
+			_ = proc.Stop()
 		}
 	}()
 
@@ -279,9 +279,9 @@ func runHTTPProbeBatch(items []*httpBatchItem, allOutbounds []any, testURL strin
 	if err := proc.Start(); err != nil {
 		if errors.Is(err, fs.ErrNotExist) {
 			// Binary missing — per-item retries would all fail the same way.
-			return false, fmt.Errorf("Failed to start test xray instance: %v", err)
+			return false, fmt.Errorf("Failed to start test xray instance: %w", err)
 		}
-		return true, fmt.Errorf("Failed to start test xray instance: %v", err)
+		return true, fmt.Errorf("Failed to start test xray instance: %w", err)
 	}
 
 	if err := waitForPortsReady(proc, ports, batchPortsReadyTimeout); err != nil {
@@ -330,7 +330,7 @@ func waitForPortsReady(proc batchProcess, ports []int, timeout time.Duration) *p
 			if !proc.IsRunning() {
 				return &portsReadyError{msg: "Xray process exited: " + proc.GetResult(), exited: true}
 			}
-			conn, err := net.DialTimeout("tcp", fmt.Sprintf("127.0.0.1:%d", port), 100*time.Millisecond)
+			conn, err := (&net.Dialer{Timeout: 100 * time.Millisecond}).DialContext(context.Background(), "tcp", fmt.Sprintf("127.0.0.1:%d", port))
 			if err == nil {
 				conn.Close()
 				break
@@ -529,7 +529,7 @@ func reserveLoopbackPorts(n int) ([]int, func(), error) {
 	}
 	ports := make([]int, 0, n)
 	for range n {
-		l, err := net.Listen("tcp", "127.0.0.1:0")
+		l, err := (&net.ListenConfig{}).Listen(context.Background(), "tcp", "127.0.0.1:0")
 		if err != nil {
 			release()
 			return nil, nil, err

+ 2 - 2
internal/web/service/outbound_subscription.go

@@ -281,7 +281,7 @@ func (s *OutboundSubscriptionService) fetchAndStore(sub *model.OutboundSubscript
 		return rejectPrivateHost(ctx, req.URL.Hostname())
 	}
 
-	req, err := http.NewRequest("GET", sub.Url, nil)
+	req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, sub.Url, nil)
 	if err != nil {
 		s.recordError(sub, err)
 		return nil, err
@@ -295,7 +295,7 @@ func (s *OutboundSubscriptionService) fetchAndStore(sub *model.OutboundSubscript
 	}
 	defer resp.Body.Close()
 
-	if resp.StatusCode != 200 {
+	if resp.StatusCode != http.StatusOK {
 		err := fmt.Errorf("http %d", resp.StatusCode)
 		s.recordError(sub, err)
 		return nil, err

+ 14 - 5
internal/web/service/panel/panel.go

@@ -1,6 +1,7 @@
 package panel
 
 import (
+	"context"
 	"encoding/json"
 	"fmt"
 	"io"
@@ -157,7 +158,7 @@ func (s *PanelService) startUpdate(useDev bool) error {
 
 	if systemdRun, err := exec.LookPath("systemd-run"); err == nil {
 		unitName := fmt.Sprintf("x-ui-web-update-%d", time.Now().Unix())
-		cmd := exec.Command(systemdRun,
+		cmd := exec.CommandContext(context.Background(), systemdRun,
 			"--unit", unitName,
 			"--setenv", "XUI_MAIN_FOLDER="+mainFolder,
 			"--setenv", "XUI_SERVICE="+serviceFolder,
@@ -179,7 +180,7 @@ func (s *PanelService) startUpdate(useDev bool) error {
 		}
 	}
 
-	cmd := exec.Command(bash, "-lc", updateScript)
+	cmd := exec.CommandContext(context.Background(), bash, "-lc", updateScript)
 	cmd.Env = append(os.Environ(),
 		"XUI_MAIN_FOLDER="+mainFolder,
 		"XUI_SERVICE="+serviceFolder,
@@ -199,7 +200,11 @@ func (s *PanelService) startUpdate(useDev bool) error {
 
 func downloadPanelUpdater() (string, error) {
 	client := (&service.SettingService{}).NewProxiedHTTPClient(15 * time.Second)
-	resp, err := client.Get(panelUpdaterURL)
+	req, reqErr := http.NewRequestWithContext(context.Background(), http.MethodGet, panelUpdaterURL, nil)
+	if reqErr != nil {
+		return "", fmt.Errorf("download panel updater: %w", reqErr)
+	}
+	resp, err := client.Do(req)
 	if err != nil {
 		return "", fmt.Errorf("download panel updater: %w", err)
 	}
@@ -228,7 +233,7 @@ func downloadPanelUpdater() (string, error) {
 	if n > maxPanelUpdaterBytes {
 		return "", fmt.Errorf("panel updater exceeds %d bytes", maxPanelUpdaterBytes)
 	}
-	if err := file.Chmod(0700); err != nil {
+	if err := file.Chmod(0o700); err != nil {
 		return "", err
 	}
 	ok = true
@@ -254,7 +259,11 @@ func fetchPanelRelease(tag string) (*service.Release, error) {
 		url = "https://api.github.com/repos/MHSanaei/3x-ui/releases/tags/" + tag
 	}
 	client := (&service.SettingService{}).NewProxiedHTTPClient(10 * time.Second)
-	resp, err := client.Get(url)
+	req, reqErr := http.NewRequestWithContext(context.Background(), http.MethodGet, url, nil)
+	if reqErr != nil {
+		return nil, reqErr
+	}
+	resp, err := client.Do(req)
 	if err != nil {
 		return nil, err
 	}

+ 6 - 7
internal/web/service/panel/user.go

@@ -3,14 +3,15 @@ package panel
 import (
 	"errors"
 
+	"github.com/xlzd/gotp"
+	"gorm.io/gorm"
+
 	"github.com/mhsanaei/3x-ui/v3/internal/database"
 	"github.com/mhsanaei/3x-ui/v3/internal/database/model"
 	"github.com/mhsanaei/3x-ui/v3/internal/logger"
 	"github.com/mhsanaei/3x-ui/v3/internal/util/crypto"
 	ldaputil "github.com/mhsanaei/3x-ui/v3/internal/util/ldap"
 	"github.com/mhsanaei/3x-ui/v3/internal/web/service"
-	"github.com/xlzd/gotp"
-	"gorm.io/gorm"
 )
 
 // UserService provides business logic for user management and authentication.
@@ -43,7 +44,7 @@ func (s *UserService) CheckUser(username string, password string, twoFactorCode
 		Where("username = ?", username).
 		First(user).
 		Error
-	if err == gorm.ErrRecordNotFound {
+	if errors.Is(err, gorm.ErrRecordNotFound) {
 		return nil, errors.New("invalid credentials")
 	} else if err != nil {
 		logger.Warning("check user err:", err)
@@ -89,7 +90,6 @@ func (s *UserService) CheckUser(username string, password string, twoFactorCode
 
 	if twoFactorEnable {
 		twoFactorToken, err := s.settingService.GetTwoFactorToken()
-
 		if err != nil {
 			logger.Warning("check two factor token err:", err)
 			return nil, err
@@ -114,7 +114,6 @@ func (s *UserService) BumpLoginEpoch() error {
 func (s *UserService) UpdateUser(id int, username string, password string) error {
 	db := database.GetDB()
 	hashedPassword, err := crypto.HashPasswordAsBcrypt(password)
-
 	if err != nil {
 		return err
 	}
@@ -125,8 +124,8 @@ func (s *UserService) UpdateUser(id int, username string, password string) error
 	}
 
 	if twoFactorEnable {
-		s.settingService.SetTwoFactorEnable(false)
-		s.settingService.SetTwoFactorToken("")
+		_ = s.settingService.SetTwoFactorEnable(false)
+		_ = s.settingService.SetTwoFactorToken("")
 	}
 
 	return db.Model(model.User{}).

+ 4 - 4
internal/web/service/panel/websocket.go

@@ -65,7 +65,7 @@ func (s *WebSocketService) readPump(client *websocket.Client, conn *ws.Conn) {
 	}()
 
 	conn.SetReadLimit(wsClientReadLimit)
-	conn.SetReadDeadline(time.Now().Add(wsPongWait))
+	_ = conn.SetReadDeadline(time.Now().Add(wsPongWait))
 	conn.SetPongHandler(func(string) error {
 		return conn.SetReadDeadline(time.Now().Add(wsPongWait))
 	})
@@ -94,9 +94,9 @@ func (s *WebSocketService) writePump(client *websocket.Client, conn *ws.Conn) {
 	for {
 		select {
 		case msg, ok := <-client.Send:
-			conn.SetWriteDeadline(time.Now().Add(wsWriteWait))
+			_ = conn.SetWriteDeadline(time.Now().Add(wsWriteWait))
 			if !ok {
-				conn.WriteMessage(ws.CloseMessage, []byte{})
+				_ = conn.WriteMessage(ws.CloseMessage, []byte{})
 				return
 			}
 			if err := conn.WriteMessage(ws.TextMessage, msg); err != nil {
@@ -105,7 +105,7 @@ func (s *WebSocketService) writePump(client *websocket.Client, conn *ws.Conn) {
 			}
 
 		case <-ticker.C:
-			conn.SetWriteDeadline(time.Now().Add(wsWriteWait))
+			_ = conn.SetWriteDeadline(time.Now().Add(wsWriteWait))
 			if err := conn.WriteMessage(ws.PingMessage, nil); err != nil {
 				logger.Debugf("WebSocket ping error for client %s: %v", client.ID, err)
 				return

+ 7 - 9
internal/web/service/port_conflict_test.go

@@ -6,10 +6,11 @@ import (
 	"sync"
 	"testing"
 
+	"github.com/op/go-logging"
+
 	"github.com/mhsanaei/3x-ui/v3/internal/database"
 	"github.com/mhsanaei/3x-ui/v3/internal/database/model"
 	xuilogger "github.com/mhsanaei/3x-ui/v3/internal/logger"
-	"github.com/op/go-logging"
 )
 
 // the panel logger is a process-wide singleton. init it once per test
@@ -57,9 +58,6 @@ func seedInboundConflictNode(t *testing.T, tag, listen string, port int, protoco
 	}
 }
 
-//go:fix inline
-func intPtr(v int) *int { return new(v) }
-
 func TestInboundTransports(t *testing.T) {
 	cases := []struct {
 		name           string
@@ -410,7 +408,7 @@ func TestResolveInboundTag_RespectsCallerTagWhenFree(t *testing.T) {
 		Port:           5000,
 		Protocol:       model.VLESS,
 		StreamSettings: `{"network":"tcp"}`,
-		NodeID:         intPtr(1),
+		NodeID:         new(1),
 	}
 	got, err := svc.resolveInboundTag(pushed, 0)
 	if err != nil {
@@ -481,7 +479,7 @@ func TestGenerateInboundTag_NodePrefix(t *testing.T) {
 		Listen:   "0.0.0.0",
 		Port:     443,
 		Protocol: model.VLESS,
-		NodeID:   intPtr(1),
+		NodeID:   new(1),
 	}
 	got, err := svc.generateInboundTag(in, 0)
 	if err != nil {
@@ -503,7 +501,7 @@ func TestGenerateInboundTag_NodePrefixedDoesNotCollideWithLocal(t *testing.T) {
 		Listen:   "0.0.0.0",
 		Port:     443,
 		Protocol: model.VLESS,
-		NodeID:   intPtr(1),
+		NodeID:   new(1),
 	}
 	got, err := svc.generateInboundTag(in, 0)
 	if err != nil {
@@ -653,7 +651,7 @@ func TestIsAutoGeneratedTag(t *testing.T) {
 		{"canonical", "in-443-tcp", 443, nil, tcp, true},
 		{"canonical udp", "in-443-udp", 443, nil, transportUDP, true},
 		{"dedup suffix", "in-443-tcp-2", 443, nil, tcp, true},
-		{"node prefixed", "n1-in-443-tcp", 443, intPtr(1), tcp, true},
+		{"node prefixed", "n1-in-443-tcp", 443, new(1), tcp, true},
 		{"legacy listen-scoped is now custom", "in-127.0.0.1:443-tcp", 443, nil, tcp, false},
 		{"custom tag", "my-cool-tag", 443, nil, tcp, false},
 		{"stale port", "in-443-tcp", 8443, nil, tcp, false},
@@ -708,7 +706,7 @@ func TestCheckPortConflict_ReservedAPIPortAllowedOnNode(t *testing.T) {
 		Port:           defaultXrayAPIPort,
 		Protocol:       model.VLESS,
 		StreamSettings: `{"network":"tcp"}`,
-		NodeID:         intPtr(1),
+		NodeID:         new(1),
 	}
 	if got, err := svc.checkPortConflict(candidate, 0); err != nil || got != nil {
 		t.Fatalf("node inbound on the reserved API port must be allowed; got=%v err=%v", got, err)

+ 37 - 20
internal/web/service/server.go

@@ -4,6 +4,7 @@ import (
 	"archive/zip"
 	"bufio"
 	"bytes"
+	"context"
 	"crypto/sha256"
 	"crypto/x509"
 	"encoding/hex"
@@ -233,7 +234,7 @@ func (s *ServerService) isFail2banInstalled() bool {
 		return s.fail2banInstalled
 	}
 
-	err := exec.Command("fail2ban-client", "-h").Run()
+	err := exec.CommandContext(context.Background(), "fail2ban-client", "-h").Run()
 	s.fail2banInstalled = err == nil
 	s.fail2banCheckedAt = time.Now()
 	return s.fail2banInstalled
@@ -351,7 +352,11 @@ func getPublicIP(url string) string {
 		Timeout: 3 * time.Second,
 	}
 
-	resp, err := client.Get(url)
+	req, reqErr := http.NewRequestWithContext(context.Background(), http.MethodGet, url, nil)
+	if reqErr != nil {
+		return "N/A"
+	}
+	resp, err := client.Do(req)
 	if err != nil {
 		return "N/A"
 	}
@@ -772,7 +777,11 @@ func (s *ServerService) GetXrayVersions() ([]string, error) {
 		bufferSize = 8192
 	)
 
-	resp, err := s.settingService.NewProxiedHTTPClient(10 * time.Second).Get(XrayURL)
+	req, reqErr := http.NewRequestWithContext(context.Background(), http.MethodGet, XrayURL, nil)
+	if reqErr != nil {
+		return nil, reqErr
+	}
+	resp, err := s.settingService.NewProxiedHTTPClient(10 * time.Second).Do(req)
 	if err != nil {
 		return nil, err
 	}
@@ -872,7 +881,11 @@ func (s *ServerService) downloadXRay(version string) (string, error) {
 	fileName := fmt.Sprintf("Xray-%s-%s.zip", osName, arch)
 	url := fmt.Sprintf("https://github.com/XTLS/Xray-core/releases/download/%s/%s", version, fileName)
 	client := s.settingService.NewProxiedHTTPClient(60 * time.Second)
-	resp, err := client.Get(url)
+	req, reqErr := http.NewRequestWithContext(context.Background(), http.MethodGet, url, nil)
+	if reqErr != nil {
+		return "", reqErr
+	}
+	resp, err := client.Do(req)
 	if err != nil {
 		return "", err
 	}
@@ -934,7 +947,11 @@ func (s *ServerService) downloadXRay(version string) (string, error) {
 // fetchXrayDigestSHA256 downloads the .dgst sidecar XTLS publishes next to each
 // release asset and returns the SHA2-256 hex digest it lists.
 func (s *ServerService) fetchXrayDigestSHA256(client *http.Client, dgstURL string) (string, error) {
-	resp, err := client.Get(dgstURL)
+	req, reqErr := http.NewRequestWithContext(context.Background(), http.MethodGet, dgstURL, nil)
+	if reqErr != nil {
+		return "", fmt.Errorf("download xray checksum: %w", reqErr)
+	}
+	resp, err := client.Do(req)
 	if err != nil {
 		return "", fmt.Errorf("download xray checksum: %w", err)
 	}
@@ -1009,7 +1026,7 @@ func (s *ServerService) UpdateXray(version string) error {
 			return err
 		}
 		defer zipFile.Close()
-		if err := os.MkdirAll(filepath.Dir(fileName), 0755); err != nil {
+		if err := os.MkdirAll(filepath.Dir(fileName), 0o755); err != nil {
 			return err
 		}
 		tmpFile, err := os.CreateTemp(filepath.Dir(fileName), ".xray-*")
@@ -1031,7 +1048,7 @@ func (s *ServerService) UpdateXray(version string) error {
 		if n > maxXrayBinaryBytes {
 			return fmt.Errorf("xray binary exceeds %d bytes", maxXrayBinaryBytes)
 		}
-		if err := tmpFile.Chmod(0755); err != nil {
+		if err := tmpFile.Chmod(0o755); err != nil {
 			return err
 		}
 		if err := tmpFile.Close(); err != nil {
@@ -1099,7 +1116,7 @@ func (s *ServerService) GetLogs(count string, level string, syslog string) []str
 		}
 
 		// Use hardcoded command with validated parameters
-		cmd := exec.Command("journalctl", "-u", "x-ui", "--no-pager", "-n", strconv.Itoa(countInt), "-p", level)
+		cmd := exec.CommandContext(context.Background(), "journalctl", "-u", "x-ui", "--no-pager", "-n", strconv.Itoa(countInt), "-p", level)
 		var out bytes.Buffer
 		cmd.Stdout = &out
 		err = cmd.Run()
@@ -1121,8 +1138,8 @@ func (s *ServerService) GetXrayLogs(
 	showBlocked string,
 	showProxy string,
 	freedoms []string,
-	blackholes []string) []LogEntry {
-
+	blackholes []string,
+) []LogEntry {
 	const (
 		Direct = iota
 		Blocked
@@ -1149,12 +1166,12 @@ func (s *ServerService) GetXrayLogs(
 		line := strings.TrimSpace(scanner.Text())
 
 		if line == "" || strings.Contains(line, "api -> api") {
-			//skipping empty lines and api calls
+			// skipping empty lines and api calls
 			continue
 		}
 
 		if filter != "" && !strings.Contains(line, filter) {
-			//applying filter if it's not empty
+			// applying filter if it's not empty
 			continue
 		}
 
@@ -1580,7 +1597,7 @@ func (s *ServerService) exportPostgresDB() ([]byte, error) {
 	if err != nil {
 		return nil, common.NewErrorf("invalid PostgreSQL DSN: %v", err)
 	}
-	cmd := exec.Command(bin, "--format=custom", "--no-owner", "--no-privileges", "--dbname", dbname)
+	cmd := exec.CommandContext(context.Background(), bin, "--format=custom", "--no-owner", "--no-privileges", "--dbname", dbname)
 	cmd.Env = env
 	var out, stderr bytes.Buffer
 	cmd.Stdout = &out
@@ -1642,7 +1659,7 @@ func (s *ServerService) importPostgresDB(file multipart.File) error {
 		logger.Warningf("Failed to close existing DB before restore: %v", errClose)
 	}
 
-	cmd := exec.Command(bin,
+	cmd := exec.CommandContext(context.Background(), bin,
 		"--clean", "--if-exists", "--no-owner", "--no-privileges",
 		"--single-transaction", "--dbname", dbname, tempPath,
 	)
@@ -1721,7 +1738,7 @@ func (s *ServerService) UpdateGeofile(fileName string) error {
 
 	downloadFile := func(url, destPath string) error {
 		var req *http.Request
-		req, err := http.NewRequest("GET", url, nil)
+		req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, url, nil)
 		if err != nil {
 			return common.NewErrorf("Failed to create HTTP request for %s: %v", url, err)
 		}
@@ -1818,7 +1835,7 @@ func (s *ServerService) UpdateGeofile(fileName string) error {
 
 func (s *ServerService) GetNewX25519Cert() (any, error) {
 	// Run the command
-	cmd := exec.Command(xray.GetBinaryPath(), "x25519")
+	cmd := exec.CommandContext(context.Background(), xray.GetBinaryPath(), "x25519")
 	var out bytes.Buffer
 	cmd.Stdout = &out
 	err := cmd.Run()
@@ -1844,7 +1861,7 @@ func (s *ServerService) GetNewX25519Cert() (any, error) {
 
 func (s *ServerService) GetNewmldsa65() (any, error) {
 	// Run the command
-	cmd := exec.Command(xray.GetBinaryPath(), "mldsa65")
+	cmd := exec.CommandContext(context.Background(), xray.GetBinaryPath(), "mldsa65")
 	var out bytes.Buffer
 	cmd.Stdout = &out
 	err := cmd.Run()
@@ -2048,7 +2065,7 @@ func (s *ServerService) GetRemoteCertHash(server string) ([]string, error) {
 
 func (s *ServerService) GetNewEchCert(sni string) (any, error) {
 	// Run the command
-	cmd := exec.Command(xray.GetBinaryPath(), "tls", "ech", "--serverName", sni)
+	cmd := exec.CommandContext(context.Background(), xray.GetBinaryPath(), "tls", "ech", "--serverName", sni)
 	var out bytes.Buffer
 	cmd.Stdout = &out
 	err := cmd.Run()
@@ -2071,7 +2088,7 @@ func (s *ServerService) GetNewEchCert(sni string) (any, error) {
 }
 
 func (s *ServerService) GetNewVlessEnc() (any, error) {
-	cmd := exec.Command(xray.GetBinaryPath(), "vlessenc")
+	cmd := exec.CommandContext(context.Background(), xray.GetBinaryPath(), "vlessenc")
 	var out bytes.Buffer
 	cmd.Stdout = &out
 	if err := cmd.Run(); err != nil {
@@ -2166,7 +2183,7 @@ func (s *ServerService) GetNewUUID() (map[string]string, error) {
 
 func (s *ServerService) GetNewmlkem768() (any, error) {
 	// Run the command
-	cmd := exec.Command(xray.GetBinaryPath(), "mlkem768")
+	cmd := exec.CommandContext(context.Background(), xray.GetBinaryPath(), "mlkem768")
 	var out bytes.Buffer
 	cmd.Stdout = &out
 	err := cmd.Run()

+ 1 - 0
internal/web/service/setting.go

@@ -14,6 +14,7 @@ import (
 	"time"
 
 	"github.com/google/uuid"
+
 	"github.com/mhsanaei/3x-ui/v3/internal/config"
 	"github.com/mhsanaei/3x-ui/v3/internal/database"
 	"github.com/mhsanaei/3x-ui/v3/internal/database/model"

+ 1 - 0
internal/web/service/sync_scale_postgres_test.go

@@ -8,6 +8,7 @@ import (
 	"time"
 
 	"github.com/google/uuid"
+
 	"github.com/mhsanaei/3x-ui/v3/internal/database"
 	"github.com/mhsanaei/3x-ui/v3/internal/database/model"
 

+ 2 - 2
internal/web/service/tgbot/tgbot.go

@@ -283,7 +283,7 @@ func (t *Tgbot) Start(i18nFS embed.FS) error {
 				logger.Warning("Failed to parse admin ID from Telegram bot chat ID:", err)
 				return err
 			}
-			parsedAdminIds = append(parsedAdminIds, int64(id))
+			parsedAdminIds = append(parsedAdminIds, id)
 		}
 	}
 	tgBotMutex.Lock()
@@ -472,7 +472,7 @@ func StopBot() {
 	userStateMgr.reset()
 
 	if handler != nil {
-		handler.Stop()
+		_ = handler.Stop()
 	}
 
 	if cancel != nil {

+ 9 - 10
internal/web/service/tgbot/tgbot_client.go

@@ -74,13 +74,13 @@ func (t *Tgbot) BuildClientDraftMessage() string {
 
 	var b strings.Builder
 	b.WriteString("📝 *New client draft*\r\n")
-	b.WriteString(fmt.Sprintf("📧 Email: `%s`\r\n", client_Email))
-	b.WriteString(fmt.Sprintf("🔗 Attached: %s\r\n", attached))
-	b.WriteString(fmt.Sprintf("📊 Traffic: %s\r\n", traffic))
-	b.WriteString(fmt.Sprintf("📅 Expire: %s\r\n", expiry))
-	b.WriteString(fmt.Sprintf("🔢 IP limit: %s\r\n", ipLimit))
-	b.WriteString(fmt.Sprintf("👤 TG user: %s\r\n", tgID))
-	b.WriteString(fmt.Sprintf("💬 Comment: %s\r\n", comment))
+	fmt.Fprintf(&b, "📧 Email: `%s`\r\n", client_Email)
+	fmt.Fprintf(&b, "🔗 Attached: %s\r\n", attached)
+	fmt.Fprintf(&b, "📊 Traffic: %s\r\n", traffic)
+	fmt.Fprintf(&b, "📅 Expire: %s\r\n", expiry)
+	fmt.Fprintf(&b, "🔢 IP limit: %s\r\n", ipLimit)
+	fmt.Fprintf(&b, "👤 TG user: %s\r\n", tgID)
+	fmt.Fprintf(&b, "💬 Comment: %s\r\n", comment)
 	return b.String()
 }
 
@@ -216,7 +216,6 @@ func (t *Tgbot) buildSubscriptionURLs(email string) (string, string, error) {
 		}
 		subJsonURL = fmt.Sprintf("%s%s", subJsonURI, client.SubID)
 	} else {
-
 		subJsonURL = fmt.Sprintf("%s://%s%s%s", scheme, host, subJsonPath, client.SubID)
 	}
 
@@ -258,7 +257,7 @@ func (t *Tgbot) sendClientIndividualLinks(chatId int64, email string) {
 	}
 
 	// Try to fetch raw subscription links. Prefer plain text response.
-	req, err := http.NewRequest("GET", subURL, nil)
+	req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, subURL, nil)
 	if err != nil {
 		t.SendMsgToTgbot(chatId, t.I18nBot("tgbot.answers.errorOperation")+"\r\n"+err.Error())
 		return
@@ -376,7 +375,7 @@ func (t *Tgbot) sendClientQRLinks(chatId int64, email string) {
 
 	// Also generate a few individual links' QRs (first up to 5)
 	subPageURL := subURL
-	req, err := http.NewRequest("GET", subPageURL, nil)
+	req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, subPageURL, nil)
 	if err == nil {
 		req.Header.Set("Accept", "text/plain, */*;q=0.1")
 		ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)

+ 1 - 5
internal/web/service/tgbot/tgbot_inbound.go

@@ -31,7 +31,7 @@ func (t *Tgbot) getInboundUsages() string {
 
 		clients, listErr := t.clientService.ListForInbound(nil, inbound.Id)
 		if listErr == nil {
-			info.WriteString(fmt.Sprintf("👥 Clients: %d\r\n", len(clients)))
+			fmt.Fprintf(&info, "👥 Clients: %d\r\n", len(clients))
 		}
 
 		if inbound.ExpiryTime == 0 {
@@ -126,11 +126,9 @@ func (t *Tgbot) getInboundClientsFor(inboundID int, action string) (*telego.Inli
 			for _, client := range clients {
 				buttons = append(buttons, tu.InlineKeyboardButton(client.Email).WithCallbackData(t.encodeQuery(action+" "+client.Email)))
 			}
-
 		} else {
 			return nil, errors.New(t.I18nBot("tgbot.answers.getClientsFailed"))
 		}
-
 	}
 	cols := 0
 	if len(buttons) < 6 {
@@ -252,11 +250,9 @@ func (t *Tgbot) getInboundClients(id int) (*telego.InlineKeyboardMarkup, error)
 			for _, client := range clients {
 				buttons = append(buttons, tu.InlineKeyboardButton(client.Email).WithCallbackData(t.encodeQuery("client_get_usage "+client.Email)))
 			}
-
 		} else {
 			return nil, errors.New(t.I18nBot("tgbot.answers.getClientsFailed"))
 		}
-
 	}
 	cols := 0
 	if len(buttons) < 6 {

+ 2 - 2
internal/web/service/tgbot/tgbot_report.go

@@ -49,7 +49,7 @@ func (t *Tgbot) SendBackupToAdmins() {
 		return
 	}
 	for i, adminId := range adminIds {
-		t.sendBackup(int64(adminId))
+		t.sendBackup(adminId)
 		// Add delay between sends to avoid Telegram rate limits
 		if i < len(adminIds)-1 {
 			time.Sleep(1 * time.Second)
@@ -63,7 +63,7 @@ func (t *Tgbot) sendExhaustedToAdmins() {
 		return
 	}
 	for _, adminId := range adminIds {
-		t.getExhausted(int64(adminId))
+		t.getExhausted(adminId)
 	}
 }
 

+ 8 - 11
internal/web/service/tgbot/tgbot_router.go

@@ -141,7 +141,6 @@ func (t *Tgbot) OnReceive() {
 					userStateMgr.clear(message.Chat.ID)
 					t.addClient(message.Chat.ID, t.BuildClientDraftMessage())
 				}
-
 			} else {
 				if message.UsersShared != nil {
 					if checkAdmin(message.From.ID) {
@@ -167,7 +166,7 @@ func (t *Tgbot) OnReceive() {
 			return nil
 		}, th.AnyMessage())
 
-		h.Start()
+		_ = h.Start()
 	}()
 }
 
@@ -205,7 +204,7 @@ func (t *Tgbot) answerCommand(message *telego.Message, chatId int64, isAdmin boo
 			if isAdmin {
 				t.searchClient(chatId, commandArgs[0])
 			} else {
-				t.getClientUsage(chatId, int64(message.From.ID), commandArgs[0])
+				t.getClientUsage(chatId, message.From.ID, commandArgs[0])
 			}
 		} else {
 			msg += t.I18nBot("tgbot.commands.usage")
@@ -595,12 +594,12 @@ func (t *Tgbot) answerCallback(callbackQuery *telego.CallbackQuery, isAdmin bool
 
 							if traffic.ExpiryTime > 0 {
 								if traffic.ExpiryTime-time.Now().Unix()*1000 < 0 {
-									date = -int64(days * 24 * 60 * 60000)
+									date = -(days * 24 * 60 * 60000)
 								} else {
-									date = traffic.ExpiryTime + int64(days*24*60*60000)
+									date = traffic.ExpiryTime + days*24*60*60000
 								}
 							} else {
-								date = traffic.ExpiryTime - int64(days*24*60*60000)
+								date = traffic.ExpiryTime - days*24*60*60000
 							}
 
 						}
@@ -685,12 +684,12 @@ func (t *Tgbot) answerCallback(callbackQuery *telego.CallbackQuery, isAdmin bool
 				var date int64
 				if client_ExpiryTime > 0 {
 					if client_ExpiryTime-time.Now().Unix()*1000 < 0 {
-						date = -int64(days * 24 * 60 * 60000)
+						date = -(days * 24 * 60 * 60000)
 					} else {
-						date = client_ExpiryTime + int64(days*24*60*60000)
+						date = client_ExpiryTime + days*24*60*60000
 					}
 				} else {
-					date = client_ExpiryTime - int64(days*24*60*60000)
+					date = client_ExpiryTime - days*24*60*60000
 				}
 				client_ExpiryTime = date
 
@@ -1111,7 +1110,6 @@ func (t *Tgbot) answerCallback(callbackQuery *telego.CallbackQuery, isAdmin bool
 				}
 				t.SendMsgToTgbot(chatId, t.I18nBot("tgbot.answers.chooseInbound"), inbounds)
 			}
-
 		}
 	}
 
@@ -1458,7 +1456,6 @@ func (t *Tgbot) answerCallback(callbackQuery *telego.CallbackQuery, isAdmin bool
 	case "get_sorted_traffic_usage_report":
 		t.deleteMessageTgBot(chatId, callbackQuery.Message.GetMessageID())
 		emails, err := t.inboundService.GetAllEmails()
-
 		if err != nil {
 			t.SendMsgToTgbot(chatId, t.I18nBot("tgbot.answers.errorOperation"), tu.ReplyKeyboardRemove())
 			return

+ 3 - 3
internal/web/service/xray.go

@@ -143,7 +143,7 @@ func (s *XrayService) GetXrayConfig() (*xray.Config, error) {
 			continue
 		}
 		settings := map[string]any{}
-		json.Unmarshal([]byte(inbound.Settings), &settings)
+		_ = json.Unmarshal([]byte(inbound.Settings), &settings)
 
 		dbClients, listErr := s.inboundService.clientService.ListForInbound(nil, inbound.Id)
 		if listErr != nil {
@@ -240,7 +240,7 @@ func (s *XrayService) GetXrayConfig() (*xray.Config, error) {
 		if len(inbound.StreamSettings) > 0 {
 			// Unmarshal stream JSON
 			var stream map[string]any
-			json.Unmarshal([]byte(inbound.StreamSettings), &stream)
+			_ = json.Unmarshal([]byte(inbound.StreamSettings), &stream)
 
 			// Remove the "settings" field under "tlsSettings" and "realitySettings"
 			tlsSettings, ok1 := stream["tlsSettings"].(map[string]any)
@@ -930,7 +930,7 @@ func (s *XrayService) RestartXray(isForce bool) error {
 			logger.Info("Xray config changes applied through the core API, no restart needed")
 			return nil
 		}
-		p.Stop()
+		_ = p.Stop()
 	}
 
 	p = xray.NewProcess(xrayConfig)

+ 1 - 1
internal/web/service/xray_setting.go

@@ -29,7 +29,7 @@ func (s *XraySettingService) SaveXraySetting(newXraySettings string) error {
 	if hoisted, err := EnsureStatsRouting(newXraySettings); err == nil {
 		newXraySettings = hoisted
 	}
-	return s.SettingService.saveSetting("xrayTemplateConfig", newXraySettings)
+	return s.saveSetting("xrayTemplateConfig", newXraySettings)
 }
 
 func (s *XraySettingService) CheckXrayConfig(XrayTemplateConfig string) error {

+ 24 - 24
internal/web/web.go

@@ -309,10 +309,10 @@ func (s *Server) startTask(restartXray bool) {
 		}
 	}
 	// Check whether xray is running every second
-	s.cron.AddJob(cadenceXrayRunning, job.NewCheckXrayRunningJob())
+	_, _ = s.cron.AddJob(cadenceXrayRunning, job.NewCheckXrayRunningJob())
 
 	// Check if xray needs to be restarted every 30 seconds
-	s.cron.AddFunc(cadenceXrayRestart, func() {
+	_, _ = s.cron.AddFunc(cadenceXrayRestart, func() {
 		if s.xrayService.IsNeedRestartAndSetFalse() {
 			err := s.xrayService.RestartXray(false)
 			if err != nil {
@@ -323,37 +323,37 @@ func (s *Server) startTask(restartXray bool) {
 
 	go func() {
 		time.Sleep(time.Second * 5)
-		s.cron.AddJob(cadenceXrayTraffic, job.NewXrayTrafficJob())
+		_, _ = s.cron.AddJob(cadenceXrayTraffic, job.NewXrayTrafficJob())
 	}()
 
 	// Reconcile mtproto (mtg) sidecars and scrape their traffic
 	mtJob := job.NewMtprotoJob()
-	s.cron.AddJob(cadenceMtproto, mtJob)
+	_, _ = s.cron.AddJob(cadenceMtproto, mtJob)
 	go mtJob.Run()
 
 	// check client ips from log file every 10 sec
-	s.cron.AddJob(cadenceClientIPScan, job.NewCheckClientIpJob())
+	_, _ = s.cron.AddJob(cadenceClientIPScan, job.NewCheckClientIpJob())
 
-	s.cron.AddJob(cadenceNodeHeartbeat, job.NewNodeHeartbeatJob())
+	_, _ = s.cron.AddJob(cadenceNodeHeartbeat, job.NewNodeHeartbeatJob())
 
-	s.cron.AddJob(cadenceNodeTraffic, job.NewNodeTrafficSyncJob())
+	_, _ = s.cron.AddJob(cadenceNodeTraffic, job.NewNodeTrafficSyncJob())
 
 	// Outbound subscription auto-refresh (respects per-sub updateInterval)
-	s.cron.AddJob(cadenceOutboundSub, job.NewOutboundSubscriptionJob())
+	_, _ = s.cron.AddJob(cadenceOutboundSub, job.NewOutboundSubscriptionJob())
 
 	// check client ips from log file every day
-	s.cron.AddJob("@daily", job.NewClearLogsJob())
-	s.cron.AddJob("@hourly", job.NewWarpIpJob())
+	_, _ = s.cron.AddJob("@daily", job.NewClearLogsJob())
+	_, _ = s.cron.AddJob("@hourly", job.NewWarpIpJob())
 
 	// Inbound traffic reset jobs
 	// Run every hour
-	s.cron.AddJob("@hourly", job.NewPeriodicTrafficResetJob("hourly"))
+	_, _ = s.cron.AddJob("@hourly", job.NewPeriodicTrafficResetJob("hourly"))
 	// Run once a day, midnight
-	s.cron.AddJob("@daily", job.NewPeriodicTrafficResetJob("daily"))
+	_, _ = s.cron.AddJob("@daily", job.NewPeriodicTrafficResetJob("daily"))
 	// Run once a week, midnight between Sat/Sun
-	s.cron.AddJob("@weekly", job.NewPeriodicTrafficResetJob("weekly"))
+	_, _ = s.cron.AddJob("@weekly", job.NewPeriodicTrafficResetJob("weekly"))
 	// Run once a month, midnight, first of month
-	s.cron.AddJob("@monthly", job.NewPeriodicTrafficResetJob("monthly"))
+	_, _ = s.cron.AddJob("@monthly", job.NewPeriodicTrafficResetJob("monthly"))
 
 	// LDAP sync scheduling
 	if ldapEnabled, _ := s.settingService.GetLdapEnable(); ldapEnabled {
@@ -363,7 +363,7 @@ func (s *Server) startTask(restartXray bool) {
 		}
 		j := job.NewLdapSyncJob()
 		// job has zero-value services with method receivers that read settings on demand
-		s.cron.AddJob(runtime, j)
+		_, _ = s.cron.AddJob(runtime, j)
 	}
 
 	// Telegram-bot–dependent jobs: periodic stats report + callback-hash cleanup.
@@ -383,21 +383,21 @@ func (s *Server) startTask(restartXray bool) {
 		}
 
 		// check for Telegram bot callback query hash storage reset
-		s.cron.AddJob(cadenceCheckHash, job.NewCheckHashStorageJob())
+		_, _ = s.cron.AddJob(cadenceCheckHash, job.NewCheckHashStorageJob())
 	}
 
 	// CPU monitor publishes cpu.high events; register it whenever any notifier
 	// (Telegram or Email) wants them, independent of the Telegram bot being on.
 	if s.cpuAlarmWanted() {
-		s.cron.AddJob(cadenceCPUAlarm, job.NewCheckCpuJob())
+		_, _ = s.cron.AddJob(cadenceCPUAlarm, job.NewCheckCpuJob())
 	}
 	// Memory monitor publishes memory.high events; register it whenever any notifier wants them.
 	if s.memoryAlarmWanted() {
-		s.cron.AddJob(cadenceMemoryAlarm, job.NewCheckMemJob())
+		_, _ = s.cron.AddJob(cadenceMemoryAlarm, job.NewCheckMemJob())
 	}
 
 	if mins := sys.MemoryReleaseIntervalMinutes(); mins > 0 {
-		s.cron.AddJob(fmt.Sprintf("@every %dm", mins), job.NewMemoryReleaseJob())
+		_, _ = s.cron.AddJob(fmt.Sprintf("@every %dm", mins), job.NewMemoryReleaseJob())
 		go func() {
 			time.Sleep(time.Minute)
 			job.NewMemoryReleaseJob().Run()
@@ -479,7 +479,7 @@ func (s *Server) start(restartXray bool, startTgBot bool) (err error) {
 	// This is an anonymous function, no function name
 	defer func() {
 		if err != nil {
-			s.Stop()
+			_ = s.Stop()
 		}
 	}()
 
@@ -553,7 +553,7 @@ func (s *Server) start(restartXray bool, startTgBot bool) (err error) {
 		}
 	}
 	listenAddr := net.JoinHostPort(listen, strconv.Itoa(port))
-	listener, err := net.Listen("tcp", listenAddr)
+	listener, err := (&net.ListenConfig{}).Listen(context.Background(), "tcp", listenAddr)
 	if err != nil {
 		return err
 	}
@@ -593,7 +593,7 @@ func (s *Server) start(restartXray bool, startTgBot bool) (err error) {
 	}
 
 	go func() {
-		s.httpServer.Serve(listener)
+		_ = s.httpServer.Serve(listener)
 	}()
 
 	// Create event bus before startTask so jobs can use it
@@ -660,7 +660,7 @@ func (s *Server) start(restartXray bool, startTgBot bool) (err error) {
 		isTgbotenabled, err := s.settingService.GetTgbotEnabled()
 		if (err == nil) && (isTgbotenabled) {
 			tgBot := s.tgbotService.NewTgbot()
-			tgBot.Start(i18nFS)
+			_ = tgBot.Start(i18nFS)
 			// Subscribe Telegram notifications for event bus
 			s.bus.Subscribe("tg-notifier", s.tgbotService.HandleEvent)
 		}
@@ -681,7 +681,7 @@ func (s *Server) StopPanelOnly() error {
 func (s *Server) stop(stopXray bool, stopTgBot bool) error {
 	s.cancel()
 	if stopXray {
-		s.xrayService.StopXray()
+		_ = s.xrayService.StopXray()
 		mtproto.GetManager().StopAll()
 	}
 	if s.cron != nil {

+ 2 - 1
internal/web/websocket/hub_test.go

@@ -7,8 +7,9 @@ import (
 	"testing"
 	"time"
 
-	xuilogger "github.com/mhsanaei/3x-ui/v3/internal/logger"
 	"github.com/op/go-logging"
+
+	xuilogger "github.com/mhsanaei/3x-ui/v3/internal/logger"
 )
 
 func TestMain(m *testing.M) {

+ 5 - 4
internal/xray/process.go

@@ -2,6 +2,7 @@ package xray
 
 import (
 	"bytes"
+	"context"
 	"encoding/json"
 	"errors"
 	"fmt"
@@ -74,7 +75,7 @@ func GetAccessLogPath() (string, error) {
 	}
 
 	jsonConfig := map[string]any{}
-	err = json.Unmarshal([]byte(config), &jsonConfig)
+	err = json.Unmarshal(config, &jsonConfig)
 	if err != nil {
 		logger.Warningf("Failed to parse JSON configuration: %s", err)
 		return "", err
@@ -92,7 +93,7 @@ func GetAccessLogPath() (string, error) {
 
 // stopProcess calls Stop on the given Process instance.
 func stopProcess(p *Process) {
-	p.Stop()
+	_ = p.Stop()
 }
 
 // Process wraps an Xray process instance and provides management methods.
@@ -475,7 +476,7 @@ func (p *process) refreshAPIPort() {
 
 // refreshVersion updates the version string by running the Xray binary with -version.
 func (p *process) refreshVersion() {
-	cmd := exec.Command(GetBinaryPath(), "-version")
+	cmd := exec.CommandContext(context.Background(), GetBinaryPath(), "-version")
 	data, err := cmd.Output()
 	if err != nil {
 		p.version = "Unknown"
@@ -521,7 +522,7 @@ func (p *process) Start() (err error) {
 		return common.NewErrorf("Failed to write configuration file: %v", err)
 	}
 
-	cmd := exec.Command(GetBinaryPath(), "-c", configPath)
+	cmd := exec.CommandContext(context.Background(), GetBinaryPath(), "-c", configPath)
 	cmd.Stdout = p.logWriter
 	cmd.Stderr = p.logWriter
 

+ 3 - 2
internal/xray/process_test.go

@@ -12,8 +12,9 @@ import (
 	"testing"
 	"time"
 
-	xuilogger "github.com/mhsanaei/3x-ui/v3/internal/logger"
 	"github.com/op/go-logging"
+
+	xuilogger "github.com/mhsanaei/3x-ui/v3/internal/logger"
 )
 
 func TestWriteFileAtomicModeAndRenameFailure(t *testing.T) {
@@ -203,7 +204,7 @@ func markProcessHelperReady(t *testing.T) {
 	if readyPath == "" {
 		t.Fatal("XRAY_PROCESS_READY is not set")
 	}
-	if err := os.WriteFile(readyPath, []byte("ready"), 0644); err != nil {
+	if err := os.WriteFile(readyPath, []byte("ready"), 0o644); err != nil {
 		t.Fatalf("write helper ready file: %v", err)
 	}
 }

+ 4 - 3
internal/xray/process_windows.go

@@ -7,8 +7,9 @@ import (
 	"sync"
 	"unsafe"
 
-	"github.com/mhsanaei/3x-ui/v3/internal/logger"
 	"golang.org/x/sys/windows"
+
+	"github.com/mhsanaei/3x-ui/v3/internal/logger"
 )
 
 var (
@@ -36,7 +37,7 @@ func ensureKillOnExitJob() (windows.Handle, error) {
 			uint32(unsafe.Sizeof(info)),
 		)
 		if err != nil {
-			windows.CloseHandle(h)
+			_ = windows.CloseHandle(h)
 			killOnExitJobErr = err
 			return
 		}
@@ -59,7 +60,7 @@ func attachChildLifetime(cmd *exec.Cmd) {
 		logger.Warning("xray: OpenProcess for job attach failed:", err)
 		return
 	}
-	defer windows.CloseHandle(h)
+	defer func() { _ = windows.CloseHandle(h) }()
 	if err := windows.AssignProcessToJobObject(job, h); err != nil {
 		logger.Warning("xray: AssignProcessToJobObject failed:", err)
 	}

+ 4 - 4
main.go

@@ -51,7 +51,7 @@ func runWebServer() {
 		log.Fatalf("Unknown log level: %v", config.GetLogLevel())
 	}
 
-	godotenv.Load()
+	_ = godotenv.Load()
 
 	for _, line := range sys.ApplyMemoryTuning() {
 		logger.Info(line)
@@ -171,8 +171,8 @@ func runWebServer() {
 			tgbot.StopBot()
 			// ------------------------------------------------------------
 
-			server.Stop()
-			subServer.Stop()
+			_ = server.Stop()
+			_ = subServer.Stop()
 			log.Println("Shutting down servers.")
 			return
 		}
@@ -350,7 +350,7 @@ func updateSetting(port int, username string, password string, webBasePath strin
 		if err != nil {
 			fmt.Println("Failed to reset two-factor authentication:", err)
 		} else {
-			settingService.SetTwoFactorToken("")
+			_ = settingService.SetTwoFactorToken("")
 			fmt.Println("Two-factor authentication reset successfully")
 		}
 	}

+ 1 - 3
tools/openapigen/walker.go

@@ -57,9 +57,7 @@ func walkPackages(requests []packageRequest) ([]Schema, []Alias, error) {
 							}
 							overrides := req.Overrides[ts.Name.Name]
 							for _, fld := range strct.Fields.List {
-								for _, f := range buildFields(fld, overrides) {
-									s.Fields = append(s.Fields, f)
-								}
+								s.Fields = append(s.Fields, buildFields(fld, overrides)...)
 							}
 							schemas = append(schemas, s)
 							continue