@nocios/crudify-ui 3.0.60 → 3.0.64
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +17 -2
- package/dist/index.d.ts +17 -2
- package/dist/index.js +87 -58
- package/dist/index.mjs +87 -58
- package/package.json +2 -2
package/dist/index.d.mts
CHANGED
|
@@ -163,15 +163,24 @@ type TokenData = {
|
|
|
163
163
|
expiresAt: number;
|
|
164
164
|
refreshExpiresAt: number;
|
|
165
165
|
};
|
|
166
|
-
type StorageType =
|
|
166
|
+
type StorageType = "localStorage" | "sessionStorage" | "none";
|
|
167
167
|
declare class TokenStorage {
|
|
168
168
|
private static readonly TOKEN_KEY;
|
|
169
|
-
private static readonly
|
|
169
|
+
private static readonly ENCRYPTION_KEY_STORAGE;
|
|
170
|
+
private static encryptionKey;
|
|
170
171
|
private static storageType;
|
|
171
172
|
/**
|
|
172
173
|
* Configurar tipo de almacenamiento
|
|
173
174
|
*/
|
|
174
175
|
static setStorageType(type: StorageType): void;
|
|
176
|
+
/**
|
|
177
|
+
* Generar clave de encriptación única y persistente
|
|
178
|
+
*/
|
|
179
|
+
private static generateEncryptionKey;
|
|
180
|
+
/**
|
|
181
|
+
* Obtener o generar clave de encriptación
|
|
182
|
+
*/
|
|
183
|
+
private static getEncryptionKey;
|
|
175
184
|
/**
|
|
176
185
|
* Verificar si el storage está disponible
|
|
177
186
|
*/
|
|
@@ -200,6 +209,10 @@ declare class TokenStorage {
|
|
|
200
209
|
* Limpiar tokens almacenados
|
|
201
210
|
*/
|
|
202
211
|
static clearTokens(): void;
|
|
212
|
+
/**
|
|
213
|
+
* Rotar clave de encriptación (limpia tokens existentes por seguridad)
|
|
214
|
+
*/
|
|
215
|
+
static rotateEncryptionKey(): void;
|
|
203
216
|
/**
|
|
204
217
|
* Verificar si hay tokens válidos guardados
|
|
205
218
|
*/
|
|
@@ -399,6 +412,7 @@ type NotificationOptions = {
|
|
|
399
412
|
vertical: "top" | "bottom";
|
|
400
413
|
horizontal: "left" | "center" | "right";
|
|
401
414
|
};
|
|
415
|
+
allowHtml?: boolean;
|
|
402
416
|
};
|
|
403
417
|
type SessionProviderProps = {
|
|
404
418
|
children: ReactNode;
|
|
@@ -771,6 +785,7 @@ interface GlobalNotificationProviderProps {
|
|
|
771
785
|
horizontal: "left" | "center" | "right";
|
|
772
786
|
};
|
|
773
787
|
enabled?: boolean;
|
|
788
|
+
allowHtml?: boolean;
|
|
774
789
|
}
|
|
775
790
|
declare const GlobalNotificationProvider: React.FC<GlobalNotificationProviderProps>;
|
|
776
791
|
declare const useGlobalNotification: () => GlobalNotificationContextValue;
|
package/dist/index.d.ts
CHANGED
|
@@ -163,15 +163,24 @@ type TokenData = {
|
|
|
163
163
|
expiresAt: number;
|
|
164
164
|
refreshExpiresAt: number;
|
|
165
165
|
};
|
|
166
|
-
type StorageType =
|
|
166
|
+
type StorageType = "localStorage" | "sessionStorage" | "none";
|
|
167
167
|
declare class TokenStorage {
|
|
168
168
|
private static readonly TOKEN_KEY;
|
|
169
|
-
private static readonly
|
|
169
|
+
private static readonly ENCRYPTION_KEY_STORAGE;
|
|
170
|
+
private static encryptionKey;
|
|
170
171
|
private static storageType;
|
|
171
172
|
/**
|
|
172
173
|
* Configurar tipo de almacenamiento
|
|
173
174
|
*/
|
|
174
175
|
static setStorageType(type: StorageType): void;
|
|
176
|
+
/**
|
|
177
|
+
* Generar clave de encriptación única y persistente
|
|
178
|
+
*/
|
|
179
|
+
private static generateEncryptionKey;
|
|
180
|
+
/**
|
|
181
|
+
* Obtener o generar clave de encriptación
|
|
182
|
+
*/
|
|
183
|
+
private static getEncryptionKey;
|
|
175
184
|
/**
|
|
176
185
|
* Verificar si el storage está disponible
|
|
177
186
|
*/
|
|
@@ -200,6 +209,10 @@ declare class TokenStorage {
|
|
|
200
209
|
* Limpiar tokens almacenados
|
|
201
210
|
*/
|
|
202
211
|
static clearTokens(): void;
|
|
212
|
+
/**
|
|
213
|
+
* Rotar clave de encriptación (limpia tokens existentes por seguridad)
|
|
214
|
+
*/
|
|
215
|
+
static rotateEncryptionKey(): void;
|
|
203
216
|
/**
|
|
204
217
|
* Verificar si hay tokens válidos guardados
|
|
205
218
|
*/
|
|
@@ -399,6 +412,7 @@ type NotificationOptions = {
|
|
|
399
412
|
vertical: "top" | "bottom";
|
|
400
413
|
horizontal: "left" | "center" | "right";
|
|
401
414
|
};
|
|
415
|
+
allowHtml?: boolean;
|
|
402
416
|
};
|
|
403
417
|
type SessionProviderProps = {
|
|
404
418
|
children: ReactNode;
|
|
@@ -771,6 +785,7 @@ interface GlobalNotificationProviderProps {
|
|
|
771
785
|
horizontal: "left" | "center" | "right";
|
|
772
786
|
};
|
|
773
787
|
enabled?: boolean;
|
|
788
|
+
allowHtml?: boolean;
|
|
774
789
|
}
|
|
775
790
|
declare const GlobalNotificationProvider: React.FC<GlobalNotificationProviderProps>;
|
|
776
791
|
declare const useGlobalNotification: () => GlobalNotificationContextValue;
|
package/dist/index.js
CHANGED
|
@@ -504,6 +504,47 @@ var _TokenStorage = class _TokenStorage {
|
|
|
504
504
|
static setStorageType(type) {
|
|
505
505
|
_TokenStorage.storageType = type;
|
|
506
506
|
}
|
|
507
|
+
/**
|
|
508
|
+
* Generar clave de encriptación única y persistente
|
|
509
|
+
*/
|
|
510
|
+
static generateEncryptionKey() {
|
|
511
|
+
const browserInfo = [
|
|
512
|
+
navigator.userAgent,
|
|
513
|
+
navigator.language,
|
|
514
|
+
navigator.platform,
|
|
515
|
+
screen.width,
|
|
516
|
+
screen.height,
|
|
517
|
+
Date.now().toString(),
|
|
518
|
+
Math.random().toString(36)
|
|
519
|
+
].join("|");
|
|
520
|
+
return import_crypto_js.default.SHA256(browserInfo).toString();
|
|
521
|
+
}
|
|
522
|
+
/**
|
|
523
|
+
* Obtener o generar clave de encriptación
|
|
524
|
+
*/
|
|
525
|
+
static getEncryptionKey() {
|
|
526
|
+
if (_TokenStorage.encryptionKey) {
|
|
527
|
+
return _TokenStorage.encryptionKey;
|
|
528
|
+
}
|
|
529
|
+
const storage = window.localStorage;
|
|
530
|
+
if (!storage) {
|
|
531
|
+
_TokenStorage.encryptionKey = _TokenStorage.generateEncryptionKey();
|
|
532
|
+
return _TokenStorage.encryptionKey;
|
|
533
|
+
}
|
|
534
|
+
try {
|
|
535
|
+
let existingKey = storage.getItem(_TokenStorage.ENCRYPTION_KEY_STORAGE);
|
|
536
|
+
if (!existingKey || existingKey.length < 32) {
|
|
537
|
+
existingKey = _TokenStorage.generateEncryptionKey();
|
|
538
|
+
storage.setItem(_TokenStorage.ENCRYPTION_KEY_STORAGE, existingKey);
|
|
539
|
+
}
|
|
540
|
+
_TokenStorage.encryptionKey = existingKey;
|
|
541
|
+
return existingKey;
|
|
542
|
+
} catch (error) {
|
|
543
|
+
console.warn("Crudify: Cannot persist encryption key, using temporary key");
|
|
544
|
+
_TokenStorage.encryptionKey = _TokenStorage.generateEncryptionKey();
|
|
545
|
+
return _TokenStorage.encryptionKey;
|
|
546
|
+
}
|
|
547
|
+
}
|
|
507
548
|
/**
|
|
508
549
|
* Verificar si el storage está disponible
|
|
509
550
|
*/
|
|
@@ -534,7 +575,8 @@ var _TokenStorage = class _TokenStorage {
|
|
|
534
575
|
*/
|
|
535
576
|
static encrypt(data) {
|
|
536
577
|
try {
|
|
537
|
-
|
|
578
|
+
const encryptionKey = _TokenStorage.getEncryptionKey();
|
|
579
|
+
return import_crypto_js.default.AES.encrypt(data, encryptionKey).toString();
|
|
538
580
|
} catch (error) {
|
|
539
581
|
console.error("Crudify: Encryption failed", error);
|
|
540
582
|
return data;
|
|
@@ -545,7 +587,8 @@ var _TokenStorage = class _TokenStorage {
|
|
|
545
587
|
*/
|
|
546
588
|
static decrypt(encryptedData) {
|
|
547
589
|
try {
|
|
548
|
-
const
|
|
590
|
+
const encryptionKey = _TokenStorage.getEncryptionKey();
|
|
591
|
+
const bytes = import_crypto_js.default.AES.decrypt(encryptedData, encryptionKey);
|
|
549
592
|
const decrypted = bytes.toString(import_crypto_js.default.enc.Utf8);
|
|
550
593
|
return decrypted || encryptedData;
|
|
551
594
|
} catch (error) {
|
|
@@ -620,6 +663,22 @@ var _TokenStorage = class _TokenStorage {
|
|
|
620
663
|
console.error("Crudify: Failed to clear tokens", error);
|
|
621
664
|
}
|
|
622
665
|
}
|
|
666
|
+
/**
|
|
667
|
+
* Rotar clave de encriptación (limpia tokens existentes por seguridad)
|
|
668
|
+
*/
|
|
669
|
+
static rotateEncryptionKey() {
|
|
670
|
+
try {
|
|
671
|
+
_TokenStorage.clearTokens();
|
|
672
|
+
_TokenStorage.encryptionKey = null;
|
|
673
|
+
const storage = window.localStorage;
|
|
674
|
+
if (storage) {
|
|
675
|
+
storage.removeItem(_TokenStorage.ENCRYPTION_KEY_STORAGE);
|
|
676
|
+
}
|
|
677
|
+
console.info("Crudify: Encryption key rotated successfully");
|
|
678
|
+
} catch (error) {
|
|
679
|
+
console.error("Crudify: Failed to rotate encryption key", error);
|
|
680
|
+
}
|
|
681
|
+
}
|
|
623
682
|
/**
|
|
624
683
|
* Verificar si hay tokens válidos guardados
|
|
625
684
|
*/
|
|
@@ -658,7 +717,8 @@ var _TokenStorage = class _TokenStorage {
|
|
|
658
717
|
}
|
|
659
718
|
};
|
|
660
719
|
_TokenStorage.TOKEN_KEY = "crudify_tokens";
|
|
661
|
-
_TokenStorage.
|
|
720
|
+
_TokenStorage.ENCRYPTION_KEY_STORAGE = "crudify_enc_key";
|
|
721
|
+
_TokenStorage.encryptionKey = null;
|
|
662
722
|
_TokenStorage.storageType = "localStorage";
|
|
663
723
|
var TokenStorage = _TokenStorage;
|
|
664
724
|
|
|
@@ -1424,8 +1484,10 @@ var GlobalNotificationProvider = ({
|
|
|
1424
1484
|
maxNotifications = 5,
|
|
1425
1485
|
defaultAutoHideDuration = 6e3,
|
|
1426
1486
|
position = { vertical: "top", horizontal: "right" },
|
|
1427
|
-
enabled = false
|
|
1487
|
+
enabled = false,
|
|
1428
1488
|
// ✅ Por defecto DESACTIVADO
|
|
1489
|
+
allowHtml = false
|
|
1490
|
+
// Por defecto no permitir HTML
|
|
1429
1491
|
}) => {
|
|
1430
1492
|
const [notifications, setNotifications] = (0, import_react6.useState)([]);
|
|
1431
1493
|
const showNotification = (0, import_react6.useCallback)(
|
|
@@ -1448,8 +1510,8 @@ var GlobalNotificationProvider = ({
|
|
|
1448
1510
|
severity,
|
|
1449
1511
|
autoHideDuration: options?.autoHideDuration ?? defaultAutoHideDuration,
|
|
1450
1512
|
persistent: options?.persistent ?? false,
|
|
1451
|
-
allowHtml: options?.allowHtml ??
|
|
1452
|
-
//
|
|
1513
|
+
allowHtml: options?.allowHtml ?? allowHtml
|
|
1514
|
+
// Usar valor del provider por defecto
|
|
1453
1515
|
};
|
|
1454
1516
|
setNotifications((prev) => {
|
|
1455
1517
|
const updatedNotifications = prev.length >= maxNotifications ? prev.slice(-(maxNotifications - 1)) : prev;
|
|
@@ -1457,7 +1519,7 @@ var GlobalNotificationProvider = ({
|
|
|
1457
1519
|
});
|
|
1458
1520
|
return id;
|
|
1459
1521
|
},
|
|
1460
|
-
[maxNotifications, defaultAutoHideDuration, enabled]
|
|
1522
|
+
[maxNotifications, defaultAutoHideDuration, enabled, allowHtml]
|
|
1461
1523
|
);
|
|
1462
1524
|
const hideNotification = (0, import_react6.useCallback)((id) => {
|
|
1463
1525
|
setNotifications((prev) => prev.filter((notification) => notification.id !== id));
|
|
@@ -1569,15 +1631,17 @@ function InnerSessionProvider({
|
|
|
1569
1631
|
showNotificationFn = showNotification;
|
|
1570
1632
|
} catch {
|
|
1571
1633
|
}
|
|
1572
|
-
const enhancedOptions = import_react7.default.useMemo(
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1634
|
+
const enhancedOptions = import_react7.default.useMemo(
|
|
1635
|
+
() => ({
|
|
1636
|
+
...options,
|
|
1637
|
+
showNotification: showNotificationFn,
|
|
1638
|
+
// TODO: Agregar translateFn cuando esté disponible
|
|
1639
|
+
onSessionExpired: () => {
|
|
1640
|
+
options.onSessionExpired?.();
|
|
1641
|
+
}
|
|
1642
|
+
}),
|
|
1643
|
+
[options, showNotificationFn]
|
|
1644
|
+
);
|
|
1581
1645
|
const sessionHook = useSession(enhancedOptions);
|
|
1582
1646
|
const resolvedConfig = (0, import_react7.useMemo)(() => {
|
|
1583
1647
|
let publicApiKey;
|
|
@@ -1626,12 +1690,6 @@ function InnerSessionProvider({
|
|
|
1626
1690
|
loginActions = decodedActions.split(",").map((s) => s.trim()).filter(Boolean);
|
|
1627
1691
|
}
|
|
1628
1692
|
}
|
|
1629
|
-
console.log("\u{1F50D} SessionProvider - Configuration Detection:");
|
|
1630
|
-
console.log(" - Config source:", configSource);
|
|
1631
|
-
console.log(" - PublicApiKey:", publicApiKey ? publicApiKey.substring(0, 15) + "..." : "undefined");
|
|
1632
|
-
console.log(" - Environment:", env);
|
|
1633
|
-
console.log(" - App name:", appName);
|
|
1634
|
-
console.log(" - Login actions:", loginActions);
|
|
1635
1693
|
return {
|
|
1636
1694
|
publicApiKey,
|
|
1637
1695
|
env,
|
|
@@ -1683,7 +1741,9 @@ function SessionProvider(props) {
|
|
|
1683
1741
|
enabled: props.showNotifications,
|
|
1684
1742
|
maxNotifications: props.notificationOptions?.maxNotifications || 5,
|
|
1685
1743
|
defaultAutoHideDuration: props.notificationOptions?.defaultAutoHideDuration || 6e3,
|
|
1686
|
-
position: props.notificationOptions?.position || { vertical: "top", horizontal: "right" }
|
|
1744
|
+
position: props.notificationOptions?.position || { vertical: "top", horizontal: "right" },
|
|
1745
|
+
allowHtml: props.notificationOptions?.allowHtml || false
|
|
1746
|
+
// Pasar allowHtml desde notificationOptions
|
|
1687
1747
|
};
|
|
1688
1748
|
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(GlobalNotificationProvider, { ...notificationConfig, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(InnerSessionProvider, { ...props }) });
|
|
1689
1749
|
}
|
|
@@ -2057,11 +2117,13 @@ var LoginForm = ({ onScreenChange, onExternalNavigate, onLoginSuccess, onError,
|
|
|
2057
2117
|
const { crudify: crudify7 } = useCrudify();
|
|
2058
2118
|
const { state, updateFormData, setFieldError, clearErrors, setLoading } = useLoginState();
|
|
2059
2119
|
const { login: sessionLogin } = useSessionContext();
|
|
2060
|
-
const
|
|
2120
|
+
const translationContext = useTranslation();
|
|
2121
|
+
const { t } = translationContext;
|
|
2122
|
+
const i18n = translationContext.i18n;
|
|
2061
2123
|
const usernameInputRef = (0, import_react8.useRef)(null);
|
|
2062
2124
|
const errorTranslator = createErrorTranslator(t, {
|
|
2063
2125
|
currentLanguage: i18n?.language,
|
|
2064
|
-
enableDebug:
|
|
2126
|
+
enableDebug: false
|
|
2065
2127
|
});
|
|
2066
2128
|
const getRedirectUrl = () => {
|
|
2067
2129
|
if (state.searchParams.redirect) {
|
|
@@ -4035,7 +4097,6 @@ var useUserData = (options = {}) => {
|
|
|
4035
4097
|
}, []);
|
|
4036
4098
|
const refreshProfile = (0, import_react18.useCallback)(async () => {
|
|
4037
4099
|
const userEmail = getUserEmail();
|
|
4038
|
-
console.log("\u{1F464} useUserData - Refreshing profile for:", userEmail);
|
|
4039
4100
|
if (!userEmail) {
|
|
4040
4101
|
if (mountedRef.current) {
|
|
4041
4102
|
setError("No user email available from session data");
|
|
@@ -4061,91 +4122,59 @@ var useUserData = (options = {}) => {
|
|
|
4061
4122
|
setLoading(true);
|
|
4062
4123
|
setError(null);
|
|
4063
4124
|
}
|
|
4064
|
-
console.log("\u{1F464} useUserData - Fetching profile data from database");
|
|
4065
4125
|
const response = await import_crudify_browser4.default.readItems("users", {
|
|
4066
4126
|
filter: { email: userEmail },
|
|
4067
4127
|
pagination: { limit: 1 }
|
|
4068
4128
|
});
|
|
4069
|
-
console.log("\u{1F464} useUserData - Database response:", response);
|
|
4070
|
-
console.log("\u{1F464} useUserData - response.data:", response.data);
|
|
4071
|
-
console.log("\u{1F464} useUserData - response.data type:", typeof response.data);
|
|
4072
4129
|
if (currentRequestId === requestIdRef.current && mountedRef.current && !abortController.signal.aborted) {
|
|
4073
4130
|
let userData2 = null;
|
|
4074
4131
|
if (response.success) {
|
|
4075
|
-
console.log("\u{1F464} useUserData - Processing successful response:", {
|
|
4076
|
-
dataType: typeof response.data,
|
|
4077
|
-
isArray: Array.isArray(response.data),
|
|
4078
|
-
hasResponse: !!response.data?.response,
|
|
4079
|
-
hasResponseData: !!response.data?.response?.data,
|
|
4080
|
-
responseDataType: typeof response.data?.response?.data
|
|
4081
|
-
});
|
|
4082
4132
|
if (Array.isArray(response.data) && response.data.length > 0) {
|
|
4083
|
-
console.log("\u{1F464} useUserData - Found direct array format");
|
|
4084
4133
|
userData2 = response.data[0];
|
|
4085
4134
|
} else if (response.data?.response?.data) {
|
|
4086
|
-
console.log("\u{1F464} useUserData - Found nested response.data format");
|
|
4087
4135
|
try {
|
|
4088
4136
|
const rawData = response.data.response.data;
|
|
4089
|
-
console.log("\u{1F464} useUserData - Raw nested data:", rawData);
|
|
4090
|
-
console.log("\u{1F464} useUserData - Raw data type:", typeof rawData);
|
|
4091
4137
|
const parsedData = typeof rawData === "string" ? JSON.parse(rawData) : rawData;
|
|
4092
|
-
console.log("\u{1F464} useUserData - Parsed nested data:", parsedData);
|
|
4093
4138
|
if (parsedData && parsedData.items && Array.isArray(parsedData.items) && parsedData.items.length > 0) {
|
|
4094
4139
|
userData2 = parsedData.items[0];
|
|
4095
|
-
console.log("\u{1F464} useUserData - Extracted user from nested items:", userData2);
|
|
4096
4140
|
} else {
|
|
4097
|
-
console.log("\u{1F464} useUserData - No items found in parsed data or items array is empty");
|
|
4098
4141
|
}
|
|
4099
4142
|
} catch (parseError) {
|
|
4100
|
-
console.error("\u{1F464} useUserData - Error parsing nested response data:", parseError);
|
|
4101
4143
|
}
|
|
4102
4144
|
} else if (response.data && typeof response.data === "object") {
|
|
4103
|
-
console.log("\u{1F464} useUserData - Found object format, checking for items");
|
|
4104
4145
|
if (response.data.items && Array.isArray(response.data.items) && response.data.items.length > 0) {
|
|
4105
|
-
console.log("\u{1F464} useUserData - Found items in object format");
|
|
4106
4146
|
userData2 = response.data.items[0];
|
|
4107
4147
|
} else {
|
|
4108
|
-
console.log("\u{1F464} useUserData - No items found in object format");
|
|
4109
4148
|
}
|
|
4110
4149
|
} else if (response.data?.data?.response?.data) {
|
|
4111
|
-
console.log("\u{1F464} useUserData - Found double-nested data.data.response.data format");
|
|
4112
4150
|
try {
|
|
4113
4151
|
const rawData = response.data.data.response.data;
|
|
4114
|
-
console.log("\u{1F464} useUserData - Raw double-nested data:", rawData);
|
|
4115
4152
|
const parsedData = typeof rawData === "string" ? JSON.parse(rawData) : rawData;
|
|
4116
|
-
console.log("\u{1F464} useUserData - Parsed double-nested data:", parsedData);
|
|
4117
4153
|
if (parsedData && parsedData.items && Array.isArray(parsedData.items) && parsedData.items.length > 0) {
|
|
4118
4154
|
userData2 = parsedData.items[0];
|
|
4119
|
-
console.log("\u{1F464} useUserData - Extracted user from double-nested items:", userData2);
|
|
4120
4155
|
}
|
|
4121
4156
|
} catch (parseError) {
|
|
4122
|
-
console.error("\u{1F464} useUserData - Error parsing double-nested response data:", parseError);
|
|
4123
4157
|
}
|
|
4124
4158
|
}
|
|
4125
4159
|
}
|
|
4126
4160
|
if (userData2) {
|
|
4127
|
-
console.log("\u{1F464} useUserData - User data found:", userData2);
|
|
4128
4161
|
setUserData(userData2);
|
|
4129
4162
|
setError(null);
|
|
4130
4163
|
retryCountRef.current = 0;
|
|
4131
|
-
console.log("\u{1F464} useUserData - Profile loaded successfully:", userData2);
|
|
4132
4164
|
} else {
|
|
4133
4165
|
setError("User profile not found in database");
|
|
4134
4166
|
setUserData(null);
|
|
4135
|
-
console.warn("\u{1F464} useUserData - User not found for email:", userEmail);
|
|
4136
4167
|
}
|
|
4137
4168
|
}
|
|
4138
4169
|
} catch (err) {
|
|
4139
4170
|
if (currentRequestId === requestIdRef.current && mountedRef.current) {
|
|
4140
4171
|
const error2 = err;
|
|
4141
|
-
console.error("\u{1F464} useUserData - Error fetching profile:", error2);
|
|
4142
4172
|
if (error2.name === "AbortError") {
|
|
4143
4173
|
return;
|
|
4144
4174
|
}
|
|
4145
4175
|
const shouldRetry = retryOnError && retryCountRef.current < maxRetries && (error2.message?.includes("Network Error") || error2.message?.includes("Failed to fetch"));
|
|
4146
4176
|
if (shouldRetry) {
|
|
4147
4177
|
retryCountRef.current++;
|
|
4148
|
-
console.log(`\u{1F464} useUserData - Retrying profile fetch (${retryCountRef.current}/${maxRetries})`);
|
|
4149
4178
|
setTimeout(() => {
|
|
4150
4179
|
if (mountedRef.current) {
|
|
4151
4180
|
refreshProfile();
|
package/dist/index.mjs
CHANGED
|
@@ -431,6 +431,47 @@ var _TokenStorage = class _TokenStorage {
|
|
|
431
431
|
static setStorageType(type) {
|
|
432
432
|
_TokenStorage.storageType = type;
|
|
433
433
|
}
|
|
434
|
+
/**
|
|
435
|
+
* Generar clave de encriptación única y persistente
|
|
436
|
+
*/
|
|
437
|
+
static generateEncryptionKey() {
|
|
438
|
+
const browserInfo = [
|
|
439
|
+
navigator.userAgent,
|
|
440
|
+
navigator.language,
|
|
441
|
+
navigator.platform,
|
|
442
|
+
screen.width,
|
|
443
|
+
screen.height,
|
|
444
|
+
Date.now().toString(),
|
|
445
|
+
Math.random().toString(36)
|
|
446
|
+
].join("|");
|
|
447
|
+
return CryptoJS.SHA256(browserInfo).toString();
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* Obtener o generar clave de encriptación
|
|
451
|
+
*/
|
|
452
|
+
static getEncryptionKey() {
|
|
453
|
+
if (_TokenStorage.encryptionKey) {
|
|
454
|
+
return _TokenStorage.encryptionKey;
|
|
455
|
+
}
|
|
456
|
+
const storage = window.localStorage;
|
|
457
|
+
if (!storage) {
|
|
458
|
+
_TokenStorage.encryptionKey = _TokenStorage.generateEncryptionKey();
|
|
459
|
+
return _TokenStorage.encryptionKey;
|
|
460
|
+
}
|
|
461
|
+
try {
|
|
462
|
+
let existingKey = storage.getItem(_TokenStorage.ENCRYPTION_KEY_STORAGE);
|
|
463
|
+
if (!existingKey || existingKey.length < 32) {
|
|
464
|
+
existingKey = _TokenStorage.generateEncryptionKey();
|
|
465
|
+
storage.setItem(_TokenStorage.ENCRYPTION_KEY_STORAGE, existingKey);
|
|
466
|
+
}
|
|
467
|
+
_TokenStorage.encryptionKey = existingKey;
|
|
468
|
+
return existingKey;
|
|
469
|
+
} catch (error) {
|
|
470
|
+
console.warn("Crudify: Cannot persist encryption key, using temporary key");
|
|
471
|
+
_TokenStorage.encryptionKey = _TokenStorage.generateEncryptionKey();
|
|
472
|
+
return _TokenStorage.encryptionKey;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
434
475
|
/**
|
|
435
476
|
* Verificar si el storage está disponible
|
|
436
477
|
*/
|
|
@@ -461,7 +502,8 @@ var _TokenStorage = class _TokenStorage {
|
|
|
461
502
|
*/
|
|
462
503
|
static encrypt(data) {
|
|
463
504
|
try {
|
|
464
|
-
|
|
505
|
+
const encryptionKey = _TokenStorage.getEncryptionKey();
|
|
506
|
+
return CryptoJS.AES.encrypt(data, encryptionKey).toString();
|
|
465
507
|
} catch (error) {
|
|
466
508
|
console.error("Crudify: Encryption failed", error);
|
|
467
509
|
return data;
|
|
@@ -472,7 +514,8 @@ var _TokenStorage = class _TokenStorage {
|
|
|
472
514
|
*/
|
|
473
515
|
static decrypt(encryptedData) {
|
|
474
516
|
try {
|
|
475
|
-
const
|
|
517
|
+
const encryptionKey = _TokenStorage.getEncryptionKey();
|
|
518
|
+
const bytes = CryptoJS.AES.decrypt(encryptedData, encryptionKey);
|
|
476
519
|
const decrypted = bytes.toString(CryptoJS.enc.Utf8);
|
|
477
520
|
return decrypted || encryptedData;
|
|
478
521
|
} catch (error) {
|
|
@@ -547,6 +590,22 @@ var _TokenStorage = class _TokenStorage {
|
|
|
547
590
|
console.error("Crudify: Failed to clear tokens", error);
|
|
548
591
|
}
|
|
549
592
|
}
|
|
593
|
+
/**
|
|
594
|
+
* Rotar clave de encriptación (limpia tokens existentes por seguridad)
|
|
595
|
+
*/
|
|
596
|
+
static rotateEncryptionKey() {
|
|
597
|
+
try {
|
|
598
|
+
_TokenStorage.clearTokens();
|
|
599
|
+
_TokenStorage.encryptionKey = null;
|
|
600
|
+
const storage = window.localStorage;
|
|
601
|
+
if (storage) {
|
|
602
|
+
storage.removeItem(_TokenStorage.ENCRYPTION_KEY_STORAGE);
|
|
603
|
+
}
|
|
604
|
+
console.info("Crudify: Encryption key rotated successfully");
|
|
605
|
+
} catch (error) {
|
|
606
|
+
console.error("Crudify: Failed to rotate encryption key", error);
|
|
607
|
+
}
|
|
608
|
+
}
|
|
550
609
|
/**
|
|
551
610
|
* Verificar si hay tokens válidos guardados
|
|
552
611
|
*/
|
|
@@ -585,7 +644,8 @@ var _TokenStorage = class _TokenStorage {
|
|
|
585
644
|
}
|
|
586
645
|
};
|
|
587
646
|
_TokenStorage.TOKEN_KEY = "crudify_tokens";
|
|
588
|
-
_TokenStorage.
|
|
647
|
+
_TokenStorage.ENCRYPTION_KEY_STORAGE = "crudify_enc_key";
|
|
648
|
+
_TokenStorage.encryptionKey = null;
|
|
589
649
|
_TokenStorage.storageType = "localStorage";
|
|
590
650
|
var TokenStorage = _TokenStorage;
|
|
591
651
|
|
|
@@ -1351,8 +1411,10 @@ var GlobalNotificationProvider = ({
|
|
|
1351
1411
|
maxNotifications = 5,
|
|
1352
1412
|
defaultAutoHideDuration = 6e3,
|
|
1353
1413
|
position = { vertical: "top", horizontal: "right" },
|
|
1354
|
-
enabled = false
|
|
1414
|
+
enabled = false,
|
|
1355
1415
|
// ✅ Por defecto DESACTIVADO
|
|
1416
|
+
allowHtml = false
|
|
1417
|
+
// Por defecto no permitir HTML
|
|
1356
1418
|
}) => {
|
|
1357
1419
|
const [notifications, setNotifications] = useState4([]);
|
|
1358
1420
|
const showNotification = useCallback2(
|
|
@@ -1375,8 +1437,8 @@ var GlobalNotificationProvider = ({
|
|
|
1375
1437
|
severity,
|
|
1376
1438
|
autoHideDuration: options?.autoHideDuration ?? defaultAutoHideDuration,
|
|
1377
1439
|
persistent: options?.persistent ?? false,
|
|
1378
|
-
allowHtml: options?.allowHtml ??
|
|
1379
|
-
//
|
|
1440
|
+
allowHtml: options?.allowHtml ?? allowHtml
|
|
1441
|
+
// Usar valor del provider por defecto
|
|
1380
1442
|
};
|
|
1381
1443
|
setNotifications((prev) => {
|
|
1382
1444
|
const updatedNotifications = prev.length >= maxNotifications ? prev.slice(-(maxNotifications - 1)) : prev;
|
|
@@ -1384,7 +1446,7 @@ var GlobalNotificationProvider = ({
|
|
|
1384
1446
|
});
|
|
1385
1447
|
return id;
|
|
1386
1448
|
},
|
|
1387
|
-
[maxNotifications, defaultAutoHideDuration, enabled]
|
|
1449
|
+
[maxNotifications, defaultAutoHideDuration, enabled, allowHtml]
|
|
1388
1450
|
);
|
|
1389
1451
|
const hideNotification = useCallback2((id) => {
|
|
1390
1452
|
setNotifications((prev) => prev.filter((notification) => notification.id !== id));
|
|
@@ -1496,15 +1558,17 @@ function InnerSessionProvider({
|
|
|
1496
1558
|
showNotificationFn = showNotification;
|
|
1497
1559
|
} catch {
|
|
1498
1560
|
}
|
|
1499
|
-
const enhancedOptions = React5.useMemo(
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1561
|
+
const enhancedOptions = React5.useMemo(
|
|
1562
|
+
() => ({
|
|
1563
|
+
...options,
|
|
1564
|
+
showNotification: showNotificationFn,
|
|
1565
|
+
// TODO: Agregar translateFn cuando esté disponible
|
|
1566
|
+
onSessionExpired: () => {
|
|
1567
|
+
options.onSessionExpired?.();
|
|
1568
|
+
}
|
|
1569
|
+
}),
|
|
1570
|
+
[options, showNotificationFn]
|
|
1571
|
+
);
|
|
1508
1572
|
const sessionHook = useSession(enhancedOptions);
|
|
1509
1573
|
const resolvedConfig = useMemo2(() => {
|
|
1510
1574
|
let publicApiKey;
|
|
@@ -1553,12 +1617,6 @@ function InnerSessionProvider({
|
|
|
1553
1617
|
loginActions = decodedActions.split(",").map((s) => s.trim()).filter(Boolean);
|
|
1554
1618
|
}
|
|
1555
1619
|
}
|
|
1556
|
-
console.log("\u{1F50D} SessionProvider - Configuration Detection:");
|
|
1557
|
-
console.log(" - Config source:", configSource);
|
|
1558
|
-
console.log(" - PublicApiKey:", publicApiKey ? publicApiKey.substring(0, 15) + "..." : "undefined");
|
|
1559
|
-
console.log(" - Environment:", env);
|
|
1560
|
-
console.log(" - App name:", appName);
|
|
1561
|
-
console.log(" - Login actions:", loginActions);
|
|
1562
1620
|
return {
|
|
1563
1621
|
publicApiKey,
|
|
1564
1622
|
env,
|
|
@@ -1610,7 +1668,9 @@ function SessionProvider(props) {
|
|
|
1610
1668
|
enabled: props.showNotifications,
|
|
1611
1669
|
maxNotifications: props.notificationOptions?.maxNotifications || 5,
|
|
1612
1670
|
defaultAutoHideDuration: props.notificationOptions?.defaultAutoHideDuration || 6e3,
|
|
1613
|
-
position: props.notificationOptions?.position || { vertical: "top", horizontal: "right" }
|
|
1671
|
+
position: props.notificationOptions?.position || { vertical: "top", horizontal: "right" },
|
|
1672
|
+
allowHtml: props.notificationOptions?.allowHtml || false
|
|
1673
|
+
// Pasar allowHtml desde notificationOptions
|
|
1614
1674
|
};
|
|
1615
1675
|
return /* @__PURE__ */ jsx5(GlobalNotificationProvider, { ...notificationConfig, children: /* @__PURE__ */ jsx5(InnerSessionProvider, { ...props }) });
|
|
1616
1676
|
}
|
|
@@ -1984,11 +2044,13 @@ var LoginForm = ({ onScreenChange, onExternalNavigate, onLoginSuccess, onError,
|
|
|
1984
2044
|
const { crudify: crudify7 } = useCrudify();
|
|
1985
2045
|
const { state, updateFormData, setFieldError, clearErrors, setLoading } = useLoginState();
|
|
1986
2046
|
const { login: sessionLogin } = useSessionContext();
|
|
1987
|
-
const
|
|
2047
|
+
const translationContext = useTranslation();
|
|
2048
|
+
const { t } = translationContext;
|
|
2049
|
+
const i18n = translationContext.i18n;
|
|
1988
2050
|
const usernameInputRef = useRef(null);
|
|
1989
2051
|
const errorTranslator = createErrorTranslator(t, {
|
|
1990
2052
|
currentLanguage: i18n?.language,
|
|
1991
|
-
enableDebug:
|
|
2053
|
+
enableDebug: false
|
|
1992
2054
|
});
|
|
1993
2055
|
const getRedirectUrl = () => {
|
|
1994
2056
|
if (state.searchParams.redirect) {
|
|
@@ -4021,7 +4083,6 @@ var useUserData = (options = {}) => {
|
|
|
4021
4083
|
}, []);
|
|
4022
4084
|
const refreshProfile = useCallback4(async () => {
|
|
4023
4085
|
const userEmail = getUserEmail();
|
|
4024
|
-
console.log("\u{1F464} useUserData - Refreshing profile for:", userEmail);
|
|
4025
4086
|
if (!userEmail) {
|
|
4026
4087
|
if (mountedRef.current) {
|
|
4027
4088
|
setError("No user email available from session data");
|
|
@@ -4047,91 +4108,59 @@ var useUserData = (options = {}) => {
|
|
|
4047
4108
|
setLoading(true);
|
|
4048
4109
|
setError(null);
|
|
4049
4110
|
}
|
|
4050
|
-
console.log("\u{1F464} useUserData - Fetching profile data from database");
|
|
4051
4111
|
const response = await crudify4.readItems("users", {
|
|
4052
4112
|
filter: { email: userEmail },
|
|
4053
4113
|
pagination: { limit: 1 }
|
|
4054
4114
|
});
|
|
4055
|
-
console.log("\u{1F464} useUserData - Database response:", response);
|
|
4056
|
-
console.log("\u{1F464} useUserData - response.data:", response.data);
|
|
4057
|
-
console.log("\u{1F464} useUserData - response.data type:", typeof response.data);
|
|
4058
4115
|
if (currentRequestId === requestIdRef.current && mountedRef.current && !abortController.signal.aborted) {
|
|
4059
4116
|
let userData2 = null;
|
|
4060
4117
|
if (response.success) {
|
|
4061
|
-
console.log("\u{1F464} useUserData - Processing successful response:", {
|
|
4062
|
-
dataType: typeof response.data,
|
|
4063
|
-
isArray: Array.isArray(response.data),
|
|
4064
|
-
hasResponse: !!response.data?.response,
|
|
4065
|
-
hasResponseData: !!response.data?.response?.data,
|
|
4066
|
-
responseDataType: typeof response.data?.response?.data
|
|
4067
|
-
});
|
|
4068
4118
|
if (Array.isArray(response.data) && response.data.length > 0) {
|
|
4069
|
-
console.log("\u{1F464} useUserData - Found direct array format");
|
|
4070
4119
|
userData2 = response.data[0];
|
|
4071
4120
|
} else if (response.data?.response?.data) {
|
|
4072
|
-
console.log("\u{1F464} useUserData - Found nested response.data format");
|
|
4073
4121
|
try {
|
|
4074
4122
|
const rawData = response.data.response.data;
|
|
4075
|
-
console.log("\u{1F464} useUserData - Raw nested data:", rawData);
|
|
4076
|
-
console.log("\u{1F464} useUserData - Raw data type:", typeof rawData);
|
|
4077
4123
|
const parsedData = typeof rawData === "string" ? JSON.parse(rawData) : rawData;
|
|
4078
|
-
console.log("\u{1F464} useUserData - Parsed nested data:", parsedData);
|
|
4079
4124
|
if (parsedData && parsedData.items && Array.isArray(parsedData.items) && parsedData.items.length > 0) {
|
|
4080
4125
|
userData2 = parsedData.items[0];
|
|
4081
|
-
console.log("\u{1F464} useUserData - Extracted user from nested items:", userData2);
|
|
4082
4126
|
} else {
|
|
4083
|
-
console.log("\u{1F464} useUserData - No items found in parsed data or items array is empty");
|
|
4084
4127
|
}
|
|
4085
4128
|
} catch (parseError) {
|
|
4086
|
-
console.error("\u{1F464} useUserData - Error parsing nested response data:", parseError);
|
|
4087
4129
|
}
|
|
4088
4130
|
} else if (response.data && typeof response.data === "object") {
|
|
4089
|
-
console.log("\u{1F464} useUserData - Found object format, checking for items");
|
|
4090
4131
|
if (response.data.items && Array.isArray(response.data.items) && response.data.items.length > 0) {
|
|
4091
|
-
console.log("\u{1F464} useUserData - Found items in object format");
|
|
4092
4132
|
userData2 = response.data.items[0];
|
|
4093
4133
|
} else {
|
|
4094
|
-
console.log("\u{1F464} useUserData - No items found in object format");
|
|
4095
4134
|
}
|
|
4096
4135
|
} else if (response.data?.data?.response?.data) {
|
|
4097
|
-
console.log("\u{1F464} useUserData - Found double-nested data.data.response.data format");
|
|
4098
4136
|
try {
|
|
4099
4137
|
const rawData = response.data.data.response.data;
|
|
4100
|
-
console.log("\u{1F464} useUserData - Raw double-nested data:", rawData);
|
|
4101
4138
|
const parsedData = typeof rawData === "string" ? JSON.parse(rawData) : rawData;
|
|
4102
|
-
console.log("\u{1F464} useUserData - Parsed double-nested data:", parsedData);
|
|
4103
4139
|
if (parsedData && parsedData.items && Array.isArray(parsedData.items) && parsedData.items.length > 0) {
|
|
4104
4140
|
userData2 = parsedData.items[0];
|
|
4105
|
-
console.log("\u{1F464} useUserData - Extracted user from double-nested items:", userData2);
|
|
4106
4141
|
}
|
|
4107
4142
|
} catch (parseError) {
|
|
4108
|
-
console.error("\u{1F464} useUserData - Error parsing double-nested response data:", parseError);
|
|
4109
4143
|
}
|
|
4110
4144
|
}
|
|
4111
4145
|
}
|
|
4112
4146
|
if (userData2) {
|
|
4113
|
-
console.log("\u{1F464} useUserData - User data found:", userData2);
|
|
4114
4147
|
setUserData(userData2);
|
|
4115
4148
|
setError(null);
|
|
4116
4149
|
retryCountRef.current = 0;
|
|
4117
|
-
console.log("\u{1F464} useUserData - Profile loaded successfully:", userData2);
|
|
4118
4150
|
} else {
|
|
4119
4151
|
setError("User profile not found in database");
|
|
4120
4152
|
setUserData(null);
|
|
4121
|
-
console.warn("\u{1F464} useUserData - User not found for email:", userEmail);
|
|
4122
4153
|
}
|
|
4123
4154
|
}
|
|
4124
4155
|
} catch (err) {
|
|
4125
4156
|
if (currentRequestId === requestIdRef.current && mountedRef.current) {
|
|
4126
4157
|
const error2 = err;
|
|
4127
|
-
console.error("\u{1F464} useUserData - Error fetching profile:", error2);
|
|
4128
4158
|
if (error2.name === "AbortError") {
|
|
4129
4159
|
return;
|
|
4130
4160
|
}
|
|
4131
4161
|
const shouldRetry = retryOnError && retryCountRef.current < maxRetries && (error2.message?.includes("Network Error") || error2.message?.includes("Failed to fetch"));
|
|
4132
4162
|
if (shouldRetry) {
|
|
4133
4163
|
retryCountRef.current++;
|
|
4134
|
-
console.log(`\u{1F464} useUserData - Retrying profile fetch (${retryCountRef.current}/${maxRetries})`);
|
|
4135
4164
|
setTimeout(() => {
|
|
4136
4165
|
if (mountedRef.current) {
|
|
4137
4166
|
refreshProfile();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nocios/crudify-ui",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.64",
|
|
4
4
|
"description": "Biblioteca de componentes UI para Crudify",
|
|
5
5
|
"author": "Nocios",
|
|
6
6
|
"license": "MIT",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"@mui/icons-material": "^7.1.0",
|
|
26
26
|
"@mui/material": "^7.1.0",
|
|
27
27
|
"@mui/x-data-grid": "^8.5.1",
|
|
28
|
-
"@nocios/crudify-browser": "^2.0.
|
|
28
|
+
"@nocios/crudify-browser": "^2.0.62",
|
|
29
29
|
"@types/dompurify": "^3.0.5",
|
|
30
30
|
"@types/uuid": "^10.0.0",
|
|
31
31
|
"crypto-js": "^4.2.0",
|