mirror of
https://github.com/swissmakers/fail2ban-ui.git
synced 2026-04-17 05:53:15 +02:00
Implement version check on load, if the footer has data-update-check=true,it fetches /api/version and sets the version badge. No request is made when update check is disabled.
This commit is contained in:
@@ -50,6 +50,7 @@ import (
|
||||
"github.com/swissmakers/fail2ban-ui/internal/fail2ban"
|
||||
"github.com/swissmakers/fail2ban-ui/internal/integrations"
|
||||
"github.com/swissmakers/fail2ban-ui/internal/storage"
|
||||
"github.com/swissmakers/fail2ban-ui/internal/version"
|
||||
)
|
||||
|
||||
// wsHub is the global WebSocket hub instance
|
||||
@@ -1141,15 +1142,97 @@ func renderIndexPage(c *gin.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
// Update check: default true; set UPDATE_CHECK=false to disable external GitHub request
|
||||
updateCheckEnabled := os.Getenv("UPDATE_CHECK") != "false"
|
||||
|
||||
c.HTML(http.StatusOK, "index.html", gin.H{
|
||||
"timestamp": time.Now().Format(time.RFC1123),
|
||||
"version": time.Now().Unix(),
|
||||
"disableExternalIP": disableExternalIP,
|
||||
"oidcEnabled": oidcEnabled,
|
||||
"skipLoginPage": skipLoginPage,
|
||||
"timestamp": time.Now().Format(time.RFC1123),
|
||||
"version": time.Now().Unix(),
|
||||
"appVersion": version.Version,
|
||||
"updateCheckEnabled": updateCheckEnabled,
|
||||
"disableExternalIP": disableExternalIP,
|
||||
"oidcEnabled": oidcEnabled,
|
||||
"skipLoginPage": skipLoginPage,
|
||||
})
|
||||
}
|
||||
|
||||
// githubReleaseResponse is used to parse the GitHub releases/latest API response
|
||||
type githubReleaseResponse struct {
|
||||
TagName string `json:"tag_name"`
|
||||
}
|
||||
|
||||
// versionLess returns true if a < b (e.g. "1.3.5" < "1.3.6")
|
||||
func versionLess(a, b string) bool {
|
||||
parse := func(s string) []int {
|
||||
s = strings.TrimPrefix(strings.TrimSpace(s), "v")
|
||||
parts := strings.Split(s, ".")
|
||||
out := make([]int, 0, len(parts))
|
||||
for _, p := range parts {
|
||||
n, _ := strconv.Atoi(p)
|
||||
out = append(out, n)
|
||||
}
|
||||
return out
|
||||
}
|
||||
pa, pb := parse(a), parse(b)
|
||||
for i := 0; i < len(pa) || i < len(pb); i++ {
|
||||
va, vb := 0, 0
|
||||
if i < len(pa) {
|
||||
va = pa[i]
|
||||
}
|
||||
if i < len(pb) {
|
||||
vb = pb[i]
|
||||
}
|
||||
if va < vb {
|
||||
return true
|
||||
}
|
||||
if va > vb {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// GetVersionHandler returns the current app version and optionally the latest GitHub release.
|
||||
// UPDATE_CHECK=false disables the external request to GitHub.
|
||||
func GetVersionHandler(c *gin.Context) {
|
||||
updateCheckEnabled := os.Getenv("UPDATE_CHECK") != "false"
|
||||
out := gin.H{
|
||||
"version": version.Version,
|
||||
"update_check_enabled": updateCheckEnabled,
|
||||
}
|
||||
if !updateCheckEnabled {
|
||||
c.JSON(http.StatusOK, out)
|
||||
return
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(c.Request.Context(), 8*time.Second)
|
||||
defer cancel()
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://api.github.com/repos/swissmakers/fail2ban-ui/releases/latest", nil)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, out)
|
||||
return
|
||||
}
|
||||
req.Header.Set("Accept", "application/vnd.github.v3+json")
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, out)
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
c.JSON(http.StatusOK, out)
|
||||
return
|
||||
}
|
||||
var gh githubReleaseResponse
|
||||
if err := json.NewDecoder(resp.Body).Decode(&gh); err != nil {
|
||||
c.JSON(http.StatusOK, out)
|
||||
return
|
||||
}
|
||||
latest := strings.TrimPrefix(strings.TrimSpace(gh.TagName), "v")
|
||||
out["latest_version"] = latest
|
||||
out["update_available"] = versionLess(version.Version, latest)
|
||||
c.JSON(http.StatusOK, out)
|
||||
}
|
||||
|
||||
// GetJailFilterConfigHandler returns both the filter config and jail config for a given jail
|
||||
func GetJailFilterConfigHandler(c *gin.Context) {
|
||||
config.DebugLog("----------------------------")
|
||||
|
||||
@@ -58,6 +58,9 @@ func RegisterRoutes(r *gin.Engine, hub *Hub) {
|
||||
api.POST("/jails", CreateJailHandler)
|
||||
api.DELETE("/jails/:jail", DeleteJailHandler)
|
||||
|
||||
// Version and update check (only on page load; UPDATE_CHECK=false disables GitHub request)
|
||||
api.GET("/version", GetVersionHandler)
|
||||
|
||||
// Settings endpoints
|
||||
api.GET("/settings", GetSettingsHandler)
|
||||
api.POST("/settings", UpdateSettingsHandler)
|
||||
|
||||
@@ -81,6 +81,26 @@ function initializeApp() {
|
||||
console.warn('Could not check LOTR on load:', err);
|
||||
});
|
||||
|
||||
// Version and update check: only on page load; UPDATE_CHECK=false disables external GitHub request
|
||||
var versionContainer = document.getElementById('version-badge-container');
|
||||
if (versionContainer && versionContainer.getAttribute('data-update-check') === 'true') {
|
||||
fetch('/api/version')
|
||||
.then(function(res) { return res.json(); })
|
||||
.then(function(data) {
|
||||
if (!data.update_check_enabled || versionContainer.innerHTML) return;
|
||||
var latestLabel = typeof t === 'function' ? t('footer.latest', 'Latest') : 'Latest';
|
||||
var updateHint = (typeof t === 'function' && translations && translations['footer.update_available'])
|
||||
? translations['footer.update_available'].replace('{version}', data.latest_version || '')
|
||||
: ('Update available: v' + (data.latest_version || ''));
|
||||
if (data.update_available && data.latest_version) {
|
||||
versionContainer.innerHTML = '<a href="https://github.com/swissmakers/fail2ban-ui/releases" target="_blank" rel="noopener" class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-amber-100 text-amber-800 hover:bg-amber-200" title="' + updateHint + '">' + updateHint + '</a>';
|
||||
} else {
|
||||
versionContainer.innerHTML = '<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-green-100 text-green-800" title="' + latestLabel + '">' + latestLabel + '</span>';
|
||||
}
|
||||
})
|
||||
.catch(function() { /* ignore; no badge on error */ });
|
||||
}
|
||||
|
||||
Promise.all([
|
||||
loadServers(),
|
||||
getTranslationsSettingsOnPageload()
|
||||
|
||||
@@ -949,6 +949,8 @@
|
||||
<footer id="footer" class="hidden bg-gray-100 py-4">
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 text-center text-gray-600 text-sm">
|
||||
<p class="mb-0">
|
||||
Fail2ban-UI v<span id="footer-app-version">{{.appVersion}}</span>
|
||||
<span id="version-badge-container" class="ml-2" data-update-check="{{.updateCheckEnabled}}"></span>
|
||||
© <a href="https://swissmakers.ch" target="_blank" class="text-blue-600 hover:text-blue-800">Swissmakers GmbH</a>
|
||||
-
|
||||
<a href="https://github.com/swissmakers/fail2ban-ui" target="_blank" class="text-blue-600 hover:text-blue-800">GitHub</a>
|
||||
|
||||
Reference in New Issue
Block a user