Files
wol/app/static/js/app.js
Marius Mutu acf234c600 Major feature enhancement: Windows PowerShell network scanning integration
- Added Windows PowerShell network scanner with auto-detection and interactive mode
- Implemented dual scanning system (Windows + Linux fallback)
- Added computer management features (rename, delete, duplicate checking)
- Enhanced UI with modern responsive design and Romanian localization
- Added comprehensive Windows-Linux integration with WSL interop
- Improved error handling and user feedback throughout
- Added hot reload for development and comprehensive documentation

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-05 17:27:27 +03:00

608 lines
21 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// WOL Manager JavaScript
let scanModal, addModal, renameModal;
// Initialize on page load
window.onload = function() {
scanModal = document.getElementById('scanModal');
addModal = document.getElementById('addModal');
renameModal = document.getElementById('renameModal');
refreshComputers();
};
function showMessage(message, type) {
const messageArea = document.getElementById('message-area');
messageArea.innerHTML = `<div class="message ${type}">${message}</div>`;
setTimeout(() => {
messageArea.innerHTML = '';
}, 5000);
}
function refreshComputers() {
fetch('/api/computers')
.then(response => response.json())
.then(computers => {
displayComputers(computers);
})
.catch(error => {
showMessage('Eroare la încărcarea calculatoarelor: ' + error.message, 'error');
});
}
function displayComputers(computers) {
const tbody = document.getElementById('computers-tbody');
const noComputersDiv = document.getElementById('no-computers');
if (computers.length === 0) {
tbody.innerHTML = '';
noComputersDiv.style.display = 'block';
return;
}
noComputersDiv.style.display = 'none';
tbody.innerHTML = computers.map(computer => `
<tr>
<td>
<button class="wake-btn" onclick="wakeComputer('${computer.mac}', '${computer.name}', '${computer.ip || ''}')" title="Trezește calculatorul">
</button>
<button class="rename-btn" onclick="openRenameModal('${computer.name}')" title="Redenumește calculatorul">
📝
</button>
<button class="delete-btn" onclick="deleteComputer('${computer.name}', '${computer.mac}')" title="Șterge calculatorul">
🗑️
</button>
</td>
<td>${computer.name}</td>
<td style="font-family: monospace;">${computer.mac}</td>
<td>${computer.ip || '-'}</td>
<td><span class="status ${computer.status}">${computer.status}</span></td>
</tr>
`).join('');
}
function wakeComputer(mac, name, ip) {
showMessage(`Se trimite magic packet pentru ${name}...`, 'success');
fetch('/api/wake', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({mac: mac, name: name, ip: ip})
})
.then(response => response.json())
.then(result => {
if (result.success) {
showMessage(result.message, 'success');
setTimeout(refreshComputers, 2000);
} else {
showMessage(result.message, 'error');
}
})
.catch(error => {
showMessage('Eroare la trezirea calculatorului: ' + error.message, 'error');
});
}
function wakeAllComputers() {
showMessage('Se trezesc toate calculatoarele...', 'success');
fetch('/api/wake-all', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
}
})
.then(response => response.json())
.then(data => {
let message = 'Comenzi trimise:<br>';
data.results.forEach(result => {
message += `${result.name}: ${result.result.message}<br>`;
});
showMessage(message, 'success');
setTimeout(refreshComputers, 3000);
})
.catch(error => {
showMessage('Eroare la trezirea calculatoarelor: ' + error.message, 'error');
});
}
function openAddModal() {
addModal.style.display = 'block';
}
function closeAddModal() {
addModal.style.display = 'none';
document.getElementById('computerName').value = '';
document.getElementById('computerMac').value = '';
document.getElementById('computerIp').value = '';
}
function addComputer() {
const name = document.getElementById('computerName').value;
const mac = document.getElementById('computerMac').value;
const ip = document.getElementById('computerIp').value;
if (!name || !mac) {
showMessage('Numele și MAC-ul sunt obligatorii!', 'error');
return;
}
fetch('/api/add', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({name: name, mac: mac, ip: ip})
})
.then(response => response.json())
.then(result => {
if (result.success) {
showMessage(result.message, 'success');
closeAddModal();
refreshComputers();
} else {
showMessage(result.message, 'error');
}
})
.catch(error => {
showMessage('Eroare la adăugarea calculatorului: ' + error.message, 'error');
});
}
function toggleCustomNetwork() {
const select = document.getElementById('networkSelect');
const customInput = document.getElementById('customNetwork');
if (select.value === 'custom') {
customInput.style.display = 'inline';
customInput.focus();
} else {
customInput.style.display = 'none';
}
}
function getSelectedNetwork() {
const select = document.getElementById('networkSelect');
const customInput = document.getElementById('customNetwork');
if (select.value === 'custom') {
return customInput.value.trim();
} else {
return select.value;
}
}
function scanNetwork() {
scanModal.style.display = 'block';
document.getElementById('scan-loading').style.display = 'block';
document.getElementById('scan-results').innerHTML = '';
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 => {
document.getElementById('scan-loading').style.display = 'none';
if (result.success) {
if (result.computers && result.computers.length > 0) {
displayScanResults(result.computers);
if (result.message) {
// Afișează mesajul deasupra tabelului
document.getElementById('scan-results').innerHTML =
`<div class="message" style="background: #fef3c7; color: #92400e; border-color: #fbbf24;">${result.message}</div>` +
document.getElementById('scan-results').innerHTML;
}
} else if (result.message) {
document.getElementById('scan-results').innerHTML =
`<div class="message" style="background: #fef3c7; color: #92400e; border-color: #fbbf24;">${result.message}</div>`;
}
} else {
document.getElementById('scan-results').innerHTML =
`<div class="message error">${result.message}</div>`;
}
})
.catch(error => {
document.getElementById('scan-loading').style.display = 'none';
document.getElementById('scan-results').innerHTML =
`<div class="message error">Eroare la scanare: ${error.message}</div>`;
});
}
function triggerWindowsScan() {
scanModal.style.display = 'block';
document.getElementById('scan-loading').style.display = 'block';
document.getElementById('scan-results').innerHTML = '';
const network = getSelectedNetwork();
const requestData = network ? { network: network } : {};
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 => {
document.getElementById('scan-loading').style.display = 'none';
if (data.success && data.computers) {
showMessage(data.message || 'Scan Windows completat cu succes!', 'success');
displayScanResults(data.computers);
} else {
let message = data.message || 'Scanul Windows a eșuat';
if (data.instructions) {
message += '<br><br><strong>Instrucțiuni:</strong><br>' + data.instructions;
if (data.commands) {
message += '<br><strong>Comenzi disponibile:</strong>';
data.commands.forEach(cmd => {
message += `<br>• <code>${cmd}</code>`;
});
}
}
showMessage(message, 'error');
document.getElementById('scan-results').innerHTML =
'<div style="padding: 20px; text-align: center; color: #666;">' +
'<h3>Scanul Windows nu poate fi executat din container</h3>' +
'<p>Pentru a scana rețeaua Windows și obține MAC addresses:</p>' +
'<ol style="text-align: left; margin: 20px 0;">' +
'<li>Deschide Command Prompt sau PowerShell ca Administrator pe Windows</li>' +
'<li>Navighează la directorul proiectului WOL Manager</li>' +
'<li>Rulează una din comenzile de mai jos:</li>' +
'</ol>' +
'<div style="background: #f5f5f5; padding: 15px; border-radius: 5px; margin: 10px 0; font-family: monospace;">' +
(data.commands ? data.commands.map(cmd => `<div style="margin: 5px 0;">${cmd}</div>`).join('') :
'scripts\\scan-network.bat') +
'</div>' +
'<p><small>După rularea comenzii, apasă "Scanează Rețeaua" pentru a vedea rezultatele.</small></p>' +
'</div>';
}
})
.catch(error => {
document.getElementById('scan-loading').style.display = 'none';
showMessage('Eroare la declanșarea scanului Windows: ' + error.message, 'error');
console.error('Error:', error);
});
}
function displayScanResults(computers) {
if (computers.length === 0) {
document.getElementById('scan-results').innerHTML =
'<div class="message error">Nu s-au găsit calculatoare în rețea</div>';
return;
}
let html = `
<div class="scan-controls">
<label class="select-all-container">
<input type="checkbox" id="selectAll" onchange="toggleSelectAll()">
<span>Selectează toate</span>
</label>
<button class="add-selected-btn" onclick="addSelectedFromScan()" disabled title="Adaugă calculatoarele selectate">
Adaugă Selectate
</button>
</div>
<table class="scan-table">
<thead>
<tr>
<th>Selectează</th>
<th>IP</th>
<th>MAC</th>
<th>Hostname</th>
<th>Status</th>
<th>Acțiune</th>
</tr>
</thead>
<tbody>
`;
computers.forEach((computer, index) => {
html += `
<tr>
<td>
<input type="checkbox" class="device-checkbox"
data-hostname="${computer.hostname}"
data-mac="${computer.mac}"
data-ip="${computer.ip}"
onchange="updateAddButton()">
</td>
<td>${computer.ip}</td>
<td style="font-family: monospace;">${computer.mac}</td>
<td>${computer.hostname}</td>
<td><span class="status ${computer.status}">${computer.status}</span></td>
<td>
<button class="add-btn" onclick="addFromScan('${computer.hostname}', '${computer.mac}', '${computer.ip}')" title="Adaugă calculatorul">
</button>
</td>
</tr>
`;
});
html += '</tbody></table>';
document.getElementById('scan-results').innerHTML = html;
}
function toggleSelectAll() {
const selectAllCheckbox = document.getElementById('selectAll');
const deviceCheckboxes = document.querySelectorAll('.device-checkbox');
deviceCheckboxes.forEach(checkbox => {
checkbox.checked = selectAllCheckbox.checked;
});
updateAddButton();
}
function updateAddButton() {
const deviceCheckboxes = document.querySelectorAll('.device-checkbox');
const checkedBoxes = document.querySelectorAll('.device-checkbox:checked');
const addButton = document.querySelector('.add-selected-btn');
const selectAllCheckbox = document.getElementById('selectAll');
// Enable/disable the "Add Selected" button
if (addButton) {
addButton.disabled = checkedBoxes.length === 0;
addButton.textContent = checkedBoxes.length > 0 ?
` Adaugă Selectate (${checkedBoxes.length})` : ' Adaugă Selectate';
}
// Update "Select All" checkbox state
if (selectAllCheckbox) {
if (checkedBoxes.length === 0) {
selectAllCheckbox.indeterminate = false;
selectAllCheckbox.checked = false;
} else if (checkedBoxes.length === deviceCheckboxes.length) {
selectAllCheckbox.indeterminate = false;
selectAllCheckbox.checked = true;
} else {
selectAllCheckbox.indeterminate = true;
}
}
}
function addSelectedFromScan() {
const checkedBoxes = document.querySelectorAll('.device-checkbox:checked');
if (checkedBoxes.length === 0) {
showMessage('Nu ai selectat niciun dispozitiv!', 'error');
return;
}
const devices = Array.from(checkedBoxes).map(checkbox => ({
name: checkbox.dataset.hostname,
mac: checkbox.dataset.mac,
ip: checkbox.dataset.ip
}));
// Show progress message
showMessage(`Se adaugă ${devices.length} dispozitive...`, 'success');
// Add devices one by one
let addedCount = 0;
let failedCount = 0;
let duplicateCount = 0;
let failedDevices = [];
let duplicateDevices = [];
const addDevice = (device, index) => {
return fetch('/api/add', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(device)
})
.then(response => response.json())
.then(result => {
if (result.success) {
addedCount++;
} else {
// Verifică dacă este o eroare de duplicat
if (result.message.includes('există deja')) {
duplicateCount++;
duplicateDevices.push(`${device.name} (${result.message})`);
} else {
failedCount++;
failedDevices.push(`${device.name}: ${result.message}`);
}
console.warn(`Failed to add ${device.name}:`, result.message);
}
})
.catch(error => {
failedCount++;
failedDevices.push(`${device.name}: ${error.message}`);
console.error(`Error adding ${device.name}:`, error);
});
};
// Add all devices in parallel
Promise.all(devices.map(addDevice))
.then(() => {
let message = '';
let messageType = 'success';
if (addedCount > 0 && failedCount === 0 && duplicateCount === 0) {
message = `${addedCount} dispozitive adăugate cu succes!`;
} else if (addedCount > 0) {
message = `${addedCount} dispozitive adăugate cu succes`;
if (duplicateCount > 0) {
message += `, ${duplicateCount} duplicate ignorate`;
}
if (failedCount > 0) {
message += `, ${failedCount} eșuate`;
}
message += '.';
if (duplicateCount > 0 || failedCount > 0) {
messageType = 'warning';
}
} else {
if (duplicateCount > 0 && failedCount === 0) {
message = `Toate ${duplicateCount} dispozitivele selectate există deja în sistem.`;
messageType = 'warning';
} else if (duplicateCount > 0) {
message = `${duplicateCount} dispozitive duplicate, ${failedCount} eșuate.`;
messageType = 'error';
} else {
message = `Toate ${failedCount} dispozitivele au eșuat să fie adăugate.`;
messageType = 'error';
}
}
// Dacă există duplicate sau erori, afișează detalii suplimentare în consolă
if (duplicateDevices.length > 0) {
console.info('Dispozitive duplicate:', duplicateDevices);
}
if (failedDevices.length > 0) {
console.warn('Dispozitive eșuate:', failedDevices);
}
showMessage(message, messageType);
if (addedCount > 0) {
closeScanModal();
refreshComputers();
}
});
}
function addFromScan(hostname, mac, ip) {
fetch('/api/add', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({name: hostname, mac: mac, ip: ip})
})
.then(response => response.json())
.then(result => {
if (result.success) {
showMessage(result.message, 'success');
closeScanModal();
refreshComputers();
} else {
showMessage(result.message, 'error');
}
})
.catch(error => {
showMessage('Eroare la adăugarea calculatorului: ' + error.message, 'error');
});
}
function closeScanModal() {
scanModal.style.display = 'none';
}
function openRenameModal(currentName) {
document.getElementById('currentName').value = currentName;
document.getElementById('newName').value = '';
renameModal.style.display = 'block';
document.getElementById('newName').focus();
}
function closeRenameModal() {
renameModal.style.display = 'none';
document.getElementById('currentName').value = '';
document.getElementById('newName').value = '';
}
function performRename() {
const oldName = document.getElementById('currentName').value;
const newName = document.getElementById('newName').value.trim();
if (!newName) {
showMessage('Numele nou nu poate fi gol!', 'error');
return;
}
if (oldName === newName) {
showMessage('Numele nou trebuie să fie diferit de cel actual!', 'error');
return;
}
fetch('/api/rename', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({old_name: oldName, new_name: newName})
})
.then(response => response.json())
.then(result => {
if (result.success) {
showMessage(result.message, 'success');
closeRenameModal();
refreshComputers();
} else {
showMessage(result.message, 'error');
}
})
.catch(error => {
showMessage('Eroare la redenumirea calculatorului: ' + error.message, 'error');
});
}
function deleteComputer(name, mac) {
const displayName = name && name.trim() ? name : `Calculator cu MAC ${mac}`;
if (!confirm(`Sigur vrei să ștergi calculatorul "${displayName}"?`)) {
return;
}
fetch('/api/delete', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({name: name, mac: mac})
})
.then(response => response.json())
.then(result => {
if (result.success) {
showMessage(result.message, 'success');
refreshComputers();
} else {
showMessage(result.message, 'error');
}
})
.catch(error => {
showMessage('Eroare la ștergerea calculatorului: ' + error.message, 'error');
});
}
// Close modals when clicking outside
window.onclick = function(event) {
if (event.target == addModal) {
closeAddModal();
}
if (event.target == scanModal) {
closeScanModal();
}
if (event.target == renameModal) {
closeRenameModal();
}
}
// Allow Enter key to perform rename
document.addEventListener('keydown', function(event) {
if (event.key === 'Enter' && renameModal.style.display === 'block') {
performRename();
}
});