329 lines
19 KiB
HTML
329 lines
19 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="it" class="">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>ADM WiFi - Gestione Connessioni</title>
|
|
<link rel="shortcut icon" href="/favicon/favicon.png">
|
|
<script>
|
|
const savedTheme = localStorage.getItem('theme');
|
|
if (savedTheme === 'dark') { document.documentElement.classList.add('dark'); }
|
|
</script>
|
|
<script src="./css/tailwind.min.js"></script>
|
|
<script>
|
|
tailwind.config = { darkMode: 'class' }
|
|
</script>
|
|
<style>
|
|
* { transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease; }
|
|
</style>
|
|
</head>
|
|
<body class="bg-gradient-to-br from-purple-50 to-blue-100 dark:from-gray-900 dark:to-gray-800 min-h-screen">
|
|
<div class="container mx-auto px-4 py-4 sm:py-8">
|
|
<div class="bg-white dark:bg-gray-800 shadow-xl rounded-lg p-4 sm:p-6">
|
|
<div class="flex flex-col sm:flex-row justify-between items-start sm:items-center mb-6 gap-4">
|
|
<div class="flex items-center space-x-3">
|
|
<svg class="w-6 h-6 sm:w-8 sm:h-8 text-purple-500 dark:text-purple-400" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8.111 16.404a5.5 5.5 0 017.778 0M12 20h.01m-7.08-7.071c3.904-3.905 10.236-3.905 14.141 0M1.394 9.393c5.857-5.857 15.355-5.857 21.213 0"></path>
|
|
</svg>
|
|
<h1 class="text-xl sm:text-2xl font-bold text-gray-800 dark:text-gray-100">Gestione Connessioni WiFi</h1>
|
|
</div>
|
|
<div class="flex items-center space-x-2 sm:space-x-4 w-full sm:w-auto">
|
|
<span id="totalConnected" class="text-gray-600 dark:text-gray-400 text-xs sm:text-sm"></span>
|
|
<button
|
|
onclick="loadConnectedUsers()"
|
|
class="bg-green-500 hover:bg-green-700 dark:bg-green-600 dark:hover:bg-green-700 text-white font-bold py-2 px-3 sm:px-4 rounded focus:outline-none focus:shadow-outline transition duration-300 text-xs sm:text-sm"
|
|
title="Aggiorna"
|
|
>
|
|
<svg class="w-4 h-4 sm:w-5 sm:h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path>
|
|
</svg>
|
|
</button>
|
|
<button
|
|
id="themeToggle"
|
|
class="bg-gray-200 dark:bg-gray-700 hover:bg-gray-300 dark:hover:bg-gray-600 text-gray-800 dark:text-gray-200 font-semibold py-2 px-3 sm:px-4 rounded focus:outline-none transition duration-300 text-xs sm:text-sm"
|
|
>
|
|
<svg class="w-4 h-4 sm:w-5 sm:h-5 hidden dark:block" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z"></path>
|
|
</svg>
|
|
<svg class="w-4 h-4 sm:w-5 sm:h-5 block dark:hidden" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"></path>
|
|
</svg>
|
|
</button>
|
|
<button onclick="window.location.href='admin.html'" class="bg-blue-500 hover:bg-blue-700 dark:bg-blue-600 dark:hover:bg-blue-700 text-white font-bold py-2 px-3 sm:px-4 rounded focus:outline-none focus:shadow-outline transition duration-300 text-xs sm:text-sm">
|
|
← Admin
|
|
</button>
|
|
<button onclick="logout()" class="bg-gray-500 hover:bg-gray-700 dark:bg-gray-600 dark:hover:bg-gray-700 text-white font-bold py-2 px-3 sm:px-4 rounded focus:outline-none focus:shadow-outline transition duration-300 text-xs sm:text-sm">
|
|
Logout
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Filtri e Ricerca -->
|
|
<div class="mb-6 flex flex-col gap-3">
|
|
<div class="w-full">
|
|
<input
|
|
type="text"
|
|
id="searchInput"
|
|
placeholder="Cerca per email, MAC o IP..."
|
|
class="w-full px-3 sm:px-4 py-2 border dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-purple-500 dark:focus:ring-purple-400 text-sm sm:text-base"
|
|
>
|
|
</div>
|
|
<div class="flex flex-wrap gap-2">
|
|
<button onclick="filterUsers('all')" class="flex-1 sm:flex-none bg-blue-500 hover:bg-blue-700 dark:bg-blue-600 dark:hover:bg-blue-700 text-white font-bold py-2 px-3 sm:px-4 rounded focus:outline-none focus:shadow-outline transition duration-300 text-xs sm:text-sm">
|
|
Tutti
|
|
</button>
|
|
<button onclick="filterUsers('connected')" class="flex-1 sm:flex-none bg-green-500 hover:bg-green-700 dark:bg-green-600 dark:hover:bg-green-700 text-white font-bold py-2 px-3 sm:px-4 rounded focus:outline-none focus:shadow-outline transition duration-300 text-xs sm:text-sm">
|
|
Connessi
|
|
</button>
|
|
<button onclick="filterUsers('disconnected')" class="flex-1 sm:flex-none bg-gray-500 hover:bg-gray-700 dark:bg-gray-600 dark:hover:bg-gray-700 text-white font-bold py-2 px-3 sm:px-4 rounded focus:outline-none focus:shadow-outline transition duration-300 text-xs sm:text-sm">
|
|
Disconnessi
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Info Box -->
|
|
<div class="mb-6 p-4 bg-purple-50 dark:bg-purple-900/20 rounded-lg border border-purple-200 dark:border-purple-800">
|
|
<p class="text-purple-700 dark:text-purple-300 text-sm">
|
|
<strong>Nota:</strong> Questa pagina mostra lo storico delle connessioni WiFi.
|
|
Gli utenti attualmente connessi possono essere disconnessi cliccando sul pulsante "Disconnetti".
|
|
</p>
|
|
</div>
|
|
|
|
<!-- Tabella Utenti -->
|
|
<div class="overflow-x-auto -mx-4 sm:mx-0">
|
|
<table class="min-w-full bg-white dark:bg-gray-800">
|
|
<thead>
|
|
<tr class="bg-gray-100 dark:bg-gray-700">
|
|
<th class="px-3 sm:px-6 py-2 sm:py-3 border-b-2 border-gray-200 dark:border-gray-600 text-left text-xs font-semibold text-gray-600 dark:text-gray-300 uppercase tracking-wider">
|
|
Stato
|
|
</th>
|
|
<th class="px-3 sm:px-6 py-2 sm:py-3 border-b-2 border-gray-200 dark:border-gray-600 text-left text-xs font-semibold text-gray-600 dark:text-gray-300 uppercase tracking-wider">
|
|
Email
|
|
</th>
|
|
<th class="px-3 sm:px-6 py-2 sm:py-3 border-b-2 border-gray-200 dark:border-gray-600 text-left text-xs font-semibold text-gray-600 dark:text-gray-300 uppercase tracking-wider">
|
|
MAC Address
|
|
</th>
|
|
<th class="px-3 sm:px-6 py-2 sm:py-3 border-b-2 border-gray-200 dark:border-gray-600 text-left text-xs font-semibold text-gray-600 dark:text-gray-300 uppercase tracking-wider hidden sm:table-cell">
|
|
IP Address
|
|
</th>
|
|
<th class="px-3 sm:px-6 py-2 sm:py-3 border-b-2 border-gray-200 dark:border-gray-600 text-left text-xs font-semibold text-gray-600 dark:text-gray-300 uppercase tracking-wider hidden md:table-cell">
|
|
Inizio
|
|
</th>
|
|
<th class="px-3 sm:px-6 py-2 sm:py-3 border-b-2 border-gray-200 dark:border-gray-600 text-left text-xs font-semibold text-gray-600 dark:text-gray-300 uppercase tracking-wider hidden lg:table-cell">
|
|
Fine
|
|
</th>
|
|
<th class="px-3 sm:px-6 py-2 sm:py-3 border-b-2 border-gray-200 dark:border-gray-600 text-left text-xs font-semibold text-gray-600 dark:text-gray-300 uppercase tracking-wider">
|
|
Durata
|
|
</th>
|
|
<th class="px-3 sm:px-6 py-2 sm:py-3 border-b-2 border-gray-200 dark:border-gray-600 text-center text-xs font-semibold text-gray-600 dark:text-gray-300 uppercase tracking-wider">
|
|
Azioni
|
|
</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="connectedUsersTableBody">
|
|
<!-- La tabella verrà popolata dinamicamente -->
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<!-- Messaggio se non ci sono utenti -->
|
|
<div id="noUsersMessage" class="hidden text-center py-8">
|
|
<svg class="w-16 h-16 mx-auto text-gray-400 dark:text-gray-600 mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M18.364 5.636a9 9 0 010 12.728m0 0l-2.829-2.829m2.829 2.829L21 21M15.536 8.464a5 5 0 010 7.072m0 0l-2.829-2.829m-4.243 2.829a4.978 4.978 0 01-1.414-2.83m-1.414 5.658a9 9 0 01-2.167-9.238m7.824 2.167a1 1 0 111.414 1.414m-1.414-1.414L3 3m8.293 8.293l1.414 1.414"></path>
|
|
</svg>
|
|
<p class="text-gray-500 dark:text-gray-400 text-lg">Nessuna connessione registrata</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
// Dati utenti globali per il filtro
|
|
let allUsers = [];
|
|
let currentFilter = 'all';
|
|
|
|
// Verifica autenticazione admin
|
|
function checkAuth() {
|
|
const token = localStorage.getItem('adminToken');
|
|
if (!token) {
|
|
window.location.href = 'login_admin.html';
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Logout
|
|
function logout() {
|
|
localStorage.removeItem('adminToken');
|
|
window.location.href = 'index.html';
|
|
}
|
|
|
|
// Funzione per caricare gli utenti
|
|
async function loadConnectedUsers() {
|
|
if (!checkAuth()) return;
|
|
|
|
try {
|
|
const token = localStorage.getItem('adminToken');
|
|
const response = await fetch('/api/connected-users', {
|
|
headers: {
|
|
'Authorization': `Bearer ${token}`
|
|
}
|
|
});
|
|
|
|
if (response.status === 401) {
|
|
logout();
|
|
return;
|
|
}
|
|
|
|
allUsers = await response.json();
|
|
filterUsers(currentFilter);
|
|
updateTotalConnected();
|
|
} catch (error) {
|
|
console.error('Errore nel caricamento degli utenti:', error);
|
|
}
|
|
}
|
|
|
|
// Funzione per filtrare gli utenti
|
|
function filterUsers(filter) {
|
|
currentFilter = filter;
|
|
let filteredUsers = allUsers;
|
|
|
|
if (filter === 'connected') {
|
|
filteredUsers = allUsers.filter(user => user.is_connected);
|
|
} else if (filter === 'disconnected') {
|
|
filteredUsers = allUsers.filter(user => !user.is_connected);
|
|
}
|
|
|
|
displayUsers(filteredUsers);
|
|
}
|
|
|
|
// Funzione per visualizzare gli utenti
|
|
function displayUsers(users) {
|
|
const tableBody = document.getElementById('connectedUsersTableBody');
|
|
const noUsersMessage = document.getElementById('noUsersMessage');
|
|
tableBody.innerHTML = '';
|
|
|
|
if (users.length === 0) {
|
|
noUsersMessage.classList.remove('hidden');
|
|
return;
|
|
}
|
|
|
|
noUsersMessage.classList.add('hidden');
|
|
|
|
users.forEach(user => {
|
|
const row = document.createElement('tr');
|
|
row.className = user.is_connected
|
|
? 'hover:bg-green-50 dark:hover:bg-green-900/20 bg-green-50/50 dark:bg-green-900/10'
|
|
: 'hover:bg-gray-50 dark:hover:bg-gray-700';
|
|
row.setAttribute('data-connected', user.is_connected);
|
|
|
|
const statusBadge = user.is_connected
|
|
? '<span class="px-2 py-1 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-400">Connesso</span>'
|
|
: '<span class="px-2 py-1 inline-flex text-xs leading-5 font-semibold rounded-full bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-400">Disconnesso</span>';
|
|
|
|
const actionButton = user.is_connected
|
|
? `<button
|
|
onclick="disconnectUser('${user.mac}')"
|
|
class="bg-red-500 hover:bg-red-700 dark:bg-red-600 dark:hover:bg-red-700 text-white font-bold py-1 px-3 rounded text-xs sm:text-sm transition duration-300"
|
|
>
|
|
Disconnetti
|
|
</button>`
|
|
: '<span class="text-gray-400 dark:text-gray-600 text-xs">-</span>';
|
|
|
|
row.innerHTML = `
|
|
<td class="px-3 sm:px-6 py-3 sm:py-4 whitespace-nowrap border-b border-gray-200 dark:border-gray-600">
|
|
${statusBadge}
|
|
</td>
|
|
<td class="px-3 sm:px-6 py-3 sm:py-4 whitespace-nowrap border-b border-gray-200 dark:border-gray-600">
|
|
<div class="text-xs sm:text-sm text-gray-900 dark:text-gray-100">${user.email}</div>
|
|
</td>
|
|
<td class="px-3 sm:px-6 py-3 sm:py-4 whitespace-nowrap border-b border-gray-200 dark:border-gray-600">
|
|
<div class="text-xs sm:text-sm font-mono text-gray-900 dark:text-gray-100">${user.mac}</div>
|
|
</td>
|
|
<td class="px-3 sm:px-6 py-3 sm:py-4 whitespace-nowrap border-b border-gray-200 dark:border-gray-600 hidden sm:table-cell">
|
|
<div class="text-xs sm:text-sm font-mono text-gray-900 dark:text-gray-100">${user.ip}</div>
|
|
</td>
|
|
<td class="px-3 sm:px-6 py-3 sm:py-4 whitespace-nowrap border-b border-gray-200 dark:border-gray-600 hidden md:table-cell">
|
|
<div class="text-xs sm:text-sm text-gray-600 dark:text-gray-400">${user.starttime}</div>
|
|
</td>
|
|
<td class="px-3 sm:px-6 py-3 sm:py-4 whitespace-nowrap border-b border-gray-200 dark:border-gray-600 hidden lg:table-cell">
|
|
<div class="text-xs sm:text-sm text-gray-600 dark:text-gray-400">${user.endtime || '-'}</div>
|
|
</td>
|
|
<td class="px-3 sm:px-6 py-3 sm:py-4 whitespace-nowrap border-b border-gray-200 dark:border-gray-600">
|
|
<div class="text-xs sm:text-sm font-semibold ${user.is_connected ? 'text-green-600 dark:text-green-400' : 'text-gray-600 dark:text-gray-400'}">${user.duration}</div>
|
|
</td>
|
|
<td class="px-3 sm:px-6 py-3 sm:py-4 whitespace-nowrap border-b border-gray-200 dark:border-gray-600 text-center">
|
|
${actionButton}
|
|
</td>
|
|
`;
|
|
tableBody.appendChild(row);
|
|
});
|
|
}
|
|
|
|
// Aggiorna conteggio utenti
|
|
function updateTotalConnected() {
|
|
const connected = allUsers.filter(u => u.is_connected).length;
|
|
const total = allUsers.length;
|
|
document.getElementById('totalConnected').textContent = `Connessi: ${connected} / Totali: ${total}`;
|
|
}
|
|
|
|
// Disconnetti un utente
|
|
async function disconnectUser(mac) {
|
|
if (!confirm(`Sei sicuro di voler disconnettere l'utente con MAC ${mac}?`)) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const token = localStorage.getItem('adminToken');
|
|
const response = await fetch('/api/disconnect-user', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'Authorization': `Bearer ${token}`
|
|
},
|
|
body: JSON.stringify({ mac: mac })
|
|
});
|
|
|
|
if (response.ok) {
|
|
loadConnectedUsers();
|
|
} else {
|
|
const data = await response.json();
|
|
alert('Errore: ' + (data.message || 'Impossibile disconnettere l\'utente'));
|
|
}
|
|
} catch (error) {
|
|
console.error('Errore nella disconnessione:', error);
|
|
alert('Errore durante la disconnessione');
|
|
}
|
|
}
|
|
|
|
// Event listener per la ricerca
|
|
document.getElementById('searchInput').addEventListener('input', function(e) {
|
|
const searchTerm = e.target.value.toLowerCase();
|
|
const rows = document.querySelectorAll('#connectedUsersTableBody tr');
|
|
|
|
rows.forEach(row => {
|
|
const text = row.textContent.toLowerCase();
|
|
row.style.display = text.includes(searchTerm) ? '' : 'none';
|
|
});
|
|
});
|
|
|
|
// Inizializzazione pagina
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
if (checkAuth()) {
|
|
loadConnectedUsers();
|
|
|
|
// Aggiorna automaticamente ogni 30 secondi
|
|
setInterval(loadConnectedUsers, 30000);
|
|
}
|
|
|
|
// Dark mode toggle
|
|
const themeToggle = document.getElementById('themeToggle');
|
|
const htmlElement = document.documentElement;
|
|
themeToggle.addEventListener('click', () => {
|
|
htmlElement.classList.toggle('dark');
|
|
const newTheme = htmlElement.classList.contains('dark') ? 'dark' : 'light';
|
|
localStorage.setItem('theme', newTheme);
|
|
});
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|