Add IP field, ping service, status endpoint and UI; update README

This commit is contained in:
2025-08-21 14:37:07 +02:00
parent 4a56cbd310
commit 504ca23442
8 changed files with 248 additions and 28 deletions

View File

@ -6,6 +6,8 @@ Ein moderner Wake-on-LAN Manager, entwickelt mit Go und einer schönen Web-Oberf
- **PC-Verwaltung**: Hinzufügen, Anzeigen, Bearbeiten und Löschen von PC-Einträgen - **PC-Verwaltung**: Hinzufügen, Anzeigen, Bearbeiten und Löschen von PC-Einträgen
- **Wake-on-LAN**: Ein-Klick-Aufwecken von Computern über MAC-Adressen - **Wake-on-LAN**: Ein-Klick-Aufwecken von Computern über MAC-Adressen
- **IP-Adressverwaltung**: Pro Gerät wird eine IP-Adresse gespeichert
- **Online-Status (Ping)**: Geräte können per Ping geprüft und im UI als Online/Offline angezeigt werden
- **Moderne Web-Oberfläche**: Responsive Design mit Bootstrap und FontAwesome - **Moderne Web-Oberfläche**: Responsive Design mit Bootstrap und FontAwesome
- **SQLite-Datenbank**: Einfache lokale Datenspeicherung - **SQLite-Datenbank**: Einfache lokale Datenspeicherung
- **Cross-Platform**: Läuft auf Windows und Linux - **Cross-Platform**: Läuft auf Windows und Linux
@ -91,17 +93,23 @@ Falls weder Kommandozeilenparameter noch Umgebungsvariable gesetzt sind, wird Po
### PC hinzufügen ### PC hinzufügen
1. Geben Sie den Namen des PCs ein 1. Geben Sie den Namen des PCs ein
2. Geben Sie die MAC-Adresse im Format `XX:XX:XX:XX:XX:XX` ein 2. Geben Sie die MAC-Adresse im Format `XX:XX:XX:XX:XX:XX` ein
3. Klicken Sie auf "PC hinzufügen" 3. Geben Sie die IP-Adresse des PCs ein (z. B. `192.168.0.10`)
4. Klicken Sie auf "PC hinzufügen"
### PC bearbeiten ### PC bearbeiten
1. Klicken Sie auf den "Bearbeiten"-Button neben dem gewünschten PC 1. Klicken Sie auf den "Bearbeiten"-Button neben dem gewünschten PC
2. Ändern Sie Name und/oder MAC-Adresse 2. Ändern Sie Name, MAC-Adresse und/oder IP-Adresse
3. Klicken Sie auf "Speichern" 3. Klicken Sie auf "Speichern"
### PC aufwecken ### PC aufwecken
- Klicken Sie auf den "Aufwecken"-Button neben dem gewünschten PC - Klicken Sie auf den "Aufwecken"-Button neben dem gewünschten PC
- Das System sendet automatisch ein Wake-on-LAN Paket - Das System sendet automatisch ein Wake-on-LAN Paket
### Online-Status prüfen (Ping)
- Klicken Sie auf den Button "Status aktualisieren" in der Tabelle
- Die Online/Offline-Badges werden pro Gerät aktualisiert
- Optional kann ein automatisches Intervall ergänzt werden (siehe Entwicklung)
### PC löschen ### PC löschen
- Klicken Sie auf den "Löschen"-Button neben dem gewünschten PC - Klicken Sie auf den "Löschen"-Button neben dem gewünschten PC
- Bestätigen Sie die Löschung - Bestätigen Sie die Löschung
@ -261,11 +269,22 @@ medi-wol/
- `PUT /api/pcs/:id` - PC aktualisieren - `PUT /api/pcs/:id` - PC aktualisieren
- `DELETE /api/pcs/:id` - PC löschen - `DELETE /api/pcs/:id` - PC löschen
- `POST /api/pcs/:id/wake` - PC aufwecken - `POST /api/pcs/:id/wake` - PC aufwecken
- `GET /api/pcs/status` - Online-Status aller PCs abrufen (Ping)
## Datenbank ## Datenbank
Die Anwendung verwendet SQLite als lokale Datenbank. Die Datenbankdatei `medi-wol.db` wird automatisch im Projektverzeichnis erstellt. Die Anwendung verwendet SQLite als lokale Datenbank. Die Datenbankdatei `medi-wol.db` wird automatisch im Projektverzeichnis erstellt.
### Tabellenstruktur `pcs`
| Spalte | Typ | Hinweis |
|-------------|----------|------------------------|
| id | INTEGER | Primärschlüssel |
| name | TEXT | Pflichtfeld |
| mac | TEXT | Pflichtfeld, eindeutig |
| ip | TEXT | Pflichtfeld |
| created_at | DATETIME | Automatisch |
| updated_at | DATETIME | Automatisch |
## Wake-on-LAN ## Wake-on-LAN
Das System sendet Magic Packets an die gespeicherten MAC-Adressen. Stellen Sie sicher, dass: Das System sendet Magic Packets an die gespeicherten MAC-Adressen. Stellen Sie sicher, dass:
@ -285,6 +304,12 @@ go run cmd/server/main.go
go run cmd/server/main.go -port 9090 go run cmd/server/main.go -port 9090
``` ```
#### Hinweise zum Ping
- Der Ping wird aktuell über den Button "Status aktualisieren" ausgelöst (`GET /api/pcs/status`).
- Unter Windows wird der Systembefehl `ping -n 1 -w 1000 <IP>` verwendet, unter Linux/macOS `ping -c 1 -W 1 <IP>`.
- Falls ICMP blockiert ist, wird als Fallback eine kurze TCP-Portprobe versucht (80, sonst 22).
- Optional kann ein automatisches Intervall im Frontend ergänzt werden (z. B. alle 30s).
### Build für Produktion ### Build für Produktion
```bash ```bash
# Windows # Windows

