@nocios/crudify-ui 1.2.34 → 1.3.0
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/MIGRATION-GUIDE.md +312 -0
- package/dist/index.d.mts +234 -1
- package/dist/index.d.ts +234 -1
- package/dist/index.js +862 -18
- package/dist/index.mjs +851 -16
- package/example-app.tsx +197 -0
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1289,9 +1289,16 @@ __export(index_exports, {
|
|
|
1289
1289
|
CrudifyLogin: () => CrudifyLogin_default,
|
|
1290
1290
|
ERROR_CODES: () => ERROR_CODES,
|
|
1291
1291
|
ERROR_SEVERITY_MAP: () => ERROR_SEVERITY_MAP,
|
|
1292
|
+
LoginComponent: () => LoginComponent,
|
|
1293
|
+
ProtectedRoute: () => ProtectedRoute,
|
|
1294
|
+
SessionDebugInfo: () => SessionDebugInfo,
|
|
1295
|
+
SessionManager: () => SessionManager,
|
|
1296
|
+
SessionProvider: () => SessionProvider,
|
|
1297
|
+
SessionStatus: () => SessionStatus,
|
|
1298
|
+
TokenStorage: () => TokenStorage,
|
|
1292
1299
|
UserProfileDisplay: () => UserProfileDisplay_default,
|
|
1293
1300
|
configurationManager: () => configurationManager,
|
|
1294
|
-
crudify: () =>
|
|
1301
|
+
crudify: () => import_crudify_browser9.default,
|
|
1295
1302
|
crudifyInitializer: () => crudifyInitializer,
|
|
1296
1303
|
decodeJwtSafely: () => decodeJwtSafely,
|
|
1297
1304
|
getCookie: () => getCookie,
|
|
@@ -1314,10 +1321,12 @@ __export(index_exports, {
|
|
|
1314
1321
|
useCrudifyInstance: () => useCrudifyInstance,
|
|
1315
1322
|
useCrudifyLogin: () => useCrudifyLogin,
|
|
1316
1323
|
useCrudifyUser: () => useCrudifyUser,
|
|
1324
|
+
useSession: () => useSession,
|
|
1325
|
+
useSessionContext: () => useSessionContext,
|
|
1317
1326
|
useUserProfile: () => useUserProfile
|
|
1318
1327
|
});
|
|
1319
1328
|
module.exports = __toCommonJS(index_exports);
|
|
1320
|
-
var
|
|
1329
|
+
var import_crudify_browser9 = __toESM(require("@nocios/crudify-browser"));
|
|
1321
1330
|
__reExport(index_exports, require("@nocios/crudify-browser"), module.exports);
|
|
1322
1331
|
|
|
1323
1332
|
// src/components/CrudifyLogin/index.tsx
|
|
@@ -2009,7 +2018,7 @@ var useCrudifyAuth = () => {
|
|
|
2009
2018
|
// src/components/CrudifyLogin/Forms/LoginForm.tsx
|
|
2010
2019
|
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
2011
2020
|
var LoginForm = ({ onScreenChange, onExternalNavigate, onLoginSuccess, onError, redirectUrl = "/" }) => {
|
|
2012
|
-
const { crudify:
|
|
2021
|
+
const { crudify: crudify9 } = useCrudify();
|
|
2013
2022
|
const { state, updateFormData, setFieldError, clearErrors, setLoading } = useLoginState();
|
|
2014
2023
|
const { setToken } = useCrudifyAuth();
|
|
2015
2024
|
const { t } = useTranslation();
|
|
@@ -2061,10 +2070,10 @@ var LoginForm = ({ onScreenChange, onExternalNavigate, onLoginSuccess, onError,
|
|
|
2061
2070
|
clearErrors();
|
|
2062
2071
|
setLoading(true);
|
|
2063
2072
|
try {
|
|
2064
|
-
if (!
|
|
2073
|
+
if (!crudify9) {
|
|
2065
2074
|
throw new Error("Crudify not initialized");
|
|
2066
2075
|
}
|
|
2067
|
-
const response = await
|
|
2076
|
+
const response = await crudify9.login(state.formData.username, state.formData.password);
|
|
2068
2077
|
setLoading(false);
|
|
2069
2078
|
if (response.success) {
|
|
2070
2079
|
console.log("\u{1F510} LoginForm - Login successful, setting tokens");
|
|
@@ -2227,7 +2236,7 @@ var import_react7 = require("react");
|
|
|
2227
2236
|
var import_material2 = require("@mui/material");
|
|
2228
2237
|
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
2229
2238
|
var ForgotPasswordForm = ({ onScreenChange, onError }) => {
|
|
2230
|
-
const { crudify:
|
|
2239
|
+
const { crudify: crudify9 } = useCrudify();
|
|
2231
2240
|
const [email, setEmail] = (0, import_react7.useState)("");
|
|
2232
2241
|
const [loading, setLoading] = (0, import_react7.useState)(false);
|
|
2233
2242
|
const [errors, setErrors] = (0, import_react7.useState)([]);
|
|
@@ -2256,7 +2265,7 @@ var ForgotPasswordForm = ({ onScreenChange, onError }) => {
|
|
|
2256
2265
|
return emailRegex.test(email2);
|
|
2257
2266
|
};
|
|
2258
2267
|
const handleSubmit = async () => {
|
|
2259
|
-
if (loading || !
|
|
2268
|
+
if (loading || !crudify9) return;
|
|
2260
2269
|
setErrors([]);
|
|
2261
2270
|
setHelperTextEmail(null);
|
|
2262
2271
|
if (!email) {
|
|
@@ -2270,7 +2279,7 @@ var ForgotPasswordForm = ({ onScreenChange, onError }) => {
|
|
|
2270
2279
|
setLoading(true);
|
|
2271
2280
|
try {
|
|
2272
2281
|
const data = [{ operation: "requestPasswordReset", data: { email } }];
|
|
2273
|
-
const response = await
|
|
2282
|
+
const response = await crudify9.transaction(data);
|
|
2274
2283
|
if (response.success) {
|
|
2275
2284
|
if (response.data && response.data.existingCodeValid) {
|
|
2276
2285
|
setCodeAlreadyExists(true);
|
|
@@ -2373,7 +2382,7 @@ var import_react8 = require("react");
|
|
|
2373
2382
|
var import_material3 = require("@mui/material");
|
|
2374
2383
|
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
2375
2384
|
var ResetPasswordForm = ({ onScreenChange, onError, searchParams, onResetSuccess }) => {
|
|
2376
|
-
const { crudify:
|
|
2385
|
+
const { crudify: crudify9 } = useCrudify();
|
|
2377
2386
|
const [newPassword, setNewPassword] = (0, import_react8.useState)("");
|
|
2378
2387
|
const [confirmPassword, setConfirmPassword] = (0, import_react8.useState)("");
|
|
2379
2388
|
const [loading, setLoading] = (0, import_react8.useState)(false);
|
|
@@ -2453,9 +2462,9 @@ var ResetPasswordForm = ({ onScreenChange, onError, searchParams, onResetSuccess
|
|
|
2453
2462
|
setErrors([t("resetPassword.invalidCode")]);
|
|
2454
2463
|
setValidatingCode(false);
|
|
2455
2464
|
setTimeout(() => onScreenChange?.("forgotPassword"), 3e3);
|
|
2456
|
-
}, [searchParams,
|
|
2465
|
+
}, [searchParams, crudify9, t, onScreenChange]);
|
|
2457
2466
|
(0, import_react8.useEffect)(() => {
|
|
2458
|
-
if (
|
|
2467
|
+
if (crudify9 && pendingValidation && !isValidating) {
|
|
2459
2468
|
setIsValidating(true);
|
|
2460
2469
|
const validateCode = async (emailToValidate, codeToValidate) => {
|
|
2461
2470
|
try {
|
|
@@ -2465,7 +2474,7 @@ var ResetPasswordForm = ({ onScreenChange, onError, searchParams, onResetSuccess
|
|
|
2465
2474
|
data: { email: emailToValidate, codePassword: codeToValidate }
|
|
2466
2475
|
}
|
|
2467
2476
|
];
|
|
2468
|
-
const response = await
|
|
2477
|
+
const response = await crudify9.transaction(data);
|
|
2469
2478
|
if (response.data && Array.isArray(response.data)) {
|
|
2470
2479
|
const validationResult = response.data[0];
|
|
2471
2480
|
if (validationResult && validationResult.response && validationResult.response.status === "OK") {
|
|
@@ -2494,7 +2503,7 @@ var ResetPasswordForm = ({ onScreenChange, onError, searchParams, onResetSuccess
|
|
|
2494
2503
|
};
|
|
2495
2504
|
validateCode(pendingValidation.email, pendingValidation.code);
|
|
2496
2505
|
}
|
|
2497
|
-
}, [
|
|
2506
|
+
}, [crudify9, pendingValidation, t, onScreenChange]);
|
|
2498
2507
|
const validatePassword = (password) => {
|
|
2499
2508
|
if (password.length < 8) {
|
|
2500
2509
|
return t("resetPassword.passwordTooShort");
|
|
@@ -2502,7 +2511,7 @@ var ResetPasswordForm = ({ onScreenChange, onError, searchParams, onResetSuccess
|
|
|
2502
2511
|
return null;
|
|
2503
2512
|
};
|
|
2504
2513
|
const handleSubmit = async () => {
|
|
2505
|
-
if (loading || !
|
|
2514
|
+
if (loading || !crudify9) return;
|
|
2506
2515
|
setErrors([]);
|
|
2507
2516
|
setHelperTextNewPassword(null);
|
|
2508
2517
|
setHelperTextConfirmPassword(null);
|
|
@@ -2533,7 +2542,7 @@ var ResetPasswordForm = ({ onScreenChange, onError, searchParams, onResetSuccess
|
|
|
2533
2542
|
data: { email, codePassword: code, newPassword }
|
|
2534
2543
|
}
|
|
2535
2544
|
];
|
|
2536
|
-
const response = await
|
|
2545
|
+
const response = await crudify9.transaction(data);
|
|
2537
2546
|
if (response.success) {
|
|
2538
2547
|
setErrors([]);
|
|
2539
2548
|
setTimeout(() => {
|
|
@@ -2644,7 +2653,7 @@ var import_react9 = require("react");
|
|
|
2644
2653
|
var import_material4 = require("@mui/material");
|
|
2645
2654
|
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
2646
2655
|
var CheckCodeForm = ({ onScreenChange, onError, searchParams }) => {
|
|
2647
|
-
const { crudify:
|
|
2656
|
+
const { crudify: crudify9 } = useCrudify();
|
|
2648
2657
|
const [code, setCode] = (0, import_react9.useState)("");
|
|
2649
2658
|
const [loading, setLoading] = (0, import_react9.useState)(false);
|
|
2650
2659
|
const [errors, setErrors] = (0, import_react9.useState)([]);
|
|
@@ -2683,7 +2692,7 @@ var CheckCodeForm = ({ onScreenChange, onError, searchParams }) => {
|
|
|
2683
2692
|
}
|
|
2684
2693
|
}, [searchParams, onScreenChange]);
|
|
2685
2694
|
const handleSubmit = async () => {
|
|
2686
|
-
if (loading || !
|
|
2695
|
+
if (loading || !crudify9) return;
|
|
2687
2696
|
setErrors([]);
|
|
2688
2697
|
setHelperTextCode(null);
|
|
2689
2698
|
if (!code) {
|
|
@@ -2702,7 +2711,7 @@ var CheckCodeForm = ({ onScreenChange, onError, searchParams }) => {
|
|
|
2702
2711
|
data: { email, codePassword: code }
|
|
2703
2712
|
}
|
|
2704
2713
|
];
|
|
2705
|
-
const response = await
|
|
2714
|
+
const response = await crudify9.transaction(data);
|
|
2706
2715
|
if (response.success) {
|
|
2707
2716
|
onScreenChange?.("resetPassword", { email, code, fromCodeVerification: "true" });
|
|
2708
2717
|
} else {
|
|
@@ -3699,12 +3708,18 @@ var useCrudifyInstance = () => {
|
|
|
3699
3708
|
}
|
|
3700
3709
|
}, [isReady, waitForReady]);
|
|
3701
3710
|
const ensureTokenSync = (0, import_react15.useCallback)(async () => {
|
|
3711
|
+
console.log("\u{1F504} useCrudifyInstance - ensureTokenSync: Starting token sync check");
|
|
3702
3712
|
const { tokenManager: tokenManager2 } = await Promise.resolve().then(() => (init_TokenManager(), TokenManager_exports));
|
|
3703
3713
|
const tmToken = tokenManager2.getToken();
|
|
3704
3714
|
const crudifyToken = import_crudify_browser7.default.getToken?.();
|
|
3715
|
+
console.log(" - TokenManager token in sync check:", tmToken ? `${tmToken.substring(0, 20)}...` : "null");
|
|
3716
|
+
console.log(" - crudify token in sync check:", crudifyToken ? `${crudifyToken.substring(0, 20)}...` : "null");
|
|
3705
3717
|
if (tmToken && tmToken !== crudifyToken) {
|
|
3706
3718
|
console.log("\u{1F504} useCrudifyInstance - Forcing token sync for authenticated operation");
|
|
3707
3719
|
import_crudify_browser7.default.setToken(tmToken);
|
|
3720
|
+
console.log(" - crudify token after sync:", import_crudify_browser7.default.getToken?.() ? `${import_crudify_browser7.default.getToken?.()?.substring(0, 20)}...` : "null");
|
|
3721
|
+
} else {
|
|
3722
|
+
console.log("\u{1F504} useCrudifyInstance - No token sync needed, tokens match or no token");
|
|
3708
3723
|
}
|
|
3709
3724
|
}, []);
|
|
3710
3725
|
const getStructure = (0, import_react15.useCallback)(async (options) => {
|
|
@@ -3869,12 +3884,839 @@ init_CrudifyDataProvider();
|
|
|
3869
3884
|
init_jwtUtils();
|
|
3870
3885
|
init_cookies();
|
|
3871
3886
|
init_secureStorage();
|
|
3887
|
+
|
|
3888
|
+
// src/core/SessionManager.ts
|
|
3889
|
+
var import_crudify_browser8 = __toESM(require("@nocios/crudify-browser"));
|
|
3890
|
+
|
|
3891
|
+
// src/utils/tokenStorage.ts
|
|
3892
|
+
var import_crypto_js2 = __toESM(require("crypto-js"));
|
|
3893
|
+
var _TokenStorage = class _TokenStorage {
|
|
3894
|
+
/**
|
|
3895
|
+
* Configurar tipo de almacenamiento
|
|
3896
|
+
*/
|
|
3897
|
+
static setStorageType(type) {
|
|
3898
|
+
_TokenStorage.storageType = type;
|
|
3899
|
+
}
|
|
3900
|
+
/**
|
|
3901
|
+
* Verificar si el storage está disponible
|
|
3902
|
+
*/
|
|
3903
|
+
static isStorageAvailable(type) {
|
|
3904
|
+
try {
|
|
3905
|
+
const storage = window[type];
|
|
3906
|
+
const testKey = "__storage_test__";
|
|
3907
|
+
storage.setItem(testKey, "test");
|
|
3908
|
+
storage.removeItem(testKey);
|
|
3909
|
+
return true;
|
|
3910
|
+
} catch {
|
|
3911
|
+
return false;
|
|
3912
|
+
}
|
|
3913
|
+
}
|
|
3914
|
+
/**
|
|
3915
|
+
* Obtener instancia de storage
|
|
3916
|
+
*/
|
|
3917
|
+
static getStorage() {
|
|
3918
|
+
if (_TokenStorage.storageType === "none") return null;
|
|
3919
|
+
if (!_TokenStorage.isStorageAvailable(_TokenStorage.storageType)) {
|
|
3920
|
+
console.warn(`Crudify: ${_TokenStorage.storageType} not available, tokens won't persist`);
|
|
3921
|
+
return null;
|
|
3922
|
+
}
|
|
3923
|
+
return window[_TokenStorage.storageType];
|
|
3924
|
+
}
|
|
3925
|
+
/**
|
|
3926
|
+
* Encriptar datos sensibles
|
|
3927
|
+
*/
|
|
3928
|
+
static encrypt(data) {
|
|
3929
|
+
try {
|
|
3930
|
+
return import_crypto_js2.default.AES.encrypt(data, _TokenStorage.ENCRYPTION_KEY).toString();
|
|
3931
|
+
} catch (error) {
|
|
3932
|
+
console.error("Crudify: Encryption failed", error);
|
|
3933
|
+
return data;
|
|
3934
|
+
}
|
|
3935
|
+
}
|
|
3936
|
+
/**
|
|
3937
|
+
* Desencriptar datos
|
|
3938
|
+
*/
|
|
3939
|
+
static decrypt(encryptedData) {
|
|
3940
|
+
try {
|
|
3941
|
+
const bytes = import_crypto_js2.default.AES.decrypt(encryptedData, _TokenStorage.ENCRYPTION_KEY);
|
|
3942
|
+
const decrypted = bytes.toString(import_crypto_js2.default.enc.Utf8);
|
|
3943
|
+
return decrypted || encryptedData;
|
|
3944
|
+
} catch (error) {
|
|
3945
|
+
console.error("Crudify: Decryption failed", error);
|
|
3946
|
+
return encryptedData;
|
|
3947
|
+
}
|
|
3948
|
+
}
|
|
3949
|
+
/**
|
|
3950
|
+
* Guardar tokens de forma segura
|
|
3951
|
+
*/
|
|
3952
|
+
static saveTokens(tokens) {
|
|
3953
|
+
const storage = _TokenStorage.getStorage();
|
|
3954
|
+
if (!storage) return;
|
|
3955
|
+
try {
|
|
3956
|
+
const tokenData = {
|
|
3957
|
+
accessToken: tokens.accessToken,
|
|
3958
|
+
refreshToken: tokens.refreshToken,
|
|
3959
|
+
expiresAt: tokens.expiresAt,
|
|
3960
|
+
refreshExpiresAt: tokens.refreshExpiresAt,
|
|
3961
|
+
savedAt: Date.now()
|
|
3962
|
+
};
|
|
3963
|
+
const encrypted = _TokenStorage.encrypt(JSON.stringify(tokenData));
|
|
3964
|
+
storage.setItem(_TokenStorage.TOKEN_KEY, encrypted);
|
|
3965
|
+
console.debug("Crudify: Tokens saved successfully");
|
|
3966
|
+
} catch (error) {
|
|
3967
|
+
console.error("Crudify: Failed to save tokens", error);
|
|
3968
|
+
}
|
|
3969
|
+
}
|
|
3970
|
+
/**
|
|
3971
|
+
* Obtener tokens guardados
|
|
3972
|
+
*/
|
|
3973
|
+
static getTokens() {
|
|
3974
|
+
const storage = _TokenStorage.getStorage();
|
|
3975
|
+
if (!storage) return null;
|
|
3976
|
+
try {
|
|
3977
|
+
const encrypted = storage.getItem(_TokenStorage.TOKEN_KEY);
|
|
3978
|
+
if (!encrypted) return null;
|
|
3979
|
+
const decrypted = _TokenStorage.decrypt(encrypted);
|
|
3980
|
+
const tokenData = JSON.parse(decrypted);
|
|
3981
|
+
if (!tokenData.accessToken || !tokenData.refreshToken || !tokenData.expiresAt || !tokenData.refreshExpiresAt) {
|
|
3982
|
+
console.warn("Crudify: Incomplete token data found, clearing storage");
|
|
3983
|
+
_TokenStorage.clearTokens();
|
|
3984
|
+
return null;
|
|
3985
|
+
}
|
|
3986
|
+
if (Date.now() >= tokenData.refreshExpiresAt) {
|
|
3987
|
+
console.info("Crudify: Refresh token expired, clearing storage");
|
|
3988
|
+
_TokenStorage.clearTokens();
|
|
3989
|
+
return null;
|
|
3990
|
+
}
|
|
3991
|
+
return {
|
|
3992
|
+
accessToken: tokenData.accessToken,
|
|
3993
|
+
refreshToken: tokenData.refreshToken,
|
|
3994
|
+
expiresAt: tokenData.expiresAt,
|
|
3995
|
+
refreshExpiresAt: tokenData.refreshExpiresAt
|
|
3996
|
+
};
|
|
3997
|
+
} catch (error) {
|
|
3998
|
+
console.error("Crudify: Failed to retrieve tokens", error);
|
|
3999
|
+
_TokenStorage.clearTokens();
|
|
4000
|
+
return null;
|
|
4001
|
+
}
|
|
4002
|
+
}
|
|
4003
|
+
/**
|
|
4004
|
+
* Limpiar tokens almacenados
|
|
4005
|
+
*/
|
|
4006
|
+
static clearTokens() {
|
|
4007
|
+
const storage = _TokenStorage.getStorage();
|
|
4008
|
+
if (!storage) return;
|
|
4009
|
+
try {
|
|
4010
|
+
storage.removeItem(_TokenStorage.TOKEN_KEY);
|
|
4011
|
+
console.debug("Crudify: Tokens cleared from storage");
|
|
4012
|
+
} catch (error) {
|
|
4013
|
+
console.error("Crudify: Failed to clear tokens", error);
|
|
4014
|
+
}
|
|
4015
|
+
}
|
|
4016
|
+
/**
|
|
4017
|
+
* Verificar si hay tokens válidos guardados
|
|
4018
|
+
*/
|
|
4019
|
+
static hasValidTokens() {
|
|
4020
|
+
const tokens = _TokenStorage.getTokens();
|
|
4021
|
+
return tokens !== null;
|
|
4022
|
+
}
|
|
4023
|
+
/**
|
|
4024
|
+
* Obtener información de expiración
|
|
4025
|
+
*/
|
|
4026
|
+
static getExpirationInfo() {
|
|
4027
|
+
const tokens = _TokenStorage.getTokens();
|
|
4028
|
+
if (!tokens) return null;
|
|
4029
|
+
const now = Date.now();
|
|
4030
|
+
return {
|
|
4031
|
+
accessExpired: now >= tokens.expiresAt,
|
|
4032
|
+
refreshExpired: now >= tokens.refreshExpiresAt,
|
|
4033
|
+
accessExpiresIn: Math.max(0, tokens.expiresAt - now),
|
|
4034
|
+
refreshExpiresIn: Math.max(0, tokens.refreshExpiresAt - now)
|
|
4035
|
+
};
|
|
4036
|
+
}
|
|
4037
|
+
/**
|
|
4038
|
+
* Actualizar solo el access token (después de refresh)
|
|
4039
|
+
*/
|
|
4040
|
+
static updateAccessToken(newAccessToken, newExpiresAt) {
|
|
4041
|
+
const existingTokens = _TokenStorage.getTokens();
|
|
4042
|
+
if (!existingTokens) {
|
|
4043
|
+
console.warn("Crudify: Cannot update access token, no existing tokens found");
|
|
4044
|
+
return;
|
|
4045
|
+
}
|
|
4046
|
+
_TokenStorage.saveTokens({
|
|
4047
|
+
...existingTokens,
|
|
4048
|
+
accessToken: newAccessToken,
|
|
4049
|
+
expiresAt: newExpiresAt
|
|
4050
|
+
});
|
|
4051
|
+
}
|
|
4052
|
+
};
|
|
4053
|
+
_TokenStorage.TOKEN_KEY = "crudify_tokens";
|
|
4054
|
+
_TokenStorage.ENCRYPTION_KEY = "crudify_secure_key_v1";
|
|
4055
|
+
_TokenStorage.storageType = "localStorage";
|
|
4056
|
+
var TokenStorage = _TokenStorage;
|
|
4057
|
+
|
|
4058
|
+
// src/core/SessionManager.ts
|
|
4059
|
+
var SessionManager = class _SessionManager {
|
|
4060
|
+
constructor() {
|
|
4061
|
+
this.config = {};
|
|
4062
|
+
this.initialized = false;
|
|
4063
|
+
}
|
|
4064
|
+
static getInstance() {
|
|
4065
|
+
if (!_SessionManager.instance) {
|
|
4066
|
+
_SessionManager.instance = new _SessionManager();
|
|
4067
|
+
}
|
|
4068
|
+
return _SessionManager.instance;
|
|
4069
|
+
}
|
|
4070
|
+
/**
|
|
4071
|
+
* Inicializar el SessionManager
|
|
4072
|
+
*/
|
|
4073
|
+
async initialize(config = {}) {
|
|
4074
|
+
if (this.initialized) {
|
|
4075
|
+
console.warn("SessionManager: Already initialized");
|
|
4076
|
+
return;
|
|
4077
|
+
}
|
|
4078
|
+
this.config = {
|
|
4079
|
+
storageType: "localStorage",
|
|
4080
|
+
autoRestore: true,
|
|
4081
|
+
enableLogging: false,
|
|
4082
|
+
...config
|
|
4083
|
+
};
|
|
4084
|
+
TokenStorage.setStorageType(this.config.storageType || "localStorage");
|
|
4085
|
+
if (this.config.enableLogging) {
|
|
4086
|
+
}
|
|
4087
|
+
if (this.config.autoRestore) {
|
|
4088
|
+
await this.restoreSession();
|
|
4089
|
+
}
|
|
4090
|
+
this.initialized = true;
|
|
4091
|
+
this.log("SessionManager initialized successfully");
|
|
4092
|
+
}
|
|
4093
|
+
/**
|
|
4094
|
+
* Login con persistencia automática
|
|
4095
|
+
*/
|
|
4096
|
+
async login(email, password) {
|
|
4097
|
+
try {
|
|
4098
|
+
this.log("Attempting login...");
|
|
4099
|
+
const response = await import_crudify_browser8.default.login(email, password);
|
|
4100
|
+
if (!response.success) {
|
|
4101
|
+
this.log("Login failed:", response.errors);
|
|
4102
|
+
return {
|
|
4103
|
+
success: false,
|
|
4104
|
+
error: this.formatError(response.errors)
|
|
4105
|
+
};
|
|
4106
|
+
}
|
|
4107
|
+
const tokens = {
|
|
4108
|
+
accessToken: response.data.token,
|
|
4109
|
+
refreshToken: response.data.refreshToken,
|
|
4110
|
+
expiresAt: response.data.expiresAt,
|
|
4111
|
+
refreshExpiresAt: response.data.refreshExpiresAt
|
|
4112
|
+
};
|
|
4113
|
+
TokenStorage.saveTokens(tokens);
|
|
4114
|
+
this.log("Login successful, tokens saved");
|
|
4115
|
+
this.config.onLoginSuccess?.(tokens);
|
|
4116
|
+
return {
|
|
4117
|
+
success: true,
|
|
4118
|
+
tokens
|
|
4119
|
+
};
|
|
4120
|
+
} catch (error) {
|
|
4121
|
+
this.log("Login error:", error);
|
|
4122
|
+
return {
|
|
4123
|
+
success: false,
|
|
4124
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
4125
|
+
};
|
|
4126
|
+
}
|
|
4127
|
+
}
|
|
4128
|
+
/**
|
|
4129
|
+
* Logout con limpieza de tokens
|
|
4130
|
+
*/
|
|
4131
|
+
async logout() {
|
|
4132
|
+
try {
|
|
4133
|
+
this.log("Logging out...");
|
|
4134
|
+
await import_crudify_browser8.default.logout();
|
|
4135
|
+
TokenStorage.clearTokens();
|
|
4136
|
+
this.log("Logout successful");
|
|
4137
|
+
this.config.onLogout?.();
|
|
4138
|
+
} catch (error) {
|
|
4139
|
+
this.log("Logout error:", error);
|
|
4140
|
+
TokenStorage.clearTokens();
|
|
4141
|
+
}
|
|
4142
|
+
}
|
|
4143
|
+
/**
|
|
4144
|
+
* Restaurar sesión desde storage
|
|
4145
|
+
*/
|
|
4146
|
+
async restoreSession() {
|
|
4147
|
+
try {
|
|
4148
|
+
this.log("Attempting to restore session...");
|
|
4149
|
+
const savedTokens = TokenStorage.getTokens();
|
|
4150
|
+
if (!savedTokens) {
|
|
4151
|
+
this.log("No valid tokens found in storage");
|
|
4152
|
+
return false;
|
|
4153
|
+
}
|
|
4154
|
+
import_crudify_browser8.default.setTokens({
|
|
4155
|
+
accessToken: savedTokens.accessToken,
|
|
4156
|
+
refreshToken: savedTokens.refreshToken,
|
|
4157
|
+
expiresAt: savedTokens.expiresAt,
|
|
4158
|
+
refreshExpiresAt: savedTokens.refreshExpiresAt
|
|
4159
|
+
});
|
|
4160
|
+
this.log("Session restored successfully");
|
|
4161
|
+
this.config.onSessionRestored?.(savedTokens);
|
|
4162
|
+
return true;
|
|
4163
|
+
} catch (error) {
|
|
4164
|
+
this.log("Session restore error:", error);
|
|
4165
|
+
TokenStorage.clearTokens();
|
|
4166
|
+
return false;
|
|
4167
|
+
}
|
|
4168
|
+
}
|
|
4169
|
+
/**
|
|
4170
|
+
* Verificar si el usuario está autenticado
|
|
4171
|
+
*/
|
|
4172
|
+
isAuthenticated() {
|
|
4173
|
+
return import_crudify_browser8.default.isLogin() || TokenStorage.hasValidTokens();
|
|
4174
|
+
}
|
|
4175
|
+
/**
|
|
4176
|
+
* Obtener información de tokens actuales
|
|
4177
|
+
*/
|
|
4178
|
+
getTokenInfo() {
|
|
4179
|
+
const crudifyTokens = import_crudify_browser8.default.getTokenData();
|
|
4180
|
+
const storageInfo = TokenStorage.getExpirationInfo();
|
|
4181
|
+
return {
|
|
4182
|
+
isLoggedIn: this.isAuthenticated(),
|
|
4183
|
+
crudifyTokens,
|
|
4184
|
+
storageInfo,
|
|
4185
|
+
hasValidTokens: TokenStorage.hasValidTokens()
|
|
4186
|
+
};
|
|
4187
|
+
}
|
|
4188
|
+
/**
|
|
4189
|
+
* Refrescar tokens manualmente
|
|
4190
|
+
*/
|
|
4191
|
+
async refreshTokens() {
|
|
4192
|
+
try {
|
|
4193
|
+
this.log("Manually refreshing tokens...");
|
|
4194
|
+
const response = await import_crudify_browser8.default.refreshAccessToken();
|
|
4195
|
+
if (!response.success) {
|
|
4196
|
+
this.log("Token refresh failed:", response.errors);
|
|
4197
|
+
TokenStorage.clearTokens();
|
|
4198
|
+
this.config.onSessionExpired?.();
|
|
4199
|
+
return false;
|
|
4200
|
+
}
|
|
4201
|
+
const newTokens = {
|
|
4202
|
+
accessToken: response.data.token,
|
|
4203
|
+
refreshToken: response.data.refreshToken,
|
|
4204
|
+
expiresAt: response.data.expiresAt,
|
|
4205
|
+
refreshExpiresAt: response.data.refreshExpiresAt
|
|
4206
|
+
};
|
|
4207
|
+
TokenStorage.saveTokens(newTokens);
|
|
4208
|
+
this.log("Tokens refreshed and saved successfully");
|
|
4209
|
+
return true;
|
|
4210
|
+
} catch (error) {
|
|
4211
|
+
this.log("Token refresh error:", error);
|
|
4212
|
+
TokenStorage.clearTokens();
|
|
4213
|
+
this.config.onSessionExpired?.();
|
|
4214
|
+
return false;
|
|
4215
|
+
}
|
|
4216
|
+
}
|
|
4217
|
+
/**
|
|
4218
|
+
* Configurar interceptor de respuesta para manejo automático de errores
|
|
4219
|
+
*/
|
|
4220
|
+
setupResponseInterceptor() {
|
|
4221
|
+
import_crudify_browser8.default.setResponseInterceptor(async (response) => {
|
|
4222
|
+
if (response.errors) {
|
|
4223
|
+
const hasAuthError = response.errors.some(
|
|
4224
|
+
(error) => error.message?.includes("Unauthorized") || error.message?.includes("Token") || error.extensions?.code === "UNAUTHENTICATED"
|
|
4225
|
+
);
|
|
4226
|
+
if (hasAuthError && TokenStorage.hasValidTokens()) {
|
|
4227
|
+
this.log("Auth error detected, attempting token refresh...");
|
|
4228
|
+
const refreshSuccess = await this.refreshTokens();
|
|
4229
|
+
if (!refreshSuccess) {
|
|
4230
|
+
this.log("Session expired, triggering callback");
|
|
4231
|
+
this.config.onSessionExpired?.();
|
|
4232
|
+
}
|
|
4233
|
+
}
|
|
4234
|
+
}
|
|
4235
|
+
return response;
|
|
4236
|
+
});
|
|
4237
|
+
this.log("Response interceptor configured");
|
|
4238
|
+
}
|
|
4239
|
+
/**
|
|
4240
|
+
* Limpiar sesión completamente
|
|
4241
|
+
*/
|
|
4242
|
+
clearSession() {
|
|
4243
|
+
TokenStorage.clearTokens();
|
|
4244
|
+
import_crudify_browser8.default.logout();
|
|
4245
|
+
this.log("Session cleared completely");
|
|
4246
|
+
}
|
|
4247
|
+
// Métodos privados
|
|
4248
|
+
log(message, ...args) {
|
|
4249
|
+
if (this.config.enableLogging) {
|
|
4250
|
+
console.log(`[SessionManager] ${message}`, ...args);
|
|
4251
|
+
}
|
|
4252
|
+
}
|
|
4253
|
+
formatError(errors) {
|
|
4254
|
+
if (!errors) return "Unknown error";
|
|
4255
|
+
if (typeof errors === "string") return errors;
|
|
4256
|
+
if (typeof errors === "object") {
|
|
4257
|
+
const errorMessages = Object.values(errors).flat();
|
|
4258
|
+
return errorMessages.join(", ");
|
|
4259
|
+
}
|
|
4260
|
+
return "Authentication failed";
|
|
4261
|
+
}
|
|
4262
|
+
};
|
|
4263
|
+
|
|
4264
|
+
// src/hooks/useSession.ts
|
|
4265
|
+
var import_react16 = require("react");
|
|
4266
|
+
function useSession(options = {}) {
|
|
4267
|
+
const [state, setState] = (0, import_react16.useState)({
|
|
4268
|
+
isAuthenticated: false,
|
|
4269
|
+
isLoading: true,
|
|
4270
|
+
isInitialized: false,
|
|
4271
|
+
tokens: null,
|
|
4272
|
+
error: null
|
|
4273
|
+
});
|
|
4274
|
+
const sessionManager = SessionManager.getInstance();
|
|
4275
|
+
const initialize = (0, import_react16.useCallback)(async () => {
|
|
4276
|
+
try {
|
|
4277
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
4278
|
+
const config = {
|
|
4279
|
+
autoRestore: options.autoRestore ?? true,
|
|
4280
|
+
enableLogging: options.enableLogging ?? false,
|
|
4281
|
+
onSessionExpired: () => {
|
|
4282
|
+
setState((prev) => ({
|
|
4283
|
+
...prev,
|
|
4284
|
+
isAuthenticated: false,
|
|
4285
|
+
tokens: null,
|
|
4286
|
+
error: "Session expired"
|
|
4287
|
+
}));
|
|
4288
|
+
options.onSessionExpired?.();
|
|
4289
|
+
},
|
|
4290
|
+
onSessionRestored: (tokens) => {
|
|
4291
|
+
setState((prev) => ({
|
|
4292
|
+
...prev,
|
|
4293
|
+
isAuthenticated: true,
|
|
4294
|
+
tokens,
|
|
4295
|
+
error: null
|
|
4296
|
+
}));
|
|
4297
|
+
options.onSessionRestored?.(tokens);
|
|
4298
|
+
},
|
|
4299
|
+
onLoginSuccess: (tokens) => {
|
|
4300
|
+
setState((prev) => ({
|
|
4301
|
+
...prev,
|
|
4302
|
+
isAuthenticated: true,
|
|
4303
|
+
tokens,
|
|
4304
|
+
error: null
|
|
4305
|
+
}));
|
|
4306
|
+
},
|
|
4307
|
+
onLogout: () => {
|
|
4308
|
+
setState((prev) => ({
|
|
4309
|
+
...prev,
|
|
4310
|
+
isAuthenticated: false,
|
|
4311
|
+
tokens: null,
|
|
4312
|
+
error: null
|
|
4313
|
+
}));
|
|
4314
|
+
}
|
|
4315
|
+
};
|
|
4316
|
+
await sessionManager.initialize(config);
|
|
4317
|
+
sessionManager.setupResponseInterceptor();
|
|
4318
|
+
const isAuth = sessionManager.isAuthenticated();
|
|
4319
|
+
const tokenInfo = sessionManager.getTokenInfo();
|
|
4320
|
+
setState((prev) => ({
|
|
4321
|
+
...prev,
|
|
4322
|
+
isAuthenticated: isAuth,
|
|
4323
|
+
isInitialized: true,
|
|
4324
|
+
isLoading: false,
|
|
4325
|
+
tokens: tokenInfo.crudifyTokens.accessToken ? {
|
|
4326
|
+
accessToken: tokenInfo.crudifyTokens.accessToken,
|
|
4327
|
+
refreshToken: tokenInfo.crudifyTokens.refreshToken,
|
|
4328
|
+
expiresAt: tokenInfo.crudifyTokens.expiresAt,
|
|
4329
|
+
refreshExpiresAt: tokenInfo.crudifyTokens.refreshExpiresAt
|
|
4330
|
+
} : null
|
|
4331
|
+
}));
|
|
4332
|
+
} catch (error) {
|
|
4333
|
+
setState((prev) => ({
|
|
4334
|
+
...prev,
|
|
4335
|
+
isLoading: false,
|
|
4336
|
+
isInitialized: true,
|
|
4337
|
+
error: error instanceof Error ? error.message : "Initialization failed"
|
|
4338
|
+
}));
|
|
4339
|
+
}
|
|
4340
|
+
}, [options.autoRestore, options.enableLogging, options.onSessionExpired, options.onSessionRestored]);
|
|
4341
|
+
const login = (0, import_react16.useCallback)(async (email, password) => {
|
|
4342
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
4343
|
+
try {
|
|
4344
|
+
const result = await sessionManager.login(email, password);
|
|
4345
|
+
if (result.success && result.tokens) {
|
|
4346
|
+
setState((prev) => ({
|
|
4347
|
+
...prev,
|
|
4348
|
+
isAuthenticated: true,
|
|
4349
|
+
tokens: result.tokens,
|
|
4350
|
+
isLoading: false,
|
|
4351
|
+
error: null
|
|
4352
|
+
}));
|
|
4353
|
+
} else {
|
|
4354
|
+
setState((prev) => ({
|
|
4355
|
+
...prev,
|
|
4356
|
+
isAuthenticated: false,
|
|
4357
|
+
tokens: null,
|
|
4358
|
+
isLoading: false,
|
|
4359
|
+
error: result.error || "Login failed"
|
|
4360
|
+
}));
|
|
4361
|
+
}
|
|
4362
|
+
return result;
|
|
4363
|
+
} catch (error) {
|
|
4364
|
+
const errorMsg = error instanceof Error ? error.message : "Login failed";
|
|
4365
|
+
setState((prev) => ({
|
|
4366
|
+
...prev,
|
|
4367
|
+
isAuthenticated: false,
|
|
4368
|
+
tokens: null,
|
|
4369
|
+
isLoading: false,
|
|
4370
|
+
error: errorMsg
|
|
4371
|
+
}));
|
|
4372
|
+
return {
|
|
4373
|
+
success: false,
|
|
4374
|
+
error: errorMsg
|
|
4375
|
+
};
|
|
4376
|
+
}
|
|
4377
|
+
}, [sessionManager]);
|
|
4378
|
+
const logout = (0, import_react16.useCallback)(async () => {
|
|
4379
|
+
setState((prev) => ({ ...prev, isLoading: true }));
|
|
4380
|
+
try {
|
|
4381
|
+
await sessionManager.logout();
|
|
4382
|
+
setState((prev) => ({
|
|
4383
|
+
...prev,
|
|
4384
|
+
isAuthenticated: false,
|
|
4385
|
+
tokens: null,
|
|
4386
|
+
isLoading: false,
|
|
4387
|
+
error: null
|
|
4388
|
+
}));
|
|
4389
|
+
} catch (error) {
|
|
4390
|
+
setState((prev) => ({
|
|
4391
|
+
...prev,
|
|
4392
|
+
isAuthenticated: false,
|
|
4393
|
+
tokens: null,
|
|
4394
|
+
isLoading: false,
|
|
4395
|
+
error: error instanceof Error ? error.message : "Logout error"
|
|
4396
|
+
}));
|
|
4397
|
+
}
|
|
4398
|
+
}, [sessionManager]);
|
|
4399
|
+
const refreshTokens = (0, import_react16.useCallback)(async () => {
|
|
4400
|
+
try {
|
|
4401
|
+
const success = await sessionManager.refreshTokens();
|
|
4402
|
+
if (success) {
|
|
4403
|
+
const tokenInfo = sessionManager.getTokenInfo();
|
|
4404
|
+
setState((prev) => ({
|
|
4405
|
+
...prev,
|
|
4406
|
+
tokens: tokenInfo.crudifyTokens.accessToken ? {
|
|
4407
|
+
accessToken: tokenInfo.crudifyTokens.accessToken,
|
|
4408
|
+
refreshToken: tokenInfo.crudifyTokens.refreshToken,
|
|
4409
|
+
expiresAt: tokenInfo.crudifyTokens.expiresAt,
|
|
4410
|
+
refreshExpiresAt: tokenInfo.crudifyTokens.refreshExpiresAt
|
|
4411
|
+
} : null,
|
|
4412
|
+
error: null
|
|
4413
|
+
}));
|
|
4414
|
+
} else {
|
|
4415
|
+
setState((prev) => ({
|
|
4416
|
+
...prev,
|
|
4417
|
+
isAuthenticated: false,
|
|
4418
|
+
tokens: null,
|
|
4419
|
+
error: "Token refresh failed"
|
|
4420
|
+
}));
|
|
4421
|
+
}
|
|
4422
|
+
return success;
|
|
4423
|
+
} catch (error) {
|
|
4424
|
+
setState((prev) => ({
|
|
4425
|
+
...prev,
|
|
4426
|
+
isAuthenticated: false,
|
|
4427
|
+
tokens: null,
|
|
4428
|
+
error: error instanceof Error ? error.message : "Token refresh failed"
|
|
4429
|
+
}));
|
|
4430
|
+
return false;
|
|
4431
|
+
}
|
|
4432
|
+
}, [sessionManager]);
|
|
4433
|
+
const clearError = (0, import_react16.useCallback)(() => {
|
|
4434
|
+
setState((prev) => ({ ...prev, error: null }));
|
|
4435
|
+
}, []);
|
|
4436
|
+
const getTokenInfo = (0, import_react16.useCallback)(() => {
|
|
4437
|
+
return sessionManager.getTokenInfo();
|
|
4438
|
+
}, [sessionManager]);
|
|
4439
|
+
(0, import_react16.useEffect)(() => {
|
|
4440
|
+
initialize();
|
|
4441
|
+
}, [initialize]);
|
|
4442
|
+
return {
|
|
4443
|
+
// Estado
|
|
4444
|
+
...state,
|
|
4445
|
+
// Acciones
|
|
4446
|
+
login,
|
|
4447
|
+
logout,
|
|
4448
|
+
refreshTokens,
|
|
4449
|
+
clearError,
|
|
4450
|
+
getTokenInfo,
|
|
4451
|
+
// Utilidades
|
|
4452
|
+
isExpiringSoon: state.tokens ? state.tokens.expiresAt - Date.now() < 5 * 60 * 1e3 : false,
|
|
4453
|
+
// 5 minutos
|
|
4454
|
+
expiresIn: state.tokens ? Math.max(0, state.tokens.expiresAt - Date.now()) : 0,
|
|
4455
|
+
refreshExpiresIn: state.tokens ? Math.max(0, state.tokens.refreshExpiresAt - Date.now()) : 0
|
|
4456
|
+
};
|
|
4457
|
+
}
|
|
4458
|
+
|
|
4459
|
+
// src/providers/SessionProvider.tsx
|
|
4460
|
+
var import_react17 = require("react");
|
|
4461
|
+
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
4462
|
+
var SessionContext = (0, import_react17.createContext)(void 0);
|
|
4463
|
+
function SessionProvider({ children, options = {} }) {
|
|
4464
|
+
const sessionData = useSession(options);
|
|
4465
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SessionContext.Provider, { value: sessionData, children });
|
|
4466
|
+
}
|
|
4467
|
+
function useSessionContext() {
|
|
4468
|
+
const context = (0, import_react17.useContext)(SessionContext);
|
|
4469
|
+
if (context === void 0) {
|
|
4470
|
+
throw new Error("useSessionContext must be used within a SessionProvider");
|
|
4471
|
+
}
|
|
4472
|
+
return context;
|
|
4473
|
+
}
|
|
4474
|
+
function ProtectedRoute({
|
|
4475
|
+
children,
|
|
4476
|
+
fallback = /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { children: "Please log in to access this content" }),
|
|
4477
|
+
redirectTo
|
|
4478
|
+
}) {
|
|
4479
|
+
const { isAuthenticated, isLoading, isInitialized } = useSessionContext();
|
|
4480
|
+
if (!isInitialized || isLoading) {
|
|
4481
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { children: "Loading..." });
|
|
4482
|
+
}
|
|
4483
|
+
if (!isAuthenticated) {
|
|
4484
|
+
if (redirectTo) {
|
|
4485
|
+
redirectTo();
|
|
4486
|
+
return null;
|
|
4487
|
+
}
|
|
4488
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_jsx_runtime12.Fragment, { children: fallback });
|
|
4489
|
+
}
|
|
4490
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_jsx_runtime12.Fragment, { children });
|
|
4491
|
+
}
|
|
4492
|
+
function SessionDebugInfo() {
|
|
4493
|
+
const session = useSessionContext();
|
|
4494
|
+
if (!session.isInitialized) {
|
|
4495
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { children: "Session not initialized" });
|
|
4496
|
+
}
|
|
4497
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { style: {
|
|
4498
|
+
padding: "10px",
|
|
4499
|
+
margin: "10px",
|
|
4500
|
+
border: "1px solid #ccc",
|
|
4501
|
+
borderRadius: "4px",
|
|
4502
|
+
fontSize: "12px",
|
|
4503
|
+
fontFamily: "monospace"
|
|
4504
|
+
}, children: [
|
|
4505
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("h4", { children: "Session Debug Info" }),
|
|
4506
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { children: [
|
|
4507
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("strong", { children: "Authenticated:" }),
|
|
4508
|
+
" ",
|
|
4509
|
+
session.isAuthenticated ? "Yes" : "No"
|
|
4510
|
+
] }),
|
|
4511
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { children: [
|
|
4512
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("strong", { children: "Loading:" }),
|
|
4513
|
+
" ",
|
|
4514
|
+
session.isLoading ? "Yes" : "No"
|
|
4515
|
+
] }),
|
|
4516
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { children: [
|
|
4517
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("strong", { children: "Error:" }),
|
|
4518
|
+
" ",
|
|
4519
|
+
session.error || "None"
|
|
4520
|
+
] }),
|
|
4521
|
+
session.tokens && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
|
|
4522
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { children: [
|
|
4523
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("strong", { children: "Access Token:" }),
|
|
4524
|
+
" ",
|
|
4525
|
+
session.tokens.accessToken.substring(0, 20),
|
|
4526
|
+
"..."
|
|
4527
|
+
] }),
|
|
4528
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { children: [
|
|
4529
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("strong", { children: "Refresh Token:" }),
|
|
4530
|
+
" ",
|
|
4531
|
+
session.tokens.refreshToken.substring(0, 20),
|
|
4532
|
+
"..."
|
|
4533
|
+
] }),
|
|
4534
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { children: [
|
|
4535
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("strong", { children: "Access Expires In:" }),
|
|
4536
|
+
" ",
|
|
4537
|
+
Math.round(session.expiresIn / 1e3 / 60),
|
|
4538
|
+
" minutes"
|
|
4539
|
+
] }),
|
|
4540
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { children: [
|
|
4541
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("strong", { children: "Refresh Expires In:" }),
|
|
4542
|
+
" ",
|
|
4543
|
+
Math.round(session.refreshExpiresIn / 1e3 / 60 / 60),
|
|
4544
|
+
" hours"
|
|
4545
|
+
] }),
|
|
4546
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { children: [
|
|
4547
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("strong", { children: "Expiring Soon:" }),
|
|
4548
|
+
" ",
|
|
4549
|
+
session.isExpiringSoon ? "Yes" : "No"
|
|
4550
|
+
] })
|
|
4551
|
+
] })
|
|
4552
|
+
] });
|
|
4553
|
+
}
|
|
4554
|
+
|
|
4555
|
+
// src/components/LoginComponent.tsx
|
|
4556
|
+
var import_react18 = require("react");
|
|
4557
|
+
var import_material8 = require("@mui/material");
|
|
4558
|
+
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
4559
|
+
function LoginComponent() {
|
|
4560
|
+
const [email, setEmail] = (0, import_react18.useState)("");
|
|
4561
|
+
const [password, setPassword] = (0, import_react18.useState)("");
|
|
4562
|
+
const [showForm, setShowForm] = (0, import_react18.useState)(false);
|
|
4563
|
+
const {
|
|
4564
|
+
isAuthenticated,
|
|
4565
|
+
isLoading,
|
|
4566
|
+
error,
|
|
4567
|
+
login,
|
|
4568
|
+
logout,
|
|
4569
|
+
refreshTokens,
|
|
4570
|
+
clearError,
|
|
4571
|
+
isExpiringSoon,
|
|
4572
|
+
expiresIn
|
|
4573
|
+
} = useSessionContext();
|
|
4574
|
+
const handleLogin = async (e) => {
|
|
4575
|
+
e.preventDefault();
|
|
4576
|
+
if (!email || !password) {
|
|
4577
|
+
return;
|
|
4578
|
+
}
|
|
4579
|
+
const result = await login(email, password);
|
|
4580
|
+
if (result.success) {
|
|
4581
|
+
setEmail("");
|
|
4582
|
+
setPassword("");
|
|
4583
|
+
setShowForm(false);
|
|
4584
|
+
}
|
|
4585
|
+
};
|
|
4586
|
+
const handleLogout = async () => {
|
|
4587
|
+
await logout();
|
|
4588
|
+
};
|
|
4589
|
+
const handleRefreshTokens = async () => {
|
|
4590
|
+
await refreshTokens();
|
|
4591
|
+
};
|
|
4592
|
+
if (isAuthenticated) {
|
|
4593
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_material8.Box, { sx: { maxWidth: 600, mx: "auto", p: 3 }, children: [
|
|
4594
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_material8.Typography, { variant: "h4", gutterBottom: true, children: "Welcome! \u{1F389}" }),
|
|
4595
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_material8.Alert, { severity: "success", sx: { mb: 3 }, children: "You are successfully logged in with Refresh Token Pattern enabled" }),
|
|
4596
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_material8.Box, { sx: { mb: 3, p: 2, bgcolor: "background.paper", border: 1, borderColor: "divider", borderRadius: 1 }, children: [
|
|
4597
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_material8.Typography, { variant: "h6", gutterBottom: true, children: "Token Status" }),
|
|
4598
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_material8.Typography, { variant: "body2", color: "text.secondary", children: [
|
|
4599
|
+
"Access Token expires in: ",
|
|
4600
|
+
Math.round(expiresIn / 1e3 / 60),
|
|
4601
|
+
" minutes"
|
|
4602
|
+
] }),
|
|
4603
|
+
isExpiringSoon && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_material8.Alert, { severity: "warning", sx: { mt: 1 }, children: "Token expires soon - automatic refresh will happen" })
|
|
4604
|
+
] }),
|
|
4605
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_material8.Box, { sx: { display: "flex", gap: 2, flexWrap: "wrap" }, children: [
|
|
4606
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
4607
|
+
import_material8.Button,
|
|
4608
|
+
{
|
|
4609
|
+
variant: "contained",
|
|
4610
|
+
onClick: handleRefreshTokens,
|
|
4611
|
+
disabled: isLoading,
|
|
4612
|
+
startIcon: isLoading ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_material8.CircularProgress, { size: 16 }) : null,
|
|
4613
|
+
children: "Refresh Tokens"
|
|
4614
|
+
}
|
|
4615
|
+
),
|
|
4616
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
4617
|
+
import_material8.Button,
|
|
4618
|
+
{
|
|
4619
|
+
variant: "outlined",
|
|
4620
|
+
color: "error",
|
|
4621
|
+
onClick: handleLogout,
|
|
4622
|
+
disabled: isLoading,
|
|
4623
|
+
children: "Logout"
|
|
4624
|
+
}
|
|
4625
|
+
)
|
|
4626
|
+
] }),
|
|
4627
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_material8.Alert, { severity: "error", sx: { mt: 2 }, onClose: clearError, children: error })
|
|
4628
|
+
] });
|
|
4629
|
+
}
|
|
4630
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_material8.Box, { sx: { maxWidth: 400, mx: "auto", p: 3 }, children: [
|
|
4631
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_material8.Typography, { variant: "h4", gutterBottom: true, align: "center", children: "Login with Refresh Tokens" }),
|
|
4632
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_material8.Alert, { severity: "info", sx: { mb: 3 }, children: "This demo shows the new Refresh Token Pattern with automatic session management" }),
|
|
4633
|
+
!showForm ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
4634
|
+
import_material8.Button,
|
|
4635
|
+
{
|
|
4636
|
+
fullWidth: true,
|
|
4637
|
+
variant: "contained",
|
|
4638
|
+
size: "large",
|
|
4639
|
+
onClick: () => setShowForm(true),
|
|
4640
|
+
sx: { mt: 2 },
|
|
4641
|
+
children: "Show Login Form"
|
|
4642
|
+
}
|
|
4643
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("form", { onSubmit: handleLogin, children: [
|
|
4644
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
4645
|
+
import_material8.TextField,
|
|
4646
|
+
{
|
|
4647
|
+
fullWidth: true,
|
|
4648
|
+
label: "Email",
|
|
4649
|
+
type: "email",
|
|
4650
|
+
value: email,
|
|
4651
|
+
onChange: (e) => setEmail(e.target.value),
|
|
4652
|
+
margin: "normal",
|
|
4653
|
+
required: true,
|
|
4654
|
+
autoComplete: "email"
|
|
4655
|
+
}
|
|
4656
|
+
),
|
|
4657
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
4658
|
+
import_material8.TextField,
|
|
4659
|
+
{
|
|
4660
|
+
fullWidth: true,
|
|
4661
|
+
label: "Password",
|
|
4662
|
+
type: "password",
|
|
4663
|
+
value: password,
|
|
4664
|
+
onChange: (e) => setPassword(e.target.value),
|
|
4665
|
+
margin: "normal",
|
|
4666
|
+
required: true,
|
|
4667
|
+
autoComplete: "current-password"
|
|
4668
|
+
}
|
|
4669
|
+
),
|
|
4670
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
4671
|
+
import_material8.Button,
|
|
4672
|
+
{
|
|
4673
|
+
type: "submit",
|
|
4674
|
+
fullWidth: true,
|
|
4675
|
+
variant: "contained",
|
|
4676
|
+
size: "large",
|
|
4677
|
+
disabled: isLoading,
|
|
4678
|
+
startIcon: isLoading ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_material8.CircularProgress, { size: 16 }) : null,
|
|
4679
|
+
sx: { mt: 3, mb: 2 },
|
|
4680
|
+
children: isLoading ? "Logging in..." : "Login"
|
|
4681
|
+
}
|
|
4682
|
+
)
|
|
4683
|
+
] }),
|
|
4684
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_material8.Alert, { severity: "error", sx: { mt: 2 }, onClose: clearError, children: error })
|
|
4685
|
+
] });
|
|
4686
|
+
}
|
|
4687
|
+
function SessionStatus() {
|
|
4688
|
+
const { isAuthenticated, isLoading, isExpiringSoon, expiresIn } = useSessionContext();
|
|
4689
|
+
if (isLoading) {
|
|
4690
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_material8.Box, { sx: { display: "flex", alignItems: "center", gap: 1 }, children: [
|
|
4691
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_material8.CircularProgress, { size: 16 }),
|
|
4692
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_material8.Typography, { variant: "caption", children: "Loading session..." })
|
|
4693
|
+
] });
|
|
4694
|
+
}
|
|
4695
|
+
if (!isAuthenticated) {
|
|
4696
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_material8.Typography, { variant: "caption", color: "text.secondary", children: "Not logged in" });
|
|
4697
|
+
}
|
|
4698
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_material8.Box, { children: [
|
|
4699
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_material8.Typography, { variant: "caption", color: "success.main", children: "\u2713 Authenticated" }),
|
|
4700
|
+
isExpiringSoon && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_material8.Typography, { variant: "caption", color: "warning.main", display: "block", children: [
|
|
4701
|
+
"\u26A0 Token expires in ",
|
|
4702
|
+
Math.round(expiresIn / 1e3 / 60),
|
|
4703
|
+
" min"
|
|
4704
|
+
] })
|
|
4705
|
+
] });
|
|
4706
|
+
}
|
|
3872
4707
|
// Annotate the CommonJS export names for ESM import in node:
|
|
3873
4708
|
0 && (module.exports = {
|
|
3874
4709
|
CrudifyDataProvider,
|
|
3875
4710
|
CrudifyLogin,
|
|
3876
4711
|
ERROR_CODES,
|
|
3877
4712
|
ERROR_SEVERITY_MAP,
|
|
4713
|
+
LoginComponent,
|
|
4714
|
+
ProtectedRoute,
|
|
4715
|
+
SessionDebugInfo,
|
|
4716
|
+
SessionManager,
|
|
4717
|
+
SessionProvider,
|
|
4718
|
+
SessionStatus,
|
|
4719
|
+
TokenStorage,
|
|
3878
4720
|
UserProfileDisplay,
|
|
3879
4721
|
configurationManager,
|
|
3880
4722
|
crudify,
|
|
@@ -3900,6 +4742,8 @@ init_secureStorage();
|
|
|
3900
4742
|
useCrudifyInstance,
|
|
3901
4743
|
useCrudifyLogin,
|
|
3902
4744
|
useCrudifyUser,
|
|
4745
|
+
useSession,
|
|
4746
|
+
useSessionContext,
|
|
3903
4747
|
useUserProfile,
|
|
3904
4748
|
...require("@nocios/crudify-browser")
|
|
3905
4749
|
});
|