/* Atlus — Service Manager app */ (function () { 'use strict'; let container = null; let listEl = null; let searchInput = null; let allServices = []; let filterActive = false; async function loadServices() { try { const res = await Atlus.apiFetch('/api/services'); if (!res.ok) return; allServices = await res.json(); renderServices(); } catch (e) {} } function renderServices() { if (!listEl) return; const query = searchInput ? searchInput.value.toLowerCase() : ''; let filtered = allServices; if (query) { filtered = filtered.filter(s => s.name.toLowerCase().includes(query) || s.description.toLowerCase().includes(query) ); } if (filterActive) { filtered = filtered.filter(s => s.active === 'active'); } listEl.innerHTML = ''; // Header const header = document.createElement('div'); header.className = 'services-header'; header.innerHTML = 'ServiceStateSub'; listEl.appendChild(header); filtered.forEach(svc => { const row = document.createElement('div'); row.className = 'service-row'; const isActive = svc.active === 'active'; const stateClass = svc.active === 'active' ? 'active' : svc.active === 'failed' ? 'failed' : 'inactive'; row.innerHTML = `
${svc.name}
${svc.description}
${svc.active} ${svc.sub}
`; // Toggle handler const toggle = row.querySelector('.service-toggle'); toggle.addEventListener('click', async () => { const action = toggle.classList.contains('on') ? 'stop' : 'start'; await Atlus.apiFetch('/api/services/action', { method: 'POST', body: { unit: svc.unit, action }, }); loadServices(); }); // Restart handler const restartBtn = row.querySelector('[data-action="restart"]'); restartBtn.addEventListener('click', async () => { await Atlus.apiFetch('/api/services/action', { method: 'POST', body: { unit: svc.unit, action: 'restart' }, }); loadServices(); }); listEl.appendChild(row); }); } Atlus.registerApp('services', { title: 'Services', init(el) { container = el; container.classList.add('app-services'); // Toolbar const toolbar = document.createElement('div'); toolbar.className = 'services-toolbar'; searchInput = document.createElement('input'); searchInput.className = 'services-search'; searchInput.type = 'text'; searchInput.placeholder = 'Search services…'; searchInput.addEventListener('input', renderServices); toolbar.appendChild(searchInput); const filterBtn = document.createElement('button'); filterBtn.className = 'services-filter-btn'; filterBtn.textContent = 'Active'; filterBtn.addEventListener('click', () => { filterActive = !filterActive; filterBtn.classList.toggle('active', filterActive); renderServices(); }); toolbar.appendChild(filterBtn); container.appendChild(toolbar); // List listEl = document.createElement('div'); listEl.className = 'services-list'; container.appendChild(listEl); loadServices(); }, destroy() { container = null; listEl = null; searchInput = null; allServices = []; filterActive = false; }, }); // Also register as "network" since it's a similar list-based view Atlus.registerApp('network', { title: 'Network', init(el) { el.classList.add('app-view'); el.style.padding = '24px'; el.style.overflow = 'auto'; el.innerHTML = '
Loading network info…
'; Atlus.apiFetch('/api/stats').then(res => res.json()).then(data => { let html = '
'; const ifaces = data.network.interfaces; for (const [name, info] of Object.entries(ifaces)) { html += `
${name}
Status ${info.up ? 'Up' : 'Down'} IPv4 ${info.ipv4 || '--'} IPv6 ${info.ipv6 || '--'}
`; } html += `
Total sent: ${Atlus.formatBytes(data.network.bytes_sent)} · Total recv: ${Atlus.formatBytes(data.network.bytes_recv)}
`; html += '
'; el.innerHTML = html; }); }, destroy() {}, }); })();