412 lines
15 KiB
HTML
412 lines
15 KiB
HTML
{% extends "base.html" %}
|
||
|
||
{% block title %}Gestione Utenti - Proxmox Manager{% endblock %}
|
||
|
||
{% block extra_styles %}
|
||
<style>
|
||
.users-table-container {
|
||
overflow-x: auto;
|
||
}
|
||
|
||
.action-buttons {
|
||
display: flex;
|
||
gap: 0.5rem;
|
||
}
|
||
|
||
.action-buttons .btn {
|
||
padding: 0.5rem 0.75rem;
|
||
font-size: 0.85rem;
|
||
}
|
||
|
||
.modal-content {
|
||
max-width: 600px;
|
||
}
|
||
|
||
.vm-assignments {
|
||
background: #f8f9fa;
|
||
padding: 1rem;
|
||
border-radius: 5px;
|
||
margin-top: 1rem;
|
||
}
|
||
|
||
.vm-assignment-item {
|
||
background: white;
|
||
padding: 0.75rem;
|
||
border-radius: 5px;
|
||
margin-bottom: 0.5rem;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
}
|
||
|
||
.badge {
|
||
padding: 0.25rem 0.5rem;
|
||
border-radius: 3px;
|
||
font-size: 0.85rem;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.badge-active {
|
||
background: #d3f9d8;
|
||
color: #2b8a3e;
|
||
}
|
||
|
||
.badge-inactive {
|
||
background: #ffe3e3;
|
||
color: #c92a2a;
|
||
}
|
||
</style>
|
||
{% endblock %}
|
||
|
||
{% block content %}
|
||
<div class="card">
|
||
<div style="display: flex; justify-content: space-between; align-items: center;">
|
||
<div>
|
||
<h1 class="card-title">Gestione Utenti</h1>
|
||
<p style="color: #868e96;">Amministra utenti e assegnazioni VM</p>
|
||
</div>
|
||
<button class="btn btn-primary" onclick="showCreateUserModal()">➕ Nuovo Utente</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="card">
|
||
<div id="usersTableContainer">
|
||
<div class="loading">
|
||
<div class="spinner"></div>
|
||
<p>Caricamento utenti...</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Modal Crea Utente -->
|
||
<div id="createUserModal" class="modal">
|
||
<div class="modal-content">
|
||
<span class="close" onclick="closeModal('createUserModal')">×</span>
|
||
<h2>Crea Nuovo Utente</h2>
|
||
<form id="createUserForm" onsubmit="createUser(event)">
|
||
<div class="form-group">
|
||
<label for="newUsername">Username *</label>
|
||
<input type="text" id="newUsername" required>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="newEmail">Email *</label>
|
||
<input type="email" id="newEmail" required>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="newPassword">Password *</label>
|
||
<input type="password" id="newPassword" required>
|
||
</div>
|
||
<div class="form-group">
|
||
<label style="display: flex; align-items: center; gap: 0.5rem;">
|
||
<input type="checkbox" id="newIsAdmin" style="width: auto;">
|
||
Amministratore
|
||
</label>
|
||
</div>
|
||
<div style="display: flex; gap: 1rem; justify-content: flex-end;">
|
||
<button type="button" class="btn btn-secondary" onclick="closeModal('createUserModal')">Annulla</button>
|
||
<button type="submit" class="btn btn-primary">Crea Utente</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Modal Gestisci VM -->
|
||
<div id="manageVMsModal" class="modal">
|
||
<div class="modal-content">
|
||
<span class="close" onclick="closeModal('manageVMsModal')">×</span>
|
||
<h2 id="manageVMsTitle">Gestisci VM</h2>
|
||
|
||
<div class="vm-assignments" id="currentVMsList">
|
||
<div class="loading">
|
||
<div class="spinner"></div>
|
||
<p>Caricamento VM...</p>
|
||
</div>
|
||
</div>
|
||
|
||
<h3 style="margin-top: 1.5rem;">Aggiungi Nuova VM</h3>
|
||
<form id="assignVMForm" onsubmit="assignVM(event)">
|
||
<input type="hidden" id="assignUserId">
|
||
<div class="form-group">
|
||
<label for="vmId">VM ID *</label>
|
||
<input type="number" id="vmId" required placeholder="es. 114">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="vmName">Nome VM</label>
|
||
<input type="text" id="vmName" placeholder="es. buslino-vm">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="vmNotes">Note</label>
|
||
<textarea id="vmNotes" rows="3" placeholder="Note opzionali sulla VM"></textarea>
|
||
</div>
|
||
<div style="display: flex; gap: 1rem; justify-content: flex-end;">
|
||
<button type="button" class="btn btn-secondary" onclick="closeModal('manageVMsModal')">Chiudi</button>
|
||
<button type="submit" class="btn btn-primary">Assegna VM</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Modal Imposta IP -->
|
||
<div id="setIPModal" class="modal">
|
||
<div class="modal-content" style="max-width: 500px;">
|
||
<span class="close" onclick="closeModal('setIPModal')">×</span>
|
||
<h2>Imposta Indirizzo IP</h2>
|
||
<p id="setIPVmName" style="color: var(--text-secondary); margin-bottom: 1rem;"></p>
|
||
|
||
<form id="setIPForm" onsubmit="updateVMIP(event)">
|
||
<input type="hidden" id="setIPVmId">
|
||
<div class="form-group">
|
||
<label for="vmIpAddress">Indirizzo IP</label>
|
||
<input type="text"
|
||
id="vmIpAddress"
|
||
placeholder="es. 192.168.1.100"
|
||
pattern="^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$">
|
||
<small style="color: var(--text-secondary); font-size: 0.85rem;">
|
||
Lascia vuoto per rimuovere l'IP
|
||
</small>
|
||
</div>
|
||
<div style="display: flex; gap: 1rem; justify-content: flex-end; margin-top: 1.5rem;">
|
||
<button type="button" class="btn btn-secondary" onclick="closeModal('setIPModal')">Annulla</button>
|
||
<button type="submit" class="btn btn-primary">Salva IP</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
{% endblock %}
|
||
|
||
{% block extra_scripts %}
|
||
<script>
|
||
let currentUsers = [];
|
||
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
loadUsers();
|
||
});
|
||
|
||
async function loadUsers() {
|
||
const container = document.getElementById('usersTableContainer');
|
||
|
||
const result = await apiCall('/api/admin/users');
|
||
|
||
if (result.status === 'success') {
|
||
currentUsers = result.data;
|
||
|
||
container.innerHTML = `
|
||
<div class="users-table-container">
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>ID</th>
|
||
<th>Username</th>
|
||
<th>Email</th>
|
||
<th>Ruolo</th>
|
||
<th>Ultimo Login</th>
|
||
<th>Stato</th>
|
||
<th>Azioni</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
${currentUsers.map(user => `
|
||
<tr>
|
||
<td>${user.id}</td>
|
||
<td><strong>${user.username}</strong></td>
|
||
<td>${user.email}</td>
|
||
<td>
|
||
${user.is_admin ?
|
||
'<span class="badge-admin">ADMIN</span>' :
|
||
'<span>Utente</span>'}
|
||
</td>
|
||
<td>${user.last_login ? new Date(user.last_login).toLocaleString('it-IT') : 'Mai'}</td>
|
||
<td>
|
||
<span class="badge ${user.active ? 'badge-active' : 'badge-inactive'}">
|
||
${user.active ? 'Attivo' : 'Disattivo'}
|
||
</span>
|
||
</td>
|
||
<td>
|
||
<div class="action-buttons">
|
||
<button class="btn btn-primary" onclick="manageUserVMs(${user.id}, '${user.username}')">
|
||
🖥️ VM
|
||
</button>
|
||
${user.active ? `
|
||
<button class="btn btn-warning" onclick="toggleUserStatus(${user.id}, false, '${user.username}')">
|
||
🚫 Disattiva
|
||
</button>
|
||
` : `
|
||
<button class="btn btn-success" onclick="toggleUserStatus(${user.id}, true, '${user.username}')">
|
||
✅ Attiva
|
||
</button>
|
||
`}
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
`).join('')}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
`;
|
||
} else {
|
||
container.innerHTML = `<div class="alert alert-error">${result.message}</div>`;
|
||
}
|
||
}
|
||
|
||
function showCreateUserModal() {
|
||
document.getElementById('createUserModal').style.display = 'block';
|
||
}
|
||
|
||
function closeModal(modalId) {
|
||
document.getElementById(modalId).style.display = 'none';
|
||
}
|
||
|
||
async function createUser(event) {
|
||
event.preventDefault();
|
||
|
||
const data = {
|
||
username: document.getElementById('newUsername').value,
|
||
email: document.getElementById('newEmail').value,
|
||
password: document.getElementById('newPassword').value,
|
||
is_admin: document.getElementById('newIsAdmin').checked
|
||
};
|
||
|
||
const result = await apiCall('/api/admin/user/create', 'POST', data);
|
||
|
||
if (result.status === 'success') {
|
||
showAlert('Utente creato con successo!', 'success');
|
||
closeModal('createUserModal');
|
||
document.getElementById('createUserForm').reset();
|
||
loadUsers();
|
||
} else {
|
||
showAlert('Errore: ' + result.message, 'error');
|
||
}
|
||
}
|
||
|
||
async function manageUserVMs(userId, username) {
|
||
document.getElementById('manageVMsTitle').textContent = `Gestisci VM - ${username}`;
|
||
document.getElementById('assignUserId').value = userId;
|
||
document.getElementById('manageVMsModal').style.display = 'block';
|
||
|
||
const vmsList = document.getElementById('currentVMsList');
|
||
vmsList.innerHTML = '<div class="loading"><div class="spinner"></div><p>Caricamento...</p></div>';
|
||
|
||
const result = await apiCall(`/api/admin/user/${userId}/vms`);
|
||
|
||
if (result.status === 'success') {
|
||
if (result.data.length === 0) {
|
||
vmsList.innerHTML = '<p style="text-align: center; color: #868e96;">Nessuna VM assegnata</p>';
|
||
} else {
|
||
vmsList.innerHTML = `
|
||
<h3>VM Assegnate</h3>
|
||
${result.data.map(vm => `
|
||
<div class="vm-assignment-item">
|
||
<div style="flex: 1;">
|
||
<strong>VM ${vm.vm_id}</strong> - ${vm.vm_name || 'N/A'}
|
||
${vm.notes ? `<br><small style="color: #868e96;">${vm.notes}</small>` : ''}
|
||
${vm.ip_address ? `<br><code style="font-size: 0.8rem; color: #58a6ff;">${vm.ip_address}</code>` : '<br><small style="color: #868e96;">Nessun IP impostato</small>'}
|
||
</div>
|
||
<div style="display: flex; gap: 8px;">
|
||
<button class="btn btn-secondary btn-sm" onclick="showSetIPModal(${vm.vm_id}, '${vm.vm_name || 'VM ' + vm.vm_id}', '${vm.ip_address || ''}')">
|
||
🌐 IP
|
||
</button>
|
||
<button class="btn btn-danger btn-sm" onclick="removeVM(${userId}, ${vm.vm_id}, '${username}')">
|
||
🗑️
|
||
</button>
|
||
</div>
|
||
</div>
|
||
`).join('')}
|
||
`;
|
||
}
|
||
} else {
|
||
vmsList.innerHTML = `<div class="alert alert-error">${result.message}</div>`;
|
||
}
|
||
}
|
||
|
||
async function assignVM(event) {
|
||
event.preventDefault();
|
||
|
||
const userId = document.getElementById('assignUserId').value;
|
||
const data = {
|
||
vm_id: parseInt(document.getElementById('vmId').value),
|
||
vm_name: document.getElementById('vmName').value,
|
||
notes: document.getElementById('vmNotes').value
|
||
};
|
||
|
||
const result = await apiCall(`/api/admin/user/${userId}/assign-vm`, 'POST', data);
|
||
|
||
if (result.status === 'success') {
|
||
showAlert('VM assegnata con successo!', 'success');
|
||
document.getElementById('assignVMForm').reset();
|
||
// Ricarica la lista delle VM
|
||
const username = document.getElementById('manageVMsTitle').textContent.split(' - ')[1];
|
||
manageUserVMs(userId, username);
|
||
} else {
|
||
showAlert('Errore: ' + result.message, 'error');
|
||
}
|
||
}
|
||
|
||
async function removeVM(userId, vmId, username) {
|
||
if (!confirm(`Vuoi rimuovere la VM ${vmId} da ${username}?`)) return;
|
||
|
||
const result = await apiCall(`/api/admin/user/${userId}/remove-vm/${vmId}`, 'DELETE');
|
||
|
||
if (result.status === 'success') {
|
||
showAlert('VM rimossa con successo!', 'success');
|
||
manageUserVMs(userId, username);
|
||
} else {
|
||
showAlert('Errore: ' + result.message, 'error');
|
||
}
|
||
}
|
||
|
||
async function toggleUserStatus(userId, active, username) {
|
||
const action = active ? 'attivare' : 'disattivare';
|
||
if (!confirm(`Vuoi davvero ${action} l'utente ${username}?`)) return;
|
||
|
||
const result = await apiCall(`/api/admin/user/${userId}/toggle-status`, 'POST', { active });
|
||
|
||
if (result.status === 'success') {
|
||
showAlert(result.message, 'success');
|
||
loadUsers();
|
||
} else {
|
||
showAlert('Errore: ' + result.message, 'error');
|
||
}
|
||
}
|
||
|
||
function showSetIPModal(vmId, vmName, currentIp) {
|
||
document.getElementById('setIPVmId').value = vmId;
|
||
document.getElementById('setIPVmName').textContent = `VM ${vmId} - ${vmName}`;
|
||
document.getElementById('vmIpAddress').value = currentIp || '';
|
||
document.getElementById('setIPModal').style.display = 'block';
|
||
}
|
||
|
||
async function updateVMIP(event) {
|
||
event.preventDefault();
|
||
|
||
const vmId = document.getElementById('setIPVmId').value;
|
||
const ipAddress = document.getElementById('vmIpAddress').value.trim();
|
||
|
||
const result = await apiCall(`/api/admin/vm/${vmId}/update-ip`, 'PUT', {
|
||
ip_address: ipAddress
|
||
});
|
||
|
||
if (result.status === 'success') {
|
||
showAlert('IP aggiornato con successo!', 'success');
|
||
closeModal('setIPModal');
|
||
|
||
// Ricarica la lista VM se il modal è aperto
|
||
const manageVMsModal = document.getElementById('manageVMsModal');
|
||
if (manageVMsModal.style.display === 'block') {
|
||
const userId = document.getElementById('assignUserId').value;
|
||
const username = document.getElementById('manageVMsTitle').textContent.split(' - ')[1];
|
||
manageUserVMs(userId, username);
|
||
}
|
||
} else {
|
||
showAlert('Errore: ' + result.message, 'error');
|
||
}
|
||
}
|
||
|
||
// Chiudi modal cliccando fuori
|
||
window.onclick = function(event) {
|
||
if (event.target.className === 'modal') {
|
||
event.target.style.display = 'none';
|
||
}
|
||
}
|
||
</script>
|
||
{% endblock %}
|