// Admin Dashboard JavaScript document.addEventListener('DOMContentLoaded', () => { loadUsers(); }); function switchTab(tab) { document.querySelectorAll('.admin-tab').forEach(t => t.classList.remove('active')); document.querySelectorAll('.admin-panel').forEach(p => p.style.display = 'none'); event.target.classList.add('active'); if (tab === 'users') { document.getElementById('usersPanel').style.display = 'block'; loadUsers(); } else if (tab === 'audit') { document.getElementById('auditPanel').style.display = 'block'; loadAuditLog(); } else if (tab === 'ai-usage') { document.getElementById('aiUsagePanel').style.display = 'block'; loadAiUsage(); } } // --- Users --- async function loadUsers() { try { const resp = await fetch(BASE_PATH + '/admin/users?include_inactive=true'); const data = await resp.json(); if (data.success) { renderUsersTable(data.users); populateAuditUserFilter(data.users); } } catch (err) { console.error('Failed to load users:', err); } } function renderUsersTable(users) { const tbody = document.getElementById('usersTableBody'); if (!users.length) { tbody.innerHTML = 'No users found'; return; } tbody.innerHTML = users.map(u => ` ${u.id} ${escapeHtml(u.username)} ${escapeHtml(u.email || '-')} ${u.role} ${u.auth_method || 'local'} ${u.last_login ? formatDate(u.last_login) : 'Never'} ${u.is_active ? 'Active' : 'Inactive'} ${u.is_active ? `` : `` } `).join(''); } async function toggleUser(userId, activate) { try { const resp = await fetch(`${BASE_PATH}/admin/users/${userId}`, { method: 'PUT', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({is_active: activate ? 1 : 0}), }); const data = await resp.json(); if (data.success) loadUsers(); else alert(data.error || 'Failed to update user'); } catch (err) { alert('Error: ' + err.message); } } async function toggleRole(userId, currentRole) { const newRole = currentRole === 'admin' ? 'user' : 'admin'; if (!confirm(`Change user role to "${newRole}"?`)) return; try { const resp = await fetch(`${BASE_PATH}/admin/users/${userId}`, { method: 'PUT', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({role: newRole}), }); const data = await resp.json(); if (data.success) loadUsers(); else alert(data.error || 'Failed to update role'); } catch (err) { alert('Error: ' + err.message); } } function showCreateUserModal() { document.getElementById('createUserModal').style.display = 'flex'; } function closeCreateUserModal() { document.getElementById('createUserModal').style.display = 'none'; document.getElementById('newUsername').value = ''; document.getElementById('newEmail').value = ''; document.getElementById('newFullName').value = ''; document.getElementById('newPassword').value = ''; document.getElementById('newRole').value = 'user'; document.getElementById('newAuthMethod').value = 'local'; } async function createUser() { const username = document.getElementById('newUsername').value.trim(); if (!username) { alert('Username is required'); return; } const payload = { username, email: document.getElementById('newEmail').value.trim(), full_name: document.getElementById('newFullName').value.trim(), password: document.getElementById('newPassword').value || null, role: document.getElementById('newRole').value, auth_method: document.getElementById('newAuthMethod').value, }; try { const resp = await fetch(BASE_PATH + '/admin/users', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(payload), }); const data = await resp.json(); if (data.success) { closeCreateUserModal(); loadUsers(); } else { alert(data.error || 'Failed to create user'); } } catch (err) { alert('Error: ' + err.message); } } // --- Audit Log --- function populateAuditUserFilter(users) { const select = document.getElementById('auditUserFilter'); const currentVal = select.value; select.innerHTML = ''; users.forEach(u => { select.innerHTML += ``; }); select.value = currentVal; } async function loadAuditLog() { const userId = document.getElementById('auditUserFilter').value; let url = BASE_PATH + '/admin/audit?limit=200'; if (userId) url += `&user_id=${userId}`; try { const resp = await fetch(url); const data = await resp.json(); if (data.success) { renderAuditTable(data.entries); } } catch (err) { console.error('Failed to load audit log:', err); } } function renderAuditTable(entries) { const tbody = document.getElementById('auditTableBody'); if (!entries.length) { tbody.innerHTML = 'No audit entries'; return; } tbody.innerHTML = entries.map(e => ` ${formatDate(e.timestamp)} ${escapeHtml(e.username || 'Unknown')} ${escapeHtml(e.action)} ${escapeHtml(e.details || '-')} `).join(''); } // --- AI Usage --- async function loadAiUsage() { try { const resp = await fetch(BASE_PATH + '/admin/ai-usage'); const data = await resp.json(); if (data.success) { renderAiStats(data.stats); renderAiUsageTable(data.by_user); } } catch (err) { console.error('Failed to load AI usage:', err); } } function renderAiStats(stats) { const grid = document.getElementById('aiStatsGrid'); grid.innerHTML = `
${stats.total_requests || 0}
Total Requests
${(stats.total_tokens || 0).toLocaleString()}
Total Tokens
${stats.requests_24h || 0}
Requests (24h)
${(stats.tokens_24h || 0).toLocaleString()}
Tokens (24h)
${stats.requests_7d || 0}
Requests (7d)
${(stats.tokens_7d || 0).toLocaleString()}
Tokens (7d)
`; } function renderAiUsageTable(byUser) { const tbody = document.getElementById('aiUsageTableBody'); if (!byUser.length) { tbody.innerHTML = 'No AI usage data'; return; } tbody.innerHTML = byUser.map(u => ` ${escapeHtml(u.username)} ${u.request_count} ${(u.total_tokens || 0).toLocaleString()} ${u.last_used ? formatDate(u.last_used) : '-'} `).join(''); } // --- Helpers --- function escapeHtml(str) { if (!str) return ''; const div = document.createElement('div'); div.textContent = str; return div.innerHTML; } function formatDate(dateStr) { if (!dateStr) return '-'; try { const d = new Date(dateStr); return d.toLocaleString(); } catch { return dateStr; } }