First Commit

This commit is contained in:
dedhersel
2026-02-17 12:43:27 +01:00
commit 38f85dc498
26 changed files with 9939 additions and 0 deletions

411
templates/admin_users.html Normal file
View File

@@ -0,0 +1,411 @@
{% 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')">&times;</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')">&times;</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')">&times;</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 %}