PageSourceSearch

https://vdomain.com/login.html

html vdomain.com collected 2026-10-08 00:37:46 UTC 8,822 bytes, 202 lines download raw bytes

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.