diff --git a/src/hub/web_ui.py b/src/hub/web_ui.py
index ac4b210..703ac3c 100644
--- a/src/hub/web_ui.py
+++ b/src/hub/web_ui.py
@@ -658,7 +658,7 @@ class WebUIRenderer:
🌐 Server Version & Upstream Release Status
Upstream release tracking via Gitea production channels
- {('UPDATE AVAILABLE' if update_status.get("server_update_available") else 'UP TO DATE')}
+ {('UPDATE AVAILABLE' if update_status.get("server_update_available") else 'UP TO DATE')}
@@ -667,16 +667,16 @@ class WebUIRenderer:
Latest Upstream Release
-
v{update_status.get('latest_upstream_version') or LOGAR_VERSION}
+
v{update_status.get('latest_upstream_version') or LOGAR_VERSION}
Last Checked
-
{update_status.get('last_checked_utc') or 'Never'}
+
{update_status.get('last_checked_utc') or 'Never'}
@@ -771,18 +771,18 @@ class WebUIRenderer:
| WINDOWS |
{html.escape(hosted_packages.get('windows', {}).get('filename') or 'LOGAR_Client_Setup.exe')} |
- v{html.escape(str(hosted_packages.get('windows', {}).get('version') or hosted_client_ver))} |
- {((hosted_packages.get('windows', {}).get('size_bytes', 0) // 1024) if hosted_packages.get('windows', {}).get('exists') else 0)} KB |
- {(hosted_packages.get('windows', {}).get('sha256', 'N/A')[:16] + '...') if hosted_packages.get('windows', {}).get('sha256') else 'N/A'} |
- {('Ready' if hosted_packages.get('windows', {}).get('exists') else 'Failed')} |
+ v{html.escape(str(hosted_packages.get('windows', {}).get('version') or hosted_client_ver))} |
+ {((hosted_packages.get('windows', {}).get('size_bytes', 0) // 1024) if hosted_packages.get('windows', {}).get('exists') else 0)} KB |
+ {(hosted_packages.get('windows', {}).get('sha256', 'N/A')[:16] + '...') if hosted_packages.get('windows', {}).get('sha256') else 'N/A'} |
+ {('Ready' if hosted_packages.get('windows', {}).get('exists') else 'Failed')} |
| LINUX |
{html.escape(hosted_packages.get('linux', {}).get('filename') or 'Linux_Client.bin')} |
- v{html.escape(str(hosted_packages.get('linux', {}).get('version') or hosted_client_ver))} |
- {((hosted_packages.get('linux', {}).get('size_bytes', 0) // 1024) if hosted_packages.get('linux', {}).get('exists') else 0)} KB |
- {(hosted_packages.get('linux', {}).get('sha256', 'N/A')[:16] + '...') if hosted_packages.get('linux', {}).get('sha256') else 'N/A'} |
- {('Ready' if hosted_packages.get('linux', {}).get('exists') else 'Failed')} |
+ v{html.escape(str(hosted_packages.get('linux', {}).get('version') or hosted_client_ver))} |
+ {((hosted_packages.get('linux', {}).get('size_bytes', 0) // 1024) if hosted_packages.get('linux', {}).get('exists') else 0)} KB |
+ {(hosted_packages.get('linux', {}).get('sha256', 'N/A')[:16] + '...') if hosted_packages.get('linux', {}).get('sha256') else 'N/A'} |
+ {('Ready' if hosted_packages.get('linux', {}).get('exists') else 'Failed')} |
@@ -1440,9 +1440,19 @@ class WebUIRenderer:
if (btn && pane) {{
btn.classList.add('active');
pane.classList.add('active');
+ try {{ localStorage.setItem('logar_active_tab', tabId); }} catch(e) {{}}
}}
}}
+ window.addEventListener('DOMContentLoaded', () => {{
+ try {{
+ const savedTab = localStorage.getItem('logar_active_tab');
+ if (savedTab && document.getElementById('tabBtn-' + savedTab)) {{
+ switchTab(savedTab);
+ }}
+ }} catch(e) {{}}
+ }});
+
function showAlert(msg, isSuccess = true) {{
const el = document.getElementById('statusAlert');
el.className = 'alert ' + (isSuccess ? 'alert-success' : 'alert-error');
@@ -1935,17 +1945,71 @@ class WebUIRenderer:
el.appendChild(img);
}}
+ function updatePackageTableInPlace(pkgs) {{
+ if (!pkgs) return;
+ if (pkgs.windows) {{
+ const w = pkgs.windows;
+ const vEl = document.getElementById('pkgWinVersion');
+ if (vEl && w.version) vEl.textContent = 'v' + w.version;
+ const szEl = document.getElementById('pkgWinSize');
+ if (szEl && w.size_bytes !== undefined) szEl.textContent = Math.floor((w.size_bytes || 0) / 1024) + ' KB';
+ const shaEl = document.getElementById('pkgWinSha');
+ if (shaEl && w.sha256) {{ shaEl.textContent = w.sha256.substring(0, 16) + '...'; shaEl.title = w.sha256; }}
+ const stEl = document.getElementById('pkgWinStatus');
+ if (stEl) stEl.innerHTML = w.exists ? 'Ready' : 'Failed';
+ }}
+ if (pkgs.linux) {{
+ const l = pkgs.linux;
+ const vEl = document.getElementById('pkgLinuxVersion');
+ if (vEl && l.version) vEl.textContent = 'v' + l.version;
+ const szEl = document.getElementById('pkgLinuxSize');
+ if (szEl && l.size_bytes !== undefined) szEl.textContent = Math.floor((l.size_bytes || 0) / 1024) + ' KB';
+ const shaEl = document.getElementById('pkgLinuxSha');
+ if (shaEl && l.sha256) {{ shaEl.textContent = l.sha256.substring(0, 16) + '...'; shaEl.title = l.sha256; }}
+ const stEl = document.getElementById('pkgLinuxStatus');
+ if (stEl) stEl.innerHTML = l.exists ? 'Ready' : 'Failed';
+ }}
+ }}
+
async function checkForUpdatesNow() {{
showAlert('Checking upstream repository for new releases...');
try {{
const res = await fetch('/api/server/update/check', {{ method: 'POST' }});
const data = await res.json();
+
+ const badgeEl = document.getElementById('upstreamReleaseStatusBadge');
+ if (badgeEl) {{
+ badgeEl.innerHTML = data.server_update_available
+ ? 'UPDATE AVAILABLE'
+ : 'UP TO DATE';
+ }}
+
+ const verEl = document.getElementById('latestUpstreamVersionText');
+ if (verEl && data.latest_upstream_version) {{
+ verEl.textContent = 'v' + data.latest_upstream_version;
+ }}
+
+ const checkEl = document.getElementById('lastCheckedText');
+ if (checkEl) {{
+ checkEl.textContent = data.last_checked_utc || (new Date().toISOString().replace('T', ' ').substring(0, 19) + ' UTC');
+ }}
+
+ const linkEl = document.getElementById('viewReleaseLinkContainer');
+ if (linkEl) {{
+ linkEl.innerHTML = data.release_url
+ ? '🌐 View Release on Gitea'
+ : '';
+ }}
+
+ if (data.hosted_packages) {{
+ updatePackageTableInPlace(data.hosted_packages);
+ }}
+
if (data.server_update_available) {{
showAlert('🔔 Update Available: v' + data.latest_upstream_version + ' is ready!');
}} else {{
- showAlert('✅ LOGAR is up to date (Current: v' + data.current_server_version + ').');
+ showAlert('✅ LOGAR is up to date (Current: v' + (data.current_server_version || '0.3.5.2') + ').');
}}
- setTimeout(() => location.reload(), 1200);
}} catch (err) {{
alert('Failed to check for updates: ' + err.message);
}}
@@ -1972,7 +2036,6 @@ class WebUIRenderer:
const data = await res.json();
if (res.ok && data.status === 'success') {{
showAlert('✅ Fleet update configuration saved successfully!');
- setTimeout(() => location.reload(), 1200);
}} else {{
let errMsg = 'Failed to save configuration';
if (typeof data.detail === 'string') errMsg = data.detail;
@@ -1992,7 +2055,14 @@ class WebUIRenderer:
if (res.ok && data.status === 'success') {{
const dl = (data.results && data.results.downloaded) ? data.results.downloaded.length : 0;
showAlert('✅ Upstream synchronization complete (' + dl + ' package(s) downloaded/verified)!');
- setTimeout(() => location.reload(), 1500);
+
+ try {{
+ const statusRes = await fetch('/api/server/update/status');
+ const statusData = await statusRes.json();
+ if (statusData && statusData.hosted_packages) {{
+ updatePackageTableInPlace(statusData.hosted_packages);
+ }}
+ }} catch(e) {{}}
}} else {{
let errMsg = 'Synchronization failed';
if (typeof data.detail === 'string') errMsg = data.detail;