// auth.js — gates the whole site behind your PromptPCB account (SHARED with app.promptpcb.ai via its // SSO bridge). Nothing loads until you're signed in; the catalog + file URLs come only from the authed // Corben function, which itself verifies your PromptPCB session. (function () { var CORBEN = 'https://api.corben.world'; // Corben functions (catalog/autofill/submit) var AUTH = 'https://app.promptpcb.ai'; // shared PromptPCB accounts live here var FN_CATALOG = '0f3cd09e-81ce-43f4-88e7-cde3d91f122e'; var TK_CATALOG = '687d2466cea418815433b60946d6742a'; var LS = 'ppcb_token'; window.PPCB = { token: null, user: null }; window.ppcbReady = function (fn) { if (window.PPCB.token) fn(); else addEventListener('ppcb:auth', function () { fn(); }, { once: true }); }; window.getCatalog = async function () { var r = await fetch(CORBEN + '/functions/' + FN_CATALOG + '/invoke?token=' + TK_CATALOG, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + window.PPCB.token }, body: '{}' }); if (r.status === 401) { logout(); throw new Error('session expired'); } var d = await r.json(); return d.parts || []; }; window.ppcbAuth = function () { return { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + (window.PPCB.token || '') }; }; function authed(user) { window.PPCB.user = user; document.documentElement.classList.add('ppcb-authed'); var g = document.getElementById('ppcb-gate'); if (g) g.style.display = 'none'; var c = document.querySelector('.credits'); if (c) c.innerHTML = '◆ ' + (user.name || user.email.split('@')[0]) + ' · sign out'; var lo = document.getElementById('ppcb-logout'); if (lo) lo.onclick = function (e) { e.preventDefault(); logout(); }; dispatchEvent(new Event('ppcb:auth')); } function logout() { localStorage.removeItem(LS); location.reload(); } window.ppcbLogout = logout; async function check() { var t = localStorage.getItem(LS); if (!t) return gate(); try { var r = await fetch(AUTH + '/api/auth/sso/verify', { headers: { Authorization: 'Bearer ' + t } }); if (!r.ok) return gate(); var u = await r.json(); if (!u.valid) return gate(); window.PPCB.token = t; authed({ email: u.email, name: u.name }); } catch (e) { gate(); } } function gate() { document.documentElement.classList.remove('ppcb-authed'); if (document.getElementById('ppcb-gate')) { document.getElementById('ppcb-gate').style.display = 'flex'; return; } var el = document.createElement('div'); el.id = 'ppcb-gate'; el.innerHTML = '
' + '
PromptPCB / parts
' + '
Sign in to the part library
' + '
Your PromptPCB account — the same one you use at app.promptpcb.ai. Real specs, 3D, SPICE for every part, once you’re in.
' + '
' + '' + '' + '' + '' + '
' + '
One account across all of PromptPCB · app.promptpcb.ai ↗
' + '
'; document.body.appendChild(el); var mode = 'login'; el.querySelectorAll('.gate-tabs button').forEach(function (b) { b.onclick = function () { mode = b.dataset.m; el.querySelectorAll('.gate-tabs button').forEach(function (x) { x.classList.toggle('on', x === b); }); document.getElementById('g-name').style.display = mode === 'signup' ? 'block' : 'none'; document.getElementById('g-go').textContent = mode === 'signup' ? 'Create account' : 'Sign in'; document.getElementById('g-msg').textContent = ''; }; }); document.getElementById('g-go').onclick = go; el.addEventListener('keydown', function (e) { if (e.key === 'Enter') go(); }); async function go() { var email = document.getElementById('g-email').value.trim(), pass = document.getElementById('g-pass').value; var msg = document.getElementById('g-msg'); msg.style.color = 'var(--dim)'; msg.innerHTML = ' …'; var path = mode === 'signup' ? '/api/auth/signup' : '/api/auth/login'; var body = { email: email, password: pass }; if (mode === 'signup') body.name = document.getElementById('g-name').value.trim(); try { var r = await fetch(AUTH + path, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) }); var d = await r.json(); if (!r.ok || !d.token) { msg.style.color = '#ff6b6b'; msg.textContent = d.error || 'failed'; return; } localStorage.setItem(LS, d.token); window.PPCB.token = d.token; authed({ email: d.user.email, name: d.user.name }); } catch (e) { msg.style.color = '#ff6b6b'; msg.textContent = 'network error'; } } } if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', check); else check(); })();