Spaces:
Sleeping
Sleeping
| --- | |
| 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> | |