PageSourceSearch

https://explainly-ai.netlify.app/js/auth.js

js explainly-ai.netlify.app collected 2026-10-03 10:43:06 UTC 2,480 bytes, 90 lines download raw bytes

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.