diff --git a/aggregator-server/internal/api/handlers/agent_updates.go b/aggregator-server/internal/api/handlers/agent_updates.go index 2b588a0..281d4d0 100644 --- a/aggregator-server/internal/api/handlers/agent_updates.go +++ b/aggregator-server/internal/api/handlers/agent_updates.go @@ -12,6 +12,7 @@ import ( "github.com/Fimeg/RedFlag/aggregator-server/internal/database/queries" "github.com/Fimeg/RedFlag/aggregator-server/internal/models" "github.com/Fimeg/RedFlag/aggregator-server/internal/services" + "github.com/Fimeg/RedFlag/aggregator-server/internal/version" "github.com/gin-gonic/gin" "github.com/google/uuid" ) @@ -578,8 +579,10 @@ func (h *AgentUpdateHandler) CheckForUpdateAvailable(c *gin.Context) { return } - // Check if this is actually newer than current version - hasUpdate := isVersionUpgrade(latestVersion, agent.CurrentVersion) + // Check if this is actually newer than current version using version package + currentVer := version.Version(agent.CurrentVersion) + latestVer := version.Version(latestVersion) + hasUpdate := currentVer.IsUpgrade(latestVer) log.Printf("[DEBUG] Version comparison - latest: %s, current: %s, hasUpdate: %v for platform: %s/%s", latestVersion, agent.CurrentVersion, hasUpdate, osType, osArch) @@ -589,11 +592,12 @@ func (h *AgentUpdateHandler) CheckForUpdateAvailable(c *gin.Context) { log.Printf("[DEBUG] Detected sub-version upgrade: %s -> %s", agent.CurrentVersion, latestVersion) } + platform := version.Platform(osType + "-" + osArch) c.JSON(http.StatusOK, gin.H{ "hasUpdate": hasUpdate, "currentVersion": agent.CurrentVersion, "latestVersion": latestVersion, - "platform": osType + "-" + osArch, + "platform": platform.String(), }) } @@ -641,40 +645,4 @@ func (h *AgentUpdateHandler) GetUpdateStatus(c *gin.Context) { "progress": progress, "error": errorMsg, }) -} - -// isVersionUpgrade returns true if new version is greater than current version -func isVersionUpgrade(newVersion string, currentVersion string) bool { - // Parse semantic versions - newParts := strings.Split(newVersion, ".") - curParts := strings.Split(currentVersion, ".") - - // Pad arrays to 3 parts - for len(newParts) < 3 { - newParts = append(newParts, "0") - } - for len(curParts) < 3 { - curParts = append(curParts, "0") - } - - // Convert to integers for comparison - newMajor, _ := strconv.Atoi(newParts[0]) - newMinor, _ := strconv.Atoi(newParts[1]) - newPatch, _ := strconv.Atoi(newParts[2]) - - curMajor, _ := strconv.Atoi(curParts[0]) - curMinor, _ := strconv.Atoi(curParts[1]) - curPatch, _ := strconv.Atoi(curParts[2]) - - // Check if new > current (not equal, not less) - if newMajor > curMajor { - return true - } - if newMajor == curMajor && newMinor > curMinor { - return true - } - if newMajor == curMajor && newMinor == curMinor && newPatch > curPatch { - return true - } - return false } \ No newline at end of file diff --git a/aggregator-server/internal/database/queries/agent_updates.go b/aggregator-server/internal/database/queries/agent_updates.go index 8f93d1e..75457df 100644 --- a/aggregator-server/internal/database/queries/agent_updates.go +++ b/aggregator-server/internal/database/queries/agent_updates.go @@ -240,17 +240,20 @@ func (q *AgentUpdateQueries) GetLatestVersion(platform string) (string, error) { // GetLatestVersionByTypeAndArch retrieves the latest available version for a specific os_type and architecture func (q *AgentUpdateQueries) GetLatestVersionByTypeAndArch(osType, osArch string) (string, error) { + // Use combined platform format to match agent_update_packages storage + platformStr := osType + "-" + osArch + query := ` SELECT version FROM agent_update_packages - WHERE platform = $1 AND architecture = $2 AND is_active = true + WHERE (platform || '-' || architecture) = $1 AND is_active = true ORDER BY version DESC LIMIT 1 ` var latestVersion string - err := q.db.Get(&latestVersion, query, osType, osArch) + err := q.db.Get(&latestVersion, query, platformStr) if err != nil { if err == sql.ErrNoRows { - return "", fmt.Errorf("no update packages available for platform %s/%s", osType, osArch) + return "", fmt.Errorf("no update packages available for platform %s", platformStr) } return "", fmt.Errorf("failed to get latest version: %w", err) } diff --git a/aggregator-server/internal/version/version.go b/aggregator-server/internal/version/version.go new file mode 100644 index 0000000..585663c --- /dev/null +++ b/aggregator-server/internal/version/version.go @@ -0,0 +1,68 @@ +package version + +import ( + "strconv" + "strings" +) + +// Version represents a semantic version string +type Version string + +// Platform represents combined platform-architecture format (e.g., "linux-amd64") +type Platform string + +// ParsePlatform converts "linux-amd64" → platform="linux", arch="amd64" +func ParsePlatform(p Platform) (platform, architecture string) { + parts := strings.SplitN(string(p), "-", 2) + if len(parts) == 2 { + return parts[0], parts[1] + } + return string(p), "" +} + +// String returns the full platform string +func (p Platform) String() string { + return string(p) +} + +// Compare returns -1, 0, or 1 for v < other, v == other, v > other +func (v Version) Compare(other Version) int { + v1Parts := strings.Split(string(v), ".") + v2Parts := strings.Split(string(other), ".") + + maxLen := len(v1Parts) + if len(v2Parts) > maxLen { + maxLen = len(v2Parts) + } + + for i := 0; i < maxLen; i++ { + v1Num := 0 + v2Num := 0 + + if i < len(v1Parts) { + v1Num, _ = strconv.Atoi(v1Parts[i]) + } + if i < len(v2Parts) { + v2Num, _ = strconv.Atoi(v2Parts[i]) + } + + if v1Num < v2Num { + return -1 + } + if v1Num > v2Num { + return 1 + } + } + + return 0 +} + +// IsUpgrade returns true if other is newer than v +func (v Version) IsUpgrade(other Version) bool { + return v.Compare(other) < 0 +} + +// IsValid returns true if version string is non-empty +func (v Version) IsValid() bool { + return string(v) != "" +}