merge: sync with upstream/main and resolve conflicts

This commit is contained in:
jgor20
2026-01-11 14:28:36 +00:00
2 changed files with 73 additions and 3 deletions

View File

@@ -17,6 +17,7 @@ document.addEventListener('alpine:init', () => {
initialLoad: true, // Track first load for skeleton screen
connectionStatus: 'connecting',
lastUpdated: '-',
healthCheckTimer: null,
// Filters state
filters: {
@@ -39,6 +40,9 @@ document.addEventListener('alpine:init', () => {
// Watch filters to recompute
// Alpine stores don't have $watch automatically unless inside a component?
// We can manually call compute when filters change.
// Start health check monitoring
this.startHealthCheck();
},
loadFromCache() {
@@ -111,7 +115,6 @@ document.addEventListener('alpine:init', () => {
this.saveToCache(); // Save fresh data
this.computeQuotaRows();
this.connectionStatus = 'connected';
this.lastUpdated = new Date().toLocaleTimeString();
// Fetch version from config endpoint if not already loaded
@@ -120,7 +123,6 @@ document.addEventListener('alpine:init', () => {
}
} catch (error) {
console.error('Fetch error:', error);
this.connectionStatus = 'disconnected';
const store = Alpine.store('global');
store.showToast(store.t('connectionLost'), 'error');
} finally {
@@ -143,6 +145,67 @@ document.addEventListener('alpine:init', () => {
}
},
async performHealthCheck() {
try {
// Get password from global store
const password = Alpine.store('global').webuiPassword;
// Use lightweight endpoint (no quota fetching)
const { response, newPassword } = await window.utils.request('/api/config', {}, password);
if (newPassword) Alpine.store('global').webuiPassword = newPassword;
if (response.ok) {
this.connectionStatus = 'connected';
} else {
this.connectionStatus = 'disconnected';
}
} catch (error) {
console.error('Health check error:', error);
this.connectionStatus = 'disconnected';
}
},
startHealthCheck() {
// Clear existing timer
if (this.healthCheckTimer) {
clearInterval(this.healthCheckTimer);
}
// Setup visibility change listener (only once)
if (!this._healthVisibilitySetup) {
this._healthVisibilitySetup = true;
this._visibilityHandler = () => {
if (document.hidden) {
// Tab hidden - stop health checks
this.stopHealthCheck();
} else {
// Tab visible - restart health checks
this.startHealthCheck();
}
};
document.addEventListener('visibilitychange', this._visibilityHandler);
}
// Perform immediate health check
this.performHealthCheck();
// Schedule regular health checks every 15 seconds
this.healthCheckTimer = setInterval(() => {
// Only perform health check if tab is visible
if (!document.hidden) {
this.performHealthCheck();
}
}, 15000);
},
stopHealthCheck() {
if (this.healthCheckTimer) {
clearInterval(this.healthCheckTimer);
this.healthCheckTimer = null;
}
},
computeQuotaRows() {
const models = this.models || [];
const rows = [];
@@ -312,6 +375,13 @@ document.addEventListener('alpine:init', () => {
});
return rows;
},
destroy() {
this.stopHealthCheck();
if (this._visibilityHandler) {
document.removeEventListener('visibilitychange', this._visibilityHandler);
}
}
});
});