86 lines
5.1 KiB
JavaScript
86 lines
5.1 KiB
JavaScript
// auth.js — gates the whole site behind a PromptPCB account (Corben tenant auth for this site).
|
||
// Nothing loads until you're signed in; the catalog + file URLs come only from the authed function.
|
||
(function () {
|
||
var API = 'https://api.corben.world';
|
||
var SITE = '6ab5ec79-8b91-453d-aacc-5be76f9271d1'; // this site IS the tenant
|
||
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(API + '/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 = '<span title="' + user.email + '" style="cursor:default">◆ ' + (user.display_name || user.email.split('@')[0]) + '</span> · <a href="#" id="ppcb-logout" style="color:var(--dim)">sign out</a>';
|
||
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(API + '/auth/tenant/me', { headers: { Authorization: 'Bearer ' + t } });
|
||
if (!r.ok) return gate();
|
||
var u = await r.json(); window.PPCB.token = t; authed(u);
|
||
} 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 =
|
||
'<div class="gate-card">' +
|
||
'<div class="gate-brand"><span class="mk"></span> PromptPCB <small>/ parts</small></div>' +
|
||
'<div class="gate-h">Sign in to the part library</div>' +
|
||
'<div class="gate-sub">One PromptPCB account. Every part—real specs, 3D, SPICE—once you’re in.</div>' +
|
||
'<div class="gate-tabs"><button data-m="login" class="on">Sign in</button><button data-m="signup">Create account</button></div>' +
|
||
'<input id="g-name" placeholder="Display name" style="display:none">' +
|
||
'<input id="g-email" type="email" placeholder="you@example.com" autocomplete="email">' +
|
||
'<input id="g-pass" type="password" placeholder="Password (8+ characters)" autocomplete="current-password">' +
|
||
'<button class="btn gate-go" id="g-go">Sign in</button>' +
|
||
'<div class="gate-msg" id="g-msg"></div>' +
|
||
'</div>';
|
||
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 = '<span class="spin"></span> …';
|
||
var path = mode === 'signup' ? '/auth/tenant/register' : '/auth/tenant/login';
|
||
var body = { tenant_id: SITE, email: email, password: pass };
|
||
if (mode === 'signup') body.display_name = document.getElementById('g-name').value.trim();
|
||
try {
|
||
var r = await fetch(API + path, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) });
|
||
var d = await r.json();
|
||
if (!r.ok) { msg.style.color = 'var(--bad,#ff6b6b)'; msg.textContent = d.error || 'failed'; return; }
|
||
localStorage.setItem(LS, d.token); window.PPCB.token = d.token; authed(d.user);
|
||
} catch (e) { msg.style.color = '#ff6b6b'; msg.textContent = 'network error'; }
|
||
}
|
||
}
|
||
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', check); else check();
|
||
})();
|