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.
This commit is contained in:
MHSanaei
2026-06-27 15:42:22 +02:00
parent 7efa0d9ddd
commit fa1a19c03c
81 changed files with 410 additions and 286 deletions
+8 -7
View File
@@ -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
}
}
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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)