dptxa-proxy / src /pages /index.astro
TXAVLOG
Fix third-party cookie blocking and infinite reload loops inside iframes
a9cf72a
Raw
History Blame Contribute Delete
40.2 kB
---
import Layout from '../layouts/Layout.astro';
import { txasupabase } from '../lib/txasupabase.js';
// Kiểm tra xem đã có cookie duyệt chưa từ phía máy chủ để hiển thị nút thích hợp nếu cần
const isApprovedServer = Astro.cookies.get('zalo_access_approved')?.value === 'true';
---
<Layout
title="DPTXA - Động Phim TXA | Nền Tảng Xem Phim Trực Tuyến Đỉnh Cao"
description="DPTXA - Trải nghiệm điện ảnh đỉnh cao, không quảng cáo, đường truyền siêu tốc và cộng đồng Zalo lớn mạnh."
slug="/"
>
<!-- Ambient Premium Glows -->
<div class="absolute inset-0 overflow-hidden pointer-events-none -z-10">
<div class="absolute top-0 left-1/4 w-[500px] h-[500px] bg-primary/10 rounded-full blur-[150px] animate-pulse"></div>
<div class="absolute bottom-10 right-1/4 w-[600px] h-[600px] bg-purple-900/10 rounded-full blur-[180px]"></div>
</div>
<div class="relative min-h-[90vh] flex flex-col justify-center items-center px-4 py-16 text-center max-w-7xl mx-auto z-10">
<!-- Badge -->
<div class="inline-flex items-center gap-2 px-4 py-2 rounded-full bg-white/[0.03] border border-white/10 mb-8 backdrop-blur-md animate-fade-in shadow-[0_0_20px_rgba(255,255,255,0.02)]">
<span class="flex h-2 w-2 rounded-full bg-primary animate-ping"></span>
<span class="text-[10px] font-black uppercase tracking-[0.2em] text-gray-300">Nền tảng điện ảnh độc quyền</span>
</div>
<!-- Title & Slogan -->
<h1 class="text-4xl md:text-7xl lg:text-8xl font-black tracking-tighter text-white mb-6 leading-none max-w-5xl">
KHÁM PHÁ THẾ GIỚI <br class="hidden md:inline" />
<span class="bg-gradient-to-r from-primary via-purple-400 to-indigo-400 bg-clip-text text-transparent drop-shadow-[0_0_30px_rgba(124,58,237,0.3)] animate-pulse">ĐIỆN ẢNH TUYỆT MỸ</span>
</h1>
<p class="text-sm md:text-xl font-medium text-gray-400 max-w-2xl mb-12 leading-relaxed">
Chào mừng bạn đến với <span class="text-white font-black">DPTXA</span>. Trải nghiệm xem phim Full HD tốc độ cao không quảng cáo, cùng tham gia cộng đồng mọt phim chất lượng cao.
</p>
<!-- Main Action Button -->
<div class="flex flex-col items-center justify-center gap-4 mb-24 min-h-[90px]" id="gatekeeper-button-container">
<!-- Mặc định khi tải trang sẽ hiển thị nút "Khám phá ngay" -->
<button
id="explore-btn"
class="group relative inline-flex items-center gap-3 px-10 py-5 rounded-2xl bg-gradient-to-r from-primary to-indigo-600 text-white font-black text-sm uppercase tracking-[0.2em] transition-all hover:scale-105 active:scale-95 shadow-[0_0_40px_rgba(124,58,237,0.4)] border border-primary/20 cursor-pointer overflow-hidden"
>
<span class="absolute inset-0 w-full h-full bg-gradient-to-r from-transparent via-white/10 to-transparent -translate-x-full group-hover:translate-x-full transition-transform duration-1000"></span>
<i class="fas fa-play text-xs transition-transform group-hover:translate-x-1"></i>
<span>Khám phá ngay</span>
</button>
</div>
<!-- Core Features Grid -->
<div class="w-full">
<div class="flex items-center justify-center gap-3 mb-12">
<span class="h-1 w-12 bg-gradient-to-r from-transparent to-primary"></span>
<h2 class="text-[11px] font-black uppercase tracking-[0.4em] text-gray-500">Đặc quyền tại DPTXA</h2>
<span class="h-1 w-12 bg-gradient-to-l from-transparent to-primary"></span>
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 max-w-5xl mx-auto text-left">
<!-- Feature 1 -->
<div class="glass-card group hover:border-primary/30 transition-all duration-500 rounded-3xl p-8 border border-white/5 bg-white/[0.02] backdrop-blur-xl relative overflow-hidden">
<div class="absolute -right-8 -top-8 w-24 h-24 bg-primary/10 rounded-full blur-2xl group-hover:scale-120 transition-transform duration-700"></div>
<div class="h-12 w-12 rounded-2xl bg-primary/10 border border-primary/20 flex items-center justify-center mb-6 text-primary shadow-[0_0_15px_rgba(124,58,237,0.2)]">
<i class="fas fa-bolt text-lg"></i>
</div>
<h3 class="text-lg font-black text-white mb-2">Đường truyền cực hạn</h3>
<p class="text-xs text-gray-400 leading-relaxed font-medium">Băng thông không giới hạn, tối ưu hóa phát luồng HLS cho tốc độ tải phim tức thì, không giật lag.</p>
</div>
<!-- Feature 2 -->
<div class="glass-card group hover:border-primary/30 transition-all duration-500 rounded-3xl p-8 border border-white/5 bg-white/[0.02] backdrop-blur-xl relative overflow-hidden">
<div class="absolute -right-8 -top-8 w-24 h-24 bg-purple-900/10 rounded-full blur-2xl group-hover:scale-120 transition-transform duration-700"></div>
<div class="h-12 w-12 rounded-2xl bg-purple-500/10 border border-purple-500/20 flex items-center justify-center mb-6 text-purple-400 shadow-[0_0_15px_rgba(168,85,247,0.2)]">
<i class="fas fa-ban text-lg"></i>
</div>
<h3 class="text-lg font-black text-white mb-2">Không Quảng Cáo</h3>
<p class="text-xs text-gray-400 leading-relaxed font-medium">Nói không với các quảng cáo pop-up, banner phản cảm che mắt người xem. Trải nghiệm xem phim thuần khiết nhất.</p>
</div>
<!-- Feature 3 -->
<div class="glass-card group hover:border-primary/30 transition-all duration-500 rounded-3xl p-8 border border-white/5 bg-white/[0.02] backdrop-blur-xl relative overflow-hidden">
<div class="absolute -right-8 -top-8 w-24 h-24 bg-indigo-900/10 rounded-full blur-2xl group-hover:scale-120 transition-transform duration-700"></div>
<div class="h-12 w-12 rounded-2xl bg-indigo-500/10 border border-indigo-500/20 flex items-center justify-center mb-6 text-indigo-400 shadow-[0_0_15px_rgba(99,102,241,0.2)]">
<i class="fas fa-users text-lg"></i>
</div>
<h3 class="text-lg font-black text-white mb-2">Cộng đồng Giao lưu</h3>
<p class="text-xs text-gray-400 leading-relaxed font-medium">Kết nối trực tiếp với hàng ngàn mọt phim chất lượng cao qua nhóm Zalo, thảo luận những tập phim mới nhất mỗi ngày.</p>
</div>
</div>
</div>
</div>
<!-- TXAModal markup for Zalo verification -->
<div id="zalo-modal" class="txamodal-container hidden">
<div class="txamodal-overlay"></div>
<div class="txamodal-wrapper max-w-md w-full">
<!-- Content wrapper -->
<div class="glass-card border border-white/10 rounded-[32px] bg-[#12141d]/90 p-8 shadow-2xl relative overflow-hidden backdrop-blur-2xl">
<!-- Ambient glow inside modal -->
<div class="absolute -left-20 -top-20 w-40 h-40 bg-primary/10 rounded-full blur-2xl"></div>
<!-- Close Button -->
<button id="modal-close-btn" class="absolute top-6 right-6 h-8 w-8 rounded-full bg-white/5 hover:bg-white/10 flex items-center justify-center text-gray-400 hover:text-white transition-all cursor-pointer">
<i class="fas fa-times text-sm"></i>
</button>
<div class="text-center relative z-10">
<div class="h-12 w-12 rounded-2xl bg-primary/20 border border-primary/30 flex items-center justify-center mx-auto mb-6 text-primary shadow-[0_0_20px_rgba(124,58,237,0.2)]">
<i class="fas fa-users text-xl"></i>
</div>
<h3 class="text-xl font-black text-white mb-2">Tham gia Nhóm Zalo</h3>
<p class="text-xs text-gray-400 mb-6 leading-relaxed">Vui lòng quét mã QR bên dưới để tham gia nhóm Zalo chính thức của DPTXA và nhập tên tài khoản của bạn để Admin duyệt quyền.</p>
<!-- QR Code Mockup (Styled Neon Border) -->
<div class="relative w-48 h-48 mx-auto mb-6 bg-white/5 rounded-3xl border border-white/10 p-4 flex items-center justify-center group overflow-hidden shadow-inner">
<!-- Neon Running Border effect -->
<div class="absolute inset-0 border-2 border-primary/20 rounded-3xl pointer-events-none"></div>
<!-- Custom Animated Zalo QR Code -->
<div id="zalo-qr-canvas" class="w-full h-full flex items-center justify-center p-2"></div>
<script is:inline src="https://unpkg.com/qr-code-styling@1.5.0/lib/qr-code-styling.js"></script>
<script is:inline>
document.addEventListener('astro:page-load', () => {
const qrContainer = document.getElementById("zalo-qr-canvas");
if (qrContainer && !qrContainer.hasChildNodes()) {
const qrCode = new QRCodeStyling({
width: 140,
height: 140,
type: "svg",
data: "https://zalo.me/g/nc4aaozaxnr5fszvnxdb",
image: "/logo-icon.png",
dotsOptions: {
color: "#a78bfa", // violet-400
type: "rounded"
},
backgroundOptions: {
color: "transparent",
},
imageOptions: {
crossOrigin: "anonymous",
margin: 4
},
cornersSquareOptions: {
color: "#60a5fa", // blue-400
type: "extra-rounded"
},
cornersDotOptions: {
color: "#a78bfa",
type: "dot"
}
});
qrCode.append(qrContainer);
}
});
</script>
<!-- Hover overlay to scan directly via click link -->
<a
href="https://zalo.me/g/nc4aaozaxnr5fszvnxdb"
target="_blank"
class="absolute inset-0 bg-[#0068FF]/95 flex flex-col items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity duration-300 rounded-3xl text-white font-black text-xs gap-2"
>
<i class="fas fa-external-link-alt text-lg animate-bounce"></i>
<span>BẤM ĐỂ VÀO NHÓM</span>
<span class="text-[9px] font-bold text-white/70 uppercase">zalo.me/g/nc4aaozaxn...</span>
</a>
</div>
<!-- Zalo Direct Link Button for mobile -->
<a
href="https://zalo.me/g/nc4aaozaxnr5fszvnxdb"
target="_blank"
class="inline-flex items-center gap-2 text-xs font-black text-[#0068FF] hover:underline mb-6 uppercase tracking-wider"
>
<i class="fas fa-users-viewfinder"></i> Hoặc nhấn vào đây để vào nhóm
</a>
<!-- Access form -->
<div class="text-left space-y-4">
<div>
<label for="zalo-nickname-input" class="block text-[10px] font-black uppercase tracking-wider text-gray-500 mb-2">Nick Zalo đã dùng tham gia nhóm</label>
<input
type="text"
id="zalo-nickname-input"
placeholder="Ví dụ: Nguyễn Văn A..."
class="w-full bg-white/[0.03] border border-white/10 rounded-xl px-4 py-3 text-xs text-white focus:outline-none focus:border-primary/50 transition-all font-bold"
/>
</div>
<!-- Submit button inside modal -->
<button
id="submit-access-btn"
disabled
class="w-full relative flex items-center justify-center gap-2 py-3.5 rounded-xl bg-white/5 border border-white/5 text-gray-500 font-black text-xs uppercase tracking-widest transition-all cursor-not-allowed"
>
<i class="fas fa-paper-plane" id="submit-icon"></i>
<span id="submit-text">Gửi yêu cầu duyệt</span>
</button>
</div>
</div>
</div>
</div>
</div>
</Layout>
<script is:inline>
// MD5 Hash helper function (Pure JS implementation)
function md5(string) {
function md5_RotateLeft(lValue, iShiftBits) {
return (lValue<<iShiftBits) | (lValue>>>(32-iShiftBits));
}
function md5_AddUnsigned(lX,lY) {
var lX4,lY4,lX8,lY8,lResult;
lX8 = (lX & 0x80000000);
lY8 = (lY & 0x80000000);
lX4 = (lX & 0x40000000);
lY4 = (lY & 0x40000000);
lResult = (lX & 0x3FFFFFFF)+(lY & 0x3FFFFFFF);
if (lX4 & lY4) {
return (lResult ^ 0x80000000 ^ lX8 ^ lY8);
}
if (lX4 | lY4) {
if (lResult & 0x40000000) {
return (lResult ^ 0xC0000000 ^ lX8 ^ lY8);
} else {
return (lResult ^ 0x40000000 ^ lX8 ^ lY8);
}
} else {
return (lResult ^ lX8 ^ lY8);
}
}
function md5_F(x,y,z) { return (x & y) | ((~x) & z); }
function md5_G(x,y,z) { return (x & z) | (y & (~z)); }
function md5_H(x,y,z) { return (x ^ y ^ z); }
function md5_I(x,y,z) { return (y ^ (x | (~z))); }
function md5_FF(a,b,c,d,x,s,ac) {
a = md5_AddUnsigned(a, md5_AddUnsigned(md5_AddUnsigned(md5_F(b,c,d), x), ac));
return md5_AddUnsigned(md5_RotateLeft(a, s), b);
}
function md5_GG(a,b,c,d,x,s,ac) {
a = md5_AddUnsigned(a, md5_AddUnsigned(md5_AddUnsigned(md5_G(b,c,d), x), ac));
return md5_AddUnsigned(md5_RotateLeft(a, s), b);
}
function md5_HH(a,b,c,d,x,s,ac) {
a = md5_AddUnsigned(a, md5_AddUnsigned(md5_AddUnsigned(md5_H(b,c,d), x), ac));
return md5_AddUnsigned(md5_RotateLeft(a, s), b);
}
function md5_II(a,b,c,d,x,s,ac) {
a = md5_AddUnsigned(a, md5_AddUnsigned(md5_AddUnsigned(md5_I(b,c,d), x), ac));
return md5_AddUnsigned(md5_RotateLeft(a, s), b);
}
function md5_ConvertToWordArray(string) {
var lWordCount;
var lMessageLength = string.length;
var lNumberOfWords_temp1=lMessageLength + 8;
var lNumberOfWords_temp2=(lNumberOfWords_temp1 - (lNumberOfWords_temp1 % 64)) / 64;
var lNumberOfWords = (lNumberOfWords_temp2 + 1) * 16;
var lWordArray=Array(lNumberOfWords-1);
var lBytePosition = 0;
var lByteCount = 0;
while ( lByteCount < lMessageLength ) {
lWordCount = (lByteCount - (lByteCount % 4)) / 4;
lBytePosition = (lByteCount % 4) * 8;
lWordArray[lWordCount] = (lWordArray[lWordCount] | (string.charCodeAt(lByteCount)<<lBytePosition));
lByteCount++;
}
lWordCount = (lByteCount - (lByteCount % 4)) / 4;
lBytePosition = (lByteCount % 4) * 8;
lWordArray[lWordCount] = lWordArray[lWordCount] | (0x80<<lBytePosition);
lWordArray[lNumberOfWords-2] = lMessageLength << 3;
lWordArray[lNumberOfWords-1] = lMessageLength >>> 29;
return lWordArray;
}
function md5_WordToHex(lValue) {
var WordToHexValue="",WordToHexValue_temp="",lByte,lCount;
for (lCount = 0;lCount<=3;lCount++) {
lByte = (lValue>>>(lCount*8)) & 255;
WordToHexValue_temp = "0" + lByte.toString(16);
WordToHexValue = WordToHexValue + WordToHexValue_temp.substr(WordToHexValue_temp.length-2,2);
}
return WordToHexValue;
}
function md5_Utf8Encode(string) {
string = string.replace(/\r\n/g,"\n");
var utftext = "";
for (var n = 0; n < string.length; n++) {
var c = string.charCodeAt(n);
if (c < 128) {
utftext += String.fromCharCode(c);
} else if((c > 127) && (c < 2048)) {
utftext += String.fromCharCode((c >> 6) | 192);
utftext += String.fromCharCode((c & 63) | 128);
} else {
utftext += String.fromCharCode((c >> 12) | 224);
utftext += String.fromCharCode(((c >> 6) & 63) | 128);
utftext += String.fromCharCode((c & 63) | 128);
}
}
return utftext;
}
var x=Array();
var k,AA,BB,CC,DD,a,b,c,d;
var S11=7, S12=12, S13=17, S14=22;
var S21=5, S22=9 , S23=14, S24=20;
var S31=4, S32=11, S33=16, S34=23;
var S41=6, S42=10, S43=15, S44=21;
string = md5_Utf8Encode(string);
x = md5_ConvertToWordArray(string);
a = 0x67452301; b = 0xEFCDAB89; c = 0x98BADCFE; d = 0x10325476;
for (k=0;k<x.length;k+=16) {
AA=a; BB=b; CC=c; DD=d;
a=md5_FF(a,b,c,d,x[k+0], S11,0xD76AA478); d=md5_FF(d,a,b,c,x[k+1], S12,0xE8C7B756); c=md5_FF(c,d,a,b,x[k+2], S13,0x242070DB); b=md5_FF(b,c,d,a,x[k+3], S14,0xC1BDCEEE);
a=md5_FF(a,b,c,d,x[k+4], S11,0xF57C0FAF); d=md5_FF(d,a,b,c,x[k+5], S12,0x4787C62A); c=md5_FF(c,d,a,b,x[k+6], S13,0xA8304613); b=md5_FF(b,c,d,a,x[k+7], S14,0xFD469501);
a=md5_FF(a,b,c,d,x[k+8], S11,0x698098D8); d=md5_FF(d,a,b,c,x[k+9], S12,0x8B44F7AF); c=md5_FF(c,d,a,b,x[k+10],S13,0xFFFF5BB1); b=md5_FF(b,c,d,a,x[k+11],S14,0x895CD7BE);
a=md5_FF(a,b,c,d,x[k+12],S11,0x6B901122); d=md5_FF(d,a,b,c,x[k+13],S12,0xFD987193); c=md5_FF(c,d,a,b,x[k+14],S13,0xA679438E); b=md5_FF(b,c,d,a,x[k+15],S14,0x49B40821);
a=md5_GG(a,b,c,d,x[k+1], S21,0xF61E2562); d=md5_GG(d,a,b,c,x[k+6], S22,0xC040B340); c=md5_GG(c,d,a,b,x[k+11],S23,0x265E5A51); b=md5_GG(b,c,d,a,x[k+0], S24,0xE9B6C7AA);
a=md5_GG(a,b,c,d,x[k+5], S21,0xD62F105D); d=md5_GG(d,a,b,c,x[k+10],S22,0x02441453); c=md5_GG(c,d,a,b,x[k+15],S23,0xD8A1E681); b=md5_GG(b,c,d,a,x[k+4], S24,0xE7D3FBC8);
a=md5_GG(a,b,c,d,x[k+9], S21,0x21E1CDE6); d=md5_GG(d,a,b,c,x[k+14],S22,0xC33707D6); c=md5_GG(c,d,a,b,x[k+3], S23,0xF4D50D87); b=md5_GG(b,c,d,a,x[k+8], S24,0x455A14ED);
a=md5_GG(a,b,c,d,x[k+13],S21,0xA9E3E905); d=md5_GG(d,a,b,c,x[k+2], S22,0xFCEFA3F8); c=md5_GG(c,d,a,b,x[k+7], S23,0x676F02D9); b=md5_GG(b,c,d,a,x[k+12],S24,0x8D2A4C8A);
a=md5_HH(a,b,c,d,x[k+5], S31,0xFFFA3942); d=md5_HH(d,a,b,c,x[k+8], S32,0x8771F681); c=md5_HH(c,d,a,b,x[k+11],S33,0x6D9D6122); b=md5_HH(b,c,d,a,x[k+14],S34,0xFDE5380C);
a=md5_HH(a,b,c,d,x[k+1], S31,0xA4BEEA44); d=md5_HH(d,a,b,c,x[k+4], S32,0x4BDECFA9); c=md5_HH(c,d,a,b,x[k+7], S33,0xF6BB4B60); b=md5_HH(b,c,d,a,x[k+10],S34,0xBEBFBC70);
a=md5_HH(a,b,c,d,x[k+13],S31,0x289B7EC6); d=md5_HH(d,a,b,c,x[k+0], S32,0xEAA127FA); c=md5_HH(c,d,a,b,x[k+3], S33,0xD4EF3085); b=md5_HH(b,c,d,a,x[k+6], S34,0x04881D05);
a=md5_HH(a,b,c,d,x[k+9], S31,0xD9D4D039); d=md5_HH(d,a,b,c,x[k+12],S32,0xE6DB99E5); c=md5_HH(c,d,a,b,x[k+15],S33,0x1FA27CF8); b=md5_HH(b,c,d,a,x[k+2], S34,0xC4AC5665);
a=md5_II(a,b,c,d,x[k+0], S41,0xF4292244); d=md5_II(d,a,b,c,x[k+7], S42,0x432AFF97); c=md5_II(c,d,a,b,x[k+14],S43,0xAB9423A7); b=md5_II(b,c,d,a,x[k+5], S44,0xFC93A039);
a=md5_II(a,b,c,d,x[k+12],S41,0x655B59C3); d=md5_II(d,a,b,c,x[k+3], S42,0x8F0CCC92); c=md5_II(c,d,a,b,x[k+10],S43,0xFFEFF47D); b=md5_II(b,c,d,a,x[k+1], S44,0x85845DD1);
a=md5_II(a,b,c,d,x[k+8], S41,0x6FA87E4F); d=md5_II(d,a,b,c,x[k+15],S42,0xFE2CE6E0); c=md5_II(c,d,a,b,x[k+6], S43,0xA3014314); b=md5_II(b,c,d,a,x[k+13],S44,0x4E0811A1);
a=md5_II(a,b,c,d,x[k+4], S41,0xF7537E82); d=md5_II(d,a,b,c,x[k+11],S42,0xBD3AF235); c=md5_II(c,d,a,b,x[k+2], S43,0x2AD7D2BB); b=md5_II(b,c,d,a,x[k+9], S44,0xEB86D391);
a=md5_AddUnsigned(a,AA); b=md5_AddUnsigned(b,BB); c=md5_AddUnsigned(c,CC); d=md5_AddUnsigned(d,DD);
}
var temp = md5_WordToHex(a)+md5_WordToHex(b)+md5_WordToHex(c)+md5_WordToHex(d);
return temp.toLowerCase();
}
// Generate unique browser token with advanced fingerprinting and IP location
async function generateBrowserToken() {
const userAgent = navigator.userAgent;
const CRYPTO_KEY = "phimtxadinhvaikk";
const TxaCipher = {
encrypt(text, key) {
try {
const utf8Text = unescape(encodeURIComponent(text));
let encrypted = '';
for (let i = 0; i < utf8Text.length; i++) {
const charCode = utf8Text.charCodeAt(i);
const keyChar = key.charCodeAt(i % key.length);
const encryptedChar = charCode ^ keyChar;
encrypted += String.fromCharCode(encryptedChar);
}
const b64 = btoa(encrypted);
return b64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
} catch (e) {
console.error("Lỗi mã hóa:", e);
return null;
}
}
};
try {
const res = await fetch('https://ipinfo.io/json');
if (!res.ok) throw new Error('Ipinfo failed');
const data = await res.json();
const rawInfo = `${userAgent} - IP: ${data.ip} - ${data.city || ''} - ${data.region || ''} - ${data.country || ''}`;
const encryptedToken = TxaCipher.encrypt(rawInfo, CRYPTO_KEY);
if (encryptedToken) {
return `tk_${encryptedToken}`;
}
const hash = md5(rawInfo);
return `tk_${hash}`;
} catch (err) {
console.warn('ipinfo.io failed, attempting fallback to api64.ipify.org...', err);
try {
const res = await fetch('https://api64.ipify.org?format=json');
if (!res.ok) throw new Error('Ipify failed');
const data = await res.json();
const ip = data.ip;
const rawInfo = `${userAgent} - IP: ${ip}`;
const encryptedToken = TxaCipher.encrypt(rawInfo, CRYPTO_KEY);
if (encryptedToken) {
return `tk_${encryptedToken}`;
}
const uaHash = md5(userAgent);
return `txanot-${ip}-${uaHash}`;
} catch (err2) {
console.error('All IP fetches failed, local fallback...', err2);
const rawInfo = `${userAgent} - Local Fallback`;
const encryptedToken = TxaCipher.encrypt(rawInfo, CRYPTO_KEY);
if (encryptedToken) {
return `tk_${encryptedToken}`;
}
const uaHash = md5(userAgent);
const rand = Math.random().toString(36).substring(2, 10);
return `txanot-noip-${uaHash}-${rand}`;
}
}
}
async function initGatekeeper() {
// Pop up toast if kicked out previously
if (localStorage.getItem('kicked_out') === 'true') {
localStorage.removeItem('kicked_out');
if (window.txatoast) {
window.txatoast.error('❌ Thiết bị và quyền truy cập Zalo của bạn đã bị Admin xóa vĩnh viễn!');
}
} else if (localStorage.getItem('kicked_out_rejected') === 'true') {
localStorage.removeItem('kicked_out_rejected');
if (window.txatoast) {
window.txatoast.error('❌ Quyền truy cập Zalo của thiết bị này đã bị từ chối bởi Admin!');
}
}
const exploreBtn = document.getElementById('explore-btn');
const zaloModal = document.getElementById('zalo-modal');
const modalCloseBtn = document.getElementById('modal-close-btn');
const zaloNicknameInput = document.getElementById('zalo-nickname-input');
const submitAccessBtn = document.getElementById('submit-access-btn');
const submitIcon = document.getElementById('submit-icon');
const submitText = document.getElementById('submit-text');
const container = document.getElementById('gatekeeper-button-container');
if (!exploreBtn || !zaloModal || !modalCloseBtn || !zaloNicknameInput || !submitAccessBtn || !container) return;
// --- ADMIN ABSOLUTE BYPASS ---
const isAdmin = document.cookie.split('; ').some(row => row.trim().startsWith('auth_token='));
if (isAdmin) {
document.cookie = "zalo_access_approved=true; path=/; max-age=" + (365 * 24 * 60 * 60) + "; SameSite=None; Secure";
localStorage.setItem('zalo_access_status', 'approved');
container.innerHTML = `
<a
href="/tphim"
id="approved-btn"
class="group relative inline-flex items-center gap-3 px-10 py-5 rounded-2xl bg-gradient-to-r from-violet-600 to-indigo-600 text-white font-black text-sm uppercase tracking-[0.2em] transition-all hover:scale-105 active:scale-95 shadow-[0_0_40px_rgba(124,58,237,0.5)] border border-violet-400/20 cursor-pointer overflow-hidden"
>
<span class="absolute inset-0 w-full h-full bg-gradient-to-r from-transparent via-white/20 to-transparent -translate-x-full group-hover:translate-x-full transition-transform duration-1000"></span>
<i class="fas fa-user-shield text-xs animate-bounce"></i>
<span>Vào trang web (Admin)</span>
</a>
<p class="text-[10px] text-violet-400 font-bold uppercase tracking-wider mt-2 animate-pulse">
Xác thực đặc quyền Admin thành công! Hệ thống miễn kiểm duyệt.
</p>
`;
return;
}
// 1. Quản lý sinh token định danh duy nhất cho trình duyệt (Browser Fingerprint / Unique Token)
let browserToken = localStorage.getItem('zalo_browser_token');
// Hàm thực hiện setup logic sau khi đã có browserToken
async function proceedWithSetup() {
// 2. Hàm set cookie phê duyệt
function setApprovedCookie() {
document.cookie = "zalo_access_approved=true; path=/; max-age=" + (365 * 24 * 60 * 60) + "; SameSite=None; Secure";
localStorage.setItem('zalo_access_status', 'approved');
}
// 3. Hàm tạo nút "Đang chờ quyền duyệt" (Amber Glow, Pulsing, Unclickable)
function renderPendingBtn() {
container.innerHTML = `
<button
id="pending-btn"
disabled
class="inline-flex items-center gap-3 px-10 py-5 rounded-2xl bg-amber-500/10 text-amber-500 font-black text-sm uppercase tracking-[0.2em] border border-amber-500/20 shadow-[0_0_30px_rgba(245,158,11,0.1)] cursor-not-allowed animate-pulse"
>
<i class="fas fa-hourglass-half"></i>
<span>Đang chờ duyệt...</span>
</button>
<p class="text-[10px] text-gray-500 font-bold uppercase tracking-wider mt-2">
Nick Zalo đang chờ Admin xác nhận. Giao diện sẽ tự động cập nhật!
</p>
`;
}
// 4. Hàm tạo nút "Vào trang web" (Neon Green Glow, Clickable)
function renderApprovedBtn() {
container.innerHTML = `
<a
href="/tphim"
id="approved-btn"
class="group relative inline-flex items-center gap-3 px-10 py-5 rounded-2xl bg-emerald-500 text-black font-black text-sm uppercase tracking-[0.2em] transition-all hover:scale-105 active:scale-95 shadow-[0_0_40px_rgba(16,185,129,0.5)] border border-emerald-400/20 cursor-pointer overflow-hidden"
>
<span class="absolute inset-0 w-full h-full bg-gradient-to-r from-transparent via-white/20 to-transparent -translate-x-full group-hover:translate-x-full transition-transform duration-1000"></span>
<i class="fas fa-door-open text-xs"></i>
<span>Vào trang web</span>
</a>
<p class="text-[10px] text-emerald-400 font-bold uppercase tracking-wider mt-2 animate-bounce">
Chúc mừng! Yêu cầu của bạn đã được phê duyệt thành công!
</p>
`;
}
// 5. Hàm kiểm tra trạng thái duyệt thực tế từ database
async function checkApprovalStatus() {
try {
const res = await fetch(`/api/access/status?token=${browserToken}`);
if (!res.ok) return;
const data = await res.json();
if (data && data.status) {
const currentStatus = data.status;
const oldStatus = localStorage.getItem('zalo_access_status');
if (currentStatus === 'approved') {
setApprovedCookie();
renderApprovedBtn();
if (zaloModal) zaloModal.style.display = 'none';
if (oldStatus !== 'approved') {
if (window.txatoast) {
window.txatoast.success('Yêu cầu truy cập của bạn đã được phê duyệt! Chào mừng tới DPTXA!');
}
}
return true; // Dừng polling
} else if (currentStatus === 'pending') {
localStorage.setItem('zalo_access_status', 'pending');
document.cookie = "zalo_access_approved=; path=/; expires=Thu, 01 Jan 1970 00:00:00 UTC;";
if (!document.getElementById('pending-btn')) {
renderPendingBtn();
}
} else if (currentStatus === 'rejected') {
// Admin đã từ chối yêu cầu này
localStorage.removeItem('zalo_browser_token');
localStorage.removeItem('zalo_access_status');
document.cookie = "zalo_access_approved=; path=/; expires=Thu, 01 Jan 1970 00:00:00 UTC;";
// Đưa nút bấm quay về trạng thái ban đầu để người dùng có thể gửi lại
container.innerHTML = `
<button
id="explore-btn"
class="group relative inline-flex items-center gap-3 px-10 py-5 rounded-2xl bg-gradient-to-r from-primary to-indigo-600 text-white font-black text-sm uppercase tracking-[0.2em] transition-all hover:scale-105 active:scale-95 shadow-[0_0_40px_rgba(124,58,237,0.4)] border border-primary/20 cursor-pointer overflow-hidden"
>
<span class="absolute inset-0 w-full h-full bg-gradient-to-r from-transparent via-white/10 to-transparent -translate-x-full group-hover:translate-x-full transition-transform duration-1000"></span>
<i class="fas fa-play text-xs transition-transform group-hover:translate-x-1"></i>
<span>Khám phá ngay</span>
</button>
`;
const newExploreBtn = document.getElementById('explore-btn');
if (newExploreBtn) {
newExploreBtn.onclick = () => {
if (zaloModal) zaloModal.style.display = 'flex';
};
}
if (window.txatoast) {
window.txatoast.error('❌ Yêu cầu truy cập Zalo của bạn đã bị từ chối bởi Admin!');
}
return true; // Dừng polling
} else if (currentStatus === 'not_found') {
// Admin đã xóa vĩnh viễn yêu cầu này khỏi DB
localStorage.removeItem('zalo_browser_token');
localStorage.removeItem('zalo_access_status');
document.cookie = "zalo_access_approved=; path=/; expires=Thu, 01 Jan 1970 00:00:00 UTC;";
// Đưa nút bấm quay về trạng thái ban đầu để người dùng có thể gửi lại
container.innerHTML = `
<button
id="explore-btn"
class="group relative inline-flex items-center gap-3 px-10 py-5 rounded-2xl bg-gradient-to-r from-primary to-indigo-600 text-white font-black text-sm uppercase tracking-[0.2em] transition-all hover:scale-105 active:scale-95 shadow-[0_0_40px_rgba(124,58,237,0.4)] border border-primary/20 cursor-pointer overflow-hidden"
>
<span class="absolute inset-0 w-full h-full bg-gradient-to-r from-transparent via-white/10 to-transparent -translate-x-full group-hover:translate-x-full transition-transform duration-1000"></span>
<i class="fas fa-play text-xs transition-transform group-hover:translate-x-1"></i>
<span>Khám phá ngay</span>
</button>
`;
const newExploreBtn = document.getElementById('explore-btn');
if (newExploreBtn) {
newExploreBtn.onclick = () => {
if (zaloModal) zaloModal.style.display = 'flex';
};
}
if (window.txatoast) {
window.txatoast.error('❌ Thiết bị và quyền truy cập Zalo của bạn đã bị Admin xóa vĩnh viễn!');
}
return true; // Dừng polling
}
}
} catch (err) {
console.error('Lỗi check status:', err);
}
return false;
}
// 6. Polling loop mỗi 4 giây nếu trạng thái hiện tại là pending
let pollingInterval = null;
function startPolling() {
if (pollingInterval) clearInterval(pollingInterval);
pollingInterval = setInterval(async () => {
const isFinished = await checkApprovalStatus();
if (isFinished && pollingInterval) {
clearInterval(pollingInterval);
}
}, 4000);
}
// 7. Khởi tạo trạng thái ban đầu dựa vào localStorage & cookies
const localStatus = localStorage.getItem('zalo_access_status');
const isCookieApproved = document.cookie.includes('zalo_access_approved=true');
if (localStatus === 'approved') {
setApprovedCookie();
renderApprovedBtn();
if (zaloModal) zaloModal.style.display = 'none';
} else if (localStatus === 'pending') {
document.cookie = "zalo_access_approved=; path=/; expires=Thu, 01 Jan 1970 00:00:00 UTC;";
renderPendingBtn();
checkApprovalStatus().then(isApproved => {
if (!isApproved) {
startPolling();
}
});
} else if (isCookieApproved) {
localStorage.setItem('zalo_access_status', 'approved');
renderApprovedBtn();
if (zaloModal) zaloModal.style.display = 'none';
}
// 8. Event Listeners mở/đóng Modal
exploreBtn.onclick = () => {
zaloModal.style.display = 'flex';
};
modalCloseBtn.onclick = () => {
zaloModal.style.display = 'none';
};
zaloModal.querySelector('.txamodal-overlay').onclick = () => {
zaloModal.style.display = 'none';
};
// 9. Form Validation cho Nickname input
zaloNicknameInput.oninput = () => {
const val = zaloNicknameInput.value.trim();
if (val.length >= 2) {
submitAccessBtn.disabled = false;
submitAccessBtn.className = "w-full relative flex items-center justify-center gap-2 py-3.5 rounded-xl bg-primary text-white font-black text-xs uppercase tracking-widest transition-all hover:scale-[1.02] active:scale-[0.98] shadow-[0_0_20px_rgba(124,58,237,0.3)] cursor-pointer";
} else {
submitAccessBtn.disabled = true;
submitAccessBtn.className = "w-full relative flex items-center justify-center gap-2 py-3.5 rounded-xl bg-white/5 border border-white/5 text-gray-500 font-black text-xs uppercase tracking-widest transition-all cursor-not-allowed";
}
};
// 10. Form submission API call
submitAccessBtn.onclick = async () => {
const val = zaloNicknameInput.value.trim();
if (val.length < 2) return;
submitAccessBtn.disabled = true;
submitAccessBtn.style.cursor = 'not-allowed';
submitIcon.className = "fas fa-spinner fa-spin";
submitText.textContent = "Đang gửi...";
try {
const res = await fetch('/api/access/request', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
browser_token: browserToken,
zalo_nickname: val
})
});
const data = await res.json();
if (res.ok) {
if (window.txatoast) window.txatoast.success(data.message || 'Gửi yêu cầu thành công!');
localStorage.setItem('zalo_access_status', 'pending');
document.cookie = "zalo_access_approved=; path=/; expires=Thu, 01 Jan 1970 00:00:00 UTC;";
zaloModal.style.display = 'none';
submitAccessBtn.disabled = false;
submitAccessBtn.style.cursor = 'pointer';
submitIcon.className = "fas fa-paper-plane";
submitText.textContent = "Gửi yêu cầu duyệt";
renderPendingBtn();
startPolling();
} else {
if (window.txatoast) window.txatoast.error(data.error || 'Gửi yêu cầu thất bại!');
submitAccessBtn.disabled = false;
submitAccessBtn.style.cursor = 'pointer';
submitIcon.className = "fas fa-paper-plane";
submitText.textContent = "Gửi yêu cầu duyệt";
}
} catch (err) {
if (window.txatoast) window.txatoast.error('Lỗi kết nối tới máy chủ!');
submitAccessBtn.disabled = false;
submitAccessBtn.style.cursor = 'pointer';
submitIcon.className = "fas fa-paper-plane";
submitText.textContent = "Gửi yêu cầu duyệt";
}
};
}
if (!browserToken) {
// Đổi sang nút đang quét vân tay/tạo token để tạo cảm giác chuyên nghiệp
container.innerHTML = `
<button
id="generating-btn"
disabled
class="inline-flex items-center gap-3 px-10 py-5 rounded-2xl bg-violet-500/10 text-violet-400 font-black text-sm uppercase tracking-[0.2em] border border-violet-500/20 shadow-[0_0_30px_rgba(124,58,237,0.1)] cursor-not-allowed animate-pulse"
>
<i class="fas fa-fingerprint animate-spin"></i>
<span>Đang tạo mã trình duyệt...</span>
</button>
`;
browserToken = await generateBrowserToken();
localStorage.setItem('zalo_browser_token', browserToken);
// Phục hồi nút Khám phá ngay
container.innerHTML = `
<button
id="explore-btn"
class="group relative inline-flex items-center gap-3 px-10 py-5 rounded-2xl bg-gradient-to-r from-primary to-indigo-600 text-white font-black text-sm uppercase tracking-[0.2em] transition-all hover:scale-105 active:scale-95 shadow-[0_0_40px_rgba(124,58,237,0.4)] border border-primary/20 cursor-pointer overflow-hidden"
>
<span class="absolute inset-0 w-full h-full bg-gradient-to-r from-transparent via-white/10 to-transparent -translate-x-full group-hover:translate-x-full transition-transform duration-1000"></span>
<i class="fas fa-play text-xs transition-transform group-hover:translate-x-1"></i>
<span>Khám phá ngay</span>
</button>
`;
proceedWithSetup();
} else {
proceedWithSetup();
}
}
document.addEventListener('astro:page-load', initGatekeeper);
</script>
<style>
.glass-card {
background: rgba(18, 20, 29, 0.6);
backdrop-filter: blur(40px) saturate(220%);
box-shadow: 0 40px 100px -20px rgba(0,0,0,0.8);
}
/* Core Modal styles for TxaModal */
.txamodal-container {
position: fixed;
inset: 0;
z-index: 9999;
display: none; /* Mặc định ẩn, điều khiển đóng mở mượt mà bằng Javascript để tránh glitch khi load */
align-items: center;
justify-content: center;
padding: 16px;
}
.txamodal-overlay {
position: absolute;
inset: 0;
background: rgba(0, 0, 0, 0.8);
backdrop-filter: blur(8px);
}
.txamodal-wrapper {
position: relative;
z-index: 10;
transform: scale(0.95);
opacity: 0;
animation: modal-enter 0.4s cubic-bezier(0.16, 1, 0.3, 1) forwards;
}
@keyframes modal-enter {
to {
transform: scale(1);
opacity: 1;
}
}
@keyframes fade-in {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.animate-fade-in {
animation: fade-in 1s ease-out forwards;
}
</style>