389 lines
14 KiB
PHP
389 lines
14 KiB
PHP
<?php
|
|
/**
|
|
* app/modules/connections/list.php
|
|
*
|
|
* Übersicht der Netzwerkverbindungen
|
|
* - Tabellarische Liste aller Verbindungen
|
|
* - Filter nach Geräten, VLANs, Status
|
|
* - Später: Visuelle Netzwerk-Topologie
|
|
*/
|
|
|
|
// =========================
|
|
// Filter einlesen
|
|
// =========================
|
|
$search = trim($_GET['search'] ?? '');
|
|
$deviceId = (int)($_GET['device_id'] ?? 0);
|
|
|
|
// Einheitliche Endpunkt-Aufloesung fuer polymorphe Port-Typen.
|
|
$endpointUnionSql = "
|
|
SELECT
|
|
'device' AS endpoint_type,
|
|
dp.id AS endpoint_id,
|
|
dp.name AS port_name,
|
|
d.name AS owner_name,
|
|
d.id AS owner_device_id
|
|
FROM device_ports dp
|
|
JOIN devices d ON d.id = dp.device_id
|
|
UNION ALL
|
|
SELECT
|
|
'module' AS endpoint_type,
|
|
mp.id AS endpoint_id,
|
|
mp.name AS port_name,
|
|
CONCAT(IFNULL(MIN(d.name), 'Unzugeordnet'), ' / ', m.name) AS owner_name,
|
|
MIN(d.id) AS owner_device_id
|
|
FROM module_ports mp
|
|
JOIN modules m ON m.id = mp.module_id
|
|
LEFT JOIN device_port_modules dpm ON dpm.module_id = m.id
|
|
LEFT JOIN device_ports dp ON dp.id = dpm.device_port_id
|
|
LEFT JOIN devices d ON d.id = dp.device_id
|
|
GROUP BY mp.id, mp.name, m.name
|
|
UNION ALL
|
|
SELECT
|
|
'outlet' AS endpoint_type,
|
|
nop.id AS endpoint_id,
|
|
nop.name AS port_name,
|
|
CONCAT(o.name, ' / ', IFNULL(r.name, ''), ' / ', IFNULL(f.name, '')) AS owner_name,
|
|
NULL AS owner_device_id
|
|
FROM network_outlet_ports nop
|
|
JOIN network_outlets o ON o.id = nop.outlet_id
|
|
LEFT JOIN rooms r ON r.id = o.room_id
|
|
LEFT JOIN floors f ON f.id = r.floor_id
|
|
UNION ALL
|
|
SELECT
|
|
'floor_patchpanel' AS endpoint_type,
|
|
fpp.id AS endpoint_id,
|
|
fpp.name AS port_name,
|
|
CONCAT(fp.name, ' / ', IFNULL(f.name, '')) AS owner_name,
|
|
NULL AS owner_device_id
|
|
FROM floor_patchpanel_ports fpp
|
|
JOIN floor_patchpanels fp ON fp.id = fpp.patchpanel_id
|
|
LEFT JOIN floors f ON f.id = fp.floor_id
|
|
";
|
|
|
|
// =========================
|
|
// WHERE-Clause bauen
|
|
// =========================
|
|
$where = [];
|
|
$types = '';
|
|
$params = [];
|
|
|
|
if ($search !== '') {
|
|
$where[] = "(e1.owner_name LIKE ? OR e2.owner_name LIKE ? OR e1.port_name LIKE ? OR e2.port_name LIKE ?)";
|
|
$types .= "ssss";
|
|
$params[] = "%$search%";
|
|
$params[] = "%$search%";
|
|
$params[] = "%$search%";
|
|
$params[] = "%$search%";
|
|
}
|
|
|
|
if ($deviceId > 0) {
|
|
$where[] = "(e1.owner_device_id = ? OR e2.owner_device_id = ?)";
|
|
$types .= "ii";
|
|
$params[] = $deviceId;
|
|
$params[] = $deviceId;
|
|
}
|
|
|
|
$whereSql = $where ? "WHERE " . implode(" AND ", $where) : "";
|
|
|
|
// =========================
|
|
// Verbindungen laden
|
|
// =========================
|
|
$connections = $sql->get(
|
|
"SELECT
|
|
c.id,
|
|
c.port_a_type, c.port_a_id, c.port_b_type, c.port_b_id,
|
|
e1.owner_name AS endpoint_a_name,
|
|
e2.owner_name AS endpoint_b_name,
|
|
e1.port_name AS port_a_name,
|
|
e2.port_name AS port_b_name,
|
|
c.vlan_config,
|
|
c.comment
|
|
FROM connections c
|
|
LEFT JOIN ($endpointUnionSql) e1
|
|
ON c.port_a_id = e1.endpoint_id
|
|
AND (
|
|
c.port_a_type = e1.endpoint_type
|
|
OR (c.port_a_type = 'device_ports' AND e1.endpoint_type = 'device')
|
|
OR (c.port_a_type = 'module_ports' AND e1.endpoint_type = 'module')
|
|
OR (c.port_a_type = 'network_outlet_ports' AND e1.endpoint_type = 'outlet')
|
|
OR (c.port_a_type = 'floor_patchpanel_ports' AND e1.endpoint_type = 'floor_patchpanel')
|
|
OR (c.port_a_type = 'patchpanel' AND e1.endpoint_type = 'floor_patchpanel')
|
|
)
|
|
LEFT JOIN ($endpointUnionSql) e2
|
|
ON c.port_b_id = e2.endpoint_id
|
|
AND (
|
|
c.port_b_type = e2.endpoint_type
|
|
OR (c.port_b_type = 'device_ports' AND e2.endpoint_type = 'device')
|
|
OR (c.port_b_type = 'module_ports' AND e2.endpoint_type = 'module')
|
|
OR (c.port_b_type = 'network_outlet_ports' AND e2.endpoint_type = 'outlet')
|
|
OR (c.port_b_type = 'floor_patchpanel_ports' AND e2.endpoint_type = 'floor_patchpanel')
|
|
OR (c.port_b_type = 'patchpanel' AND e2.endpoint_type = 'floor_patchpanel')
|
|
)
|
|
$whereSql
|
|
ORDER BY e1.owner_name, e2.owner_name",
|
|
$types,
|
|
$params
|
|
);
|
|
|
|
// =========================
|
|
// Filter-Daten
|
|
// =========================
|
|
$devices = $sql->get("SELECT id, name FROM devices ORDER BY name", "", []);
|
|
|
|
$selectedDevice = null;
|
|
$selectedDevicePorts = [];
|
|
$selectedDeviceVlans = [];
|
|
|
|
if ($deviceId > 0) {
|
|
$selectedDevice = $sql->single(
|
|
"SELECT d.id, d.name, dt.name AS type_name
|
|
FROM devices d
|
|
LEFT JOIN device_types dt ON d.device_type_id = dt.id
|
|
WHERE d.id = ?",
|
|
"i",
|
|
[$deviceId]
|
|
);
|
|
|
|
if ($selectedDevice) {
|
|
$selectedDevice['port_count'] = (int)($sql->single(
|
|
"SELECT COUNT(*) AS cnt
|
|
FROM (
|
|
SELECT dp.id
|
|
FROM device_ports dp
|
|
WHERE dp.device_id = ?
|
|
UNION ALL
|
|
SELECT DISTINCT mp.id
|
|
FROM module_ports mp
|
|
JOIN modules m ON m.id = mp.module_id
|
|
JOIN device_port_modules dpm ON dpm.module_id = m.id
|
|
JOIN device_ports dp ON dp.id = dpm.device_port_id
|
|
WHERE dp.device_id = ?
|
|
) p",
|
|
"ii",
|
|
[$deviceId, $deviceId]
|
|
)['cnt'] ?? 0);
|
|
|
|
$selectedDevice['connection_count'] = (int)($sql->single(
|
|
"SELECT COUNT(DISTINCT c.id) AS cnt
|
|
FROM connections c
|
|
LEFT JOIN ($endpointUnionSql) e1
|
|
ON c.port_a_id = e1.endpoint_id
|
|
AND (
|
|
c.port_a_type = e1.endpoint_type
|
|
OR (c.port_a_type = 'device_ports' AND e1.endpoint_type = 'device')
|
|
OR (c.port_a_type = 'module_ports' AND e1.endpoint_type = 'module')
|
|
OR (c.port_a_type = 'network_outlet_ports' AND e1.endpoint_type = 'outlet')
|
|
OR (c.port_a_type = 'floor_patchpanel_ports' AND e1.endpoint_type = 'floor_patchpanel')
|
|
OR (c.port_a_type = 'patchpanel' AND e1.endpoint_type = 'floor_patchpanel')
|
|
)
|
|
LEFT JOIN ($endpointUnionSql) e2
|
|
ON c.port_b_id = e2.endpoint_id
|
|
AND (
|
|
c.port_b_type = e2.endpoint_type
|
|
OR (c.port_b_type = 'device_ports' AND e2.endpoint_type = 'device')
|
|
OR (c.port_b_type = 'module_ports' AND e2.endpoint_type = 'module')
|
|
OR (c.port_b_type = 'network_outlet_ports' AND e2.endpoint_type = 'outlet')
|
|
OR (c.port_b_type = 'floor_patchpanel_ports' AND e2.endpoint_type = 'floor_patchpanel')
|
|
OR (c.port_b_type = 'patchpanel' AND e2.endpoint_type = 'floor_patchpanel')
|
|
)
|
|
WHERE e1.owner_device_id = ? OR e2.owner_device_id = ?",
|
|
"ii",
|
|
[$deviceId, $deviceId]
|
|
)['cnt'] ?? 0);
|
|
|
|
$selectedDevicePorts = $sql->get(
|
|
"SELECT name, vlan_config
|
|
FROM (
|
|
SELECT dp.name, dp.vlan_config, dp.id AS sort_id
|
|
FROM device_ports dp
|
|
WHERE dp.device_id = ?
|
|
UNION ALL
|
|
SELECT DISTINCT CONCAT(m.name, ' / ', mp.name) AS name, NULL AS vlan_config, (1000000 + mp.id) AS sort_id
|
|
FROM module_ports mp
|
|
JOIN modules m ON m.id = mp.module_id
|
|
JOIN device_port_modules dpm ON dpm.module_id = m.id
|
|
JOIN device_ports dp ON dp.id = dpm.device_port_id
|
|
WHERE dp.device_id = ?
|
|
) p
|
|
ORDER BY sort_id
|
|
LIMIT 12",
|
|
"ii",
|
|
[$deviceId, $deviceId]
|
|
);
|
|
|
|
foreach ($selectedDevicePorts as $port) {
|
|
if (empty($port['vlan_config'])) {
|
|
continue;
|
|
}
|
|
|
|
$vlans = json_decode($port['vlan_config'], true);
|
|
foreach ((array)$vlans as $vlan) {
|
|
$vlan = trim((string)$vlan);
|
|
if ($vlan !== '') {
|
|
$selectedDeviceVlans[$vlan] = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
$selectedDeviceVlans = array_keys($selectedDeviceVlans);
|
|
natcasesort($selectedDeviceVlans);
|
|
}
|
|
}
|
|
|
|
?>
|
|
|
|
<div class="connections-container">
|
|
<h1>Netzwerkverbindungen</h1>
|
|
|
|
<!-- =========================
|
|
Filter-Toolbar
|
|
========================= -->
|
|
<div class="filter-form">
|
|
<form method="GET">
|
|
<input type="hidden" name="module" value="connections">
|
|
<input type="hidden" name="action" value="list">
|
|
|
|
<input type="text" name="search" placeholder="Suche nach Gerät oder Port…"
|
|
value="<?php echo htmlspecialchars($search); ?>" class="search-input">
|
|
|
|
<select name="device_id">
|
|
<option value="">- Alle Geräte -</option>
|
|
<?php foreach ($devices as $device): ?>
|
|
<option value="<?php echo $device['id']; ?>"
|
|
<?php echo $device['id'] === $deviceId ? 'selected' : ''; ?>>
|
|
<?php echo htmlspecialchars($device['name']); ?>
|
|
</option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
|
|
<button type="submit" class="button">Filter</button>
|
|
<a href="?module=connections&action=list" class="button">Reset</a>
|
|
<a href="?module=connections&action=edit" class="button button-primary">+ Neue Verbindung</a>
|
|
</form>
|
|
</div>
|
|
|
|
<!-- =========================
|
|
Verbindungs-Tabelle
|
|
========================= -->
|
|
<?php if (!empty($connections)): ?>
|
|
<table class="connections-list">
|
|
<thead>
|
|
<tr>
|
|
<th>Von (Gerät → Port)</th>
|
|
<th>Nach (Gerät → Port)</th>
|
|
<th>VLANs</th>
|
|
<th>Beschreibung</th>
|
|
<th>Status</th>
|
|
<th>Aktionen</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php foreach ($connections as $conn): ?>
|
|
<?php
|
|
$comment = trim($conn['comment'] ?? '');
|
|
$hasMissingInfo = empty($conn['endpoint_a_name']) || empty($conn['endpoint_b_name'])
|
|
|| empty($conn['port_a_name']) || empty($conn['port_b_name']);
|
|
$commentLower = mb_strtolower($comment, 'UTF-8');
|
|
$warningFromComment = preg_match('/warn|achtung|critical/', $commentLower);
|
|
$hasWarning = $hasMissingInfo || $warningFromComment;
|
|
?>
|
|
<tr>
|
|
<td>
|
|
<strong><?php echo htmlspecialchars($conn['endpoint_a_name'] ?? 'N/A'); ?></strong><br>
|
|
<small><?php echo htmlspecialchars($conn['port_a_name'] ?? '—'); ?></small>
|
|
</td>
|
|
|
|
<td>
|
|
<strong><?php echo htmlspecialchars($conn['endpoint_b_name'] ?? 'N/A'); ?></strong><br>
|
|
<small><?php echo htmlspecialchars($conn['port_b_name'] ?? '—'); ?></small>
|
|
</td>
|
|
|
|
<td>
|
|
<small>
|
|
<?php
|
|
if ($conn['vlan_config']) {
|
|
$vlan = json_decode($conn['vlan_config'], true);
|
|
echo htmlspecialchars(implode(', ', (array)$vlan));
|
|
} else {
|
|
echo '—';
|
|
}
|
|
?>
|
|
</small>
|
|
</td>
|
|
|
|
<td>
|
|
<small><?php echo htmlspecialchars($conn['comment'] ?? ''); ?></small>
|
|
</td>
|
|
<td class="status-cell">
|
|
<?php if ($hasWarning): ?>
|
|
<span class="status-badge status-badge-warning" title="Unvollständige oder kritische Verbindung">
|
|
⚠️ Warnung
|
|
</span>
|
|
<?php else: ?>
|
|
<span class="status-badge status-badge-ok" title="Verbindung vollständig">
|
|
✔️ OK
|
|
</span>
|
|
<?php endif; ?>
|
|
</td>
|
|
|
|
<td class="actions">
|
|
<a href="?module=connections&action=edit&id=<?php echo $conn['id']; ?>" class="button button-small">Bearbeiten</a>
|
|
<a href="?module=connections&action=swap&id=<?php echo $conn['id']; ?>" class="button button-small" onclick="return confirm('Von/Nach fuer diese Verbindung vertauschen?');">Von/Nach tauschen</a>
|
|
<a href="#" class="button button-small button-danger"
|
|
data-confirm-delete="true"
|
|
data-confirm-message="Diese Verbindung wirklich löschen?"
|
|
data-confirm-feedback="Löschen noch nicht implementiert">
|
|
Löschen
|
|
</a>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
|
|
<?php else: ?>
|
|
<div class="empty-state">
|
|
<p>Keine Verbindungen gefunden.</p>
|
|
<p>
|
|
<a href="?module=connections&action=edit" class="button button-primary">
|
|
Erste Verbindung anlegen
|
|
</a>
|
|
</p>
|
|
</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
|
|
<!-- =========================
|
|
Sidebar / Details
|
|
========================= -->
|
|
|
|
<aside class="sidebar">
|
|
<?php if ($selectedDevice): ?>
|
|
<h3>Ausgewähltes Gerät</h3>
|
|
<p><strong><?php echo htmlspecialchars($selectedDevice['name']); ?></strong></p>
|
|
<p>Typ: <?php echo htmlspecialchars($selectedDevice['type_name'] ?? '—'); ?></p>
|
|
<p>Ports: <?php echo (int)$selectedDevice['port_count']; ?></p>
|
|
<p>Verbindungen: <?php echo (int)$selectedDevice['connection_count']; ?></p>
|
|
<p>
|
|
VLANs:
|
|
<?php if (!empty($selectedDeviceVlans)): ?>
|
|
<?php echo htmlspecialchars(implode(', ', $selectedDeviceVlans)); ?>
|
|
<?php else: ?>
|
|
—
|
|
<?php endif; ?>
|
|
</p>
|
|
<?php if (!empty($selectedDevicePorts)): ?>
|
|
<h4>Ports (max. 12)</h4>
|
|
<ul>
|
|
<?php foreach ($selectedDevicePorts as $port): ?>
|
|
<li><?php echo htmlspecialchars($port['name']); ?></li>
|
|
<?php endforeach; ?>
|
|
</ul>
|
|
<?php endif; ?>
|
|
<?php else: ?>
|
|
<p><em>Bitte ein Gerät im Filter auswählen.</em></p>
|
|
<?php endif; ?>
|
|
|
|
<!-- TODO: Verbindung bearbeiten / löschen -->
|
|
</aside>
|