@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.mjs
CHANGED
|
@@ -704,7 +704,7 @@ var useCrudifyAuth = () => {
|
|
|
704
704
|
// src/components/CrudifyLogin/Forms/LoginForm.tsx
|
|
705
705
|
import { Fragment, jsx as jsx4, jsxs } from "react/jsx-runtime";
|
|
706
706
|
var LoginForm = ({ onScreenChange, onExternalNavigate, onLoginSuccess, onError, redirectUrl = "/" }) => {
|
|
707
|
-
const { crudify:
|
|
707
|
+
const { crudify: crudify7 } = useCrudify();
|
|
708
708
|
const { state, updateFormData, setFieldError, clearErrors, setLoading } = useLoginState();
|
|
709
709
|
const { setToken } = useCrudifyAuth();
|
|
710
710
|
const { t } = useTranslation();
|
|
@@ -756,10 +756,10 @@ var LoginForm = ({ onScreenChange, onExternalNavigate, onLoginSuccess, onError,
|
|
|
756
756
|
clearErrors();
|
|
757
757
|
setLoading(true);
|
|
758
758
|
try {
|
|
759
|
-
if (!
|
|
759
|
+
if (!crudify7) {
|
|
760
760
|
throw new Error("Crudify not initialized");
|
|
761
761
|
}
|
|
762
|
-
const response = await
|
|
762
|
+
const response = await crudify7.login(state.formData.username, state.formData.password);
|
|
763
763
|
setLoading(false);
|
|
764
764
|
if (response.success) {
|
|
765
765
|
console.log("\u{1F510} LoginForm - Login successful, setting tokens");
|
|
@@ -922,7 +922,7 @@ import { useState as useState3 } from "react";
|
|
|
922
922
|
import { Typography as Typography2, TextField as TextField2, Button as Button2, Box as Box2, CircularProgress as CircularProgress2, Alert as Alert2, Link as Link2 } from "@mui/material";
|
|
923
923
|
import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
924
924
|
var ForgotPasswordForm = ({ onScreenChange, onError }) => {
|
|
925
|
-
const { crudify:
|
|
925
|
+
const { crudify: crudify7 } = useCrudify();
|
|
926
926
|
const [email, setEmail] = useState3("");
|
|
927
927
|
const [loading, setLoading] = useState3(false);
|
|
928
928
|
const [errors, setErrors] = useState3([]);
|
|
@@ -951,7 +951,7 @@ var ForgotPasswordForm = ({ onScreenChange, onError }) => {
|
|
|
951
951
|
return emailRegex.test(email2);
|
|
952
952
|
};
|
|
953
953
|
const handleSubmit = async () => {
|
|
954
|
-
if (loading || !
|
|
954
|
+
if (loading || !crudify7) return;
|
|
955
955
|
setErrors([]);
|
|
956
956
|
setHelperTextEmail(null);
|
|
957
957
|
if (!email) {
|
|
@@ -965,7 +965,7 @@ var ForgotPasswordForm = ({ onScreenChange, onError }) => {
|
|
|
965
965
|
setLoading(true);
|
|
966
966
|
try {
|
|
967
967
|
const data = [{ operation: "requestPasswordReset", data: { email } }];
|
|
968
|
-
const response = await
|
|
968
|
+
const response = await crudify7.transaction(data);
|
|
969
969
|
if (response.success) {
|
|
970
970
|
if (response.data && response.data.existingCodeValid) {
|
|
971
971
|
setCodeAlreadyExists(true);
|
|
@@ -1068,7 +1068,7 @@ import { useState as useState4, useEffect as useEffect5 } from "react";
|
|
|
1068
1068
|
import { Typography as Typography3, TextField as TextField3, Button as Button3, Box as Box3, CircularProgress as CircularProgress3, Alert as Alert3, Link as Link3 } from "@mui/material";
|
|
1069
1069
|
import { Fragment as Fragment3, jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1070
1070
|
var ResetPasswordForm = ({ onScreenChange, onError, searchParams, onResetSuccess }) => {
|
|
1071
|
-
const { crudify:
|
|
1071
|
+
const { crudify: crudify7 } = useCrudify();
|
|
1072
1072
|
const [newPassword, setNewPassword] = useState4("");
|
|
1073
1073
|
const [confirmPassword, setConfirmPassword] = useState4("");
|
|
1074
1074
|
const [loading, setLoading] = useState4(false);
|
|
@@ -1148,9 +1148,9 @@ var ResetPasswordForm = ({ onScreenChange, onError, searchParams, onResetSuccess
|
|
|
1148
1148
|
setErrors([t("resetPassword.invalidCode")]);
|
|
1149
1149
|
setValidatingCode(false);
|
|
1150
1150
|
setTimeout(() => onScreenChange?.("forgotPassword"), 3e3);
|
|
1151
|
-
}, [searchParams,
|
|
1151
|
+
}, [searchParams, crudify7, t, onScreenChange]);
|
|
1152
1152
|
useEffect5(() => {
|
|
1153
|
-
if (
|
|
1153
|
+
if (crudify7 && pendingValidation && !isValidating) {
|
|
1154
1154
|
setIsValidating(true);
|
|
1155
1155
|
const validateCode = async (emailToValidate, codeToValidate) => {
|
|
1156
1156
|
try {
|
|
@@ -1160,7 +1160,7 @@ var ResetPasswordForm = ({ onScreenChange, onError, searchParams, onResetSuccess
|
|
|
1160
1160
|
data: { email: emailToValidate, codePassword: codeToValidate }
|
|
1161
1161
|
}
|
|
1162
1162
|
];
|
|
1163
|
-
const response = await
|
|
1163
|
+
const response = await crudify7.transaction(data);
|
|
1164
1164
|
if (response.data && Array.isArray(response.data)) {
|
|
1165
1165
|
const validationResult = response.data[0];
|
|
1166
1166
|
if (validationResult && validationResult.response && validationResult.response.status === "OK") {
|
|
@@ -1189,7 +1189,7 @@ var ResetPasswordForm = ({ onScreenChange, onError, searchParams, onResetSuccess
|
|
|
1189
1189
|
};
|
|
1190
1190
|
validateCode(pendingValidation.email, pendingValidation.code);
|
|
1191
1191
|
}
|
|
1192
|
-
}, [
|
|
1192
|
+
}, [crudify7, pendingValidation, t, onScreenChange]);
|
|
1193
1193
|
const validatePassword = (password) => {
|
|
1194
1194
|
if (password.length < 8) {
|
|
1195
1195
|
return t("resetPassword.passwordTooShort");
|
|
@@ -1197,7 +1197,7 @@ var ResetPasswordForm = ({ onScreenChange, onError, searchParams, onResetSuccess
|
|
|
1197
1197
|
return null;
|
|
1198
1198
|
};
|
|
1199
1199
|
const handleSubmit = async () => {
|
|
1200
|
-
if (loading || !
|
|
1200
|
+
if (loading || !crudify7) return;
|
|
1201
1201
|
setErrors([]);
|
|
1202
1202
|
setHelperTextNewPassword(null);
|
|
1203
1203
|
setHelperTextConfirmPassword(null);
|
|
@@ -1228,7 +1228,7 @@ var ResetPasswordForm = ({ onScreenChange, onError, searchParams, onResetSuccess
|
|
|
1228
1228
|
data: { email, codePassword: code, newPassword }
|
|
1229
1229
|
}
|
|
1230
1230
|
];
|
|
1231
|
-
const response = await
|
|
1231
|
+
const response = await crudify7.transaction(data);
|
|
1232
1232
|
if (response.success) {
|
|
1233
1233
|
setErrors([]);
|
|
1234
1234
|
setTimeout(() => {
|
|
@@ -1339,7 +1339,7 @@ import { useState as useState5, useEffect as useEffect6 } from "react";
|
|
|
1339
1339
|
import { Typography as Typography4, TextField as TextField4, Button as Button4, Box as Box4, CircularProgress as CircularProgress4, Alert as Alert4, Link as Link4 } from "@mui/material";
|
|
1340
1340
|
import { Fragment as Fragment4, jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1341
1341
|
var CheckCodeForm = ({ onScreenChange, onError, searchParams }) => {
|
|
1342
|
-
const { crudify:
|
|
1342
|
+
const { crudify: crudify7 } = useCrudify();
|
|
1343
1343
|
const [code, setCode] = useState5("");
|
|
1344
1344
|
const [loading, setLoading] = useState5(false);
|
|
1345
1345
|
const [errors, setErrors] = useState5([]);
|
|
@@ -1378,7 +1378,7 @@ var CheckCodeForm = ({ onScreenChange, onError, searchParams }) => {
|
|
|
1378
1378
|
}
|
|
1379
1379
|
}, [searchParams, onScreenChange]);
|
|
1380
1380
|
const handleSubmit = async () => {
|
|
1381
|
-
if (loading || !
|
|
1381
|
+
if (loading || !crudify7) return;
|
|
1382
1382
|
setErrors([]);
|
|
1383
1383
|
setHelperTextCode(null);
|
|
1384
1384
|
if (!code) {
|
|
@@ -1397,7 +1397,7 @@ var CheckCodeForm = ({ onScreenChange, onError, searchParams }) => {
|
|
|
1397
1397
|
data: { email, codePassword: code }
|
|
1398
1398
|
}
|
|
1399
1399
|
];
|
|
1400
|
-
const response = await
|
|
1400
|
+
const response = await crudify7.transaction(data);
|
|
1401
1401
|
if (response.success) {
|
|
1402
1402
|
onScreenChange?.("resetPassword", { email, code, fromCodeVerification: "true" });
|
|
1403
1403
|
} else {
|
|
@@ -2411,12 +2411,18 @@ var useCrudifyInstance = () => {
|
|
|
2411
2411
|
}
|
|
2412
2412
|
}, [isReady, waitForReady]);
|
|
2413
2413
|
const ensureTokenSync = useCallback4(async () => {
|
|
2414
|
+
console.log("\u{1F504} useCrudifyInstance - ensureTokenSync: Starting token sync check");
|
|
2414
2415
|
const { tokenManager: tokenManager2 } = await import("./TokenManager-AYUNIXQU.mjs");
|
|
2415
2416
|
const tmToken = tokenManager2.getToken();
|
|
2416
2417
|
const crudifyToken = crudify5.getToken?.();
|
|
2418
|
+
console.log(" - TokenManager token in sync check:", tmToken ? `${tmToken.substring(0, 20)}...` : "null");
|
|
2419
|
+
console.log(" - crudify token in sync check:", crudifyToken ? `${crudifyToken.substring(0, 20)}...` : "null");
|
|
2417
2420
|
if (tmToken && tmToken !== crudifyToken) {
|
|
2418
2421
|
console.log("\u{1F504} useCrudifyInstance - Forcing token sync for authenticated operation");
|
|
2419
2422
|
crudify5.setToken(tmToken);
|
|
2423
|
+
console.log(" - crudify token after sync:", crudify5.getToken?.() ? `${crudify5.getToken?.()?.substring(0, 20)}...` : "null");
|
|
2424
|
+
} else {
|
|
2425
|
+
console.log("\u{1F504} useCrudifyInstance - No token sync needed, tokens match or no token");
|
|
2420
2426
|
}
|
|
2421
2427
|
}, []);
|
|
2422
2428
|
const getStructure = useCallback4(async (options) => {
|
|
@@ -2575,11 +2581,838 @@ var getCrudifyInstanceSync = async () => {
|
|
|
2575
2581
|
}
|
|
2576
2582
|
return crudify5;
|
|
2577
2583
|
};
|
|
2584
|
+
|
|
2585
|
+
// src/core/SessionManager.ts
|
|
2586
|
+
import crudify6 from "@nocios/crudify-browser";
|
|
2587
|
+
|
|
2588
|
+
// src/utils/tokenStorage.ts
|
|
2589
|
+
import CryptoJS from "crypto-js";
|
|
2590
|
+
var _TokenStorage = class _TokenStorage {
|
|
2591
|
+
/**
|
|
2592
|
+
* Configurar tipo de almacenamiento
|
|
2593
|
+
*/
|
|
2594
|
+
static setStorageType(type) {
|
|
2595
|
+
_TokenStorage.storageType = type;
|
|
2596
|
+
}
|
|
2597
|
+
/**
|
|
2598
|
+
* Verificar si el storage está disponible
|
|
2599
|
+
*/
|
|
2600
|
+
static isStorageAvailable(type) {
|
|
2601
|
+
try {
|
|
2602
|
+
const storage = window[type];
|
|
2603
|
+
const testKey = "__storage_test__";
|
|
2604
|
+
storage.setItem(testKey, "test");
|
|
2605
|
+
storage.removeItem(testKey);
|
|
2606
|
+
return true;
|
|
2607
|
+
} catch {
|
|
2608
|
+
return false;
|
|
2609
|
+
}
|
|
2610
|
+
}
|
|
2611
|
+
/**
|
|
2612
|
+
* Obtener instancia de storage
|
|
2613
|
+
*/
|
|
2614
|
+
static getStorage() {
|
|
2615
|
+
if (_TokenStorage.storageType === "none") return null;
|
|
2616
|
+
if (!_TokenStorage.isStorageAvailable(_TokenStorage.storageType)) {
|
|
2617
|
+
console.warn(`Crudify: ${_TokenStorage.storageType} not available, tokens won't persist`);
|
|
2618
|
+
return null;
|
|
2619
|
+
}
|
|
2620
|
+
return window[_TokenStorage.storageType];
|
|
2621
|
+
}
|
|
2622
|
+
/**
|
|
2623
|
+
* Encriptar datos sensibles
|
|
2624
|
+
*/
|
|
2625
|
+
static encrypt(data) {
|
|
2626
|
+
try {
|
|
2627
|
+
return CryptoJS.AES.encrypt(data, _TokenStorage.ENCRYPTION_KEY).toString();
|
|
2628
|
+
} catch (error) {
|
|
2629
|
+
console.error("Crudify: Encryption failed", error);
|
|
2630
|
+
return data;
|
|
2631
|
+
}
|
|
2632
|
+
}
|
|
2633
|
+
/**
|
|
2634
|
+
* Desencriptar datos
|
|
2635
|
+
*/
|
|
2636
|
+
static decrypt(encryptedData) {
|
|
2637
|
+
try {
|
|
2638
|
+
const bytes = CryptoJS.AES.decrypt(encryptedData, _TokenStorage.ENCRYPTION_KEY);
|
|
2639
|
+
const decrypted = bytes.toString(CryptoJS.enc.Utf8);
|
|
2640
|
+
return decrypted || encryptedData;
|
|
2641
|
+
} catch (error) {
|
|
2642
|
+
console.error("Crudify: Decryption failed", error);
|
|
2643
|
+
return encryptedData;
|
|
2644
|
+
}
|
|
2645
|
+
}
|
|
2646
|
+
/**
|
|
2647
|
+
* Guardar tokens de forma segura
|
|
2648
|
+
*/
|
|
2649
|
+
static saveTokens(tokens) {
|
|
2650
|
+
const storage = _TokenStorage.getStorage();
|
|
2651
|
+
if (!storage) return;
|
|
2652
|
+
try {
|
|
2653
|
+
const tokenData = {
|
|
2654
|
+
accessToken: tokens.accessToken,
|
|
2655
|
+
refreshToken: tokens.refreshToken,
|
|
2656
|
+
expiresAt: tokens.expiresAt,
|
|
2657
|
+
refreshExpiresAt: tokens.refreshExpiresAt,
|
|
2658
|
+
savedAt: Date.now()
|
|
2659
|
+
};
|
|
2660
|
+
const encrypted = _TokenStorage.encrypt(JSON.stringify(tokenData));
|
|
2661
|
+
storage.setItem(_TokenStorage.TOKEN_KEY, encrypted);
|
|
2662
|
+
console.debug("Crudify: Tokens saved successfully");
|
|
2663
|
+
} catch (error) {
|
|
2664
|
+
console.error("Crudify: Failed to save tokens", error);
|
|
2665
|
+
}
|
|
2666
|
+
}
|
|
2667
|
+
/**
|
|
2668
|
+
* Obtener tokens guardados
|
|
2669
|
+
*/
|
|
2670
|
+
static getTokens() {
|
|
2671
|
+
const storage = _TokenStorage.getStorage();
|
|
2672
|
+
if (!storage) return null;
|
|
2673
|
+
try {
|
|
2674
|
+
const encrypted = storage.getItem(_TokenStorage.TOKEN_KEY);
|
|
2675
|
+
if (!encrypted) return null;
|
|
2676
|
+
const decrypted = _TokenStorage.decrypt(encrypted);
|
|
2677
|
+
const tokenData = JSON.parse(decrypted);
|
|
2678
|
+
if (!tokenData.accessToken || !tokenData.refreshToken || !tokenData.expiresAt || !tokenData.refreshExpiresAt) {
|
|
2679
|
+
console.warn("Crudify: Incomplete token data found, clearing storage");
|
|
2680
|
+
_TokenStorage.clearTokens();
|
|
2681
|
+
return null;
|
|
2682
|
+
}
|
|
2683
|
+
if (Date.now() >= tokenData.refreshExpiresAt) {
|
|
2684
|
+
console.info("Crudify: Refresh token expired, clearing storage");
|
|
2685
|
+
_TokenStorage.clearTokens();
|
|
2686
|
+
return null;
|
|
2687
|
+
}
|
|
2688
|
+
return {
|
|
2689
|
+
accessToken: tokenData.accessToken,
|
|
2690
|
+
refreshToken: tokenData.refreshToken,
|
|
2691
|
+
expiresAt: tokenData.expiresAt,
|
|
2692
|
+
refreshExpiresAt: tokenData.refreshExpiresAt
|
|
2693
|
+
};
|
|
2694
|
+
} catch (error) {
|
|
2695
|
+
console.error("Crudify: Failed to retrieve tokens", error);
|
|
2696
|
+
_TokenStorage.clearTokens();
|
|
2697
|
+
return null;
|
|
2698
|
+
}
|
|
2699
|
+
}
|
|
2700
|
+
/**
|
|
2701
|
+
* Limpiar tokens almacenados
|
|
2702
|
+
*/
|
|
2703
|
+
static clearTokens() {
|
|
2704
|
+
const storage = _TokenStorage.getStorage();
|
|
2705
|
+
if (!storage) return;
|
|
2706
|
+
try {
|
|
2707
|
+
storage.removeItem(_TokenStorage.TOKEN_KEY);
|
|
2708
|
+
console.debug("Crudify: Tokens cleared from storage");
|
|
2709
|
+
} catch (error) {
|
|
2710
|
+
console.error("Crudify: Failed to clear tokens", error);
|
|
2711
|
+
}
|
|
2712
|
+
}
|
|
2713
|
+
/**
|
|
2714
|
+
* Verificar si hay tokens válidos guardados
|
|
2715
|
+
*/
|
|
2716
|
+
static hasValidTokens() {
|
|
2717
|
+
const tokens = _TokenStorage.getTokens();
|
|
2718
|
+
return tokens !== null;
|
|
2719
|
+
}
|
|
2720
|
+
/**
|
|
2721
|
+
* Obtener información de expiración
|
|
2722
|
+
*/
|
|
2723
|
+
static getExpirationInfo() {
|
|
2724
|
+
const tokens = _TokenStorage.getTokens();
|
|
2725
|
+
if (!tokens) return null;
|
|
2726
|
+
const now = Date.now();
|
|
2727
|
+
return {
|
|
2728
|
+
accessExpired: now >= tokens.expiresAt,
|
|
2729
|
+
refreshExpired: now >= tokens.refreshExpiresAt,
|
|
2730
|
+
accessExpiresIn: Math.max(0, tokens.expiresAt - now),
|
|
2731
|
+
refreshExpiresIn: Math.max(0, tokens.refreshExpiresAt - now)
|
|
2732
|
+
};
|
|
2733
|
+
}
|
|
2734
|
+
/**
|
|
2735
|
+
* Actualizar solo el access token (después de refresh)
|
|
2736
|
+
*/
|
|
2737
|
+
static updateAccessToken(newAccessToken, newExpiresAt) {
|
|
2738
|
+
const existingTokens = _TokenStorage.getTokens();
|
|
2739
|
+
if (!existingTokens) {
|
|
2740
|
+
console.warn("Crudify: Cannot update access token, no existing tokens found");
|
|
2741
|
+
return;
|
|
2742
|
+
}
|
|
2743
|
+
_TokenStorage.saveTokens({
|
|
2744
|
+
...existingTokens,
|
|
2745
|
+
accessToken: newAccessToken,
|
|
2746
|
+
expiresAt: newExpiresAt
|
|
2747
|
+
});
|
|
2748
|
+
}
|
|
2749
|
+
};
|
|
2750
|
+
_TokenStorage.TOKEN_KEY = "crudify_tokens";
|
|
2751
|
+
_TokenStorage.ENCRYPTION_KEY = "crudify_secure_key_v1";
|
|
2752
|
+
_TokenStorage.storageType = "localStorage";
|
|
2753
|
+
var TokenStorage = _TokenStorage;
|
|
2754
|
+
|
|
2755
|
+
// src/core/SessionManager.ts
|
|
2756
|
+
var SessionManager = class _SessionManager {
|
|
2757
|
+
constructor() {
|
|
2758
|
+
this.config = {};
|
|
2759
|
+
this.initialized = false;
|
|
2760
|
+
}
|
|
2761
|
+
static getInstance() {
|
|
2762
|
+
if (!_SessionManager.instance) {
|
|
2763
|
+
_SessionManager.instance = new _SessionManager();
|
|
2764
|
+
}
|
|
2765
|
+
return _SessionManager.instance;
|
|
2766
|
+
}
|
|
2767
|
+
/**
|
|
2768
|
+
* Inicializar el SessionManager
|
|
2769
|
+
*/
|
|
2770
|
+
async initialize(config = {}) {
|
|
2771
|
+
if (this.initialized) {
|
|
2772
|
+
console.warn("SessionManager: Already initialized");
|
|
2773
|
+
return;
|
|
2774
|
+
}
|
|
2775
|
+
this.config = {
|
|
2776
|
+
storageType: "localStorage",
|
|
2777
|
+
autoRestore: true,
|
|
2778
|
+
enableLogging: false,
|
|
2779
|
+
...config
|
|
2780
|
+
};
|
|
2781
|
+
TokenStorage.setStorageType(this.config.storageType || "localStorage");
|
|
2782
|
+
if (this.config.enableLogging) {
|
|
2783
|
+
}
|
|
2784
|
+
if (this.config.autoRestore) {
|
|
2785
|
+
await this.restoreSession();
|
|
2786
|
+
}
|
|
2787
|
+
this.initialized = true;
|
|
2788
|
+
this.log("SessionManager initialized successfully");
|
|
2789
|
+
}
|
|
2790
|
+
/**
|
|
2791
|
+
* Login con persistencia automática
|
|
2792
|
+
*/
|
|
2793
|
+
async login(email, password) {
|
|
2794
|
+
try {
|
|
2795
|
+
this.log("Attempting login...");
|
|
2796
|
+
const response = await crudify6.login(email, password);
|
|
2797
|
+
if (!response.success) {
|
|
2798
|
+
this.log("Login failed:", response.errors);
|
|
2799
|
+
return {
|
|
2800
|
+
success: false,
|
|
2801
|
+
error: this.formatError(response.errors)
|
|
2802
|
+
};
|
|
2803
|
+
}
|
|
2804
|
+
const tokens = {
|
|
2805
|
+
accessToken: response.data.token,
|
|
2806
|
+
refreshToken: response.data.refreshToken,
|
|
2807
|
+
expiresAt: response.data.expiresAt,
|
|
2808
|
+
refreshExpiresAt: response.data.refreshExpiresAt
|
|
2809
|
+
};
|
|
2810
|
+
TokenStorage.saveTokens(tokens);
|
|
2811
|
+
this.log("Login successful, tokens saved");
|
|
2812
|
+
this.config.onLoginSuccess?.(tokens);
|
|
2813
|
+
return {
|
|
2814
|
+
success: true,
|
|
2815
|
+
tokens
|
|
2816
|
+
};
|
|
2817
|
+
} catch (error) {
|
|
2818
|
+
this.log("Login error:", error);
|
|
2819
|
+
return {
|
|
2820
|
+
success: false,
|
|
2821
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
2822
|
+
};
|
|
2823
|
+
}
|
|
2824
|
+
}
|
|
2825
|
+
/**
|
|
2826
|
+
* Logout con limpieza de tokens
|
|
2827
|
+
*/
|
|
2828
|
+
async logout() {
|
|
2829
|
+
try {
|
|
2830
|
+
this.log("Logging out...");
|
|
2831
|
+
await crudify6.logout();
|
|
2832
|
+
TokenStorage.clearTokens();
|
|
2833
|
+
this.log("Logout successful");
|
|
2834
|
+
this.config.onLogout?.();
|
|
2835
|
+
} catch (error) {
|
|
2836
|
+
this.log("Logout error:", error);
|
|
2837
|
+
TokenStorage.clearTokens();
|
|
2838
|
+
}
|
|
2839
|
+
}
|
|
2840
|
+
/**
|
|
2841
|
+
* Restaurar sesión desde storage
|
|
2842
|
+
*/
|
|
2843
|
+
async restoreSession() {
|
|
2844
|
+
try {
|
|
2845
|
+
this.log("Attempting to restore session...");
|
|
2846
|
+
const savedTokens = TokenStorage.getTokens();
|
|
2847
|
+
if (!savedTokens) {
|
|
2848
|
+
this.log("No valid tokens found in storage");
|
|
2849
|
+
return false;
|
|
2850
|
+
}
|
|
2851
|
+
crudify6.setTokens({
|
|
2852
|
+
accessToken: savedTokens.accessToken,
|
|
2853
|
+
refreshToken: savedTokens.refreshToken,
|
|
2854
|
+
expiresAt: savedTokens.expiresAt,
|
|
2855
|
+
refreshExpiresAt: savedTokens.refreshExpiresAt
|
|
2856
|
+
});
|
|
2857
|
+
this.log("Session restored successfully");
|
|
2858
|
+
this.config.onSessionRestored?.(savedTokens);
|
|
2859
|
+
return true;
|
|
2860
|
+
} catch (error) {
|
|
2861
|
+
this.log("Session restore error:", error);
|
|
2862
|
+
TokenStorage.clearTokens();
|
|
2863
|
+
return false;
|
|
2864
|
+
}
|
|
2865
|
+
}
|
|
2866
|
+
/**
|
|
2867
|
+
* Verificar si el usuario está autenticado
|
|
2868
|
+
*/
|
|
2869
|
+
isAuthenticated() {
|
|
2870
|
+
return crudify6.isLogin() || TokenStorage.hasValidTokens();
|
|
2871
|
+
}
|
|
2872
|
+
/**
|
|
2873
|
+
* Obtener información de tokens actuales
|
|
2874
|
+
*/
|
|
2875
|
+
getTokenInfo() {
|
|
2876
|
+
const crudifyTokens = crudify6.getTokenData();
|
|
2877
|
+
const storageInfo = TokenStorage.getExpirationInfo();
|
|
2878
|
+
return {
|
|
2879
|
+
isLoggedIn: this.isAuthenticated(),
|
|
2880
|
+
crudifyTokens,
|
|
2881
|
+
storageInfo,
|
|
2882
|
+
hasValidTokens: TokenStorage.hasValidTokens()
|
|
2883
|
+
};
|
|
2884
|
+
}
|
|
2885
|
+
/**
|
|
2886
|
+
* Refrescar tokens manualmente
|
|
2887
|
+
*/
|
|
2888
|
+
async refreshTokens() {
|
|
2889
|
+
try {
|
|
2890
|
+
this.log("Manually refreshing tokens...");
|
|
2891
|
+
const response = await crudify6.refreshAccessToken();
|
|
2892
|
+
if (!response.success) {
|
|
2893
|
+
this.log("Token refresh failed:", response.errors);
|
|
2894
|
+
TokenStorage.clearTokens();
|
|
2895
|
+
this.config.onSessionExpired?.();
|
|
2896
|
+
return false;
|
|
2897
|
+
}
|
|
2898
|
+
const newTokens = {
|
|
2899
|
+
accessToken: response.data.token,
|
|
2900
|
+
refreshToken: response.data.refreshToken,
|
|
2901
|
+
expiresAt: response.data.expiresAt,
|
|
2902
|
+
refreshExpiresAt: response.data.refreshExpiresAt
|
|
2903
|
+
};
|
|
2904
|
+
TokenStorage.saveTokens(newTokens);
|
|
2905
|
+
this.log("Tokens refreshed and saved successfully");
|
|
2906
|
+
return true;
|
|
2907
|
+
} catch (error) {
|
|
2908
|
+
this.log("Token refresh error:", error);
|
|
2909
|
+
TokenStorage.clearTokens();
|
|
2910
|
+
this.config.onSessionExpired?.();
|
|
2911
|
+
return false;
|
|
2912
|
+
}
|
|
2913
|
+
}
|
|
2914
|
+
/**
|
|
2915
|
+
* Configurar interceptor de respuesta para manejo automático de errores
|
|
2916
|
+
*/
|
|
2917
|
+
setupResponseInterceptor() {
|
|
2918
|
+
crudify6.setResponseInterceptor(async (response) => {
|
|
2919
|
+
if (response.errors) {
|
|
2920
|
+
const hasAuthError = response.errors.some(
|
|
2921
|
+
(error) => error.message?.includes("Unauthorized") || error.message?.includes("Token") || error.extensions?.code === "UNAUTHENTICATED"
|
|
2922
|
+
);
|
|
2923
|
+
if (hasAuthError && TokenStorage.hasValidTokens()) {
|
|
2924
|
+
this.log("Auth error detected, attempting token refresh...");
|
|
2925
|
+
const refreshSuccess = await this.refreshTokens();
|
|
2926
|
+
if (!refreshSuccess) {
|
|
2927
|
+
this.log("Session expired, triggering callback");
|
|
2928
|
+
this.config.onSessionExpired?.();
|
|
2929
|
+
}
|
|
2930
|
+
}
|
|
2931
|
+
}
|
|
2932
|
+
return response;
|
|
2933
|
+
});
|
|
2934
|
+
this.log("Response interceptor configured");
|
|
2935
|
+
}
|
|
2936
|
+
/**
|
|
2937
|
+
* Limpiar sesión completamente
|
|
2938
|
+
*/
|
|
2939
|
+
clearSession() {
|
|
2940
|
+
TokenStorage.clearTokens();
|
|
2941
|
+
crudify6.logout();
|
|
2942
|
+
this.log("Session cleared completely");
|
|
2943
|
+
}
|
|
2944
|
+
// Métodos privados
|
|
2945
|
+
log(message, ...args) {
|
|
2946
|
+
if (this.config.enableLogging) {
|
|
2947
|
+
console.log(`[SessionManager] ${message}`, ...args);
|
|
2948
|
+
}
|
|
2949
|
+
}
|
|
2950
|
+
formatError(errors) {
|
|
2951
|
+
if (!errors) return "Unknown error";
|
|
2952
|
+
if (typeof errors === "string") return errors;
|
|
2953
|
+
if (typeof errors === "object") {
|
|
2954
|
+
const errorMessages = Object.values(errors).flat();
|
|
2955
|
+
return errorMessages.join(", ");
|
|
2956
|
+
}
|
|
2957
|
+
return "Authentication failed";
|
|
2958
|
+
}
|
|
2959
|
+
};
|
|
2960
|
+
|
|
2961
|
+
// src/hooks/useSession.ts
|
|
2962
|
+
import { useState as useState9, useEffect as useEffect9, useCallback as useCallback5 } from "react";
|
|
2963
|
+
function useSession(options = {}) {
|
|
2964
|
+
const [state, setState] = useState9({
|
|
2965
|
+
isAuthenticated: false,
|
|
2966
|
+
isLoading: true,
|
|
2967
|
+
isInitialized: false,
|
|
2968
|
+
tokens: null,
|
|
2969
|
+
error: null
|
|
2970
|
+
});
|
|
2971
|
+
const sessionManager = SessionManager.getInstance();
|
|
2972
|
+
const initialize = useCallback5(async () => {
|
|
2973
|
+
try {
|
|
2974
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
2975
|
+
const config = {
|
|
2976
|
+
autoRestore: options.autoRestore ?? true,
|
|
2977
|
+
enableLogging: options.enableLogging ?? false,
|
|
2978
|
+
onSessionExpired: () => {
|
|
2979
|
+
setState((prev) => ({
|
|
2980
|
+
...prev,
|
|
2981
|
+
isAuthenticated: false,
|
|
2982
|
+
tokens: null,
|
|
2983
|
+
error: "Session expired"
|
|
2984
|
+
}));
|
|
2985
|
+
options.onSessionExpired?.();
|
|
2986
|
+
},
|
|
2987
|
+
onSessionRestored: (tokens) => {
|
|
2988
|
+
setState((prev) => ({
|
|
2989
|
+
...prev,
|
|
2990
|
+
isAuthenticated: true,
|
|
2991
|
+
tokens,
|
|
2992
|
+
error: null
|
|
2993
|
+
}));
|
|
2994
|
+
options.onSessionRestored?.(tokens);
|
|
2995
|
+
},
|
|
2996
|
+
onLoginSuccess: (tokens) => {
|
|
2997
|
+
setState((prev) => ({
|
|
2998
|
+
...prev,
|
|
2999
|
+
isAuthenticated: true,
|
|
3000
|
+
tokens,
|
|
3001
|
+
error: null
|
|
3002
|
+
}));
|
|
3003
|
+
},
|
|
3004
|
+
onLogout: () => {
|
|
3005
|
+
setState((prev) => ({
|
|
3006
|
+
...prev,
|
|
3007
|
+
isAuthenticated: false,
|
|
3008
|
+
tokens: null,
|
|
3009
|
+
error: null
|
|
3010
|
+
}));
|
|
3011
|
+
}
|
|
3012
|
+
};
|
|
3013
|
+
await sessionManager.initialize(config);
|
|
3014
|
+
sessionManager.setupResponseInterceptor();
|
|
3015
|
+
const isAuth = sessionManager.isAuthenticated();
|
|
3016
|
+
const tokenInfo = sessionManager.getTokenInfo();
|
|
3017
|
+
setState((prev) => ({
|
|
3018
|
+
...prev,
|
|
3019
|
+
isAuthenticated: isAuth,
|
|
3020
|
+
isInitialized: true,
|
|
3021
|
+
isLoading: false,
|
|
3022
|
+
tokens: tokenInfo.crudifyTokens.accessToken ? {
|
|
3023
|
+
accessToken: tokenInfo.crudifyTokens.accessToken,
|
|
3024
|
+
refreshToken: tokenInfo.crudifyTokens.refreshToken,
|
|
3025
|
+
expiresAt: tokenInfo.crudifyTokens.expiresAt,
|
|
3026
|
+
refreshExpiresAt: tokenInfo.crudifyTokens.refreshExpiresAt
|
|
3027
|
+
} : null
|
|
3028
|
+
}));
|
|
3029
|
+
} catch (error) {
|
|
3030
|
+
setState((prev) => ({
|
|
3031
|
+
...prev,
|
|
3032
|
+
isLoading: false,
|
|
3033
|
+
isInitialized: true,
|
|
3034
|
+
error: error instanceof Error ? error.message : "Initialization failed"
|
|
3035
|
+
}));
|
|
3036
|
+
}
|
|
3037
|
+
}, [options.autoRestore, options.enableLogging, options.onSessionExpired, options.onSessionRestored]);
|
|
3038
|
+
const login = useCallback5(async (email, password) => {
|
|
3039
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
3040
|
+
try {
|
|
3041
|
+
const result = await sessionManager.login(email, password);
|
|
3042
|
+
if (result.success && result.tokens) {
|
|
3043
|
+
setState((prev) => ({
|
|
3044
|
+
...prev,
|
|
3045
|
+
isAuthenticated: true,
|
|
3046
|
+
tokens: result.tokens,
|
|
3047
|
+
isLoading: false,
|
|
3048
|
+
error: null
|
|
3049
|
+
}));
|
|
3050
|
+
} else {
|
|
3051
|
+
setState((prev) => ({
|
|
3052
|
+
...prev,
|
|
3053
|
+
isAuthenticated: false,
|
|
3054
|
+
tokens: null,
|
|
3055
|
+
isLoading: false,
|
|
3056
|
+
error: result.error || "Login failed"
|
|
3057
|
+
}));
|
|
3058
|
+
}
|
|
3059
|
+
return result;
|
|
3060
|
+
} catch (error) {
|
|
3061
|
+
const errorMsg = error instanceof Error ? error.message : "Login failed";
|
|
3062
|
+
setState((prev) => ({
|
|
3063
|
+
...prev,
|
|
3064
|
+
isAuthenticated: false,
|
|
3065
|
+
tokens: null,
|
|
3066
|
+
isLoading: false,
|
|
3067
|
+
error: errorMsg
|
|
3068
|
+
}));
|
|
3069
|
+
return {
|
|
3070
|
+
success: false,
|
|
3071
|
+
error: errorMsg
|
|
3072
|
+
};
|
|
3073
|
+
}
|
|
3074
|
+
}, [sessionManager]);
|
|
3075
|
+
const logout = useCallback5(async () => {
|
|
3076
|
+
setState((prev) => ({ ...prev, isLoading: true }));
|
|
3077
|
+
try {
|
|
3078
|
+
await sessionManager.logout();
|
|
3079
|
+
setState((prev) => ({
|
|
3080
|
+
...prev,
|
|
3081
|
+
isAuthenticated: false,
|
|
3082
|
+
tokens: null,
|
|
3083
|
+
isLoading: false,
|
|
3084
|
+
error: null
|
|
3085
|
+
}));
|
|
3086
|
+
} catch (error) {
|
|
3087
|
+
setState((prev) => ({
|
|
3088
|
+
...prev,
|
|
3089
|
+
isAuthenticated: false,
|
|
3090
|
+
tokens: null,
|
|
3091
|
+
isLoading: false,
|
|
3092
|
+
error: error instanceof Error ? error.message : "Logout error"
|
|
3093
|
+
}));
|
|
3094
|
+
}
|
|
3095
|
+
}, [sessionManager]);
|
|
3096
|
+
const refreshTokens = useCallback5(async () => {
|
|
3097
|
+
try {
|
|
3098
|
+
const success = await sessionManager.refreshTokens();
|
|
3099
|
+
if (success) {
|
|
3100
|
+
const tokenInfo = sessionManager.getTokenInfo();
|
|
3101
|
+
setState((prev) => ({
|
|
3102
|
+
...prev,
|
|
3103
|
+
tokens: tokenInfo.crudifyTokens.accessToken ? {
|
|
3104
|
+
accessToken: tokenInfo.crudifyTokens.accessToken,
|
|
3105
|
+
refreshToken: tokenInfo.crudifyTokens.refreshToken,
|
|
3106
|
+
expiresAt: tokenInfo.crudifyTokens.expiresAt,
|
|
3107
|
+
refreshExpiresAt: tokenInfo.crudifyTokens.refreshExpiresAt
|
|
3108
|
+
} : null,
|
|
3109
|
+
error: null
|
|
3110
|
+
}));
|
|
3111
|
+
} else {
|
|
3112
|
+
setState((prev) => ({
|
|
3113
|
+
...prev,
|
|
3114
|
+
isAuthenticated: false,
|
|
3115
|
+
tokens: null,
|
|
3116
|
+
error: "Token refresh failed"
|
|
3117
|
+
}));
|
|
3118
|
+
}
|
|
3119
|
+
return success;
|
|
3120
|
+
} catch (error) {
|
|
3121
|
+
setState((prev) => ({
|
|
3122
|
+
...prev,
|
|
3123
|
+
isAuthenticated: false,
|
|
3124
|
+
tokens: null,
|
|
3125
|
+
error: error instanceof Error ? error.message : "Token refresh failed"
|
|
3126
|
+
}));
|
|
3127
|
+
return false;
|
|
3128
|
+
}
|
|
3129
|
+
}, [sessionManager]);
|
|
3130
|
+
const clearError = useCallback5(() => {
|
|
3131
|
+
setState((prev) => ({ ...prev, error: null }));
|
|
3132
|
+
}, []);
|
|
3133
|
+
const getTokenInfo = useCallback5(() => {
|
|
3134
|
+
return sessionManager.getTokenInfo();
|
|
3135
|
+
}, [sessionManager]);
|
|
3136
|
+
useEffect9(() => {
|
|
3137
|
+
initialize();
|
|
3138
|
+
}, [initialize]);
|
|
3139
|
+
return {
|
|
3140
|
+
// Estado
|
|
3141
|
+
...state,
|
|
3142
|
+
// Acciones
|
|
3143
|
+
login,
|
|
3144
|
+
logout,
|
|
3145
|
+
refreshTokens,
|
|
3146
|
+
clearError,
|
|
3147
|
+
getTokenInfo,
|
|
3148
|
+
// Utilidades
|
|
3149
|
+
isExpiringSoon: state.tokens ? state.tokens.expiresAt - Date.now() < 5 * 60 * 1e3 : false,
|
|
3150
|
+
// 5 minutos
|
|
3151
|
+
expiresIn: state.tokens ? Math.max(0, state.tokens.expiresAt - Date.now()) : 0,
|
|
3152
|
+
refreshExpiresIn: state.tokens ? Math.max(0, state.tokens.refreshExpiresAt - Date.now()) : 0
|
|
3153
|
+
};
|
|
3154
|
+
}
|
|
3155
|
+
|
|
3156
|
+
// src/providers/SessionProvider.tsx
|
|
3157
|
+
import { createContext as createContext4, useContext as useContext4 } from "react";
|
|
3158
|
+
import { Fragment as Fragment7, jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
3159
|
+
var SessionContext = createContext4(void 0);
|
|
3160
|
+
function SessionProvider({ children, options = {} }) {
|
|
3161
|
+
const sessionData = useSession(options);
|
|
3162
|
+
return /* @__PURE__ */ jsx11(SessionContext.Provider, { value: sessionData, children });
|
|
3163
|
+
}
|
|
3164
|
+
function useSessionContext() {
|
|
3165
|
+
const context = useContext4(SessionContext);
|
|
3166
|
+
if (context === void 0) {
|
|
3167
|
+
throw new Error("useSessionContext must be used within a SessionProvider");
|
|
3168
|
+
}
|
|
3169
|
+
return context;
|
|
3170
|
+
}
|
|
3171
|
+
function ProtectedRoute({
|
|
3172
|
+
children,
|
|
3173
|
+
fallback = /* @__PURE__ */ jsx11("div", { children: "Please log in to access this content" }),
|
|
3174
|
+
redirectTo
|
|
3175
|
+
}) {
|
|
3176
|
+
const { isAuthenticated, isLoading, isInitialized } = useSessionContext();
|
|
3177
|
+
if (!isInitialized || isLoading) {
|
|
3178
|
+
return /* @__PURE__ */ jsx11("div", { children: "Loading..." });
|
|
3179
|
+
}
|
|
3180
|
+
if (!isAuthenticated) {
|
|
3181
|
+
if (redirectTo) {
|
|
3182
|
+
redirectTo();
|
|
3183
|
+
return null;
|
|
3184
|
+
}
|
|
3185
|
+
return /* @__PURE__ */ jsx11(Fragment7, { children: fallback });
|
|
3186
|
+
}
|
|
3187
|
+
return /* @__PURE__ */ jsx11(Fragment7, { children });
|
|
3188
|
+
}
|
|
3189
|
+
function SessionDebugInfo() {
|
|
3190
|
+
const session = useSessionContext();
|
|
3191
|
+
if (!session.isInitialized) {
|
|
3192
|
+
return /* @__PURE__ */ jsx11("div", { children: "Session not initialized" });
|
|
3193
|
+
}
|
|
3194
|
+
return /* @__PURE__ */ jsxs8("div", { style: {
|
|
3195
|
+
padding: "10px",
|
|
3196
|
+
margin: "10px",
|
|
3197
|
+
border: "1px solid #ccc",
|
|
3198
|
+
borderRadius: "4px",
|
|
3199
|
+
fontSize: "12px",
|
|
3200
|
+
fontFamily: "monospace"
|
|
3201
|
+
}, children: [
|
|
3202
|
+
/* @__PURE__ */ jsx11("h4", { children: "Session Debug Info" }),
|
|
3203
|
+
/* @__PURE__ */ jsxs8("div", { children: [
|
|
3204
|
+
/* @__PURE__ */ jsx11("strong", { children: "Authenticated:" }),
|
|
3205
|
+
" ",
|
|
3206
|
+
session.isAuthenticated ? "Yes" : "No"
|
|
3207
|
+
] }),
|
|
3208
|
+
/* @__PURE__ */ jsxs8("div", { children: [
|
|
3209
|
+
/* @__PURE__ */ jsx11("strong", { children: "Loading:" }),
|
|
3210
|
+
" ",
|
|
3211
|
+
session.isLoading ? "Yes" : "No"
|
|
3212
|
+
] }),
|
|
3213
|
+
/* @__PURE__ */ jsxs8("div", { children: [
|
|
3214
|
+
/* @__PURE__ */ jsx11("strong", { children: "Error:" }),
|
|
3215
|
+
" ",
|
|
3216
|
+
session.error || "None"
|
|
3217
|
+
] }),
|
|
3218
|
+
session.tokens && /* @__PURE__ */ jsxs8(Fragment7, { children: [
|
|
3219
|
+
/* @__PURE__ */ jsxs8("div", { children: [
|
|
3220
|
+
/* @__PURE__ */ jsx11("strong", { children: "Access Token:" }),
|
|
3221
|
+
" ",
|
|
3222
|
+
session.tokens.accessToken.substring(0, 20),
|
|
3223
|
+
"..."
|
|
3224
|
+
] }),
|
|
3225
|
+
/* @__PURE__ */ jsxs8("div", { children: [
|
|
3226
|
+
/* @__PURE__ */ jsx11("strong", { children: "Refresh Token:" }),
|
|
3227
|
+
" ",
|
|
3228
|
+
session.tokens.refreshToken.substring(0, 20),
|
|
3229
|
+
"..."
|
|
3230
|
+
] }),
|
|
3231
|
+
/* @__PURE__ */ jsxs8("div", { children: [
|
|
3232
|
+
/* @__PURE__ */ jsx11("strong", { children: "Access Expires In:" }),
|
|
3233
|
+
" ",
|
|
3234
|
+
Math.round(session.expiresIn / 1e3 / 60),
|
|
3235
|
+
" minutes"
|
|
3236
|
+
] }),
|
|
3237
|
+
/* @__PURE__ */ jsxs8("div", { children: [
|
|
3238
|
+
/* @__PURE__ */ jsx11("strong", { children: "Refresh Expires In:" }),
|
|
3239
|
+
" ",
|
|
3240
|
+
Math.round(session.refreshExpiresIn / 1e3 / 60 / 60),
|
|
3241
|
+
" hours"
|
|
3242
|
+
] }),
|
|
3243
|
+
/* @__PURE__ */ jsxs8("div", { children: [
|
|
3244
|
+
/* @__PURE__ */ jsx11("strong", { children: "Expiring Soon:" }),
|
|
3245
|
+
" ",
|
|
3246
|
+
session.isExpiringSoon ? "Yes" : "No"
|
|
3247
|
+
] })
|
|
3248
|
+
] })
|
|
3249
|
+
] });
|
|
3250
|
+
}
|
|
3251
|
+
|
|
3252
|
+
// src/components/LoginComponent.tsx
|
|
3253
|
+
import { useState as useState10 } from "react";
|
|
3254
|
+
import { Button as Button5, TextField as TextField5, Box as Box8, Alert as Alert7, Typography as Typography8, CircularProgress as CircularProgress7 } from "@mui/material";
|
|
3255
|
+
import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
3256
|
+
function LoginComponent() {
|
|
3257
|
+
const [email, setEmail] = useState10("");
|
|
3258
|
+
const [password, setPassword] = useState10("");
|
|
3259
|
+
const [showForm, setShowForm] = useState10(false);
|
|
3260
|
+
const {
|
|
3261
|
+
isAuthenticated,
|
|
3262
|
+
isLoading,
|
|
3263
|
+
error,
|
|
3264
|
+
login,
|
|
3265
|
+
logout,
|
|
3266
|
+
refreshTokens,
|
|
3267
|
+
clearError,
|
|
3268
|
+
isExpiringSoon,
|
|
3269
|
+
expiresIn
|
|
3270
|
+
} = useSessionContext();
|
|
3271
|
+
const handleLogin = async (e) => {
|
|
3272
|
+
e.preventDefault();
|
|
3273
|
+
if (!email || !password) {
|
|
3274
|
+
return;
|
|
3275
|
+
}
|
|
3276
|
+
const result = await login(email, password);
|
|
3277
|
+
if (result.success) {
|
|
3278
|
+
setEmail("");
|
|
3279
|
+
setPassword("");
|
|
3280
|
+
setShowForm(false);
|
|
3281
|
+
}
|
|
3282
|
+
};
|
|
3283
|
+
const handleLogout = async () => {
|
|
3284
|
+
await logout();
|
|
3285
|
+
};
|
|
3286
|
+
const handleRefreshTokens = async () => {
|
|
3287
|
+
await refreshTokens();
|
|
3288
|
+
};
|
|
3289
|
+
if (isAuthenticated) {
|
|
3290
|
+
return /* @__PURE__ */ jsxs9(Box8, { sx: { maxWidth: 600, mx: "auto", p: 3 }, children: [
|
|
3291
|
+
/* @__PURE__ */ jsx12(Typography8, { variant: "h4", gutterBottom: true, children: "Welcome! \u{1F389}" }),
|
|
3292
|
+
/* @__PURE__ */ jsx12(Alert7, { severity: "success", sx: { mb: 3 }, children: "You are successfully logged in with Refresh Token Pattern enabled" }),
|
|
3293
|
+
/* @__PURE__ */ jsxs9(Box8, { sx: { mb: 3, p: 2, bgcolor: "background.paper", border: 1, borderColor: "divider", borderRadius: 1 }, children: [
|
|
3294
|
+
/* @__PURE__ */ jsx12(Typography8, { variant: "h6", gutterBottom: true, children: "Token Status" }),
|
|
3295
|
+
/* @__PURE__ */ jsxs9(Typography8, { variant: "body2", color: "text.secondary", children: [
|
|
3296
|
+
"Access Token expires in: ",
|
|
3297
|
+
Math.round(expiresIn / 1e3 / 60),
|
|
3298
|
+
" minutes"
|
|
3299
|
+
] }),
|
|
3300
|
+
isExpiringSoon && /* @__PURE__ */ jsx12(Alert7, { severity: "warning", sx: { mt: 1 }, children: "Token expires soon - automatic refresh will happen" })
|
|
3301
|
+
] }),
|
|
3302
|
+
/* @__PURE__ */ jsxs9(Box8, { sx: { display: "flex", gap: 2, flexWrap: "wrap" }, children: [
|
|
3303
|
+
/* @__PURE__ */ jsx12(
|
|
3304
|
+
Button5,
|
|
3305
|
+
{
|
|
3306
|
+
variant: "contained",
|
|
3307
|
+
onClick: handleRefreshTokens,
|
|
3308
|
+
disabled: isLoading,
|
|
3309
|
+
startIcon: isLoading ? /* @__PURE__ */ jsx12(CircularProgress7, { size: 16 }) : null,
|
|
3310
|
+
children: "Refresh Tokens"
|
|
3311
|
+
}
|
|
3312
|
+
),
|
|
3313
|
+
/* @__PURE__ */ jsx12(
|
|
3314
|
+
Button5,
|
|
3315
|
+
{
|
|
3316
|
+
variant: "outlined",
|
|
3317
|
+
color: "error",
|
|
3318
|
+
onClick: handleLogout,
|
|
3319
|
+
disabled: isLoading,
|
|
3320
|
+
children: "Logout"
|
|
3321
|
+
}
|
|
3322
|
+
)
|
|
3323
|
+
] }),
|
|
3324
|
+
error && /* @__PURE__ */ jsx12(Alert7, { severity: "error", sx: { mt: 2 }, onClose: clearError, children: error })
|
|
3325
|
+
] });
|
|
3326
|
+
}
|
|
3327
|
+
return /* @__PURE__ */ jsxs9(Box8, { sx: { maxWidth: 400, mx: "auto", p: 3 }, children: [
|
|
3328
|
+
/* @__PURE__ */ jsx12(Typography8, { variant: "h4", gutterBottom: true, align: "center", children: "Login with Refresh Tokens" }),
|
|
3329
|
+
/* @__PURE__ */ jsx12(Alert7, { severity: "info", sx: { mb: 3 }, children: "This demo shows the new Refresh Token Pattern with automatic session management" }),
|
|
3330
|
+
!showForm ? /* @__PURE__ */ jsx12(
|
|
3331
|
+
Button5,
|
|
3332
|
+
{
|
|
3333
|
+
fullWidth: true,
|
|
3334
|
+
variant: "contained",
|
|
3335
|
+
size: "large",
|
|
3336
|
+
onClick: () => setShowForm(true),
|
|
3337
|
+
sx: { mt: 2 },
|
|
3338
|
+
children: "Show Login Form"
|
|
3339
|
+
}
|
|
3340
|
+
) : /* @__PURE__ */ jsxs9("form", { onSubmit: handleLogin, children: [
|
|
3341
|
+
/* @__PURE__ */ jsx12(
|
|
3342
|
+
TextField5,
|
|
3343
|
+
{
|
|
3344
|
+
fullWidth: true,
|
|
3345
|
+
label: "Email",
|
|
3346
|
+
type: "email",
|
|
3347
|
+
value: email,
|
|
3348
|
+
onChange: (e) => setEmail(e.target.value),
|
|
3349
|
+
margin: "normal",
|
|
3350
|
+
required: true,
|
|
3351
|
+
autoComplete: "email"
|
|
3352
|
+
}
|
|
3353
|
+
),
|
|
3354
|
+
/* @__PURE__ */ jsx12(
|
|
3355
|
+
TextField5,
|
|
3356
|
+
{
|
|
3357
|
+
fullWidth: true,
|
|
3358
|
+
label: "Password",
|
|
3359
|
+
type: "password",
|
|
3360
|
+
value: password,
|
|
3361
|
+
onChange: (e) => setPassword(e.target.value),
|
|
3362
|
+
margin: "normal",
|
|
3363
|
+
required: true,
|
|
3364
|
+
autoComplete: "current-password"
|
|
3365
|
+
}
|
|
3366
|
+
),
|
|
3367
|
+
/* @__PURE__ */ jsx12(
|
|
3368
|
+
Button5,
|
|
3369
|
+
{
|
|
3370
|
+
type: "submit",
|
|
3371
|
+
fullWidth: true,
|
|
3372
|
+
variant: "contained",
|
|
3373
|
+
size: "large",
|
|
3374
|
+
disabled: isLoading,
|
|
3375
|
+
startIcon: isLoading ? /* @__PURE__ */ jsx12(CircularProgress7, { size: 16 }) : null,
|
|
3376
|
+
sx: { mt: 3, mb: 2 },
|
|
3377
|
+
children: isLoading ? "Logging in..." : "Login"
|
|
3378
|
+
}
|
|
3379
|
+
)
|
|
3380
|
+
] }),
|
|
3381
|
+
error && /* @__PURE__ */ jsx12(Alert7, { severity: "error", sx: { mt: 2 }, onClose: clearError, children: error })
|
|
3382
|
+
] });
|
|
3383
|
+
}
|
|
3384
|
+
function SessionStatus() {
|
|
3385
|
+
const { isAuthenticated, isLoading, isExpiringSoon, expiresIn } = useSessionContext();
|
|
3386
|
+
if (isLoading) {
|
|
3387
|
+
return /* @__PURE__ */ jsxs9(Box8, { sx: { display: "flex", alignItems: "center", gap: 1 }, children: [
|
|
3388
|
+
/* @__PURE__ */ jsx12(CircularProgress7, { size: 16 }),
|
|
3389
|
+
/* @__PURE__ */ jsx12(Typography8, { variant: "caption", children: "Loading session..." })
|
|
3390
|
+
] });
|
|
3391
|
+
}
|
|
3392
|
+
if (!isAuthenticated) {
|
|
3393
|
+
return /* @__PURE__ */ jsx12(Typography8, { variant: "caption", color: "text.secondary", children: "Not logged in" });
|
|
3394
|
+
}
|
|
3395
|
+
return /* @__PURE__ */ jsxs9(Box8, { children: [
|
|
3396
|
+
/* @__PURE__ */ jsx12(Typography8, { variant: "caption", color: "success.main", children: "\u2713 Authenticated" }),
|
|
3397
|
+
isExpiringSoon && /* @__PURE__ */ jsxs9(Typography8, { variant: "caption", color: "warning.main", display: "block", children: [
|
|
3398
|
+
"\u26A0 Token expires in ",
|
|
3399
|
+
Math.round(expiresIn / 1e3 / 60),
|
|
3400
|
+
" min"
|
|
3401
|
+
] })
|
|
3402
|
+
] });
|
|
3403
|
+
}
|
|
2578
3404
|
export {
|
|
2579
3405
|
CrudifyDataProvider,
|
|
2580
3406
|
CrudifyLogin_default as CrudifyLogin,
|
|
2581
3407
|
ERROR_CODES,
|
|
2582
3408
|
ERROR_SEVERITY_MAP,
|
|
3409
|
+
LoginComponent,
|
|
3410
|
+
ProtectedRoute,
|
|
3411
|
+
SessionDebugInfo,
|
|
3412
|
+
SessionManager,
|
|
3413
|
+
SessionProvider,
|
|
3414
|
+
SessionStatus,
|
|
3415
|
+
TokenStorage,
|
|
2583
3416
|
UserProfileDisplay_default as UserProfileDisplay,
|
|
2584
3417
|
configurationManager,
|
|
2585
3418
|
default2 as crudify,
|
|
@@ -2605,5 +3438,7 @@ export {
|
|
|
2605
3438
|
useCrudifyInstance,
|
|
2606
3439
|
useCrudifyLogin,
|
|
2607
3440
|
useCrudifyUser,
|
|
3441
|
+
useSession,
|
|
3442
|
+
useSessionContext,
|
|
2608
3443
|
useUserProfile
|
|
2609
3444
|
};
|