react-session.manager.sk 2.0.5 → 2.0.7

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/src/index.js DELETED
@@ -1,467 +0,0 @@
1
- import React, { createContext, useState, useEffect, useCallback } from "react";
2
- import versionCompare from "./components/versionCompare";
3
- import getDeviceFingerprint from "./components/FingerPrint";
4
- import ErrorBoundary from "./components/ErrorBoundary";
5
- // Notifications
6
- import { ToastContainer, toast } from "react-toastify";
7
- import "react-toastify/dist/ReactToastify.css";
8
- import "./styling/toast.css";
9
-
10
- export const SessionManager = createContext({
11
- isLoggedIn: null,
12
- header: null,
13
- isAdmin: null,
14
- userInfo: null,
15
- refreshData: null,
16
- setHeader: null,
17
- setLoggedin: null,
18
- setRefreshData: null,
19
- hasRole: null,
20
- deviceUID: null,
21
- loadingUser: null,
22
- });
23
-
24
- const SessionManagerProvider = ({
25
- AuthenticatedAxiosObject,
26
- refreshTimer,
27
- dataRefresh,
28
- userLoader,
29
- refreshToken,
30
- appVersion,
31
- toastOptions,
32
- children,
33
- }) => {
34
- // Set deivce UID
35
- const [deviceUID, setDeviceUID] = useState(
36
- localStorage.getItem("deviceUID") || false
37
- );
38
-
39
- useEffect(() => {
40
- if (!deviceUID) {
41
- try {
42
- const uid = getDeviceFingerprint();
43
- if (uid) {
44
- setDeviceUID(uid);
45
- localStorage.setItem("deviceUID", uid);
46
- AuthenticatedAxiosObject.defaults.headers.common["deviceUID"] = uid;
47
- }
48
- } catch (err) {
49
- console.error("Failed to generate device fingerprint:", err);
50
- // Generate a fallback UUID
51
- const fallbackUID =
52
- "fallback-" +
53
- Date.now() +
54
- "-" +
55
- Math.random().toString(36).substr(2, 9);
56
- setDeviceUID(fallbackUID);
57
- localStorage.setItem("deviceUID", fallbackUID);
58
- AuthenticatedAxiosObject.defaults.headers.common["deviceUID"] =
59
- fallbackUID;
60
- }
61
- }
62
- }, [deviceUID, AuthenticatedAxiosObject]);
63
-
64
- // State to hold the selected header name
65
- const [current, setCurrent] = useState("");
66
-
67
- AuthenticatedAxiosObject.defaults.withCredentials = true;
68
- AuthenticatedAxiosObject.defaults.headers.common["Authorization"] = current;
69
- AuthenticatedAxiosObject.defaults.headers.common["deviceUID"] = deviceUID;
70
- AuthenticatedAxiosObject.defaults.headers.common["appVersion"] = appVersion;
71
-
72
- const fromPrevious = useCallback(
73
- (auth, remember) => {
74
- setCurrent(auth);
75
- setTimeout(() => {
76
- refreshToken()
77
- .then((data) => {
78
- if (data?.refreshed) {
79
- const token = `Bearer ${data.access_token}`;
80
- setCurrent(token);
81
- AuthenticatedAxiosObject.defaults.headers.common[
82
- "Authorization"
83
- ] = token;
84
- if (remember) localStorage.setItem("Authorization", token);
85
- sessionStorage.setItem("Authorization", token);
86
- }
87
- })
88
- .catch((err) => {
89
- console.error("Token refresh failed:", err);
90
-
91
- // Clear invalid tokens
92
- localStorage.removeItem("Authorization");
93
- sessionStorage.removeItem("Authorization");
94
- setCurrent("");
95
- setCurrentLoggedIn(false);
96
-
97
- // Only show toast for actual network/server errors, not auth failures
98
- if (err?.code === "ERR_NETWORK" || err?.response?.status >= 500) {
99
- // Don't spam with toasts during retries
100
- const lastToast = sessionStorage.getItem("lastRefreshErrorToast");
101
- const now = Date.now();
102
- if (!lastToast || now - parseInt(lastToast) > 30000) {
103
- // 30 seconds
104
- toast.error(
105
- "Unable to refresh your session. Please login again.",
106
- {
107
- toastId: "token-refresh-error",
108
- icon: "🚪",
109
- }
110
- );
111
- sessionStorage.setItem("lastRefreshErrorToast", now.toString());
112
- }
113
- }
114
- });
115
- }, 100);
116
- },
117
- [AuthenticatedAxiosObject, refreshToken]
118
- );
119
-
120
- useEffect(() => {
121
- const local_auth = localStorage.getItem("Authorization");
122
- const sess_auth = sessionStorage.getItem("Authorization");
123
-
124
- if (local_auth) fromPrevious(local_auth, true);
125
- else if (sess_auth) fromPrevious(sess_auth, false);
126
- // No cleanup needed here since fromPrevious doesn't create intervals
127
- }, [fromPrevious]);
128
-
129
- // Wrap setHeader to store new header names in localStorage
130
- const setHeader = (header) => {
131
- setCurrent(header);
132
- };
133
-
134
- const [currentLoggin, setCurrentLoggedIn] = useState(false);
135
- const [isAdmin, setIsAdmin] = useState(false);
136
- const [userInfo, setUserInfo] = useState({});
137
- const [loadingUser, setLoadingUser] = useState(true);
138
-
139
- useEffect(() => {
140
- setTimeout(() => {
141
- userLoader()
142
- .then((res) => {
143
- const data = res?.data;
144
- if (data) {
145
- setCurrentLoggedIn(data.logged_in);
146
- setIsAdmin(data.is_admin);
147
- setUserInfo(data.Info || {});
148
- }
149
- })
150
- .catch((err) => {
151
- console.error("User data loading failed:", err);
152
-
153
- // Don't show toast for auth errors (401, 403)
154
- if (err?.response?.status !== 401 && err?.response?.status !== 403) {
155
- if (err?.code === "ERR_NETWORK" || err?.response?.status >= 500) {
156
- toast.error(
157
- "Unable to load user information. Some features may be limited.",
158
- {
159
- toastId: "user-load-error",
160
- icon: "⚠️",
161
- }
162
- );
163
- }
164
- }
165
- // Set safe defaults
166
- setCurrentLoggedIn(false);
167
- setIsAdmin(false);
168
- setUserInfo({});
169
- })
170
- .finally(() => {
171
- setLoadingUser(false);
172
- });
173
- }, 100);
174
- }, [current, currentLoggin, userLoader]);
175
-
176
- const setLoggedin = (status) => {
177
- setCurrentLoggedIn(status);
178
- };
179
-
180
- useEffect(() => {
181
- const tokenRefreshTimer = () => {
182
- const mins = refreshTimer || 60;
183
- return mins * 60 * 1000 || 10000;
184
- };
185
-
186
- let interval = null;
187
- if (currentLoggin) {
188
- const remember = localStorage.getItem("Authorization") ? true : false;
189
- interval = setInterval(() => {
190
- fromPrevious(sessionStorage.getItem("Authorization"), remember);
191
- }, tokenRefreshTimer());
192
- } else {
193
- clearInterval(interval);
194
- }
195
- return () => clearInterval(interval);
196
- }, [fromPrevious, currentLoggin, refreshTimer]);
197
-
198
- useEffect(() => {
199
- const customInterceptor = () => {
200
- AuthenticatedAxiosObject.interceptors.response.use(
201
- (response) => {
202
- // Reset connection failure count on successful response
203
- sessionStorage.setItem("connectionFailures", "0");
204
- return response;
205
- },
206
- (error) => {
207
- console.error("Axios interceptor caught error:", error);
208
-
209
- // Handle session expiry (455)
210
- if (error?.response?.status === 455) {
211
- try {
212
- const status = error.response.data?.logged_in || false;
213
- if (!status) {
214
- console.log("Session ended");
215
- setCurrentLoggedIn(false);
216
- AuthenticatedAxiosObject.defaults.headers.common[
217
- "Authorization"
218
- ] = ``;
219
- toast.info(
220
- "Your session is no longer valid, please login again.",
221
- { toastId: "Forced_log_out", icon: "🚪" }
222
- );
223
- }
224
- } catch (err) {
225
- console.error("Error handling session expiry:", err);
226
- // Don't throw here - just log and continue
227
- }
228
- return Promise.reject(error);
229
- }
230
-
231
- // Handle version mismatch (426)
232
- if (error?.response?.status === 426) {
233
- try {
234
- sessionStorage.setItem("appVersionOld", true);
235
- sessionStorage.setItem(
236
- "requiredVersion",
237
- error.response.data?.minVersion || "unknown"
238
- );
239
- let reloads = parseInt(
240
- sessionStorage.getItem("appReloads") || "0"
241
- );
242
- if (reloads < 2) {
243
- sessionStorage.setItem("appReloads", (reloads + 1).toString());
244
- setTimeout(() => {
245
- window.location.reload();
246
- }, 1000 * reloads);
247
- } else {
248
- toast.warning(
249
- "The application needs to be updated. Please wait for some time then reload the page.",
250
- {
251
- toastId: "appReloadError",
252
- icon: "🔄",
253
- }
254
- );
255
- }
256
- } catch (err) {
257
- console.error("Error handling version mismatch:", err);
258
- }
259
- return Promise.reject(error);
260
- }
261
-
262
- // Handle network errors and connection issues
263
- if (!error?.response?.status) {
264
- if (
265
- error?.code !== "ERR_CANCELED" &&
266
- error?.message !== "canceled"
267
- ) {
268
- const errorMessage =
269
- error?.code === "ERR_NETWORK"
270
- ? "Unable to connect to the server. Please check your internet connection and try again."
271
- : "The server is not responding, please reload or try again later.";
272
-
273
- // Only show toast after multiple failures (retries exhausted)
274
- const failureCount = parseInt(
275
- sessionStorage.getItem("connectionFailures") || "0"
276
- );
277
- sessionStorage.setItem(
278
- "connectionFailures",
279
- (failureCount + 1).toString()
280
- );
281
-
282
- // Show toast after 5 failed attempts (representing retry exhaustion)
283
- if (failureCount >= 5) {
284
- toast.error(errorMessage, {
285
- toastId: `connection-failed-final`,
286
- icon: "⚠️",
287
- autoClose: 10000, // Keep it longer since this is the final error
288
- });
289
- // Reset counter after showing final toast
290
- sessionStorage.setItem("connectionFailures", "0");
291
- }
292
- }
293
- return Promise.reject(error);
294
- }
295
-
296
- // For all other errors, just reject without throwing
297
- return Promise.reject(error);
298
- }
299
- );
300
- };
301
-
302
- // Call the interceptor setup
303
- customInterceptor();
304
-
305
- // Cleanup function to remove interceptors
306
- return () => {
307
- if (AuthenticatedAxiosObject?.interceptors?.response) {
308
- AuthenticatedAxiosObject.interceptors.response.clear();
309
- }
310
- };
311
- }, [AuthenticatedAxiosObject, currentLoggin, setCurrentLoggedIn]);
312
-
313
- // We will use the below to refresh our data about the user when ever we flag refreshData as true
314
- const [refreshData, setRefreshFlag] = useState(false);
315
-
316
- useEffect(() => {
317
- const refreshDelay = () => {
318
- const mins = dataRefresh || 60;
319
- return mins * 60 * 1000 || 10000;
320
- };
321
-
322
- const timer = setTimeout(() => setRefreshFlag(true), refreshDelay());
323
- return () => clearTimeout(timer);
324
- }, [dataRefresh]);
325
-
326
- useEffect(() => {
327
- if (refreshData) {
328
- userLoader()
329
- .then((res) => {
330
- const data = res?.data;
331
- if (data) {
332
- setUserInfo(data.Info || {});
333
- }
334
- })
335
- .catch((err) => {
336
- console.error("User data refresh failed:", err);
337
- // Silently fail for data refresh - don't overwhelm user with toasts
338
- })
339
- .finally(() => {
340
- setRefreshData(false);
341
- });
342
- }
343
- }, [refreshData, userLoader]);
344
-
345
- const setRefreshData = (status) => {
346
- setRefreshFlag(status);
347
- };
348
-
349
- // Check if user has specific role
350
- const hasRole = (roles) => {
351
- if (
352
- !roles ||
353
- !Array.isArray(roles) ||
354
- !userInfo?.roles ||
355
- !Array.isArray(userInfo.roles)
356
- ) {
357
- return false;
358
- }
359
- try {
360
- return roles.some((r) => userInfo.roles.indexOf(r) >= 0);
361
- } catch (err) {
362
- console.error("Error checking user roles:", err);
363
- return false;
364
- }
365
- };
366
-
367
- const contextValue = {
368
- isLoggedIn: currentLoggin,
369
- header: current,
370
- isAdmin: isAdmin,
371
- userInfo: userInfo,
372
- refreshData: refreshData,
373
- setHeader: setHeader,
374
- setLoggedin: setLoggedin,
375
- setRefreshData: setRefreshData,
376
- hasRole: hasRole,
377
- deviceUID: deviceUID,
378
- loadingUser: loadingUser,
379
- };
380
-
381
- // Show loading state while initializing
382
- return (
383
- <ErrorBoundary>
384
- <SessionManager.Provider value={contextValue}>
385
- {loadingUser && (
386
- <div
387
- style={{
388
- position: "fixed",
389
- top: "50%",
390
- left: "50%",
391
- transform: "translate(-50%, -50%)",
392
- zIndex: 9999,
393
- }}
394
- >
395
- <div
396
- style={{
397
- width: "40px",
398
- height: "40px",
399
- border: "4px solid rgba(0,0,0,0.1)",
400
- borderTop: "4px solid #3498db",
401
- borderRadius: "50%",
402
- animation: "spin 1s linear infinite",
403
- }}
404
- />
405
- <style>{`
406
- @keyframes spin {
407
- 0% { transform: rotate(0deg); }
408
- 100% { transform: rotate(360deg); }
409
- }
410
- `}</style>
411
- </div>
412
- )}
413
- <ToastContainer
414
- position="top-left"
415
- autoClose={5000}
416
- closeOnClick
417
- pauseOnFocusLoss
418
- pauseOnHover
419
- newestOnTop={false}
420
- toastClassName={"custToast materialToast"}
421
- {...toastOptions}
422
- />
423
- <VersionProtection appVersion={appVersion} />
424
- {children}
425
- </SessionManager.Provider>
426
- </ErrorBoundary>
427
- );
428
- };
429
-
430
- function VersionProtection({ appVersion }) {
431
- const [oldVersion, setOldVersion] = useState(false);
432
-
433
- useEffect(() => {
434
- try {
435
- const isOldVersion = sessionStorage.getItem("appVersionOld") === "true";
436
- setOldVersion(isOldVersion);
437
-
438
- if (
439
- isOldVersion &&
440
- sessionStorage.getItem("requiredVersion") &&
441
- appVersion &&
442
- versionCompare(appVersion, sessionStorage.getItem("requiredVersion"))
443
- ) {
444
- console.log("Update Success Toast");
445
- toast.success("Your application has been updated", {
446
- toastId: "appReload",
447
- icon: "✅",
448
- onClose: () => {
449
- try {
450
- sessionStorage.removeItem("appVersionOld");
451
- sessionStorage.removeItem("requiredVersion");
452
- sessionStorage.removeItem("appReloads");
453
- } catch (err) {
454
- console.error("Error cleaning up version storage:", err);
455
- }
456
- },
457
- });
458
- }
459
- } catch (err) {
460
- console.error("Error in version protection:", err);
461
- }
462
- }, [appVersion]);
463
-
464
- return <></>;
465
- }
466
-
467
- export default SessionManagerProvider;
@@ -1,56 +0,0 @@
1
- :root {
2
- --main: #303030;
3
- --themec: #303030;
4
- --default: #303030;
5
- --contrastText: "rgba(0, 0, 0, 0.87)";
6
- }
7
-
8
- .custToast {
9
- background: var(--themec);
10
- }
11
- .custToast .Toastify__close-button {
12
- color: var(--main);
13
- }
14
-
15
- .custToast.Toastify__toast--success {
16
- color: var(--success);
17
- }
18
- .custToast.Toastify__toast--info {
19
- color: var(--info);
20
- }
21
- .custToast.Toastify__toast--warning {
22
- color: var(--warning);
23
- }
24
- .custToast.Toastify__toast--error {
25
- color: var(--error);
26
- }
27
-
28
- .custToast .Toastify__progress-bar--success {
29
- background: var(--success);
30
- }
31
- .custToast .Toastify__progress-bar--info {
32
- background: var(--info);
33
- }
34
- .custToast .Toastify__progress-bar--warning {
35
- background: var(--warning);
36
- }
37
- .custToast .Toastify__progress-bar--error {
38
- background: var(--error);
39
- }
40
-
41
- .custToast.materialToast.Toastify__toast--success {
42
- background: var(--success);
43
- color: var(--contrastText);
44
- }
45
- .custToast.materialToast.Toastify__toast--info {
46
- background: var(--info);
47
- color: var(--contrastText);
48
- }
49
- .custToast.materialToast.Toastify__toast--warning {
50
- background: var(--warning);
51
- color: var(--contrastText);
52
- }
53
- .custToast.materialToast.Toastify__toast--error {
54
- background: var(--error);
55
- color: var(--contrastText);
56
- }
package/webpack.config.js DELETED
@@ -1,36 +0,0 @@
1
- const path = require("path");
2
-
3
- module.exports = {
4
- entry: "./src/index.js",
5
- output: {
6
- path: path.resolve(__dirname, "dist"),
7
- filename: "index.js",
8
- library: "MyComponent",
9
- libraryTarget: "umd",
10
- umdNamedDefine: true,
11
- },
12
- externals: {
13
- react: "react", // Use 'react' from the environment (do not bundle)
14
- "react-dom": "react-dom", // Use 'react-dom' from the environment (do not bundle)
15
- },
16
- module: {
17
- rules: [
18
- {
19
- test: /\.jsx?$/,
20
- exclude: /node_modules/,
21
- use: {
22
- loader: "babel-loader",
23
- },
24
- },
25
- {
26
- test: /\.css$/i,
27
- use: {
28
- loader: "css-loader",
29
- },
30
- },
31
- ],
32
- },
33
- resolve: {
34
- extensions: [".js", ".jsx"],
35
- },
36
- };