varminer-app-header 2.2.3 → 2.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/AppHeader.d.ts.map +1 -1
- package/dist/index.d.ts +9 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.esm.js +163 -11
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +163 -10
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/localStorage.d.ts +6 -1
- package/dist/utils/localStorage.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -421,17 +421,161 @@ function getAccessTokenFromAuth(auth) {
|
|
|
421
421
|
auth.token;
|
|
422
422
|
return typeof token === "string" && token.length > 0 ? token : undefined;
|
|
423
423
|
}
|
|
424
|
-
|
|
424
|
+
/** True if string looks like a JWT (three base64 parts) or a bearer token, not JSON. */
|
|
425
|
+
function looksLikeToken(s) {
|
|
426
|
+
return s.length > 0 && !s.trimStart().startsWith("{") && !s.trimStart().startsWith("[");
|
|
427
|
+
}
|
|
428
|
+
/** Recursively find first string value at keys accessToken, access_token, or token. */
|
|
429
|
+
function findTokenInObject(obj) {
|
|
430
|
+
if (obj === null || obj === undefined)
|
|
431
|
+
return undefined;
|
|
432
|
+
if (typeof obj === "string")
|
|
433
|
+
return looksLikeToken(obj) ? obj : undefined;
|
|
434
|
+
if (typeof obj !== "object")
|
|
435
|
+
return undefined;
|
|
436
|
+
const rec = obj;
|
|
437
|
+
const direct = rec.accessToken ?? rec.access_token ?? rec.token;
|
|
438
|
+
if (typeof direct === "string" && looksLikeToken(direct))
|
|
439
|
+
return direct;
|
|
440
|
+
for (const key of Object.keys(rec)) {
|
|
441
|
+
const found = findTokenInObject(rec[key]);
|
|
442
|
+
if (found)
|
|
443
|
+
return found;
|
|
444
|
+
}
|
|
445
|
+
return undefined;
|
|
446
|
+
}
|
|
447
|
+
/** Parse redux-persist style value (may be double stringified). */
|
|
448
|
+
function parsePersistValue(raw) {
|
|
449
|
+
if (!raw)
|
|
450
|
+
return null;
|
|
425
451
|
try {
|
|
452
|
+
const first = JSON.parse(raw);
|
|
453
|
+
if (typeof first === "string") {
|
|
454
|
+
try {
|
|
455
|
+
return JSON.parse(first);
|
|
456
|
+
}
|
|
457
|
+
catch {
|
|
458
|
+
return first;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
return first;
|
|
462
|
+
}
|
|
463
|
+
catch {
|
|
464
|
+
return null;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
/** Get a nested value that may be a string (need parse) or already an object. */
|
|
468
|
+
function parseNestedValue(val) {
|
|
469
|
+
if (val == null)
|
|
470
|
+
return null;
|
|
471
|
+
if (typeof val === "string")
|
|
472
|
+
return parsePersistValue(val);
|
|
473
|
+
return val;
|
|
474
|
+
}
|
|
475
|
+
const AUTH_DEBUG = true; // set to false to disable access-token debug logs
|
|
476
|
+
/**
|
|
477
|
+
* Extract access token from persist:userdb shape: authDetails (string) → parse → auth.accessToken.
|
|
478
|
+
* Structure: { authDetails: "{\"auth\":{\"accessToken\":\"...\"}}", profileInformation: "...", _persist: "..." }
|
|
479
|
+
*/
|
|
480
|
+
function getTokenFromUserDbPersist(parsed) {
|
|
481
|
+
console.log("[header-auth] getTokenFromUserDbPersist: parsed keys", Object.keys(parsed));
|
|
482
|
+
const authDetails = parsed.authDetails;
|
|
483
|
+
const inner = parseNestedValue(authDetails);
|
|
484
|
+
const obj = typeof inner === "object" && inner !== null ? inner : null;
|
|
485
|
+
console.log("[header-auth] getTokenFromUserDbPersist: inner type", typeof inner, "obj keys", obj ? Object.keys(obj) : null);
|
|
486
|
+
if (!obj)
|
|
487
|
+
return undefined;
|
|
488
|
+
const auth = obj.auth;
|
|
489
|
+
const authObj = typeof auth === "object" && auth !== null ? auth : null;
|
|
490
|
+
console.log("[header-auth] getTokenFromUserDbPersist: authObj keys", authObj ? Object.keys(authObj) : null);
|
|
491
|
+
if (!authObj)
|
|
492
|
+
return undefined;
|
|
493
|
+
const token = authObj.accessToken ??
|
|
494
|
+
authObj.access_token ??
|
|
495
|
+
authObj.token;
|
|
496
|
+
const ok = typeof token === "string" && looksLikeToken(token);
|
|
497
|
+
console.log("[header-auth] getTokenFromUserDbPersist: token found", !!ok, "length", typeof token === "string" ? token.length : 0);
|
|
498
|
+
return ok ? token : undefined;
|
|
499
|
+
}
|
|
500
|
+
/**
|
|
501
|
+
* Get access token from all known storage keys (header, IAM, userdb, and plain keys).
|
|
502
|
+
* persist:userdb shape: authDetails (stringified) contains auth.accessToken.
|
|
503
|
+
*/
|
|
504
|
+
function getAccessTokenForRequest() {
|
|
505
|
+
console.log("[header-auth] getAccessTokenForRequest: start");
|
|
506
|
+
const keysToTry = [
|
|
507
|
+
PERSIST_HEADER_KEY,
|
|
508
|
+
"persist:linn-i-am",
|
|
509
|
+
"persist:userdb",
|
|
510
|
+
"token",
|
|
511
|
+
"accessToken",
|
|
512
|
+
"auth",
|
|
513
|
+
];
|
|
514
|
+
for (const key of keysToTry) {
|
|
515
|
+
const raw = localStorage.getItem(key);
|
|
516
|
+
console.log("[header-auth] getAccessTokenForRequest: key", key, "raw present", !!raw, "raw length", raw?.length ?? 0);
|
|
517
|
+
if (!raw)
|
|
518
|
+
continue;
|
|
519
|
+
const parsed = key.startsWith("persist:") ? parsePersistValue(raw) : (() => { try {
|
|
520
|
+
return JSON.parse(raw);
|
|
521
|
+
}
|
|
522
|
+
catch {
|
|
523
|
+
return raw;
|
|
524
|
+
} })();
|
|
525
|
+
const token = findTokenInObject(parsed);
|
|
526
|
+
if (token) {
|
|
527
|
+
console.log("[header-auth] getAccessTokenForRequest: token from findTokenInObject(parsed), key", key);
|
|
528
|
+
return token;
|
|
529
|
+
}
|
|
530
|
+
if (key.startsWith("persist:")) {
|
|
531
|
+
const outer = typeof parsed === "object" && parsed !== null ? parsed : null;
|
|
532
|
+
if (outer) {
|
|
533
|
+
if (key === "persist:userdb") {
|
|
534
|
+
const fromUserDb = getTokenFromUserDbPersist(outer);
|
|
535
|
+
if (fromUserDb) {
|
|
536
|
+
console.log("[header-auth] getAccessTokenForRequest: token from getTokenFromUserDbPersist");
|
|
537
|
+
return fromUserDb;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
for (const k of Object.keys(outer)) {
|
|
541
|
+
const inner = parseNestedValue(outer[k]);
|
|
542
|
+
const t = findTokenInObject(inner);
|
|
543
|
+
if (t) {
|
|
544
|
+
console.log("[header-auth] getAccessTokenForRequest: token from inner key", k);
|
|
545
|
+
return t;
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
const allData = getAllDataFromStorage();
|
|
552
|
+
const fallback = getAccessTokenFromAuth(allData.auth);
|
|
553
|
+
console.log("[header-auth] getAccessTokenForRequest: fallback getAllDataFromStorage().auth, token present", !!fallback);
|
|
554
|
+
return fallback;
|
|
555
|
+
}
|
|
556
|
+
const fetchProfilePictureAsBlobUrl = async (baseUrl = "http://objectstore.impact0mics.local:9012", accessTokenOverride) => {
|
|
557
|
+
try {
|
|
558
|
+
if (AUTH_DEBUG)
|
|
559
|
+
console.log("[header-auth] fetchProfilePictureAsBlobUrl: start, accessTokenOverride present", typeof accessTokenOverride === "string" && accessTokenOverride.length > 0);
|
|
426
560
|
const profilePictureUrl = getProfilePictureUrl(baseUrl);
|
|
427
|
-
if (!profilePictureUrl)
|
|
561
|
+
if (!profilePictureUrl) {
|
|
562
|
+
if (AUTH_DEBUG)
|
|
563
|
+
console.log("[header-auth] fetchProfilePictureAsBlobUrl: no profilePictureUrl, abort");
|
|
428
564
|
return null;
|
|
429
|
-
|
|
430
|
-
|
|
565
|
+
}
|
|
566
|
+
if (AUTH_DEBUG)
|
|
567
|
+
console.log("[header-auth] fetchProfilePictureAsBlobUrl: profilePictureUrl", profilePictureUrl);
|
|
568
|
+
const accessToken = (typeof accessTokenOverride === "string" && accessTokenOverride.length > 0
|
|
569
|
+
? accessTokenOverride
|
|
570
|
+
: null) ?? getAccessTokenForRequest();
|
|
571
|
+
if (AUTH_DEBUG)
|
|
572
|
+
console.log("[header-auth] fetchProfilePictureAsBlobUrl: accessToken present", !!accessToken, "length", accessToken?.length ?? 0);
|
|
431
573
|
const headers = {};
|
|
432
574
|
if (accessToken) {
|
|
433
575
|
headers["Authorization"] = `Bearer ${accessToken}`;
|
|
434
576
|
}
|
|
577
|
+
if (AUTH_DEBUG)
|
|
578
|
+
console.log("[header-auth] fetchProfilePictureAsBlobUrl: request headers", { ...headers, Authorization: headers["Authorization"] ? "Bearer ***" : "(none)" });
|
|
435
579
|
const response = await fetch(profilePictureUrl, { method: "GET", headers });
|
|
436
580
|
if (!response.ok) {
|
|
437
581
|
console.warn(`Failed to fetch profile picture: ${response.status} ${response.statusText}`);
|
|
@@ -563,7 +707,7 @@ const DEFAULT_ROUTES = {
|
|
|
563
707
|
profile: "/user/profile",
|
|
564
708
|
logout: "/user/login",
|
|
565
709
|
};
|
|
566
|
-
const AppHeader = ({ language: languageProp }) => {
|
|
710
|
+
const AppHeader = ({ language: languageProp, accessToken: accessTokenProp }) => {
|
|
567
711
|
// Get initial language from props, URL, localStorage, or default to 'en'
|
|
568
712
|
const getInitialLanguage = () => {
|
|
569
713
|
// Priority 1: Props
|
|
@@ -660,12 +804,18 @@ const AppHeader = ({ language: languageProp }) => {
|
|
|
660
804
|
userRole = userRole || profileData.role || profileData.userRole || "";
|
|
661
805
|
}
|
|
662
806
|
}
|
|
807
|
+
const avatarSrc = storedUser?.avatar ??
|
|
808
|
+
allData.auth?.avatar ??
|
|
809
|
+
allData.profile?.avatar;
|
|
810
|
+
const initialsVal = storedUser?.initials ??
|
|
811
|
+
allData.auth?.initials ??
|
|
812
|
+
allData.profile?.initials;
|
|
663
813
|
return {
|
|
664
814
|
name: userName || "",
|
|
665
815
|
email: userEmail || "",
|
|
666
816
|
role: userRole || "",
|
|
667
|
-
avatar:
|
|
668
|
-
initials:
|
|
817
|
+
avatar: typeof avatarSrc === "string" ? avatarSrc : undefined,
|
|
818
|
+
initials: typeof initialsVal === "string" ? initialsVal : undefined,
|
|
669
819
|
};
|
|
670
820
|
});
|
|
671
821
|
const [notificationCount, setNotificationCount] = React.useState(() => {
|
|
@@ -680,8 +830,10 @@ const AppHeader = ({ language: languageProp }) => {
|
|
|
680
830
|
// Fetch profile picture from API when component mounts or user data changes
|
|
681
831
|
React.useEffect(() => {
|
|
682
832
|
const fetchProfilePicture = async () => {
|
|
683
|
-
|
|
684
|
-
const
|
|
833
|
+
console.log("[header-auth] AppHeader: profile picture effect, accessTokenProp present", !!accessTokenProp);
|
|
834
|
+
const token = accessTokenProp ?? getAccessTokenForRequest();
|
|
835
|
+
console.log("[header-auth] AppHeader: token for request present", !!token, "from prop", !!accessTokenProp);
|
|
836
|
+
const blobUrl = await fetchProfilePictureAsBlobUrl(undefined, token ?? undefined);
|
|
685
837
|
if (blobUrl) {
|
|
686
838
|
// Clean up previous blob URL if it exists
|
|
687
839
|
setProfilePictureBlobUrl((prevUrl) => {
|
|
@@ -702,7 +854,7 @@ const AppHeader = ({ language: languageProp }) => {
|
|
|
702
854
|
return null;
|
|
703
855
|
});
|
|
704
856
|
};
|
|
705
|
-
}, []); //
|
|
857
|
+
}, [accessTokenProp]); // Refetch when accessToken prop changes (e.g. after login)
|
|
706
858
|
React.useEffect(() => {
|
|
707
859
|
const allData = getAllDataFromStorage();
|
|
708
860
|
let userName = "";
|
|
@@ -1039,6 +1191,7 @@ exports.DrawerProvider = DrawerProvider;
|
|
|
1039
1191
|
exports.PERSIST_HEADER_KEY = PERSIST_HEADER_KEY;
|
|
1040
1192
|
exports.USER_DETAILS_STORAGE_KEY = USER_DETAILS_STORAGE_KEY;
|
|
1041
1193
|
exports.fetchProfilePictureAsBlobUrl = fetchProfilePictureAsBlobUrl;
|
|
1194
|
+
exports.getAccessTokenForRequest = getAccessTokenForRequest;
|
|
1042
1195
|
exports.getAllDataFromStorage = getAllDataFromStorage;
|
|
1043
1196
|
exports.getI18nLocaleFromStorage = getI18nLocaleFromStorage;
|
|
1044
1197
|
exports.getMessageCountFromStorage = getMessageCountFromStorage;
|