1// Requires: supabase-js CDN, config.js loaded before this file 2 3var _supabase = window.supabase.createClient(SUPABASE_URL, SUPABASE_ANON_KEY); 4 5var TOKEN_KEY = 'explainly_token'; 6var REFRESH_KEY = 'explainly_refresh'; 7var EMAIL_KEY = 'explainly_email'; 8var USAGE_KEY = 'explainly_usage_cache'; 9 10function getToken() { return localStorage.getItem(TOKEN_KEY); } 11function getRefresh() { return localStorage.getItem(REFRESH_KEY); } 12function getEmail() { return localStorage.getItem(EMAIL_KEY); } 13 14function setSession(access_token, refresh_token, email) { 15 if (access_token) localStorage.setItem(TOKEN_KEY, access_token); 16 if (refresh_token) localStorage.setItem(REFRESH_KEY, refresh_token); 17 if (email) localStorage.setItem(EMAIL_KEY, email); 18} 19 20function clearAuth() { 21 localStorage.removeItem(TOKEN_KEY); 22 localStorage.removeItem(REFRESH_KEY); 23 localStorage.removeItem(EMAIL_KEY); 24 localStorage.removeItem(USAGE_KEY); 25} 26 27function requireAuth() { 28 if (!getToken()) { 29 window.location.href = '/app/login.html'; 30 return false; 31 } 32 return true; 33} 34 35function signInWithGoogle() { 36 var next = new URLSearchParams(window.location.search).get('next'); 37 if (next) sessionStorage.setItem('explainly_login_next', next); 38 return _supabase.auth.signInWithOAuth({ 39 provider: 'google', 40 options: { redirectTo: window.location.origin + '/auth/callback.html' } 41 }); 42} 43 44function signOut() { 45 clearAuth(); 46 _supabase.auth.signOut(); 47 window.location.href = '/app/login.html'; 48} 49 50var _refreshPromise = null; 51 52function refreshToken() { 53 if (_refreshPromise) return _refreshPromise; 54 55 var rt = getRefresh(); 56 if (!rt) { 57 clearAuth(); 58 window.location.href = '/app/login.html'; 59 return Promise.reject(new Error('No refresh token')); 60 } 61 62 _refreshPromise = fetch(BACKEND_URL + '/api/auth/refresh', { 63 method: 'POST', 64 headers: { 'Content-Type': 'application/json' }, 65 body: JSON.stringify({ refresh_token: rt }) 66 }) 67 .then(function(r) { 68 if (!r.ok) throw new Error('Refresh failed'); 69 return r.json(); 70 }) 71 .then(function(data) { 72 setSession(data.access_token, data.refresh_token, null); 73 return data.access_token; 74 }) 75 .catch(function(err) { 76 clearAuth(); 77 window.location.href = '/app/login.html'; 78 throw err; 79 }) 80 .finally(function() { 81 _refreshPromise = null; 82 }); 83 84 return _refreshPromise; 85} 86 87function getAvatarInitial() { 88 var email = getEmail(); 89 return email ? email[0].toUpperCase() : '?'; 90}
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.