feat: Implement initial application structure with network view and SVG editor
- Added network-view.js for visualizing network topology with devices and connections. - Introduced svg-editor.js for managing ports on device types with drag-and-drop functionality. - Created bootstrap.php for application initialization, including configuration and database connection. - Established config.php for centralized configuration settings. - Developed index.php as the main entry point with module-based routing. - Integrated _sql.php for database abstraction. - Added auth.php for single-user authentication handling. - Included helpers.php for utility functions. - Created modules for managing connections, device types, devices, and floors. - Implemented database schema in init.sql for locations, buildings, floors, rooms, network outlets, devices, and connections. - Added Docker support with docker-compose.yml for web and database services. - Documented database structure and UI/UX concepts in respective markdown files.
This commit is contained in:
4
NOTES.md
Normal file
4
NOTES.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# Notizen
|
||||
|
||||
https://chatgpt.com/share/698517b9-b1e4-800e-bbd8-d207dfb326f0
|
||||
|
||||
250
README.md
250
README.md
@@ -1,2 +1,252 @@
|
||||
# netwatch
|
||||
|
||||
### Stockwerksplan (SVG)
|
||||
- Pro Stockwerk ein SVG
|
||||
- Enthält:
|
||||
- Räume (benennbar, nummerierbar)
|
||||
- Netzwerkdosen (frei platzierbar)
|
||||
- Elemente sind:
|
||||
- Verschiebbar
|
||||
- Nachträglich anpassbar
|
||||
- Eindeutig referenzierbar
|
||||
|
||||
### Netzwerkdose
|
||||
- Name / Nummer
|
||||
- Raum
|
||||
- Anzahl Ports
|
||||
- Porttypen (z. B. RJ45, Glasfaser, BNC)
|
||||
- Ports sind vollständig normale Ports im System
|
||||
|
||||
---
|
||||
|
||||
## Gerätetypen
|
||||
|
||||
### Gerätetyp
|
||||
Definiert das **Aussehen und die Port-Geometrie** eines Geräts.
|
||||
|
||||
**Attribute**
|
||||
- Name
|
||||
- Kategorie:
|
||||
- Switch
|
||||
- Server
|
||||
- Patchpanel
|
||||
- Sonstiges
|
||||
- Darstellung:
|
||||
- SVG **oder**
|
||||
- JPG/PNG
|
||||
|
||||
### Portdefinition im Bild
|
||||
Beim Anlegen eines Gerätetyps:
|
||||
- Bild wird angezeigt
|
||||
- Ports werden per Klick gesetzt:
|
||||
- X/Y relativ zum Bild
|
||||
- Portname
|
||||
- Porttyp
|
||||
- Diese Portdefinition ist die Vorlage für alle Geräte dieses Typs
|
||||
|
||||
---
|
||||
|
||||
## Geräte
|
||||
|
||||
### Gerät
|
||||
Instanz eines Gerätetyps.
|
||||
|
||||
**Attribute**
|
||||
- Name
|
||||
- Gerätetyp
|
||||
- Standort / Rack / Stockwerk
|
||||
- Position im Rack:
|
||||
- Start-HE
|
||||
- Höhe in HE
|
||||
- Seriennummer (optional)
|
||||
- Kommentar
|
||||
|
||||
Ports werden automatisch aus dem Gerätetyp erzeugt.
|
||||
|
||||
---
|
||||
|
||||
## Switches & Ports
|
||||
|
||||
### Ports
|
||||
- Name / Nummer
|
||||
- Porttyp (frei definierbar)
|
||||
- Geschwindigkeit(en)
|
||||
- Status (aktiv / deaktiviert)
|
||||
- VLAN-Zuweisung
|
||||
- Modus:
|
||||
- Access
|
||||
- Trunk
|
||||
- Hybrid
|
||||
- Custom (Freitext)
|
||||
|
||||
---
|
||||
|
||||
## Module (z. B. SFP, Spezialkarten)
|
||||
|
||||
### Modul
|
||||
Eigenständige Komponente, die in einen Port eingesetzt wird.
|
||||
|
||||
**Eigenschaften**
|
||||
- Name
|
||||
- Typ (z. B. SFP, QSFP, BNC-Modul)
|
||||
- Kompatible Porttypen
|
||||
- Eigene Ports (z. B. LC Duplex)
|
||||
|
||||
### Logik
|
||||
```
|
||||
Switch-Port → Modul → Modul-Port → Verbindung
|
||||
```
|
||||
|
||||
|
||||
Module können selbst Ports besitzen und sind vollwertige Verbindungspartner.
|
||||
|
||||
---
|
||||
|
||||
## Verbindungstypen
|
||||
|
||||
### Verbindungstyp (frei definierbar)
|
||||
- Name
|
||||
- Medium:
|
||||
- Kupfer
|
||||
- Glasfaser
|
||||
- Koax
|
||||
- Sonstiges
|
||||
- Duplex:
|
||||
- Half
|
||||
- Full
|
||||
- Custom
|
||||
- Max. Geschwindigkeit (optional)
|
||||
- Darstellung:
|
||||
- Farbe
|
||||
- Linientyp (durchgezogen, gestrichelt)
|
||||
|
||||
Beispiele:
|
||||
- RJ45 Cat6
|
||||
- LC-LC Singlemode
|
||||
- BNC Token Ring
|
||||
- Proprietär XYZ
|
||||
|
||||
---
|
||||
|
||||
## Verbindungen
|
||||
|
||||
### Verbindung
|
||||
- Port A ↔ Port B
|
||||
- Verbindungstyp
|
||||
- VLAN(s)
|
||||
- Modus
|
||||
- Kommentar
|
||||
|
||||
Verbindungen werden:
|
||||
- logisch gespeichert
|
||||
- grafisch in allen relevanten SVGs dargestellt
|
||||
|
||||
---
|
||||
|
||||
## Grafische Ansichten
|
||||
|
||||
### Rack-Ansicht (SVG)
|
||||
- Frontansicht mit HE-Raster
|
||||
- Geräte:
|
||||
- korrekt skaliert
|
||||
- drag & drop
|
||||
- Kabel:
|
||||
- Linien zwischen Portpunkten
|
||||
- Farbe gemäß Verbindungstyp
|
||||
- Hover zeigt Details
|
||||
|
||||
---
|
||||
|
||||
### Netzwerkansicht (Graph)
|
||||
- Geräte als Nodes
|
||||
- Verbindungen als Edges
|
||||
- Ports optional sichtbar
|
||||
- Layout:
|
||||
- automatisch (Force-Layout)
|
||||
- manuell verschiebbar
|
||||
- Filter:
|
||||
- VLAN
|
||||
- Verbindungstyp
|
||||
- Standort
|
||||
|
||||
---
|
||||
|
||||
### Stockwerks- & Raumansicht
|
||||
- SVG-Plan pro Stockwerk
|
||||
- Netzwerkdosen:
|
||||
- anklickbar
|
||||
- Ports sichtbar
|
||||
- Verbindungen zu Racks / Switches darstellbar
|
||||
|
||||
---
|
||||
|
||||
## Datenbank (konzeptionell)
|
||||
|
||||
### Zentrale Tabellen
|
||||
- `locations`
|
||||
- `buildings`
|
||||
- `floors`
|
||||
- `rooms`
|
||||
- `floor_svgs`
|
||||
- `network_outlets`
|
||||
- `device_types`
|
||||
- `device_type_ports`
|
||||
- `devices`
|
||||
- `device_ports`
|
||||
- `modules`
|
||||
- `module_ports`
|
||||
- `connection_types`
|
||||
- `connections`
|
||||
- `vlans`
|
||||
- `racks`
|
||||
|
||||
Custom-Eigenschaften werden teilweise als JSON gespeichert, um Erweiterungen
|
||||
ohne Schema-Brüche zu ermöglichen.
|
||||
|
||||
---
|
||||
|
||||
## Erweiterbarkeit
|
||||
|
||||
Geplant, aber nicht initial:
|
||||
- Mehrbenutzerfähigkeit
|
||||
- Historisierung / Änderungsverlauf
|
||||
- Rechte & Rollen
|
||||
- Export (JSON, CSV, PDF)
|
||||
- Theming / Design-System
|
||||
- API
|
||||
|
||||
---
|
||||
|
||||
## Projektphasen
|
||||
|
||||
### Phase 1 – Fundament
|
||||
- Docker Compose
|
||||
- Basisdatenbank
|
||||
- CRUD für:
|
||||
- Verbindungstypen
|
||||
- Gerätetypen
|
||||
- Standorte
|
||||
|
||||
### Phase 2 – Grafik & Geräte
|
||||
- Gerätetyp-Editor mit Port-Klick
|
||||
- Rack-Ansicht
|
||||
- Geräteplatzierung
|
||||
|
||||
### Phase 3 – Verkabelung
|
||||
- Verbindungslogik
|
||||
- Module
|
||||
- VLANs
|
||||
- Visuelle Kabeldarstellung
|
||||
|
||||
### Phase 4 – Gebäudepläne
|
||||
- Stockwerks-SVG
|
||||
- Räume
|
||||
- Netzwerkdosen
|
||||
- Verknüpfung mit Geräten
|
||||
|
||||
---
|
||||
|
||||
## Zielzustand
|
||||
Ein zuverlässiges, verständliches und visuell präzises Werkzeug zur
|
||||
Netzwerkdokumentation, das **nicht einschränkt**, sondern reale,
|
||||
auch unkonventionelle Infrastrukturen korrekt abbilden kann.
|
||||
|
||||
48
app/.htaccess
Normal file
48
app/.htaccess
Normal file
@@ -0,0 +1,48 @@
|
||||
# =========================
|
||||
# Grundschutz
|
||||
# =========================
|
||||
|
||||
# Kein Directory-Listing
|
||||
Options -Indexes
|
||||
|
||||
# Schutz für sensible Dateien
|
||||
<FilesMatch "(\.env|\.git|config\.php|_sql\.php)">
|
||||
Require all denied
|
||||
</FilesMatch>
|
||||
|
||||
# TODO: ggf. weitere Dateien schützen, z.B. uploads oder tmp
|
||||
|
||||
# =========================
|
||||
# Rewrite zu index.php
|
||||
# =========================
|
||||
RewriteEngine On
|
||||
|
||||
# Alles auf index.php umleiten, außer echte Dateien/Verzeichnisse
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteRule ^(.*)$ index.php [QSA,L]
|
||||
|
||||
# =========================
|
||||
# Standard-Dokument
|
||||
# =========================
|
||||
DirectoryIndex index.php
|
||||
|
||||
# =========================
|
||||
# Security Headers
|
||||
# =========================
|
||||
<IfModule mod_headers.c>
|
||||
Header set X-Content-Type-Options "nosniff"
|
||||
Header set X-Frame-Options "SAMEORIGIN"
|
||||
Header set X-XSS-Protection "1; mode=block"
|
||||
Header always set Referrer-Policy "no-referrer-when-downgrade"
|
||||
Header always set Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:;"
|
||||
</IfModule>
|
||||
|
||||
# =========================
|
||||
# Upload-Sicherheit
|
||||
# =========================
|
||||
<FilesMatch "\.(php|phtml|php3|php4|php5|php7|phps)$">
|
||||
Require all denied
|
||||
</FilesMatch>
|
||||
|
||||
# TODO: Optional: Upload-Verzeichnisse (device_types, floorplans) via .htaccess zusätzlich schützen
|
||||
2
app/api/connections.php
Normal file
2
app/api/connections.php
Normal file
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
// API für Verbindungsdaten (Netzwerkansicht)
|
||||
2
app/api/device_type_ports.php
Normal file
2
app/api/device_type_ports.php
Normal file
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
// Laden und Speichern von Port-Punkten für den SVG-Port-Editor
|
||||
2
app/api/upload.php
Normal file
2
app/api/upload.php
Normal file
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
// Datei-Upload für Gerätebilder und SVGs
|
||||
1
app/assets/css/app.css
Normal file
1
app/assets/css/app.css
Normal file
@@ -0,0 +1 @@
|
||||
/* Zentrales Stylesheet (Layout, Farben, Komponenten) */
|
||||
1
app/assets/js/app.js
Normal file
1
app/assets/js/app.js
Normal file
@@ -0,0 +1 @@
|
||||
// Globale JS-Funktionen, Initialisierung
|
||||
289
app/assets/js/network-view.js
Normal file
289
app/assets/js/network-view.js
Normal file
@@ -0,0 +1,289 @@
|
||||
// Netzwerk-Graph-Ansicht (Nodes, Kanten, Filter)
|
||||
/**
|
||||
* network-view.js
|
||||
*
|
||||
* Darstellung der Netzwerk-Topologie:
|
||||
* - Geräte als Nodes
|
||||
* - Ports als Ankerpunkte
|
||||
* - Verbindungen als Linien
|
||||
* - Freie / selbstdefinierte Verbindungstypen
|
||||
*
|
||||
* Kein Layout-Framework (kein D3, kein Cytoscape)
|
||||
* -> bewusst simpel & erweiterbar
|
||||
*/
|
||||
|
||||
/* =========================
|
||||
* Konfiguration
|
||||
* ========================= */
|
||||
|
||||
// TODO: Standort / Rack / View-Kontext vom Backend setzen
|
||||
const CONTEXT_ID = null;
|
||||
|
||||
// TODO: API-Endpunkte definieren
|
||||
const API_LOAD_NETWORK = '/api/network_view.php?action=load';
|
||||
const API_SAVE_POSITIONS = '/api/network_view.php?action=save_positions';
|
||||
|
||||
/* =========================
|
||||
* State
|
||||
* ========================= */
|
||||
|
||||
let svgElement = null;
|
||||
|
||||
let devices = []; // Geräte inkl. Position
|
||||
let connections = []; // Verbindungen zwischen Ports
|
||||
|
||||
let selectedDeviceId = null;
|
||||
let isDragging = false;
|
||||
let dragOffset = { x: 0, y: 0 };
|
||||
|
||||
/* =========================
|
||||
* Initialisierung
|
||||
* ========================= */
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
svgElement = document.querySelector('#network-svg');
|
||||
|
||||
if (!svgElement) {
|
||||
console.warn('Network View: #network-svg nicht gefunden');
|
||||
return;
|
||||
}
|
||||
|
||||
bindSvgEvents();
|
||||
loadNetwork();
|
||||
});
|
||||
|
||||
/* =========================
|
||||
* Events
|
||||
* ========================= */
|
||||
|
||||
function bindSvgEvents() {
|
||||
svgElement.addEventListener('mousemove', onMouseMove);
|
||||
svgElement.addEventListener('mouseup', onMouseUp);
|
||||
svgElement.addEventListener('click', onSvgClick);
|
||||
}
|
||||
|
||||
/* =========================
|
||||
* Laden
|
||||
* ========================= */
|
||||
|
||||
function loadNetwork() {
|
||||
if (!CONTEXT_ID) {
|
||||
console.warn('CONTEXT_ID nicht gesetzt');
|
||||
return;
|
||||
}
|
||||
|
||||
fetch(`${API_LOAD_NETWORK}&context_id=${CONTEXT_ID}`)
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
// TODO: Datenstruktur validieren
|
||||
devices = data.devices || [];
|
||||
connections = data.connections || [];
|
||||
|
||||
renderAll();
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('Fehler beim Laden der Netzwerkansicht', err);
|
||||
});
|
||||
}
|
||||
|
||||
/* =========================
|
||||
* Rendering
|
||||
* ========================= */
|
||||
|
||||
function renderAll() {
|
||||
clearSvg();
|
||||
|
||||
renderConnections();
|
||||
renderDevices();
|
||||
}
|
||||
|
||||
function clearSvg() {
|
||||
while (svgElement.firstChild) {
|
||||
svgElement.removeChild(svgElement.firstChild);
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------- Geräte ---------- */
|
||||
|
||||
function renderDevices() {
|
||||
devices.forEach(device => renderDevice(device));
|
||||
}
|
||||
|
||||
function renderDevice(device) {
|
||||
const group = document.createElementNS('http://www.w3.org/2000/svg', 'g');
|
||||
group.classList.add('device-node');
|
||||
group.dataset.id = device.id;
|
||||
|
||||
group.setAttribute(
|
||||
'transform',
|
||||
`translate(${device.x || 0}, ${device.y || 0})`
|
||||
);
|
||||
|
||||
// TODO: Gerätetyp (SVG oder JPG) korrekt laden
|
||||
const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
|
||||
rect.setAttribute('width', 120);
|
||||
rect.setAttribute('height', 60);
|
||||
rect.setAttribute('rx', 6);
|
||||
|
||||
rect.addEventListener('mousedown', (e) => {
|
||||
startDrag(e, device.id);
|
||||
e.stopPropagation();
|
||||
});
|
||||
|
||||
// Label
|
||||
const text = document.createElementNS('http://www.w3.org/2000/svg', 'text');
|
||||
text.setAttribute('x', 60);
|
||||
text.setAttribute('y', 35);
|
||||
text.setAttribute('text-anchor', 'middle');
|
||||
text.textContent = device.name || 'Device';
|
||||
|
||||
group.appendChild(rect);
|
||||
group.appendChild(text);
|
||||
|
||||
// TODO: Ports als kleine Kreise anlegen (Position aus Portdefinition)
|
||||
// TODO: Ports klickbar machen (für Verbindungs-Erstellung)
|
||||
|
||||
svgElement.appendChild(group);
|
||||
}
|
||||
|
||||
/* ---------- Verbindungen ---------- */
|
||||
|
||||
function renderConnections() {
|
||||
connections.forEach(conn => renderConnection(conn));
|
||||
}
|
||||
|
||||
function renderConnection(connection) {
|
||||
// TODO: Quell- & Ziel-Port-Koordinaten berechnen
|
||||
// TODO: unterschiedliche Verbindungstypen (Farbe, Strichart, Dicke)
|
||||
|
||||
const line = document.createElementNS('http://www.w3.org/2000/svg', 'line');
|
||||
|
||||
line.setAttribute('x1', 0);
|
||||
line.setAttribute('y1', 0);
|
||||
line.setAttribute('x2', 100);
|
||||
line.setAttribute('y2', 100);
|
||||
|
||||
line.classList.add('connection-line');
|
||||
|
||||
svgElement.appendChild(line);
|
||||
}
|
||||
|
||||
/* =========================
|
||||
* Interaktion
|
||||
* ========================= */
|
||||
|
||||
function onSvgClick(event) {
|
||||
// Klick ins Leere -> Auswahl aufheben
|
||||
if (event.target === svgElement) {
|
||||
selectedDeviceId = null;
|
||||
updateSelection();
|
||||
}
|
||||
}
|
||||
|
||||
function startDrag(event, deviceId) {
|
||||
const device = getDeviceById(deviceId);
|
||||
if (!device) return;
|
||||
|
||||
isDragging = true;
|
||||
selectedDeviceId = deviceId;
|
||||
|
||||
const point = getSvgCoordinates(event);
|
||||
dragOffset.x = (device.x || 0) - point.x;
|
||||
dragOffset.y = (device.y || 0) - point.y;
|
||||
|
||||
updateSelection();
|
||||
}
|
||||
|
||||
function onMouseMove(event) {
|
||||
if (!isDragging || !selectedDeviceId) return;
|
||||
|
||||
const device = getDeviceById(selectedDeviceId);
|
||||
if (!device) return;
|
||||
|
||||
const point = getSvgCoordinates(event);
|
||||
|
||||
device.x = point.x + dragOffset.x;
|
||||
device.y = point.y + dragOffset.y;
|
||||
|
||||
renderAll();
|
||||
}
|
||||
|
||||
function onMouseUp() {
|
||||
if (!isDragging) return;
|
||||
|
||||
isDragging = false;
|
||||
|
||||
// TODO: Positionen optional automatisch speichern
|
||||
}
|
||||
|
||||
/* =========================
|
||||
* Auswahl
|
||||
* ========================= */
|
||||
|
||||
function updateSelection() {
|
||||
svgElement.querySelectorAll('.device-node').forEach(el => {
|
||||
el.classList.toggle(
|
||||
'selected',
|
||||
el.dataset.id === String(selectedDeviceId)
|
||||
);
|
||||
});
|
||||
|
||||
// TODO: Sidebar mit Gerätedetails füllen
|
||||
}
|
||||
|
||||
/* =========================
|
||||
* Speichern
|
||||
* ========================= */
|
||||
|
||||
function savePositions() {
|
||||
fetch(API_SAVE_POSITIONS, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
context_id: CONTEXT_ID,
|
||||
devices: devices.map(d => ({
|
||||
id: d.id,
|
||||
x: d.x,
|
||||
y: d.y
|
||||
}))
|
||||
})
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
// TODO: Erfolg / Fehler anzeigen
|
||||
console.log('Positionen gespeichert', data);
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('Fehler beim Speichern', err);
|
||||
});
|
||||
}
|
||||
|
||||
/* =========================
|
||||
* Hilfsfunktionen
|
||||
* ========================= */
|
||||
|
||||
function getSvgCoordinates(event) {
|
||||
const pt = svgElement.createSVGPoint();
|
||||
pt.x = event.clientX;
|
||||
pt.y = event.clientY;
|
||||
|
||||
const transformed = pt.matrixTransform(svgElement.getScreenCTM().inverse());
|
||||
return { x: transformed.x, y: transformed.y };
|
||||
}
|
||||
|
||||
function getDeviceById(id) {
|
||||
return devices.find(d => d.id === id);
|
||||
}
|
||||
|
||||
/* =========================
|
||||
* Keyboard Shortcuts
|
||||
* ========================= */
|
||||
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Escape') {
|
||||
selectedDeviceId = null;
|
||||
updateSelection();
|
||||
}
|
||||
|
||||
// TODO: Delete -> Gerät entfernen?
|
||||
});
|
||||
258
app/assets/js/svg-editor.js
Normal file
258
app/assets/js/svg-editor.js
Normal file
@@ -0,0 +1,258 @@
|
||||
// Logik für den SVG-Port-Editor (Klicks, Drag & Drop, Speichern)
|
||||
/**
|
||||
* svg-editor.js
|
||||
*
|
||||
* Logik für den SVG-Port-Editor:
|
||||
* - Ports per Klick anlegen
|
||||
* - Ports auswählen
|
||||
* - Ports verschieben (Drag & Drop)
|
||||
* - Ports löschen
|
||||
* - Ports laden / speichern
|
||||
*
|
||||
* Abhängigkeiten: keine (Vanilla JS)
|
||||
*/
|
||||
|
||||
/* =========================
|
||||
* Konfiguration
|
||||
* ========================= */
|
||||
|
||||
// TODO: vom Backend setzen (z. B. via data-Attribut)
|
||||
const DEVICE_TYPE_ID = null;
|
||||
|
||||
// TODO: API-Endpunkte festlegen
|
||||
const API_LOAD_PORTS = '/api/device_type_ports.php?action=load';
|
||||
const API_SAVE_PORTS = '/api/device_type_ports.php?action=save';
|
||||
|
||||
/* =========================
|
||||
* State
|
||||
* ========================= */
|
||||
|
||||
let svgElement = null;
|
||||
let ports = [];
|
||||
let selectedPortId = null;
|
||||
let isDragging = false;
|
||||
let dragOffset = { x: 0, y: 0 };
|
||||
|
||||
/* =========================
|
||||
* Initialisierung
|
||||
* ========================= */
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
svgElement = document.querySelector('#device-svg');
|
||||
|
||||
if (!svgElement) {
|
||||
console.warn('SVG Editor: #device-svg nicht gefunden');
|
||||
return;
|
||||
}
|
||||
|
||||
bindSvgEvents();
|
||||
loadPorts();
|
||||
});
|
||||
|
||||
/* =========================
|
||||
* SVG Events
|
||||
* ========================= */
|
||||
|
||||
function bindSvgEvents() {
|
||||
svgElement.addEventListener('click', onSvgClick);
|
||||
svgElement.addEventListener('mousemove', onSvgMouseMove);
|
||||
svgElement.addEventListener('mouseup', onSvgMouseUp);
|
||||
}
|
||||
|
||||
/* =========================
|
||||
* Port-Erstellung
|
||||
* ========================= */
|
||||
|
||||
function onSvgClick(event) {
|
||||
// Klick auf bestehenden Port?
|
||||
if (event.target.classList.contains('port-point')) {
|
||||
selectPort(event.target.dataset.id);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Modifier-Key prüfen (z. B. nur mit SHIFT neuen Port erstellen?)
|
||||
const point = getSvgCoordinates(event);
|
||||
|
||||
createPort(point.x, point.y);
|
||||
}
|
||||
|
||||
function createPort(x, y) {
|
||||
const id = generateTempId();
|
||||
|
||||
const port = {
|
||||
id: id,
|
||||
name: `Port ${ports.length + 1}`,
|
||||
port_type_id: null, // TODO: Default-Porttyp?
|
||||
x: x,
|
||||
y: y,
|
||||
comment: ''
|
||||
};
|
||||
|
||||
ports.push(port);
|
||||
renderPort(port);
|
||||
selectPort(id);
|
||||
}
|
||||
|
||||
/* =========================
|
||||
* Rendering
|
||||
* ========================= */
|
||||
|
||||
function renderPort(port) {
|
||||
const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
|
||||
|
||||
circle.setAttribute('cx', port.x);
|
||||
circle.setAttribute('cy', port.y);
|
||||
circle.setAttribute('r', 6);
|
||||
circle.classList.add('port-point');
|
||||
circle.dataset.id = port.id;
|
||||
|
||||
circle.addEventListener('mousedown', (e) => {
|
||||
startDrag(e, port.id);
|
||||
e.stopPropagation();
|
||||
});
|
||||
|
||||
svgElement.appendChild(circle);
|
||||
}
|
||||
|
||||
function rerenderPorts() {
|
||||
svgElement.querySelectorAll('.port-point').forEach(p => p.remove());
|
||||
ports.forEach(renderPort);
|
||||
}
|
||||
|
||||
/* =========================
|
||||
* Auswahl
|
||||
* ========================= */
|
||||
|
||||
function selectPort(id) {
|
||||
selectedPortId = id;
|
||||
|
||||
document.querySelectorAll('.port-point').forEach(el => {
|
||||
el.classList.toggle('selected', el.dataset.id === id);
|
||||
});
|
||||
|
||||
// TODO: Sidebar-Felder mit Portdaten füllen
|
||||
}
|
||||
|
||||
/* =========================
|
||||
* Drag & Drop
|
||||
* ========================= */
|
||||
|
||||
function startDrag(event, portId) {
|
||||
const port = getPortById(portId);
|
||||
if (!port) return;
|
||||
|
||||
isDragging = true;
|
||||
selectedPortId = portId;
|
||||
|
||||
const point = getSvgCoordinates(event);
|
||||
dragOffset.x = port.x - point.x;
|
||||
dragOffset.y = port.y - point.y;
|
||||
}
|
||||
|
||||
function onSvgMouseMove(event) {
|
||||
if (!isDragging || !selectedPortId) return;
|
||||
|
||||
const port = getPortById(selectedPortId);
|
||||
if (!port) return;
|
||||
|
||||
const point = getSvgCoordinates(event);
|
||||
|
||||
port.x = point.x + dragOffset.x;
|
||||
port.y = point.y + dragOffset.y;
|
||||
|
||||
rerenderPorts();
|
||||
}
|
||||
|
||||
function onSvgMouseUp() {
|
||||
isDragging = false;
|
||||
}
|
||||
|
||||
/* =========================
|
||||
* Löschen
|
||||
* ========================= */
|
||||
|
||||
function deleteSelectedPort() {
|
||||
if (!selectedPortId) return;
|
||||
|
||||
// TODO: Sicherheitsabfrage (confirm)
|
||||
ports = ports.filter(p => p.id !== selectedPortId);
|
||||
selectedPortId = null;
|
||||
|
||||
rerenderPorts();
|
||||
|
||||
// TODO: Sidebar zurücksetzen
|
||||
}
|
||||
|
||||
/* =========================
|
||||
* Laden / Speichern
|
||||
* ========================= */
|
||||
|
||||
function loadPorts() {
|
||||
if (!DEVICE_TYPE_ID) {
|
||||
console.warn('DEVICE_TYPE_ID nicht gesetzt');
|
||||
return;
|
||||
}
|
||||
|
||||
fetch(`${API_LOAD_PORTS}&device_type_id=${DEVICE_TYPE_ID}`)
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
// TODO: Datenformat validieren
|
||||
ports = data;
|
||||
rerenderPorts();
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('Fehler beim Laden der Ports', err);
|
||||
});
|
||||
}
|
||||
|
||||
function savePorts() {
|
||||
if (!DEVICE_TYPE_ID) return;
|
||||
|
||||
fetch(API_SAVE_PORTS, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
device_type_id: DEVICE_TYPE_ID,
|
||||
ports: ports
|
||||
})
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
// TODO: Erfolg / Fehler anzeigen
|
||||
console.log('Ports gespeichert', data);
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('Fehler beim Speichern', err);
|
||||
});
|
||||
}
|
||||
|
||||
/* =========================
|
||||
* Hilfsfunktionen
|
||||
* ========================= */
|
||||
|
||||
function getSvgCoordinates(event) {
|
||||
const pt = svgElement.createSVGPoint();
|
||||
pt.x = event.clientX;
|
||||
pt.y = event.clientY;
|
||||
|
||||
const transformed = pt.matrixTransform(svgElement.getScreenCTM().inverse());
|
||||
return { x: transformed.x, y: transformed.y };
|
||||
}
|
||||
|
||||
function getPortById(id) {
|
||||
return ports.find(p => p.id === id);
|
||||
}
|
||||
|
||||
function generateTempId() {
|
||||
return 'tmp_' + Math.random().toString(36).substr(2, 9);
|
||||
}
|
||||
|
||||
/* =========================
|
||||
* Keyboard Shortcuts
|
||||
* ========================= */
|
||||
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Delete') {
|
||||
deleteSelectedPort();
|
||||
}
|
||||
});
|
||||
48
app/bootstrap.php
Normal file
48
app/bootstrap.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
/**
|
||||
* bootstrap.php
|
||||
*
|
||||
* Initialisierung der Anwendung
|
||||
* - Config laden
|
||||
* - Session starten
|
||||
* - DB-Verbindung über _sql.php
|
||||
* - Helper einbinden
|
||||
*/
|
||||
|
||||
/* =========================
|
||||
* Config laden
|
||||
* ========================= */
|
||||
require_once __DIR__ . '/config.php';
|
||||
// TODO: Config-Datei mit DB-Zugang, Pfaden, globalen Settings füllen
|
||||
|
||||
/* =========================
|
||||
* Session starten
|
||||
* ========================= */
|
||||
session_start();
|
||||
// TODO: Single-User Auth prüfen
|
||||
// z.B. $_SESSION['user'] setzen oder Login erzwingen
|
||||
|
||||
/* =========================
|
||||
* DB-Verbindung initialisieren
|
||||
* ========================= */
|
||||
require_once __DIR__ . '/lib/_sql.php';
|
||||
|
||||
// TODO: Host, User, Passwort, DB aus config.php nutzen
|
||||
$sql = new SQL(
|
||||
DB_HOST, // z.B. localhost
|
||||
DB_USER, // z.B. netdoc
|
||||
DB_PASSWORD, // z.B. netdoc
|
||||
DB_NAME // z.B. netdoc
|
||||
);
|
||||
|
||||
/* =========================
|
||||
* Helper laden
|
||||
* ========================= */
|
||||
require_once __DIR__ . '/lib/helpers.php';
|
||||
// TODO: Globale Funktionen: escape, redirect, flash messages, etc.
|
||||
|
||||
/* =========================
|
||||
* Optional: Fehlerbehandlung
|
||||
* ========================= */
|
||||
// error_reporting(E_ALL);
|
||||
// ini_set('display_errors', 1);
|
||||
2
app/config.php
Normal file
2
app/config.php
Normal file
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
// Zentrale Konfiguration (DB-Zugangsdaten, Pfade, globale Settings)
|
||||
67
app/index.php
Normal file
67
app/index.php
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
// Einstiegspunkt der Anwendung, Routing zur jeweiligen Modul-Seite
|
||||
|
||||
|
||||
/**
|
||||
* index.php
|
||||
*
|
||||
* Einstiegspunkt der Anwendung
|
||||
* - Single-User
|
||||
* - Modulbasiertes Routing
|
||||
* - Basierend auf _sql.php
|
||||
* - HTML-Layout via templates/layout.php
|
||||
*/
|
||||
|
||||
/* =========================
|
||||
* Bootstrap
|
||||
* ========================= */
|
||||
require_once __DIR__ . '/bootstrap.php'; // lädt config, DB, helper
|
||||
// TODO: Session starten / Single-User-Auth prüfen
|
||||
|
||||
/* =========================
|
||||
* Routing
|
||||
* ========================= */
|
||||
|
||||
// Standard-Modul / Aktion
|
||||
$module = $_GET['module'] ?? 'dashboard';
|
||||
$action = $_GET['action'] ?? 'list';
|
||||
|
||||
// Whitelist der Module
|
||||
$validModules = ['dashboard', 'device_types', 'devices', 'racks', 'floors', 'connections'];
|
||||
|
||||
// Whitelist der Aktionen
|
||||
$validActions = ['list', 'edit', 'save', 'ports'];
|
||||
|
||||
// Prüfen auf gültige Werte
|
||||
if (!in_array($module, $validModules)) {
|
||||
// TODO: Fehlerseite anzeigen
|
||||
die('Ungültiges Modul');
|
||||
}
|
||||
|
||||
if (!in_array($action, $validActions)) {
|
||||
// TODO: Fehlerseite anzeigen
|
||||
die('Ungültige Aktion');
|
||||
}
|
||||
|
||||
/* =========================
|
||||
* Template-Header laden
|
||||
* ========================= */
|
||||
require_once __DIR__ . '/templates/header.php';
|
||||
// TODO: ggf. Navigation einbinden
|
||||
|
||||
/* =========================
|
||||
* Modul laden
|
||||
* ========================= */
|
||||
$modulePath = __DIR__ . "/modules/$module/$action.php";
|
||||
|
||||
if (file_exists($modulePath)) {
|
||||
require_once $modulePath;
|
||||
} else {
|
||||
// TODO: Fehlerseite oder 404
|
||||
echo "<p>Die Seite existiert noch nicht.</p>";
|
||||
}
|
||||
|
||||
/* =========================
|
||||
* Template-Footer laden
|
||||
* ========================= */
|
||||
require_once __DIR__ . '/templates/footer.php';
|
||||
2
app/lib/_sql.php
Normal file
2
app/lib/_sql.php
Normal file
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
// Datenbank-Abstraktionsklasse (bereits vorhanden, hier eingebunden)
|
||||
2
app/lib/auth.php
Normal file
2
app/lib/auth.php
Normal file
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
// Single-User-Authentifizierung (Login, Session-Handling)
|
||||
2
app/lib/helpers.php
Normal file
2
app/lib/helpers.php
Normal file
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
// Hilfsfunktionen (Escaping, Redirects, Formatierungen)
|
||||
2
app/modules/connections/list.php
Normal file
2
app/modules/connections/list.php
Normal file
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
// Anzeige aller Verbindungen (Filter, Übersicht)
|
||||
2
app/modules/connections/save.php
Normal file
2
app/modules/connections/save.php
Normal file
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
// Verbindung anlegen oder ändern
|
||||
2
app/modules/device_types/edit.php
Normal file
2
app/modules/device_types/edit.php
Normal file
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
// Gerätetyp anlegen oder bearbeiten (Formular)
|
||||
2
app/modules/device_types/list.php
Normal file
2
app/modules/device_types/list.php
Normal file
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
// Liste aller Gerätetypen anzeigen
|
||||
2
app/modules/device_types/ports.php
Normal file
2
app/modules/device_types/ports.php
Normal file
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
// SVG-Port-Editor für einen Gerätetyp
|
||||
2
app/modules/device_types/save.php
Normal file
2
app/modules/device_types/save.php
Normal file
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
// Gerätetyp speichern (INSERT / UPDATE)
|
||||
2
app/modules/devices/edit.php
Normal file
2
app/modules/devices/edit.php
Normal file
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
// Gerät anlegen oder bearbeiten
|
||||
2
app/modules/devices/list.php
Normal file
2
app/modules/devices/list.php
Normal file
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
// Liste aller Geräte anzeigen
|
||||
2
app/modules/devices/save.php
Normal file
2
app/modules/devices/save.php
Normal file
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
// Gerät speichern (Rack-Position, Typ, Name)
|
||||
2
app/modules/floors/edit.php
Normal file
2
app/modules/floors/edit.php
Normal file
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
// Stockwerk bearbeiten inkl. SVG-Plan
|
||||
2
app/modules/floors/list.php
Normal file
2
app/modules/floors/list.php
Normal file
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
// Übersicht aller Stockwerke
|
||||
2
app/modules/racks/edit.php
Normal file
2
app/modules/racks/edit.php
Normal file
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
// Rack anlegen oder bearbeiten
|
||||
2
app/modules/racks/list.php
Normal file
2
app/modules/racks/list.php
Normal file
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
// Übersicht aller Racks
|
||||
19
app/templates/footer.php
Normal file
19
app/templates/footer.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
/**
|
||||
* footer.php
|
||||
*
|
||||
* HTML-Footer, Scripts, evtl. Modale oder Notifications
|
||||
* Wird am Ende jeder Seite eingebunden
|
||||
*/
|
||||
?>
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<p>© <?php echo date('Y'); ?> Meine Firma - Netzwerk Dokumentation</p>
|
||||
|
||||
<!-- TODO: Optional: Statusanzeige, Debug-Info, Session-Hinweis -->
|
||||
</footer>
|
||||
|
||||
<!-- TODO: evtl. JS für modale Fenster oder Flash Messages -->
|
||||
</body>
|
||||
</html>
|
||||
33
app/templates/header.php
Normal file
33
app/templates/header.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
/**
|
||||
* header.php
|
||||
*
|
||||
* HTML-Kopf, CSS / JS einbinden, Navigation
|
||||
* Wird am Anfang jeder Seite eingebunden
|
||||
*/
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Netzwerk-Dokumentation</title>
|
||||
|
||||
<!-- CSS -->
|
||||
<link rel="stylesheet" href="/assets/css/app.css">
|
||||
|
||||
<!-- JS -->
|
||||
<script src="/assets/js/app.js" defer></script>
|
||||
<script src="/assets/js/svg-editor.js" defer></script>
|
||||
<script src="/assets/js/network-view.js" defer></script>
|
||||
|
||||
<!-- TODO: Meta-Tags, Favicon -->
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>Netzwerk-Dokumentation</h1>
|
||||
|
||||
<!-- TODO: Navigation einfügen -->
|
||||
<!-- Beispiel: Links zu Dashboard, Gerätetypen, Geräte, Racks, Floors, Connections -->
|
||||
</header>
|
||||
|
||||
<main>
|
||||
28
app/templates/layout.php
Normal file
28
app/templates/layout.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
/**
|
||||
* layout.php
|
||||
*
|
||||
* Grundlayout: Header + Content + Footer
|
||||
* Kann als Basis-Template dienen, falls Module HTML ausgeben
|
||||
*
|
||||
* Beispiel-Aufruf in Modul:
|
||||
* include __DIR__ . '/../templates/layout.php';
|
||||
*
|
||||
* TODO: In Zukunft: zentrales Template-System (z.B. mit $content)
|
||||
*/
|
||||
?>
|
||||
|
||||
<?php include __DIR__ . '/header.php'; ?>
|
||||
|
||||
<div class="content-wrapper">
|
||||
<!-- TODO: Dynamischen Content hier einfügen -->
|
||||
<?php
|
||||
if (isset($content)) {
|
||||
echo $content;
|
||||
} else {
|
||||
echo "<p>Inhalt fehlt</p>";
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
|
||||
<?php include __DIR__ . '/footer.php'; ?>
|
||||
1
app/uploads/device_types/.keep
Normal file
1
app/uploads/device_types/.keep
Normal file
@@ -0,0 +1 @@
|
||||
# Upload-Verzeichnis für Gerätebilder
|
||||
1
app/uploads/floorplans/.keep
Normal file
1
app/uploads/floorplans/.keep
Normal file
@@ -0,0 +1 @@
|
||||
# Upload-Verzeichnis für Stockwerks-SVGs
|
||||
282
doc/DATABASE.md
Normal file
282
doc/DATABASE.md
Normal file
@@ -0,0 +1,282 @@
|
||||
# Datenbankdokumentation
|
||||
|
||||
Dieses Dokument beschreibt das Datenbankschema des
|
||||
netzwerkbasierten Dokumentations- und Verkabelungstools.
|
||||
|
||||
Die Datenbank ist so entworfen, dass sie:
|
||||
- reale Netzwerkinfrastrukturen exakt abbildet
|
||||
- keine Annahmen über Topologien erzwingt
|
||||
- auch exotische Technologien unterstützt
|
||||
- langfristig erweiterbar bleibt
|
||||
|
||||
Die Datenbank ist für **Single-User-Betrieb** ausgelegt.
|
||||
|
||||
---
|
||||
|
||||
## Überblick
|
||||
|
||||
Die Datenbank lässt sich grob in folgende Bereiche gliedern:
|
||||
|
||||
1. Standort- & Gebäude-Struktur
|
||||
2. Racks & physische Infrastruktur
|
||||
3. Gerätetypen & Geräte
|
||||
4. Ports, Module & Verbindungstypen
|
||||
5. Logische & physische Verbindungen
|
||||
|
||||
---
|
||||
|
||||
## 1. Standort- & Gebäude-Struktur
|
||||
|
||||
### `locations`
|
||||
Repräsentiert einen übergeordneten Standort (z. B. Firmencampus, Außenstelle).
|
||||
|
||||
**Verwendung**
|
||||
- Oberste organisatorische Ebene
|
||||
- Kann mehrere Gebäude enthalten
|
||||
|
||||
---
|
||||
|
||||
### `buildings`
|
||||
Ein Gebäude innerhalb eines Standorts.
|
||||
|
||||
**Verwendung**
|
||||
- Gruppiert Stockwerke
|
||||
- Ermöglicht mehrere Gebäude pro Standort
|
||||
|
||||
**Beziehung**
|
||||
- Gehört zu genau einem Standort
|
||||
|
||||
---
|
||||
|
||||
### `floors`
|
||||
Ein Stockwerk innerhalb eines Gebäudes.
|
||||
|
||||
**Verwendung**
|
||||
- Träger für Stockwerkspläne (SVG)
|
||||
- Gruppiert Räume, Racks und Netzwerkdosen
|
||||
|
||||
**Besonderheiten**
|
||||
- `svg_path` verweist auf den grafischen Stockwerksplan
|
||||
|
||||
---
|
||||
|
||||
### `rooms`
|
||||
Ein Raum innerhalb eines Stockwerks.
|
||||
|
||||
**Verwendung**
|
||||
- Dient zur räumlichen Zuordnung von Netzwerkdosen
|
||||
- Kann im SVG-Plan positioniert werden
|
||||
|
||||
**Grafische Attribute**
|
||||
- `x`, `y`, `width`, `height` zur visuellen Darstellung im Stockwerks-SVG
|
||||
|
||||
---
|
||||
|
||||
## 2. Netzwerkdosen
|
||||
|
||||
### `network_outlets`
|
||||
Physische Netzwerkdose innerhalb eines Raums.
|
||||
|
||||
**Verwendung**
|
||||
- Repräsentiert Wand- oder Bodendosen
|
||||
- Kann mehrere Ports besitzen
|
||||
|
||||
**Grafische Attribute**
|
||||
- `x`, `y` zur Platzierung im Stockwerksplan
|
||||
|
||||
---
|
||||
|
||||
### `network_outlet_ports`
|
||||
Einzelne Ports einer Netzwerkdose.
|
||||
|
||||
**Verwendung**
|
||||
- Jeder Port ist ein vollwertiger Verbindungspunkt
|
||||
- Kann direkt mit Geräten, Switches oder Modulen verbunden werden
|
||||
|
||||
---
|
||||
|
||||
## 3. Racks & physische Infrastruktur
|
||||
|
||||
### `racks`
|
||||
Ein Serverschrank oder Netzwerkschrank.
|
||||
|
||||
**Verwendung**
|
||||
- Befindet sich auf einem Stockwerk
|
||||
- Enthält Geräte
|
||||
|
||||
**Wichtige Attribute**
|
||||
- `height_he`: Gesamthöhe des Racks in Höheneinheiten (HE)
|
||||
|
||||
---
|
||||
|
||||
## 4. Gerätetypen & Geräte
|
||||
|
||||
### `device_types`
|
||||
Definiert eine Gerätevorlage.
|
||||
|
||||
**Verwendung**
|
||||
- Bestimmt Aussehen, Portanzahl und Portpositionen
|
||||
- Wird beim Anlegen eines Geräts instanziiert
|
||||
|
||||
**Grafik**
|
||||
- Unterstützt SVG und Bitmap (PNG/JPG)
|
||||
- Grundlage für alle grafischen Ansichten
|
||||
|
||||
---
|
||||
|
||||
### `device_type_ports`
|
||||
Portdefinitionen eines Gerätetyps.
|
||||
|
||||
**Verwendung**
|
||||
- Definiert, wo sich Ports im Bild befinden
|
||||
- Wird beim Erzeugen eines Geräts kopiert
|
||||
|
||||
**Grafische Attribute**
|
||||
- `x`, `y`: relative Position im Gerätebild
|
||||
|
||||
**Metadata**
|
||||
- JSON-Feld für erweiterte Eigenschaften
|
||||
|
||||
---
|
||||
|
||||
### `devices`
|
||||
Konkretes Gerät in der Infrastruktur.
|
||||
|
||||
**Verwendung**
|
||||
- Instanz eines Gerätetyps
|
||||
- Kann in einem Rack platziert werden
|
||||
|
||||
**Rack-Attribute**
|
||||
- `rack_position_he`
|
||||
- `rack_height_he`
|
||||
|
||||
---
|
||||
|
||||
### `device_ports`
|
||||
Ports eines konkreten Geräts.
|
||||
|
||||
**Verwendung**
|
||||
- Entstehen aus `device_type_ports`
|
||||
- Tragen den aktuellen Betriebszustand
|
||||
|
||||
**Technische Attribute**
|
||||
- Status (aktiv / deaktiviert)
|
||||
- VLAN-Konfiguration
|
||||
- Modus (Access, Trunk, Custom)
|
||||
|
||||
---
|
||||
|
||||
## 5. Port- & Verbindungstypen
|
||||
|
||||
### `port_types`
|
||||
Definiert die physische oder logische Art eines Ports.
|
||||
|
||||
**Beispiele**
|
||||
- RJ45
|
||||
- SFP
|
||||
- LC
|
||||
- BNC
|
||||
- Proprietär
|
||||
|
||||
**Zweck**
|
||||
- Einheitliche Typisierung von Ports
|
||||
- Grundlage für Modul-Kompatibilität
|
||||
|
||||
---
|
||||
|
||||
### `connection_types`
|
||||
Definiert die Art einer Verbindung.
|
||||
|
||||
**Verwendung**
|
||||
- Bestimmt technische Eigenschaften
|
||||
- Steuert grafische Darstellung
|
||||
|
||||
**Grafische Attribute**
|
||||
- Linienfarbe
|
||||
- Linienart (durchgezogen, gestrichelt, gepunktet)
|
||||
|
||||
---
|
||||
|
||||
## 6. Module (z. B. SFP)
|
||||
|
||||
### `modules`
|
||||
Ein Modul, das in einen Port eingesetzt werden kann.
|
||||
|
||||
**Verwendung**
|
||||
- SFP, SFP+, QSFP, Medienkonverter, Spezialkarten
|
||||
- Kann selbst Ports besitzen
|
||||
|
||||
---
|
||||
|
||||
### `module_ports`
|
||||
Ports eines Moduls.
|
||||
|
||||
**Verwendung**
|
||||
- Verbindungspunkt nach außen
|
||||
- Typischerweise Glasfaser- oder Spezialports
|
||||
|
||||
---
|
||||
|
||||
### `device_port_modules`
|
||||
Verknüpfung zwischen Geräteport und eingesetztem Modul.
|
||||
|
||||
**Verwendung**
|
||||
- Erlaubt modulare Portstrukturen
|
||||
- Unterstützt komplexe Hardware-Topologien
|
||||
|
||||
---
|
||||
|
||||
## 7. VLANs
|
||||
|
||||
### `vlans`
|
||||
Definiert VLANs unabhängig von Ports.
|
||||
|
||||
**Verwendung**
|
||||
- Wiederverwendbare VLAN-Definitionen
|
||||
- Referenz in Port- und Verbindungs-Konfigurationen
|
||||
|
||||
---
|
||||
|
||||
## 8. Verbindungen
|
||||
|
||||
### `connections`
|
||||
Zentrale Tabelle für alle Verbindungen.
|
||||
|
||||
**Verwendung**
|
||||
- Verbindet beliebige Ports miteinander
|
||||
- Unterstützt:
|
||||
- Geräteports
|
||||
- Modulports
|
||||
- Dosenports
|
||||
|
||||
**Designentscheidung**
|
||||
- Ports werden polymorph referenziert (`port_type`, `port_id`)
|
||||
- Ermöglicht maximale Flexibilität ohne Schemaänderungen
|
||||
|
||||
**Zusatzinformationen**
|
||||
- VLAN-Konfiguration
|
||||
- Betriebsmodus
|
||||
- Freitext-Kommentare
|
||||
|
||||
---
|
||||
|
||||
## Designphilosophie
|
||||
|
||||
- **Grafik ist Teil des Modells**
|
||||
- **Ports sind universelle Verbindungspunkte**
|
||||
- **Keine Einschränkung auf Ethernet**
|
||||
- **Keine Annahmen über Netzwerk-Topologie**
|
||||
- **Erweiterbarkeit vor Perfektion**
|
||||
|
||||
---
|
||||
|
||||
## Hinweis zur Erweiterung
|
||||
|
||||
Das Schema ist vorbereitet für:
|
||||
- Mehrbenutzerbetrieb
|
||||
- Historisierung
|
||||
- API-Nutzung
|
||||
- Exporte
|
||||
- Design-Themes
|
||||
|
||||
Diese Funktionen sind bewusst **nicht Bestandteil der ersten Version**.
|
||||
11
doc/DATEISTRUKTUR.md
Normal file
11
doc/DATEISTRUKTUR.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# Dateistruktur
|
||||
|
||||
```php
|
||||
<?php
|
||||
ob_start();
|
||||
// HTML / Modul-Logik
|
||||
echo "<h2>Gerätetypen</h2>";
|
||||
$content = ob_get_clean();
|
||||
|
||||
include __DIR__ . '/../templates/layout.php';
|
||||
```
|
||||
223
doc/UI-KONZEPT.md
Normal file
223
doc/UI-KONZEPT.md
Normal file
@@ -0,0 +1,223 @@
|
||||
# UI-/UX-Konzept: SVG-Port-Editor
|
||||
|
||||
Der SVG-Port-Editor dient zur Definition von Ports auf Gerätetypen
|
||||
(Switches, Patchpanels, Server, Module etc.).
|
||||
|
||||
Er ist ein **technischer Editor**, kein Grafiktool, und verfolgt folgende Ziele:
|
||||
- präzise Port-Positionierung
|
||||
- schnelle Bedienbarkeit
|
||||
- Fehlertoleranz
|
||||
- spätere Design-Erweiterbarkeit
|
||||
|
||||
---
|
||||
|
||||
## Zielgruppe
|
||||
|
||||
- Technische Administratoren
|
||||
- Netzwerkplaner
|
||||
- Dokumentationsverantwortliche
|
||||
|
||||
Keine Design-Kenntnisse notwendig.
|
||||
|
||||
---
|
||||
|
||||
## Grundidee
|
||||
|
||||
Der Editor arbeitet nach dem Prinzip:
|
||||
|
||||
> **Bild hochladen → Ports klicken → Ports konfigurieren**
|
||||
|
||||
Ports werden als **logische Punkte** auf einer Gerätegrafik definiert und
|
||||
später in allen Ansichten (Rack, Netzwerk, Verbindungen) wiederverwendet.
|
||||
|
||||
---
|
||||
|
||||
## Seitenaufbau
|
||||
|
||||
### Layout (Desktop)
|
||||
|
||||
```
|
||||
+------------------------------------------------------+
|
||||
| Header: Gerätetyp bearbeiten |
|
||||
+----------------------+-------------------------------+
|
||||
| Sidebar (Ports) | Hauptbereich (SVG / Bild) |
|
||||
| | |
|
||||
| + Port hinzufügen | [ Gerätebild ] |
|
||||
| | o o o o |
|
||||
| Portliste | |
|
||||
| - Port 1 | |
|
||||
| - Port 2 | |
|
||||
| - Port 3 | |
|
||||
| | |
|
||||
+----------------------+-------------------------------+
|
||||
| Footer: Speichern / Abbrechen |
|
||||
+------------------------------------------------------+
|
||||
```
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Schritt 1: Gerätetyp anlegen
|
||||
|
||||
### Pflichtangaben
|
||||
- Name
|
||||
- Kategorie (Switch, Server, Patchpanel, Sonstiges)
|
||||
- Bild (SVG oder PNG/JPG)
|
||||
|
||||
Nach dem Speichern wird automatisch der **Port-Editor** geöffnet.
|
||||
|
||||
---
|
||||
|
||||
## Schritt 2: Port-Editor – Interaktion
|
||||
|
||||
### Port hinzufügen
|
||||
|
||||
**Interaktion**
|
||||
- Klick auf das Bild
|
||||
- An der Klickposition erscheint ein neuer Portpunkt
|
||||
|
||||
**Visuelle Darstellung**
|
||||
- Kreis (Standard: grau)
|
||||
- Nummer / Name daneben
|
||||
|
||||
---
|
||||
|
||||
### Port auswählen
|
||||
|
||||
- Klick auf bestehenden Port
|
||||
- Port wird hervorgehoben
|
||||
- Sidebar zeigt Port-Eigenschaften
|
||||
|
||||
---
|
||||
|
||||
### Port verschieben
|
||||
|
||||
- Drag & Drop
|
||||
- Snap optional (Raster später erweiterbar)
|
||||
- Position wird relativ zum Bild gespeichert
|
||||
|
||||
---
|
||||
|
||||
### Port löschen
|
||||
|
||||
- Button in Sidebar
|
||||
- Sicherheitsabfrage
|
||||
|
||||
---
|
||||
|
||||
## Sidebar: Port-Eigenschaften
|
||||
|
||||
### Basisfelder
|
||||
- Portname (z. B. `Gi1/0/1`, `Port 1`)
|
||||
- Porttyp (Dropdown, frei definierbar)
|
||||
- Kommentar (optional)
|
||||
|
||||
### Erweiterte Eigenschaften (optional)
|
||||
- Standard-Geschwindigkeit
|
||||
- Default-Modus
|
||||
- Metadata (JSON, optional, für Sonderfälle)
|
||||
|
||||
---
|
||||
|
||||
## Visuelle Zustände von Ports
|
||||
|
||||
| Zustand | Darstellung |
|
||||
|------------------|---------------------------------|
|
||||
| Normal | Grauer Kreis |
|
||||
| Hover | Hervorgehoben |
|
||||
| Aktiv / selektiert | Farblich markiert |
|
||||
| Fehlerhaft | Rot / Warnsymbol |
|
||||
|
||||
---
|
||||
|
||||
## Koordinatensystem
|
||||
|
||||
- Ports speichern **relative Koordinaten**
|
||||
- Prozentual oder ViewBox-basiert
|
||||
- Unabhängig von Bildauflösung
|
||||
- Garantiert korrekte Skalierung:
|
||||
- Rack-Ansicht
|
||||
- Netzwerkansicht
|
||||
- Zoom
|
||||
|
||||
---
|
||||
|
||||
## SVG vs. Bitmap
|
||||
|
||||
### SVG
|
||||
- Ports werden als eigene SVG-Layer dargestellt
|
||||
- Exakte Skalierung
|
||||
- Ideal für professionelle Gerätebilder
|
||||
|
||||
### Bitmap (PNG/JPG)
|
||||
- Ports als Overlay-Layer
|
||||
- Relative Positionierung
|
||||
- Einfacher Einstieg
|
||||
|
||||
Beide Varianten nutzen **identische Logik**.
|
||||
|
||||
---
|
||||
|
||||
## Validierungen
|
||||
|
||||
Beim Speichern wird geprüft:
|
||||
- Portnamen eindeutig innerhalb des Gerätetyps
|
||||
- Porttyp gesetzt
|
||||
- Position innerhalb des Bildbereichs
|
||||
|
||||
Warnungen statt harter Fehler, wo sinnvoll.
|
||||
|
||||
---
|
||||
|
||||
## UX-Details
|
||||
|
||||
### Undo (optional, später)
|
||||
- Letzte Aktion rückgängig machen
|
||||
|
||||
### Keyboard-Shortcuts (optional)
|
||||
- `DEL`: Port löschen
|
||||
- `ESC`: Auswahl aufheben
|
||||
|
||||
---
|
||||
|
||||
## Speichern
|
||||
|
||||
- Autosave optional
|
||||
- Manuelles Speichern immer möglich
|
||||
- Nach Speichern:
|
||||
- Ports stehen für neue Geräte zur Verfügung
|
||||
- Bestehende Geräte bleiben unverändert (keine Retro-Änderung)
|
||||
|
||||
---
|
||||
|
||||
## Abgrenzung: Gerät vs. Gerätetyp
|
||||
|
||||
Wichtiges UX-Prinzip:
|
||||
- **Port-Editor arbeitet ausschließlich auf Gerätetypen**
|
||||
- Konkrete Geräte:
|
||||
- übernehmen die Portstruktur
|
||||
- können Ports nicht verschieben
|
||||
- nur konfigurieren (VLAN, Status etc.)
|
||||
|
||||
---
|
||||
|
||||
## Erweiterungen (nicht initial)
|
||||
|
||||
- Raster / Magnetlinien
|
||||
- Port-Gruppierung (z. B. Portblöcke)
|
||||
- Automatische Nummerierung
|
||||
- Copy/Paste von Ports
|
||||
- Zoom & Pan
|
||||
- Touch-Unterstützung
|
||||
|
||||
---
|
||||
|
||||
## Zielzustand
|
||||
|
||||
Der SVG-Port-Editor soll:
|
||||
- technisch korrekt
|
||||
- schnell erlernbar
|
||||
- präzise
|
||||
- robust gegen Sonderfälle
|
||||
|
||||
sein – ohne den Nutzer mit Design- oder Grafikdetails zu belasten.
|
||||
40
docker-compose.yml
Normal file
40
docker-compose.yml
Normal file
@@ -0,0 +1,40 @@
|
||||
services:
|
||||
web:
|
||||
image: php:8.3-apache
|
||||
container_name: netdoc_web
|
||||
ports:
|
||||
- "80:80"
|
||||
volumes:
|
||||
- ./app:/var/www/html
|
||||
depends_on:
|
||||
- db
|
||||
restart: "no"
|
||||
|
||||
db:
|
||||
image: mariadb:11
|
||||
container_name: netdoc_db
|
||||
environment:
|
||||
MARIADB_ROOT_PASSWORD: root
|
||||
MARIADB_DATABASE: netdoc
|
||||
MARIADB_USER: netdoc
|
||||
MARIADB_PASSWORD: netdoc
|
||||
volumes:
|
||||
- db_data:/var/lib/mysql
|
||||
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
|
||||
restart: "no"
|
||||
|
||||
phpmyadmin:
|
||||
image: phpmyadmin/phpmyadmin
|
||||
container_name: netdoc_phpmyadmin
|
||||
ports:
|
||||
- "8080:80"
|
||||
environment:
|
||||
PMA_HOST: db
|
||||
PMA_USER: netdoc
|
||||
PMA_PASSWORD: netdoc
|
||||
depends_on:
|
||||
- db
|
||||
restart: "no"
|
||||
|
||||
volumes:
|
||||
db_data:
|
||||
185
init.sql
Normal file
185
init.sql
Normal file
@@ -0,0 +1,185 @@
|
||||
CREATE TABLE locations (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
comment TEXT
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
CREATE TABLE buildings (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
location_id INT NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
comment TEXT,
|
||||
FOREIGN KEY (location_id) REFERENCES locations(id)
|
||||
ON DELETE CASCADE
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
CREATE TABLE floors (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
building_id INT NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
level INT,
|
||||
svg_path VARCHAR(255),
|
||||
comment TEXT,
|
||||
FOREIGN KEY (building_id) REFERENCES buildings(id)
|
||||
ON DELETE CASCADE
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
CREATE TABLE rooms (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
floor_id INT NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
number VARCHAR(50),
|
||||
x INT,
|
||||
y INT,
|
||||
width INT,
|
||||
height INT,
|
||||
comment TEXT,
|
||||
FOREIGN KEY (floor_id) REFERENCES floors(id)
|
||||
ON DELETE CASCADE
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
CREATE TABLE network_outlets (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
room_id INT NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
x INT,
|
||||
y INT,
|
||||
comment TEXT,
|
||||
FOREIGN KEY (room_id) REFERENCES rooms(id)
|
||||
ON DELETE CASCADE
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
CREATE TABLE network_outlet_ports (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
outlet_id INT NOT NULL,
|
||||
name VARCHAR(50) NOT NULL,
|
||||
port_type_id INT,
|
||||
FOREIGN KEY (outlet_id) REFERENCES network_outlets(id)
|
||||
ON DELETE CASCADE
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
CREATE TABLE racks (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
floor_id INT NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
height_he INT NOT NULL,
|
||||
comment TEXT,
|
||||
FOREIGN KEY (floor_id) REFERENCES floors(id)
|
||||
ON DELETE CASCADE
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
CREATE TABLE device_types (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
category ENUM('switch','server','patchpanel','other') NOT NULL,
|
||||
image_path VARCHAR(255),
|
||||
image_type ENUM('svg','bitmap') NOT NULL,
|
||||
comment TEXT
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
CREATE TABLE device_type_ports (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
device_type_id INT NOT NULL,
|
||||
name VARCHAR(50) NOT NULL,
|
||||
port_type_id INT,
|
||||
x INT NOT NULL,
|
||||
y INT NOT NULL,
|
||||
metadata JSON,
|
||||
FOREIGN KEY (device_type_id) REFERENCES device_types(id)
|
||||
ON DELETE CASCADE
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
CREATE TABLE devices (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
device_type_id INT NOT NULL,
|
||||
rack_id INT,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
rack_position_he INT,
|
||||
rack_height_he INT,
|
||||
serial_number VARCHAR(255),
|
||||
comment TEXT,
|
||||
FOREIGN KEY (device_type_id) REFERENCES device_types(id),
|
||||
FOREIGN KEY (rack_id) REFERENCES racks(id)
|
||||
ON DELETE SET NULL
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
CREATE TABLE device_ports (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
device_id INT NOT NULL,
|
||||
name VARCHAR(50) NOT NULL,
|
||||
port_type_id INT,
|
||||
status ENUM('active','disabled') DEFAULT 'active',
|
||||
mode VARCHAR(50),
|
||||
vlan_config JSON,
|
||||
FOREIGN KEY (device_id) REFERENCES devices(id)
|
||||
ON DELETE CASCADE
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
CREATE TABLE port_types (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
name VARCHAR(100) NOT NULL,
|
||||
medium ENUM('copper','fiber','coax','other') NOT NULL,
|
||||
comment TEXT
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
CREATE TABLE connection_types (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
medium ENUM('copper','fiber','coax','other') NOT NULL,
|
||||
duplex ENUM('half','full','custom') DEFAULT 'custom',
|
||||
max_speed VARCHAR(50),
|
||||
color VARCHAR(20),
|
||||
line_style ENUM('solid','dashed','dotted') DEFAULT 'solid',
|
||||
comment TEXT
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
CREATE TABLE modules (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
module_type VARCHAR(100),
|
||||
comment TEXT
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
CREATE TABLE module_ports (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
module_id INT NOT NULL,
|
||||
name VARCHAR(50) NOT NULL,
|
||||
port_type_id INT,
|
||||
FOREIGN KEY (module_id) REFERENCES modules(id)
|
||||
ON DELETE CASCADE
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
CREATE TABLE device_port_modules (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
device_port_id INT NOT NULL,
|
||||
module_id INT NOT NULL,
|
||||
FOREIGN KEY (device_port_id) REFERENCES device_ports(id)
|
||||
ON DELETE CASCADE,
|
||||
FOREIGN KEY (module_id) REFERENCES modules(id)
|
||||
ON DELETE CASCADE
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
CREATE TABLE vlans (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
vlan_id INT NOT NULL,
|
||||
name VARCHAR(255),
|
||||
comment TEXT
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
CREATE TABLE connections (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
connection_type_id INT NOT NULL,
|
||||
|
||||
port_a_type ENUM('device','module','outlet') NOT NULL,
|
||||
port_a_id INT NOT NULL,
|
||||
|
||||
port_b_type ENUM('device','module','outlet') NOT NULL,
|
||||
port_b_id INT NOT NULL,
|
||||
|
||||
vlan_config JSON,
|
||||
mode VARCHAR(50),
|
||||
comment TEXT,
|
||||
|
||||
FOREIGN KEY (connection_type_id) REFERENCES connection_types(id)
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
Reference in New Issue
Block a user