163 lines
5.3 KiB
Plaintext
163 lines
5.3 KiB
Plaintext
(function () {
|
|
'use strict';
|
|
|
|
async function init() {
|
|
document.getElementById('loading').classList.remove('hidden');
|
|
try {
|
|
await Util.fetchCsrfToken();
|
|
const [config] = await Promise.all([
|
|
fetch('/api/config', { credentials: 'same-origin' }).then(r => r.json()),
|
|
DiscordFields.fetchGuildData()
|
|
]);
|
|
Fields.setSavedConfig(config);
|
|
document.getElementById('bot-status-dot').className = 'dot online';
|
|
document.getElementById('bot-status-text').textContent = 'Connected';
|
|
Fields.populateFields(config);
|
|
Notifications.initNotificationsEditor(config);
|
|
Fields.initSmartSelects(config);
|
|
} catch (e) {
|
|
document.getElementById('bot-status-dot').className = 'dot offline';
|
|
document.getElementById('bot-status-text').textContent = 'Unreachable';
|
|
}
|
|
document.getElementById('loading').classList.add('hidden');
|
|
setupSectionToggles();
|
|
Fields.setupSaveBar();
|
|
}
|
|
|
|
function setupSectionToggles() {
|
|
document.querySelectorAll('.section-header').forEach(header => {
|
|
header.addEventListener('click', () => {
|
|
header.closest('.section').classList.toggle('collapsed');
|
|
});
|
|
});
|
|
}
|
|
|
|
function openScheduleModal() {
|
|
const modal = document.getElementById('schedule-modal');
|
|
const dt = document.getElementById('schedule-datetime');
|
|
const min = Util.formatLocalDateTime(new Date(Date.now() + 60000));
|
|
dt.min = min;
|
|
dt.value = min;
|
|
Util.openModal(modal, { initialFocus: '#schedule-datetime' });
|
|
}
|
|
|
|
async function confirmScheduledRestart() {
|
|
const dt = document.getElementById('schedule-datetime').value;
|
|
if (!dt) return;
|
|
await fetch('/api/restart', {
|
|
method: 'POST',
|
|
credentials: 'same-origin',
|
|
headers: Util.csrfHeaders({ 'Content-Type': 'application/json' }),
|
|
body: JSON.stringify({ mode: 'scheduled', scheduledFor: new Date(dt).toISOString() })
|
|
});
|
|
Util.closeModal(document.getElementById('schedule-modal'));
|
|
Util.showToast(`Restart scheduled for ${new Date(dt).toLocaleString()}`, 'warning');
|
|
}
|
|
|
|
async function doLogout() {
|
|
try {
|
|
await fetch('/logout', {
|
|
method: 'POST',
|
|
credentials: 'same-origin',
|
|
headers: Util.csrfHeaders()
|
|
});
|
|
} catch (e) { /* ignore */ }
|
|
window.location.href = '/login';
|
|
}
|
|
|
|
function setupActionButtons() {
|
|
document.getElementById('save-btn')?.addEventListener('click', () => Fields.saveConfig('save'));
|
|
document.getElementById('save-restart-btn')?.addEventListener('click', () => Fields.saveConfig('restart'));
|
|
document.getElementById('schedule-restart-btn')?.addEventListener('click', openScheduleModal);
|
|
document.getElementById('schedule-confirm-btn')?.addEventListener('click', confirmScheduledRestart);
|
|
document.getElementById('schedule-cancel-btn')?.addEventListener('click', () => {
|
|
Util.closeModal(document.getElementById('schedule-modal'));
|
|
});
|
|
document.getElementById('logout-btn')?.addEventListener('click', doLogout);
|
|
}
|
|
|
|
function setupMobileNav() {
|
|
const toggle = document.getElementById('menu-toggle');
|
|
const backdrop = document.getElementById('sidebar-backdrop');
|
|
|
|
toggle?.addEventListener('click', () => {
|
|
Util.setSidebarOpen(!document.body.classList.contains('sidebar-open'));
|
|
});
|
|
backdrop?.addEventListener('click', () => Util.setSidebarOpen(false));
|
|
document.addEventListener('keydown', (e) => {
|
|
if (e.key === 'Escape' && document.body.classList.contains('sidebar-open')) {
|
|
Util.setSidebarOpen(false);
|
|
}
|
|
});
|
|
window.addEventListener('resize', () => {
|
|
if (!Util.isMobileViewport() && document.body.classList.contains('sidebar-open')) {
|
|
Util.setSidebarOpen(false);
|
|
}
|
|
});
|
|
}
|
|
|
|
let healthPollHandle = null;
|
|
|
|
function setBotStatus(online) {
|
|
const dot = document.getElementById('bot-status-dot');
|
|
const text = document.getElementById('bot-status-text');
|
|
if (!dot || !text) return;
|
|
dot.className = online ? 'dot online' : 'dot offline';
|
|
text.textContent = online ? 'Connected' : 'Unreachable';
|
|
}
|
|
|
|
async function pollHealth() {
|
|
try {
|
|
const res = await fetch('/healthz', { credentials: 'same-origin' });
|
|
if (res.ok) {
|
|
const data = await res.json();
|
|
setBotStatus(Boolean(data.bot));
|
|
} else {
|
|
setBotStatus(false);
|
|
}
|
|
} catch (_) {
|
|
setBotStatus(false);
|
|
}
|
|
}
|
|
|
|
function scheduleNextHealthPoll() {
|
|
if (document.hidden) return;
|
|
healthPollHandle = setTimeout(async () => {
|
|
await pollHealth();
|
|
scheduleNextHealthPoll();
|
|
}, 20000);
|
|
}
|
|
|
|
function startHealthPolling() {
|
|
if (healthPollHandle) clearTimeout(healthPollHandle);
|
|
scheduleNextHealthPoll();
|
|
}
|
|
|
|
function stopHealthPolling() {
|
|
if (healthPollHandle) {
|
|
clearTimeout(healthPollHandle);
|
|
healthPollHandle = null;
|
|
}
|
|
}
|
|
|
|
function setupHealthPolling() {
|
|
document.addEventListener('visibilitychange', () => {
|
|
if (document.hidden) stopHealthPolling();
|
|
else startHealthPolling();
|
|
});
|
|
window.addEventListener('pagehide', stopHealthPolling);
|
|
startHealthPolling();
|
|
}
|
|
|
|
document.addEventListener('DOMContentLoaded', async () => {
|
|
Router.setupSidebarRouting();
|
|
setupActionButtons();
|
|
setupMobileNav();
|
|
await init();
|
|
Router.navigate(location.pathname, false);
|
|
setupHealthPolling();
|
|
});
|
|
|
|
window.App = { init };
|
|
})();
|