1<!DOCTYPE html> 2<html lang="en"> 3<head> 4<meta charset="UTF-8"> 5<meta name="viewport" content="width=device-width, initial-scale=1.0"> 6<title>Login â Vdomain</title> 7<link rel="icon" type="image/png" sizes="32x32" href="/favicon.png"> 8<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png"> 9<meta name="theme-color" content="#3f2aa3"> 10<meta property="og:image" content="https://vdomain.com/seologo.png"> 11<meta name="twitter:image" content="https://vdomain.com/seologo.png"> 12<link rel="stylesheet" href="style.css">
13<script src="/theme.js"></script>
13 14<style> 15 .auth-tabs { display: flex; margin-bottom: 24px; border-bottom: 1px solid var(--border); } 16 .auth-tab.active { color: var(--accent); border-bottom-color: var(--accent); } 17 .divider { text-align: center; color: var(--text2); font-size: 13px; margin: 16px 0; position: relative; } 18 .divider::before, .divider::after { content: ''; position: absolute; top: 50%; width: 42%; height: 1px; background: var(--border); } 19 .divider::before { left: 0; } 20 .divider::after { right: 0; } 21 .auth-brand { display:flex; align-items:center; justify-content:center; gap:10px; color:var(--text); font-size:22px; font-weight:900; margin-bottom:26px; } 22 .auth-brand img { width:42px; height:42px; border-radius:10px; background:rgba(255,255,255,.08); } 23 .auth-tools { position: fixed; right: 20px; top: 20px; } 24</style> 25</head> 26<body> 27<div class="auth-tools"> 28 <div class="theme-toggle" aria-label="Theme"> 29 <button class="theme-choice" type="button" data-theme-choice="dark">Dark</button> 30 <button class="theme-choice" type="button" data-theme-choice="light">Light</button> 31 </div> 32</div> 33<div class="page-center"> 34 <div class="card card-sm"> 35 <a href="/" class="auth-brand"><img src="/seologo.png" alt="Vdomain"><span>Vdomain</span></a> 36 37 <div class="auth-tabs"> 38 <div class="auth-tab active" id="tab-login" onclick="showTab('login')">Log in</div> 39 <div class="auth-tab" id="tab-register" onclick="showTab('register')">Register</div> 40 </div> 41 42 <div id="alert-box"></div> 43 44 <!-- Login form --> 45 <form id="login-form" onsubmit="doLogin(event)"> 46 <div class="form-group"> 47 <label>Email</label> 48 <input type="email" class="form-control" id="login-email" placeholder="[email protected]" required autocomplete="email"> 49 </div> 50 <div class="form-group"> 51 <label>Password</label> 52 <input type="password" class="form-control" id="login-password" placeholder="â¢â¢â¢â¢â¢â¢â¢â¢" required autocomplete="current-password"> 53 </div> 54 <div class="flex-between text-sm mb-2"> 55 <span></span> 56 <a href="#" onclick="showForgot()">Forgot password?</a> 57 </div> 58 <button type="submit" class="btn btn-primary btn-full" id="login-btn">Log in</button> 59 <p class="text-center text-sm mt-2 text-muted"> 60 No account? <a href="#" onclick="showTab('register')">Register</a> 61 </p> 62 </form> 63 64 <!-- Register form --> 65 <form id="register-form" class="hidden" onsubmit="doRegister(event)"> 66 <div class="form-group"> 67 <label>Email</label> 68 <input type="email" class="form-control" id="reg-email" placeholder="[email protected]" required autocomplete="email"> 69 </div> 70 <div class="form-group"> 71 <label>Password <span class="text-muted">(min 8 chars)</span></label> 72 <input type="password" class="form-control" id="reg-password" placeholder="â¢â¢â¢â¢â¢â¢â¢â¢" required autocomplete="new-password" minlength="8"> 73 </div> 74 <div class="form-group"> 75 <label>Nickname <span class="text-muted">(optional)</span></label> 76 <input type="text" class="form-control" id="reg-nickname" placeholder="Your name"> 77 </div> 78 <button type="submit" class="btn btn-primary btn-full" id="reg-btn">Create account</button> 79 <p class="text-center text-sm mt-2 text-muted"> 80 Already have an account? <a href="#" onclick="showTab('login')">Log in</a> 81 </p> 82 </form> 83 84 <!-- Forgot password form --> 85 <form id="forgot-form" class="hidden" onsubmit="doForgot(event)"> 86 <p class="text-sm text-muted mb-2">Enter your email and we'll send a reset link.</p> 87 <div class="form-group"> 88 <label>Email</label> 89 <input type="email" class="form-control" id="forgot-email" placeholder="[email protected]" required> 90 </div> 91 <button type="submit" class="btn btn-primary btn-full" id="forgot-btn">Send reset link</button> 92 <p class="text-center text-sm mt-2"><a href="#" onclick="showTab('login')">Back to login</a></p> 93 </form> 94 </div> 95</div> 96
97<script> 98function showTab(tab) { 99 document.getElementById('login-form').classList.toggle('hidden', tab !== 'login'); 100 document.getElementById('register-form').classList.toggle('hidden', tab !== 'register'); 101 document.getElementById('forgot-form').classList.add('hidden'); 102 document.getElementById('tab-login').classList.toggle('active', tab === 'login'); 103 document.getElementById('tab-register').classList.toggle('active', tab === 'register'); 104 clearAlert(); 105} 106 107function showForgot() { 108 document.getElementById('login-form').classList.add('hidden'); 109 document.getElementById('register-form').classList.add('hidden'); 110 document.getElementById('forgot-form').classList.remove('hidden'); 111 document.getElementById('tab-login').classList.remove('active'); 112 document.getElementById('tab-register').classList.remove('active'); 113 clearAlert(); 114} 115 116function showAlert(msg, type = 'error') { 117 const box = document.getElementById('alert-box'); 118 const alert = document.createElement('div'); 119 alert.className = `alert alert-${type}`; 120 alert.textContent = msg || ''; 121 box.replaceChildren(alert); 122} 123function clearAlert() { document.getElementById('alert-box').innerHTML = ''; } 124 125function setLoading(btnId, loading) { 126 const btn = document.getElementById(btnId); 127 btn.disabled = loading; 128 btn.innerHTML = loading ? '<span class="spinner"></span>' : btn.dataset.label || btn.textContent; 129} 130 131// Save original labels 132document.querySelectorAll('.btn').forEach(b => b.dataset.label = b.textContent); 133 134async function doLogin(e) { 135 e.preventDefault(); 136 clearAlert(); 137 setLoading('login-btn', true); 138 try { 139 const res = await fetch('/api/auth/login', { 140 method: 'POST', 141 headers: { 'Content-Type': 'application/json' }, 142 body: JSON.stringify({ 143 email: document.getElementById('login-email').value, 144 password: document.getElementById('login-password').value, 145 }), 146 }); 147 const data = await res.json(); 148 if (!res.ok) { showAlert(data.error); return; } 149 window.location.href = '/dashboard'; 150 } catch { showAlert('Network error. Please try again.'); } 151 finally { setLoading('login-btn', false); } 152} 153 154async function doRegister(e) { 155 e.preventDefault(); 156 clearAlert(); 157 setLoading('reg-btn', true); 158 try { 159 const res = await fetch('/api/auth/register', { 160 method: 'POST', 161 headers: { 'Content-Type': 'application/json' }, 162 body: JSON.stringify({ 163 email: document.getElementById('reg-email').value, 164 password: document.getElementById('reg-password').value, 165 nickname: document.getElementById('reg-nickname').value || undefined, 166 }), 167 }); 168 const data = await res.json(); 169 if (!res.ok) { showAlert(data.error); return; } 170 showAlert('Account created! Check your email for a verification link.', 'success'); 171 document.getElementById('register-form').reset(); 172 } catch { showAlert('Network error. Please try again.'); } 173 finally { setLoading('reg-btn', false); } 174} 175 176async function doForgot(e) { 177 e.preventDefault(); 178 clearAlert(); 179 setLoading('forgot-btn', true); 180 try { 181 const res = await fetch('/api/auth/forgot-password', { 182 method: 'POST', 183 headers: { 'Content-Type': 'application/json' }, 184 body: JSON.stringify({ email: document.getElementById('forgot-email').value }), 185 }); 186 const data = await res.json(); 187 showAlert(data.message || 'Reset link sent.', 'success'); 188 } catch { showAlert('Network error. Please try again.'); } 189 finally { setLoading('forgot-btn', false); } 190} 191 192// Auto-switch to register tab if URL has #register 193if (location.hash === '#register') showTab('register'); 194 195// Redirect if already logged in. Auth now lives in an HttpOnly cookie. 196fetch('/api/user/me').then(res => { 197 if (res.ok) window.location.href = '/dashboard'; 198}).catch(() => {}); 199</script>
vendor: 1 bytes, line 199
199
200<script type="module" src="https://static.cloudflareinsights.com/beacon.min.js/v4bc70e2c01a94c73b74392e4234840661791215815920" integrity="sha512-L0ha0OXavK/8okipN9F8BtP84dg9DUhPERbBXzwI6dgTA55d2+yweo3pn5CSFYs45/r8md2+xvUPtTdvTNRfjA==" data-cf-beacon='{"version":"2024.11.0","token":"f7c0b13893424d619161d1a03b19c1ec","r":1,"spa":2}' crossorigin="anonymous"></script>
200 201</body> 202</html>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.