incognitolm commited on
Commit
9989593
·
1 Parent(s): 87c5cf1

TOS & Subscription Info

Browse files
public/js/auth.js CHANGED
@@ -108,31 +108,43 @@ export async function logout() {
108
  // ── Session handling ──────────────────────────────────────────────────────
109
 
110
  async function handleSupabaseSession(data) {
 
111
  if (!data.access_token) throw new Error('No access token');
112
  saveAuth({ access_token: data.access_token, refresh_token: data.refresh_token, user: data.user });
113
 
114
  return new Promise((resolve, reject) => {
115
  const tempId = getTempId();
116
  const clientId = getClientId();
 
117
  send({ type: 'auth:login', accessToken: data.access_token, refreshToken: data.refresh_token,
118
  tempId, clientId });
119
 
120
- const unsubOk = on('auth:ok', (msg) => { unsubOk(); unsubErr(); applyAuthOk(msg); resolve(msg); });
121
- const unsubErr = on('auth:error', (msg) => { unsubOk(); unsubErr(); reject(new Error(msg.message)); });
 
 
 
 
 
 
122
 
123
  setTimeout(() => { unsubOk(); unsubErr(); reject(new Error('Auth timeout')); }, 12000);
124
  });
125
  }
126
 
127
  function applyAuthOk(msg) {
 
128
  currentUser = { id: msg.userId, email: msg.email };
129
  userProfile = msg.profile;
130
  userSettings = msg.settings;
131
  subscriptionInfo = msg.subscription || null;
132
- console.log('Auth OK - subscription info:', subscriptionInfo);
 
 
133
  const auth = loadAuth() || {};
134
  saveAuth({ ...auth, userId: msg.userId, deviceToken: msg.deviceToken });
135
  notifyListeners();
 
136
  updateSidebarProfile();
137
  // Apply saved theme
138
  if (msg.settings?.theme) {
@@ -174,11 +186,21 @@ on('settings:updated', (msg) => {
174
  // ── Reconnect on WS connect ────────────────────────────────────────────────
175
 
176
  on('ws:connected', async () => {
 
177
  const auth = loadAuth();
 
178
  if (auth?.access_token) {
179
- try { await handleSupabaseSession(auth); }
180
- catch { clearAuth(); initAsGuest(); }
 
 
 
 
 
 
 
181
  } else {
 
182
  initAsGuest();
183
  }
184
  });
@@ -191,16 +213,25 @@ on('ws:connected', async () => {
191
 
192
  window.addEventListener('storage', async (e) => {
193
  if (e.key !== OAUTH_PENDING_KEY || !e.newValue) return;
 
194
  // Consume immediately so other tabs don't also try to log in
195
  localStorage.removeItem(OAUTH_PENDING_KEY);
196
  let tokens;
197
- try { tokens = JSON.parse(e.newValue); } catch { return; }
198
- if (!tokens?.access_token) return;
 
 
 
 
 
 
 
199
  try {
200
  await handleSupabaseSession(tokens);
201
  import('./ui.js').then(({ showNotification }) =>
202
  showNotification({ type: 'success', message: 'Signed in!', duration: 2500 }));
203
  } catch (err) {
 
204
  import('./ui.js').then(({ showNotification }) =>
205
  showNotification({ type: 'error', message: `Sign-in failed: ${err.message}`, duration: 4000 }));
206
  }
@@ -210,9 +241,13 @@ window.addEventListener('storage', async (e) => {
210
  (function checkOAuthRedirect() {
211
  const params = new URLSearchParams(location.search);
212
  const t = params.get('t'), r = params.get('r');
 
213
  if (params.get('oauth') === '1' && t) {
 
214
  history.replaceState({}, '', '/');
215
- handleSupabaseSession({ access_token: t, refresh_token: r || '' }).catch(() => {});
 
 
216
  }
217
  })();
218
 
@@ -220,10 +255,16 @@ window.addEventListener('storage', async (e) => {
220
  window.addEventListener('message', async (e) => {
221
  if (e.origin !== location.origin) return;
222
  if (e.data?.type !== 'oauth:callback') return;
 
223
  const { access_token, refresh_token } = e.data;
224
- if (!access_token) return;
 
 
 
 
225
  try { await handleSupabaseSession({ access_token, refresh_token }); }
226
  catch (err) {
 
227
  import('./ui.js').then(({ showNotification }) =>
228
  showNotification({ type: 'error', message: `Sign-in failed: ${err.message}`, duration: 4000 }));
229
  }
@@ -232,6 +273,7 @@ window.addEventListener('message', async (e) => {
232
  // ── Sidebar profile ───────────────────────────────────────────────────────
233
 
234
  export function updateSidebarProfile() {
 
235
  const guestEl = document.getElementById('guest-section');
236
  const userEl = document.getElementById('user-section');
237
  const nameEl = document.getElementById('user-name-display');
@@ -239,6 +281,7 @@ export function updateSidebarProfile() {
239
  const avatarEl= document.getElementById('user-avatar');
240
 
241
  if (!currentUser) {
 
242
  guestEl?.classList.remove('hidden');
243
  userEl?.classList.add('hidden');
244
  return;
@@ -252,6 +295,7 @@ export function updateSidebarProfile() {
252
 
253
  const plan = subscriptionInfo?.planKey || 'free';
254
  const planName = subscriptionInfo?.planName || 'Free';
 
255
  if (planEl) { planEl.textContent = planName; planEl.setAttribute('data-plan', plan); }
256
  }
257
 
 
108
  // ── Session handling ──────────────────────────────────────────────────────
109
 
110
  async function handleSupabaseSession(data) {
111
+ console.log('[Frontend Auth] handleSupabaseSession called with token:', data.access_token?.slice(0, 20) + '...');
112
  if (!data.access_token) throw new Error('No access token');
113
  saveAuth({ access_token: data.access_token, refresh_token: data.refresh_token, user: data.user });
114
 
115
  return new Promise((resolve, reject) => {
116
  const tempId = getTempId();
117
  const clientId = getClientId();
118
+ console.log('[Frontend Auth] Sending auth:login to backend with token:', data.access_token?.slice(0, 20) + '...');
119
  send({ type: 'auth:login', accessToken: data.access_token, refreshToken: data.refresh_token,
120
  tempId, clientId });
121
 
122
+ const unsubOk = on('auth:ok', (msg) => {
123
+ console.log('[Frontend Auth] Received auth:ok response');
124
+ unsubOk(); unsubErr(); applyAuthOk(msg); resolve(msg);
125
+ });
126
+ const unsubErr = on('auth:error', (msg) => {
127
+ console.error('[Frontend Auth] Received auth:error:', msg.message);
128
+ unsubOk(); unsubErr(); reject(new Error(msg.message));
129
+ });
130
 
131
  setTimeout(() => { unsubOk(); unsubErr(); reject(new Error('Auth timeout')); }, 12000);
132
  });
133
  }
134
 
135
  function applyAuthOk(msg) {
136
+ console.log('[Frontend Auth] applyAuthOk called with msg:', msg);
137
  currentUser = { id: msg.userId, email: msg.email };
138
  userProfile = msg.profile;
139
  userSettings = msg.settings;
140
  subscriptionInfo = msg.subscription || null;
141
+ console.log('[Frontend Auth] Subscription info set to:', subscriptionInfo);
142
+ console.log('[Frontend Auth] subscriptionInfo?.planKey:', subscriptionInfo?.planKey);
143
+ console.log('[Frontend Auth] subscriptionInfo?.planName:', subscriptionInfo?.planName);
144
  const auth = loadAuth() || {};
145
  saveAuth({ ...auth, userId: msg.userId, deviceToken: msg.deviceToken });
146
  notifyListeners();
147
+ console.log('[Frontend Auth] Calling updateSidebarProfile...');
148
  updateSidebarProfile();
149
  // Apply saved theme
150
  if (msg.settings?.theme) {
 
186
  // ── Reconnect on WS connect ────────────────────────────────────────────────
187
 
188
  on('ws:connected', async () => {
189
+ console.log('[Frontend Auth] WS connected event');
190
  const auth = loadAuth();
191
+ console.log('[Frontend Auth] Loaded auth:', auth ? 'exists' : 'null');
192
  if (auth?.access_token) {
193
+ try {
194
+ console.log('[Frontend Auth] Attempting to resume session with existing token');
195
+ await handleSupabaseSession(auth);
196
+ }
197
+ catch (err) {
198
+ console.error('[Frontend Auth] Failed to resume session:', err);
199
+ clearAuth();
200
+ initAsGuest();
201
+ }
202
  } else {
203
+ console.log('[Frontend Auth] No stored auth, initializing as guest');
204
  initAsGuest();
205
  }
206
  });
 
213
 
214
  window.addEventListener('storage', async (e) => {
215
  if (e.key !== OAUTH_PENDING_KEY || !e.newValue) return;
216
+ console.log('[Frontend Auth] OAuth pending key detected in localStorage');
217
  // Consume immediately so other tabs don't also try to log in
218
  localStorage.removeItem(OAUTH_PENDING_KEY);
219
  let tokens;
220
+ try { tokens = JSON.parse(e.newValue); } catch {
221
+ console.error('[Frontend Auth] Failed to parse OAuth tokens');
222
+ return;
223
+ }
224
+ if (!tokens?.access_token) {
225
+ console.warn('[Frontend Auth] No access token in OAuth response');
226
+ return;
227
+ }
228
+ console.log('[Frontend Auth] Processing OAuth tokens:', tokens.access_token?.slice(0, 20) + '...');
229
  try {
230
  await handleSupabaseSession(tokens);
231
  import('./ui.js').then(({ showNotification }) =>
232
  showNotification({ type: 'success', message: 'Signed in!', duration: 2500 }));
233
  } catch (err) {
234
+ console.error('[Frontend Auth] OAuth sign-in error:', err);
235
  import('./ui.js').then(({ showNotification }) =>
236
  showNotification({ type: 'error', message: `Sign-in failed: ${err.message}`, duration: 4000 }));
237
  }
 
241
  (function checkOAuthRedirect() {
242
  const params = new URLSearchParams(location.search);
243
  const t = params.get('t'), r = params.get('r');
244
+ console.log('[Frontend Auth] Checking for OAuth redirect params:', t ? 'found token' : 'no token');
245
  if (params.get('oauth') === '1' && t) {
246
+ console.log('[Frontend Auth] Processing OAuth redirect with token:', t.slice(0, 20) + '...');
247
  history.replaceState({}, '', '/');
248
+ handleSupabaseSession({ access_token: t, refresh_token: r || '' }).catch((err) => {
249
+ console.error('[Frontend Auth] OAuth redirect failed:', err);
250
+ });
251
  }
252
  })();
253
 
 
255
  window.addEventListener('message', async (e) => {
256
  if (e.origin !== location.origin) return;
257
  if (e.data?.type !== 'oauth:callback') return;
258
+ console.log('[Frontend Auth] postMessage oauth:callback received');
259
  const { access_token, refresh_token } = e.data;
260
+ if (!access_token) {
261
+ console.warn('[Frontend Auth] No access_token in postMessage oauth:callback');
262
+ return;
263
+ }
264
+ console.log('[Frontend Auth] Processing postMessage tokens:', access_token?.slice(0, 20) + '...');
265
  try { await handleSupabaseSession({ access_token, refresh_token }); }
266
  catch (err) {
267
+ console.error('[Frontend Auth] postMessage sign-in error:', err);
268
  import('./ui.js').then(({ showNotification }) =>
269
  showNotification({ type: 'error', message: `Sign-in failed: ${err.message}`, duration: 4000 }));
270
  }
 
273
  // ── Sidebar profile ───────────────────────────────────────────────────────
274
 
275
  export function updateSidebarProfile() {
276
+ console.log('[Frontend Auth] updateSidebarProfile called - currentUser:', currentUser);
277
  const guestEl = document.getElementById('guest-section');
278
  const userEl = document.getElementById('user-section');
279
  const nameEl = document.getElementById('user-name-display');
 
281
  const avatarEl= document.getElementById('user-avatar');
282
 
283
  if (!currentUser) {
284
+ console.log('[Frontend Auth] Not logged in, showing guest section');
285
  guestEl?.classList.remove('hidden');
286
  userEl?.classList.add('hidden');
287
  return;
 
295
 
296
  const plan = subscriptionInfo?.planKey || 'free';
297
  const planName = subscriptionInfo?.planName || 'Free';
298
+ console.log('[Frontend Auth] Setting plan display - plan:', plan, 'planName:', planName, 'subscriptionInfo:', subscriptionInfo);
299
  if (planEl) { planEl.textContent = planName; planEl.setAttribute('data-plan', plan); }
300
  }
301
 
public/js/settings.js CHANGED
@@ -250,11 +250,15 @@ function setupAccountSettings(b) {
250
 
251
  const subHandler = (msg) => {
252
  off('account:subscription', subHandler);
 
253
  const planEl = b.querySelector('#plan-name-display');
254
  if (planEl && msg.info) {
255
  const pKey = msg.info.planKey || 'free';
256
  const pName = msg.info.planName || 'Free Tier';
 
257
  planEl.innerHTML = `<span style="color:var(--plan-${pKey})">${escHtml(pName)}</span>`;
 
 
258
  }
259
  };
260
  on('account:subscription', subHandler);
 
250
 
251
  const subHandler = (msg) => {
252
  off('account:subscription', subHandler);
253
+ console.log('[Settings] Received account:subscription:', msg);
254
  const planEl = b.querySelector('#plan-name-display');
255
  if (planEl && msg.info) {
256
  const pKey = msg.info.planKey || 'free';
257
  const pName = msg.info.planName || 'Free Tier';
258
+ console.log('[Settings] Updating plan display - key:', pKey, 'name:', pName);
259
  planEl.innerHTML = `<span style="color:var(--plan-${pKey})">${escHtml(pName)}</span>`;
260
+ } else {
261
+ console.warn('[Settings] No plan info available:', msg.info);
262
  }
263
  };
264
  on('account:subscription', subHandler);
server/auth.js CHANGED
@@ -62,11 +62,22 @@ export async function setUsername(userId, accessToken, username) {
62
 
63
  export async function getSubscriptionInfo(accessToken) {
64
  try {
 
65
  const r = await fetch('https://sharktide-lightning.hf.space/subscription', {
66
  headers: { Authorization: `Bearer ${accessToken}`, Accept: 'application/json' },
67
  });
68
- return r.ok ? r.json() : null;
69
- } catch { return null; }
 
 
 
 
 
 
 
 
 
 
70
  }
71
 
72
  export async function getTierConfig() {
 
62
 
63
  export async function getSubscriptionInfo(accessToken) {
64
  try {
65
+ console.log('[Subscription] Fetching subscription info for token:', accessToken?.slice(0, 20) + '...');
66
  const r = await fetch('https://sharktide-lightning.hf.space/subscription', {
67
  headers: { Authorization: `Bearer ${accessToken}`, Accept: 'application/json' },
68
  });
69
+ console.log('[Subscription] Response status:', r.status);
70
+ if (!r.ok) {
71
+ console.warn(`[Subscription] Failed: HTTP ${r.status}`);
72
+ return null;
73
+ }
74
+ const data = await r.json();
75
+ console.log('[Subscription] Response data:', data);
76
+ return data;
77
+ } catch (err) {
78
+ console.error('[Subscription] Error fetching subscription info:', err.message);
79
+ return null;
80
+ }
81
  }
82
 
83
  export async function getTierConfig() {
server/wsHandler.js CHANGED
@@ -78,9 +78,11 @@ const handlers = {
78
 
79
  'auth:login': async (ws, msg, client, wsClients) => {
80
  const { accessToken, tempId: clientTempId } = msg;
 
81
  if (!accessToken) return safeSend(ws, { type: 'auth:error', message: 'Missing token' });
82
  const user = await verifySupabaseToken(accessToken);
83
  if (!user) return safeSend(ws, { type: 'auth:error', message: 'Invalid token' });
 
84
 
85
  client.userId = user.id; client.accessToken = accessToken; client.authenticated = true;
86
  client.deviceToken = deviceSessionStore.create(user.id, client.ip, client.userAgent);
@@ -90,15 +92,19 @@ const handlers = {
90
  const tId = client.tempId;
91
  await sessionStore.transferTempToUser(tId, user.id, accessToken);
92
 
 
93
  const [sessions, settings, profile, subscription] = await Promise.all([
94
  sessionStore.loadUserSessions(user.id, accessToken),
95
  getUserSettings(user.id, accessToken),
96
  getUserProfile(user.id, accessToken),
97
  getSubscriptionInfo(accessToken),
98
  ]);
 
99
 
100
- safeSend(ws, { type: 'auth:ok', userId: user.id, email: user.email,
101
- deviceToken: client.deviceToken, sessions: sessions.map(ser), settings, profile, subscription });
 
 
102
 
103
  bcast(wsClients, user.id, { type: 'auth:newLogin', message: 'New login on your account.',
104
  ip: client.ip, userAgent: client.userAgent, timestamp: new Date().toISOString() }, ws);
@@ -381,7 +387,13 @@ const handlers = {
381
 
382
  'account:getProfile': async (ws, msg, c) => { if (!c.userId) return; safeSend(ws, { type: 'account:profile', profile: await getUserProfile(c.userId, c.accessToken) }); },
383
  'account:setUsername': async (ws, msg, c) => { if (!c.userId) return; safeSend(ws, { type: 'account:usernameResult', ...await setUsername(c.userId, c.accessToken, msg.username) }); },
384
- 'account:getSubscription': async (ws, msg, c) => { if (!c.userId) return; safeSend(ws, { type: 'account:subscription', info: await getSubscriptionInfo(c.accessToken) }); },
 
 
 
 
 
 
385
  'account:getUsage': async (ws, msg, c) => { safeSend(ws, { type: 'account:usage', usage: await getUsageInfo(c.accessToken) }); },
386
  'account:getTierConfig': async (ws) => { safeSend(ws, { type: 'account:tierConfig', config: await getTierConfig() }); },
387
  'account:getSessions': (ws, msg, c) => { if (!c.userId) return; safeSend(ws, { type: 'account:deviceSessions', sessions: deviceSessionStore.getForUser(c.userId), currentToken: c.deviceToken }); },
 
78
 
79
  'auth:login': async (ws, msg, client, wsClients) => {
80
  const { accessToken, tempId: clientTempId } = msg;
81
+ console.log('[Auth] Login request received, token:', accessToken?.slice(0, 20) + '...');
82
  if (!accessToken) return safeSend(ws, { type: 'auth:error', message: 'Missing token' });
83
  const user = await verifySupabaseToken(accessToken);
84
  if (!user) return safeSend(ws, { type: 'auth:error', message: 'Invalid token' });
85
+ console.log('[Auth] User verified:', user.id);
86
 
87
  client.userId = user.id; client.accessToken = accessToken; client.authenticated = true;
88
  client.deviceToken = deviceSessionStore.create(user.id, client.ip, client.userAgent);
 
92
  const tId = client.tempId;
93
  await sessionStore.transferTempToUser(tId, user.id, accessToken);
94
 
95
+ console.log('[Auth] Fetching user data...');
96
  const [sessions, settings, profile, subscription] = await Promise.all([
97
  sessionStore.loadUserSessions(user.id, accessToken),
98
  getUserSettings(user.id, accessToken),
99
  getUserProfile(user.id, accessToken),
100
  getSubscriptionInfo(accessToken),
101
  ]);
102
+ console.log('[Auth] User data fetched - subscription:', subscription);
103
 
104
+ const authOkMsg = { type: 'auth:ok', userId: user.id, email: user.email,
105
+ deviceToken: client.deviceToken, sessions: sessions.map(ser), settings, profile, subscription };
106
+ console.log('[Auth] Sending auth:ok with subscription:', subscription);
107
+ safeSend(ws, authOkMsg);
108
 
109
  bcast(wsClients, user.id, { type: 'auth:newLogin', message: 'New login on your account.',
110
  ip: client.ip, userAgent: client.userAgent, timestamp: new Date().toISOString() }, ws);
 
387
 
388
  'account:getProfile': async (ws, msg, c) => { if (!c.userId) return; safeSend(ws, { type: 'account:profile', profile: await getUserProfile(c.userId, c.accessToken) }); },
389
  'account:setUsername': async (ws, msg, c) => { if (!c.userId) return; safeSend(ws, { type: 'account:usernameResult', ...await setUsername(c.userId, c.accessToken, msg.username) }); },
390
+ 'account:getSubscription': async (ws, msg, c) => {
391
+ if (!c.userId) return console.warn('[Account] getSubscription called without userId');
392
+ console.log('[Account] getSubscription request for user:', c.userId);
393
+ const subInfo = await getSubscriptionInfo(c.accessToken);
394
+ console.log('[Account] getSubscription response:', subInfo);
395
+ safeSend(ws, { type: 'account:subscription', info: subInfo });
396
+ },
397
  'account:getUsage': async (ws, msg, c) => { safeSend(ws, { type: 'account:usage', usage: await getUsageInfo(c.accessToken) }); },
398
  'account:getTierConfig': async (ws) => { safeSend(ws, { type: 'account:tierConfig', config: await getTierConfig() }); },
399
  'account:getSessions': (ws, msg, c) => { if (!c.userId) return; safeSend(ws, { type: 'account:deviceSessions', sessions: deviceSessionStore.getForUser(c.userId), currentToken: c.deviceToken }); },