Files
antigravity-claude-proxy/public/js/app-init.js
jgor20 69440584fd feat(webui): add health check monitoring for connection status
Implement periodic health checks every 15 seconds to monitor connection status,
pausing when the tab is hidden and resuming on visibility. Update UI bindings
to use data store for connection status instead of global store. Add destroy
method to clean up timers on component teardown.
2026-01-10 22:41:15 +00:00

138 lines
5.3 KiB
JavaScript

/**
* App Initialization (Non-module version)
* This must load BEFORE Alpine initializes
*/
document.addEventListener('alpine:init', () => {
// App component registration
// Main App Controller
Alpine.data('app', () => ({
// Re-expose store properties for easier access in navbar
get connectionStatus() {
return Alpine.store('data').connectionStatus;
},
get loading() {
return Alpine.store('data').loading;
},
init() {
// App component initialization
// Theme setup
document.documentElement.setAttribute('data-theme', 'black');
document.documentElement.classList.add('dark');
// Chart Defaults
if (typeof Chart !== 'undefined') {
Chart.defaults.color = window.utils.getThemeColor('--color-text-dim');
Chart.defaults.borderColor = window.utils.getThemeColor('--color-space-border');
Chart.defaults.font.family = '"JetBrains Mono", monospace';
}
// Start Data Polling
this.startAutoRefresh();
document.addEventListener('refresh-interval-changed', () => this.startAutoRefresh());
// Initial Data Fetch (separate from health check)
Alpine.store('data').fetchData();
},
refreshTimer: null,
isTabVisible: true,
fetchData() {
Alpine.store('data').fetchData();
},
startAutoRefresh() {
if (this.refreshTimer) clearInterval(this.refreshTimer);
const baseInterval = parseInt(Alpine.store('settings').refreshInterval);
if (baseInterval > 0) {
// Setup visibility change listener (only once)
if (!this._visibilitySetup) {
this._visibilitySetup = true;
document.addEventListener('visibilitychange', () => {
this.isTabVisible = !document.hidden;
if (this.isTabVisible) {
// Tab became visible - fetch immediately and restart timer
Alpine.store('data').fetchData();
this.startAutoRefresh();
}
});
}
// Schedule next refresh with jitter
const scheduleNext = () => {
// Add ±20% random jitter to prevent synchronized requests
const jitter = (Math.random() - 0.5) * 0.4; // -0.2 to +0.2
const interval = baseInterval * (1 + jitter);
// Slow down when tab is hidden (reduce frequency by 3x)
const actualInterval = this.isTabVisible
? interval
: interval * 3;
this.refreshTimer = setTimeout(() => {
Alpine.store('data').fetchData();
scheduleNext(); // Reschedule with new jitter
}, actualInterval * 1000);
};
scheduleNext();
}
},
// Translation helper for modal (not in a component scope)
t(key) {
return Alpine.store('global').t(key);
},
// Add account handler for modal
async addAccountWeb(reAuthEmail = null) {
const password = Alpine.store('global').webuiPassword;
try {
const urlPath = reAuthEmail
? `/api/auth/url?email=${encodeURIComponent(reAuthEmail)}`
: '/api/auth/url';
const { response, newPassword } = await window.utils.request(urlPath, {}, password);
if (newPassword) Alpine.store('global').webuiPassword = newPassword;
const data = await response.json();
if (data.status === 'ok') {
const width = 600;
const height = 700;
const left = (screen.width - width) / 2;
const top = (screen.height - height) / 2;
window.open(
data.url,
'google_oauth',
`width=${width},height=${height},top=${top},left=${left},scrollbars=yes`
);
const messageHandler = (event) => {
if (event.data?.type === 'oauth-success') {
const action = reAuthEmail ? 're-authenticated' : 'added';
Alpine.store('global').showToast(`Account ${event.data.email} ${action} successfully`, 'success');
Alpine.store('data').fetchData();
const modal = document.getElementById('add_account_modal');
if (modal) modal.close();
}
};
window.addEventListener('message', messageHandler);
setTimeout(() => window.removeEventListener('message', messageHandler), 300000);
} else {
Alpine.store('global').showToast(data.error || 'Failed to get auth URL', 'error');
}
} catch (e) {
Alpine.store('global').showToast('Failed to start OAuth flow: ' + e.message, 'error');
}
}
}));
});