diff --git a/app/app.py b/app/app.py
index 4d75ef3..390ffbe 100644
--- a/app/app.py
+++ b/app/app.py
@@ -360,6 +360,9 @@ class WOLManager:
parts = line.split()
if len(parts) >= 2:
gateway = parts[1]
+ # Skip Docker bridge networks
+ if gateway.startswith('172.17.') or gateway.startswith('172.18.') or gateway.startswith('172.19.') or gateway.startswith('172.20.'):
+ continue
# Construiește rețeaua bazată pe gateway
network_parts = gateway.split('.')
network = f"{network_parts[0]}.{network_parts[1]}.{network_parts[2]}.0/24"
@@ -377,6 +380,10 @@ class WOLManager:
mac = match.group(2)
hostname = line.split()[0] if line.split() else '?'
+ # Skip Docker bridge networks
+ if ip.startswith('172.17.') or ip.startswith('172.18.') or ip.startswith('172.19.') or ip.startswith('172.20.'):
+ continue
+
# Verifică dacă IP-ul este în rețeaua specificată
if custom_network:
import ipaddress
@@ -392,7 +399,7 @@ class WOLManager:
except:
pass
else:
- # Autodetectare - adaugă toate intrările ARP
+ # Autodetectare - adaugă toate intrările ARP (fără Docker bridge)
scanned.append({
'ip': ip,
'mac': mac,
@@ -408,6 +415,14 @@ class WOLManager:
'message': f'Nu s-au găsit dispozitive cu MAC addresses în rețeaua {custom_network}. În Docker Desktop Windows, scanarea automată este limitată. Rulează scanul Windows sau folosește butonul "➕ Adaugă Calculator" pentru a adăuga manual dispozitivele cu IP și MAC cunoscute.'
}
+ # Dacă nu s-au găsit dispozitive în autodetectare, probabil rulează în Docker
+ if not custom_network and not scanned:
+ return {
+ 'success': True,
+ 'computers': [],
+ 'message': 'Nu s-au găsit dispozitive în rețeaua locală. Aplicația rulează în Docker cu acces limitat la rețea. Pentru rezultate complete, rulează scanul Windows din sistemul host sau specifică manual o rețea CIDR.'
+ }
+
return {'success': True, 'computers': scanned}
wol_manager = WOLManager()
diff --git a/app/static/css/style.css b/app/static/css/style.css
index 3c8222b..658184c 100644
--- a/app/static/css/style.css
+++ b/app/static/css/style.css
@@ -129,6 +129,23 @@ body {
background-color: #E0E0E0;
}
+.computer-name {
+ font-weight: bold;
+}
+
+.computer-name.online {
+ color: #008000;
+}
+
+.computer-name.offline {
+ color: #800000;
+}
+
+.computer-name.unknown {
+ color: #808000;
+}
+
+/* Keep status class for scan modal compatibility */
.status {
font-size: 14px;
padding: 2px 6px;
@@ -479,9 +496,45 @@ body {
margin: 0;
}
-/* Wider scan modal for the additional column */
+.device-checkbox:disabled {
+ cursor: not-allowed;
+ opacity: 0.5;
+}
+
+/* Styles for existing/disabled devices in scan modal */
+.scan-table tr.device-exists {
+ background-color: #f5f5f5;
+ color: #999;
+ opacity: 0.7;
+}
+
+.scan-table tr.device-exists td {
+ color: #999;
+}
+
+.scan-table tr.device-exists:hover {
+ background-color: #f5f5f5;
+}
+
+.scan-table tr.device-exists .add-btn {
+ background-color: #e8e8e8;
+ color: #999;
+ cursor: not-allowed;
+ border: 1px solid #d4d4d4;
+}
+
+/* Wider scan modal for the additional column with proper scrolling */
#scanModal .modal-content {
width: 700px;
+ max-height: 90vh;
+ display: flex;
+ flex-direction: column;
+}
+
+#scanModal .modal-body {
+ overflow-y: auto;
+ max-height: 70vh;
+ flex: 1;
}
/* Mobile responsiveness */
@@ -633,6 +686,12 @@ body {
#scanModal .modal-content {
width: calc(100vw - 20px);
max-width: 100%;
+ max-height: 90vh;
+ }
+
+ #scanModal .modal-body {
+ max-height: 60vh;
+ overflow-y: auto;
}
.no-computers {
@@ -646,6 +705,23 @@ body {
}
.computers-table {
- min-width: 600px;
+ min-width: 500px;
+ }
+
+ .computer-name {
+ font-size: 15px;
+ max-width: 140px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+
+ .computers-table td {
+ padding: 6px 4px;
+ }
+
+ .computers-table td:first-child {
+ padding: 4px 2px;
+ gap: 2px;
}
}
\ No newline at end of file
diff --git a/app/static/js/app.js b/app/static/js/app.js
index aa54278..cf6f409 100644
--- a/app/static/js/app.js
+++ b/app/static/js/app.js
@@ -53,10 +53,9 @@ function displayComputers(computers) {
🗑️
-
${computer.name}
- ${computer.mac}
+ ${computer.name}
${computer.ip || '-'}
- ${computer.status}
+ ${computer.mac}
`).join('');
}
@@ -182,20 +181,23 @@ function scanNetwork() {
const network = getSelectedNetwork();
const requestData = network ? { network: network } : {};
- fetch('/api/scan', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify(requestData)
- })
- .then(response => response.json())
- .then(result => {
+ // Get current computers list to compare with scan results
+ Promise.all([
+ fetch('/api/computers').then(response => response.json()),
+ fetch('/api/scan', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify(requestData)
+ }).then(response => response.json())
+ ])
+ .then(([existingComputers, result]) => {
document.getElementById('scan-loading').style.display = 'none';
if (result.success) {
if (result.computers && result.computers.length > 0) {
- displayScanResults(result.computers);
+ displayScanResults(result.computers, existingComputers);
if (result.message) {
// Afișează mesajul deasupra tabelului
document.getElementById('scan-results').innerHTML =
@@ -228,20 +230,23 @@ function triggerWindowsScan() {
showMessage('Declanșând scanul Windows...', 'success');
- fetch('/api/scan/windows', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify(requestData)
- })
- .then(response => response.json())
- .then(data => {
+ // Get current computers list to compare with scan results
+ Promise.all([
+ fetch('/api/computers').then(response => response.json()),
+ fetch('/api/scan/windows', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify(requestData)
+ }).then(response => response.json())
+ ])
+ .then(([existingComputers, data]) => {
document.getElementById('scan-loading').style.display = 'none';
if (data.success && data.computers) {
showMessage(data.message || 'Scan Windows completat cu succes!', 'success');
- displayScanResults(data.computers);
+ displayScanResults(data.computers, existingComputers);
} else {
let message = data.message || 'Scanul Windows a eșuat';
@@ -281,13 +286,16 @@ function triggerWindowsScan() {
});
}
-function displayScanResults(computers) {
+function displayScanResults(computers, existingComputers = []) {
if (computers.length === 0) {
document.getElementById('scan-results').innerHTML =
'Nu s-au găsit calculatoare în rețea
';
return;
}
+ // Create a set of existing MAC addresses for quick lookup (normalize to lowercase)
+ const existingMACs = new Set(existingComputers.map(comp => comp.mac.toLowerCase()));
+
let html = `
@@ -313,22 +321,31 @@ function displayScanResults(computers) {
`;
computers.forEach((computer, index) => {
+ const computerMAC = computer.mac.toLowerCase();
+ const deviceExists = existingMACs.has(computerMAC);
+ const rowClass = deviceExists ? 'device-exists' : '';
+ const checkboxDisabled = deviceExists ? 'disabled' : '';
+ const buttonDisabled = deviceExists ? 'disabled' : '';
+ const buttonTitle = deviceExists ? 'Device-ul există deja în sistem' : 'Adaugă calculatorul';
+
html += `
-
+
+ onchange="updateAddButton()"
+ ${checkboxDisabled}>
${computer.ip}
${computer.mac}
${computer.hostname}
${computer.status}
-
- ➕
+
+ ${deviceExists ? '✓' : '➕'}
@@ -341,7 +358,7 @@ function displayScanResults(computers) {
function toggleSelectAll() {
const selectAllCheckbox = document.getElementById('selectAll');
- const deviceCheckboxes = document.querySelectorAll('.device-checkbox');
+ const deviceCheckboxes = document.querySelectorAll('.device-checkbox:not(:disabled)');
deviceCheckboxes.forEach(checkbox => {
checkbox.checked = selectAllCheckbox.checked;
@@ -351,8 +368,8 @@ function toggleSelectAll() {
}
function updateAddButton() {
- const deviceCheckboxes = document.querySelectorAll('.device-checkbox');
- const checkedBoxes = document.querySelectorAll('.device-checkbox:checked');
+ const deviceCheckboxes = document.querySelectorAll('.device-checkbox:not(:disabled)');
+ const checkedBoxes = document.querySelectorAll('.device-checkbox:checked:not(:disabled)');
const addButton = document.querySelector('.add-selected-btn');
const selectAllCheckbox = document.getElementById('selectAll');
@@ -363,7 +380,7 @@ function updateAddButton() {
`➕ Adaugă Selectate (${checkedBoxes.length})` : '➕ Adaugă Selectate';
}
- // Update "Select All" checkbox state
+ // Update "Select All" checkbox state (only consider enabled checkboxes)
if (selectAllCheckbox) {
if (checkedBoxes.length === 0) {
selectAllCheckbox.indeterminate = false;
@@ -378,7 +395,7 @@ function updateAddButton() {
}
function addSelectedFromScan() {
- const checkedBoxes = document.querySelectorAll('.device-checkbox:checked');
+ const checkedBoxes = document.querySelectorAll('.device-checkbox:checked:not(:disabled)');
if (checkedBoxes.length === 0) {
showMessage('Nu ai selectat niciun dispozitiv!', 'error');
@@ -484,6 +501,12 @@ function addSelectedFromScan() {
}
function addFromScan(hostname, mac, ip) {
+ // Check if this is called from a disabled button (device already exists)
+ if (event && event.target && event.target.hasAttribute('disabled')) {
+ showMessage('Device-ul există deja în sistem!', 'warning');
+ return;
+ }
+
fetch('/api/add', {
method: 'POST',
headers: {
diff --git a/app/templates/index.html b/app/templates/index.html
index 97902ef..cad89bf 100644
--- a/app/templates/index.html
+++ b/app/templates/index.html
@@ -40,9 +40,8 @@
Acțiuni
Nume Calculator
- Adresa MAC
Adresa IP
- Status
+ Adresa MAC