import { h, PageHeader, Badge, StatusDot, Empty, Card, esc, enc, att_esc, $val, apiFetch, toast, dismissToast, openModal, closeModal, formModal, definePage, hComp, connect, reactive, requestUpdate, Link, createRouter, ToastContainer, render, parseZones } from '/static/hoover/index.js'; const logTabs = [ { key: 'journal', label: 'Journal', url: '/api/logs/journal' }, { key: 'nginx-access', label: 'Nginx Access', url: '/api/logs/nginx/access' }, { key: 'nginx-error', label: 'Nginx Error', url: '/api/logs/nginx/error' }, { key: 'dnsmasq', label: 'Dnsmasq', url: '/api/logs/dnsmasq' }, { key: 'app', label: 'App', url: '/api/logs/app' }, ]; async function fetchLog(state, url, signal) { state.loading = true; state.error = null; try { const res = await fetch(url, { signal }); if (signal?.aborted) return; const text = await res.text(); if (signal?.aborted) return; state.lines = text.split('\n').filter(l => l.length > 0); } catch (e) { if (signal?.aborted) return; state.error = String(e); } state.loading = false; state.refreshing = false; } export default definePage({ init() { return { activeTab: 'journal', lines: [], loading: false, refreshing: false, error: null }; }, subscribe: [], async load(state, abortController, entry) { if (state.lines?.length) state.refreshing = true; else state.loading = true; const myId = entry ? entry.requestId : 0; const tab = logTabs.find(t => t.key === state.activeTab) || logTabs[0]; await fetchLog(state, tab.url, abortController?.signal); if (abortController?.signal.aborted || (entry && entry.requestId !== myId)) return; }, onUnmount(state) { state.lines = []; }, render(state) { const tab = logTabs.find(t => t.key === state.activeTab) || logTabs[0]; const lineVnodes = state.lines.map((line, i) => h('div', { class: 'log-line', key: i }, esc(line)) ); return [ PageHeader({ title: 'Logs', subtitle: 'System & service logs' }), h('div', { class: 'tabs', key: 'log-tabs' }, logTabs.map(t => h('span', { class: 'tab ' + (state.activeTab === t.key ? 'active' : ''), 'on:click': async () => { state.activeTab = t.key; await fetchLog(state, t.url); }, style: 'cursor:pointer;', }, t.label)) ), h('div', { class: 'card', key: 'log-card' }, h('div', { class: 'card-header' }, h('span', null, tab.label), h('button', { class: 'btn btn-sm btn-outline', style: 'float:right;', 'on:click': async () => { await fetchLog(state, tab.url); }, }, '\u21BB') ), h('div', { class: 'card-body log-body' }, state.loading && !state.refreshing ? h('div', { class: 'loading' }, state.refreshing ? 'Refreshing...' : 'Loading...') : state.error ? h('div', { class: 'error-msg' }, state.error) : lineVnodes.length > 0 ? h('pre', null, lineVnodes) : h('div', { class: 'text-muted text-sm' }, 'No log lines available') ) ), ]; }, });