feat: Implement version checking and update notification in dashboard

- Added functionality to fetch the latest GitHub release version.
- Enhanced the dashboard to display current and latest version information.
- Updated the version comparison logic to handle special cases and non-semver formats.
- Improved the dashboard template to link to the latest release on GitHub.
This commit is contained in:
StarFleetCPTN
2025-04-11 20:29:47 -07:00
parent 82f73cfe60
commit e9a005b658
3 changed files with 54 additions and 4 deletions
+12 -1
View File
@@ -50,6 +50,11 @@ func isNewerVersionAvailable(current, latest string) bool {
return false return false
} }
// Special case for DEV versions - always show update available
if current == "DEV" {
return true
}
// Strip 'v' prefix if present for comparison // Strip 'v' prefix if present for comparison
if strings.HasPrefix(current, "v") { if strings.HasPrefix(current, "v") {
current = current[1:] current = current[1:]
@@ -62,6 +67,12 @@ func isNewerVersionAvailable(current, latest string) bool {
currentParts := strings.Split(current, ".") currentParts := strings.Split(current, ".")
latestParts := strings.Split(latest, ".") latestParts := strings.Split(latest, ".")
// Handle non-semver format in either version
if len(currentParts) < 2 || len(latestParts) < 2 {
// If format doesn't match semver pattern, do string comparison
return current != latest && latest != ""
}
// Compare major, minor, patch versions // Compare major, minor, patch versions
for i := 0; i < len(currentParts) && i < len(latestParts); i++ { for i := 0; i < len(currentParts) && i < len(latestParts); i++ {
// Parse to integers // Parse to integers
@@ -310,7 +321,7 @@ templ Dashboard(ctx context.Context, data DashboardData) {
<i class="fas fa-exclamation-triangle w-3 h-3 mr-1"></i> <i class="fas fa-exclamation-triangle w-3 h-3 mr-1"></i>
{ data.CurrentVersion } { data.CurrentVersion }
</span> </span>
<a href="https://github.com/starfleetcptn/gomft/releases/tag/{ data.LatestVersion }" target="_blank" <a href={ templ.SafeURL(fmt.Sprintf("https://github.com/starfleetcptn/gomft/releases/tag/%s", data.LatestVersion)) } target="_blank"
class="bg-green-100 text-green-800 text-xs font-medium inline-flex items-center px-2.5 py-0.5 rounded-full dark:bg-green-900 dark:text-green-300 hover:bg-green-200 dark:hover:bg-green-800"> class="bg-green-100 text-green-800 text-xs font-medium inline-flex items-center px-2.5 py-0.5 rounded-full dark:bg-green-900 dark:text-green-300 hover:bg-green-200 dark:hover:bg-green-800">
<i class="fas fa-arrow-circle-up w-3 h-3 mr-1"></i> <i class="fas fa-arrow-circle-up w-3 h-3 mr-1"></i>
{ data.LatestVersion } Available { data.LatestVersion } Available
+3 -3
View File
@@ -364,7 +364,7 @@ templ LayoutWithContext(title string, ctx context.Context) {
hx-get="/notifications/dropdown" hx-get="/notifications/dropdown"
hx-trigger="click once" hx-trigger="click once"
hx-target="#notification-dropdown-content" hx-target="#notification-dropdown-content"
data-dropdown-placement="bottom-end" data-dropdown-placement="bottom-start"
> >
<span class="sr-only">View notifications</span> <span class="sr-only">View notifications</span>
<i class="fas fa-bell"></i> <i class="fas fa-bell"></i>
@@ -375,10 +375,10 @@ templ LayoutWithContext(title string, ctx context.Context) {
</div> </div>
<!-- Notification dropdown --> <!-- Notification dropdown -->
<div <div
class="hidden overflow-hidden z-50 my-4 max-w-sm md:max-w-md w-full text-base list-none bg-white rounded divide-y divide-gray-100 shadow-lg dark:divide-gray-600 dark:bg-gray-700 rounded-xl" class="hidden overflow-hidden z-50 my-4 max-w-sm md:max-w-md w-full text-base list-none bg-white rounded divide-y divide-gray-100 shadow-lg dark:divide-gray-600 dark:bg-gray-700 rounded-xl fixed"
id="notification-dropdown" id="notification-dropdown"
style="min-width: 320px; width: 100%;" style="min-width: 320px; width: 100%;"
data-dropdown-placement="bottom-end" data-dropdown-placement="bottom-start"
> >
<div id="notification-dropdown-content"> <div id="notification-dropdown-content">
<!-- Content will be loaded dynamically via HTMX --> <!-- Content will be loaded dynamically via HTMX -->
@@ -1,6 +1,7 @@
package handlers package handlers
import ( import (
"encoding/json"
"fmt" "fmt"
"math" "math"
"net/http" "net/http"
@@ -13,6 +14,36 @@ import (
"github.com/starfleetcptn/gomft/internal/db" "github.com/starfleetcptn/gomft/internal/db"
) )
// getLatestGitHubRelease fetches the latest release tag from GitHub
func getLatestGitHubRelease() string {
// Create an HTTP client with a timeout
client := &http.Client{
Timeout: 5 * time.Second,
}
// Make a request to the GitHub API
resp, err := client.Get("https://api.github.com/repos/starfleetcptn/gomft/releases/latest")
if err != nil {
return ""
}
defer resp.Body.Close()
// Check if the response was successful
if resp.StatusCode != http.StatusOK {
return ""
}
// Parse the response
var release struct {
TagName string `json:"tag_name"`
}
if err := json.NewDecoder(resp.Body).Decode(&release); err != nil {
return ""
}
return release.TagName
}
// HandleDashboard handles the GET /dashboard route // HandleDashboard handles the GET /dashboard route
func (h *Handlers) HandleDashboard(c *gin.Context) { func (h *Handlers) HandleDashboard(c *gin.Context) {
@@ -71,6 +102,12 @@ func (h *Handlers) HandleDashboard(c *gin.Context) {
// Get the rclone version // Get the rclone version
rcloneVersion := components.GetRcloneVersion() rcloneVersion := components.GetRcloneVersion()
// Set current version from the application version
currentVersion := components.AppVersion
// Get the latest release version from GitHub
latestVersion := getLatestGitHubRelease()
data := components.DashboardData{ data := components.DashboardData{
RecentJobs: recentHistory, RecentJobs: recentHistory,
ActiveTransfers: int(totalJobs), ActiveTransfers: int(totalJobs),
@@ -78,6 +115,8 @@ func (h *Handlers) HandleDashboard(c *gin.Context) {
FailedTransfers: int(failedJobs), FailedTransfers: int(failedJobs),
Configs: configsMap, Configs: configsMap,
RcloneVersion: rcloneVersion, RcloneVersion: rcloneVersion,
CurrentVersion: currentVersion,
LatestVersion: latestVersion,
} }
components.Dashboard(components.CreateTemplateContext(c), data).Render(c, c.Writer) components.Dashboard(components.CreateTemplateContext(c), data).Render(c, c.Writer)