295 lines
12 KiB
HTML
295 lines
12 KiB
HTML
{% extends "base.html" %}
|
|
{% block content %}
|
|
|
|
<h2>Devices</h2>
|
|
|
|
{% if current_user.is_admin %}
|
|
<!-- Neues Gerät hinzufügen -->
|
|
<button class="btn btn-success mb-3" data-bs-toggle="modal" data-bs-target="#deviceModal" onclick="openDeviceModal()">Neues Gerät hinzufügen</button>
|
|
{% endif %}
|
|
|
|
<table class="table table-bordered">
|
|
<thead>
|
|
<tr>
|
|
<th>Hostname</th>
|
|
<th>IP-Adresse</th>
|
|
<th>MAC-Adresse</th>
|
|
<th>Port</th>
|
|
<th>Switch</th>
|
|
<th>Status</th>
|
|
{% if current_user.is_admin %}<th>Aktionen</th>{% endif %}
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for d in devices %}
|
|
<tr>
|
|
<td>{{ d['name'] }}</td>
|
|
<td>{{ d['rpi_ip'] }}</td>
|
|
<td>{{ d['mac'] }}</td>
|
|
<td>{{ d['port'] }}</td>
|
|
<td>{{ d['switch_hostname'] or '-' }}</td>
|
|
<td>
|
|
<button class="btn btn-sm {% if d['is_active'] %}btn-success{% else %}btn-secondary{% endif %}"
|
|
onclick="toggleDevice('{{ d['mac'] }}', this)">
|
|
{% if d['is_active'] %}Deaktivieren{% else %}Aktivieren{% endif %}
|
|
</button>
|
|
</td>
|
|
{% if current_user.is_admin %}
|
|
<td>
|
|
<!-- Bearbeiten Modal -->
|
|
<button class="btn btn-secondary btn-sm button login white" data-bs-toggle="modal" data-bs-target="#editDeviceModal"
|
|
onclick="openEditDeviceModal('{{ d['mac'] }}','{{ d['name'] }}','{{ d['rpi_ip'] }}','{{ d['port'] }}')">
|
|
Bearbeiten
|
|
</button>
|
|
|
|
<!-- Switch ändern Modal -->
|
|
<button class="btn btn-secondary btn-sm button login white" data-bs-toggle="modal" data-bs-target="#switchModal"
|
|
onclick="openSwitchModal('{{ d['mac'] }}','{{ d['switch_hostname'] }}')">
|
|
Switch ändern
|
|
</button>
|
|
|
|
<!-- Löschen -->
|
|
<form method="post" style="display:inline;">
|
|
<input type="hidden" name="delete_device" value="{{ d['mac'] }}">
|
|
<button class="btn btn-danger btn-sm" onclick="return confirm('Willst du das Gerät wirklich löschen?');">
|
|
Löschen
|
|
</button>
|
|
</form>
|
|
</td>
|
|
{% endif %}
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
|
|
<!-- Modal: Neues Gerät -->
|
|
<div class="modal fade" id="deviceModal" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<form method="post" onsubmit="return validateDeviceForm(this);">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title black">Neues Gerät hinzufügen</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<input type="hidden" name="add_device" value="1">
|
|
<div class="mb-3">
|
|
<label>Hostname</label>
|
|
<input type="text" name="name" class="form-control" required placeholder="z.B. Sensor01">
|
|
</div>
|
|
<div class="mb-3">
|
|
<label>IP-Adresse</label>
|
|
<input type="text" name="rpi_ip" class="form-control" required placeholder="z.B. 192.168.1.100">
|
|
<div class="invalid-feedback">Bitte eine gültige IP-Adresse eingeben (z. B. 192.168.1.100).</div>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label>MAC-Adresse</label>
|
|
<input type="text" name="mac" class="form-control" required placeholder="z.B. AA:BB:CC:DD:EE:FF">
|
|
<div class="invalid-feedback">Bitte eine gültige MAC-Adresse eingeben (xx:xx:xx:xx:xx:xx).</div>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label>Port</label>
|
|
<input type="text" name="port" class="form-control" required placeholder="z.B. 3">
|
|
</div>
|
|
<div class="mb-3">
|
|
<label>Switch</label>
|
|
<select name="switch_hostname" class="form-select" required>
|
|
{% for sw in switches %}
|
|
<option value="{{ sw['hostname'] }}">{{ sw['hostname'] }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button class="btn btn-success button login" type="submit">Hinzufügen</button>
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Abbrechen</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Modal: Bearbeiten -->
|
|
<div class="modal fade" id="editDeviceModal" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<form method="post" onsubmit="return validateDeviceForm(this);">
|
|
<input type="hidden" name="edit_device" value="1">
|
|
<input type="hidden" name="old_mac" id="edit_old_mac">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title black">Gerät bearbeiten</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="mb-3">
|
|
<label>Hostname</label>
|
|
<input type="text" name="name" id="edit_name" class="form-control" required placeholder="z.B. Sensor01">
|
|
</div>
|
|
<div class="mb-3">
|
|
<label>IP-Adresse</label>
|
|
<input type="text" name="rpi_ip" id="edit_ip" class="form-control" required placeholder="z.B. 192.168.1.100">
|
|
<div class="invalid-feedback">Bitte eine gültige IP-Adresse eingeben (z. B. 192.168.1.100).</div>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label>MAC-Adresse</label>
|
|
<input type="text" name="mac" id="edit_mac" class="form-control" required placeholder="z.B. AA:BB:CC:DD:EE:FF">
|
|
<div class="invalid-feedback">Bitte eine gültige MAC-Adresse eingeben (xx:xx:xx:xx:xx:xx).</div>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label>Port</label>
|
|
<input type="text" name="port" id="edit_port" class="form-control" required placeholder="z.B. 3">
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button class="btn btn-primary button login" type="submit">Speichern</button>
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Abbrechen</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Modal: Switch ändern -->
|
|
<div class="modal fade" id="switchModal" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<form method="post">
|
|
<input type="hidden" name="edit_device" value="1">
|
|
<input type="hidden" name="old_mac" id="switch_mac">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title black">Switch ändern</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<select name="switch_hostname" id="switch_select" class="form-select" required>
|
|
{% for sw in switches %}
|
|
<option value="{{ sw['hostname'] }}">{{ sw['hostname'] }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button class="btn btn-primary button login" type="submit">Speichern</button>
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Abbrechen</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
function openDeviceModal() {
|
|
document.querySelector("#deviceModal form").reset();
|
|
}
|
|
|
|
function openEditDeviceModal(mac, name, ip, port) {
|
|
document.getElementById("edit_old_mac").value = mac;
|
|
document.getElementById("edit_name").value = name;
|
|
document.getElementById("edit_ip").value = ip;
|
|
document.getElementById("edit_mac").value = mac;
|
|
document.getElementById("edit_port").value = port;
|
|
}
|
|
|
|
function openSwitchModal(mac, switch_hostname) {
|
|
document.getElementById("switch_mac").value = mac;
|
|
document.getElementById("switch_select").value = switch_hostname;
|
|
}
|
|
|
|
// -------------------
|
|
// IP-Validierung
|
|
// -------------------
|
|
const ipPattern = /^(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}$/;
|
|
|
|
// -------------------
|
|
// MAC-Validierung
|
|
// -------------------
|
|
const macPattern = /^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$/;
|
|
|
|
function validateIP(input) {
|
|
if (!ipPattern.test(input.value)) {
|
|
input.classList.add("is-invalid");
|
|
return false;
|
|
} else {
|
|
input.classList.remove("is-invalid");
|
|
return true;
|
|
}
|
|
}
|
|
|
|
function validateMAC(input) {
|
|
if (!macPattern.test(input.value)) {
|
|
input.classList.add("is-invalid");
|
|
return false;
|
|
} else {
|
|
input.classList.remove("is-invalid");
|
|
return true;
|
|
}
|
|
}
|
|
|
|
function validateDeviceForm(form) {
|
|
const ipInput = form.querySelector("input[name='rpi_ip']");
|
|
const macInput = form.querySelector("input[name='mac']");
|
|
let valid = true;
|
|
if (ipInput) valid = validateIP(ipInput) && valid;
|
|
if (macInput) valid = validateMAC(macInput) && valid;
|
|
return valid;
|
|
}
|
|
|
|
// Live-Feedback beim Tippen
|
|
document.addEventListener("input", function(e) {
|
|
if (e.target.name === "rpi_ip") validateIP(e.target);
|
|
if (e.target.name === "mac") validateMAC(e.target);
|
|
});
|
|
|
|
// Aktivieren/Deaktivieren
|
|
function toggleDevice(mac, btn) {
|
|
fetch(`/devices/toggle/${mac}`, { method: 'POST' })
|
|
.then(resp => resp.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
// Button aktualisieren
|
|
if (data.new_status === 1) {
|
|
btn.classList.remove('btn-secondary');
|
|
btn.classList.add('btn-success');
|
|
btn.innerText = 'Deaktivieren';
|
|
} else {
|
|
btn.classList.remove('btn-success');
|
|
btn.classList.add('btn-secondary');
|
|
btn.innerText = 'Aktivieren';
|
|
}
|
|
|
|
// Flash-Nachricht im Frontend aktualisieren
|
|
let flashContainer = document.getElementById('flash-messages');
|
|
if (!flashContainer) {
|
|
flashContainer = document.createElement('div');
|
|
flashContainer.id = 'flash-messages';
|
|
flashContainer.style.position = 'fixed';
|
|
flashContainer.style.top = '10px';
|
|
flashContainer.style.right = '10px';
|
|
flashContainer.style.zIndex = 1050;
|
|
document.body.appendChild(flashContainer);
|
|
}
|
|
|
|
const flash = document.createElement('div');
|
|
flash.className = 'alert alert-success alert-dismissible fade show';
|
|
flash.role = 'alert';
|
|
flash.innerHTML = `
|
|
${data.msg}
|
|
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
|
`;
|
|
flashContainer.appendChild(flash);
|
|
|
|
// Automatisch nach 3 Sekunden ausblenden
|
|
setTimeout(() => {
|
|
flash.classList.remove('show');
|
|
flash.classList.add('hide');
|
|
flash.addEventListener('transitionend', () => flash.remove());
|
|
}, 3000);
|
|
} else {
|
|
console.error(data.msg);
|
|
}
|
|
})
|
|
.catch(err => console.error(err));
|
|
}
|
|
</script>
|
|
|
|
{% endblock %}
|