1// nav-auth.js â replaces Log In / Get Started with My Account / Sign Out when signed in. 2// cc_token (bearer session token) is the real auth signal; cc_user is a display cache 3// refreshed from /api/auth/session in the background (stale-while-revalidate). 4(function () { 5 'use strict'; 6 7 window.signOut = window.signOut || function () { 8 if (!confirm('Sign out of CardCompass?')) return; 9 var token = localStorage.getItem('cc_token'); 10 localStorage.removeItem('cc_token'); 11 localStorage.removeItem('cc_user'); 12 localStorage.removeItem('cc_session'); 13 sessionStorage.removeItem('cc_token'); 14 sessionStorage.removeItem('cc_user'); 15 sessionStorage.removeItem('cc_session'); 16 if (token) { 17 fetch('/api/auth/logout', { method: 'POST', headers: { 'Authorization': 'Bearer ' + token } }).catch(function () {}); 18 } 19 location.href = '/'; 20 }; 21 22 function getToken() { 23 return localStorage.getItem('cc_token') || sessionStorage.getItem('cc_token'); 24 } 25 function getUser() { 26 try { return JSON.parse(localStorage.getItem('cc_user') || sessionStorage.getItem('cc_user') || 'null'); } 27 catch (e) { return null; } 28 } 29 function clearAuth() { 30 localStorage.removeItem('cc_token'); 31 localStorage.removeItem('cc_user'); 32 sessionStorage.removeItem('cc_token'); 33 sessionStorage.removeItem('cc_user'); 34 } 35 36 // Validates the cached token against the server. Returns the fresh user object, 37 // or null if the session is invalid/expired (and clears local state in that case). 38 // Merges (not replaces) into the cached cc_user â until prefs/wallet also live in 39 // D1 (Milestone 4), those fields only exist client-side and must survive this call. 40 async function requireAuth() { 41 var token = getToken(); 42 if (!token) return null; 43 try { 44 const res = await fetch('/api/auth/session', { headers: { 'Authorization': 'Bearer ' + token } }); 45 if (!res.ok) { clearAuth(); return null; } 46 const data = await res.json(); 47 var merged = Object.assign({}, getUser() || {}, data.user); 48 localStorage.setItem('cc_user', JSON.stringify(merged)); 49 return merged; 50 } catch (e) { 51 // Network failure â keep the cached user rather than force a sign-out. 52 return getUser(); 53 } 54 } 55 56 window.ccAuth = { getToken, getUser, requireAuth, clearAuth }; 57 58 var signedOutMarkup = null; 59 60 function paintSignedIn() { 61 var na = document.querySelector('.nav-actions'); 62 if (!na || na.querySelector('.user-btn')) return; // skip if page has its own dropdown 63 if (signedOutMarkup === null) signedOutMarkup = na.innerHTML; 64 65 na.innerHTML = 66 '<a href="/account" class="btn-outline">My Account</a>' + 67 '<button class="btn-primary" style="border:none;cursor:pointer;font-family:inherit;" onclick="signOut()">Sign Out</button>'; 68 69 var footerLink = document.getElementById('footer-account-link'); 70 if (footerLink) footerLink.style.display = ''; 71 } 72 73 function paintSignedOut() { 74 var na = document.querySelector('.nav-actions'); 75 if (!na || na.querySelector('.user-btn') || signedOutMarkup === null) return; 76 na.innerHTML = signedOutMarkup; 77 } 78 79 function init() { 80 if (getToken() && getUser()) paintSignedIn(); // optimistic paint from cache, avoids a flash of signed-out UI 81 requireAuth().then(function (user) { 82 if (user) paintSignedIn(); 83 else paintSignedOut(); 84 }); 85 } 86 87 if (document.readyState === 'loading') { 88 document.addEventListener('DOMContentLoaded', init); 89 } else { 90 init(); 91 } 92})();
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.