varminer-app-header 2.2.1 → 2.2.3
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/index.d.ts +28 -37
- package/dist/index.d.ts.map +1 -1
- package/dist/index.esm.js +144 -294
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +145 -294
- package/dist/index.js.map +1 -1
- package/dist/utils/localStorage.d.ts +26 -35
- package/dist/utils/localStorage.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -77,77 +77,38 @@ function getStoredUserDetails() {
|
|
|
77
77
|
return null;
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
initials: name ? name.split(/\s+/).map((s) => s[0]).join("").slice(0, 2).toUpperCase() : undefined,
|
|
92
|
-
};
|
|
80
|
+
// --- Header-owned persist store (unique key, no conflict with other apps) ---
|
|
81
|
+
/** Unique localStorage key for this header package. Used only for access token; user details come from IAM (linn-i-am-userDetails). */
|
|
82
|
+
const PERSIST_HEADER_KEY = "persist:varminer-app-header";
|
|
83
|
+
/**
|
|
84
|
+
* Store auth (e.g. accessToken) in the header's own persist key. Call this after login so the header can use the token for profile picture etc.
|
|
85
|
+
* User details are not stored here; they come from IAM (linn-i-am-userDetails).
|
|
86
|
+
*/
|
|
87
|
+
function setHeaderAuth(auth) {
|
|
88
|
+
try {
|
|
89
|
+
const payload = auth ? { auth } : {};
|
|
90
|
+
localStorage.setItem(PERSIST_HEADER_KEY, JSON.stringify(payload));
|
|
93
91
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
try {
|
|
97
|
-
const parsedOuter = JSON.parse(userDbString);
|
|
98
|
-
const parseNested = (value) => {
|
|
99
|
-
if (typeof value === 'string') {
|
|
100
|
-
try {
|
|
101
|
-
return JSON.parse(value);
|
|
102
|
-
}
|
|
103
|
-
catch {
|
|
104
|
-
return value;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
return value;
|
|
108
|
-
};
|
|
109
|
-
let userData = null;
|
|
110
|
-
if (parsedOuter.userDetails) {
|
|
111
|
-
const parsedUserDetails = parseNested(parsedOuter.userDetails);
|
|
112
|
-
userData = parsedUserDetails.user || parsedUserDetails.data || parsedUserDetails;
|
|
113
|
-
}
|
|
114
|
-
if (!userData && parsedOuter.user) {
|
|
115
|
-
const parsedUser = parseNested(parsedOuter.user);
|
|
116
|
-
userData = parsedUser.user || parsedUser.data || parsedUser.currentUser || parsedUser;
|
|
117
|
-
}
|
|
118
|
-
if (!userData && parsedOuter.authDetails) {
|
|
119
|
-
const parsedAuthDetails = parseNested(parsedOuter.authDetails);
|
|
120
|
-
if (parsedAuthDetails.auth) {
|
|
121
|
-
userData = parsedAuthDetails.auth.user || parsedAuthDetails.auth.userData;
|
|
122
|
-
}
|
|
123
|
-
if (!userData) {
|
|
124
|
-
userData = parsedAuthDetails.user || parsedAuthDetails.userData || parsedAuthDetails.currentUser;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
if (!userData && parsedOuter.profile) {
|
|
128
|
-
const parsedProfile = parseNested(parsedOuter.profile);
|
|
129
|
-
userData = parsedProfile.user || parsedProfile.data || parsedProfile;
|
|
130
|
-
}
|
|
131
|
-
if (userData) {
|
|
132
|
-
let name = userData.name || userData.fullName || userData.userName || "";
|
|
133
|
-
if (!name && (userData.firstName || userData.lastName)) {
|
|
134
|
-
name = [userData.firstName, userData.lastName].filter(Boolean).join(' ');
|
|
135
|
-
}
|
|
136
|
-
return {
|
|
137
|
-
name: name || "",
|
|
138
|
-
email: userData.email || userData.emailAddress || "",
|
|
139
|
-
role: userData.role || userData.userRole || userData.designation || userData.title || "",
|
|
140
|
-
avatar: userData.avatar || userData.profilePicture || userData.imageUrl || userData.profileImage,
|
|
141
|
-
initials: userData.initials,
|
|
142
|
-
};
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
catch (err) {
|
|
146
|
-
console.error("Error parsing user data from localStorage:", err);
|
|
147
|
-
return null;
|
|
148
|
-
}
|
|
92
|
+
catch (err) {
|
|
93
|
+
console.error("Error setting header auth:", err);
|
|
149
94
|
}
|
|
150
|
-
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* User display data: from IAM only (linn-i-am-userDetails). No dependency on persist:userdb.
|
|
98
|
+
*/
|
|
99
|
+
const getUserDataFromStorage = () => {
|
|
100
|
+
const iamUser = getStoredUserDetails();
|
|
101
|
+
if (!iamUser)
|
|
102
|
+
return null;
|
|
103
|
+
const name = [iamUser.firstName, iamUser.lastName].filter(Boolean).join(" ").trim();
|
|
104
|
+
const role = iamUser.roles?.length ? iamUser.roles[0] : iamUser.workInfo?.jobTitle ?? "";
|
|
105
|
+
return {
|
|
106
|
+
name: name || "",
|
|
107
|
+
email: iamUser.email || "",
|
|
108
|
+
role: role || "",
|
|
109
|
+
avatar: undefined,
|
|
110
|
+
initials: name ? name.split(/\s+/).map((s) => s[0]).join("").slice(0, 2).toUpperCase() : undefined,
|
|
111
|
+
};
|
|
151
112
|
};
|
|
152
113
|
const getNotificationCountFromStorage = () => {
|
|
153
114
|
const userDbString = localStorage.getItem("persist:userdb");
|
|
@@ -263,26 +224,91 @@ const decodeJWT = (token) => {
|
|
|
263
224
|
return null;
|
|
264
225
|
}
|
|
265
226
|
};
|
|
227
|
+
const emptyStorageResult = () => ({
|
|
228
|
+
auth: null,
|
|
229
|
+
user: null,
|
|
230
|
+
authDetails: null,
|
|
231
|
+
userDetails: null,
|
|
232
|
+
profile: null,
|
|
233
|
+
notifications: null,
|
|
234
|
+
messages: null,
|
|
235
|
+
app: null,
|
|
236
|
+
decodedToken: null,
|
|
237
|
+
rawData: null,
|
|
238
|
+
});
|
|
266
239
|
/**
|
|
267
|
-
* Get
|
|
268
|
-
*
|
|
269
|
-
* @returns
|
|
240
|
+
* Get auth (and decoded token) for header use. Reads from header's own persist key first; falls back to persist:userdb only for access token.
|
|
241
|
+
* User details are not read from userdb—use getStoredUserDetails / getUserDataFromStorage (IAM) for that.
|
|
242
|
+
* @returns Object with auth, decodedToken, and other keys (user/userDetails/profile null when using header key)
|
|
270
243
|
*/
|
|
271
244
|
const getAllDataFromStorage = () => {
|
|
245
|
+
// Prefer header-owned persist key (unique, no conflict with other apps)
|
|
246
|
+
const headerStr = localStorage.getItem(PERSIST_HEADER_KEY);
|
|
247
|
+
if (headerStr) {
|
|
248
|
+
try {
|
|
249
|
+
const parsed = JSON.parse(headerStr);
|
|
250
|
+
const auth = parsed?.auth;
|
|
251
|
+
const accessToken = auth?.accessToken ?? auth?.access_token ?? auth?.token;
|
|
252
|
+
if (accessToken && typeof accessToken === "string") {
|
|
253
|
+
const decodedToken = decodeJWT(accessToken);
|
|
254
|
+
return {
|
|
255
|
+
...emptyStorageResult(),
|
|
256
|
+
auth: auth,
|
|
257
|
+
decodedToken: decodedToken ? Object.fromEntries(Object.entries(decodedToken).map(([k, v]) => [k, v ?? null])) : null,
|
|
258
|
+
rawData: parsed,
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
catch (err) {
|
|
263
|
+
console.error("Error parsing header persist:", err);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
// Fallback: try IAM persist key (persist:linn-i-am) then persist:userdb
|
|
267
|
+
const tryParsePersistForAuth = (raw) => {
|
|
268
|
+
if (!raw)
|
|
269
|
+
return null;
|
|
270
|
+
try {
|
|
271
|
+
const outer = JSON.parse(raw);
|
|
272
|
+
const parseNested = (v) => typeof v === "string" ? (() => { try {
|
|
273
|
+
return JSON.parse(v);
|
|
274
|
+
}
|
|
275
|
+
catch {
|
|
276
|
+
return v;
|
|
277
|
+
} })() : v;
|
|
278
|
+
for (const key of Object.keys(outer)) {
|
|
279
|
+
const parsed = parseNested(outer[key]);
|
|
280
|
+
if (parsed?.accessToken || parsed?.access_token || parsed?.token || parsed?.refreshToken)
|
|
281
|
+
return parsed;
|
|
282
|
+
if (parsed?.auth && typeof parsed.auth === "object") {
|
|
283
|
+
const a = parsed.auth;
|
|
284
|
+
if (a.accessToken || a.access_token || a.token)
|
|
285
|
+
return a;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
catch {
|
|
290
|
+
/* ignore */
|
|
291
|
+
}
|
|
292
|
+
return null;
|
|
293
|
+
};
|
|
294
|
+
const iamPersist = localStorage.getItem("persist:linn-i-am");
|
|
295
|
+
const iamAuth = tryParsePersistForAuth(iamPersist);
|
|
296
|
+
if (iamAuth) {
|
|
297
|
+
const token = (iamAuth.accessToken ?? iamAuth.access_token ?? iamAuth.token);
|
|
298
|
+
if (token) {
|
|
299
|
+
const decodedToken = decodeJWT(token);
|
|
300
|
+
return {
|
|
301
|
+
...emptyStorageResult(),
|
|
302
|
+
auth: iamAuth,
|
|
303
|
+
decodedToken: decodedToken ? Object.fromEntries(Object.entries(decodedToken).map(([k, v]) => [k, v ?? null])) : null,
|
|
304
|
+
rawData: iamAuth,
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
// Fallback: read access token from persist:userdb (e.g. legacy or shared auth)
|
|
272
309
|
const userDbString = localStorage.getItem("persist:userdb");
|
|
273
310
|
if (!userDbString) {
|
|
274
|
-
return
|
|
275
|
-
auth: null,
|
|
276
|
-
user: null,
|
|
277
|
-
authDetails: null,
|
|
278
|
-
userDetails: null,
|
|
279
|
-
profile: null,
|
|
280
|
-
notifications: null,
|
|
281
|
-
messages: null,
|
|
282
|
-
app: null,
|
|
283
|
-
decodedToken: null,
|
|
284
|
-
rawData: null,
|
|
285
|
-
};
|
|
311
|
+
return emptyStorageResult();
|
|
286
312
|
}
|
|
287
313
|
try {
|
|
288
314
|
const parsedOuter = JSON.parse(userDbString);
|
|
@@ -301,17 +327,14 @@ const getAllDataFromStorage = () => {
|
|
|
301
327
|
// Parse all top-level keys dynamically (no hardcoding)
|
|
302
328
|
const parsedData = {};
|
|
303
329
|
let authData = null;
|
|
304
|
-
// Helper to recursively find auth data (contains accessToken or refreshToken)
|
|
330
|
+
// Helper to recursively find auth data (contains accessToken, access_token, token, or refreshToken)
|
|
305
331
|
const findAuthData = (obj) => {
|
|
306
332
|
if (!obj || typeof obj !== 'object')
|
|
307
333
|
return null;
|
|
308
|
-
|
|
309
|
-
if (obj.accessToken || obj.refreshToken) {
|
|
334
|
+
if (obj.accessToken || obj.access_token || obj.token || obj.refreshToken)
|
|
310
335
|
return obj;
|
|
311
|
-
}
|
|
312
|
-
// Recursively search in nested objects
|
|
313
336
|
for (const key in obj) {
|
|
314
|
-
if (
|
|
337
|
+
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
315
338
|
const nested = findAuthData(obj[key]);
|
|
316
339
|
if (nested)
|
|
317
340
|
return nested;
|
|
@@ -329,9 +352,11 @@ const getAllDataFromStorage = () => {
|
|
|
329
352
|
parsedData.auth = authData;
|
|
330
353
|
}
|
|
331
354
|
});
|
|
332
|
-
// Get accessToken and decode it
|
|
355
|
+
// Get accessToken (support accessToken, access_token, token) and decode it
|
|
333
356
|
let decodedToken = null;
|
|
334
|
-
const accessToken = authData?.accessToken
|
|
357
|
+
const accessToken = authData?.accessToken ?? authData?.access_token ?? authData?.token ??
|
|
358
|
+
parsedData.auth?.accessToken ?? parsedData.auth?.access_token ?? parsedData.auth?.token ??
|
|
359
|
+
parsedData.authDetails?.accessToken ?? parsedData.authDetails?.access_token ?? parsedData.authDetails?.token;
|
|
335
360
|
if (accessToken) {
|
|
336
361
|
decodedToken = decodeJWT(accessToken);
|
|
337
362
|
}
|
|
@@ -360,19 +385,7 @@ const getAllDataFromStorage = () => {
|
|
|
360
385
|
}
|
|
361
386
|
catch (err) {
|
|
362
387
|
console.error("Error parsing all data from localStorage:", err);
|
|
363
|
-
return {
|
|
364
|
-
auth: null,
|
|
365
|
-
user: null,
|
|
366
|
-
authDetails: null,
|
|
367
|
-
userDetails: null,
|
|
368
|
-
profile: null,
|
|
369
|
-
notifications: null,
|
|
370
|
-
messages: null,
|
|
371
|
-
app: null,
|
|
372
|
-
decodedToken: null,
|
|
373
|
-
rawData: null,
|
|
374
|
-
error: err instanceof Error ? err.message : 'Unknown error',
|
|
375
|
-
};
|
|
388
|
+
return { ...emptyStorageResult(), error: err instanceof Error ? err.message : "Unknown error" };
|
|
376
389
|
}
|
|
377
390
|
};
|
|
378
391
|
/**
|
|
@@ -394,206 +407,43 @@ const getProfilePictureUrl = (baseUrl = "http://objectstore.impact0mics.local:90
|
|
|
394
407
|
}
|
|
395
408
|
};
|
|
396
409
|
/**
|
|
397
|
-
*
|
|
398
|
-
* URL
|
|
399
|
-
* @param baseUrl - Base URL for the object store API (default: http://objectstore.impact0mics.local:9012)
|
|
400
|
-
* @returns Full upload URL with query params, or null if tenantId, userId, or roleId is missing
|
|
401
|
-
*/
|
|
402
|
-
const getProfilePictureUploadUrl = (baseUrl = "http://objectstore.impact0mics.local:9012") => {
|
|
403
|
-
try {
|
|
404
|
-
const allData = getAllDataFromStorage();
|
|
405
|
-
const iamUser = getStoredUserDetails();
|
|
406
|
-
const tenantId = allData.decodedToken?.tenant_id ??
|
|
407
|
-
allData.decodedToken?.tenant ??
|
|
408
|
-
allData.auth?.tenant_id ??
|
|
409
|
-
allData.auth?.tenant ??
|
|
410
|
-
null;
|
|
411
|
-
const userId = allData.decodedToken?.user_id ??
|
|
412
|
-
allData.auth?.user_id ??
|
|
413
|
-
iamUser?.userId ??
|
|
414
|
-
null;
|
|
415
|
-
const roleId = allData.decodedToken?.role_id ??
|
|
416
|
-
allData.auth?.role_id ??
|
|
417
|
-
null;
|
|
418
|
-
if (tenantId == null || userId == null || roleId == null)
|
|
419
|
-
return null;
|
|
420
|
-
const cleanBaseUrl = baseUrl.replace(/\/$/, "");
|
|
421
|
-
const params = new URLSearchParams({
|
|
422
|
-
tenantId: String(tenantId),
|
|
423
|
-
userId: String(userId),
|
|
424
|
-
roleId: String(roleId),
|
|
425
|
-
});
|
|
426
|
-
return `${cleanBaseUrl}/v1/objectStore/profilePictureUpload?${params.toString()}`;
|
|
427
|
-
}
|
|
428
|
-
catch (err) {
|
|
429
|
-
console.error("Error getting profile picture upload URL:", err);
|
|
430
|
-
return null;
|
|
431
|
-
}
|
|
432
|
-
};
|
|
433
|
-
/**
|
|
434
|
-
* Generate AWS S3 presigned URL for accessing S3 object
|
|
435
|
-
* @param s3Url - Full S3 URL (e.g., https://bucket.s3.region.amazonaws.com/key)
|
|
436
|
-
* @param accessKeyId - AWS Access Key ID
|
|
437
|
-
* @param secretAccessKey - AWS Secret Access Key
|
|
438
|
-
* @param region - AWS Region (default: ap-south-2)
|
|
439
|
-
* @param expiresIn - Expiration time in seconds (default: 3600 = 1 hour)
|
|
440
|
-
* @returns Presigned URL string
|
|
441
|
-
*/
|
|
442
|
-
const generateS3PresignedUrl = async (s3Url, accessKeyId, secretAccessKey, region = "ap-south-2", expiresIn = 3600) => {
|
|
443
|
-
try {
|
|
444
|
-
// Parse S3 URL to extract bucket, region, and key
|
|
445
|
-
// Format: https://bucket.s3.region.amazonaws.com/key or https://bucket.s3-region.amazonaws.com/key
|
|
446
|
-
const url = new URL(s3Url);
|
|
447
|
-
const hostnameParts = url.hostname.split('.');
|
|
448
|
-
let bucket = hostnameParts[0];
|
|
449
|
-
let extractedRegion = region;
|
|
450
|
-
// Try to extract region from hostname (format: bucket.s3.region.amazonaws.com)
|
|
451
|
-
if (hostnameParts.length >= 3 && hostnameParts[1] === 's3') {
|
|
452
|
-
extractedRegion = hostnameParts[2] || region;
|
|
453
|
-
}
|
|
454
|
-
else if (hostnameParts.length >= 2 && hostnameParts[1].startsWith('s3-')) {
|
|
455
|
-
// Format: bucket.s3-region.amazonaws.com
|
|
456
|
-
extractedRegion = hostnameParts[1].substring(3) || region;
|
|
457
|
-
}
|
|
458
|
-
const key = url.pathname.substring(1); // Remove leading slash
|
|
459
|
-
// AWS credentials
|
|
460
|
-
const awsAccessKeyId = accessKeyId;
|
|
461
|
-
const awsSecretAccessKey = secretAccessKey;
|
|
462
|
-
const awsRegion = extractedRegion;
|
|
463
|
-
// Current timestamp
|
|
464
|
-
const now = new Date();
|
|
465
|
-
const dateStamp = now.toISOString().slice(0, 10).replace(/-/g, '');
|
|
466
|
-
const amzDate = dateStamp + 'T' + now.toISOString().slice(11, 19).replace(/[:-]/g, '') + 'Z';
|
|
467
|
-
// Step 1: Create canonical request
|
|
468
|
-
const canonicalUri = '/' + encodeURIComponent(key).replace(/%2F/g, '/');
|
|
469
|
-
const canonicalQuerystring = `X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=${encodeURIComponent(awsAccessKeyId + '/' + dateStamp + '/' + awsRegion + '/s3/aws4_request')}&X-Amz-Date=${amzDate}&X-Amz-Expires=${expiresIn}&X-Amz-SignedHeaders=host`;
|
|
470
|
-
const canonicalHeaders = `host:${bucket}.s3.${awsRegion}.amazonaws.com\n`;
|
|
471
|
-
const signedHeaders = 'host';
|
|
472
|
-
const payloadHash = 'UNSIGNED-PAYLOAD';
|
|
473
|
-
const canonicalRequest = `GET\n${canonicalUri}\n${canonicalQuerystring}\n${canonicalHeaders}\n${signedHeaders}\n${payloadHash}`;
|
|
474
|
-
// Step 2: Create string to sign
|
|
475
|
-
const algorithm = 'AWS4-HMAC-SHA256';
|
|
476
|
-
const credentialScope = `${dateStamp}/${awsRegion}/s3/aws4_request`;
|
|
477
|
-
const stringToSign = `${algorithm}\n${amzDate}\n${credentialScope}\n${await sha256(canonicalRequest)}`;
|
|
478
|
-
// Step 3: Calculate signature
|
|
479
|
-
const kDate = await hmacSha256('AWS4' + awsSecretAccessKey, dateStamp);
|
|
480
|
-
const kRegion = await hmacSha256(kDate, awsRegion);
|
|
481
|
-
const kService = await hmacSha256(kRegion, 's3');
|
|
482
|
-
const kSigning = await hmacSha256(kService, 'aws4_request');
|
|
483
|
-
const signature = await hmacSha256(kSigning, stringToSign);
|
|
484
|
-
// Convert signature to hex
|
|
485
|
-
const signatureHex = arrayBufferToHex(signature);
|
|
486
|
-
// Step 4: Construct presigned URL
|
|
487
|
-
const presignedUrl = `https://${bucket}.s3.${awsRegion}.amazonaws.com${canonicalUri}?${canonicalQuerystring}&X-Amz-Signature=${signatureHex}`;
|
|
488
|
-
return presignedUrl;
|
|
489
|
-
}
|
|
490
|
-
catch (err) {
|
|
491
|
-
console.error("Error generating S3 presigned URL:", err);
|
|
492
|
-
throw err;
|
|
493
|
-
}
|
|
494
|
-
};
|
|
495
|
-
// Helper function for SHA-256 hashing
|
|
496
|
-
const sha256 = async (message) => {
|
|
497
|
-
const msgBuffer = new TextEncoder().encode(message);
|
|
498
|
-
const hashBuffer = await crypto.subtle.digest('SHA-256', msgBuffer);
|
|
499
|
-
return Array.from(new Uint8Array(hashBuffer))
|
|
500
|
-
.map(b => b.toString(16).padStart(2, '0'))
|
|
501
|
-
.join('');
|
|
502
|
-
};
|
|
503
|
-
// Helper function for HMAC-SHA256
|
|
504
|
-
const hmacSha256 = async (key, message) => {
|
|
505
|
-
const encoder = new TextEncoder();
|
|
506
|
-
let keyBuffer;
|
|
507
|
-
if (typeof key === 'string') {
|
|
508
|
-
keyBuffer = encoder.encode(key);
|
|
509
|
-
}
|
|
510
|
-
else {
|
|
511
|
-
keyBuffer = new Uint8Array(key);
|
|
512
|
-
}
|
|
513
|
-
const messageBuffer = encoder.encode(message);
|
|
514
|
-
const cryptoKey = await crypto.subtle.importKey('raw', keyBuffer, { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']);
|
|
515
|
-
return await crypto.subtle.sign('HMAC', cryptoKey, messageBuffer);
|
|
516
|
-
};
|
|
517
|
-
// Helper function to convert ArrayBuffer to hex string
|
|
518
|
-
const arrayBufferToHex = (buffer) => {
|
|
519
|
-
return Array.from(new Uint8Array(buffer))
|
|
520
|
-
.map(b => b.toString(16).padStart(2, '0'))
|
|
521
|
-
.join('');
|
|
522
|
-
};
|
|
523
|
-
/**
|
|
524
|
-
* Fetch profile picture from API with headers, get S3 URL, generate presigned URL, and return as blob URL
|
|
525
|
-
* This function:
|
|
526
|
-
* 1. Fetches the profile picture path from the API
|
|
527
|
-
* 2. Extracts the S3 filePath from the JSON response
|
|
528
|
-
* 3. Generates a presigned URL for the S3 object
|
|
529
|
-
* 4. Fetches the image using the presigned URL
|
|
530
|
-
* 5. Converts it to a blob URL that can be used in img src
|
|
410
|
+
* Fetch profile picture from object store API (plain GET with Authorization header).
|
|
411
|
+
* API returns the image directly; response is converted to a blob URL for use in img src.
|
|
531
412
|
* @param baseUrl - Base URL for the object store API (default: http://objectstore.impact0mics.local:9012)
|
|
532
|
-
* @param messageId - Optional message ID for X-Message-Id header (default: generated UUID)
|
|
533
|
-
* @param correlationId - Optional correlation ID for X-Correlation-Id header (default: generated UUID)
|
|
534
|
-
* @param awsConfig - AWS configuration (accessKeyId, secretAccessKey, region, bucket)
|
|
535
413
|
* @returns Promise that resolves to blob URL string or null if fetch fails
|
|
536
414
|
*/
|
|
537
|
-
|
|
415
|
+
/** Resolve access token from auth object (supports accessToken, access_token, token). */
|
|
416
|
+
function getAccessTokenFromAuth(auth) {
|
|
417
|
+
if (!auth || typeof auth !== "object")
|
|
418
|
+
return undefined;
|
|
419
|
+
const token = auth.accessToken ??
|
|
420
|
+
auth.access_token ??
|
|
421
|
+
auth.token;
|
|
422
|
+
return typeof token === "string" && token.length > 0 ? token : undefined;
|
|
423
|
+
}
|
|
424
|
+
const fetchProfilePictureAsBlobUrl = async (baseUrl = "http://objectstore.impact0mics.local:9012") => {
|
|
538
425
|
try {
|
|
539
426
|
const profilePictureUrl = getProfilePictureUrl(baseUrl);
|
|
540
|
-
if (!profilePictureUrl)
|
|
541
|
-
return null;
|
|
542
|
-
}
|
|
543
|
-
// AWS credentials (default values provided by user)
|
|
544
|
-
const defaultAwsConfig = {
|
|
545
|
-
accessKeyId: "AKIAVRUVTJGLBCYZEI5L",
|
|
546
|
-
secretAccessKey: "kbMVqmx6s29njcS5P48qAqpXlb1oir6+b7zu1Qxi",
|
|
547
|
-
region: "ap-south-2",
|
|
548
|
-
bucket: "development-varminer-test"
|
|
549
|
-
};
|
|
550
|
-
const finalAwsConfig = awsConfig || defaultAwsConfig;
|
|
551
|
-
// Generate message ID and correlation ID if not provided
|
|
552
|
-
const msgId = messageId || `msg-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
553
|
-
const corrId = correlationId || `corr-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
554
|
-
// Step 1: Fetch the profile picture path from API (returns JSON with filePath)
|
|
555
|
-
const apiResponse = await fetch(profilePictureUrl, {
|
|
556
|
-
method: 'GET',
|
|
557
|
-
headers: {
|
|
558
|
-
'X-Message-Id': msgId,
|
|
559
|
-
'X-Correlation-Id': corrId,
|
|
560
|
-
},
|
|
561
|
-
});
|
|
562
|
-
// Check if the API response is successful
|
|
563
|
-
if (!apiResponse.ok) {
|
|
564
|
-
console.warn(`Failed to fetch profile picture path: ${apiResponse.status} ${apiResponse.statusText}`);
|
|
565
|
-
return null;
|
|
566
|
-
}
|
|
567
|
-
// Parse JSON response
|
|
568
|
-
const apiData = await apiResponse.json();
|
|
569
|
-
// Extract filePath from response
|
|
570
|
-
const s3Url = apiData?.filePath;
|
|
571
|
-
if (!s3Url || typeof s3Url !== 'string') {
|
|
572
|
-
console.warn('Profile picture API response does not contain valid filePath');
|
|
427
|
+
if (!profilePictureUrl)
|
|
573
428
|
return null;
|
|
429
|
+
const allData = getAllDataFromStorage();
|
|
430
|
+
const accessToken = getAccessTokenFromAuth(allData.auth);
|
|
431
|
+
const headers = {};
|
|
432
|
+
if (accessToken) {
|
|
433
|
+
headers["Authorization"] = `Bearer ${accessToken}`;
|
|
574
434
|
}
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
const imageResponse = await fetch(presignedUrl, {
|
|
579
|
-
method: 'GET',
|
|
580
|
-
});
|
|
581
|
-
// Check if the image response is successful
|
|
582
|
-
if (!imageResponse.ok) {
|
|
583
|
-
console.warn(`Failed to fetch profile picture image: ${imageResponse.status} ${imageResponse.statusText}`);
|
|
435
|
+
const response = await fetch(profilePictureUrl, { method: "GET", headers });
|
|
436
|
+
if (!response.ok) {
|
|
437
|
+
console.warn(`Failed to fetch profile picture: ${response.status} ${response.statusText}`);
|
|
584
438
|
return null;
|
|
585
439
|
}
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
if (!contentType || !contentType.startsWith('image/')) {
|
|
440
|
+
const contentType = response.headers.get("content-type");
|
|
441
|
+
if (!contentType || !contentType.startsWith("image/")) {
|
|
589
442
|
console.warn(`Profile picture response is not an image: ${contentType}`);
|
|
590
443
|
return null;
|
|
591
444
|
}
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
// Step 5: Create blob URL
|
|
595
|
-
const blobUrl = URL.createObjectURL(blob);
|
|
596
|
-
return blobUrl;
|
|
445
|
+
const blob = await response.blob();
|
|
446
|
+
return URL.createObjectURL(blob);
|
|
597
447
|
}
|
|
598
448
|
catch (err) {
|
|
599
449
|
console.error("Error fetching profile picture:", err);
|
|
@@ -1186,17 +1036,18 @@ const AppHeader = ({ language: languageProp }) => {
|
|
|
1186
1036
|
|
|
1187
1037
|
exports.AppHeader = AppHeader;
|
|
1188
1038
|
exports.DrawerProvider = DrawerProvider;
|
|
1039
|
+
exports.PERSIST_HEADER_KEY = PERSIST_HEADER_KEY;
|
|
1189
1040
|
exports.USER_DETAILS_STORAGE_KEY = USER_DETAILS_STORAGE_KEY;
|
|
1190
1041
|
exports.fetchProfilePictureAsBlobUrl = fetchProfilePictureAsBlobUrl;
|
|
1191
1042
|
exports.getAllDataFromStorage = getAllDataFromStorage;
|
|
1192
1043
|
exports.getI18nLocaleFromStorage = getI18nLocaleFromStorage;
|
|
1193
1044
|
exports.getMessageCountFromStorage = getMessageCountFromStorage;
|
|
1194
1045
|
exports.getNotificationCountFromStorage = getNotificationCountFromStorage;
|
|
1195
|
-
exports.getProfilePictureUploadUrl = getProfilePictureUploadUrl;
|
|
1196
1046
|
exports.getProfilePictureUrl = getProfilePictureUrl;
|
|
1197
1047
|
exports.getStoredUserDetails = getStoredUserDetails;
|
|
1198
1048
|
exports.getTranslations = getTranslations;
|
|
1199
1049
|
exports.getUserDataFromStorage = getUserDataFromStorage;
|
|
1050
|
+
exports.setHeaderAuth = setHeaderAuth;
|
|
1200
1051
|
exports.setI18nLocaleToStorage = setI18nLocaleToStorage;
|
|
1201
1052
|
exports.translations = translations;
|
|
1202
1053
|
exports.useDrawer = useDrawer;
|