Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Micro Verify Widget - Auto Retry</title> | |
| <style> | |
| @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500&family=JetBrains+Mono&display=swap'); | |
| :root { | |
| --bg: #18181b; | |
| --border: #27272a; | |
| --primary: #3b82f6; | |
| --text: #f4f4f5; | |
| --muted: #71717a; | |
| --success: #10b981; | |
| } | |
| body { | |
| margin: 0; padding: 0; background: transparent; | |
| font-family: 'Inter', sans-serif; | |
| display: flex; justify-content: center; align-items: center; | |
| height: 100vh; overflow: hidden; | |
| } | |
| .widget { | |
| width: 320px; height: 70px; | |
| background: var(--bg); | |
| border: 1px solid var(--border); | |
| border-radius: 12px; | |
| display: flex; align-items: center; | |
| padding: 0 16px; box-sizing: border-box; | |
| transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); | |
| box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); | |
| } | |
| .widget:hover { border-color: var(--primary); } | |
| .check-area { | |
| width: 32px; height: 32px; | |
| display: flex; justify-content: center; align-items: center; | |
| margin-right: 12px; cursor: pointer; | |
| } | |
| .spinner { | |
| width: 20px; height: 20px; | |
| border: 2px solid rgba(59, 130, 246, 0.2); | |
| border-top-color: var(--primary); | |
| border-radius: 50%; | |
| display: none; | |
| animation: spin 0.8s linear infinite; | |
| } | |
| .checkbox-dummy { | |
| width: 20px; height: 20px; | |
| border: 2px solid var(--muted); | |
| border-radius: 6px; | |
| transition: all 0.2s; | |
| } | |
| .content { | |
| flex-grow: 1; display: flex; | |
| flex-direction: column; justify-content: center; | |
| overflow: hidden; | |
| } | |
| .title { font-size: 13px; font-weight: 500; color: var(--text); margin: 0; } | |
| .email-display { | |
| font-family: 'JetBrains Mono', monospace; | |
| font-size: 11px; color: var(--primary); | |
| cursor: pointer; margin-top: 2px; | |
| white-space: nowrap; overflow: hidden; text-overflow: ellipsis; | |
| } | |
| .brand { display: flex; flex-direction: column; align-items: flex-end; opacity: 0.3; } | |
| .brand svg { width: 18px; fill: var(--muted); } | |
| .brand-text { font-size: 7px; color: var(--muted); text-transform: uppercase; margin-top: 2px; } | |
| @keyframes spin { to { transform: rotate(360deg); } } | |
| /* Verified State */ | |
| .verified { border-color: var(--success) ; } | |
| .verified .checkbox-dummy { | |
| background: var(--success); | |
| border-color: var(--success); | |
| display: flex; justify-content: center; align-items: center; | |
| } | |
| .verified .checkbox-dummy::after { | |
| content: ''; width: 4px; height: 8px; | |
| border: solid white; border-width: 0 2px 2px 0; | |
| transform: rotate(45deg); margin-bottom: 2px; | |
| } | |
| .verified .title { color: var(--success); } | |
| .verified .email-display { color: var(--muted); cursor: default; } | |
| /* Loading/Polling State */ | |
| .loading .spinner { display: block; } | |
| .loading .checkbox-dummy { display: none; } | |
| .loading .title { color: var(--primary); } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="widget" id="widget"> | |
| <div class="check-area" onclick="handleVerify()"> | |
| <div class="checkbox-dummy"></div> | |
| <div class="spinner"></div> | |
| </div> | |
| <div class="content"> | |
| <div class="title" id="status-text">Click email to send verify</div> | |
| <div class="email-display" id="email-val" onclick="openGmail()" title="Click to copy & open Gmail">Generating...</div> | |
| </div> | |
| <div class="brand"> | |
| <svg viewBox="0 0 24 24"><path d="M12 1L3 5v6c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V5l-9-4z"/></svg> | |
| <div class="brand-text">Verified AI</div> | |
| </div> | |
| </div> | |
| <script> | |
| let token = ""; | |
| let checkInterval = null; | |
| async function init() { | |
| const emailEl = document.getElementById('email-val'); | |
| try { | |
| // 1. Lấy Domain khả dụng | |
| const domainRes = await fetch('https://api.mail.tm/domains'); | |
| const domainData = await domainRes.json(); | |
| const domain = domainData['hydra:member'][0].domain; | |
| // 2. Tạo Account ngẫu nhiên | |
| const user = Math.random().toString(36).substring(2, 10); | |
| const address = `${user}@${domain}`; | |
| const password = "pass_" + user; | |
| await fetch('https://api.mail.tm/accounts', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ address, password }) | |
| }); | |
| // 3. Lấy Token để truy cập hòm thư | |
| const tokenRes = await fetch('https://api.mail.tm/token', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ address, password }) | |
| }); | |
| const tokenData = await tokenRes.json(); | |
| token = tokenData.token; | |
| emailEl.innerText = address; | |
| } catch (e) { | |
| emailEl.innerText = "Connection Error"; | |
| console.error("Init failed:", e); | |
| } | |
| } | |
| function openGmail() { | |
| const addr = document.getElementById('email-val').innerText; | |
| if (addr.includes('@')) { | |
| // Copy vào clipboard | |
| navigator.clipboard.writeText(addr); | |
| // Mở Gmail compose | |
| const gmailUrl = `https://mail.google.com/mail/?view=cm&fs=1&to=${addr}&su=Verify%20Me&body=Just%20click%20send%20to%20verify%20your%20account.`; | |
| window.open(gmailUrl, '_blank'); | |
| // Tự động kích hoạt polling ngay khi user bấm mở mail | |
| handleVerify(); | |
| } | |
| } | |
| async function checkMail() { | |
| if (!token) return; | |
| try { | |
| const res = await fetch('https://api.mail.tm/messages', { | |
| headers: { 'Authorization': `Bearer ${token}` } | |
| }); | |
| const data = await res.json(); | |
| // Nếu tìm thấy ít nhất 1 email trong hòm thư | |
| if (data['hydra:member'] && data['hydra:member'].length > 0) { | |
| stopPolling(); | |
| markAsVerified(); | |
| } | |
| } catch (e) { | |
| console.error("Polling error:", e); | |
| } | |
| } | |
| function handleVerify() { | |
| const widget = document.getElementById('widget'); | |
| // Chống click trùng lặp hoặc khi đã xong | |
| if (widget.classList.contains('verified') || checkInterval || !token) return; | |
| widget.classList.add('loading'); | |
| document.getElementById('status-text').innerText = "Waiting for email..."; | |
| // Bắt đầu quét mỗi 3 giây | |
| checkInterval = setInterval(checkMail, 3000); | |
| checkMail(); // Chạy phát đầu tiên luôn | |
| } | |
| function stopPolling() { | |
| if (checkInterval) { | |
| clearInterval(checkInterval); | |
| checkInterval = null; | |
| } | |
| } | |
| function markAsVerified() { | |
| const widget = document.getElementById('widget'); | |
| widget.classList.remove('loading'); | |
| widget.classList.add('verified'); | |
| document.getElementById('status-text').innerText = "Identity Verified"; | |
| // Gửi tín hiệu ra cho trang web cha nếu dùng <iframe> | |
| window.parent.postMessage("verified", "*"); | |
| } | |
| window.onload = init; | |
| </script> | |
| </body> | |
| </html> | |