max-remotes-helper 1.0.1 → 1.0.2
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/components/DevButton.d.ts +20 -0
- package/dist/components/DevCard.d.ts +13 -0
- package/dist/components/DevWrapper.d.ts +6 -0
- package/dist/components/FloatingTokenButton.d.ts +6 -0
- package/dist/components/RemoteWrapper.d.ts +2 -2
- package/dist/components/TokenGenerator.d.ts +11 -0
- package/dist/components/TokenInput.d.ts +9 -0
- package/dist/context/RemoteAuthContext.d.ts +2 -2
- package/dist/index.d.ts +8 -7
- package/dist/index.js +1 -1
- package/dist/index.js.LICENSE.txt +2 -0
- package/package.json +2 -1
- package/src/components/DevButton.tsx +61 -0
- package/src/components/DevCard.tsx +25 -0
- package/src/components/DevWrapper.tsx +128 -0
- package/src/components/FloatingTokenButton.tsx +36 -0
- package/src/components/RemoteWrapper.tsx +5 -9
- package/src/components/TokenGenerator.tsx +227 -0
- package/src/components/TokenInput.tsx +103 -0
- package/src/context/RemoteAuthContext.tsx +14 -0
- package/src/index.ts +18 -17
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DevButton.tsx
|
|
3
|
+
*
|
|
4
|
+
* Componente de botón simple para componentes de desarrollo (DevWrapper, TokenInput, etc.)
|
|
5
|
+
* No depende de Aurora Web para facilitar la separación del código de desarrollo.
|
|
6
|
+
*/
|
|
7
|
+
import React from "react";
|
|
8
|
+
type ButtonVariant = "primary" | "secondary" | "text";
|
|
9
|
+
type ButtonSize = "small" | "medium" | "large";
|
|
10
|
+
interface DevButtonProps {
|
|
11
|
+
label: string;
|
|
12
|
+
onClick?: () => void;
|
|
13
|
+
disabled?: boolean;
|
|
14
|
+
variant?: ButtonVariant;
|
|
15
|
+
size?: ButtonSize;
|
|
16
|
+
className?: string;
|
|
17
|
+
type?: "button" | "submit" | "reset";
|
|
18
|
+
}
|
|
19
|
+
declare const DevButton: React.FC<DevButtonProps>;
|
|
20
|
+
export default DevButton;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DevCard.tsx
|
|
3
|
+
*
|
|
4
|
+
* Componente de tarjeta simple para componentes de desarrollo.
|
|
5
|
+
* Reemplaza EmptyCard de Aurora Web para eliminar dependencias.
|
|
6
|
+
*/
|
|
7
|
+
import React from "react";
|
|
8
|
+
interface DevCardProps {
|
|
9
|
+
children: React.ReactNode;
|
|
10
|
+
className?: string;
|
|
11
|
+
}
|
|
12
|
+
declare const DevCard: React.FC<DevCardProps>;
|
|
13
|
+
export default DevCard;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import React from
|
|
2
|
-
import { AuthProviderProps } from
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { AuthProviderProps } from "../types";
|
|
3
3
|
/**
|
|
4
4
|
* Wrapper component that provides authentication context to remote components
|
|
5
5
|
* This is the main component that remote applications should use
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
interface TokenGeneratorProps {
|
|
3
|
+
onSubmit: (token: string) => void;
|
|
4
|
+
onCancel: () => void;
|
|
5
|
+
onClearStorage: () => void;
|
|
6
|
+
currentToken?: string;
|
|
7
|
+
jsonExample?: string;
|
|
8
|
+
defaultDevRoles?: string[];
|
|
9
|
+
}
|
|
10
|
+
declare const TokenGenerator: React.FC<TokenGeneratorProps>;
|
|
11
|
+
export default TokenGenerator;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import React, { ReactNode } from
|
|
2
|
-
import { RemoteAuthContextValue } from
|
|
1
|
+
import React, { ReactNode } from "react";
|
|
2
|
+
import { RemoteAuthContextValue } from "../types";
|
|
3
3
|
interface RemoteAuthProviderProps {
|
|
4
4
|
children: ReactNode;
|
|
5
5
|
initialToken?: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
export { RemoteAuthProvider, useRemoteAuth } from
|
|
2
|
-
export { Secured } from
|
|
3
|
-
export { RemoteWrapper } from
|
|
4
|
-
export {
|
|
5
|
-
export {
|
|
6
|
-
export {
|
|
7
|
-
export
|
|
1
|
+
export { RemoteAuthProvider, useRemoteAuth } from "./context/RemoteAuthContext";
|
|
2
|
+
export { Secured } from "./components/Secured";
|
|
3
|
+
export { RemoteWrapper } from "./components/RemoteWrapper";
|
|
4
|
+
export { default as DevWrapper } from "./components/DevWrapper";
|
|
5
|
+
export { useRemoteConfig } from "./hooks/useRemoteConfig";
|
|
6
|
+
export { decodeToken, isTokenExpired, extractPermissions, tokenStorage, } from "./utils/tokenUtils";
|
|
7
|
+
export { loadRemoteModule, isRemoteAvailable, createRemoteComponentLoader, getRemoteEnvironment, } from "./utils/moduleUtils";
|
|
8
|
+
export type { RemoteAuthState, RemoteAuthContextValue, AuthProviderProps, SecuredProps, TokenInfo, RemoteConfig, SharedDependencies, } from "./types";
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/*! For license information please see index.js.LICENSE.txt */
|
|
2
|
-
(()=>{var e={20:(e,t,o)=>{"use strict";var r=o(953),n=Symbol.for("react.element"),s=Symbol.for("react.fragment"),a=Object.prototype.hasOwnProperty,i=r.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,l={key:!0,ref:!0,__self:!0,__source:!0};t.Fragment=s,t.jsx=function(e,t,o){var r,s={},c=null,d=null;for(r in void 0!==o&&(c=""+o),void 0!==t.key&&(c=""+t.key),void 0!==t.ref&&(d=t.ref),t)a.call(t,r)&&!l.hasOwnProperty(r)&&(s[r]=t[r]);if(e&&e.defaultProps)for(r in t=e.defaultProps)void 0===s[r]&&(s[r]=t[r]);return{$$typeof:n,type:e,key:c,ref:d,props:s,_owner:i.current}}},60:(e,t,o)=>{var r={"./moduleUtils":226,"./moduleUtils.ts":226,"./tokenUtils":433,"./tokenUtils.ts":433};function n(e){return Promise.resolve().then(()=>{if(!o.o(r,e)){var t=new Error("Cannot find module '"+e+"'");throw t.code="MODULE_NOT_FOUND",t}return o(r[e])})}n.keys=()=>Object.keys(r),n.id=60,e.exports=n},226:(e,t,o)=>{"use strict";o.r(t),o.d(t,{createRemoteComponentLoader:()=>i,getRemoteEnvironment:()=>l,isRemoteAvailable:()=>a,loadRemoteModule:()=>s});var r=o(953),n=o.n(r);const s=async(e,t)=>{try{return await o(60)(`${e}/${t}`)}catch(o){throw console.error(`Failed to load remote module ${e}/${t}:`,o),o}},a=async e=>{try{const t=window[e];return"function"==typeof t?.get}catch(e){return!1}},i=(e,t,o)=>n().lazy(async()=>{try{return await s(e,t)}catch(r){return console.error(`Remote component loading failed: ${e}/${t}`,r),o?{default:o}:{default:()=>n().createElement("div",{style:{padding:"20px",color:"red"}},`Failed to load remote component: ${e}/${t}`)}}}),l=()=>({isDevelopment:!1,apiBaseUrl:process.env.REACT_APP_API_URL||"",frontUrl:process.env.REACT_APP_FRONT_URL||""})},433:(e,t,o)=>{"use strict";o.r(t),o.d(t,{decodeToken:()=>r,extractPermissions:()=>s,isTokenExpired:()=>n,tokenStorage:()=>a});const r=e=>{try{if(!e)return null;const t=e.split(".");if(3!==t.length)return null;const o=t[1],r=o+"=".repeat((4-o.length%4)%4),n=atob(r);return JSON.parse(n)}catch(e){return console.error("Error decoding token:",e),null}},n=e=>{const t=r(e);if(!t||!t.exp)return!0;const o=Math.floor(Date.now()/1e3);return t.exp<o},s=e=>{if(!e)return[];const t=["permissions","roles","authorities","scope"];for(const o of t){const t=e[o];if(Array.isArray(t))return t;if("string"==typeof t)return t.split(" ").filter(Boolean)}return[]},a={key:"bo-remote-token",save:e=>{try{sessionStorage.setItem(a.key,e)}catch(e){console.error("Error saving token:",e)}},get:()=>{try{return sessionStorage.getItem(a.key)}catch(e){return console.error("Error getting token:",e),null}},remove:()=>{try{sessionStorage.removeItem(a.key)}catch(e){console.error("Error removing token:",e)}},clear:()=>{try{sessionStorage.clear()}catch(e){console.error("Error clearing storage:",e)}}}},848:(e,t,o)=>{"use strict";e.exports=o(20)},953:e=>{"use strict";e.exports=require("react")}},t={};function o(r){var n=t[r];if(void 0!==n)return n.exports;var s=t[r]={exports:{}};return e[r](s,s.exports,o),s.exports}o.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return o.d(t,{a:t}),t},o.d=(e,t)=>{for(var r in t)o.o(t,r)&&!o.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},o.e=()=>Promise.resolve(),o.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),o.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var r={};(()=>{"use strict";o.r(r),o.d(r,{RemoteAuthProvider:()=>l,RemoteWrapper:()=>u,Secured:()=>d,createRemoteComponentLoader:()=>p.createRemoteComponentLoader,decodeToken:()=>n.decodeToken,extractPermissions:()=>n.extractPermissions,getRemoteEnvironment:()=>p.getRemoteEnvironment,isRemoteAvailable:()=>p.isRemoteAvailable,isTokenExpired:()=>n.isTokenExpired,loadRemoteModule:()=>p.loadRemoteModule,tokenStorage:()=>n.tokenStorage,useRemoteAuth:()=>c,useRemoteConfig:()=>m});var e=o(848),t=o(953),n=o(433);const s={token:null,isAuthenticated:!1,permissions:[],user:void 0},a=(e,t)=>{switch(t.type){case"SET_TOKEN":const o=(0,n.decodeToken)(t.payload),r=o?(0,n.extractPermissions)(o):[];return{...e,token:t.payload,isAuthenticated:!(0,n.isTokenExpired)(t.payload),permissions:r,user:o?{id:o.sub||"",email:o.email||"",name:o.name||o.email||""}:void 0};case"CLEAR_AUTH":return{...s};case"SET_USER_INFO":return{...e,user:{id:t.payload.sub||"",email:t.payload.email||"",name:t.payload.name||t.payload.email||""}};default:return e}},i=(0,t.createContext)(void 0),l=({children:o,initialToken:r})=>{const[l,c]=(0,t.useReducer)(a,s);(0,t.useEffect)(()=>{if(console.log("🔍 RemoteAuthContext: useEffect triggered",{initialToken:r?r.substring(0,30)+"...":"No initialToken"}),r)console.log("✅ RemoteAuthContext: Using initialToken from prop"),(0,n.isTokenExpired)(r)?(console.log("🔴 RemoteAuthContext: Provided token is expired, clearing auth"),n.tokenStorage.remove(),c({type:"CLEAR_AUTH"})):(console.log("🟢 RemoteAuthContext: Token is valid, dispatching SET_TOKEN"),c({type:"SET_TOKEN",payload:r}),n.tokenStorage.save(r));else{console.log("⚪ RemoteAuthContext: No initialToken, checking stored token");const e=n.tokenStorage.get();e&&!(0,n.isTokenExpired)(e)?(console.log("💾 RemoteAuthContext: Using stored token"),c({type:"SET_TOKEN",payload:e})):e&&(console.log("🗑️ RemoteAuthContext: Stored token expired, clearing"),n.tokenStorage.remove(),c({type:"CLEAR_AUTH"}))}},[r]);const d={state:l,setToken:e=>{e&&(n.tokenStorage.save(e),c({type:"SET_TOKEN",payload:e}))},clearAuth:()=>{n.tokenStorage.remove(),c({type:"CLEAR_AUTH"})},hasPermission:e=>!!l.isAuthenticated&&l.permissions.includes(e)};return(0,e.jsx)(i.Provider,{value:d,children:o})},c=()=>{const e=(0,t.useContext)(i);if(void 0===e)throw new Error("useRemoteAuth must be used within a RemoteAuthProvider");return e},d=({children:t,permission:o,fallback:r=null})=>{const{hasPermission:n,state:s}=c();return s.isAuthenticated&&n(o)?(0,e.jsx)(e.Fragment,{children:t}):(0,e.jsx)(e.Fragment,{children:r})},u=({children:t,token:o,apiBaseUrl:r,module:n})=>(0,e.jsx)(l,{initialToken:o,children:(0,e.jsx)("div",{"data-remote-module":n,"data-api-base":r,children:t})}),m=()=>{const e=(0,t.useMemo)(()=>({react:{singleton:!0,eager:!1},"react-dom":{singleton:!0,eager:!1},"aurora-web":{singleton:!0,eager:!1},"react-i18next":{singleton:!0,eager:!1},i18next:{singleton:!0,eager:!1}}),[]);return{sharedDependencies:e,getWebpackConfig:(t,o)=>({name:t,filename:"remoteEntry.js",exposes:o,shared:e})}};var p=o(226)})(),module.exports=r})();
|
|
2
|
+
(()=>{var e={20:(e,t,r)=>{"use strict";var o=r(953),n=Symbol.for("react.element"),a=Symbol.for("react.fragment"),s=Object.prototype.hasOwnProperty,i=o.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,l={key:!0,ref:!0,__self:!0,__source:!0};function c(e,t,r){var o,a={},c=null,d=null;for(o in void 0!==r&&(c=""+r),void 0!==t.key&&(c=""+t.key),void 0!==t.ref&&(d=t.ref),t)s.call(t,o)&&!l.hasOwnProperty(o)&&(a[o]=t[o]);if(e&&e.defaultProps)for(o in t=e.defaultProps)void 0===a[o]&&(a[o]=t[o]);return{$$typeof:n,type:e,key:c,ref:d,props:a,_owner:i.current}}t.Fragment=a,t.jsx=c,t.jsxs=c},60:(e,t,r)=>{var o={"./moduleUtils":226,"./moduleUtils.ts":226,"./tokenUtils":433,"./tokenUtils.ts":433};function n(e){return Promise.resolve().then(()=>{if(!r.o(o,e)){var t=new Error("Cannot find module '"+e+"'");throw t.code="MODULE_NOT_FOUND",t}return r(o[e])})}n.keys=()=>Object.keys(o),n.id=60,e.exports=n},87:(e,t,r)=>{(()=>{var t={20:(e,t,r)=>{"use strict";var o=r(953),n=Symbol.for("react.element"),a=Symbol.for("react.fragment"),s=Object.prototype.hasOwnProperty,i=o.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,l={key:!0,ref:!0,__self:!0,__source:!0};t.Fragment=a,t.jsx=function(e,t,r){var o,a={},c=null,d=null;for(o in void 0!==r&&(c=""+r),void 0!==t.key&&(c=""+t.key),void 0!==t.ref&&(d=t.ref),t)s.call(t,o)&&!l.hasOwnProperty(o)&&(a[o]=t[o]);if(e&&e.defaultProps)for(o in t=e.defaultProps)void 0===a[o]&&(a[o]=t[o]);return{$$typeof:n,type:e,key:c,ref:d,props:a,_owner:i.current}}},60:(e,t,r)=>{var o={"./moduleUtils":226,"./moduleUtils.ts":226,"./tokenUtils":433,"./tokenUtils.ts":433};function n(e){return Promise.resolve().then(()=>{if(!r.o(o,e)){var t=new Error("Cannot find module '"+e+"'");throw t.code="MODULE_NOT_FOUND",t}return r(o[e])})}n.keys=()=>Object.keys(o),n.id=60,e.exports=n},226:(e,t,r)=>{"use strict";r.r(t),r.d(t,{createRemoteComponentLoader:()=>i,getRemoteEnvironment:()=>l,isRemoteAvailable:()=>s,loadRemoteModule:()=>a});var o=r(953),n=r.n(o);const a=async(e,t)=>{try{return await r(60)(`${e}/${t}`)}catch(r){throw console.error(`Failed to load remote module ${e}/${t}:`,r),r}},s=async e=>{try{const t=window[e];return"function"==typeof t?.get}catch(e){return!1}},i=(e,t,r)=>n().lazy(async()=>{try{return await a(e,t)}catch(o){return console.error(`Remote component loading failed: ${e}/${t}`,o),r?{default:r}:{default:()=>n().createElement("div",{style:{padding:"20px",color:"red"}},`Failed to load remote component: ${e}/${t}`)}}}),l=()=>({isDevelopment:!1,apiBaseUrl:process.env.REACT_APP_API_URL||"",frontUrl:process.env.REACT_APP_FRONT_URL||""})},433:(e,t,r)=>{"use strict";r.r(t),r.d(t,{decodeToken:()=>o,extractPermissions:()=>a,isTokenExpired:()=>n,tokenStorage:()=>s});const o=e=>{try{if(!e)return null;const t=e.split(".");if(3!==t.length)return null;const r=t[1],o=r+"=".repeat((4-r.length%4)%4),n=atob(o);return JSON.parse(n)}catch(e){return console.error("Error decoding token:",e),null}},n=e=>{const t=o(e);if(!t||!t.exp)return!0;const r=Math.floor(Date.now()/1e3);return t.exp<r},a=e=>{if(!e)return[];const t=["permissions","roles","authorities","scope"];for(const r of t){const t=e[r];if(Array.isArray(t))return t;if("string"==typeof t)return t.split(" ").filter(Boolean)}return[]},s={key:"bo-remote-token",save:e=>{try{sessionStorage.setItem(s.key,e)}catch(e){console.error("Error saving token:",e)}},get:()=>{try{return sessionStorage.getItem(s.key)}catch(e){return console.error("Error getting token:",e),null}},remove:()=>{try{sessionStorage.removeItem(s.key)}catch(e){console.error("Error removing token:",e)}},clear:()=>{try{sessionStorage.clear()}catch(e){console.error("Error clearing storage:",e)}}}},848:(e,t,r)=>{"use strict";e.exports=r(20)},953:e=>{"use strict";e.exports=r(953)}},o={};function n(e){var r=o[e];if(void 0!==r)return r.exports;var a=o[e]={exports:{}};return t[e](a,a.exports,n),a.exports}n.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return n.d(t,{a:t}),t},n.d=(e,t)=>{for(var r in t)n.o(t,r)&&!n.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},n.e=()=>Promise.resolve(),n.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),n.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var a={};(()=>{"use strict";n.r(a),n.d(a,{RemoteAuthProvider:()=>l,RemoteWrapper:()=>u,Secured:()=>d,createRemoteComponentLoader:()=>g.createRemoteComponentLoader,decodeToken:()=>r.decodeToken,extractPermissions:()=>r.extractPermissions,getRemoteEnvironment:()=>g.getRemoteEnvironment,isRemoteAvailable:()=>g.isRemoteAvailable,isTokenExpired:()=>r.isTokenExpired,loadRemoteModule:()=>g.loadRemoteModule,tokenStorage:()=>r.tokenStorage,useRemoteAuth:()=>c,useRemoteConfig:()=>m});var e=n(848),t=n(953),r=n(433);const o={token:null,isAuthenticated:!1,permissions:[],user:void 0},s=(e,t)=>{switch(t.type){case"SET_TOKEN":const n=(0,r.decodeToken)(t.payload),a=n?(0,r.extractPermissions)(n):[];return{...e,token:t.payload,isAuthenticated:!(0,r.isTokenExpired)(t.payload),permissions:a,user:n?{id:n.sub||"",email:n.email||"",name:n.name||n.email||""}:void 0};case"CLEAR_AUTH":return{...o};case"SET_USER_INFO":return{...e,user:{id:t.payload.sub||"",email:t.payload.email||"",name:t.payload.name||t.payload.email||""}};default:return e}},i=(0,t.createContext)(void 0),l=({children:n,initialToken:a})=>{const[l,c]=(0,t.useReducer)(s,o);(0,t.useEffect)(()=>{if(console.log("🔍 RemoteAuthContext: useEffect triggered",{initialToken:a?a.substring(0,30)+"...":"No initialToken"}),a)console.log("✅ RemoteAuthContext: Using initialToken from prop"),(0,r.isTokenExpired)(a)?(console.log("🔴 RemoteAuthContext: Provided token is expired, clearing auth"),r.tokenStorage.remove(),c({type:"CLEAR_AUTH"})):(console.log("🟢 RemoteAuthContext: Token is valid, dispatching SET_TOKEN"),c({type:"SET_TOKEN",payload:a}),r.tokenStorage.save(a));else{console.log("⚪ RemoteAuthContext: No initialToken, checking stored token");const e=r.tokenStorage.get();e&&!(0,r.isTokenExpired)(e)?(console.log("💾 RemoteAuthContext: Using stored token"),c({type:"SET_TOKEN",payload:e})):e&&(console.log("🗑️ RemoteAuthContext: Stored token expired, clearing"),r.tokenStorage.remove(),c({type:"CLEAR_AUTH"}))}},[a]);const d={state:l,setToken:e=>{e&&(r.tokenStorage.save(e),c({type:"SET_TOKEN",payload:e}))},clearAuth:()=>{r.tokenStorage.remove(),c({type:"CLEAR_AUTH"})},hasPermission:e=>!!l.isAuthenticated&&l.permissions.includes(e)};return(0,e.jsx)(i.Provider,{value:d,children:n})},c=()=>{const e=(0,t.useContext)(i);if(void 0===e)throw new Error("useRemoteAuth must be used within a RemoteAuthProvider");return e},d=({children:t,permission:r,fallback:o=null})=>{const{hasPermission:n,state:a}=c();return a.isAuthenticated&&n(r)?(0,e.jsx)(e.Fragment,{children:t}):(0,e.jsx)(e.Fragment,{children:o})},u=({children:t,token:r,apiBaseUrl:o,module:n})=>(0,e.jsx)(l,{initialToken:r,children:(0,e.jsx)("div",{"data-remote-module":n,"data-api-base":o,children:t})}),m=()=>{const e=(0,t.useMemo)(()=>({react:{singleton:!0,eager:!1},"react-dom":{singleton:!0,eager:!1},"aurora-web":{singleton:!0,eager:!1},"react-i18next":{singleton:!0,eager:!1},i18next:{singleton:!0,eager:!1}}),[]);return{sharedDependencies:e,getWebpackConfig:(t,r)=>({name:t,filename:"remoteEntry.js",exposes:r,shared:e})}};var g=n(226)})(),e.exports=a})()},226:(e,t,r)=>{"use strict";r.r(t),r.d(t,{createRemoteComponentLoader:()=>i,getRemoteEnvironment:()=>l,isRemoteAvailable:()=>s,loadRemoteModule:()=>a});var o=r(953),n=r.n(o);const a=async(e,t)=>{try{return await r(60)(`${e}/${t}`)}catch(r){throw console.error(`Failed to load remote module ${e}/${t}:`,r),r}},s=async e=>{try{const t=window[e];return"function"==typeof t?.get}catch(e){return!1}},i=(e,t,r)=>n().lazy(async()=>{try{return await a(e,t)}catch(o){return console.error(`Remote component loading failed: ${e}/${t}`,o),r?{default:r}:{default:()=>n().createElement("div",{style:{padding:"20px",color:"red"}},`Failed to load remote component: ${e}/${t}`)}}}),l=()=>({isDevelopment:!1,apiBaseUrl:process.env.REACT_APP_API_URL||"",frontUrl:process.env.REACT_APP_FRONT_URL||""})},433:(e,t,r)=>{"use strict";r.r(t),r.d(t,{decodeToken:()=>o,extractPermissions:()=>a,isTokenExpired:()=>n,tokenStorage:()=>s});const o=e=>{try{if(!e)return null;const t=e.split(".");if(3!==t.length)return null;const r=t[1],o=r+"=".repeat((4-r.length%4)%4),n=atob(o);return JSON.parse(n)}catch(e){return console.error("Error decoding token:",e),null}},n=e=>{const t=o(e);if(!t||!t.exp)return!0;const r=Math.floor(Date.now()/1e3);return t.exp<r},a=e=>{if(!e)return[];const t=["permissions","roles","authorities","scope"];for(const r of t){const t=e[r];if(Array.isArray(t))return t;if("string"==typeof t)return t.split(" ").filter(Boolean)}return[]},s={key:"bo-remote-token",save:e=>{try{sessionStorage.setItem(s.key,e)}catch(e){console.error("Error saving token:",e)}},get:()=>{try{return sessionStorage.getItem(s.key)}catch(e){return console.error("Error getting token:",e),null}},remove:()=>{try{sessionStorage.removeItem(s.key)}catch(e){console.error("Error removing token:",e)}},clear:()=>{try{sessionStorage.clear()}catch(e){console.error("Error clearing storage:",e)}}}},848:(e,t,r)=>{"use strict";e.exports=r(20)},953:e=>{"use strict";e.exports=require("react")}},t={};function r(o){var n=t[o];if(void 0!==n)return n.exports;var a=t[o]={exports:{}};return e[o](a,a.exports,r),a.exports}r.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return r.d(t,{a:t}),t},r.d=(e,t)=>{for(var o in t)r.o(t,o)&&!r.o(e,o)&&Object.defineProperty(e,o,{enumerable:!0,get:t[o]})},r.e=()=>Promise.resolve(),r.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var o={};(()=>{"use strict";r.r(o),r.d(o,{DevWrapper:()=>y,RemoteAuthProvider:()=>l,RemoteWrapper:()=>u,Secured:()=>d,createRemoteComponentLoader:()=>b.createRemoteComponentLoader,decodeToken:()=>n.decodeToken,extractPermissions:()=>n.extractPermissions,getRemoteEnvironment:()=>b.getRemoteEnvironment,isRemoteAvailable:()=>b.isRemoteAvailable,isTokenExpired:()=>n.isTokenExpired,loadRemoteModule:()=>b.loadRemoteModule,tokenStorage:()=>n.tokenStorage,useRemoteAuth:()=>c,useRemoteConfig:()=>v});var e=r(848),t=r(953),n=r(433);const a={token:null,isAuthenticated:!1,permissions:[],user:void 0},s=(e,t)=>{switch(t.type){case"SET_TOKEN":const r=(0,n.decodeToken)(t.payload),o=r?(0,n.extractPermissions)(r):[];return{...e,token:t.payload,isAuthenticated:!(0,n.isTokenExpired)(t.payload),permissions:o,user:r?{id:r.sub||"",email:r.email||"",name:r.name||r.email||""}:void 0};case"CLEAR_AUTH":return{...a};case"SET_USER_INFO":return{...e,user:{id:t.payload.sub||"",email:t.payload.email||"",name:t.payload.name||t.payload.email||""}};default:return e}},i=(0,t.createContext)(void 0),l=({children:r,initialToken:o})=>{const[l,c]=(0,t.useReducer)(s,a),d="dev"===process.env.REACT_APP_ENVIRONMENT?console.log:()=>{};(0,t.useEffect)(()=>{if(d("🔍 RemoteAuthContext: useEffect triggered",{initialToken:o?o.substring(0,30)+"...":"No initialToken"}),o)d("✅ RemoteAuthContext: Using initialToken from prop"),(0,n.isTokenExpired)(o)?(d("🔴 RemoteAuthContext: Provided token is expired, clearing auth"),n.tokenStorage.remove(),c({type:"CLEAR_AUTH"})):(d("🟢 RemoteAuthContext: Token is valid, dispatching SET_TOKEN"),c({type:"SET_TOKEN",payload:o}),n.tokenStorage.save(o));else{d("⚪ RemoteAuthContext: No initialToken, checking stored token");const e=n.tokenStorage.get();e&&!(0,n.isTokenExpired)(e)?(d("💾 RemoteAuthContext: Using stored token"),c({type:"SET_TOKEN",payload:e})):e&&(d("🗑️ RemoteAuthContext: Stored token expired, clearing"),n.tokenStorage.remove(),c({type:"CLEAR_AUTH"}))}},[o]);const u={state:l,setToken:e=>{e&&(n.tokenStorage.save(e),c({type:"SET_TOKEN",payload:e}))},clearAuth:()=>{n.tokenStorage.remove(),c({type:"CLEAR_AUTH"})},hasPermission:e=>!!l.isAuthenticated&&l.permissions.includes(e)};return(0,e.jsx)(i.Provider,{value:u,children:r})},c=()=>{const e=(0,t.useContext)(i);if(void 0===e)throw new Error("useRemoteAuth must be used within a RemoteAuthProvider");return e},d=({children:t,permission:r,fallback:o=null})=>{const{hasPermission:n,state:a}=c();return a.isAuthenticated&&n(r)?(0,e.jsx)(e.Fragment,{children:t}):(0,e.jsx)(e.Fragment,{children:o})},u=({children:t,token:r})=>(0,e.jsx)(l,{initialToken:r,children:(0,e.jsx)("div",{children:t})});var m=r(87);const g=({children:t,className:r=""})=>(0,e.jsx)("div",{className:`bg-white rounded-lg shadow-md border border-gray-20 ${r}`,children:t}),p=({label:t,onClick:r,disabled:o=!1,variant:n="primary",size:a="medium",className:s="",type:i="button"})=>(0,e.jsx)("button",{type:i,onClick:r,disabled:o,className:`rounded-lg font-medium transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed ${{primary:"bg-gradient-to-br from-blue-40 to-blue-70 hover:from-blue-50 hover:to-blue-90 text-white focus:ring-blue-50 shadow-sm hover:shadow-md",secondary:"bg-gray-20 hover:bg-gray-30 text-gray-80 focus:ring-gray-40 border border-gray-30",text:"bg-transparent hover:bg-gray-10 text-blue-60 hover:text-blue-80 focus:ring-blue-30"}[n]} ${{small:"px-4 py-2 text-sm",medium:"px-5 py-2.5 text-base",large:"px-6 py-3 text-lg"}[a]} ${s}`,children:t}),x=({onSubmit:r,onCancel:o,onClearStorage:n,defaultDevRoles:a=[],currentToken:s=""})=>{const[i,l]=(0,t.useState)(!1),[c,d]=(0,t.useState)(""),[u,m]=(0,t.useState)({user:"usuario@bdsol.com.ar",roles:a,channel:"mf-testing",sessionId:crypto.randomUUID()}),[x,h]=(0,t.useState)(a.join("\n")),f=(e,t)=>{m(r=>({...r,[e]:t}))};return(0,e.jsx)("div",{className:"p-6 w-full overflow-y-auto",children:(0,e.jsx)(g,{className:"p-6",children:(0,e.jsxs)(e.Fragment,{children:[(0,e.jsx)("h2",{className:"text-2xl font-bold text-gray-80 mb-4",children:"🔧 Generador de Tokens (Desarrollo)"}),(0,e.jsx)("p",{className:"text-gray-60 mb-6",children:"Genera un token JWT para desarrollo usando el BFF. Solo disponible cuando ENVIRONMENT=dev."}),c&&(0,e.jsx)("div",{className:"mb-4 p-3 bg-red-10 border border-red-40 text-red-70 rounded",children:c}),(0,e.jsxs)("div",{className:"space-y-4 mb-6",children:[(0,e.jsxs)("div",{children:[(0,e.jsx)("label",{className:"block text-sm font-medium text-gray-70 mb-1",children:"Email del usuario"}),(0,e.jsx)("input",{type:"email",value:u.user,onChange:e=>f("user",e.target.value),className:"w-full p-2 border border-gray-30 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-50",placeholder:"usuario@bdsol.com.ar"})]}),(0,e.jsxs)("div",{children:[(0,e.jsx)("label",{className:"block text-sm font-medium text-gray-70 mb-1",children:"Roles (uno por línea)"}),(0,e.jsx)("textarea",{value:x,onChange:e=>h(e.target.value),className:"w-full h-32 p-2 border border-gray-30 rounded-lg font-mono text-sm focus:outline-none focus:ring-2 focus:ring-blue-50",placeholder:a.join(" ")}),(0,e.jsx)("p",{className:"text-xs text-gray-50 mt-1",children:"Roles definidos en src/config/roles.ts"})]}),(0,e.jsxs)("div",{children:[(0,e.jsx)("label",{className:"block text-sm font-medium text-gray-70 mb-1",children:"Channel"}),(0,e.jsx)("input",{type:"text",value:u.channel,onChange:e=>f("channel",e.target.value),className:"w-full p-2 border border-gray-30 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-50",placeholder:"mf-testing"})]}),(0,e.jsxs)("div",{children:[(0,e.jsx)("label",{className:"block text-sm font-medium text-gray-70 mb-1",children:"Session ID"}),(0,e.jsxs)("div",{className:"flex gap-2",children:[(0,e.jsx)("input",{type:"text",value:u.sessionId,onChange:e=>f("sessionId",e.target.value),className:"flex-1 p-2 border border-gray-30 rounded-lg font-mono text-sm focus:outline-none focus:ring-2 focus:ring-blue-50",placeholder:"uuid-session-id"}),(0,e.jsx)(p,{label:"🎲",variant:"primary",size:"small",onClick:()=>{m(e=>({...e,sessionId:crypto.randomUUID()}))},className:"px-3"})]})]})]}),(0,e.jsxs)("div",{className:"flex gap-3 flex-wrap mb-4",children:[(0,e.jsx)(p,{label:i?"Generando...":"🚀 Generar Token",variant:"primary",size:"small",onClick:async()=>{l(!0),d(""),n();try{const e=x.split("\n").map(e=>e.trim()).filter(e=>e.length>0),t={...u,roles:e},o=process.env.REACT_APP_API_URL||"http://localhost:3020",n=await fetch(`${o}/template/api/dev/generate-token`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)});if(!n.ok){const e=await n.json();throw new Error(e.error||"Error al generar token")}const a=await n.json();r(a.token)}catch(e){d(e instanceof Error?e.message:"Error desconocido")}finally{l(!1)}},disabled:i||!u.user.trim()}),(0,e.jsx)(p,{label:"Limpiar almacenamiento",variant:"primary",size:"small",onClick:()=>{n(),alert("SessionStorage limpiado correctamente")}}),(0,e.jsx)(p,{label:"Cancelar",size:"small",variant:"secondary",onClick:o})]}),s&&(0,e.jsxs)("div",{className:"mt-4 p-4 bg-green-50 rounded-lg",children:[(0,e.jsx)("h3",{className:"font-semibold text-green-70 mb-2",children:"✅ Token cargado"}),(0,e.jsx)("p",{className:"text-sm text-green-60",children:"Tienes un token activo en el almacenamiento local."})]})," "]})})})},h=({onSubmit:r,onCancel:o,onClearStorage:n,currentToken:a=""})=>{const[s,i]=(0,t.useState)(a),[l,c]=(0,t.useState)(!0);return(0,e.jsxs)("div",{className:"bg-white rounded-lg shadow-lg p-6 w-full max-w-2xl",children:[(0,e.jsx)("h2",{className:"text-2xl font-bold text-gray-80 mb-4",children:"Modo Desarrollo - Cargar Token JWT"}),(0,e.jsx)("p",{className:"text-gray-60 mb-6",children:"Ingresa el token JWT decodificado (JSON) que normalmente recibiría desde el host:"}),(0,e.jsxs)("div",{className:"mb-4",children:[(0,e.jsx)("textarea",{value:s,onChange:e=>{const t=e.target.value;i(t),l||c(!0)},placeholder:"Pegue aquí el token JWT en formato JSON",className:`w-full h-40 p-3 border rounded-lg font-mono text-sm ${l?"border-gray-30":"border-red-50"} focus:outline-none focus:ring-2 focus:ring-blue-50`}),!l&&(0,e.jsx)("p",{className:"text-red-50 text-sm mt-2",children:"El JSON no es válido. Por favor verifica la sintaxis."})]}),(0,e.jsxs)("div",{className:"flex gap-3 flex-wrap",children:[(0,e.jsx)(p,{label:"Cargar Token",variant:"primary",size:"small",onClick:()=>{try{JSON.parse(s),c(!0),r(s)}catch(e){c(!1)}},disabled:!s.trim()}),(0,e.jsx)(p,{label:"Limpiar",size:"small",variant:"text",onClick:()=>i("")}),(0,e.jsx)(p,{label:"Limpiar Storage",variant:"primary",size:"small",onClick:()=>{n(),i(""),alert("SessionStorage limpiado correctamente")}}),(0,e.jsx)(p,{label:"Cancelar",variant:"secondary",size:"small",onClick:o})]})]})},f=({onClick:t})=>(0,e.jsxs)("button",{onClick:t,className:"fixed bottom-6 right-6 bg-gradient-to-br from-blue-40 to-blue-70 hover:from-blue-50 hover:to-blue-90 text-white p-4 rounded-full shadow-dropdown hover:shadow-2xl transition-all duration-300 z-50 group transform hover:scale-110",title:"Recargar Token",children:[(0,e.jsx)("svg",{className:"w-5 h-5",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:(0,e.jsx)("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-2.586a1 1 0 01.293-.707l5.964-5.964A6 6 0 1121 9z"})}),(0,e.jsx)("span",{className:"absolute bottom-full right-0 mb-3 px-3 py-1 text-xs text-white bg-gray-90/90 rounded-lg opacity-0 group-hover:opacity-100 transition-opacity duration-200 whitespace-nowrap backdrop-blur-sm",children:"Recargar Token"})]}),y=({children:r})=>{const[o,n]=(0,t.useState)(""),[a,s]=(0,t.useState)(!1),[i,l]=(0,t.useState)(!1),[c,d]=(0,t.useState)(!0),u="dev"===process.env.REACT_APP_ENVIRONMENT&&!o;(0,t.useEffect)(()=>{const e=m.tokenStorage.get();e?(n(e),l(!1)):u&&l(!0)},[u]);const g=e=>{console.log("🔄 DevWrapper: Nuevo token generado",{token:e.substring(0,50)+"..."}),n(e),m.tokenStorage.save(e),console.log("💾 DevWrapper: Token guardado en storage"),s(!1),l(!1)},p=()=>{m.tokenStorage.clear(),n(""),l(u)};return i?(0,e.jsx)("div",{className:"min-h-screen bg-gray-50 flex items-center justify-center p-4",children:(0,e.jsx)(x,{onSubmit:g,onCancel:()=>l(!1),onClearStorage:p,currentToken:o})}):(0,e.jsxs)("div",{className:"min-h-screen bg-gray-50",children:[a&&(0,e.jsx)("div",{className:"fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50",children:(0,e.jsxs)("div",{className:"bg-white rounded-lg shadow-lg max-w-2xl w-full mx-4 max-h-[90vh] overflow-y-auto",children:[(0,e.jsxs)("div",{className:"flex border-b",children:[(0,e.jsx)("button",{onClick:()=>d(!0),className:"flex-1 py-3 px-4 text-sm font-medium "+(c?"bg-blue-50 ":"bg-gray-10 text-gray-70 hover:bg-gray-20"),children:"Generar Token"}),(0,e.jsx)("button",{onClick:()=>d(!1),className:"flex-1 py-3 px-4 text-sm font-medium "+(c?"bg-gray-10 text-gray-70 hover:bg-gray-20":"bg-blue-50 text-gray-50"),children:"Input Manual"})]}),(0,e.jsx)("div",{className:"p-0",children:c?(0,e.jsx)(x,{onSubmit:g,onCancel:()=>s(!1),onClearStorage:p,currentToken:o}):(0,e.jsx)("div",{className:"p-6",children:(0,e.jsx)(h,{onSubmit:g,onCancel:()=>s(!1),onClearStorage:p,currentToken:o})})})]})}),(0,e.jsx)(f,{onClick:()=>{s(!a)}}),(0,e.jsx)(m.RemoteWrapper,{token:o,children:r})]})},v=()=>{const e=(0,t.useMemo)(()=>({react:{singleton:!0,eager:!1},"react-dom":{singleton:!0,eager:!1},"aurora-web":{singleton:!0,eager:!1},"react-i18next":{singleton:!0,eager:!1},i18next:{singleton:!0,eager:!1}}),[]);return{sharedDependencies:e,getWebpackConfig:(t,r)=>({name:t,filename:"remoteEntry.js",exposes:r,shared:e})}};var b=r(226)})(),module.exports=o})();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "max-remotes-helper",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Helper library for Module Federation remotes in BO ecosystem",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
"author": "BO Team",
|
|
21
21
|
"license": "ISC",
|
|
22
22
|
"dependencies": {
|
|
23
|
+
"max-remotes-helper": "^1.0.1",
|
|
23
24
|
"react": ">=18.0.0",
|
|
24
25
|
"react-dom": ">=18.0.0"
|
|
25
26
|
},
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DevButton.tsx
|
|
3
|
+
*
|
|
4
|
+
* Componente de botón simple para componentes de desarrollo (DevWrapper, TokenInput, etc.)
|
|
5
|
+
* No depende de Aurora Web para facilitar la separación del código de desarrollo.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import React from "react";
|
|
9
|
+
|
|
10
|
+
type ButtonVariant = "primary" | "secondary" | "text";
|
|
11
|
+
type ButtonSize = "small" | "medium" | "large";
|
|
12
|
+
|
|
13
|
+
interface DevButtonProps {
|
|
14
|
+
label: string;
|
|
15
|
+
onClick?: () => void;
|
|
16
|
+
disabled?: boolean;
|
|
17
|
+
variant?: ButtonVariant;
|
|
18
|
+
size?: ButtonSize;
|
|
19
|
+
className?: string;
|
|
20
|
+
type?: "button" | "submit" | "reset";
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const DevButton: React.FC<DevButtonProps> = ({
|
|
24
|
+
label,
|
|
25
|
+
onClick,
|
|
26
|
+
disabled = false,
|
|
27
|
+
variant = "primary",
|
|
28
|
+
size = "medium",
|
|
29
|
+
className = "",
|
|
30
|
+
type = "button",
|
|
31
|
+
}) => {
|
|
32
|
+
const baseStyles =
|
|
33
|
+
"rounded-lg font-medium transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed";
|
|
34
|
+
|
|
35
|
+
const variantStyles = {
|
|
36
|
+
primary:
|
|
37
|
+
"bg-gradient-to-br from-blue-40 to-blue-70 hover:from-blue-50 hover:to-blue-90 text-white focus:ring-blue-50 shadow-sm hover:shadow-md",
|
|
38
|
+
secondary:
|
|
39
|
+
"bg-gray-20 hover:bg-gray-30 text-gray-80 focus:ring-gray-40 border border-gray-30",
|
|
40
|
+
text: "bg-transparent hover:bg-gray-10 text-blue-60 hover:text-blue-80 focus:ring-blue-30",
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const sizeStyles = {
|
|
44
|
+
small: "px-4 py-2 text-sm",
|
|
45
|
+
medium: "px-5 py-2.5 text-base",
|
|
46
|
+
large: "px-6 py-3 text-lg",
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<button
|
|
51
|
+
type={type}
|
|
52
|
+
onClick={onClick}
|
|
53
|
+
disabled={disabled}
|
|
54
|
+
className={`${baseStyles} ${variantStyles[variant]} ${sizeStyles[size]} ${className}`}
|
|
55
|
+
>
|
|
56
|
+
{label}
|
|
57
|
+
</button>
|
|
58
|
+
);
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export default DevButton;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DevCard.tsx
|
|
3
|
+
*
|
|
4
|
+
* Componente de tarjeta simple para componentes de desarrollo.
|
|
5
|
+
* Reemplaza EmptyCard de Aurora Web para eliminar dependencias.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import React from "react";
|
|
9
|
+
|
|
10
|
+
interface DevCardProps {
|
|
11
|
+
children: React.ReactNode;
|
|
12
|
+
className?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const DevCard: React.FC<DevCardProps> = ({ children, className = "" }) => {
|
|
16
|
+
return (
|
|
17
|
+
<div
|
|
18
|
+
className={`bg-white rounded-lg shadow-md border border-gray-20 ${className}`}
|
|
19
|
+
>
|
|
20
|
+
{children}
|
|
21
|
+
</div>
|
|
22
|
+
);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export default DevCard;
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import React, { useState, useEffect } from "react";
|
|
2
|
+
import { RemoteWrapper, tokenStorage } from "max-remotes-helper";
|
|
3
|
+
import TokenGenerator from "./TokenGenerator";
|
|
4
|
+
import TokenInput from "./TokenInput";
|
|
5
|
+
import FloatingTokenButton from "./FloatingTokenButton";
|
|
6
|
+
|
|
7
|
+
interface DevWrapperProps {
|
|
8
|
+
children: React.ReactNode;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const DevWrapper: React.FC<DevWrapperProps> = ({ children }) => {
|
|
12
|
+
const [currentToken, setCurrentToken] = useState<string>("");
|
|
13
|
+
const [showTokenInput, setShowTokenInput] = useState<boolean>(false);
|
|
14
|
+
const [needsToken, setNeedsToken] = useState<boolean>(false);
|
|
15
|
+
const [showGenerator, setShowGenerator] = useState<boolean>(true);
|
|
16
|
+
|
|
17
|
+
// Detectar si estamos en desarrollo y sin host
|
|
18
|
+
const isDevEnvironment = process.env.REACT_APP_ENVIRONMENT === "dev";
|
|
19
|
+
const shouldShowTokenScreen = isDevEnvironment && !currentToken;
|
|
20
|
+
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
const savedToken = tokenStorage.get();
|
|
23
|
+
if (savedToken) {
|
|
24
|
+
setCurrentToken(savedToken);
|
|
25
|
+
setNeedsToken(false);
|
|
26
|
+
} else if (shouldShowTokenScreen) {
|
|
27
|
+
setNeedsToken(true);
|
|
28
|
+
}
|
|
29
|
+
}, [shouldShowTokenScreen]);
|
|
30
|
+
|
|
31
|
+
const handleTokenSubmit = (token: string) => {
|
|
32
|
+
console.log("🔄 DevWrapper: Nuevo token generado", {
|
|
33
|
+
token: token.substring(0, 50) + "...",
|
|
34
|
+
});
|
|
35
|
+
setCurrentToken(token);
|
|
36
|
+
tokenStorage.save(token);
|
|
37
|
+
console.log("💾 DevWrapper: Token guardado en storage");
|
|
38
|
+
setShowTokenInput(false);
|
|
39
|
+
setNeedsToken(false);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const handleClearStorage = () => {
|
|
43
|
+
tokenStorage.clear();
|
|
44
|
+
setCurrentToken("");
|
|
45
|
+
setNeedsToken(shouldShowTokenScreen);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const toggleTokenInput = () => {
|
|
49
|
+
setShowTokenInput(!showTokenInput);
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// Si estamos en desarrollo, sin host y sin token, mostrar pantalla de generación
|
|
53
|
+
if (needsToken) {
|
|
54
|
+
return (
|
|
55
|
+
<div className="min-h-screen bg-gray-50 flex items-center justify-center p-4">
|
|
56
|
+
<TokenGenerator
|
|
57
|
+
onSubmit={handleTokenSubmit}
|
|
58
|
+
onCancel={() => setNeedsToken(false)}
|
|
59
|
+
onClearStorage={handleClearStorage}
|
|
60
|
+
currentToken={currentToken}
|
|
61
|
+
/>
|
|
62
|
+
</div>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return (
|
|
67
|
+
<div className="min-h-screen bg-gray-50">
|
|
68
|
+
{/* Token Input/Generator Modal */}
|
|
69
|
+
{showTokenInput && (
|
|
70
|
+
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
|
71
|
+
<div className="bg-white rounded-lg shadow-lg max-w-2xl w-full mx-4 max-h-[90vh] overflow-y-auto">
|
|
72
|
+
{/* Tabs para cambiar entre generador y input manual */}
|
|
73
|
+
<div className="flex border-b">
|
|
74
|
+
<button
|
|
75
|
+
onClick={() => setShowGenerator(true)}
|
|
76
|
+
className={`flex-1 py-3 px-4 text-sm font-medium ${
|
|
77
|
+
showGenerator
|
|
78
|
+
? "bg-blue-50 "
|
|
79
|
+
: "bg-gray-10 text-gray-70 hover:bg-gray-20"
|
|
80
|
+
}`}
|
|
81
|
+
>
|
|
82
|
+
Generar Token
|
|
83
|
+
</button>
|
|
84
|
+
<button
|
|
85
|
+
onClick={() => setShowGenerator(false)}
|
|
86
|
+
className={`flex-1 py-3 px-4 text-sm font-medium ${
|
|
87
|
+
!showGenerator
|
|
88
|
+
? "bg-blue-50 text-gray-50"
|
|
89
|
+
: "bg-gray-10 text-gray-70 hover:bg-gray-20"
|
|
90
|
+
}`}
|
|
91
|
+
>
|
|
92
|
+
Input Manual
|
|
93
|
+
</button>
|
|
94
|
+
</div>
|
|
95
|
+
|
|
96
|
+
<div className="p-0">
|
|
97
|
+
{showGenerator ? (
|
|
98
|
+
<TokenGenerator
|
|
99
|
+
onSubmit={handleTokenSubmit}
|
|
100
|
+
onCancel={() => setShowTokenInput(false)}
|
|
101
|
+
onClearStorage={handleClearStorage}
|
|
102
|
+
currentToken={currentToken}
|
|
103
|
+
/>
|
|
104
|
+
) : (
|
|
105
|
+
<div className="p-6">
|
|
106
|
+
<TokenInput
|
|
107
|
+
onSubmit={handleTokenSubmit}
|
|
108
|
+
onCancel={() => setShowTokenInput(false)}
|
|
109
|
+
onClearStorage={handleClearStorage}
|
|
110
|
+
currentToken={currentToken}
|
|
111
|
+
/>
|
|
112
|
+
</div>
|
|
113
|
+
)}
|
|
114
|
+
</div>
|
|
115
|
+
</div>
|
|
116
|
+
</div>
|
|
117
|
+
)}
|
|
118
|
+
|
|
119
|
+
{/* Floating Token Button */}
|
|
120
|
+
<FloatingTokenButton onClick={toggleTokenInput} />
|
|
121
|
+
|
|
122
|
+
{/* Main Content wrapped with RemoteWrapper */}
|
|
123
|
+
<RemoteWrapper token={currentToken}>{children}</RemoteWrapper>
|
|
124
|
+
</div>
|
|
125
|
+
);
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
export default DevWrapper;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
interface FloatingTokenButtonProps {
|
|
4
|
+
onClick: () => void;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
const FloatingTokenButton: React.FC<FloatingTokenButtonProps> = ({
|
|
8
|
+
onClick,
|
|
9
|
+
}) => {
|
|
10
|
+
return (
|
|
11
|
+
<button
|
|
12
|
+
onClick={onClick}
|
|
13
|
+
className="fixed bottom-6 right-6 bg-gradient-to-br from-blue-40 to-blue-70 hover:from-blue-50 hover:to-blue-90 text-white p-4 rounded-full shadow-dropdown hover:shadow-2xl transition-all duration-300 z-50 group transform hover:scale-110"
|
|
14
|
+
title="Recargar Token"
|
|
15
|
+
>
|
|
16
|
+
<svg
|
|
17
|
+
className="w-5 h-5"
|
|
18
|
+
fill="none"
|
|
19
|
+
stroke="currentColor"
|
|
20
|
+
viewBox="0 0 24 24"
|
|
21
|
+
>
|
|
22
|
+
<path
|
|
23
|
+
strokeLinecap="round"
|
|
24
|
+
strokeLinejoin="round"
|
|
25
|
+
strokeWidth={2}
|
|
26
|
+
d="M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-2.586a1 1 0 01.293-.707l5.964-5.964A6 6 0 1121 9z"
|
|
27
|
+
/>
|
|
28
|
+
</svg>
|
|
29
|
+
<span className="absolute bottom-full right-0 mb-3 px-3 py-1 text-xs text-white bg-gray-90/90 rounded-lg opacity-0 group-hover:opacity-100 transition-opacity duration-200 whitespace-nowrap backdrop-blur-sm">
|
|
30
|
+
{"Recargar Token"}
|
|
31
|
+
</span>
|
|
32
|
+
</button>
|
|
33
|
+
);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export default FloatingTokenButton;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import React from
|
|
2
|
-
import { RemoteAuthProvider } from
|
|
3
|
-
import { AuthProviderProps } from
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { RemoteAuthProvider } from "../context/RemoteAuthContext";
|
|
3
|
+
import { AuthProviderProps } from "../types";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Wrapper component that provides authentication context to remote components
|
|
@@ -9,14 +9,10 @@ import { AuthProviderProps } from '../types';
|
|
|
9
9
|
export const RemoteWrapper: React.FC<AuthProviderProps> = ({
|
|
10
10
|
children,
|
|
11
11
|
token,
|
|
12
|
-
apiBaseUrl,
|
|
13
|
-
module,
|
|
14
12
|
}) => {
|
|
15
13
|
return (
|
|
16
14
|
<RemoteAuthProvider initialToken={token}>
|
|
17
|
-
<div
|
|
18
|
-
{children}
|
|
19
|
-
</div>
|
|
15
|
+
<div>{children}</div>
|
|
20
16
|
</RemoteAuthProvider>
|
|
21
17
|
);
|
|
22
|
-
};
|
|
18
|
+
};
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import React, { useState } from "react";
|
|
2
|
+
|
|
3
|
+
import DevCard from "./DevCard";
|
|
4
|
+
import DevButton from "./DevButton";
|
|
5
|
+
|
|
6
|
+
interface TokenGeneratorProps {
|
|
7
|
+
onSubmit: (token: string) => void;
|
|
8
|
+
onCancel: () => void;
|
|
9
|
+
onClearStorage: () => void;
|
|
10
|
+
currentToken?: string;
|
|
11
|
+
jsonExample?: string;
|
|
12
|
+
defaultDevRoles?: string[];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface TokenPayload {
|
|
16
|
+
user: string;
|
|
17
|
+
roles: string[];
|
|
18
|
+
channel: string;
|
|
19
|
+
sessionId: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const TokenGenerator: React.FC<TokenGeneratorProps> = ({
|
|
23
|
+
onSubmit,
|
|
24
|
+
onCancel,
|
|
25
|
+
onClearStorage,
|
|
26
|
+
defaultDevRoles = [],
|
|
27
|
+
currentToken = "",
|
|
28
|
+
}) => {
|
|
29
|
+
const [loading, setLoading] = useState(false);
|
|
30
|
+
const [error, setError] = useState<string>("");
|
|
31
|
+
|
|
32
|
+
// Estado del formulario
|
|
33
|
+
const [formData, setFormData] = useState<TokenPayload>({
|
|
34
|
+
user: "usuario@bdsol.com.ar",
|
|
35
|
+
roles: defaultDevRoles,
|
|
36
|
+
channel: "mf-testing",
|
|
37
|
+
sessionId: crypto.randomUUID(),
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const [rolesText, setRolesText] = useState(defaultDevRoles.join("\n"));
|
|
41
|
+
|
|
42
|
+
const generateToken = async () => {
|
|
43
|
+
setLoading(true);
|
|
44
|
+
setError("");
|
|
45
|
+
onClearStorage();
|
|
46
|
+
try {
|
|
47
|
+
// Parsear roles del textarea
|
|
48
|
+
const roles = rolesText
|
|
49
|
+
.split("\n")
|
|
50
|
+
.map((role) => role.trim())
|
|
51
|
+
.filter((role) => role.length > 0);
|
|
52
|
+
|
|
53
|
+
const payload = {
|
|
54
|
+
...formData,
|
|
55
|
+
roles,
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const bffUrl = process.env.REACT_APP_API_URL || "http://localhost:3020";
|
|
59
|
+
const response = await fetch(
|
|
60
|
+
`${bffUrl}/template/api/dev/generate-token`,
|
|
61
|
+
{
|
|
62
|
+
method: "POST",
|
|
63
|
+
headers: {
|
|
64
|
+
"Content-Type": "application/json",
|
|
65
|
+
},
|
|
66
|
+
body: JSON.stringify(payload),
|
|
67
|
+
},
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
if (!response.ok) {
|
|
71
|
+
const errorData = await response.json();
|
|
72
|
+
throw new Error(errorData.error || "Error al generar token");
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const data = await response.json();
|
|
76
|
+
onSubmit(data.token);
|
|
77
|
+
} catch (err) {
|
|
78
|
+
setError(err instanceof Error ? err.message : "Error desconocido");
|
|
79
|
+
} finally {
|
|
80
|
+
setLoading(false);
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const handleInputChange = (field: keyof TokenPayload, value: string) => {
|
|
85
|
+
setFormData((prev) => ({
|
|
86
|
+
...prev,
|
|
87
|
+
[field]: value,
|
|
88
|
+
}));
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const generateNewSessionId = () => {
|
|
92
|
+
setFormData((prev) => ({
|
|
93
|
+
...prev,
|
|
94
|
+
sessionId: crypto.randomUUID(),
|
|
95
|
+
}));
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const handleClearStorageClick = () => {
|
|
99
|
+
onClearStorage();
|
|
100
|
+
alert("SessionStorage limpiado correctamente");
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
return (
|
|
104
|
+
<div className="p-6 w-full overflow-y-auto">
|
|
105
|
+
<DevCard className="p-6">
|
|
106
|
+
<>
|
|
107
|
+
<h2 className="text-2xl font-bold text-gray-80 mb-4">
|
|
108
|
+
🔧 Generador de Tokens (Desarrollo)
|
|
109
|
+
</h2>
|
|
110
|
+
<p className="text-gray-60 mb-6">
|
|
111
|
+
Genera un token JWT para desarrollo usando el BFF. Solo disponible
|
|
112
|
+
cuando ENVIRONMENT=dev.
|
|
113
|
+
</p>
|
|
114
|
+
{error && (
|
|
115
|
+
<div className="mb-4 p-3 bg-red-10 border border-red-40 text-red-70 rounded">
|
|
116
|
+
{error}
|
|
117
|
+
</div>
|
|
118
|
+
)}
|
|
119
|
+
<div className="space-y-4 mb-6">
|
|
120
|
+
{/* Email */}
|
|
121
|
+
<div>
|
|
122
|
+
<label className="block text-sm font-medium text-gray-70 mb-1">
|
|
123
|
+
Email del usuario
|
|
124
|
+
</label>
|
|
125
|
+
<input
|
|
126
|
+
type="email"
|
|
127
|
+
value={formData.user}
|
|
128
|
+
onChange={(e) => handleInputChange("user", e.target.value)}
|
|
129
|
+
className="w-full p-2 border border-gray-30 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-50"
|
|
130
|
+
placeholder="usuario@bdsol.com.ar"
|
|
131
|
+
/>
|
|
132
|
+
</div>
|
|
133
|
+
|
|
134
|
+
{/* Roles */}
|
|
135
|
+
<div>
|
|
136
|
+
<label className="block text-sm font-medium text-gray-70 mb-1">
|
|
137
|
+
Roles (uno por línea)
|
|
138
|
+
</label>
|
|
139
|
+
<textarea
|
|
140
|
+
value={rolesText}
|
|
141
|
+
onChange={(e) => setRolesText(e.target.value)}
|
|
142
|
+
className="w-full h-32 p-2 border border-gray-30 rounded-lg font-mono text-sm focus:outline-none focus:ring-2 focus:ring-blue-50"
|
|
143
|
+
placeholder={defaultDevRoles.join(" ")}
|
|
144
|
+
/>
|
|
145
|
+
<p className="text-xs text-gray-50 mt-1">
|
|
146
|
+
Roles definidos en src/config/roles.ts
|
|
147
|
+
</p>
|
|
148
|
+
</div>
|
|
149
|
+
|
|
150
|
+
{/* Channel */}
|
|
151
|
+
<div>
|
|
152
|
+
<label className="block text-sm font-medium text-gray-70 mb-1">
|
|
153
|
+
Channel
|
|
154
|
+
</label>
|
|
155
|
+
<input
|
|
156
|
+
type="text"
|
|
157
|
+
value={formData.channel}
|
|
158
|
+
onChange={(e) => handleInputChange("channel", e.target.value)}
|
|
159
|
+
className="w-full p-2 border border-gray-30 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-50"
|
|
160
|
+
placeholder="mf-testing"
|
|
161
|
+
/>
|
|
162
|
+
</div>
|
|
163
|
+
|
|
164
|
+
{/* Session ID */}
|
|
165
|
+
<div>
|
|
166
|
+
<label className="block text-sm font-medium text-gray-70 mb-1">
|
|
167
|
+
Session ID
|
|
168
|
+
</label>
|
|
169
|
+
<div className="flex gap-2">
|
|
170
|
+
<input
|
|
171
|
+
type="text"
|
|
172
|
+
value={formData.sessionId}
|
|
173
|
+
onChange={(e) =>
|
|
174
|
+
handleInputChange("sessionId", e.target.value)
|
|
175
|
+
}
|
|
176
|
+
className="flex-1 p-2 border border-gray-30 rounded-lg font-mono text-sm focus:outline-none focus:ring-2 focus:ring-blue-50"
|
|
177
|
+
placeholder="uuid-session-id"
|
|
178
|
+
/>
|
|
179
|
+
<DevButton
|
|
180
|
+
label="🎲"
|
|
181
|
+
variant="primary"
|
|
182
|
+
size="small"
|
|
183
|
+
onClick={generateNewSessionId}
|
|
184
|
+
className="px-3"
|
|
185
|
+
/>
|
|
186
|
+
</div>
|
|
187
|
+
</div>
|
|
188
|
+
</div>
|
|
189
|
+
<div className="flex gap-3 flex-wrap mb-4">
|
|
190
|
+
<DevButton
|
|
191
|
+
label={loading ? "Generando..." : "🚀 Generar Token"}
|
|
192
|
+
variant="primary"
|
|
193
|
+
size="small"
|
|
194
|
+
onClick={generateToken}
|
|
195
|
+
disabled={loading || !formData.user.trim()}
|
|
196
|
+
/>
|
|
197
|
+
<DevButton
|
|
198
|
+
label="Limpiar almacenamiento"
|
|
199
|
+
variant="primary"
|
|
200
|
+
size="small"
|
|
201
|
+
onClick={handleClearStorageClick}
|
|
202
|
+
/>
|
|
203
|
+
<DevButton
|
|
204
|
+
label="Cancelar"
|
|
205
|
+
size="small"
|
|
206
|
+
variant="secondary"
|
|
207
|
+
onClick={onCancel}
|
|
208
|
+
/>
|
|
209
|
+
</div>
|
|
210
|
+
{/* Información del token actual */}
|
|
211
|
+
{currentToken && (
|
|
212
|
+
<div className="mt-4 p-4 bg-green-50 rounded-lg">
|
|
213
|
+
<h3 className="font-semibold text-green-70 mb-2">
|
|
214
|
+
✅ Token cargado
|
|
215
|
+
</h3>
|
|
216
|
+
<p className="text-sm text-green-60">
|
|
217
|
+
Tienes un token activo en el almacenamiento local.
|
|
218
|
+
</p>
|
|
219
|
+
</div>
|
|
220
|
+
)}{" "}
|
|
221
|
+
</>
|
|
222
|
+
</DevCard>
|
|
223
|
+
</div>
|
|
224
|
+
);
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
export default TokenGenerator;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import React, { useState } from "react";
|
|
2
|
+
import DevButton from "./DevButton";
|
|
3
|
+
|
|
4
|
+
interface TokenInputProps {
|
|
5
|
+
onSubmit: (token: string) => void;
|
|
6
|
+
onCancel: () => void;
|
|
7
|
+
onClearStorage: () => void;
|
|
8
|
+
currentToken?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const TokenInput: React.FC<TokenInputProps> = ({
|
|
12
|
+
onSubmit,
|
|
13
|
+
onCancel,
|
|
14
|
+
onClearStorage,
|
|
15
|
+
currentToken = "",
|
|
16
|
+
}) => {
|
|
17
|
+
const [token, setToken] = useState(currentToken);
|
|
18
|
+
const [isValidJson, setIsValidJson] = useState(true);
|
|
19
|
+
|
|
20
|
+
const validateAndSubmit = () => {
|
|
21
|
+
try {
|
|
22
|
+
JSON.parse(token);
|
|
23
|
+
setIsValidJson(true);
|
|
24
|
+
onSubmit(token);
|
|
25
|
+
} catch (error) {
|
|
26
|
+
setIsValidJson(false);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const handleTextareaChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
|
31
|
+
const value = e.target.value;
|
|
32
|
+
setToken(value);
|
|
33
|
+
|
|
34
|
+
// Reset validation state when user starts typing
|
|
35
|
+
if (!isValidJson) {
|
|
36
|
+
setIsValidJson(true);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const handleClearStorageClick = () => {
|
|
41
|
+
onClearStorage();
|
|
42
|
+
setToken("");
|
|
43
|
+
alert("SessionStorage limpiado correctamente");
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<div className="bg-white rounded-lg shadow-lg p-6 w-full max-w-2xl">
|
|
48
|
+
<h2 className="text-2xl font-bold text-gray-80 mb-4">
|
|
49
|
+
Modo Desarrollo - Cargar Token JWT
|
|
50
|
+
</h2>
|
|
51
|
+
<p className="text-gray-60 mb-6">
|
|
52
|
+
Ingresa el token JWT decodificado (JSON) que normalmente recibiría desde
|
|
53
|
+
el host:
|
|
54
|
+
</p>
|
|
55
|
+
|
|
56
|
+
<div className="mb-4">
|
|
57
|
+
<textarea
|
|
58
|
+
value={token}
|
|
59
|
+
onChange={handleTextareaChange}
|
|
60
|
+
placeholder="Pegue aquí el token JWT en formato JSON"
|
|
61
|
+
className={`w-full h-40 p-3 border rounded-lg font-mono text-sm ${
|
|
62
|
+
!isValidJson ? "border-red-50" : "border-gray-30"
|
|
63
|
+
} focus:outline-none focus:ring-2 focus:ring-blue-50`}
|
|
64
|
+
/>
|
|
65
|
+
{!isValidJson && (
|
|
66
|
+
<p className="text-red-50 text-sm mt-2">
|
|
67
|
+
El JSON no es válido. Por favor verifica la sintaxis.
|
|
68
|
+
</p>
|
|
69
|
+
)}
|
|
70
|
+
</div>
|
|
71
|
+
|
|
72
|
+
<div className="flex gap-3 flex-wrap">
|
|
73
|
+
<DevButton
|
|
74
|
+
label="Cargar Token"
|
|
75
|
+
variant="primary"
|
|
76
|
+
size="small"
|
|
77
|
+
onClick={validateAndSubmit}
|
|
78
|
+
disabled={!token.trim()}
|
|
79
|
+
/>
|
|
80
|
+
<DevButton
|
|
81
|
+
label="Limpiar"
|
|
82
|
+
size="small"
|
|
83
|
+
variant="text"
|
|
84
|
+
onClick={() => setToken("")}
|
|
85
|
+
/>
|
|
86
|
+
<DevButton
|
|
87
|
+
label="Limpiar Storage"
|
|
88
|
+
variant="primary"
|
|
89
|
+
size="small"
|
|
90
|
+
onClick={handleClearStorageClick}
|
|
91
|
+
/>
|
|
92
|
+
<DevButton
|
|
93
|
+
label="Cancelar"
|
|
94
|
+
variant="secondary"
|
|
95
|
+
size="small"
|
|
96
|
+
onClick={onCancel}
|
|
97
|
+
/>
|
|
98
|
+
</div>
|
|
99
|
+
</div>
|
|
100
|
+
);
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
export default TokenInput;
|
|
@@ -86,25 +86,39 @@ export const RemoteAuthProvider: React.FC<RemoteAuthProviderProps> = ({
|
|
|
86
86
|
initialToken,
|
|
87
87
|
}) => {
|
|
88
88
|
const [state, dispatch] = useReducer(authReducer, initialState);
|
|
89
|
+
const isDev = process.env.REACT_APP_ENVIRONMENT === "dev";
|
|
90
|
+
const log = isDev ? console.log : () => {};
|
|
89
91
|
|
|
90
92
|
// Initialize with token from storage or props
|
|
91
93
|
useEffect(() => {
|
|
94
|
+
log("🔍 RemoteAuthContext: useEffect triggered", {
|
|
95
|
+
initialToken: initialToken
|
|
96
|
+
? initialToken.substring(0, 30) + "..."
|
|
97
|
+
: "No initialToken",
|
|
98
|
+
});
|
|
99
|
+
|
|
92
100
|
// Prioritize the prop token over stored token when initialToken is provided
|
|
93
101
|
if (initialToken) {
|
|
102
|
+
log("✅ RemoteAuthContext: Using initialToken from prop");
|
|
94
103
|
if (!isTokenExpired(initialToken)) {
|
|
104
|
+
log("🟢 RemoteAuthContext: Token is valid, dispatching SET_TOKEN");
|
|
95
105
|
dispatch({ type: "SET_TOKEN", payload: initialToken });
|
|
96
106
|
tokenStorage.save(initialToken); // Also update storage
|
|
97
107
|
} else {
|
|
108
|
+
log("🔴 RemoteAuthContext: Provided token is expired, clearing auth");
|
|
98
109
|
// If provided token is expired, clear it
|
|
99
110
|
tokenStorage.remove();
|
|
100
111
|
dispatch({ type: "CLEAR_AUTH" });
|
|
101
112
|
}
|
|
102
113
|
} else {
|
|
114
|
+
log("⚪ RemoteAuthContext: No initialToken, checking stored token");
|
|
103
115
|
// Fallback to stored token when no initialToken prop
|
|
104
116
|
const storedToken = tokenStorage.get();
|
|
105
117
|
if (storedToken && !isTokenExpired(storedToken)) {
|
|
118
|
+
log("💾 RemoteAuthContext: Using stored token");
|
|
106
119
|
dispatch({ type: "SET_TOKEN", payload: storedToken });
|
|
107
120
|
} else if (storedToken) {
|
|
121
|
+
log("🗑️ RemoteAuthContext: Stored token expired, clearing");
|
|
108
122
|
// Remove expired stored token
|
|
109
123
|
tokenStorage.remove();
|
|
110
124
|
dispatch({ type: "CLEAR_AUTH" });
|
package/src/index.ts
CHANGED
|
@@ -1,29 +1,30 @@
|
|
|
1
1
|
// Main entry point for bo-remotes-helper
|
|
2
2
|
|
|
3
3
|
// Context and Providers
|
|
4
|
-
export { RemoteAuthProvider, useRemoteAuth } from
|
|
4
|
+
export { RemoteAuthProvider, useRemoteAuth } from "./context/RemoteAuthContext";
|
|
5
5
|
|
|
6
6
|
// Components
|
|
7
|
-
export { Secured } from
|
|
8
|
-
export { RemoteWrapper } from
|
|
7
|
+
export { Secured } from "./components/Secured";
|
|
8
|
+
export { RemoteWrapper } from "./components/RemoteWrapper";
|
|
9
|
+
export { default as DevWrapper } from "./components/DevWrapper";
|
|
9
10
|
|
|
10
11
|
// Hooks
|
|
11
|
-
export { useRemoteConfig } from
|
|
12
|
+
export { useRemoteConfig } from "./hooks/useRemoteConfig";
|
|
12
13
|
|
|
13
14
|
// Utils
|
|
14
|
-
export {
|
|
15
|
-
decodeToken,
|
|
16
|
-
isTokenExpired,
|
|
17
|
-
extractPermissions,
|
|
18
|
-
tokenStorage
|
|
19
|
-
} from
|
|
15
|
+
export {
|
|
16
|
+
decodeToken,
|
|
17
|
+
isTokenExpired,
|
|
18
|
+
extractPermissions,
|
|
19
|
+
tokenStorage,
|
|
20
|
+
} from "./utils/tokenUtils";
|
|
20
21
|
|
|
21
|
-
export {
|
|
22
|
-
loadRemoteModule,
|
|
23
|
-
isRemoteAvailable,
|
|
24
|
-
createRemoteComponentLoader,
|
|
25
|
-
getRemoteEnvironment
|
|
26
|
-
} from
|
|
22
|
+
export {
|
|
23
|
+
loadRemoteModule,
|
|
24
|
+
isRemoteAvailable,
|
|
25
|
+
createRemoteComponentLoader,
|
|
26
|
+
getRemoteEnvironment,
|
|
27
|
+
} from "./utils/moduleUtils";
|
|
27
28
|
|
|
28
29
|
// Types
|
|
29
30
|
export type {
|
|
@@ -34,4 +35,4 @@ export type {
|
|
|
34
35
|
TokenInfo,
|
|
35
36
|
RemoteConfig,
|
|
36
37
|
SharedDependencies,
|
|
37
|
-
} from
|
|
38
|
+
} from "./types";
|