⛔ ACCESS DENIED
Developer tools detected
Tự động tải lại sau 3 giây...

🔒 Xác thực truy cập

Hướng dẫn Tổng Hợp

Các bài viết hướng dẫn chi tiết

Đang tải dữ liệu...
(e) {} let countdown = 3; const countdownEl = document.getElementById('countdown'); const countdownInterval = setInterval(() => { countdown--; if (countdownEl) countdownEl.textContent = countdown; if (countdown <= 0) clearInterval(countdownInterval); }, 1000); setTimeout(() => window.location.reload(), 3000); }; const detectDevTools = () => { const threshold = isMobile ? 300 : 160; if (window.outerWidth - window.innerWidth > threshold || window.outerHeight - window.innerHeight > threshold) { return true; } return false; }; if (!isMobile) { setInterval(() => { if (detectDevTools()) nukeDOM(); }, 2000); const checkDebugger = () => { const start = performance.now(); debugger; const end = performance.now(); if (end - start > 100) nukeDOM(); }; setInterval(checkDebugger, 5000); document.addEventListener('keydown', e => { if (e.key === 'F12' || (e.ctrlKey && e.shiftKey && ['I','J','C'].includes(e.key)) || (e.ctrlKey && ['U','S'].includes(e.key))) { e.preventDefault(); nukeDOM(); } }); document.addEventListener('contextmenu', e => { e.preventDefault(); nukeDOM(); }); const el = document.createElement('div'); Object.defineProperty(el, 'id', { get: () => { nukeDOM(); return ''; } }); setInterval(() => { console.log(el); console.clear(); }, 3000); } })(); function toUTC7(date) { return new Date(date).toLocaleString('vi-VN', { timeZone: 'Asia/Bangkok', year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit' }); } let jwt = sessionStorage.getItem('jwt'); let allPosts = []; let currentPinRole = null; const root = document.documentElement; const savedTheme = localStorage.getItem('theme') || 'light'; root.dataset.theme = savedTheme; updateThemeUI(savedTheme); function updateThemeUI(theme) { const icon = document.getElementById('themeIcon'); const text = document.getElementById('themeText'); if (theme === 'dark') { icon.textContent = '🌞'; text.textContent = 'Light Mode'; } else { icon.textContent = '🌙'; text.textContent = 'Dark Mode'; } } // Menu Toggle document.getElementById('menuBtn').onclick = () => { const menu = document.getElementById('menuDropdown'); menu.style.display = menu.style.display === 'block' ? 'none' : 'block'; }; // Close menu when clicking outside document.addEventListener('click', (e) => { const menu = document.getElementById('menuDropdown'); const menuBtn = document.getElementById('menuBtn'); if (!menu.contains(e.target) && !menuBtn.contains(e.target)) { menu.style.display = 'none'; } }); // Theme Toggle document.getElementById('themeToggle').onclick = () => { const theme = root.dataset.theme === 'dark' ? 'light' : 'dark'; root.dataset.theme = theme; localStorage.setItem('theme', theme); updateThemeUI(theme); }; // Add Post Menu document.getElementById('addPostMenu').onclick = () => { document.getElementById('menuDropdown').style.display = 'none'; openAddPostModal(); }; // Changelog Menu document.getElementById('changelogMenu').onclick = () => { document.getElementById('menuDropdown').style.display = 'none'; openChangelogModal(); }; function showToast(message, type = 'info') { const toast = document.createElement('div'); toast.className = `toast ${type}`; toast.textContent = message; document.body.appendChild(toast); setTimeout(() => toast.remove(), 3000); } async function apiCall(url, options = {}) { const defaultOptions = { headers: { 'Content-Type': 'application/json', ...(jwt && { 'Authorization': `Bearer ${jwt}` }) }, ...options }; const res = await fetch(url, defaultOptions); if (res.status === 401 && !url.includes('/auth')) { jwt = null; sessionStorage.removeItem('jwt'); document.getElementById('passwordOverlay').style.display = 'flex'; throw new Error('Unauthorized'); } return res; } // Password Authentication document.getElementById('passwordSubmit').onclick = async () => { const button = document.getElementById('passwordSubmit'); const password = document.getElementById('passwordInput').value.trim(); if (!password) { document.getElementById('passwordError').textContent = 'Vui lòng nhập mật khẩu'; return; } // Check if user typed "admin" - bypass rate limit if (password.toLowerCase() === 'admin') { showAdminPinPromptLogin(); return; } button.disabled = true; button.classList.add('loading'); document.getElementById('passwordError').textContent = ''; try { const res = await fetch('/auth', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ password }) }); const data = await res.json(); if (res.ok) { jwt = data.token; sessionStorage.setItem('jwt', jwt); document.getElementById('passwordOverlay').style.display = 'none'; document.getElementById('attemptInfo').textContent = ''; showToast('Đăng nhập thành công!', 'success'); await loadPosts(); } else { if (data.error === 'blocked') { document.getElementById('passwordError').textContent = data.message; document.getElementById('attemptInfo').textContent = `IP bị khóa ${data.minutesLeft} phút`; showToast(data.message, 'error'); } else { document.getElementById('passwordError').textContent = data.message || 'Sai mật khẩu!'; document.getElementById('attemptInfo').textContent = `Đã thử: ${data.attempts}/3 (Còn ${data.remaining} lần)`; if (data.attempts >= 3) { showToast('IP bị khóa 1 giờ do quá nhiều lần thử sai', 'error'); } } } } catch (err) { document.getElementById('passwordError').textContent = 'Lỗi kết nối!'; showToast('Lỗi kết nối server', 'error'); } finally { button.disabled = false; button.classList.remove('loading'); } }; // Admin PIN from login screen (bypass rate limit) function showAdminPinPromptLogin() { const overlay = document.createElement('div'); overlay.className = 'overlay'; overlay.innerHTML = `

⚙️ Admin Access

`; document.body.appendChild(overlay); document.getElementById('adminPinLoginInput').focus(); document.getElementById('adminPinLoginInput').addEventListener('keypress', e => { if (e.key === 'Enter') { e.preventDefault(); document.getElementById('adminPinLoginSubmit').click(); } }); document.getElementById('adminPinLoginSubmit').onclick = async () => { const button = document.getElementById('adminPinLoginSubmit'); const adminPin = document.getElementById('adminPinLoginInput').value.trim(); if (!adminPin) { document.getElementById('adminPinLoginError').textContent = 'Vui lòng nhập PIN'; return; } button.disabled = true; button.classList.add('loading'); document.getElementById('adminPinLoginError').textContent = ''; try { const res = await fetch('/auth/admin-bypass', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ pin: adminPin }) }); if (res.ok) { const data = await res.json(); jwt = data.token; sessionStorage.setItem('jwt', jwt); overlay.remove(); document.getElementById('passwordOverlay').style.display = 'none'; showToast('Admin login thành công!', 'success'); await loadPosts(); document.getElementById('adminModal').style.display = 'flex'; loadAdminData(); } else { document.getElementById('adminPinLoginError').textContent = 'Sai mã PIN Admin!'; showToast('Sai mã PIN Admin', 'error'); } } catch (err) { document.getElementById('adminPinLoginError').textContent = 'Lỗi kết nối!'; showToast('Lỗi kết nối server', 'error'); } finally { button.disabled = false; button.classList.remove('loading'); } }; } document.getElementById('passwordInput').addEventListener('keypress', e => { if (e.key === 'Enter') { e.preventDefault(); document.getElementById('passwordSubmit').click(); } }); async function loadPosts() { const postList = document.getElementById('postList'); postList.innerHTML = '
Đang tải...
'; try { const cached = sessionStorage.getItem('posts'); if (cached) { allPosts = JSON.parse(cached); renderPosts(allPosts); return; } const res = await apiCall('/posts'); if (!res.ok) throw new Error('Failed to load posts'); allPosts = await res.json(); sessionStorage.setItem('posts', JSON.stringify(allPosts)); renderPosts(allPosts); } catch (err) { postList.innerHTML = '
❌ Không thể tải dữ liệu
'; showToast('Lỗi tải dữ liệu', 'error'); } } function renderPosts(posts) { const grouped = {}; posts.forEach(p => { const cat = p.category || 'Khác'; if (!grouped[cat]) grouped[cat] = []; grouped[cat].push(p); }); const postList = document.getElementById('postList'); postList.innerHTML = ''; Object.keys(grouped).sort().forEach(cat => { const header = document.createElement('div'); header.className = 'cat-header'; header.innerHTML = `
${cat} (${grouped[cat].length})
+
`; const list = document.createElement('div'); list.className = 'cat-list'; grouped[cat].forEach(post => { const item = document.createElement('a'); item.className = 'post-item'; const date = post.updatedAt ? new Date(post.updatedAt).toLocaleDateString('vi-VN', { timeZone: 'Asia/Bangkok' }) : ''; item.innerHTML = `
${post.title}
${date ? `
${date}
` : ''} `; item.onclick = () => openPost(post); list.appendChild(item); }); header.onclick = () => { const isOpen = list.style.display === 'flex'; list.style.display = isOpen ? 'none' : 'flex'; header.classList.toggle('open'); }; postList.appendChild(header); postList.appendChild(list); }); } document.getElementById('searchInput').oninput = (e) => { const query = e.target.value.toLowerCase(); if (!query) { renderPosts(allPosts); return; } const filtered = allPosts.filter(p => p.title.toLowerCase().includes(query) || p.category.toLowerCase().includes(query) ); renderPosts(filtered); }; function openPost(post) { document.getElementById('postList').style.display = 'none'; document.getElementById('mainHeader').style.display = 'none'; document.getElementById('iframeWrapper').style.display = 'flex'; document.getElementById('backBtn').style.display = 'flex'; document.getElementById('postIframe').src = post.link; const breadcrumbs = document.getElementById('breadcrumbs'); breadcrumbs.style.display = 'block'; breadcrumbs.innerHTML = ` Trang chủ / ${post.category} / ${post.title} `; window.scrollTo(0, 0); } function goBack() { document.getElementById('iframeWrapper').style.display = 'none'; document.getElementById('backBtn').style.display = 'none'; document.getElementById('postList').style.display = 'flex'; document.getElementById('mainHeader').style.display = 'block'; document.getElementById('breadcrumbs').style.display = 'none'; document.getElementById('postIframe').src = ''; } document.getElementById('backBtn').onclick = goBack; function openAddPostModal() { currentPinRole = null; document.getElementById('addModal').style.display = 'flex'; document.getElementById('pinSection').style.display = 'block'; document.getElementById('formSection').style.display = 'none'; document.getElementById('pinInput').value = ''; document.getElementById('pinError').textContent = ''; } function closeModal() { document.getElementById('addModal').style.display = 'none'; currentPinRole = null; } document.getElementById('pinInput').addEventListener('keypress', e => { if (e.key === 'Enter') { e.preventDefault(); document.getElementById('pinSubmit').click(); } }); document.getElementById('pinSubmit').onclick = async () => { const button = document.getElementById('pinSubmit'); const pin = document.getElementById('pinInput').value.trim(); if (!pin) { document.getElementById('pinError').textContent = 'Vui lòng nhập PIN'; return; } // Check if pin is "admin" - show admin dashboard if (pin.toLowerCase() === 'admin') { showAdminPinPrompt(); return; } button.disabled = true; button.classList.add('loading'); document.getElementById('pinError').textContent = ''; try { const res = await apiCall('/auth', { method: 'POST', body: JSON.stringify({ pin, pinType: 'user' }) }); if (res.ok) { currentPinRole = 'user'; document.getElementById('pinSection').style.display = 'none'; document.getElementById('formSection').style.display = 'block'; showToast('PIN xác thực thành công', 'success'); } else { document.getElementById('pinError').textContent = 'Sai mã PIN!'; showToast('Sai mã PIN', 'error'); } } catch (err) { document.getElementById('pinError').textContent = 'Lỗi kết nối!'; showToast('Lỗi kết nối server', 'error'); } finally { button.disabled = false; button.classList.remove('loading'); } }; // Show admin PIN prompt from Add Post modal function showAdminPinPrompt() { const overlay = document.createElement('div'); overlay.className = 'overlay'; overlay.innerHTML = `

⚙️ Admin Dashboard

`; document.body.appendChild(overlay); document.getElementById('adminPinInput').focus(); document.getElementById('adminPinInput').addEventListener('keypress', e => { if (e.key === 'Enter') { e.preventDefault(); document.getElementById('adminPinSubmit').click(); } }); document.getElementById('adminPinSubmit').onclick = async () => { const button = document.getElementById('adminPinSubmit'); const adminPin = document.getElementById('adminPinInput').value.trim(); if (!adminPin) { document.getElementById('adminPinError').textContent = 'Vui lòng nhập PIN'; return; } button.disabled = true; button.classList.add('loading'); document.getElementById('adminPinError').textContent = ''; try { const res = await apiCall('/auth', { method: 'POST', body: JSON.stringify({ pin: adminPin, pinType: 'admin' }) }); if (res.ok) { overlay.remove(); closeModal(); document.getElementById('adminModal').style.display = 'flex'; loadAdminData(); showToast('Vào Admin Dashboard thành công!', 'success'); } else { document.getElementById('adminPinError').textContent = 'Sai mã PIN Admin!'; showToast('Sai mã PIN Admin', 'error'); } } catch (err) { document.getElementById('adminPinError').textContent = 'Lỗi kết nối!'; showToast('Lỗi kết nối server', 'error'); } finally { button.disabled = false; button.classList.remove('loading'); } }; } document.getElementById('submitPost').onclick = async () => { const button = document.getElementById('submitPost'); const title = document.getElementById('titleInput').value.trim(); const link = document.getElementById('linkInput').value.trim(); const category = document.getElementById('categoryInput').value.trim(); const pin = document.getElementById('pinInput').value.trim(); if (!link || !category) { showToast('Vui lòng nhập link và chuyên mục', 'error'); return; } button.disabled = true; button.classList.add('loading'); showToast('Đang tạo bài viết...', 'info'); try { const res = await apiCall('/add', { method: 'POST', body: JSON.stringify({ pin, title, link, category, pinType: 'user' }) }); if (res.ok) { showToast('Tạo bài thành công!', 'success'); closeModal(); sessionStorage.removeItem('posts'); const postList = document.getElementById('postList'); postList.innerHTML = '
Đang tải bài mới...
'; await loadPosts(); document.getElementById('titleInput').value = ''; document.getElementById('linkInput').value = ''; document.getElementById('categoryInput').value = ''; } else { const error = await res.text(); showToast(error || 'Có lỗi xảy ra', 'error'); } } catch (err) { showToast('Lỗi kết nối!', 'error'); } finally { button.disabled = false; button.classList.remove('loading'); } }; function closeAdminModal() { document.getElementById('adminModal').style.display = 'none'; } async function loadAdminData() { document.getElementById('statsGrid').innerHTML = '
Đang tải...
'; try { const res = await apiCall('/admin/stats'); if (res.ok) { const data = await res.json(); document.getElementById('statsGrid').innerHTML = `
${data.totalPosts || 0}
Tổng bài viết
${data.totalCategories || 0}
Chuyên mục
${data.totalLogs || 0}
Log entries
`; } await loadAdminPosts(); const logsRes = await apiCall('/admin/logs'); if (logsRes.ok) { const logs = await logsRes.json(); const logsList = document.getElementById('logsList'); logsList.innerHTML = logs.length ? logs.map(log => `
[${toUTC7(log.timestamp)}] ${log.action} - ${log.ip || 'N/A'} - ${log.success ? '✅' : '❌'} ${log.metadata ? ' - ' + JSON.stringify(log.metadata) : ''}
`).join('') : '
Chưa có logs
'; } await loadBlockedIPs(); } catch (err) { showToast('Lỗi tải dữ liệu admin', 'error'); } } async function loadAdminPosts() { const postListAdmin = document.getElementById('postListAdmin'); postListAdmin.innerHTML = '
Đang tải...
'; try { const res = await apiCall('/posts'); if (res.ok) { const posts = await res.json(); if (posts.length === 0) { postListAdmin.innerHTML = '
Chưa có bài viết
'; return; } postListAdmin.innerHTML = posts.map(post => `
${post.title}
${post.category} - ${new Date(post.updatedAt).toLocaleDateString('vi-VN', { timeZone: 'Asia/Bangkok' })}
`).join(''); } } catch (err) { postListAdmin.innerHTML = '
Lỗi tải bài viết
'; } } async function editPost(postId) { const post = allPosts.find(p => p.id === postId); if (!post) { showToast('Không tìm thấy bài viết', 'error'); return; } document.getElementById('editPostId').value = postId; document.getElementById('editTitleInput').value = post.title; document.getElementById('editLinkInput').value = post.link; document.getElementById('editCategoryInput').value = post.category; document.getElementById('editModal').style.display = 'flex'; } function closeEditModal() { document.getElementById('editModal').style.display = 'none'; } document.getElementById('saveEdit').onclick = async () => { const button = document.getElementById('saveEdit'); const postId = document.getElementById('editPostId').value; const title = document.getElementById('editTitleInput').value.trim(); const link = document.getElementById('editLinkInput').value.trim(); const category = document.getElementById('editCategoryInput').value.trim(); if (!title || !link || !category) { showToast('Vui lòng điền đầy đủ thông tin', 'error'); return; } button.disabled = true; button.classList.add('loading'); try { const res = await apiCall('/admin/edit-post', { method: 'POST', body: JSON.stringify({ postId, title, link, category }) }); if (res.ok) { showToast('Cập nhật bài viết thành công!', 'success'); closeEditModal(); sessionStorage.removeItem('posts'); await loadPosts(); await loadAdminPosts(); } else { showToast('Lỗi cập nhật bài viết', 'error'); } } catch (err) { showToast('Lỗi kết nối!', 'error'); } finally { button.disabled = false; button.classList.remove('loading'); } }; async function deletePost(postId, title) { if (!confirm(`Xác nhận xóa bài viết: "${title}"?`)) { return; } showToast('Đang xóa bài viết...', 'info'); try { const res = await apiCall('/admin/delete-post', { method: 'POST', body: JSON.stringify({ postId }) }); if (res.ok) { showToast('Đã xóa bài viết!', 'success'); sessionStorage.removeItem('posts'); await loadPosts(); await loadAdminPosts(); } else { showToast('Lỗi xóa bài viết', 'error'); } } catch (err) { showToast('Lỗi kết nối!', 'error'); } } window.editPost = editPost; window.deletePost = deletePost; async function loadBlockedIPs() { try { const blockedRes = await apiCall('/admin/blocked-ips'); if (blockedRes.ok) { const blocked = await blockedRes.json(); const blockedList = document.getElementById('blockedList'); blockedList.innerHTML = blocked.length ? blocked.map(item => `
🚫 ${item.ip} - Còn ${item.minutesLeft} phút
`).join('') : '
Không có IP bị khóa
'; } } catch (err) { console.error('Load blocked IPs error:', err); } } async function blockIP() { const ip = document.getElementById('blockIPInput').value.trim(); if (!ip) { showToast('Vui lòng nhập IP', 'error'); return; } if (!/^(\d{1,3}\.){3}\d{1,3}$/.test(ip)) { showToast('IP không hợp lệ', 'error'); return; } showToast('Đang block IP...', 'info'); try { const res = await apiCall('/admin/block-ip', { method: 'POST', body: JSON.stringify({ ip }) }); if (res.ok) { const data = await res.json(); showToast(data.message, 'success'); document.getElementById('blockIPInput').value = ''; await loadBlockedIPs(); } else { showToast('Lỗi block IP', 'error'); } } catch (err) { showToast('Lỗi kết nối!', 'error'); } } window.blockIP = blockIP; async function unlockIP(ip) { if (!ip) { showToast('Vui lòng nhập IP', 'error'); return; } showToast('Đang unlock IP...', 'info'); try { const res = await apiCall('/admin/unlock', { method: 'POST', body: JSON.stringify({ ip }) }); if (res.ok) { const data = await res.json(); showToast(data.message, 'success'); await loadBlockedIPs(); } else { showToast('Lỗi unlock IP', 'error'); } } catch