document.addEventListener('DOMContentLoaded', () => { loadStats(); loadReportedNotes(); loadUsers(); loadBannedIps(); initCollapsibles(); initSearch(); }); let selectedUserId = null; let selectedNotepadId = null; let allUsers = []; async function loadStats() { const container = document.getElementById('admin-stats'); const reportedBadge = document.getElementById('reported-badge'); try { const res = await fetch('/admin/stats'); if (res.status === 403) { window.location.href = '/login.html'; return; } const stats = await res.json(); container.innerHTML = `
Users
${stats.banned_user_count} bannedNotepads
${stats.public_notepad_count} Public ยท ${stats.private_notepad_count} EncryptedTotal Notes
${stats.public_note_count} Public ยท ${stats.encrypted_note_count} EncryptedReported Notes
${stats.reported_note_count > 0 ? 'Requires attention' : 'All clear'}Loading reported notes...
'; try { const res = await fetch('/admin/reported_notes'); if (!res.ok) throw new Error('Failed to load reported notes'); const notes = await res.json(); listDiv.innerHTML = ''; if (notes.length === 0) { listDiv.innerHTML = 'No notes reported. Public content is clean.
'; return; } notes.forEach(note => { const item = document.createElement('div'); item.className = 'user-item'; const info = document.createElement('div'); info.className = 'item-info'; info.innerHTML = `Error loading reported notes.
'; } } async function restoreNote(noteId) { try { const res = await fetch(`/admin/notes/${noteId}/restore`, { method: 'POST' }); const data = await res.json(); alert(data.message || 'Note restored'); loadReportedNotes(); loadStats(); } catch (err) { console.error(err); alert('Error restoring note'); } } async function loadUsers() { const userListDiv = document.getElementById('user-list'); userListDiv.innerHTML = 'Loading users...
'; try { const res = await fetch('/admin/users'); if (res.status === 403) { window.location.href = '/login.html'; return; } allUsers = await res.json(); renderUsers(allUsers); } catch (err) { console.error(err); userListDiv.innerHTML = 'Error loading users.
'; } } function renderUsers(users) { const userListDiv = document.getElementById('user-list'); userListDiv.innerHTML = ''; if (users.length === 0) { userListDiv.innerHTML = 'No users found.
'; return; } users.forEach(user => { const userDiv = document.createElement('div'); userDiv.className = 'user-item'; if (selectedUserId === user.id) userDiv.classList.add('selected'); const infoDiv = document.createElement('div'); infoDiv.className = 'item-info'; const adminBadge = user.is_admin ? 'Admin' : ''; const bannedBadge = user.is_banned ? 'Banned' : ''; const ipDisplay = user.id === 0 ? '' : `Select a user above to see their notepads.
'; document.getElementById('note-list').innerHTML = 'Select a notepad to see its notes.
'; } catch (err) { console.error(err); alert('Error deleting user'); } } async function loadBannedIps() { const listDiv = document.getElementById('banned-ip-list'); try { const res = await fetch('/admin/banned_ips'); const ips = await res.json(); listDiv.innerHTML = ''; if (ips.length === 0) { listDiv.innerHTML = 'No IP addresses currently banned.
'; return; } ips.forEach(item => { const row = document.createElement('div'); row.className = 'user-item'; const info = document.createElement('div'); info.className = 'item-info'; info.innerHTML = `๐ซ ${escapeHtml(item.ip_address)} ยท Banned ${new Date(item.created_at).toLocaleString()}`; const unbanBtn = document.createElement('button'); unbanBtn.className = 'admin-button btn-success'; unbanBtn.textContent = 'Unban IP'; unbanBtn.onclick = async () => { if (confirm(`Unban IP address ${item.ip_address}?`)) { await unbanIp(item.id); } }; row.appendChild(info); row.appendChild(unbanBtn); listDiv.appendChild(row); }); } catch (err) { console.error(err); listDiv.innerHTML = 'Error loading banned IPs.
'; } } async function unbanIp(ipId) { try { const res = await fetch(`/admin/banned_ips/${ipId}`, { method: 'DELETE' }); const data = await res.json(); alert(data.message || 'IP unbanned'); loadBannedIps(); } catch (err) { console.error(err); alert('Error unbanning IP'); } } async function banUserIp(userId) { if(!confirm("Are you sure you want to ban this IP address? This will block registration and login from this IP.")) return; try { const res = await fetch(`/admin/users/${userId}/ban_ip`, { method: 'POST' }); const data = await res.json(); alert(data.message || data.error); loadBannedIps(); } catch (err) { console.error(err); alert("Error banning IP"); } } async function loadNotepads(userId, userEmail) { const notepadListDiv = document.getElementById('notepad-list'); const headerTitle = document.getElementById('notepads-header-title'); if (headerTitle) { headerTitle.textContent = `๐ Notepads for: ${userEmail}`; } notepadListDiv.innerHTML = 'Loading notepads...
'; try { const res = await fetch(`/admin/users/${userId}/notepads`); const notepads = await res.json(); notepadListDiv.innerHTML = ''; if (notepads.length === 0) { notepadListDiv.innerHTML = 'No notepads found for this user.
'; return; } notepads.forEach(notepad => { const notepadDiv = document.createElement('div'); notepadDiv.className = 'user-item'; if (selectedNotepadId === notepad.id) notepadDiv.classList.add('selected'); const badge = notepad.is_public ? '๐ Public' : '๐ Zero-Knowledge Encrypted'; const infoDiv = document.createElement('div'); infoDiv.className = 'item-info'; infoDiv.innerHTML = `${escapeHtml(notepad.name)} ${badge}Error loading notepads.
'; } } async function loadNotes(notepadId, notepadName, isPublic) { const noteListDiv = document.getElementById('note-list'); const headerTitle = document.getElementById('notes-header-title'); if (headerTitle) { headerTitle.textContent = `๐ Notes in: ${notepadName}`; } noteListDiv.innerHTML = 'Loading notes...
'; try { const res = await fetch(`/admin/notepads/${notepadId}/notes`); const notes = await res.json(); noteListDiv.innerHTML = ''; if (notes.length === 0) { noteListDiv.innerHTML = 'This notepad is empty.
'; return; } notes.forEach(note => { const noteDiv = document.createElement('div'); noteDiv.className = 'user-item'; const infoDiv = document.createElement('div'); infoDiv.className = 'item-info'; if (note.is_encrypted) { const preview = note.content ? (note.content.length > 50 ? note.content.substring(0, 50) + '...' : note.content) : ''; infoDiv.innerHTML = `Error loading notes.
'; } } async function deleteNote(noteId) { try { const res = await fetch(`/admin/notes/${noteId}`, { method: 'DELETE' }); if (res.ok) { alert('Note deleted successfully.'); } else { alert('Failed to delete note.'); } } catch (err) { console.error(err); alert('Error deleting note.'); } } async function deleteNotepad(notepadId) { try { const res = await fetch(`/admin/notepads/${notepadId}`, { method: 'DELETE' }); if (res.ok) { alert('Notepad deleted.'); document.getElementById('note-list').innerHTML = 'Select a notepad to see its notes.
'; } else { alert('Failed to delete notepad.'); } } catch (err) { console.error(err); } } function initSearch() { const searchInput = document.getElementById('user-search'); if (!searchInput) return; searchInput.addEventListener('input', (e) => { const term = e.target.value.toLowerCase().trim(); const filtered = allUsers.filter(user => (user.email && user.email.toLowerCase().includes(term)) || (user.first_name && user.first_name.toLowerCase().includes(term)) || (user.last_name && user.last_name.toLowerCase().includes(term)) || (user.last_ip && user.last_ip.toLowerCase().includes(term)) ); renderUsers(filtered); }); } function initCollapsibles() { const headers = document.querySelectorAll('.section-title'); headers.forEach(header => { header.addEventListener('click', () => { const section = header.parentElement; const content = section.querySelector('.section-content'); if (content) { content.classList.toggle('collapsed'); const icon = header.querySelector('.toggle-icon'); if (icon) { icon.textContent = content.classList.contains('collapsed') ? 'โถ' : 'โผ'; } } }); }); } function expandSection(id) { const el = document.getElementById(id); if (el) { el.classList.remove('collapsed'); const header = el.parentElement.querySelector('.section-title .toggle-icon'); if (header) header.textContent = 'โผ'; } } function escapeHtml(text) { if (text === null || text === undefined) return ''; return String(text) .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """) .replace(/'/g, "'"); }