View File

@ -65,6 +65,7 @@ func main() {
r.PUT("/api/pcs/:id", pcHandler.UpdatePC) r.PUT("/api/pcs/:id", pcHandler.UpdatePC)
r.DELETE("/api/pcs/:id", pcHandler.DeletePC) r.DELETE("/api/pcs/:id", pcHandler.DeletePC)
r.POST("/api/pcs/:id/wake", pcHandler.WakePC) r.POST("/api/pcs/:id/wake", pcHandler.WakePC)
r.GET("/api/pcs/status", pcHandler.GetPCStatus)
// Server starten // Server starten
serverAddr := fmt.Sprintf(":%d", port) serverAddr := fmt.Sprintf(":%d", port)

View File

@ -27,6 +27,7 @@ func InitDB() (*DB, error) {
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL, name TEXT NOT NULL,
mac TEXT NOT NULL UNIQUE, mac TEXT NOT NULL UNIQUE,
ip TEXT NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP, created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);` );`
@ -36,13 +37,19 @@ func InitDB() (*DB, error) {
return nil, err return nil, err
} }
// Füge IP-Spalte hinzu, falls sie nicht existiert
_, err = db.Exec("ALTER TABLE pcs ADD COLUMN ip TEXT DEFAULT ''")
if err != nil {
// Spalte existiert bereits, ignorieren
}
log.Println("Datenbank erfolgreich initialisiert") log.Println("Datenbank erfolgreich initialisiert")
return &DB{db}, nil return &DB{db}, nil
} }
// GetAllPCs holt alle PCs aus der Datenbank // GetAllPCs holt alle PCs aus der Datenbank
func (db *DB) GetAllPCs() ([]models.PC, error) { func (db *DB) GetAllPCs() ([]models.PC, error) {
rows, err := db.Query("SELECT id, name, mac, created_at, updated_at FROM pcs ORDER BY name") rows, err := db.Query("SELECT id, name, mac, ip, created_at, updated_at FROM pcs ORDER BY name")
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -51,7 +58,7 @@ func (db *DB) GetAllPCs() ([]models.PC, error) {
var pcs []models.PC var pcs []models.PC
for rows.Next() { for rows.Next() {
var pc models.PC var pc models.PC
err := rows.Scan(&pc.ID, &pc.Name, &pc.MAC, &pc.CreatedAt, &pc.UpdatedAt) err := rows.Scan(&pc.ID, &pc.Name, &pc.MAC, &pc.IP, &pc.CreatedAt, &pc.UpdatedAt)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -62,11 +69,11 @@ func (db *DB) GetAllPCs() ([]models.PC, error) {
} }
// CreatePC erstellt einen neuen PC-Eintrag // CreatePC erstellt einen neuen PC-Eintrag
func (db *DB) CreatePC(name, mac string) (*models.PC, error) { func (db *DB) CreatePC(name, mac, ip string) (*models.PC, error) {
now := time.Now() now := time.Now()
result, err := db.Exec( result, err := db.Exec(
"INSERT INTO pcs (name, mac, created_at, updated_at) VALUES (?, ?, ?, ?)", "INSERT INTO pcs (name, mac, ip, created_at, updated_at) VALUES (?, ?, ?, ?, ?)",
name, mac, now, now, name, mac, ip, now, now,
) )
if err != nil { if err != nil {
return nil, err return nil, err
@ -81,17 +88,18 @@ func (db *DB) CreatePC(name, mac string) (*models.PC, error) {
ID: int(id), ID: int(id),
Name: name, Name: name,
MAC: mac, MAC: mac,
IP: ip,
CreatedAt: now, CreatedAt: now,
UpdatedAt: now, UpdatedAt: now,
}, nil }, nil
} }
// UpdatePC aktualisiert einen bestehenden PC-Eintrag // UpdatePC aktualisiert einen bestehenden PC-Eintrag
func (db *DB) UpdatePC(id int, name, mac string) (*models.PC, error) { func (db *DB) UpdatePC(id int, name, mac, ip string) (*models.PC, error) {
now := time.Now() now := time.Now()
_, err := db.Exec( _, err := db.Exec(
"UPDATE pcs SET name = ?, mac = ?, updated_at = ? WHERE id = ?", "UPDATE pcs SET name = ?, mac = ?, ip = ?, updated_at = ? WHERE id = ?",
name, mac, now, id, name, mac, ip, now, id,
) )
if err != nil { if err != nil {
return nil, err return nil, err
@ -101,6 +109,7 @@ func (db *DB) UpdatePC(id int, name, mac string) (*models.PC, error) {
ID: id, ID: id,
Name: name, Name: name,
MAC: mac, MAC: mac,
IP: ip,
UpdatedAt: now, UpdatedAt: now,
}, nil }, nil
} }
@ -115,9 +124,9 @@ func (db *DB) DeletePC(id int) error {
func (db *DB) GetPCByID(id int) (*models.PC, error) { func (db *DB) GetPCByID(id int) (*models.PC, error) {
var pc models.PC var pc models.PC
err := db.QueryRow( err := db.QueryRow(
"SELECT id, name, mac, created_at, updated_at FROM pcs WHERE id = ?", "SELECT id, name, mac, ip, created_at, updated_at FROM pcs WHERE id = ?",
id, id,
).Scan(&pc.ID, &pc.Name, &pc.MAC, &pc.CreatedAt, &pc.UpdatedAt) ).Scan(&pc.ID, &pc.Name, &pc.MAC, &pc.IP, &pc.CreatedAt, &pc.UpdatedAt)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -3,6 +3,7 @@ package handlers
import ( import (
"medi-wol/internal/database" "medi-wol/internal/database"
"medi-wol/internal/models" "medi-wol/internal/models"
"medi-wol/internal/ping"
"medi-wol/internal/wol" "medi-wol/internal/wol"
"net/http" "net/http"
"strconv" "strconv"
@ -14,6 +15,7 @@ import (
type PCHandler struct { type PCHandler struct {
db *database.DB db *database.DB
wolService *wol.Service wolService *wol.Service
pingService *ping.PingService
} }
// NewPCHandler erstellt einen neuen PC-Handler // NewPCHandler erstellt einen neuen PC-Handler
@ -21,6 +23,7 @@ func NewPCHandler(db *database.DB, wolService *wol.Service) *PCHandler {
return &PCHandler{ return &PCHandler{
db: db, db: db,
wolService: wolService, wolService: wolService,
pingService: ping.NewPingService(),
} }
} }
@ -69,7 +72,7 @@ func (h *PCHandler) CreatePC(c *gin.Context) {
} }
// PC erstellen // PC erstellen
pc, err := h.db.CreatePC(req.Name, req.MAC) pc, err := h.db.CreatePC(req.Name, req.MAC, req.IP)
if err != nil { if err != nil {
c.JSON(http.StatusInternalServerError, models.PCResponse{ c.JSON(http.StatusInternalServerError, models.PCResponse{
Success: false, Success: false,
@ -116,7 +119,7 @@ func (h *PCHandler) UpdatePC(c *gin.Context) {
} }
// PC aktualisieren // PC aktualisieren
pc, err := h.db.UpdatePC(id, req.Name, req.MAC) pc, err := h.db.UpdatePC(id, req.Name, req.MAC, req.IP)
if err != nil { if err != nil {
c.JSON(http.StatusInternalServerError, models.PCResponse{ c.JSON(http.StatusInternalServerError, models.PCResponse{
Success: false, Success: false,
@ -196,3 +199,33 @@ func (h *PCHandler) WakePC(c *gin.Context) {
Message: "Wake-on-LAN Paket erfolgreich gesendet an " + pc.Name, Message: "Wake-on-LAN Paket erfolgreich gesendet an " + pc.Name,
}) })
} }
// GetPCStatus gibt den Online-Status aller PCs zurück
func (h *PCHandler) GetPCStatus(c *gin.Context) {
pcs, err := h.db.GetAllPCs()
if err != nil {
c.JSON(http.StatusInternalServerError, models.PCResponse{
Success: false,
Message: "Fehler beim Laden der PCs: " + err.Error(),
})
return
}
// Online-Status für alle PCs überprüfen
var statusList []models.PCStatus
for _, pc := range pcs {
online := h.pingService.IsOnline(pc.IP)
statusList = append(statusList, models.PCStatus{
ID: pc.ID,
Name: pc.Name,
MAC: pc.MAC,
IP: pc.IP,
Online: online,
})
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"status": statusList,
})
}

View File

@ -9,7 +9,8 @@ type PC struct {
ID int `json:"id" db:"id"` ID int `json:"id" db:"id"`
Name string `json:"name" db:"name"` Name string `json:"name" db:"name"`
MAC string `json:"mac" db:"mac"` MAC string `json:"mac" db:"mac"`
CreatedAt time.Time `json:"created_at" db:"created_at"` IP string `json:"ip" db:"ip"`
CreatedAt time.Time `json:"created_at" db:"updated_at"`
UpdatedAt time.Time `json:"updated_at" db:"updated_at"` UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
} }
@ -17,12 +18,14 @@ type PC struct {
type CreatePCRequest struct { type CreatePCRequest struct {
Name string `json:"name" binding:"required"` Name string `json:"name" binding:"required"`
MAC string `json:"mac" binding:"required"` MAC string `json:"mac" binding:"required"`
IP string `json:"ip" binding:"required"`
} }
// UpdatePCRequest repräsentiert die Anfrage zum Aktualisieren eines PCs // UpdatePCRequest repräsentiert die Anfrage zum Aktualisieren eines PCs
type UpdatePCRequest struct { type UpdatePCRequest struct {
Name string `json:"name" binding:"required"` Name string `json:"name" binding:"required"`
MAC string `json:"mac" binding:"required"` MAC string `json:"mac" binding:"required"`
IP string `json:"ip" binding:"required"`
} }
// PCResponse repräsentiert die Antwort für PC-Operationen // PCResponse repräsentiert die Antwort für PC-Operationen
@ -32,3 +35,12 @@ type PCResponse struct {
PC *PC `json:"pc,omitempty"` PC *PC `json:"pc,omitempty"`
PCs []PC `json:"pcs,omitempty"` PCs []PC `json:"pcs,omitempty"`
} }
// PCStatus repräsentiert den Online-Status eines PCs
type PCStatus struct {
ID int `json:"id"`
Name string `json:"name"`
MAC string `json:"mac"`
IP string `json:"ip"`
Online bool `json:"online"`
}

81
internal/ping/ping.go Normal file
View File

@ -0,0 +1,81 @@
package ping
import (
"fmt"
"net"
"os/exec"
"runtime"
"time"
)
// PingService bietet Funktionen zum Überprüfen des Online-Status von PCs
type PingService struct{}
// NewPingService erstellt eine neue Instanz des Ping-Services
func NewPingService() *PingService {
return &PingService{}
}
// IsOnline überprüft, ob ein PC online ist
func (ps *PingService) IsOnline(ip string) bool {
if ip == "" {
return false
}
// Verwende system-spezifischen Ping-Befehl
switch runtime.GOOS {
case "windows":
return ps.pingWindows(ip)
case "linux", "darwin":
return ps.pingUnix(ip)
default:
return ps.pingGeneric(ip)
}
}
// pingWindows führt einen Ping unter Windows aus
func (ps *PingService) pingWindows(ip string) bool {
cmd := exec.Command("ping", "-n", "1", "-w", "1000", ip)
err := cmd.Run()
return err == nil
}
// pingUnix führt einen Ping unter Unix-Systemen aus
func (ps *PingService) pingUnix(ip string) bool {
cmd := exec.Command("ping", "-c", "1", "-W", "1", ip)
err := cmd.Run()
return err == nil
}
// pingGeneric ist eine plattformunabhängige Alternative
func (ps *PingService) pingGeneric(ip string) bool {
// Versuche TCP-Verbindung auf Port 80 (HTTP)
conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:80", ip), 2*time.Second)
if err != nil {
// Versuche TCP-Verbindung auf Port 22 (SSH)
conn, err = net.DialTimeout("tcp", fmt.Sprintf("%s:22", ip), 2*time.Second)
if err != nil {
return false
}
}
defer conn.Close()
return true
}
// CheckAllPCs überprüft den Online-Status aller PCs
func (ps *PingService) CheckAllPCs(pcs []interface{}) map[int]bool {
results := make(map[int]bool)
for _, pc := range pcs {
// Type assertion für PC-Interface
if pcMap, ok := pc.(map[string]interface{}); ok {
if id, ok := pcMap["id"].(float64); ok {
if ip, ok := pcMap["ip"].(string); ok {
results[int(id)] = ps.IsOnline(ip)
}
}
}
}
return results
}

View File

@ -39,6 +39,11 @@ class PCManager {
document.getElementById('clearSearchBtn').addEventListener('click', () => { document.getElementById('clearSearchBtn').addEventListener('click', () => {
this.clearSearch(); this.clearSearch();
}); });
// Status aktualisieren Button
document.getElementById('refreshStatusBtn').addEventListener('click', () => {
this.refreshStatus();
});
} }
async loadPCs() { async loadPCs() {
@ -129,6 +134,13 @@ class PCManager {
<tr> <tr>
<td><strong>${this.escapeHtml(pc.name)}</strong></td> <td><strong>${this.escapeHtml(pc.name)}</strong></td>
<td><code>${this.escapeHtml(pc.mac)}</code></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>${new Date(pc.created_at).toLocaleDateString('de-DE')}</td>
<td> <td>
<div class="btn-group" role="group"> <div class="btn-group" role="group">
@ -136,7 +148,7 @@ class PCManager {
title="PC aufwecken"> title="PC aufwecken">
<i class="fas fa-power-off"></i> Aufwecken <i class="fas fa-power-off"></i> Aufwecken
</button> </button>
<button class="btn btn-warning btn-sm" onclick="pcManager.editPC(${pc.id}, '${this.escapeHtml(pc.name)}', '${this.escapeHtml(pc.mac)}')" <button class="btn btn-warning btn-sm" onclick="pcManager.editPC(${pc.id}, '${this.escapeHtml(pc.name)}', '${this.escapeHtml(pc.mac)}', '${this.escapeHtml(pc.ip || '')}')"
title="PC bearbeiten"> title="PC bearbeiten">
<i class="fas fa-edit"></i> Bearbeiten <i class="fas fa-edit"></i> Bearbeiten
</button> </button>
@ -153,8 +165,9 @@ class PCManager {
async addPC() { async addPC() {
const name = document.getElementById('pcName').value.trim(); const name = document.getElementById('pcName').value.trim();
const mac = document.getElementById('macAddress').value.trim(); const mac = document.getElementById('macAddress').value.trim();
const ip = document.getElementById('ipAddress').value.trim();
if (!name || !mac) { if (!name || !mac || !ip) {
this.showNotification('Warnung', 'Bitte füllen Sie alle Felder aus', 'warning'); this.showNotification('Warnung', 'Bitte füllen Sie alle Felder aus', 'warning');
return; return;
} }
@ -165,7 +178,7 @@ class PCManager {
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
}, },
body: JSON.stringify({ name, mac }) body: JSON.stringify({ name, mac, ip })
}); });
const data = await response.json(); const data = await response.json();
@ -182,11 +195,12 @@ class PCManager {
} }
} }
editPC(id, name, mac) { editPC(id, name, mac, ip) {
// Modal mit PC-Daten füllen // Modal mit PC-Daten füllen
document.getElementById('editPCId').value = id; document.getElementById('editPCId').value = id;
document.getElementById('editPCName').value = name; document.getElementById('editPCName').value = name;
document.getElementById('editMACAddress').value = mac; document.getElementById('editMACAddress').value = mac;
document.getElementById('editIPAddress').value = ip;
// Modal öffnen // Modal öffnen
const editModal = new bootstrap.Modal(document.getElementById('editPCModal')); const editModal = new bootstrap.Modal(document.getElementById('editPCModal'));
@ -197,8 +211,9 @@ class PCManager {
const id = document.getElementById('editPCId').value; const id = document.getElementById('editPCId').value;
const name = document.getElementById('editPCName').value.trim(); const name = document.getElementById('editPCName').value.trim();
const mac = document.getElementById('editMACAddress').value.trim(); const mac = document.getElementById('editMACAddress').value.trim();
const ip = document.getElementById('editIPAddress').value.trim();
if (!name || !mac) { if (!name || !mac || !ip) {
this.showNotification('Warnung', 'Bitte füllen Sie alle Felder aus', 'warning'); this.showNotification('Warnung', 'Bitte füllen Sie alle Felder aus', 'warning');
return; return;
} }
@ -209,7 +224,7 @@ class PCManager {
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
}, },
body: JSON.stringify({ name, mac }) body: JSON.stringify({ name, mac, ip })
}); });
const data = await response.json(); const data = await response.json();
@ -288,6 +303,33 @@ class PCManager {
bsToast.show(); bsToast.show();
} }
async refreshStatus() {
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');
}
}
escapeHtml(text) { escapeHtml(text) {
const div = document.createElement('div'); const div = document.createElement('div');
div.textContent = text; div.textContent = text;

View File

@ -31,19 +31,26 @@
<div class="card-body"> <div class="card-body">
<form id="addPCForm"> <form id="addPCForm">
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-4">
<div class="mb-3"> <div class="mb-3">
<label for="pcName" class="form-label">PC-Name</label> <label for="pcName" class="form-label">PC-Name</label>
<input type="text" class="form-control" id="pcName" required> <input type="text" class="form-control" id="pcName" required>
</div> </div>
</div> </div>
<div class="col-md-6"> <div class="col-md-4">
<div class="mb-3"> <div class="mb-3">
<label for="macAddress" class="form-label">MAC-Adresse</label> <label for="macAddress" class="form-label">MAC-Adresse</label>
<input type="text" class="form-control" id="macAddress" <input type="text" class="form-control" id="macAddress"
placeholder="XX:XX:XX:XX:XX:XX" required> placeholder="XX:XX:XX:XX:XX:XX" required>
</div> </div>
</div> </div>
<div class="col-md-4">
<div class="mb-3">
<label for="ipAddress" class="form-label">IP-Adresse</label>
<input type="text" class="form-control" id="ipAddress"
placeholder="192.168.1.100" required>
</div>
</div>
</div> </div>
<button type="submit" class="btn btn-primary"> <button type="submit" class="btn btn-primary">
<i class="fas fa-save"></i> PC hinzufügen <i class="fas fa-save"></i> PC hinzufügen
@ -81,6 +88,9 @@
</div> </div>
</div> </div>
<div class="col-md-6 text-end"> <div class="col-md-6 text-end">
<button class="btn btn-outline-info me-2" id="refreshStatusBtn" title="Online-Status aktualisieren">
<i class="fas fa-sync-alt"></i> Status aktualisieren
</button>
<span class="badge bg-primary" id="resultCount">0 PCs gefunden</span> <span class="badge bg-primary" id="resultCount">0 PCs gefunden</span>
</div> </div>
</div> </div>
@ -91,6 +101,8 @@
<tr> <tr>
<th>Name</th> <th>Name</th>
<th>MAC-Adresse</th> <th>MAC-Adresse</th>
<th>IP-Adresse</th>
<th>Status</th>
<th>Erstellt am</th> <th>Erstellt am</th>
<th>Aktionen</th> <th>Aktionen</th>
</tr> </tr>
@ -136,6 +148,11 @@
<input type="text" class="form-control" id="editMACAddress" <input type="text" class="form-control" id="editMACAddress"
placeholder="XX:XX:XX:XX:XX:XX" required> placeholder="XX:XX:XX:XX:XX:XX" required>
</div> </div>
<div class="mb-3">
<label for="editIPAddress" class="form-label">IP-Adresse</label>
<input type="text" class="form-control" id="editIPAddress"
placeholder="192.168.1.100" required>
</div>
</form> </form>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">