sdsad
All checks were successful
Deploy / deploy-staging (push) Successful in 6s
Deploy / deploy-production (push) Has been skipped

This commit is contained in:
2026-04-27 02:23:44 +02:00
parent 0174fa9d27
commit f4fa8acb97
2 changed files with 134 additions and 2 deletions

View File

@@ -198,6 +198,47 @@
color: var(--muted); color: var(--muted);
} }
.pihole-console-modal {
width: min(720px, 96vw);
}
.pihole-console-body {
margin-top: 16px;
display: grid;
gap: 10px;
max-height: 48vh;
overflow: auto;
padding: 4px;
}
.pihole-console-line {
display: grid;
gap: 4px;
padding: 12px 14px;
border-radius: 14px;
border: 1px solid var(--line);
background: rgba(255, 255, 255, 0.6);
}
.pihole-console-line span {
font-size: 0.8rem;
color: var(--muted);
}
.pihole-console-line strong {
font-weight: 600;
}
.pihole-console-line.is-success {
border-color: rgba(0, 179, 164, 0.22);
background: rgba(0, 179, 164, 0.08);
}
.pihole-console-line.is-error {
border-color: rgba(255, 90, 61, 0.24);
background: rgba(255, 90, 61, 0.08);
}
.pihole-instance-card { .pihole-instance-card {
padding: 16px; padding: 16px;
background: var(--panel-2); background: var(--panel-2);

View File

@@ -19,6 +19,9 @@
let refreshTimer = null; let refreshTimer = null;
let loadInFlight = false; let loadInFlight = false;
let actionInFlight = false; let actionInFlight = false;
let actionConsoleApi = null;
let actionConsoleBody = null;
let actionConsoleClose = null;
const apiCall = async (action, payload = {}) => { const apiCall = async (action, payload = {}) => {
const res = await fetch(`/module/pihole/api?action=${encodeURIComponent(action)}`, const res = await fetch(`/module/pihole/api?action=${encodeURIComponent(action)}`,
@@ -47,9 +50,82 @@
el.textContent = value; el.textContent = value;
}; };
const ensureActionConsole = () => {
if (actionConsoleApi && actionConsoleBody && actionConsoleClose) {
return;
}
const root = document.createElement('div');
root.className = 'modal';
root.dataset.piholeConsoleModal = '1';
root.setAttribute('aria-hidden', 'true');
root.innerHTML = `
<div class="modal-card pihole-console-modal" role="dialog" aria-modal="true" aria-labelledby="pihole-console-title">
<div class="modal-header">
<div>
<strong id="pihole-console-title">Pi-hole Aktion</strong>
<div class="muted">Status und Rueckmeldungen zur laufenden Aktion.</div>
</div>
<div class="modal-actions">
<button class="module-button module-button--secondary module-button--small" type="button" data-pihole-console-clear>Leeren</button>
<button class="icon-button" type="button" data-pihole-console-close aria-label="Konsole schliessen">×</button>
</div>
</div>
<div class="pihole-console-body" data-pihole-console-body></div>
</div>
`;
document.body.appendChild(root);
actionConsoleBody = root.querySelector('[data-pihole-console-body]');
actionConsoleClose = root.querySelector('[data-pihole-console-close]');
const clearBtn = root.querySelector('[data-pihole-console-clear]');
actionConsoleApi = window.NexusModal && typeof window.NexusModal.create === 'function'
? window.NexusModal.create(root, { initialFocus: '[data-pihole-console-close]' })
: {
open() {
root.classList.add('is-open');
root.setAttribute('aria-hidden', 'false');
},
close() {
root.classList.remove('is-open');
root.setAttribute('aria-hidden', 'true');
},
};
if (actionConsoleClose) {
actionConsoleClose.addEventListener('click', () => {
if (!actionInFlight) {
actionConsoleApi.close();
}
});
}
if (clearBtn) {
clearBtn.addEventListener('click', () => {
if (actionConsoleBody) {
actionConsoleBody.innerHTML = '';
}
});
}
};
const appendActionLog = (message, tone = 'info') => {
ensureActionConsole();
if (!actionConsoleBody) {
return;
}
const row = document.createElement('div');
row.className = `pihole-console-line is-${tone}`;
row.innerHTML = `<span>${new Date().toLocaleTimeString('de-DE')}</span><strong>${message}</strong>`;
actionConsoleBody.appendChild(row);
actionConsoleBody.scrollTop = actionConsoleBody.scrollHeight;
};
const setActionLock = (locked, message = 'Bitte warten ...') => { const setActionLock = (locked, message = 'Bitte warten ...') => {
actionInFlight = locked; actionInFlight = locked;
page.classList.toggle('is-busy', locked); page.classList.toggle('is-busy', locked);
ensureActionConsole();
let overlay = page.querySelector('[data-pihole-busy-overlay]'); let overlay = page.querySelector('[data-pihole-busy-overlay]');
if (!overlay) { if (!overlay) {
@@ -67,6 +143,9 @@
} }
overlay.hidden = !locked; overlay.hidden = !locked;
if (actionConsoleClose) {
actionConsoleClose.disabled = locked;
}
page.querySelectorAll('button, input, select, textarea').forEach((el) => { page.querySelectorAll('button, input, select, textarea').forEach((el) => {
const formControl = el; const formControl = el;
@@ -229,26 +308,38 @@
: action === 'gravity' : action === 'gravity'
? 'Listen werden aktualisiert ...' ? 'Listen werden aktualisiert ...'
: 'Aktion wird ausgefuehrt ...'; : 'Aktion wird ausgefuehrt ...';
ensureActionConsole();
if (actionConsoleBody) {
actionConsoleBody.innerHTML = '';
}
actionConsoleApi.open();
appendActionLog(actionLabel, 'info');
setActionLock(true, actionLabel); setActionLock(true, actionLabel);
if (action === 'enable') { if (action === 'enable') {
appendActionLog(`Aktiviere ${instance === 'all' ? 'alle Instanzen' : `Instanz ${instance}`}.`, 'info');
await apiCall('enable', payload); await apiCall('enable', payload);
} else if (action === 'disable' || action === 'disable-custom') { } else if (action === 'disable' || action === 'disable-custom') {
if (!payload.minutes || payload.minutes <= 0) { if (!payload.minutes || payload.minutes <= 0) {
alert('Bitte Minuten angeben.'); appendActionLog('Fehler: Bitte Minuten angeben.', 'error');
return; return;
} }
appendActionLog(`Deaktiviere ${instance === 'all' ? 'alle Instanzen' : `Instanz ${instance}`} fuer ${payload.minutes} Minuten.`, 'info');
await apiCall('disable', payload); await apiCall('disable', payload);
} else if (action === 'gravity') { } else if (action === 'gravity') {
appendActionLog(`Starte Listen-Update fuer ${instance === 'all' ? 'alle Instanzen' : `Instanz ${instance}`}.`, 'info');
await apiCall('gravity', payload); await apiCall('gravity', payload);
const status = document.querySelector('[data-list-update-status]'); const status = document.querySelector('[data-list-update-status]');
if (status) status.textContent = 'Listen-Update gestartet.'; if (status) status.textContent = 'Listen-Update gestartet.';
} else if (action === 'update') { } else if (action === 'update') {
appendActionLog(`Starte Pi-hole-Update fuer ${instance === 'all' ? 'alle Instanzen' : `Instanz ${instance}`}.`, 'info');
await apiCall('update', payload); await apiCall('update', payload);
} }
appendActionLog('Aktion abgeschlossen. Dashboard wird aktualisiert.', 'success');
await loadDashboard(); await loadDashboard();
appendActionLog('Anzeige erfolgreich aktualisiert.', 'success');
} catch (err) { } catch (err) {
alert(`Aktion fehlgeschlagen: ${err.message}`); appendActionLog(`Aktion fehlgeschlagen: ${err.message}`, 'error');
} finally { } finally {
setActionLock(false); setActionLock(false);
} }