Martechsol commited on
Commit
2a507e3
·
1 Parent(s): b336189

Restore original proven localStorage scan + add Bearer token API fallback + retry

Browse files
Files changed (1) hide show
  1. static/addon.html +95 -185
static/addon.html CHANGED
@@ -343,216 +343,65 @@
343
  }
344
 
345
  // ── User Integration & History ──────────────────────────────────────────
346
-
347
- // ── Name Discovery Engine ───────────────────────────────────────────────
348
- function _extractNameFromObj(obj, depth) {
349
- if (!obj || typeof obj !== 'object' || depth > 3) return null;
350
- // Direct field paths (covers virtually every HR/auth framework)
351
- const directPaths = [
352
- 'name', 'fullName', 'full_name', 'displayName', 'display_name',
353
- 'userName', 'username', 'user_name',
354
- 'firstName', 'first_name', 'lastname', 'lastName', 'last_name',
355
- 'empName', 'emp_name', 'employeeName', 'employee_name',
356
- ];
357
- for (const key of directPaths) {
358
- if (typeof obj[key] === 'string' && obj[key].trim().length > 1) {
359
- return obj[key].trim();
360
- }
361
- }
362
- // Composite: firstName + lastName
363
- const fn = obj.firstName || obj.first_name || obj.fname || '';
364
- const ln = obj.lastName || obj.last_name || obj.lname || '';
365
- if (fn && ln) return `${fn} ${ln}`.trim();
366
- if (fn) return fn.trim();
367
-
368
- // Nested objects — recurse into common wrapper keys
369
- const nestedKeys = [
370
- 'user', 'data', 'profile', 'personalDetails', 'personal_details',
371
- 'employee', 'emp', 'info', 'userInfo', 'userData', 'userProfile',
372
- 'account', 'auth', 'session', 'result', 'payload', 'attributes',
373
- 'claims', 'identity',
374
- ];
375
- for (const nk of nestedKeys) {
376
- if (obj[nk] && typeof obj[nk] === 'object') {
377
- const found = _extractNameFromObj(obj[nk], depth + 1);
378
- if (found) return found;
379
- }
380
- }
381
- return null;
382
- }
383
-
384
- function _decodeJWT(token) {
385
  try {
386
- const parts = token.split('.');
387
- if (parts.length !== 3) return null;
388
- const payload = JSON.parse(atob(parts[1].replace(/-/g, '+').replace(/_/g, '/')));
389
- return _extractNameFromObj(payload, 0);
390
- } catch (e) { return null; }
391
- }
392
-
393
- function _discoverFromStorage() {
394
- const stores = [localStorage, sessionStorage];
395
- for (const store of stores) {
396
- try {
397
- for (let i = 0; i < store.length; i++) {
398
- const key = store.key(i);
399
- if (key.startsWith('martech_')) continue;
400
- const val = store.getItem(key);
401
- if (!val) continue;
402
-
403
  try {
404
- const parsed = JSON.parse(val);
405
- if (typeof parsed === 'object' && parsed !== null) {
406
- const found = _extractNameFromObj(parsed, 0);
407
- if (found) return found;
 
 
408
  }
409
- } catch (e) {}
410
-
411
- if (val.split('.').length === 3 && val.length > 30) {
412
- const jwtName = _decodeJWT(val);
413
- if (jwtName) return jwtName;
414
- }
415
-
416
- if (typeof val === 'string' && val.length > 1 && val.length < 60
417
- && !val.includes('{') && !val.includes('http') && !val.includes('=')
418
- && /^[A-Za-z\s.\-']+$/.test(val)
419
- && (key.toLowerCase().includes('name') || key.toLowerCase().includes('user'))) {
420
- return val.trim();
421
- }
422
- }
423
- } catch (e) {}
424
- }
425
-
426
- // JWT tokens from cookies
427
- try {
428
- const cookies = document.cookie.split(';');
429
- for (const c of cookies) {
430
- const val = c.split('=').slice(1).join('=').trim();
431
- if (val.split('.').length === 3 && val.length > 30) {
432
- const jwtName = _decodeJWT(val);
433
- if (jwtName) return jwtName;
434
- }
435
- }
436
- } catch (e) {}
437
-
438
- // DOM elements
439
- try {
440
- const selectors = [
441
- '.user-name', '.username', '.user-display-name', '.profile-name',
442
- '#user-name', '#username', '#userName', '#profileName',
443
- '[data-user-name]', '[data-username]',
444
- '.navbar .name', '.header .name', '.topbar .name',
445
- '.user-info .name', '.user-profile .name',
446
- ];
447
- for (const sel of selectors) {
448
- const el = document.querySelector(sel);
449
- if (el) {
450
- const text = (el.textContent || el.getAttribute('data-user-name') || '').trim();
451
- if (text.length > 1 && text.length < 60 && /^[A-Za-z\s.\-']+$/.test(text)) {
452
- return text;
453
  }
454
  }
455
  }
456
  } catch (e) {}
457
 
458
- return null;
459
- }
460
-
461
- function _findAuthToken() {
462
- const tokenKeys = [
463
- 'token', 'accessToken', 'access_token', 'authToken', 'auth_token',
464
- 'jwt', 'jwtToken', 'jwt_token', 'bearer', 'bearerToken',
465
- 'id_token', 'idToken', 'userToken', 'user_token', 'session_token',
466
- 'x-auth-token', 'Authorization',
467
- ];
468
- const stores = [localStorage, sessionStorage];
469
- for (const store of stores) {
470
  try {
471
- // Check common token key names first
472
- for (const key of tokenKeys) {
473
- const val = store.getItem(key);
474
- if (val && val.length > 20) return val.replace(/^Bearer\s+/i, '');
475
- }
476
- // Brute-scan: any value that looks like a JWT
477
- for (let i = 0; i < store.length; i++) {
478
- const key = store.key(i);
479
- if (key.startsWith('martech_')) continue;
480
- const val = store.getItem(key);
481
- if (val && val.split('.').length === 3 && val.length > 50 && !val.includes(' ')) {
482
- return val;
483
- }
484
  }
485
- } catch (e) {}
486
- }
487
- return null;
488
- }
489
 
490
- async function _tryResolveName() {
491
- const authToken = _findAuthToken();
492
 
493
- // 1. PRIMARY: Fetch from the HR portal profile API with Bearer token
494
- if (authToken) {
495
- try {
496
  const pResp = await fetch("https://hrmmartechsol-ba71e8dc3fa2.herokuapp.com/api/users/profile", {
497
- headers: { 'Authorization': 'Bearer ' + authToken },
498
  credentials: 'include'
499
  });
500
  if (pResp.ok) {
501
  const pData = await pResp.json();
502
- const n = pData.name || _extractNameFromObj(pData, 0);
503
- if (n) return n;
504
  }
505
  } catch (e) {}
506
  }
507
 
508
- // 2. Try without token (cookie-based fallback)
509
- try {
510
- const pResp = await fetch("https://hrmmartechsol-ba71e8dc3fa2.herokuapp.com/api/users/profile", {
511
- credentials: 'include'
512
- });
513
- if (pResp.ok) {
514
- const pData = await pResp.json();
515
- const n = pData.name || _extractNameFromObj(pData, 0);
516
- if (n) return n;
517
- }
518
- } catch (e) {}
519
-
520
- // 3. FALLBACK: Deep-scan localStorage, sessionStorage, cookies, DOM
521
- return _discoverFromStorage();
522
- }
523
-
524
- async function _syncNameWithBackend(name) {
525
  const lastKnownName = localStorage.getItem('martech_last_user_name');
526
- if (name && lastKnownName && name !== lastKnownName) {
527
  localStorage.removeItem('martech_session_id');
528
  sessionId = 'user-' + Date.now() + '-' + Math.random().toString(36).slice(2, 9);
529
  localStorage.setItem('martech_session_id', sessionId);
530
  messagesContainer.innerHTML = '<div class="chat-message bot">Welcome! How can I help you today?</div>';
531
  }
532
- if (name) {
533
- localStorage.setItem('martech_last_user_name', name);
534
- }
535
- await fetch(BASE_URL + "/api/session/update-name", {
536
- method: 'POST',
537
- headers: { 'Content-Type': 'application/json' },
538
- body: JSON.stringify({ session_id: sessionId, user_name: name })
539
- }).then(r => r.json()).then(rData => {
540
- if (rData.new_session_id && rData.new_session_id !== sessionId) {
541
- sessionId = rData.new_session_id;
542
- localStorage.setItem('martech_session_id', sessionId);
543
- }
544
- }).catch(() => {});
545
- }
546
-
547
- async function initUserSession() {
548
- let userName = await _tryResolveName();
549
-
550
- // If name found, sync immediately
551
  if (userName) {
552
- await _syncNameWithBackend(userName);
553
  }
554
 
555
- // Fetch history for this session
556
  try {
557
  const hResp = await fetch(BASE_URL + "/api/session/history/" + sessionId);
558
  if (hResp.ok) {
@@ -567,17 +416,78 @@
567
  }
568
  } catch (e) {}
569
 
570
- // If name NOT found, keep retrying in the background (every 5s, up to 1 min)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
571
  if (!userName) {
572
  let retries = 0;
573
- const maxRetries = 12;
574
  const retryInterval = setInterval(async () => {
575
  retries++;
576
- const resolved = await _tryResolveName();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
577
  if (resolved) {
578
  clearInterval(retryInterval);
579
- await _syncNameWithBackend(resolved);
580
- } else if (retries >= maxRetries) {
 
 
 
 
 
 
 
 
 
 
581
  clearInterval(retryInterval);
582
  }
583
  }, 5000);
 
343
  }
344
 
345
  // ── User Integration & History ──────────────────────────────────────────
346
+ async function initUserSession() {
347
+ // 1. Discover User Name from localStorage/sessionStorage (PROVEN - was working)
348
+ let userName = null;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
349
  try {
350
+ const storageKeys = ['userInfo', 'user', 'profile', 'auth', 'currentUser', 'account'];
351
+ for (const key of storageKeys) {
352
+ const stored = localStorage.getItem(key) || sessionStorage.getItem(key);
353
+ if (stored) {
 
 
 
 
 
 
 
 
 
 
 
 
 
354
  try {
355
+ const parsed = JSON.parse(stored);
356
+ userName = parsed.name || parsed.fullName || (parsed.user && parsed.user.name) || parsed.display_name;
357
+ if (userName) break;
358
+ } catch(err) {
359
+ if (typeof stored === 'string' && stored.length < 50 && !stored.includes('{')) {
360
+ userName = stored; break;
361
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
362
  }
363
  }
364
  }
365
  } catch (e) {}
366
 
367
+ // 2. If localStorage didn't have it, try the profile API with Bearer token
368
+ if (!userName) {
 
 
 
 
 
 
 
 
 
 
369
  try {
370
+ // Find auth token from localStorage
371
+ const tokenKeys = ['token', 'accessToken', 'access_token', 'authToken', 'auth_token', 'jwt', 'jwtToken'];
372
+ let authToken = null;
373
+ for (const tk of tokenKeys) {
374
+ const tv = localStorage.getItem(tk) || sessionStorage.getItem(tk);
375
+ if (tv && tv.length > 20) { authToken = tv.replace(/^Bearer\s+/i, ''); break; }
 
 
 
 
 
 
 
376
  }
 
 
 
 
377
 
378
+ const headers = {};
379
+ if (authToken) headers['Authorization'] = 'Bearer ' + authToken;
380
 
 
 
 
381
  const pResp = await fetch("https://hrmmartechsol-ba71e8dc3fa2.herokuapp.com/api/users/profile", {
382
+ headers: headers,
383
  credentials: 'include'
384
  });
385
  if (pResp.ok) {
386
  const pData = await pResp.json();
387
+ userName = pData.name || (pData.personalDetails && pData.personalDetails.fullName);
 
388
  }
389
  } catch (e) {}
390
  }
391
 
392
+ // 3. Multi-user logic: if name changed, clear old session and start fresh
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
393
  const lastKnownName = localStorage.getItem('martech_last_user_name');
394
+ if (userName && lastKnownName && userName !== lastKnownName) {
395
  localStorage.removeItem('martech_session_id');
396
  sessionId = 'user-' + Date.now() + '-' + Math.random().toString(36).slice(2, 9);
397
  localStorage.setItem('martech_session_id', sessionId);
398
  messagesContainer.innerHTML = '<div class="chat-message bot">Welcome! How can I help you today?</div>';
399
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
400
  if (userName) {
401
+ localStorage.setItem('martech_last_user_name', userName);
402
  }
403
 
404
+ // 4. Fetch history for this session
405
  try {
406
  const hResp = await fetch(BASE_URL + "/api/session/history/" + sessionId);
407
  if (hResp.ok) {
 
416
  }
417
  } catch (e) {}
418
 
419
+ // 5. Sync name with backend
420
+ if (userName) {
421
+ await fetch(BASE_URL + "/api/session/update-name", {
422
+ method: 'POST',
423
+ headers: { 'Content-Type': 'application/json' },
424
+ body: JSON.stringify({ session_id: sessionId, user_name: userName })
425
+ }).then(r => r.json()).then(rData => {
426
+ if (rData.new_session_id && rData.new_session_id !== sessionId) {
427
+ sessionId = rData.new_session_id;
428
+ localStorage.setItem('martech_session_id', sessionId);
429
+ }
430
+ }).catch(() => {});
431
+ }
432
+
433
+ // 6. If name NOT found, keep retrying in the background (every 5s, up to 1 min)
434
  if (!userName) {
435
  let retries = 0;
 
436
  const retryInterval = setInterval(async () => {
437
  retries++;
438
+ let resolved = null;
439
+
440
+ // Re-check localStorage
441
+ try {
442
+ const storageKeys = ['userInfo', 'user', 'profile', 'auth', 'currentUser', 'account'];
443
+ for (const key of storageKeys) {
444
+ const stored = localStorage.getItem(key) || sessionStorage.getItem(key);
445
+ if (stored) {
446
+ try {
447
+ const parsed = JSON.parse(stored);
448
+ resolved = parsed.name || parsed.fullName || (parsed.user && parsed.user.name) || parsed.display_name;
449
+ if (resolved) break;
450
+ } catch(err) {}
451
+ }
452
+ }
453
+ } catch (e) {}
454
+
455
+ // Re-try profile API
456
+ if (!resolved) {
457
+ try {
458
+ const tokenKeys = ['token', 'accessToken', 'access_token', 'authToken', 'auth_token', 'jwt', 'jwtToken'];
459
+ let authToken = null;
460
+ for (const tk of tokenKeys) {
461
+ const tv = localStorage.getItem(tk) || sessionStorage.getItem(tk);
462
+ if (tv && tv.length > 20) { authToken = tv.replace(/^Bearer\s+/i, ''); break; }
463
+ }
464
+ const headers = {};
465
+ if (authToken) headers['Authorization'] = 'Bearer ' + authToken;
466
+ const pResp = await fetch("https://hrmmartechsol-ba71e8dc3fa2.herokuapp.com/api/users/profile", {
467
+ headers: headers,
468
+ credentials: 'include'
469
+ });
470
+ if (pResp.ok) {
471
+ const pData = await pResp.json();
472
+ resolved = pData.name || (pData.personalDetails && pData.personalDetails.fullName);
473
+ }
474
+ } catch (e) {}
475
+ }
476
+
477
  if (resolved) {
478
  clearInterval(retryInterval);
479
+ localStorage.setItem('martech_last_user_name', resolved);
480
+ await fetch(BASE_URL + "/api/session/update-name", {
481
+ method: 'POST',
482
+ headers: { 'Content-Type': 'application/json' },
483
+ body: JSON.stringify({ session_id: sessionId, user_name: resolved })
484
+ }).then(r => r.json()).then(rData => {
485
+ if (rData.new_session_id && rData.new_session_id !== sessionId) {
486
+ sessionId = rData.new_session_id;
487
+ localStorage.setItem('martech_session_id', sessionId);
488
+ }
489
+ }).catch(() => {});
490
+ } else if (retries >= 12) {
491
  clearInterval(retryInterval);
492
  }
493
  }, 5000);