545 lines
20 KiB
JavaScript
545 lines
20 KiB
JavaScript
// JavaScript für Medi-WOL Web-Oberfläche
|
|
|
|
class PCManager {
|
|
constructor() {
|
|
this.allPCs = []; // Alle PCs im Speicher
|
|
this.filteredPCs = []; // Gefilterte PCs
|
|
this.searchTerm = ''; // Aktueller Suchbegriff
|
|
this.statusTimer = null; // Intervall für Auto-Status
|
|
this.refreshing = false; // Guard gegen parallele Status-Requests
|
|
this.init();
|
|
}
|
|
|
|
init() {
|
|
this.loadPCs();
|
|
this.loadVacationMode();
|
|
this.setupEventListeners();
|
|
this.startAutoStatus();
|
|
}
|
|
|
|
setupEventListeners() {
|
|
// Formular für neuen PC
|
|
document.getElementById('addPCForm').addEventListener('submit', (e) => {
|
|
e.preventDefault();
|
|
this.addPC();
|
|
});
|
|
|
|
// Edit PC Modal speichern Button
|
|
document.getElementById('saveEditBtn').addEventListener('click', () => {
|
|
this.saveEditPC();
|
|
});
|
|
|
|
// MAC-Adresse Formatierung für Edit-Formular
|
|
document.getElementById('editMACAddress')?.addEventListener('input', (e) => {
|
|
this.formatMACAddress(e.target);
|
|
});
|
|
|
|
// Suchfeld Event Listener
|
|
document.getElementById('searchInput').addEventListener('input', (e) => {
|
|
this.searchPCs(e.target.value);
|
|
});
|
|
|
|
// Suchfeld löschen Button
|
|
document.getElementById('clearSearchBtn').addEventListener('click', () => {
|
|
this.clearSearch();
|
|
});
|
|
|
|
// Status aktualisieren Button
|
|
document.getElementById('refreshStatusBtn').addEventListener('click', () => {
|
|
this.refreshStatus();
|
|
});
|
|
|
|
// Urlaubsmodus Checkbox
|
|
document.getElementById('vacationMode').addEventListener('change', (e) => {
|
|
this.setVacationMode(e.target.checked);
|
|
});
|
|
}
|
|
|
|
async loadPCs() {
|
|
try {
|
|
const response = await fetch('/api/pcs');
|
|
const data = await response.json();
|
|
|
|
if (data.success) {
|
|
this.allPCs = data.pcs;
|
|
this.filteredPCs = [...this.allPCs];
|
|
this.displayPCs();
|
|
// Ersten Status direkt nach dem Laden abfragen
|
|
this.refreshStatus();
|
|
} else {
|
|
this.showNotification('Fehler', data.message, 'danger');
|
|
}
|
|
} catch (error) {
|
|
this.showNotification('Fehler', 'Fehler beim Laden der PCs', 'danger');
|
|
}
|
|
}
|
|
|
|
startAutoStatus() {
|
|
if (this.statusTimer) {
|
|
clearInterval(this.statusTimer);
|
|
}
|
|
this.statusTimer = setInterval(() => {
|
|
this.refreshStatus();
|
|
}, 30000); // alle 30 Sekunden
|
|
}
|
|
|
|
searchPCs(searchTerm) {
|
|
this.searchTerm = searchTerm.toLowerCase().trim();
|
|
|
|
if (this.searchTerm === '') {
|
|
// Keine Suche - alle PCs anzeigen
|
|
this.filteredPCs = [...this.allPCs];
|
|
this.hideClearSearchButton();
|
|
} else {
|
|
// PCs nach Namen filtern
|
|
this.filteredPCs = this.allPCs.filter(pc =>
|
|
pc.name.toLowerCase().includes(this.searchTerm)
|
|
);
|
|
this.showClearSearchButton();
|
|
}
|
|
|
|
this.displayPCs();
|
|
}
|
|
|
|
clearSearch() {
|
|
document.getElementById('searchInput').value = '';
|
|
this.searchPCs('');
|
|
this.hideClearSearchButton();
|
|
}
|
|
|
|
showClearSearchButton() {
|
|
document.getElementById('clearSearchBtn').style.display = 'block';
|
|
}
|
|
|
|
hideClearSearchButton() {
|
|
document.getElementById('clearSearchBtn').style.display = 'none';
|
|
}
|
|
|
|
displayPCs() {
|
|
const tableBody = document.getElementById('pcTableBody');
|
|
const noPCs = document.getElementById('noPCs');
|
|
const pcList = document.getElementById('pcList');
|
|
const noSearchResults = document.getElementById('noSearchResults');
|
|
const searchTerm = document.getElementById('searchTerm');
|
|
const resultCount = document.getElementById('resultCount');
|
|
|
|
// Ergebnis-Zähler aktualisieren
|
|
resultCount.textContent = `${this.filteredPCs.length} PC${this.filteredPCs.length !== 1 ? 's' : ''} gefunden`;
|
|
|
|
if (this.allPCs.length === 0) {
|
|
// Keine PCs vorhanden
|
|
tableBody.innerHTML = '';
|
|
noPCs.style.display = 'block';
|
|
pcList.style.display = 'none';
|
|
noSearchResults.style.display = 'none';
|
|
return;
|
|
}
|
|
|
|
if (this.filteredPCs.length === 0 && this.searchTerm !== '') {
|
|
// Keine Suchergebnisse
|
|
tableBody.innerHTML = '';
|
|
noPCs.style.display = 'none';
|
|
pcList.style.display = 'none';
|
|
noSearchResults.style.display = 'block';
|
|
searchTerm.textContent = this.searchTerm;
|
|
return;
|
|
}
|
|
|
|
// PCs anzeigen
|
|
noPCs.style.display = 'none';
|
|
noSearchResults.style.display = 'none';
|
|
pcList.style.display = 'block';
|
|
|
|
tableBody.innerHTML = this.filteredPCs.map(pc => `
|
|
<tr data-pc-id="${pc.id}">
|
|
<td><strong>${this.escapeHtml(pc.name)}</strong></td>
|
|
<td><code>${this.escapeHtml(pc.mac)}</code></td>
|
|
<td><code>${this.escapeHtml(pc.ip || 'N/A')}</code></td>
|
|
<td>
|
|
<span class="badge ${pc.online ? 'bg-success' : 'bg-danger'}" id="status-${pc.id}">
|
|
<i class="fas fa-${pc.online ? 'wifi' : 'times'}"></i>
|
|
${pc.online ? 'Online' : 'Offline'}
|
|
</span>
|
|
</td>
|
|
<td>${new Date(pc.created_at).toLocaleDateString('de-DE')}</td>
|
|
<td>
|
|
<div class="mb-1">
|
|
<small class="text-muted">
|
|
<i class="fas fa-clock"></i> Autostart:
|
|
${pc.autostart_enabled ?
|
|
`<span class="text-success">${this.escapeHtml(pc.autostart_cron || '30 7 * * Mon-Fri')}</span>` :
|
|
'<span class="text-muted">Deaktiviert</span>'
|
|
}
|
|
</small>
|
|
</div>
|
|
<div class="btn-group" role="group">
|
|
<button class="btn btn-success btn-sm" onclick="pcManager.wakePC(${pc.id})"
|
|
title="PC aufwecken">
|
|
<i class="fas fa-power-off"></i> Aufwecken
|
|
</button>
|
|
<button class="btn btn-warning btn-sm" onclick="pcManager.editPC(${pc.id}, '${this.escapeHtml(pc.name)}', '${this.escapeHtml(pc.mac)}', '${this.escapeHtml(pc.ip || '')}', '${this.escapeHtml(pc.autostart_cron || '')}', ${pc.autostart_enabled})"
|
|
title="PC bearbeiten">
|
|
<i class="fas fa-edit"></i> Bearbeiten
|
|
</button>
|
|
<button class="btn btn-danger btn-sm" onclick="pcManager.deletePC(${pc.id})"
|
|
title="PC löschen">
|
|
<i class="fas fa-trash"></i> Löschen
|
|
</button>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
`).join('');
|
|
|
|
// Tooltips für alle PC-Zeilen laden
|
|
this.loadTooltipsForAllPCs();
|
|
}
|
|
|
|
async addPC() {
|
|
const name = document.getElementById('pcName').value.trim();
|
|
const mac = document.getElementById('macAddress').value.trim();
|
|
const ip = document.getElementById('ipAddress').value.trim();
|
|
const autostartCron = document.getElementById('autostartCron').value.trim();
|
|
const autostartEnabled = document.getElementById('autostartEnabled').checked;
|
|
|
|
if (!name || !mac || !ip) {
|
|
this.showNotification('Warnung', 'Bitte füllen Sie alle Pflichtfelder aus', 'warning');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const response = await fetch('/api/pcs', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
name,
|
|
mac,
|
|
ip,
|
|
autostart_cron: autostartCron || '30 7 * * Mon-Fri',
|
|
autostart_enabled: autostartEnabled
|
|
})
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (data.success) {
|
|
this.showNotification('Erfolg', 'PC erfolgreich hinzugefügt', 'success');
|
|
document.getElementById('addPCForm').reset();
|
|
await this.loadPCs(); // PCs neu laden
|
|
} else {
|
|
this.showNotification('Fehler', data.message, 'danger');
|
|
}
|
|
} catch (error) {
|
|
this.showNotification('Fehler', 'Fehler beim Hinzufügen des PCs', 'danger');
|
|
}
|
|
}
|
|
|
|
editPC(id, name, mac, ip, autostartCron, autostartEnabled) {
|
|
// Modal mit PC-Daten füllen
|
|
document.getElementById('editPCId').value = id;
|
|
document.getElementById('editPCName').value = name;
|
|
document.getElementById('editMACAddress').value = mac;
|
|
document.getElementById('editIPAddress').value = ip;
|
|
document.getElementById('editAutostartCron').value = autostartCron || '30 7 * * Mon-Fri';
|
|
document.getElementById('editAutostartEnabled').checked = autostartEnabled || false;
|
|
|
|
// Modal öffnen
|
|
const editModal = new bootstrap.Modal(document.getElementById('editPCModal'));
|
|
editModal.show();
|
|
}
|
|
|
|
async saveEditPC() {
|
|
const id = document.getElementById('editPCId').value;
|
|
const name = document.getElementById('editPCName').value.trim();
|
|
const mac = document.getElementById('editMACAddress').value.trim();
|
|
const ip = document.getElementById('editIPAddress').value.trim();
|
|
const autostartCron = document.getElementById('editAutostartCron').value.trim();
|
|
const autostartEnabled = document.getElementById('editAutostartEnabled').checked;
|
|
|
|
if (!name || !mac || !ip) {
|
|
this.showNotification('Warnung', 'Bitte füllen Sie alle Pflichtfelder aus', 'warning');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const response = await fetch(`/api/pcs/${id}`, {
|
|
method: 'PUT',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
name,
|
|
mac,
|
|
ip,
|
|
autostart_cron: autostartCron || '30 7 * * Mon-Fri',
|
|
autostart_enabled: autostartEnabled
|
|
})
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (data.success) {
|
|
this.showNotification('Erfolg', 'PC erfolgreich aktualisiert', 'success');
|
|
|
|
// Modal schließen
|
|
const editModal = bootstrap.Modal.getInstance(document.getElementById('editPCModal'));
|
|
editModal.hide();
|
|
|
|
// PCs neu laden
|
|
await this.loadPCs();
|
|
} else {
|
|
this.showNotification('Fehler', data.message, 'danger');
|
|
}
|
|
} catch (error) {
|
|
this.showNotification('Fehler', 'Fehler beim Aktualisieren des PCs', 'danger');
|
|
}
|
|
}
|
|
|
|
async deletePC(id) {
|
|
if (!confirm('Sind Sie sicher, dass Sie diesen PC löschen möchten?')) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const response = await fetch(`/api/pcs/${id}`, {
|
|
method: 'DELETE'
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (data.success) {
|
|
this.showNotification('Erfolg', 'PC erfolgreich gelöscht', 'success');
|
|
await this.loadPCs(); // PCs neu laden
|
|
} else {
|
|
this.showNotification('Fehler', data.message, 'danger');
|
|
}
|
|
} catch (error) {
|
|
this.showNotification('Fehler', 'Fehler beim Löschen des PCs', 'danger');
|
|
}
|
|
}
|
|
|
|
async wakePC(id) {
|
|
try {
|
|
const response = await fetch(`/api/pcs/${id}/wake`, {
|
|
method: 'POST'
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (data.success) {
|
|
this.showNotification('Erfolg', data.message, 'success');
|
|
} else {
|
|
this.showNotification('Fehler', data.message, 'danger');
|
|
}
|
|
} catch (error) {
|
|
this.showNotification('Fehler', 'Fehler beim Senden des Wake-on-LAN Pakets', 'danger');
|
|
}
|
|
}
|
|
|
|
showNotification(title, message, type = 'info') {
|
|
const toast = document.getElementById('notificationToast');
|
|
const toastTitle = document.getElementById('toastTitle');
|
|
const toastMessage = document.getElementById('toastMessage');
|
|
|
|
// Toast-Typ setzen
|
|
toast.className = `toast ${type === 'success' ? 'bg-success' : type === 'danger' ? 'bg-danger' : type === 'warning' ? 'bg-warning' : 'bg-info'} text-white`;
|
|
|
|
toastTitle.textContent = title;
|
|
toastMessage.textContent = message;
|
|
|
|
// Toast anzeigen
|
|
const bsToast = new bootstrap.Toast(toast);
|
|
bsToast.show();
|
|
}
|
|
|
|
async refreshStatus() {
|
|
if (this.refreshing) return;
|
|
this.refreshing = true;
|
|
try {
|
|
const response = await fetch('/api/pcs/status');
|
|
const data = await response.json();
|
|
|
|
if (data.success) {
|
|
// Status für jeden PC aktualisieren
|
|
data.status.forEach(pcStatus => {
|
|
const statusElement = document.getElementById(`status-${pcStatus.id}`);
|
|
if (statusElement) {
|
|
statusElement.className = `badge ${pcStatus.online ? 'bg-success' : 'bg-danger'}`;
|
|
statusElement.innerHTML = `
|
|
<i class="fas fa-${pcStatus.online ? 'wifi' : 'times'}"></i>
|
|
${pcStatus.online ? 'Online' : 'Offline'}
|
|
`;
|
|
}
|
|
});
|
|
|
|
this.showNotification('Info', 'Online-Status aktualisiert', 'info');
|
|
} else {
|
|
this.showNotification('Fehler', data.message, 'danger');
|
|
}
|
|
} catch (error) {
|
|
this.showNotification('Fehler', 'Fehler beim Aktualisieren des Online-Status', 'danger');
|
|
} finally {
|
|
this.refreshing = false;
|
|
}
|
|
}
|
|
|
|
escapeHtml(text) {
|
|
const div = document.createElement('div');
|
|
div.textContent = text;
|
|
return div.innerHTML;
|
|
}
|
|
|
|
formatMACAddress(input) {
|
|
let value = input.value.replace(/[^0-9A-Fa-f]/g, '');
|
|
|
|
if (value.length > 12) {
|
|
value = value.substring(0, 12);
|
|
}
|
|
|
|
// MAC-Adresse mit Doppelpunkten formatieren
|
|
if (value.length >= 2) {
|
|
value = value.match(/.{1,2}/g).join(':');
|
|
}
|
|
|
|
input.value = value.toUpperCase();
|
|
}
|
|
|
|
// Tooltip für alle PCs laden
|
|
async loadTooltipsForAllPCs() {
|
|
for (const pc of this.filteredPCs) {
|
|
await this.loadTooltipForPC(pc.id);
|
|
}
|
|
}
|
|
|
|
// Tooltip für einen bestimmten PC laden
|
|
async loadTooltipForPC(pcId) {
|
|
try {
|
|
const response = await fetch(`/api/logs/pc/${pcId}/recent`);
|
|
const data = await response.json();
|
|
|
|
if (data.success) {
|
|
const tooltipContent = this.createTooltipContent(data.logs);
|
|
const row = document.querySelector(`tr[data-pc-id="${pcId}"]`);
|
|
|
|
if (row) {
|
|
// Tooltip-Attribut setzen
|
|
row.setAttribute('data-bs-toggle', 'tooltip');
|
|
row.setAttribute('data-bs-html', 'true');
|
|
row.setAttribute('title', tooltipContent);
|
|
|
|
// Bootstrap Tooltip initialisieren
|
|
new bootstrap.Tooltip(row, {
|
|
placement: 'top',
|
|
trigger: 'hover',
|
|
html: true
|
|
});
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error(`Fehler beim Laden des Tooltips für PC ${pcId}:`, error);
|
|
}
|
|
}
|
|
|
|
// Tooltip-Inhalt erstellen
|
|
createTooltipContent(logs) {
|
|
if (!logs || logs.length === 0) {
|
|
return '<div class="text-muted">Keine WOL-Ereignisse</div>';
|
|
}
|
|
|
|
let content = '<div class="text-start"><strong>Letzte WOL-Ereignisse:</strong><br>';
|
|
|
|
logs.forEach(log => {
|
|
const timestamp = new Date(log.timestamp).toLocaleString('de-DE', {
|
|
day: '2-digit',
|
|
month: '2-digit',
|
|
hour: '2-digit',
|
|
minute: '2-digit'
|
|
});
|
|
|
|
const triggerText = log.trigger === 'button' ? 'Button' :
|
|
log.trigger === 'cron' ? 'Auto' : log.trigger;
|
|
|
|
content += `• ${timestamp} (${triggerText})<br>`;
|
|
});
|
|
|
|
content += '</div>';
|
|
return content;
|
|
}
|
|
|
|
// Urlaubsmodus laden
|
|
async loadVacationMode() {
|
|
try {
|
|
const response = await fetch('/api/settings/vacation-mode');
|
|
const data = await response.json();
|
|
|
|
if (data.success) {
|
|
const vacationModeCheckbox = document.getElementById('vacationMode');
|
|
if (vacationModeCheckbox) {
|
|
vacationModeCheckbox.checked = data.vacation_mode;
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error('Fehler beim Laden des Urlaubsmodus:', error);
|
|
}
|
|
}
|
|
|
|
// Urlaubsmodus setzen
|
|
async setVacationMode(enabled) {
|
|
try {
|
|
const response = await fetch('/api/settings/vacation-mode', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
vacation_mode: enabled
|
|
})
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (data.success) {
|
|
const status = enabled ? 'aktiviert' : 'deaktiviert';
|
|
this.showNotification('Erfolg', `Urlaubsmodus ${status}`, 'success');
|
|
} else {
|
|
this.showNotification('Fehler', data.message, 'danger');
|
|
// Checkbox zurücksetzen bei Fehler
|
|
const vacationModeCheckbox = document.getElementById('vacationMode');
|
|
if (vacationModeCheckbox) {
|
|
vacationModeCheckbox.checked = !enabled;
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error('Fehler beim Setzen des Urlaubsmodus:', error);
|
|
this.showNotification('Fehler', 'Fehler beim Setzen des Urlaubsmodus', 'danger');
|
|
// Checkbox zurücksetzen bei Fehler
|
|
const vacationModeCheckbox = document.getElementById('vacationMode');
|
|
if (vacationModeCheckbox) {
|
|
vacationModeCheckbox.checked = !enabled;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// PC Manager initialisieren, wenn die Seite geladen ist
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
window.pcManager = new PCManager();
|
|
});
|
|
|
|
// MAC-Adresse Formatierung für das Hauptformular
|
|
document.getElementById('macAddress')?.addEventListener('input', (e) => {
|
|
let value = e.target.value.replace(/[^0-9A-Fa-f]/g, '');
|
|
|
|
if (value.length > 12) {
|
|
value = value.substring(0, 12);
|
|
}
|
|
|
|
// MAC-Adresse mit Doppelpunkten formatieren
|
|
if (value.length >= 2) {
|
|
value = value.match(/.{1,2}/g).join(':');
|
|
}
|
|
|
|
e.target.value = value.toUpperCase();
|
|
});
|