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.
This commit is contained in:
MHSanaei
2026-10-02 23:56:52 +02:00
parent 05a083eaef
commit 97bee832f4
4 changed files with 62 additions and 48 deletions
+3 -39
View File
@@ -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, "'", "'\\''") + "'"
}
-9
View File
@@ -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)