academe-kit 0.6.0 → 0.6.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/index.cjs +112 -53
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +63 -0
- package/dist/index.esm.js +112 -53
- package/dist/index.esm.js.map +1 -1
- package/dist/types/context/SecurityProvider/types.d.ts +4 -3
- package/dist/types/services/StorageFileService.d.ts +61 -0
- package/dist/types/services/index.d.ts +3 -0
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -5772,6 +5772,54 @@ function createCourseService(apiClient) {
|
|
|
5772
5772
|
};
|
|
5773
5773
|
}
|
|
5774
5774
|
|
|
5775
|
+
function createStorageFileService(apiClient) {
|
|
5776
|
+
return {
|
|
5777
|
+
/**
|
|
5778
|
+
* Upload a new file
|
|
5779
|
+
* Note: This method requires FormData with a 'file' field
|
|
5780
|
+
*/
|
|
5781
|
+
upload(file) {
|
|
5782
|
+
const formData = new FormData();
|
|
5783
|
+
formData.append("file", file);
|
|
5784
|
+
// Note: Route not typed in OpenAPI yet, using type assertion
|
|
5785
|
+
return apiClient.POST("/storage-files", {
|
|
5786
|
+
body: formData,
|
|
5787
|
+
bodySerializer: (body) => body,
|
|
5788
|
+
});
|
|
5789
|
+
},
|
|
5790
|
+
/**
|
|
5791
|
+
* List all storage files
|
|
5792
|
+
*/
|
|
5793
|
+
getAll() {
|
|
5794
|
+
// Note: Route not typed in OpenAPI yet, using type assertion
|
|
5795
|
+
return apiClient.GET("/storage-files", {});
|
|
5796
|
+
},
|
|
5797
|
+
/**
|
|
5798
|
+
* Get storage file by ID
|
|
5799
|
+
*/
|
|
5800
|
+
getById(id) {
|
|
5801
|
+
// Note: Route not typed in OpenAPI yet, using type assertion
|
|
5802
|
+
return apiClient.GET(`/storage-files/${id}`, {});
|
|
5803
|
+
},
|
|
5804
|
+
/**
|
|
5805
|
+
* Update storage file metadata
|
|
5806
|
+
*/
|
|
5807
|
+
update(id, data) {
|
|
5808
|
+
// Note: Route not typed in OpenAPI yet, using type assertion
|
|
5809
|
+
return apiClient.PATCH(`/storage-files/${id}`, {
|
|
5810
|
+
body: data,
|
|
5811
|
+
});
|
|
5812
|
+
},
|
|
5813
|
+
/**
|
|
5814
|
+
* Delete storage file
|
|
5815
|
+
*/
|
|
5816
|
+
delete(id) {
|
|
5817
|
+
// Note: Route not typed in OpenAPI yet, using type assertion
|
|
5818
|
+
return apiClient.DELETE(`/storage-files/${id}`, {});
|
|
5819
|
+
},
|
|
5820
|
+
};
|
|
5821
|
+
}
|
|
5822
|
+
|
|
5775
5823
|
function createAcademeApiClient(baseUrl) {
|
|
5776
5824
|
return createClient({ baseUrl });
|
|
5777
5825
|
}
|
|
@@ -5793,6 +5841,7 @@ function createAcademeServices(apiClient) {
|
|
|
5793
5841
|
certificateTemplate: createCertificateTemplateService(apiClient),
|
|
5794
5842
|
seatCode: createSeatCodeService(apiClient),
|
|
5795
5843
|
product: createProductService(apiClient),
|
|
5844
|
+
storageFile: createStorageFileService(apiClient),
|
|
5796
5845
|
};
|
|
5797
5846
|
}
|
|
5798
5847
|
|
|
@@ -5805,6 +5854,37 @@ exports.GLOBAL_ROLES = void 0;
|
|
|
5805
5854
|
GLOBAL_ROLES["GUARDIAN"] = "guardian";
|
|
5806
5855
|
})(exports.GLOBAL_ROLES || (exports.GLOBAL_ROLES = {}));
|
|
5807
5856
|
|
|
5857
|
+
function detectMobileTokenSync() {
|
|
5858
|
+
if (typeof window === "undefined") {
|
|
5859
|
+
return { token: undefined, isMobile: false, decoded: null };
|
|
5860
|
+
}
|
|
5861
|
+
const keycloakConfig = window.keycloakConfig;
|
|
5862
|
+
if (!keycloakConfig?.fromMobile || !keycloakConfig?.token) {
|
|
5863
|
+
return { token: undefined, isMobile: false, decoded: null };
|
|
5864
|
+
}
|
|
5865
|
+
// Validar e decodificar o token mobile
|
|
5866
|
+
try {
|
|
5867
|
+
const decoded = jwtDecode(keycloakConfig.token);
|
|
5868
|
+
if (!decoded) {
|
|
5869
|
+
console.error("[SecurityProvider] Token mobile inválido - ignorando");
|
|
5870
|
+
return { token: undefined, isMobile: false, decoded: null };
|
|
5871
|
+
}
|
|
5872
|
+
// Verificar se o token não está expirado
|
|
5873
|
+
const now = Math.floor(Date.now() / 1000);
|
|
5874
|
+
if (decoded.exp && decoded.exp < now) {
|
|
5875
|
+
console.error("[SecurityProvider] Token mobile expirado - ignorando");
|
|
5876
|
+
return { token: undefined, isMobile: false, decoded: null };
|
|
5877
|
+
}
|
|
5878
|
+
console.log("[SecurityProvider] ✅ Token mobile detectado síncronamente");
|
|
5879
|
+
return { token: keycloakConfig.token, isMobile: true, decoded };
|
|
5880
|
+
}
|
|
5881
|
+
catch (error) {
|
|
5882
|
+
console.error("[SecurityProvider] Erro ao decodificar token mobile:", error);
|
|
5883
|
+
return { token: undefined, isMobile: false, decoded: null };
|
|
5884
|
+
}
|
|
5885
|
+
}
|
|
5886
|
+
// Detecta o token mobile UMA VEZ no carregamento do módulo
|
|
5887
|
+
const initialMobileState = detectMobileTokenSync();
|
|
5808
5888
|
const AcademeAuthProvider = ({ realm, hubUrl, children, clientId, keycloakUrl, apiBaseUrl, skipApiUserFetch, }) => {
|
|
5809
5889
|
const oidcConfig = {
|
|
5810
5890
|
authority: `${keycloakUrl}/realms/${realm}`,
|
|
@@ -5851,12 +5931,14 @@ const decodeAccessToken = (token) => {
|
|
|
5851
5931
|
const SecurityProvider = ({ apiBaseUrl = "https://stg-api.academe.com.br", skipApiUserFetch = false, children, }) => {
|
|
5852
5932
|
const auth = useAuth();
|
|
5853
5933
|
const [currentUser, setCurrentUser] = React2.useState(null);
|
|
5854
|
-
const
|
|
5855
|
-
|
|
5856
|
-
//
|
|
5857
|
-
const
|
|
5858
|
-
const [
|
|
5859
|
-
|
|
5934
|
+
const [isRefreshing, setIsRefreshing] = React2.useState(false);
|
|
5935
|
+
// CRÍTICO: Inicializar estados com valores do token mobile detectado síncronamente
|
|
5936
|
+
// Isso garante que no primeiro render já temos os valores corretos
|
|
5937
|
+
const hasTriedSignInSilent = React2.useRef(initialMobileState.isMobile);
|
|
5938
|
+
const [isTokenReady, setIsTokenReady] = React2.useState(initialMobileState.isMobile);
|
|
5939
|
+
// --- Estado para autenticação mobile (inicializado com detecção síncrona) ---
|
|
5940
|
+
const [isMobileAuth, setIsMobileAuth] = React2.useState(initialMobileState.isMobile);
|
|
5941
|
+
const [mobileToken, setMobileToken] = React2.useState(initialMobileState.token);
|
|
5860
5942
|
// Ref para armazenar o resolver da Promise de token
|
|
5861
5943
|
const tokenReadyResolverRef = React2.useRef(null);
|
|
5862
5944
|
const tokenReadyPromiseRef = React2.useRef(null);
|
|
@@ -5867,48 +5949,13 @@ const SecurityProvider = ({ apiBaseUrl = "https://stg-api.academe.com.br", skipA
|
|
|
5867
5949
|
const accessToken = auth.user?.access_token;
|
|
5868
5950
|
const userProfile = auth.user?.profile;
|
|
5869
5951
|
const userProfileSub = auth.user?.profile?.sub;
|
|
5870
|
-
//
|
|
5871
|
-
|
|
5872
|
-
|
|
5873
|
-
|
|
5874
|
-
|
|
5875
|
-
|
|
5876
|
-
|
|
5877
|
-
mobileInitializedRef.current = true;
|
|
5878
|
-
if (typeof window === "undefined")
|
|
5879
|
-
return;
|
|
5880
|
-
const keycloakConfig = window.keycloakConfig;
|
|
5881
|
-
if (!keycloakConfig?.fromMobile || !keycloakConfig?.token) {
|
|
5882
|
-
console.debug("[SecurityProvider] Modo web normal - sem token mobile");
|
|
5883
|
-
return;
|
|
5884
|
-
}
|
|
5885
|
-
// Validar e decodificar o token mobile
|
|
5886
|
-
const decoded = decodeAccessToken(keycloakConfig.token);
|
|
5887
|
-
if (!decoded) {
|
|
5888
|
-
console.error("[SecurityProvider] Token mobile inválido - ignorando");
|
|
5889
|
-
return;
|
|
5890
|
-
}
|
|
5891
|
-
// Verificar se o token não está expirado
|
|
5892
|
-
const now = Math.floor(Date.now() / 1000);
|
|
5893
|
-
if (decoded.exp && decoded.exp < now) {
|
|
5894
|
-
console.error("[SecurityProvider] Token mobile expirado - ignorando");
|
|
5895
|
-
return;
|
|
5896
|
-
}
|
|
5897
|
-
console.log("[SecurityProvider] ✅ Token mobile detectado e válido");
|
|
5898
|
-
// Ativar modo mobile
|
|
5899
|
-
setMobileToken(keycloakConfig.token);
|
|
5900
|
-
setIsMobileAuth(true);
|
|
5901
|
-
// Sincronizar com window.accessToken e currentTokenRef
|
|
5902
|
-
window.accessToken = keycloakConfig.token;
|
|
5903
|
-
currentTokenRef.current = keycloakConfig.token;
|
|
5904
|
-
// Marcar que já tentamos SSO (para pular o check)
|
|
5905
|
-
hasTriedSignInSilent.current = true;
|
|
5906
|
-
// Resolver promise de token imediatamente
|
|
5907
|
-
if (tokenReadyResolverRef.current) {
|
|
5908
|
-
tokenReadyResolverRef.current();
|
|
5909
|
-
}
|
|
5910
|
-
setIsTokenReady(true);
|
|
5911
|
-
}, []);
|
|
5952
|
+
// CRÍTICO: Inicializar currentTokenRef com o token mobile detectado síncronamente
|
|
5953
|
+
// Isso garante que o middleware do apiClient já tenha o token no primeiro request
|
|
5954
|
+
const currentTokenRef = React2.useRef(initialMobileState.token);
|
|
5955
|
+
// Sincronizar window.accessToken se tivermos token mobile (executa síncronamente)
|
|
5956
|
+
if (initialMobileState.token && typeof window !== "undefined" && !window.accessToken) {
|
|
5957
|
+
window.accessToken = initialMobileState.token;
|
|
5958
|
+
}
|
|
5912
5959
|
// --- 1. Silent Check Inicial (Check SSO) - Pulado se mobile ---
|
|
5913
5960
|
React2.useEffect(() => {
|
|
5914
5961
|
// Pular SSO check se já estamos em modo mobile
|
|
@@ -5930,10 +5977,18 @@ const SecurityProvider = ({ apiBaseUrl = "https://stg-api.academe.com.br", skipA
|
|
|
5930
5977
|
// --- 2. Configuração de API e Services ---
|
|
5931
5978
|
const apiClient = React2.useMemo(() => {
|
|
5932
5979
|
const client = createAcademeApiClient(apiBaseUrl);
|
|
5933
|
-
//
|
|
5934
|
-
|
|
5935
|
-
|
|
5936
|
-
|
|
5980
|
+
// CRÍTICO: Se já temos token mobile, criar Promise já resolvida
|
|
5981
|
+
// Isso evita que o middleware aguarde uma Promise que nunca será resolvida
|
|
5982
|
+
if (initialMobileState.token) {
|
|
5983
|
+
tokenReadyPromiseRef.current = Promise.resolve();
|
|
5984
|
+
tokenReadyResolverRef.current = () => { };
|
|
5985
|
+
}
|
|
5986
|
+
else {
|
|
5987
|
+
// Inicializa a Promise de token ready para modo OIDC
|
|
5988
|
+
tokenReadyPromiseRef.current = new Promise((resolve) => {
|
|
5989
|
+
tokenReadyResolverRef.current = resolve;
|
|
5990
|
+
});
|
|
5991
|
+
}
|
|
5937
5992
|
// Middleware que aguarda o token estar disponível antes de fazer requests
|
|
5938
5993
|
client.use({
|
|
5939
5994
|
async onRequest({ request }) {
|
|
@@ -6004,6 +6059,7 @@ const SecurityProvider = ({ apiBaseUrl = "https://stg-api.academe.com.br", skipA
|
|
|
6004
6059
|
email: profile?.email || "",
|
|
6005
6060
|
name: profile?.given_name || "",
|
|
6006
6061
|
lastName: profile?.family_name || "",
|
|
6062
|
+
avatar_url: decodedAccessToken?.avatar_url
|
|
6007
6063
|
};
|
|
6008
6064
|
}, [userProfile, isMobileAuth, decodedAccessToken]);
|
|
6009
6065
|
const hasRealmRole = React2.useCallback((role) => {
|
|
@@ -6071,8 +6127,10 @@ const SecurityProvider = ({ apiBaseUrl = "https://stg-api.academe.com.br", skipA
|
|
|
6071
6127
|
isMobileAuth,
|
|
6072
6128
|
]);
|
|
6073
6129
|
const refreshUserData = React2.useCallback(async () => {
|
|
6130
|
+
setIsRefreshing(true);
|
|
6074
6131
|
if (effectiveIsAuthenticated) {
|
|
6075
6132
|
if (skipApiUserFetch) {
|
|
6133
|
+
await auth.signinSilent();
|
|
6076
6134
|
const academeUser = {
|
|
6077
6135
|
keycloakUser: getKeycloakUser(),
|
|
6078
6136
|
};
|
|
@@ -6093,6 +6151,7 @@ const SecurityProvider = ({ apiBaseUrl = "https://stg-api.academe.com.br", skipA
|
|
|
6093
6151
|
console.error("[SecurityProvider] Error refreshing user data:", error);
|
|
6094
6152
|
}
|
|
6095
6153
|
}
|
|
6154
|
+
setIsRefreshing(false);
|
|
6096
6155
|
}, [effectiveIsAuthenticated, getKeycloakUser, services, skipApiUserFetch]);
|
|
6097
6156
|
// --- 5. Ações de Auth ---
|
|
6098
6157
|
// SignOut para modo OIDC (web normal)
|
|
@@ -6148,7 +6207,7 @@ const SecurityProvider = ({ apiBaseUrl = "https://stg-api.academe.com.br", skipA
|
|
|
6148
6207
|
}, [isMobileAuth]);
|
|
6149
6208
|
// Memoizar o value do context para evitar re-renders desnecessários
|
|
6150
6209
|
const contextValue = React2.useMemo(() => ({
|
|
6151
|
-
isInitialized: isMobileAuth ? true : !isLoading,
|
|
6210
|
+
isInitialized: isMobileAuth ? true : (!isLoading || isRefreshing),
|
|
6152
6211
|
isTokenReady,
|
|
6153
6212
|
isMobileAuth,
|
|
6154
6213
|
user: currentUser,
|