verbings administration
This commit is contained in:
@@ -8,7 +8,7 @@ $module = $_GET['module'] ?? 'dashboard';
|
||||
$action = $_GET['action'] ?? 'list';
|
||||
|
||||
$validModules = ['dashboard', 'locations', 'buildings', 'rooms', 'device_types', 'devices', 'racks', 'floors', 'floor_infrastructure', 'connections', 'port_types'];
|
||||
$validActions = ['list', 'edit', 'save', 'ports', 'delete'];
|
||||
$validActions = ['list', 'edit', 'save', 'ports', 'delete', 'swap'];
|
||||
|
||||
if (!in_array($module, $validModules, true)) {
|
||||
renderClientError(400, 'Ungueltiges Modul');
|
||||
@@ -20,7 +20,7 @@ if (!in_array($action, $validActions, true)) {
|
||||
exit;
|
||||
}
|
||||
|
||||
if (!in_array($action, ['save', 'delete'], true)) {
|
||||
if (!in_array($action, ['save', 'delete', 'swap'], true)) {
|
||||
require_once __DIR__ . '/templates/header.php';
|
||||
}
|
||||
|
||||
@@ -32,6 +32,6 @@ if (file_exists($modulePath)) {
|
||||
renderClientError(404, 'Die angeforderte Seite existiert nicht.');
|
||||
}
|
||||
|
||||
if (!in_array($action, ['save', 'delete'], true)) {
|
||||
if (!in_array($action, ['save', 'delete', 'swap'], true)) {
|
||||
require_once __DIR__ . '/templates/footer.php';
|
||||
}
|
||||
|
||||
@@ -46,6 +46,68 @@ $endpointOptions = [
|
||||
'patchpanel' => [],
|
||||
];
|
||||
|
||||
$occupiedByType = [
|
||||
'device' => [],
|
||||
'module' => [],
|
||||
'outlet' => [],
|
||||
'patchpanel' => [],
|
||||
];
|
||||
$occupiedRows = $sql->get(
|
||||
"SELECT id, port_a_type, port_a_id, port_b_type, port_b_id
|
||||
FROM connections
|
||||
WHERE id <> ?",
|
||||
"i",
|
||||
[$connectionId]
|
||||
);
|
||||
foreach ((array)$occupiedRows as $row) {
|
||||
$typeA = $normalizePortType((string)($row['port_a_type'] ?? ''));
|
||||
$idA = (int)($row['port_a_id'] ?? 0);
|
||||
if ($idA > 0 && isset($occupiedByType[$typeA])) {
|
||||
$occupiedByType[$typeA][$idA] = true;
|
||||
}
|
||||
|
||||
$typeB = $normalizePortType((string)($row['port_b_type'] ?? ''));
|
||||
$idB = (int)($row['port_b_id'] ?? 0);
|
||||
if ($idB > 0 && isset($occupiedByType[$typeB])) {
|
||||
$occupiedByType[$typeB][$idB] = true;
|
||||
}
|
||||
}
|
||||
|
||||
$isEndpointAllowed = static function (string $type, int $id) use ($occupiedByType, $portAType, $portAId, $portBType, $portBId): bool {
|
||||
if ($id <= 0) {
|
||||
return false;
|
||||
}
|
||||
if ($type === $portAType && $id === $portAId) {
|
||||
return true;
|
||||
}
|
||||
if ($type === $portBType && $id === $portBId) {
|
||||
return true;
|
||||
}
|
||||
return empty($occupiedByType[$type][$id]);
|
||||
};
|
||||
|
||||
// Auto-heal: ensure each outlet has at least one selectable port.
|
||||
$outletsWithoutPorts = $sql->get(
|
||||
"SELECT o.id
|
||||
FROM network_outlets o
|
||||
LEFT JOIN network_outlet_ports nop ON nop.outlet_id = o.id
|
||||
GROUP BY o.id
|
||||
HAVING COUNT(nop.id) = 0",
|
||||
"",
|
||||
[]
|
||||
);
|
||||
foreach ((array)$outletsWithoutPorts as $outletRow) {
|
||||
$outletId = (int)($outletRow['id'] ?? 0);
|
||||
if ($outletId <= 0) {
|
||||
continue;
|
||||
}
|
||||
$sql->set(
|
||||
"INSERT INTO network_outlet_ports (outlet_id, name) VALUES (?, 'Port 1')",
|
||||
"i",
|
||||
[$outletId]
|
||||
);
|
||||
}
|
||||
|
||||
$devicePorts = $sql->get(
|
||||
"SELECT dp.id, dp.name, d.name AS owner_name
|
||||
FROM device_ports dp
|
||||
@@ -55,8 +117,12 @@ $devicePorts = $sql->get(
|
||||
[]
|
||||
);
|
||||
foreach ($devicePorts as $row) {
|
||||
$id = (int)$row['id'];
|
||||
if (!$isEndpointAllowed('device', $id)) {
|
||||
continue;
|
||||
}
|
||||
$endpointOptions['device'][] = [
|
||||
'id' => (int)$row['id'],
|
||||
'id' => $id,
|
||||
'label' => $row['owner_name'] . ' / ' . $row['name'],
|
||||
];
|
||||
}
|
||||
@@ -78,27 +144,35 @@ $modulePorts = $sql->get(
|
||||
[]
|
||||
);
|
||||
foreach ($modulePorts as $row) {
|
||||
$id = (int)$row['id'];
|
||||
if (!$isEndpointAllowed('module', $id)) {
|
||||
continue;
|
||||
}
|
||||
$deviceName = trim((string)($row['device_name'] ?? '')) ?: 'Unzugeordnet';
|
||||
$endpointOptions['module'][] = [
|
||||
'id' => (int)$row['id'],
|
||||
'id' => $id,
|
||||
'label' => $deviceName . ' / ' . $row['module_name'] . ' / ' . $row['name'],
|
||||
];
|
||||
}
|
||||
|
||||
$outletPorts = $sql->get(
|
||||
"SELECT nop.id, nop.name, no.name AS outlet_name, r.name AS room_name, f.name AS floor_name
|
||||
"SELECT nop.id, nop.name, o.name AS outlet_name, r.name AS room_name, f.name AS floor_name
|
||||
FROM network_outlet_ports nop
|
||||
JOIN network_outlets no ON no.id = nop.outlet_id
|
||||
LEFT JOIN rooms r ON r.id = no.room_id
|
||||
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
|
||||
ORDER BY floor_name, room_name, outlet_name, nop.name",
|
||||
"",
|
||||
[]
|
||||
);
|
||||
foreach ($outletPorts as $row) {
|
||||
$id = (int)$row['id'];
|
||||
if (!$isEndpointAllowed('outlet', $id)) {
|
||||
continue;
|
||||
}
|
||||
$parts = array_filter([(string)($row['floor_name'] ?? ''), (string)($row['room_name'] ?? ''), (string)$row['outlet_name'], (string)$row['name']]);
|
||||
$endpointOptions['outlet'][] = [
|
||||
'id' => (int)$row['id'],
|
||||
'id' => $id,
|
||||
'label' => implode(' / ', $parts),
|
||||
];
|
||||
}
|
||||
@@ -113,9 +187,13 @@ $patchpanelPorts = $sql->get(
|
||||
[]
|
||||
);
|
||||
foreach ($patchpanelPorts as $row) {
|
||||
$id = (int)$row['id'];
|
||||
if (!$isEndpointAllowed('patchpanel', $id)) {
|
||||
continue;
|
||||
}
|
||||
$parts = array_filter([(string)($row['floor_name'] ?? ''), (string)$row['patchpanel_name'], (string)$row['name']]);
|
||||
$endpointOptions['patchpanel'][] = [
|
||||
'id' => (int)$row['id'],
|
||||
'id' => $id,
|
||||
'label' => implode(' / ', $parts),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -42,11 +42,11 @@ $endpointUnionSql = "
|
||||
'outlet' AS endpoint_type,
|
||||
nop.id AS endpoint_id,
|
||||
nop.name AS port_name,
|
||||
CONCAT(no.name, ' / ', IFNULL(r.name, ''), ' / ', IFNULL(f.name, '')) AS owner_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 no ON no.id = nop.outlet_id
|
||||
LEFT JOIN rooms r ON r.id = no.room_id
|
||||
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
|
||||
@@ -328,6 +328,7 @@ if ($deviceId > 0) {
|
||||
|
||||
<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?"
|
||||
|
||||
@@ -51,6 +51,41 @@ if ($portAId <= 0 || $portBId <= 0) {
|
||||
$errors[] = "Beide Ports sind erforderlich";
|
||||
}
|
||||
|
||||
$otherConnections = $sql->get(
|
||||
"SELECT id, port_a_type, port_a_id, port_b_type, port_b_id
|
||||
FROM connections
|
||||
WHERE id <> ?",
|
||||
"i",
|
||||
[$connId]
|
||||
);
|
||||
|
||||
$isEndpointUsed = static function (string $endpointType, int $endpointId) use ($otherConnections, $normalizePortType): bool {
|
||||
if ($endpointId <= 0) {
|
||||
return false;
|
||||
}
|
||||
foreach ((array)$otherConnections as $row) {
|
||||
$typeA = $normalizePortType((string)($row['port_a_type'] ?? ''));
|
||||
$idA = (int)($row['port_a_id'] ?? 0);
|
||||
if ($typeA === $endpointType && $idA === $endpointId) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$typeB = $normalizePortType((string)($row['port_b_type'] ?? ''));
|
||||
$idB = (int)($row['port_b_id'] ?? 0);
|
||||
if ($typeB === $endpointType && $idB === $endpointId) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
if ($isEndpointUsed($portAType, $portAId)) {
|
||||
$errors[] = "Port an Endpunkt A ist bereits in Verwendung";
|
||||
}
|
||||
if ($isEndpointUsed($portBType, $portBId)) {
|
||||
$errors[] = "Port an Endpunkt B ist bereits in Verwendung";
|
||||
}
|
||||
|
||||
if (!empty($errors)) {
|
||||
$_SESSION['error'] = implode(', ', $errors);
|
||||
$redirectUrl = $connId ? "?module=connections&action=edit&id=$connId" : "?module=connections&action=edit";
|
||||
@@ -67,15 +102,36 @@ if ($connId > 0) {
|
||||
// UPDATE
|
||||
$sql->set(
|
||||
"UPDATE connections SET port_a_type = ?, port_a_id = ?, port_b_type = ?, port_b_id = ?, vlan_config = ?, comment = ? WHERE id = ?",
|
||||
"siisisi",
|
||||
"sisissi",
|
||||
[$portAType, $portAId, $portBType, $portBId, $vlanJson, $comment, $connId]
|
||||
);
|
||||
} else {
|
||||
$connectionTypeId = (int)($sql->single(
|
||||
"SELECT id FROM connection_types ORDER BY id LIMIT 1",
|
||||
"",
|
||||
[]
|
||||
)['id'] ?? 0);
|
||||
|
||||
if ($connectionTypeId <= 0) {
|
||||
$connectionTypeId = (int)$sql->set(
|
||||
"INSERT INTO connection_types (name, medium, duplex, line_style, comment) VALUES (?, ?, ?, ?, ?)",
|
||||
"sssss",
|
||||
['Default', 'copper', 'custom', 'solid', 'Auto-created by connections/save'],
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
if ($connectionTypeId <= 0) {
|
||||
$_SESSION['error'] = "Kein Verbindungstyp verfuegbar";
|
||||
header("Location: ?module=connections&action=edit");
|
||||
exit;
|
||||
}
|
||||
|
||||
// INSERT
|
||||
$sql->set(
|
||||
"INSERT INTO connections (port_a_type, port_a_id, port_b_type, port_b_id, vlan_config, comment) VALUES (?, ?, ?, ?, ?, ?)",
|
||||
"siisis",
|
||||
[$portAType, $portAId, $portBType, $portBId, $vlanJson, $comment]
|
||||
"INSERT INTO connections (connection_type_id, port_a_type, port_a_id, port_b_type, port_b_id, vlan_config, comment) VALUES (?, ?, ?, ?, ?, ?, ?)",
|
||||
"isisiss",
|
||||
[$connectionTypeId, $portAType, $portAId, $portBType, $portBId, $vlanJson, $comment]
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
46
app/modules/connections/swap.php
Normal file
46
app/modules/connections/swap.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
/**
|
||||
* app/modules/connections/swap.php
|
||||
*
|
||||
* Vertauscht Endpunkt A und Endpunkt B einer Verbindung.
|
||||
*/
|
||||
|
||||
$connectionId = (int)($_GET['id'] ?? 0);
|
||||
|
||||
if ($connectionId <= 0) {
|
||||
$_SESSION['error'] = 'Ungueltige Verbindungs-ID';
|
||||
header('Location: ?module=connections&action=list');
|
||||
exit;
|
||||
}
|
||||
|
||||
$connection = $sql->single(
|
||||
"SELECT id, port_a_type, port_a_id, port_b_type, port_b_id
|
||||
FROM connections
|
||||
WHERE id = ?",
|
||||
"i",
|
||||
[$connectionId]
|
||||
);
|
||||
|
||||
if (!$connection) {
|
||||
$_SESSION['error'] = 'Verbindung nicht gefunden';
|
||||
header('Location: ?module=connections&action=list');
|
||||
exit;
|
||||
}
|
||||
|
||||
$sql->set(
|
||||
"UPDATE connections
|
||||
SET port_a_type = ?, port_a_id = ?, port_b_type = ?, port_b_id = ?
|
||||
WHERE id = ?",
|
||||
"sisii",
|
||||
[
|
||||
(string)$connection['port_b_type'],
|
||||
(int)$connection['port_b_id'],
|
||||
(string)$connection['port_a_type'],
|
||||
(int)$connection['port_a_id'],
|
||||
$connectionId
|
||||
]
|
||||
);
|
||||
|
||||
$_SESSION['success'] = 'Endpunkte wurden vertauscht';
|
||||
header('Location: ?module=connections&action=list');
|
||||
exit;
|
||||
@@ -21,6 +21,8 @@ if ($type === 'patchpanel') {
|
||||
$portCount = (int)($_POST['port_count'] ?? 0);
|
||||
$comment = trim($_POST['comment'] ?? '');
|
||||
|
||||
$panelId = $id;
|
||||
|
||||
if ($id > 0) {
|
||||
$sql->set(
|
||||
"UPDATE floor_patchpanels SET name = ?, floor_id = ?, pos_x = ?, pos_y = ?, width = ?, height = ?, port_count = ?, comment = ? WHERE id = ?",
|
||||
@@ -28,18 +30,38 @@ if ($type === 'patchpanel') {
|
||||
[$name, $floorId, $posX, $posY, $width, $height, $portCount, $comment, $id]
|
||||
);
|
||||
} else {
|
||||
$sql->set(
|
||||
$panelId = (int)$sql->set(
|
||||
"INSERT INTO floor_patchpanels (name, floor_id, pos_x, pos_y, width, height, port_count, comment) VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
|
||||
"siiiiiss",
|
||||
[$name, $floorId, $posX, $posY, $width, $height, $portCount, $comment]
|
||||
[$name, $floorId, $posX, $posY, $width, $height, $portCount, $comment],
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
if ($panelId > 0 && $portCount > 0) {
|
||||
$existingCount = (int)($sql->single(
|
||||
"SELECT COUNT(*) AS cnt FROM floor_patchpanel_ports WHERE patchpanel_id = ?",
|
||||
"i",
|
||||
[$panelId]
|
||||
)['cnt'] ?? 0);
|
||||
|
||||
if ($existingCount < $portCount) {
|
||||
for ($i = $existingCount + 1; $i <= $portCount; $i++) {
|
||||
$sql->set(
|
||||
"INSERT INTO floor_patchpanel_ports (patchpanel_id, name) VALUES (?, ?)",
|
||||
"is",
|
||||
[$panelId, 'Port ' . $i]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
} elseif ($type === 'outlet') {
|
||||
$name = trim($_POST['name'] ?? '');
|
||||
$roomId = (int)($_POST['room_id'] ?? 0);
|
||||
$x = (int)($_POST['x'] ?? 0);
|
||||
$y = (int)($_POST['y'] ?? 0);
|
||||
$comment = trim($_POST['comment'] ?? '');
|
||||
$outletId = $id;
|
||||
|
||||
if ($id > 0) {
|
||||
$sql->set(
|
||||
@@ -48,12 +70,29 @@ if ($type === 'patchpanel') {
|
||||
[$name, $roomId, $x, $y, $comment, $id]
|
||||
);
|
||||
} else {
|
||||
$sql->set(
|
||||
$outletId = (int)$sql->set(
|
||||
"INSERT INTO network_outlets (name, room_id, x, y, comment) VALUES (?, ?, ?, ?, ?)",
|
||||
"siiis",
|
||||
[$name, $roomId, $x, $y, $comment]
|
||||
[$name, $roomId, $x, $y, $comment],
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
if ($outletId > 0) {
|
||||
$existingPortCount = (int)($sql->single(
|
||||
"SELECT COUNT(*) AS cnt FROM network_outlet_ports WHERE outlet_id = ?",
|
||||
"i",
|
||||
[$outletId]
|
||||
)['cnt'] ?? 0);
|
||||
|
||||
if ($existingPortCount === 0) {
|
||||
$sql->set(
|
||||
"INSERT INTO network_outlet_ports (outlet_id, name) VALUES (?, 'Port 1')",
|
||||
"i",
|
||||
[$outletId]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
header('Location: ?module=floor_infrastructure&action=list');
|
||||
|
||||
Reference in New Issue
Block a user