PageSourceSearch

https://peeksolve.com/login.js?v=1790900810

js peeksolve.com collected 2026-10-02 01:38:58 UTC 6,874 bytes, 177 lines download raw bytes

1const API = 'https://api.peeksolve.com';
2// A referral link points straight at the sign-in page. Remember the code, send
3// the person to the plan picker after sign-in, and show what they get.
4const inviteRef = (() => { try { const ref = new URLSearchParams(location.search).get('ref'); return (ref && /^peek-[a-z2-9]{4}$/i.test(ref.trim())) ? ref.trim().toUpperCase() : null; } catch (e) { return null; } })();
5if (inviteRef) { try { localStorage.setItem('ps_ref', inviteRef); localStorage.setItem('ps_next', '/account.html?checkout=1'); } catch (e) {} }
6// PeekBench (peekbench.com) signs in here and gets the session handed back in
7// the URL fragment. The wish expires after 30 minutes so a later, ordinary
8// sign-in isn't sent there.
9if (new URLSearchParams(location.search).get('to') === 'peekbench') { try { localStorage.setItem('ps_bench', String(Date.now())); } catch (e) {} }
10function benchHandoff() {
11  try {
12    const at = Number(localStorage.getItem('ps_bench') || 0);
13    const jwt = localStorage.getItem('peeksolve_jwt');
14    if (!at || Date.now() - at > 30 * 60 * 1000 || !jwt) return null;
15    localStorage.removeItem('ps_bench');
16    let email = '';
17    try { email = (JSON.parse(localStorage.getItem('peeksolve_user') || '{}') || {}).email || ''; } catch (e) {}
18    return `https://peekbench.com/auth.html#jwt=${encodeURIComponent(jwt)}&email=${encodeURIComponent(email)}`;
19  } catch (e) { return null; }
20}
21function nextUrl() { const bench = benchHandoff(); if (bench) return bench; try { const n = localStorage.getItem('ps_next'); if (n && n.startsWith('/account.html')) return n; } catch (e) {} return '/account.html'; }
22const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
23
24// If already signed in, jump to account.
25if (localStorage.getItem('peeksolve_jwt')) {
26  location.href = nextUrl();
27}
28
29const form = document.getElementById('magicForm');
30const email = document.getElementById('email');
31const emailErr = document.getElementById('emailErr');
32const status = document.getElementById('status');
33const submitBtn = document.getElementById('submitBtn');
34const googleBtn = document.getElementById('googleSignIn');
35const googleDivider = document.getElementById('googleDivider');
36const formDefault = document.getElementById('formDefault');
37const sentState = document.getElementById('sentState');
38const sentEmail = document.getElementById('sentEmail');
39const changeEmail = document.getElementById('changeEmail');
40const resendTimer = document.getElementById('resendTimer');
41
42googleBtn.href = `${API}/auth/google`;
43
44const inviteBanner = document.getElementById('inviteBanner');
45if (inviteRef && inviteBanner) {
46  inviteBanner.hidden = false;
47  fetch(`${API}/referral/check?code=${encodeURIComponent(inviteRef)}`).then((r) => r.json()).then((d) => {
48    if (!d.valid) { inviteBanner.hidden = true; try { localStorage.removeItem('ps_ref'); localStorage.removeItem('ps_next'); } catch (e) {} }
49  }).catch(() => {});
50}
51
52// Show Google button only if backend reports it configured.
53fetch(`${API}/auth/providers`)
54  .then((r) => (r.ok ? r.json() : { google: false }))
55  .then((providers) => {
56    if (providers.google) {
57      googleDivider.style.display = '';
58      googleBtn.style.display = '';
59    }
60  })
61  .catch(() => {
62    // Backend unreachable — leave Google hidden, magic link still works.
63  });
64
65function showError(msg) {
66  status.textContent = msg;
67  status.classList.add('show');
68  status.classList.remove('ok');
69}
70function clearError() {
71  status.textContent = '';
72  status.classList.remove('show');
73  email.classList.remove('bad');
74  emailErr.classList.remove('show');
75}
76
77email.addEventListener('input', clearError);
78
79async function sendMagicLink(addr) {
80  const res = await fetch(`${API}/auth/magic-link`, {
81    method: 'POST',
82    headers: { 'Content-Type': 'application/json' },
83    body: JSON.stringify({ email: addr })
84  });
85  if (!res.ok) throw new Error(`HTTP ${res.status}`);
86  return res.json().catch(() => ({}));
87}
88
89// Cross-device: poll until the emailed link is clicked (on ANY device), then log
90// THIS browser in (localStorage + postMessage so the extension picks it up) and
91// go to the account. That's what makes "clicked on phone → desktop logs in" work.
92let pollTimer;
93function startPolling(pollId, secret) {
94  if (!pollId || !secret) return;
95  clearInterval(pollTimer);
96  pollTimer = setInterval(async () => {
97    try {
98      const r = await fetch(`${API}/auth/magic-link/poll?id=${encodeURIComponent(pollId)}&secret=${encodeURIComponent(secret)}`);
99      if (!r.ok) return;
100      const d = await r.json();
101      if (d.status === 'ok' && d.jwt) {
102        clearInterval(pollTimer);
103        localStorage.setItem('peeksolve_jwt', d.jwt);
104        localStorage.setItem('peeksolve_user', JSON.stringify(d.user || {}));
105        window.postMessage({ source: 'peeksolve-auth', jwt: d.jwt, user: d.user || {} }, location.origin);
106        setTimeout(() => { location.href = nextUrl(); }, 400);
107      } else if (d.status === 'expired') {
108        clearInterval(pollTimer);
109      }
110    } catch { /* transient — keep polling */ }
111  }, 2500);
112}
113
114form.addEventListener('submit', async (e) => {
115  e.preventDefault();
116  const addr = email.value.trim();
117  if (!EMAIL_RE.test(addr)) {
118    email.classList.add('bad');
119    emailErr.classList.add('show');
120    email.focus();
121    return;
122  }
123  clearError();
124  submitBtn.disabled = true;
125  submitBtn.textContent = 'Sending…';
126  try {
127    const data = await sendMagicLink(addr);
128    sentEmail.textContent = addr;
129    formDefault.classList.add('hide');
130    sentState.classList.add('show');
131    startResend(addr);
132    startPolling(data.pollId, data.secret);
133  } catch (err) {
134    showError('Failed to send. Try again or contact [email protected].');
135    submitBtn.disabled = false;
136    submitBtn.textContent = 'Send magic link';
137  }
138});
139
140changeEmail.addEventListener('click', () => {
141  sentState.classList.remove('show');
142  formDefault.classList.remove('hide');
143  submitBtn.disabled = false;
144  submitBtn.textContent = 'Send magic link';
145  email.focus();
146  email.select();
147});
148
149let resendInterval;
150function startResend(addr) {
151  let n = 30;
152  resendTimer.classList.add('dim');
153  resendTimer.textContent = n + 's';
154  resendTimer.onclick = null;
155  clearInterval(resendInterval);
156  resendInterval = setInterval(() => {
157    n--;
158    if (n <= 0) {
159      clearInterval(resendInterval);
160      resendTimer.classList.remove('dim');
161      resendTimer.textContent = 'Resend now';
162      resendTimer.onclick = async () => {
163        resendTimer.onclick = null;
164        try {
165          const data = await sendMagicLink(addr);
166          startResend(addr);
167          startPolling(data.pollId, data.secret);
168        } catch {
169          resendTimer.textContent = 'Resend now';
170          resendTimer.onclick = () => startResend(addr);
171        }
172      };
173    } else {
174      resendTimer.textContent = n + 's';
175    }
176  }, 1000);
177}

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.