parts-store/assets/auth.js

86 lines
5.1 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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();
})();