Browse Source

refactor(util): move panel version comparison into a shared package

The node-assignment path in the service package needs the same
MAJOR.MINOR.PATCH comparison the panel updater uses, but service/panel
imports service, so the helper cannot stay there.
MHSanaei 13 hours ago
parent
commit
97bee832f4

+ 47 - 0
internal/util/version/version.go

@@ -0,0 +1,47 @@
+// Package version compares 3x-ui release versions ("v3.8.0" or "3.8.0").
+package version
+
+import (
+	"strconv"
+	"strings"
+)
+
+// Compare returns -1, 0 or 1 as a is older than, equal to or newer than b, and
+// false when either side is not a plain MAJOR.MINOR.PATCH version.
+func Compare(a, b string) (int, bool) {
+	aParts, okA := parse(a)
+	bParts, okB := parse(b)
+	if !okA || !okB {
+		return 0, false
+	}
+	for i := range len(aParts) {
+		if aParts[i] > bParts[i] {
+			return 1, true
+		}
+		if aParts[i] < bParts[i] {
+			return -1, true
+		}
+	}
+	return 0, true
+}
+
+// Normalize strips surrounding space and a leading "v" from a version tag.
+func Normalize(v string) string {
+	return strings.TrimPrefix(strings.TrimSpace(v), "v")
+}
+
+func parse(v string) ([3]int, bool) {
+	var result [3]int
+	parts := strings.Split(Normalize(v), ".")
+	if len(parts) != 3 {
+		return result, false
+	}
+	for i, part := range parts {
+		n, err := strconv.Atoi(part)
+		if err != nil {
+			return result, false
+		}
+		result[i] = n
+	}
+	return result, true
+}

+ 12 - 0
internal/util/version/version_test.go

@@ -0,0 +1,12 @@
+package version
+
+import "testing"
+
+func TestCompareRejectsUnexpectedFormats(t *testing.T) {
+	if _, ok := Compare("latest", "2.9.3"); ok {
+		t.Fatal("expected non-semver latest tag to be rejected")
+	}
+	if _, ok := Compare("v2.9", "2.9.3"); ok {
+		t.Fatal("expected short version to be rejected")
+	}
+}

+ 3 - 39
internal/web/service/panel/panel.go

@@ -19,6 +19,7 @@ import (
 
 	"github.com/mhsanaei/3x-ui/v3/internal/config"
 	"github.com/mhsanaei/3x-ui/v3/internal/logger"
+	"github.com/mhsanaei/3x-ui/v3/internal/util/version"
 	"github.com/mhsanaei/3x-ui/v3/internal/web/global"
 	"github.com/mhsanaei/3x-ui/v3/internal/web/service"
 )
@@ -522,50 +523,13 @@ func resolveUpdateFolders() (string, string) {
 }
 
 func isNewerVersion(latest string, current string) bool {
-	cmp, ok := compareVersionStrings(latest, current)
+	cmp, ok := version.Compare(latest, current)
 	if !ok {
-		return normalizeVersionTag(latest) != normalizeVersionTag(current)
+		return version.Normalize(latest) != version.Normalize(current)
 	}
 	return cmp > 0
 }
 
-func compareVersionStrings(a string, b string) (int, bool) {
-	aParts, okA := parseVersionParts(a)
-	bParts, okB := parseVersionParts(b)
-	if !okA || !okB {
-		return 0, false
-	}
-	for i := range len(aParts) {
-		if aParts[i] > bParts[i] {
-			return 1, true
-		}
-		if aParts[i] < bParts[i] {
-			return -1, true
-		}
-	}
-	return 0, true
-}
-
-func parseVersionParts(version string) ([3]int, bool) {
-	var result [3]int
-	parts := strings.Split(normalizeVersionTag(version), ".")
-	if len(parts) != 3 {
-		return result, false
-	}
-	for i, part := range parts {
-		n, err := strconv.Atoi(part)
-		if err != nil {
-			return result, false
-		}
-		result[i] = n
-	}
-	return result, true
-}
-
-func normalizeVersionTag(version string) string {
-	return strings.TrimPrefix(strings.TrimSpace(version), "v")
-}
-
 func shellQuote(value string) string {
 	return "'" + strings.ReplaceAll(value, "'", "'\\''") + "'"
 }

+ 0 - 9
internal/web/service/panel/panel_test.go

@@ -33,15 +33,6 @@ func TestIsNewerVersion(t *testing.T) {
 	}
 }
 
-func TestCompareVersionStringsRejectsUnexpectedFormats(t *testing.T) {
-	if _, ok := compareVersionStrings("latest", "2.9.3"); ok {
-		t.Fatal("expected non-semver latest tag to be rejected")
-	}
-	if _, ok := compareVersionStrings("v2.9", "2.9.3"); ok {
-		t.Fatal("expected short version to be rejected")
-	}
-}
-
 func TestShellQuote(t *testing.T) {
 	if got := shellQuote("/usr/bin/curl"); got != "'/usr/bin/curl'" {
 		t.Fatalf("unexpected quote result: %s", got)