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

This commit is contained in:
2026-06-24 00:24:47 +02:00
parent 80874e2e1d
commit fa48a381ff
2 changed files with 81 additions and 13 deletions

View File

@@ -305,12 +305,24 @@
background: rgba(255, 255, 255, 0.98); background: rgba(255, 255, 255, 0.98);
box-shadow: 0 28px 64px rgba(15, 23, 42, 0.18); box-shadow: 0 28px 64px rgba(15, 23, 42, 0.18);
padding: 20px; padding: 20px;
color: #0f172a;
} }
.modal-header { .modal-header {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
gap: 16px; gap: 16px;
align-items: flex-start;
}
.pihole-console-heading {
display: grid;
gap: 6px;
}
.pihole-console-heading strong {
font-size: 1.05rem;
color: #0f172a;
} }
.icon-button { .icon-button {
@@ -318,9 +330,11 @@
border-radius: 12px; border-radius: 12px;
background: #fff; background: #fff;
color: #0f172a; color: #0f172a;
width: 40px; min-width: 110px;
height: 40px; height: 40px;
cursor: pointer; cursor: pointer;
font: inherit;
font-weight: 600;
} }
.pihole-console-body { .pihole-console-body {
@@ -339,6 +353,14 @@
border-radius: 14px; border-radius: 14px;
border: 1px solid rgba(148, 163, 184, 0.24); border: 1px solid rgba(148, 163, 184, 0.24);
background: rgba(255, 255, 255, 0.75); background: rgba(255, 255, 255, 0.75);
color: #0f172a;
}
.pihole-console-meta {
display: flex;
justify-content: space-between;
gap: 12px;
flex-wrap: wrap;
} }
.pihole-console-line span { .pihole-console-line span {
@@ -348,6 +370,12 @@
.pihole-console-line strong { .pihole-console-line strong {
font-weight: 600; font-weight: 600;
color: #0f172a;
}
.pihole-console-detail {
font-size: 0.92rem;
color: #334155;
} }
.pihole-console-line.is-success { .pihole-console-line.is-success {

View File

@@ -27,6 +27,8 @@
let actionConsoleApi = null; let actionConsoleApi = null;
let actionConsoleBody = null; let actionConsoleBody = null;
let actionConsoleClose = null; let actionConsoleClose = null;
let actionConsoleTitle = null;
let actionConsoleSubtitle = null;
const apiCall = async (action, payload = {}) => { const apiCall = async (action, payload = {}) => {
const res = await fetch(`/api/pihole/index.php?action=${encodeURIComponent(action)}`, { const res = await fetch(`/api/pihole/index.php?action=${encodeURIComponent(action)}`, {
@@ -64,13 +66,13 @@
root.innerHTML = ` root.innerHTML = `
<div class="modal-card pihole-console-modal" role="dialog" aria-modal="true" aria-labelledby="pihole-console-title"> <div class="modal-card pihole-console-modal" role="dialog" aria-modal="true" aria-labelledby="pihole-console-title">
<div class="modal-header"> <div class="modal-header">
<div> <div class="pihole-console-heading">
<strong id="pihole-console-title">Pi-hole Aktion</strong> <strong id="pihole-console-title">Pi-hole Aktion</strong>
<div class="muted">Status und Rueckmeldungen zur laufenden Aktion.</div> <div class="muted" data-pihole-console-subtitle>Status und Rueckmeldungen zur laufenden Aktion.</div>
</div> </div>
<div class="pihole-card-actions"> <div class="pihole-card-actions">
<button class="pihole-link-button" type="button" data-pihole-console-clear>Leeren</button> <button class="pihole-link-button" type="button" data-pihole-console-clear>Protokoll leeren</button>
<button class="icon-button" type="button" data-pihole-console-close aria-label="Konsole schliessen">×</button> <button class="icon-button" type="button" data-pihole-console-close aria-label="Fenster schliessen">Schliessen</button>
</div> </div>
</div> </div>
<div class="pihole-console-body" data-pihole-console-body></div> <div class="pihole-console-body" data-pihole-console-body></div>
@@ -78,6 +80,8 @@
`; `;
document.body.appendChild(root); document.body.appendChild(root);
actionConsoleTitle = root.querySelector('#pihole-console-title');
actionConsoleSubtitle = root.querySelector('[data-pihole-console-subtitle]');
actionConsoleBody = root.querySelector('[data-pihole-console-body]'); actionConsoleBody = root.querySelector('[data-pihole-console-body]');
actionConsoleClose = root.querySelector('[data-pihole-console-close]'); actionConsoleClose = root.querySelector('[data-pihole-console-close]');
const clearBtn = root.querySelector('[data-pihole-console-clear]'); const clearBtn = root.querySelector('[data-pihole-console-clear]');
@@ -102,12 +106,33 @@
}); });
}; };
const appendActionLog = (message, tone = 'info') => { const setActionConsoleMeta = (title, subtitle) => {
ensureActionConsole();
if (actionConsoleTitle) actionConsoleTitle.textContent = title || 'Pi-hole Aktion';
if (actionConsoleSubtitle) actionConsoleSubtitle.textContent = subtitle || 'Status und Rueckmeldungen zur laufenden Aktion.';
};
const appendActionLog = (message, tone = 'info', detail = '') => {
ensureActionConsole(); ensureActionConsole();
if (!actionConsoleBody) return; if (!actionConsoleBody) return;
const row = document.createElement('div'); const row = document.createElement('div');
row.className = `pihole-console-line is-${tone}`; row.className = `pihole-console-line is-${tone}`;
row.innerHTML = `<span>${new Date().toLocaleTimeString('de-DE')}</span><strong>${message}</strong>`; const timestamp = new Date().toLocaleString('de-DE', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
});
row.innerHTML = `
<div class="pihole-console-meta">
<span>${timestamp}</span>
<span>${tone === 'success' ? 'Erfolg' : tone === 'error' ? 'Fehler' : 'Info'}</span>
</div>
<strong>${message}</strong>
${detail ? `<div class="pihole-console-detail">${detail}</div>` : ''}
`;
actionConsoleBody.appendChild(row); actionConsoleBody.appendChild(row);
actionConsoleBody.scrollTop = actionConsoleBody.scrollHeight; actionConsoleBody.scrollTop = actionConsoleBody.scrollHeight;
}; };
@@ -138,6 +163,20 @@
return 'Unbekannt'; return 'Unbekannt';
}; };
const actionLabel = (action) => {
if (action === 'enable') return 'Pi-hole aktivieren';
if (action === 'disable' || action === 'disable-custom') return 'Pi-hole temporaer deaktivieren';
if (action === 'gravity') return 'Listen aktualisieren';
if (action === 'update') return 'Pi-hole aktualisieren';
return 'Pi-hole Aktion';
};
const targetLabel = (instance) => {
if (!instance || instance === 'all') return 'alle Instanzen';
if (instance === 'primary') return 'die Primaer-Instanz';
return `Instanz ${instance}`;
};
const setStatusBadge = (el, status) => { const setStatusBadge = (el, status) => {
if (!el) return; if (!el) return;
el.textContent = statusLabel(status); el.textContent = statusLabel(status);
@@ -345,34 +384,35 @@
try { try {
ensureActionConsole(); ensureActionConsole();
if (actionConsoleBody) actionConsoleBody.innerHTML = ''; if (actionConsoleBody) actionConsoleBody.innerHTML = '';
setActionConsoleMeta(actionLabel(action), `Rueckmeldungen fuer ${targetLabel(instance)}.`);
actionConsoleApi.open(); actionConsoleApi.open();
setActionLock(true); setActionLock(true);
baselineData = action === 'gravity' ? await apiCall('dashboard').catch(() => null) : null; baselineData = action === 'gravity' ? await apiCall('dashboard').catch(() => null) : null;
if (action === 'enable') { if (action === 'enable') {
appendActionLog(`Aktiviere ${instance === 'all' ? 'alle Instanzen' : `Instanz ${instance}`}.`, 'info'); appendActionLog('Aktivierung wird ausgefuehrt.', 'info', `Ziel: ${targetLabel(instance)}`);
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) {
appendActionLog('Fehler: Bitte Minuten angeben.', 'error'); appendActionLog('Fehler: Bitte Minuten angeben.', 'error');
return; return;
} }
appendActionLog(`Deaktiviere ${instance === 'all' ? 'alle Instanzen' : `Instanz ${instance}`} fuer ${payload.minutes} Minuten.`, 'info'); appendActionLog('Temporaere Deaktivierung wird ausgefuehrt.', 'info', `Ziel: ${targetLabel(instance)} | Dauer: ${payload.minutes} Minuten`);
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'); appendActionLog('Listen-Update wurde gestartet.', 'info', `Ziel: ${targetLabel(instance)}`);
await apiCall('gravity', payload); await apiCall('gravity', payload);
const status = query('[data-list-update-status]'); const status = query('[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'); appendActionLog('Pi-hole-Update wurde gestartet.', 'info', `Ziel: ${targetLabel(instance)}`);
await apiCall('update', payload); await apiCall('update', payload);
} }
appendActionLog('Aktion abgeschlossen. Dashboard wird aktualisiert.', 'success'); appendActionLog('Aktion erfolgreich abgeschlossen.', 'success', 'Dashboard wird jetzt aktualisiert.');
if (action === 'gravity') { if (action === 'gravity') {
await monitorGravityProgress(baselineData, instance === 'all' ? 'alle Instanzen' : `Instanz ${instance}`); await monitorGravityProgress(baselineData, targetLabel(instance));
} else { } else {
await loadDashboard(); await loadDashboard();
} }