academe-kit 0.6.7 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +80 -156
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +8 -118
- package/dist/index.esm.js +81 -157
- package/dist/index.esm.js.map +1 -1
- package/dist/types/context/SecurityProvider/index.d.ts +0 -7
- package/dist/types/context/SecurityProvider/types.d.ts +0 -2
- package/dist/types/services/CourseService.d.ts +4 -40
- package/dist/types/types/academe-api.d.ts +4 -70
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -5984,37 +5984,20 @@ exports.GLOBAL_ROLES = void 0;
|
|
|
5984
5984
|
GLOBAL_ROLES["GUARDIAN"] = "guardian";
|
|
5985
5985
|
})(exports.GLOBAL_ROLES || (exports.GLOBAL_ROLES = {}));
|
|
5986
5986
|
|
|
5987
|
-
|
|
5988
|
-
if (typeof window === "undefined")
|
|
5989
|
-
return
|
|
5990
|
-
}
|
|
5991
|
-
const
|
|
5992
|
-
if (
|
|
5993
|
-
return
|
|
5994
|
-
}
|
|
5995
|
-
// Validar e decodificar o token mobile
|
|
5996
|
-
try {
|
|
5997
|
-
const decoded = jwtDecode(keycloakConfig.token);
|
|
5998
|
-
if (!decoded) {
|
|
5999
|
-
console.error("[SecurityProvider] Token mobile inválido - ignorando");
|
|
6000
|
-
return { token: undefined, isMobile: false, decoded: null };
|
|
6001
|
-
}
|
|
6002
|
-
// Verificar se o token não está expirado
|
|
6003
|
-
const now = Math.floor(Date.now() / 1000);
|
|
6004
|
-
if (decoded.exp && decoded.exp < now) {
|
|
6005
|
-
console.error("[SecurityProvider] Token mobile expirado - ignorando");
|
|
6006
|
-
return { token: undefined, isMobile: false, decoded: null };
|
|
6007
|
-
}
|
|
6008
|
-
console.log("[SecurityProvider] ✅ Token mobile detectado síncronamente");
|
|
6009
|
-
return { token: keycloakConfig.token, isMobile: true, decoded };
|
|
6010
|
-
}
|
|
6011
|
-
catch (error) {
|
|
6012
|
-
console.error("[SecurityProvider] Erro ao decodificar token mobile:", error);
|
|
6013
|
-
return { token: undefined, isMobile: false, decoded: null };
|
|
5987
|
+
const getCookie = (name) => {
|
|
5988
|
+
if (typeof window === "undefined")
|
|
5989
|
+
return null;
|
|
5990
|
+
const value = `; ${document.cookie}`;
|
|
5991
|
+
const parts = value.split(`; ${name}=`);
|
|
5992
|
+
if (parts.length === 2) {
|
|
5993
|
+
return parts.pop()?.split(";").shift() || null;
|
|
6014
5994
|
}
|
|
6015
|
-
|
|
6016
|
-
|
|
6017
|
-
const
|
|
5995
|
+
return null;
|
|
5996
|
+
};
|
|
5997
|
+
const getAccessTokenFromCookies = () => {
|
|
5998
|
+
const token = getCookie("KC_FORCE_AUTH_TOKEN");
|
|
5999
|
+
return token || undefined;
|
|
6000
|
+
};
|
|
6018
6001
|
const AcademeAuthProvider = ({ realm, hubUrl, children, clientId, keycloakUrl, apiBaseUrl, skipApiUserFetch, }) => {
|
|
6019
6002
|
const oidcConfig = {
|
|
6020
6003
|
authority: `${keycloakUrl}/realms/${realm}`,
|
|
@@ -6033,7 +6016,6 @@ const AcademeAuthProvider = ({ realm, hubUrl, children, clientId, keycloakUrl, a
|
|
|
6033
6016
|
const SecurityContext = React2.createContext({
|
|
6034
6017
|
isInitialized: false,
|
|
6035
6018
|
isTokenReady: false,
|
|
6036
|
-
isMobileAuth: false,
|
|
6037
6019
|
user: null,
|
|
6038
6020
|
refreshUserData: async () => { },
|
|
6039
6021
|
signOut: () => null,
|
|
@@ -6062,35 +6044,53 @@ const SecurityProvider = ({ apiBaseUrl = "https://stg-api.academe.com.br", skipA
|
|
|
6062
6044
|
const auth = useAuth();
|
|
6063
6045
|
const [currentUser, setCurrentUser] = React2.useState(null);
|
|
6064
6046
|
const [isRefreshing, setIsRefreshing] = React2.useState(false);
|
|
6065
|
-
|
|
6066
|
-
|
|
6067
|
-
const hasTriedSignInSilent = React2.useRef(initialMobileState.isMobile);
|
|
6068
|
-
const [isTokenReady, setIsTokenReady] = React2.useState(initialMobileState.isMobile);
|
|
6069
|
-
// --- Estado para autenticação mobile (inicializado com detecção síncrona) ---
|
|
6070
|
-
const [isMobileAuth, setIsMobileAuth] = React2.useState(initialMobileState.isMobile);
|
|
6071
|
-
const [mobileToken, setMobileToken] = React2.useState(initialMobileState.token);
|
|
6072
|
-
// Ref para armazenar o resolver da Promise de token
|
|
6047
|
+
const hasTriedSignInSilent = React2.useRef(false);
|
|
6048
|
+
const [isTokenReady, setIsTokenReady] = React2.useState(false);
|
|
6073
6049
|
const tokenReadyResolverRef = React2.useRef(null);
|
|
6074
6050
|
const tokenReadyPromiseRef = React2.useRef(null);
|
|
6075
|
-
|
|
6076
|
-
|
|
6051
|
+
const [accessToken, setAccessToken] = React2.useState(() => {
|
|
6052
|
+
const cookieToken = getAccessTokenFromCookies();
|
|
6053
|
+
return cookieToken || auth.user?.access_token;
|
|
6054
|
+
});
|
|
6055
|
+
const decodedAccessToken = React2.useMemo(() => {
|
|
6056
|
+
return accessToken ? decodeAccessToken(accessToken) : null;
|
|
6057
|
+
}, [accessToken]);
|
|
6058
|
+
const userProfileSub = auth.user?.profile?.sub || decodedAccessToken?.sub;
|
|
6059
|
+
const isAuthenticated = auth.isAuthenticated || !!accessToken;
|
|
6077
6060
|
const isLoading = auth.isLoading;
|
|
6078
6061
|
const activeNavigator = auth.activeNavigator;
|
|
6079
|
-
const accessToken = auth.user?.access_token;
|
|
6080
6062
|
const userProfile = auth.user?.profile;
|
|
6081
|
-
|
|
6082
|
-
|
|
6083
|
-
|
|
6084
|
-
|
|
6085
|
-
//
|
|
6086
|
-
|
|
6087
|
-
|
|
6088
|
-
|
|
6089
|
-
|
|
6063
|
+
console.log(">> isAuthenticated:", isAuthenticated);
|
|
6064
|
+
console.log(">> accessToken:", accessToken);
|
|
6065
|
+
const currentTokenRef = React2.useRef(undefined);
|
|
6066
|
+
// Efeito para sincronizar o token do auth com o state
|
|
6067
|
+
// e também verificar cookies periodicamente
|
|
6068
|
+
React2.useEffect(() => {
|
|
6069
|
+
const cookieToken = getAccessTokenFromCookies();
|
|
6070
|
+
const authToken = auth.user?.access_token;
|
|
6071
|
+
// Prioridade: Cookie > Auth token
|
|
6072
|
+
const newToken = cookieToken || authToken;
|
|
6073
|
+
if (newToken && newToken !== accessToken) {
|
|
6074
|
+
setAccessToken(newToken);
|
|
6075
|
+
}
|
|
6076
|
+
}, [auth.user?.access_token, accessToken]);
|
|
6077
|
+
// Efeito opcional: verificar cookies em intervalos (útil se o cookie pode mudar externamente)
|
|
6090
6078
|
React2.useEffect(() => {
|
|
6091
|
-
|
|
6092
|
-
|
|
6093
|
-
|
|
6079
|
+
const checkCookieToken = () => {
|
|
6080
|
+
const cookieToken = getAccessTokenFromCookies();
|
|
6081
|
+
if (cookieToken && cookieToken !== accessToken) {
|
|
6082
|
+
setAccessToken(cookieToken);
|
|
6083
|
+
}
|
|
6084
|
+
};
|
|
6085
|
+
// Verifica a cada 5 segundos (ajuste conforme necessário)
|
|
6086
|
+
const interval = setInterval(checkCookieToken, 5000);
|
|
6087
|
+
return () => clearInterval(interval);
|
|
6088
|
+
}, [accessToken]);
|
|
6089
|
+
// --- 1. Silent Check Inicial (Check SSO) ---
|
|
6090
|
+
React2.useEffect(() => {
|
|
6091
|
+
// Se já temos um token do cookie, não precisamos fazer silent check
|
|
6092
|
+
if (accessToken) {
|
|
6093
|
+
hasTriedSignInSilent.current = true;
|
|
6094
6094
|
return;
|
|
6095
6095
|
}
|
|
6096
6096
|
if (!isAuthenticated &&
|
|
@@ -6103,31 +6103,19 @@ const SecurityProvider = ({ apiBaseUrl = "https://stg-api.academe.com.br", skipA
|
|
|
6103
6103
|
});
|
|
6104
6104
|
}
|
|
6105
6105
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
6106
|
-
}, [isAuthenticated, isLoading, activeNavigator,
|
|
6106
|
+
}, [isAuthenticated, isLoading, activeNavigator, accessToken]);
|
|
6107
6107
|
// --- 2. Configuração de API e Services ---
|
|
6108
6108
|
const apiClient = React2.useMemo(() => {
|
|
6109
6109
|
const client = createAcademeApiClient(apiBaseUrl);
|
|
6110
|
-
|
|
6111
|
-
|
|
6112
|
-
|
|
6113
|
-
tokenReadyPromiseRef.current = Promise.resolve();
|
|
6114
|
-
tokenReadyResolverRef.current = () => { };
|
|
6115
|
-
}
|
|
6116
|
-
else {
|
|
6117
|
-
// Inicializa a Promise de token ready para modo OIDC
|
|
6118
|
-
tokenReadyPromiseRef.current = new Promise((resolve) => {
|
|
6119
|
-
tokenReadyResolverRef.current = resolve;
|
|
6120
|
-
});
|
|
6121
|
-
}
|
|
6122
|
-
// Middleware que aguarda o token estar disponível antes de fazer requests
|
|
6110
|
+
tokenReadyPromiseRef.current = new Promise((resolve) => {
|
|
6111
|
+
tokenReadyResolverRef.current = resolve;
|
|
6112
|
+
});
|
|
6123
6113
|
client.use({
|
|
6124
6114
|
async onRequest({ request }) {
|
|
6125
|
-
// Se já tem token, usa imediatamente
|
|
6126
6115
|
if (currentTokenRef.current) {
|
|
6127
6116
|
request.headers.set("Authorization", `Bearer ${currentTokenRef.current}`);
|
|
6128
6117
|
return request;
|
|
6129
6118
|
}
|
|
6130
|
-
// Se ainda está carregando a auth, aguarda o token ficar pronto
|
|
6131
6119
|
if (tokenReadyPromiseRef.current) {
|
|
6132
6120
|
await tokenReadyPromiseRef.current;
|
|
6133
6121
|
if (currentTokenRef.current) {
|
|
@@ -6142,56 +6130,33 @@ const SecurityProvider = ({ apiBaseUrl = "https://stg-api.academe.com.br", skipA
|
|
|
6142
6130
|
const services = React2.useMemo(() => {
|
|
6143
6131
|
return createAcademeServices(apiClient);
|
|
6144
6132
|
}, [apiClient]);
|
|
6145
|
-
// Token efetivo: prioriza mobile, depois OIDC
|
|
6146
|
-
const effectiveToken = mobileToken || accessToken;
|
|
6147
|
-
const decodedAccessToken = React2.useMemo(() => {
|
|
6148
|
-
return effectiveToken ? decodeAccessToken(effectiveToken) : null;
|
|
6149
|
-
}, [effectiveToken]);
|
|
6150
|
-
// Atualização do Token e resolução da Promise
|
|
6151
|
-
// Não sobrescreve se estamos em modo mobile
|
|
6152
6133
|
React2.useEffect(() => {
|
|
6153
|
-
// Se estamos em modo mobile, o token já foi configurado
|
|
6154
|
-
if (isMobileAuth) {
|
|
6155
|
-
return;
|
|
6156
|
-
}
|
|
6157
6134
|
currentTokenRef.current = accessToken;
|
|
6158
6135
|
if (typeof window !== "undefined") {
|
|
6159
6136
|
window.accessToken = accessToken;
|
|
6160
6137
|
}
|
|
6161
6138
|
if (accessToken) {
|
|
6162
|
-
// Resolve a promise indicando que o token está pronto
|
|
6163
6139
|
if (tokenReadyResolverRef.current) {
|
|
6164
6140
|
tokenReadyResolverRef.current();
|
|
6165
6141
|
setIsTokenReady(true);
|
|
6166
6142
|
}
|
|
6167
6143
|
}
|
|
6168
6144
|
else if (!isLoading && !isAuthenticated) {
|
|
6169
|
-
// Usuário não autenticado - resolve a promise para não bloquear requests públicas
|
|
6170
6145
|
if (tokenReadyResolverRef.current) {
|
|
6171
6146
|
tokenReadyResolverRef.current();
|
|
6172
6147
|
setIsTokenReady(true);
|
|
6173
6148
|
}
|
|
6174
6149
|
}
|
|
6175
|
-
}, [accessToken, isLoading, isAuthenticated
|
|
6176
|
-
// --- 3. Helpers de Usuário e Roles ---
|
|
6150
|
+
}, [accessToken, isLoading, isAuthenticated]);
|
|
6177
6151
|
const getKeycloakUser = React2.useCallback(() => {
|
|
6178
|
-
// Se estamos em modo mobile, extrair do token decodificado
|
|
6179
|
-
if (isMobileAuth && decodedAccessToken) {
|
|
6180
|
-
return {
|
|
6181
|
-
email: decodedAccessToken.email || "",
|
|
6182
|
-
name: decodedAccessToken.given_name || "",
|
|
6183
|
-
lastName: decodedAccessToken.family_name || "",
|
|
6184
|
-
};
|
|
6185
|
-
}
|
|
6186
|
-
// Modo OIDC normal
|
|
6187
6152
|
const profile = userProfile;
|
|
6188
6153
|
return {
|
|
6189
|
-
email: profile?.email || "",
|
|
6190
|
-
name: profile?.given_name || "",
|
|
6191
|
-
lastName: profile?.family_name || "",
|
|
6192
|
-
avatar_url: decodedAccessToken?.avatar_url
|
|
6154
|
+
email: profile?.email || decodedAccessToken?.email || "",
|
|
6155
|
+
name: profile?.given_name || decodedAccessToken?.given_name || "",
|
|
6156
|
+
lastName: profile?.family_name || decodedAccessToken?.family_name || "",
|
|
6157
|
+
avatar_url: decodedAccessToken?.avatar_url,
|
|
6193
6158
|
};
|
|
6194
|
-
}, [userProfile,
|
|
6159
|
+
}, [userProfile, decodedAccessToken]);
|
|
6195
6160
|
const hasRealmRole = React2.useCallback((role) => {
|
|
6196
6161
|
return decodedAccessToken?.realm_access?.roles?.includes(role) ?? false;
|
|
6197
6162
|
}, [decodedAccessToken]);
|
|
@@ -6206,20 +6171,17 @@ const SecurityProvider = ({ apiBaseUrl = "https://stg-api.academe.com.br", skipA
|
|
|
6206
6171
|
}
|
|
6207
6172
|
return Object.values(decodedAccessToken.resource_access).some((resource) => resource.roles?.includes(role));
|
|
6208
6173
|
}, [decodedAccessToken]);
|
|
6209
|
-
// Autenticação efetiva: mobile ou OIDC
|
|
6210
|
-
const effectiveIsAuthenticated = isMobileAuth || isAuthenticated;
|
|
6211
|
-
const effectiveUserSub = isMobileAuth ? decodedAccessToken?.sub : userProfileSub;
|
|
6212
6174
|
// --- 4. Fetch de Dados do Usuário (Backend) ---
|
|
6213
6175
|
React2.useEffect(() => {
|
|
6214
6176
|
let isMounted = true;
|
|
6215
6177
|
const fetchUserData = async () => {
|
|
6216
|
-
if (
|
|
6178
|
+
if (isAuthenticated) {
|
|
6217
6179
|
if (skipApiUserFetch) {
|
|
6218
6180
|
if (isMounted) {
|
|
6219
6181
|
const academeUser = {
|
|
6220
6182
|
keycloakUser: getKeycloakUser(),
|
|
6221
6183
|
};
|
|
6222
|
-
setCurrentUser({ ...academeUser, id:
|
|
6184
|
+
setCurrentUser({ ...academeUser, id: userProfileSub || "" });
|
|
6223
6185
|
}
|
|
6224
6186
|
return;
|
|
6225
6187
|
}
|
|
@@ -6237,7 +6199,7 @@ const SecurityProvider = ({ apiBaseUrl = "https://stg-api.academe.com.br", skipA
|
|
|
6237
6199
|
console.error("[SecurityProvider] Error fetching user data:", error);
|
|
6238
6200
|
}
|
|
6239
6201
|
}
|
|
6240
|
-
else if (!
|
|
6202
|
+
else if (!isAuthenticated && !isLoading) {
|
|
6241
6203
|
if (isMounted) {
|
|
6242
6204
|
setCurrentUser(null);
|
|
6243
6205
|
}
|
|
@@ -6248,17 +6210,16 @@ const SecurityProvider = ({ apiBaseUrl = "https://stg-api.academe.com.br", skipA
|
|
|
6248
6210
|
isMounted = false;
|
|
6249
6211
|
};
|
|
6250
6212
|
}, [
|
|
6251
|
-
|
|
6213
|
+
isAuthenticated,
|
|
6252
6214
|
isLoading,
|
|
6253
6215
|
getKeycloakUser,
|
|
6254
6216
|
services,
|
|
6255
6217
|
skipApiUserFetch,
|
|
6256
|
-
|
|
6257
|
-
isMobileAuth,
|
|
6218
|
+
userProfileSub,
|
|
6258
6219
|
]);
|
|
6259
6220
|
const refreshUserData = React2.useCallback(async () => {
|
|
6260
6221
|
setIsRefreshing(true);
|
|
6261
|
-
if (
|
|
6222
|
+
if (isAuthenticated) {
|
|
6262
6223
|
if (skipApiUserFetch) {
|
|
6263
6224
|
await auth.signinSilent();
|
|
6264
6225
|
const academeUser = {
|
|
@@ -6282,47 +6243,17 @@ const SecurityProvider = ({ apiBaseUrl = "https://stg-api.academe.com.br", skipA
|
|
|
6282
6243
|
}
|
|
6283
6244
|
}
|
|
6284
6245
|
setIsRefreshing(false);
|
|
6285
|
-
}, [
|
|
6246
|
+
}, [getKeycloakUser, services, skipApiUserFetch]);
|
|
6286
6247
|
// --- 5. Ações de Auth ---
|
|
6287
|
-
|
|
6288
|
-
|
|
6289
|
-
console.log("[KC LOGOUT - OIDC]");
|
|
6248
|
+
const signOut = React2.useCallback(() => {
|
|
6249
|
+
console.log("[KC LOGOUT!]");
|
|
6290
6250
|
setCurrentUser(null);
|
|
6291
6251
|
auth.removeUser();
|
|
6292
|
-
auth.
|
|
6252
|
+
auth.signoutRedirect({
|
|
6293
6253
|
id_token_hint: auth.user?.id_token,
|
|
6294
6254
|
});
|
|
6295
6255
|
auth.clearStaleState();
|
|
6296
|
-
setTimeout(() => {
|
|
6297
|
-
window.location.reload();
|
|
6298
|
-
}, 500);
|
|
6299
6256
|
}, [auth]);
|
|
6300
|
-
// SignOut para modo mobile
|
|
6301
|
-
const signOutMobile = React2.useCallback(() => {
|
|
6302
|
-
console.log("[KC LOGOUT - Mobile]");
|
|
6303
|
-
setCurrentUser(null);
|
|
6304
|
-
setMobileToken(undefined);
|
|
6305
|
-
setIsMobileAuth(false);
|
|
6306
|
-
// Limpar tokens globais
|
|
6307
|
-
if (typeof window !== "undefined") {
|
|
6308
|
-
window.accessToken = undefined;
|
|
6309
|
-
window.keycloakConfig = undefined;
|
|
6310
|
-
}
|
|
6311
|
-
currentTokenRef.current = undefined;
|
|
6312
|
-
// Notificar o app mobile para fazer logout
|
|
6313
|
-
if (window.ReactNativeWebView) {
|
|
6314
|
-
window.ReactNativeWebView.postMessage(JSON.stringify({ type: "LOGOUT_REQUESTED" }));
|
|
6315
|
-
}
|
|
6316
|
-
}, []);
|
|
6317
|
-
// SignOut unificado: escolhe automaticamente baseado no modo
|
|
6318
|
-
const signOut = React2.useCallback(() => {
|
|
6319
|
-
if (isMobileAuth) {
|
|
6320
|
-
signOutMobile();
|
|
6321
|
-
}
|
|
6322
|
-
else {
|
|
6323
|
-
signOutOidc();
|
|
6324
|
-
}
|
|
6325
|
-
}, [isMobileAuth, signOutMobile, signOutOidc]);
|
|
6326
6257
|
const hasSchool = React2.useCallback((schoolId) => {
|
|
6327
6258
|
if (hasRealmRole(exports.GLOBAL_ROLES.ADMIN_ACADEME)) {
|
|
6328
6259
|
return true;
|
|
@@ -6330,43 +6261,36 @@ const SecurityProvider = ({ apiBaseUrl = "https://stg-api.academe.com.br", skipA
|
|
|
6330
6261
|
return (currentUser?.institutionRegistrations?.some((registration) => registration.institutionId === schoolId) ?? false);
|
|
6331
6262
|
}, [hasRealmRole, currentUser?.institutionRegistrations]);
|
|
6332
6263
|
const goToLogin = React2.useCallback(() => {
|
|
6333
|
-
// Em modo mobile, notificar o app para fazer login
|
|
6334
|
-
if (isMobileAuth && window.ReactNativeWebView) {
|
|
6335
|
-
window.ReactNativeWebView.postMessage(JSON.stringify({ type: "LOGIN_REQUESTED" }));
|
|
6336
|
-
return Promise.resolve();
|
|
6337
|
-
}
|
|
6338
6264
|
return auth.signinRedirect();
|
|
6339
6265
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
6340
|
-
}, [
|
|
6266
|
+
}, []);
|
|
6341
6267
|
// Memoizar o value do context para evitar re-renders desnecessários
|
|
6342
6268
|
const contextValue = React2.useMemo(() => ({
|
|
6343
|
-
isInitialized:
|
|
6269
|
+
isInitialized: !isLoading || isRefreshing,
|
|
6344
6270
|
isTokenReady,
|
|
6345
|
-
isMobileAuth,
|
|
6346
6271
|
user: currentUser,
|
|
6347
6272
|
refreshUserData,
|
|
6348
6273
|
signOut,
|
|
6349
|
-
isAuthenticated: () =>
|
|
6274
|
+
isAuthenticated: () => isAuthenticated,
|
|
6350
6275
|
hasSchool,
|
|
6351
6276
|
goToLogin,
|
|
6352
6277
|
hasRealmRole,
|
|
6353
6278
|
hasClientRole,
|
|
6354
|
-
accessToken
|
|
6279
|
+
accessToken,
|
|
6355
6280
|
apiClient,
|
|
6356
6281
|
services,
|
|
6357
6282
|
}), [
|
|
6358
|
-
isMobileAuth,
|
|
6359
6283
|
isLoading,
|
|
6360
6284
|
isTokenReady,
|
|
6361
6285
|
currentUser,
|
|
6362
6286
|
refreshUserData,
|
|
6363
6287
|
signOut,
|
|
6364
|
-
|
|
6288
|
+
isAuthenticated,
|
|
6365
6289
|
hasSchool,
|
|
6366
6290
|
goToLogin,
|
|
6367
6291
|
hasRealmRole,
|
|
6368
6292
|
hasClientRole,
|
|
6369
|
-
|
|
6293
|
+
accessToken,
|
|
6370
6294
|
apiClient,
|
|
6371
6295
|
services,
|
|
6372
6296
|
]);
|