Martechsol commited on
Commit ·
2a507e3
1
Parent(s): b336189
Restore original proven localStorage scan + add Bearer token API fallback + retry
Browse files- static/addon.html +95 -185
static/addon.html
CHANGED
|
@@ -343,216 +343,65 @@
|
|
| 343 |
}
|
| 344 |
|
| 345 |
// ── User Integration & History ──────────────────────────────────────────
|
| 346 |
-
|
| 347 |
-
|
| 348 |
-
|
| 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
|
| 387 |
-
|
| 388 |
-
|
| 389 |
-
|
| 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(
|
| 405 |
-
|
| 406 |
-
|
| 407 |
-
|
|
|
|
|
|
|
| 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 |
-
|
| 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 |
-
//
|
| 472 |
-
|
| 473 |
-
|
| 474 |
-
|
| 475 |
-
|
| 476 |
-
|
| 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 |
-
|
| 491 |
-
|
| 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:
|
| 498 |
credentials: 'include'
|
| 499 |
});
|
| 500 |
if (pResp.ok) {
|
| 501 |
const pData = await pResp.json();
|
| 502 |
-
|
| 503 |
-
if (n) return n;
|
| 504 |
}
|
| 505 |
} catch (e) {}
|
| 506 |
}
|
| 507 |
|
| 508 |
-
//
|
| 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 (
|
| 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 |
-
|
| 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 |
-
//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 571 |
if (!userName) {
|
| 572 |
let retries = 0;
|
| 573 |
-
const maxRetries = 12;
|
| 574 |
const retryInterval = setInterval(async () => {
|
| 575 |
retries++;
|
| 576 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 577 |
if (resolved) {
|
| 578 |
clearInterval(retryInterval);
|
| 579 |
-
|
| 580 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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);
|