@iblai/web-utils 0.3.0 → 1.0.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/README.md +504 -0
- package/dist/data-layer/src/features/chat-files/api-slice.d.ts +185 -0
- package/dist/data-layer/src/features/chat-files/types.d.ts +32 -0
- package/dist/data-layer/src/features/core/api-slice.d.ts +419 -61
- package/dist/data-layer/src/features/core/constants.d.ts +3 -0
- package/dist/data-layer/src/features/core/custom-api-slice.d.ts +50 -50
- package/dist/data-layer/src/features/core/custom-public-image-asset-api-slice.d.ts +333 -0
- package/dist/data-layer/src/features/core/types.d.ts +33 -0
- package/dist/data-layer/src/features/credentials/api-slice.d.ts +62 -39
- package/dist/data-layer/src/features/mentor/api-slice.d.ts +980 -188
- package/dist/data-layer/src/features/notifications/constants.d.ts +6 -0
- package/dist/data-layer/src/features/notifications/custom-api-slice.d.ts +43 -43
- package/dist/data-layer/src/features/notifications/types.d.ts +25 -2
- package/dist/data-layer/src/index.d.ts +3 -0
- package/dist/index.d.ts +425 -66
- package/dist/index.esm.js +999 -109
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +1030 -107
- package/dist/index.js.map +1 -1
- package/dist/package.json +4 -2
- package/dist/web-utils/src/constants/chat.d.ts +8 -0
- package/dist/web-utils/src/features/files/filesSlice.d.ts +20 -0
- package/dist/web-utils/src/features/index.d.ts +1 -0
- package/dist/web-utils/src/hooks/chat/use-advanced-chat.d.ts +6 -4
- package/dist/web-utils/src/hooks/chat/use-chat-v2.d.ts +11 -1
- package/dist/web-utils/src/index.d.ts +2 -0
- package/dist/web-utils/src/index.web.d.ts +14 -12
- package/dist/web-utils/src/providers/auth-provider.d.ts +9 -1
- package/dist/web-utils/src/providers/tenant-provider.d.ts +2 -1
- package/dist/web-utils/src/services/__tests__/file-upload.test.d.ts +1 -0
- package/dist/web-utils/src/services/file-upload.d.ts +60 -0
- package/dist/web-utils/src/services/index.d.ts +1 -0
- package/dist/web-utils/src/types/file-upload.d.ts +62 -0
- package/dist/web-utils/src/types/index.d.ts +1 -0
- package/dist/web-utils/src/utils/auth.d.ts +180 -0
- package/dist/web-utils/src/utils/index.d.ts +1 -0
- package/dist/web-utils/tsconfig.tsbuildinfo +1 -1
- package/package.json +11 -12
package/dist/index.esm.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import React__default, { useCallback, useDebugValue, useMemo, useState, useEffect, useRef, useLayoutEffect, createContext, useContext } from 'react';
|
|
3
3
|
import { OpenAPI, CoreService, ReportsService, AiAnalyticsService, AiMentorService, SearchService, AiPromptService, AiIndexService, CatalogService, SkillsService, AiAccountService, CredentialsService, FeaturesService, CommerceService, NotificationsService } from '@iblai/iblai-api';
|
|
4
|
-
import { jsx
|
|
4
|
+
import { jsx } from 'react/jsx-runtime';
|
|
5
|
+
import axios from 'axios';
|
|
5
6
|
|
|
6
7
|
const SUBSCRIPTION_TRIGGERS = {
|
|
7
8
|
PRICING_MODAL: "TRIGGER_PRICING_MODAL",
|
|
@@ -3380,7 +3381,7 @@ function updateMutationSubstateIfExists(state, id, update) {
|
|
|
3380
3381
|
update(substate);
|
|
3381
3382
|
}
|
|
3382
3383
|
}
|
|
3383
|
-
var initialState$
|
|
3384
|
+
var initialState$2 = {};
|
|
3384
3385
|
function buildSlice({
|
|
3385
3386
|
reducerPath,
|
|
3386
3387
|
queryThunk,
|
|
@@ -3456,7 +3457,7 @@ function buildSlice({
|
|
|
3456
3457
|
}
|
|
3457
3458
|
const querySlice = createSlice$1({
|
|
3458
3459
|
name: `${reducerPath}/queries`,
|
|
3459
|
-
initialState: initialState$
|
|
3460
|
+
initialState: initialState$2,
|
|
3460
3461
|
reducers: {
|
|
3461
3462
|
removeQueryResult: {
|
|
3462
3463
|
reducer(draft, {
|
|
@@ -3590,7 +3591,7 @@ function buildSlice({
|
|
|
3590
3591
|
});
|
|
3591
3592
|
const mutationSlice = createSlice$1({
|
|
3592
3593
|
name: `${reducerPath}/mutations`,
|
|
3593
|
-
initialState: initialState$
|
|
3594
|
+
initialState: initialState$2,
|
|
3594
3595
|
reducers: {
|
|
3595
3596
|
removeMutationResult: {
|
|
3596
3597
|
reducer(draft, {
|
|
@@ -3759,7 +3760,7 @@ function buildSlice({
|
|
|
3759
3760
|
}
|
|
3760
3761
|
const subscriptionSlice = createSlice$1({
|
|
3761
3762
|
name: `${reducerPath}/subscriptions`,
|
|
3762
|
-
initialState: initialState$
|
|
3763
|
+
initialState: initialState$2,
|
|
3763
3764
|
reducers: {
|
|
3764
3765
|
updateSubscriptionOptions(d, a) {
|
|
3765
3766
|
},
|
|
@@ -3771,7 +3772,7 @@ function buildSlice({
|
|
|
3771
3772
|
});
|
|
3772
3773
|
const internalSubscriptionsSlice = createSlice$1({
|
|
3773
3774
|
name: `${reducerPath}/internalSubscriptions`,
|
|
3774
|
-
initialState: initialState$
|
|
3775
|
+
initialState: initialState$2,
|
|
3775
3776
|
reducers: {
|
|
3776
3777
|
subscriptionsUpdated: {
|
|
3777
3778
|
reducer(state, action) {
|
|
@@ -6441,6 +6442,25 @@ createApi({
|
|
|
6441
6442
|
}),
|
|
6442
6443
|
});
|
|
6443
6444
|
|
|
6445
|
+
createApi({
|
|
6446
|
+
reducerPath: 'chatFilesApiSlice',
|
|
6447
|
+
baseQuery: iblFetchBaseQuery,
|
|
6448
|
+
endpoints: (builder) => ({
|
|
6449
|
+
/**
|
|
6450
|
+
* Get presigned S3 URL for file upload
|
|
6451
|
+
* POST /api/ai-mentor/orgs/{org}/users/{userId}/chat/files/upload-url/
|
|
6452
|
+
*/
|
|
6453
|
+
getFileUploadUrl: builder.mutation({
|
|
6454
|
+
query: ({ org, userId, requestBody, }) => ({
|
|
6455
|
+
url: `/api/ai-mentor/orgs/${org}/users/${userId}/chat/files/upload-url/`,
|
|
6456
|
+
method: 'POST',
|
|
6457
|
+
body: requestBody,
|
|
6458
|
+
service: SERVICES.DM,
|
|
6459
|
+
}),
|
|
6460
|
+
}),
|
|
6461
|
+
}),
|
|
6462
|
+
});
|
|
6463
|
+
|
|
6444
6464
|
createApi({
|
|
6445
6465
|
reducerPath: 'llmsApiSlice',
|
|
6446
6466
|
baseQuery: iblFakeBaseQuery,
|
|
@@ -6461,6 +6481,7 @@ const mentorApiSlice = createApi({
|
|
|
6461
6481
|
'mentorPublicSettings',
|
|
6462
6482
|
'shareableLinks',
|
|
6463
6483
|
'publicMentors',
|
|
6484
|
+
'moderationLogs',
|
|
6464
6485
|
],
|
|
6465
6486
|
endpoints: (builder) => ({
|
|
6466
6487
|
createMentor: builder.mutation({
|
|
@@ -6526,7 +6547,7 @@ const mentorApiSlice = createApi({
|
|
|
6526
6547
|
}));
|
|
6527
6548
|
}
|
|
6528
6549
|
catch (error) {
|
|
6529
|
-
console.error(error);
|
|
6550
|
+
console.error(JSON.stringify(error));
|
|
6530
6551
|
}
|
|
6531
6552
|
},
|
|
6532
6553
|
}),
|
|
@@ -6582,9 +6603,17 @@ const mentorApiSlice = createApi({
|
|
|
6582
6603
|
...buildEndpointFromDmService(AiMentorService.aiMentorOrgsUsersMentorsForkCreate),
|
|
6583
6604
|
invalidatesTags: ['mentors'],
|
|
6584
6605
|
}),
|
|
6606
|
+
getRecentlyAccessedMentors: builder.query({
|
|
6607
|
+
...buildEndpointFromDmService(AiMentorService.aiMentorOrgsUsersRecentlyAccessedMentorsList),
|
|
6608
|
+
providesTags: ['mentors'],
|
|
6609
|
+
}),
|
|
6610
|
+
getModerationLogs: builder.query({
|
|
6611
|
+
...buildEndpointFromDmService(AiMentorService.aiMentorOrgsUsersModerationLogsList),
|
|
6612
|
+
providesTags: ['moderationLogs'],
|
|
6613
|
+
}),
|
|
6585
6614
|
}),
|
|
6586
6615
|
});
|
|
6587
|
-
const { useLazyGetMentorsQuery, useLazySeedMentorsQuery, useGetMentorSettingsQuery, useGetMentorPublicSettingsQuery, useLazyGetMentorPublicSettingsQuery, useLazyGetFreeUsageCountQuery} = mentorApiSlice;
|
|
6616
|
+
const { useLazyGetMentorsQuery, useLazySeedMentorsQuery, useGetMentorSettingsQuery, useGetMentorPublicSettingsQuery, useLazyGetMentorPublicSettingsQuery, useLazyGetFreeUsageCountQuery, useLazyGetRecentlyAccessedMentorsQuery} = mentorApiSlice;
|
|
6588
6617
|
|
|
6589
6618
|
createApi({
|
|
6590
6619
|
reducerPath: 'mentorCategoriesApiSlice',
|
|
@@ -7051,7 +7080,20 @@ const coreApiSlice = createApi({
|
|
|
7051
7080
|
invalidatesTags: ['RbacGroups'],
|
|
7052
7081
|
}),
|
|
7053
7082
|
getRbacGroupDetails: builder.query({
|
|
7054
|
-
...
|
|
7083
|
+
...buildEndpointFromService(SERVICES.DM, async (args) => {
|
|
7084
|
+
const url = `${OpenAPI.BASE}/api/core/rbac/groups/${args.id}/`;
|
|
7085
|
+
const response = await fetch(url, {
|
|
7086
|
+
method: 'GET',
|
|
7087
|
+
headers: OpenAPI.HEADERS,
|
|
7088
|
+
});
|
|
7089
|
+
if (!response.ok) {
|
|
7090
|
+
const error = new Error('Failed to fetch RBAC group details');
|
|
7091
|
+
error.status = response.status;
|
|
7092
|
+
error.body = await response.text();
|
|
7093
|
+
throw error;
|
|
7094
|
+
}
|
|
7095
|
+
return response.json();
|
|
7096
|
+
}),
|
|
7055
7097
|
providesTags: ['RbacGroups'],
|
|
7056
7098
|
}),
|
|
7057
7099
|
//RBAC POLICIES
|
|
@@ -7072,7 +7114,22 @@ const coreApiSlice = createApi({
|
|
|
7072
7114
|
invalidatesTags: ['RbacPolicies'],
|
|
7073
7115
|
}),
|
|
7074
7116
|
getRbacPolicyDetails: builder.query({
|
|
7075
|
-
...
|
|
7117
|
+
...buildEndpointFromService(SERVICES.DM, async (args) => {
|
|
7118
|
+
const queryParams = new URLSearchParams({ platform_key: args.platform_key });
|
|
7119
|
+
const queryString = queryParams.toString();
|
|
7120
|
+
const url = `${OpenAPI.BASE}/api/core/rbac/policies/${args.id}/?${queryString}`;
|
|
7121
|
+
const response = await fetch(url, {
|
|
7122
|
+
method: 'GET',
|
|
7123
|
+
headers: OpenAPI.HEADERS,
|
|
7124
|
+
});
|
|
7125
|
+
if (!response.ok) {
|
|
7126
|
+
const error = new Error('Failed to fetch RBAC policy details');
|
|
7127
|
+
error.status = response.status;
|
|
7128
|
+
error.body = await response.text();
|
|
7129
|
+
throw error;
|
|
7130
|
+
}
|
|
7131
|
+
return response.json();
|
|
7132
|
+
}),
|
|
7076
7133
|
providesTags: ['RbacPolicies'],
|
|
7077
7134
|
}),
|
|
7078
7135
|
//RBAC ROLES
|
|
@@ -7097,14 +7154,36 @@ const coreApiSlice = createApi({
|
|
|
7097
7154
|
invalidatesTags: ['RbacRoles'],
|
|
7098
7155
|
}),
|
|
7099
7156
|
getRbacRoleDetails: builder.query({
|
|
7100
|
-
...
|
|
7157
|
+
...buildEndpointFromService(SERVICES.DM, async (args) => {
|
|
7158
|
+
const queryParams = new URLSearchParams({ platform_key: args.platform_key });
|
|
7159
|
+
const queryString = queryParams.toString();
|
|
7160
|
+
const url = `${OpenAPI.BASE}/api/core/rbac/roles/${args.id}/?${queryString}`;
|
|
7161
|
+
const response = await fetch(url, {
|
|
7162
|
+
method: 'GET',
|
|
7163
|
+
headers: OpenAPI.HEADERS,
|
|
7164
|
+
});
|
|
7165
|
+
if (!response.ok) {
|
|
7166
|
+
const error = new Error('Failed to fetch RBAC role details');
|
|
7167
|
+
error.status = response.status;
|
|
7168
|
+
error.body = await response.text();
|
|
7169
|
+
throw error;
|
|
7170
|
+
}
|
|
7171
|
+
return response.json();
|
|
7172
|
+
}),
|
|
7101
7173
|
providesTags: ['RbacRoles'],
|
|
7102
7174
|
}),
|
|
7175
|
+
getRbacMentorAccessList: builder.query({
|
|
7176
|
+
...buildEndpointFromDmService(CoreService.coreRbacMentorAccessList),
|
|
7177
|
+
}),
|
|
7178
|
+
updateRbacMentorAccess: builder.mutation({
|
|
7179
|
+
...buildEndpointFromDmService(CoreService.coreRbacMentorAccessCreate),
|
|
7180
|
+
}),
|
|
7103
7181
|
}),
|
|
7104
7182
|
});
|
|
7105
7183
|
const { useGetRbacPermissionsMutation} = coreApiSlice;
|
|
7106
7184
|
|
|
7107
7185
|
const CORE_CUSTOM_REDUCER_PATH = 'coreCustomApiSlice';
|
|
7186
|
+
const CORE_FAKE_CUSTOM_REDUCER_PATH = 'coreFakeCustomApiSlice';
|
|
7108
7187
|
const PLATFORM_MEMBERSHIP_TAG = 'PlatformMembership';
|
|
7109
7188
|
const CORE_CUSTOM_ENDPOINTS = {
|
|
7110
7189
|
GET_PLATFORM_MEMBERSHIP: {
|
|
@@ -7138,7 +7217,13 @@ const CORE_CUSTOM_ENDPOINTS = {
|
|
|
7138
7217
|
DELETE_PLATFORM_IMAGE_ASSET: {
|
|
7139
7218
|
path: (platform_key, asset_id) => `/api/core/platforms/${platform_key}/public-image-assets/${asset_id}/`,
|
|
7140
7219
|
service: SERVICES.DM,
|
|
7141
|
-
}
|
|
7220
|
+
},
|
|
7221
|
+
GET_PUBLIC_PLATFORM_IMAGE_ASSET_FILE_URL: {
|
|
7222
|
+
path: (platform_key, asset_id) => `/api/core/platforms/${platform_key}/public-image-assets/${asset_id}/file/`,
|
|
7223
|
+
baseUrl: getServiceUrl(SERVICES.DM),
|
|
7224
|
+
service: SERVICES.DM,
|
|
7225
|
+
},
|
|
7226
|
+
};
|
|
7142
7227
|
|
|
7143
7228
|
createApi({
|
|
7144
7229
|
// TODO: replace to catalogSlice
|
|
@@ -7210,7 +7295,13 @@ createApi({
|
|
|
7210
7295
|
...buildEndpointFromDmService(AiAccountService.aiAccountOrgsIntegrationCredentialPartialUpdate),
|
|
7211
7296
|
}),
|
|
7212
7297
|
getConnectedServiceAuthUrl: builder.query({
|
|
7213
|
-
|
|
7298
|
+
queryFn: async (queryParams, api, extraOptions) => {
|
|
7299
|
+
const url = `/api/ai-account/connected-services/orgs/${queryParams.org}/users/${queryParams.userId}/${queryParams.provider}/${queryParams.service}/`;
|
|
7300
|
+
return iblFetchBaseQuery({
|
|
7301
|
+
url,
|
|
7302
|
+
service: SERVICES.DM,
|
|
7303
|
+
}, api, extraOptions);
|
|
7304
|
+
},
|
|
7214
7305
|
}),
|
|
7215
7306
|
connectedServicesCallback: builder.query({
|
|
7216
7307
|
queryFn: async (queryParams, api, extraOptions) => {
|
|
@@ -7342,7 +7433,7 @@ const sessionApiSlice = createApi({
|
|
|
7342
7433
|
}),
|
|
7343
7434
|
}),
|
|
7344
7435
|
});
|
|
7345
|
-
const { useCreateSessionIdMutation, useEditSessionMutation, } = sessionApiSlice;
|
|
7436
|
+
const { useCreateSessionIdMutation, useLazyGetSessionIdQuery, useEditSessionMutation, } = sessionApiSlice;
|
|
7346
7437
|
|
|
7347
7438
|
createApi({
|
|
7348
7439
|
reducerPath: 'datasetsApiSlice',
|
|
@@ -8279,6 +8370,12 @@ createApi({
|
|
|
8279
8370
|
});
|
|
8280
8371
|
|
|
8281
8372
|
const NOTIFICATIONS_CUSTOM_REDUCER_PATH = 'notificationsCustomApiSlice';
|
|
8373
|
+
const NOTIFICATIONS_CUSTOM_TAGS = {
|
|
8374
|
+
TEMPLATES: 'NOTIFICATIONS_CUSTOM_TEMPLATES',
|
|
8375
|
+
TEMPLATE_DETAILS: 'NOTIFICATIONS_CUSTOM_TEMPLATE_DETAILS',
|
|
8376
|
+
UPDATE_TEMPLATE: 'NOTIFICATIONS_CUSTOM_UPDATE_TEMPLATE',
|
|
8377
|
+
TOGGLE_TEMPLATE: 'NOTIFICATIONS_CUSTOM_TOGGLE_TEMPLATE',
|
|
8378
|
+
};
|
|
8282
8379
|
const NOTIFICATIONS_CUSTOM_ENDPOINTS = {
|
|
8283
8380
|
GET_TEMPLATES: {
|
|
8284
8381
|
service: SERVICES.DM,
|
|
@@ -8300,6 +8397,7 @@ const NOTIFICATIONS_CUSTOM_ENDPOINTS = {
|
|
|
8300
8397
|
|
|
8301
8398
|
createApi({
|
|
8302
8399
|
reducerPath: NOTIFICATIONS_CUSTOM_REDUCER_PATH,
|
|
8400
|
+
tagTypes: [...Object.values(NOTIFICATIONS_CUSTOM_TAGS)],
|
|
8303
8401
|
baseQuery: iblFetchBaseQuery,
|
|
8304
8402
|
endpoints: (builder) => ({
|
|
8305
8403
|
getTemplates: builder.query({
|
|
@@ -8309,6 +8407,7 @@ createApi({
|
|
|
8309
8407
|
service: NOTIFICATIONS_CUSTOM_ENDPOINTS.GET_TEMPLATES.service,
|
|
8310
8408
|
};
|
|
8311
8409
|
},
|
|
8410
|
+
providesTags: [NOTIFICATIONS_CUSTOM_TAGS.TEMPLATES],
|
|
8312
8411
|
}),
|
|
8313
8412
|
getTemplateDetails: builder.query({
|
|
8314
8413
|
query: (args) => {
|
|
@@ -8317,6 +8416,7 @@ createApi({
|
|
|
8317
8416
|
service: NOTIFICATIONS_CUSTOM_ENDPOINTS.GET_TEMPLATE_DETAILS.service,
|
|
8318
8417
|
};
|
|
8319
8418
|
},
|
|
8419
|
+
providesTags: [NOTIFICATIONS_CUSTOM_TAGS.TEMPLATE_DETAILS],
|
|
8320
8420
|
}),
|
|
8321
8421
|
updateTemplate: builder.mutation({
|
|
8322
8422
|
query: (args) => {
|
|
@@ -8327,6 +8427,7 @@ createApi({
|
|
|
8327
8427
|
body: args.template,
|
|
8328
8428
|
};
|
|
8329
8429
|
},
|
|
8430
|
+
invalidatesTags: [NOTIFICATIONS_CUSTOM_TAGS.TEMPLATE_DETAILS],
|
|
8330
8431
|
}),
|
|
8331
8432
|
toggleTemplate: builder.mutation({
|
|
8332
8433
|
query: (args) => {
|
|
@@ -8339,6 +8440,7 @@ createApi({
|
|
|
8339
8440
|
},
|
|
8340
8441
|
};
|
|
8341
8442
|
},
|
|
8443
|
+
invalidatesTags: [NOTIFICATIONS_CUSTOM_TAGS.TEMPLATES],
|
|
8342
8444
|
}),
|
|
8343
8445
|
}),
|
|
8344
8446
|
});
|
|
@@ -8619,7 +8721,7 @@ const CUSTOM_DOMAIN_QUERY_KEYS = {
|
|
|
8619
8721
|
CREATE_CUSTOM_DOMAIN: () => ['CREATE_CUSTOM_DOMAIN'],
|
|
8620
8722
|
};
|
|
8621
8723
|
|
|
8622
|
-
createApi({
|
|
8724
|
+
const customDomainApiSlice = createApi({
|
|
8623
8725
|
reducerPath: CUSTOM_DOMAIN_REDUCER_PATH,
|
|
8624
8726
|
baseQuery: iblFetchBaseQuery,
|
|
8625
8727
|
tagTypes: [...CUSTOM_DOMAIN_QUERY_KEYS.GET_CUSTOM_DOMAINS()],
|
|
@@ -8658,6 +8760,7 @@ createApi({
|
|
|
8658
8760
|
}),
|
|
8659
8761
|
}),
|
|
8660
8762
|
});
|
|
8763
|
+
const { useGetCustomDomainsQuery} = customDomainApiSlice;
|
|
8661
8764
|
|
|
8662
8765
|
const PLATFORM_CUSTOM_REDUCER_PATH = "platformCustomApiSlice";
|
|
8663
8766
|
const PLATFORM_CONFIGURATION_TAG = "PlatformConfiguration";
|
|
@@ -8720,7 +8823,7 @@ createApi({
|
|
|
8720
8823
|
const coreCustomApiSlice = createApi({
|
|
8721
8824
|
reducerPath: CORE_CUSTOM_REDUCER_PATH,
|
|
8722
8825
|
baseQuery: iblFetchBaseQuery,
|
|
8723
|
-
tagTypes: [PLATFORM_MEMBERSHIP_TAG],
|
|
8826
|
+
tagTypes: [PLATFORM_MEMBERSHIP_TAG, 'PlatformImageAssetFileUrl'],
|
|
8724
8827
|
endpoints: (builder) => ({
|
|
8725
8828
|
getPlatformMembership: builder.query({
|
|
8726
8829
|
query: ({ platform_key }) => ({
|
|
@@ -8793,6 +8896,19 @@ const coreCustomApiSlice = createApi({
|
|
|
8793
8896
|
});
|
|
8794
8897
|
const { useJoinTenantMutation} = coreCustomApiSlice;
|
|
8795
8898
|
|
|
8899
|
+
createApi({
|
|
8900
|
+
reducerPath: CORE_FAKE_CUSTOM_REDUCER_PATH,
|
|
8901
|
+
baseQuery: iblFakeBaseQuery,
|
|
8902
|
+
endpoints: (builder) => ({
|
|
8903
|
+
getPublicPlatformImageAssetFileUrl: builder.query({
|
|
8904
|
+
...buildEndpointFromService(SERVICES.DM, async (args) => {
|
|
8905
|
+
const url = `${OpenAPI.BASE}${CORE_CUSTOM_ENDPOINTS.GET_PUBLIC_PLATFORM_IMAGE_ASSET_FILE_URL.path(args.platform_key, args.asset_id)}`;
|
|
8906
|
+
return url;
|
|
8907
|
+
}),
|
|
8908
|
+
}),
|
|
8909
|
+
}),
|
|
8910
|
+
});
|
|
8911
|
+
|
|
8796
8912
|
const EDX_PROCTORING_ENDPOINTS = {
|
|
8797
8913
|
GET_EXAM_INFO: {
|
|
8798
8914
|
service: SERVICES.LMS,
|
|
@@ -10305,6 +10421,299 @@ const formatRelativeTime = (timestamp) => {
|
|
|
10305
10421
|
return `${diffInYears} year${diffInYears === 1 ? "" : "s"} ago`;
|
|
10306
10422
|
};
|
|
10307
10423
|
|
|
10424
|
+
/**
|
|
10425
|
+
* Authentication and Authorization Utilities
|
|
10426
|
+
*
|
|
10427
|
+
* This module provides utility functions for handling authentication flows,
|
|
10428
|
+
* including redirects to auth SPA, cookie management, and session handling.
|
|
10429
|
+
*/
|
|
10430
|
+
/**
|
|
10431
|
+
* Check if the current window is inside an iframe
|
|
10432
|
+
* @returns {boolean} True if running in an iframe, false otherwise
|
|
10433
|
+
*/
|
|
10434
|
+
function isInIframe() {
|
|
10435
|
+
if (typeof window === "undefined") {
|
|
10436
|
+
return false;
|
|
10437
|
+
}
|
|
10438
|
+
return (window === null || window === void 0 ? void 0 : window.self) !== (window === null || window === void 0 ? void 0 : window.top);
|
|
10439
|
+
}
|
|
10440
|
+
/**
|
|
10441
|
+
* Send a message to the parent website (when in iframe)
|
|
10442
|
+
* @param payload - The data to send to parent window
|
|
10443
|
+
*/
|
|
10444
|
+
function sendMessageToParentWebsite(payload) {
|
|
10445
|
+
window.parent.postMessage(payload, "*");
|
|
10446
|
+
}
|
|
10447
|
+
/**
|
|
10448
|
+
* Delete a cookie with specific name, path, and domain
|
|
10449
|
+
* @param name - Cookie name
|
|
10450
|
+
* @param path - Cookie path
|
|
10451
|
+
* @param domain - Cookie domain
|
|
10452
|
+
*/
|
|
10453
|
+
function deleteCookie(name, path, domain) {
|
|
10454
|
+
const expires = "expires=Thu, 01 Jan 1970 00:00:00 UTC;";
|
|
10455
|
+
const cookieValue = `${name}=;`;
|
|
10456
|
+
const pathValue = path ? `path=${path};` : "";
|
|
10457
|
+
const domainValue = domain ? `domain=${domain};` : "";
|
|
10458
|
+
document.cookie = cookieValue + expires + pathValue + domainValue;
|
|
10459
|
+
}
|
|
10460
|
+
/**
|
|
10461
|
+
* Get all possible domain parts for cookie deletion
|
|
10462
|
+
* @param domain - The domain to split
|
|
10463
|
+
* @returns Array of domain parts
|
|
10464
|
+
* @example
|
|
10465
|
+
* getDomainParts('app.example.com') // returns ['app.example.com', 'example.com', 'com']
|
|
10466
|
+
*/
|
|
10467
|
+
function getDomainParts(domain) {
|
|
10468
|
+
const parts = domain.split(".");
|
|
10469
|
+
const domains = [];
|
|
10470
|
+
for (let i = parts.length - 1; i >= 0; i--) {
|
|
10471
|
+
domains.push(parts.slice(i).join("."));
|
|
10472
|
+
}
|
|
10473
|
+
return domains;
|
|
10474
|
+
}
|
|
10475
|
+
/**
|
|
10476
|
+
* Delete a cookie across all possible domain variations
|
|
10477
|
+
* @param name - Cookie name to delete
|
|
10478
|
+
* @param childDomain - The current domain
|
|
10479
|
+
*/
|
|
10480
|
+
function deleteCookieOnAllDomains(name, childDomain) {
|
|
10481
|
+
getDomainParts(childDomain).forEach((domainPart) => {
|
|
10482
|
+
deleteCookie(name, "/", domainPart);
|
|
10483
|
+
deleteCookie(name, "", domainPart);
|
|
10484
|
+
});
|
|
10485
|
+
}
|
|
10486
|
+
/**
|
|
10487
|
+
* Get the parent domain from a given domain
|
|
10488
|
+
* @param domain - The domain to process
|
|
10489
|
+
* @returns The parent domain (e.g., 'app.example.com' → '.example.com')
|
|
10490
|
+
*/
|
|
10491
|
+
function getParentDomain(domain) {
|
|
10492
|
+
if (!domain) {
|
|
10493
|
+
return "";
|
|
10494
|
+
}
|
|
10495
|
+
const parts = domain.split(".");
|
|
10496
|
+
return parts.length > 1 ? `.${parts.slice(-2).join(".")}` : domain;
|
|
10497
|
+
}
|
|
10498
|
+
/**
|
|
10499
|
+
* Set a cookie for authentication with cross-domain support
|
|
10500
|
+
* @param name - Cookie name
|
|
10501
|
+
* @param value - Cookie value
|
|
10502
|
+
* @param days - Number of days until expiration (default: 365)
|
|
10503
|
+
*/
|
|
10504
|
+
function setCookieForAuth(name, value, days = 365) {
|
|
10505
|
+
const expires = new Date();
|
|
10506
|
+
expires.setTime(expires.getTime() + days * 24 * 60 * 60 * 1000);
|
|
10507
|
+
const hostname = window.location.hostname;
|
|
10508
|
+
let baseDomain = hostname;
|
|
10509
|
+
// Calculate base domain (skip for localhost and IP addresses)
|
|
10510
|
+
if (hostname !== "localhost" && !/^\d+\.\d+\.\d+\.\d+$/.test(hostname)) {
|
|
10511
|
+
const parts = hostname.split(".");
|
|
10512
|
+
if (parts.length > 2) {
|
|
10513
|
+
baseDomain = `.${parts.slice(-2).join(".")}`;
|
|
10514
|
+
}
|
|
10515
|
+
}
|
|
10516
|
+
const domainAttr = baseDomain ? `;domain=${baseDomain}` : "";
|
|
10517
|
+
document.cookie = `${name}=${encodeURIComponent(value)};expires=${expires.toUTCString()};path=/;SameSite=None;Secure${domainAttr}`;
|
|
10518
|
+
}
|
|
10519
|
+
/**
|
|
10520
|
+
* Clear all cookies for the current domain
|
|
10521
|
+
*/
|
|
10522
|
+
function clearCookies() {
|
|
10523
|
+
const cookies = document.cookie.split(";");
|
|
10524
|
+
for (let i = 0; i < cookies.length; i++) {
|
|
10525
|
+
const cookie = cookies[i];
|
|
10526
|
+
const eqPos = cookie.indexOf("=");
|
|
10527
|
+
const name = eqPos > -1 ? cookie.substr(0, eqPos) : cookie;
|
|
10528
|
+
deleteCookieOnAllDomains(name, window.location.hostname);
|
|
10529
|
+
document.cookie = `${name}=;expires=Thu, 01 Jan 1970 00:00:00 GMT;path=/;Domain=${getParentDomain(window.location.hostname)}`;
|
|
10530
|
+
}
|
|
10531
|
+
}
|
|
10532
|
+
/**
|
|
10533
|
+
* Check if user is currently logged in
|
|
10534
|
+
* @param tokenKey - The localStorage key for the auth token (default: 'axd_token')
|
|
10535
|
+
* @returns True if logged in, false otherwise
|
|
10536
|
+
*/
|
|
10537
|
+
function isLoggedIn(tokenKey = "axd_token") {
|
|
10538
|
+
return !!localStorage.getItem(tokenKey);
|
|
10539
|
+
}
|
|
10540
|
+
/**
|
|
10541
|
+
* Extract platform/tenant key from URL
|
|
10542
|
+
* @param url - The URL to parse
|
|
10543
|
+
* @param pattern - RegExp pattern to match platform key (default matches /platform/[key]/...)
|
|
10544
|
+
* @returns The platform key or null if not found
|
|
10545
|
+
*/
|
|
10546
|
+
function getPlatformKey(url, pattern = /\/platform\/([^/]+)/) {
|
|
10547
|
+
const match = url.match(pattern);
|
|
10548
|
+
return match ? match[1] : null;
|
|
10549
|
+
}
|
|
10550
|
+
/**
|
|
10551
|
+
* Redirect to authentication SPA for login/logout
|
|
10552
|
+
*
|
|
10553
|
+
* This function handles the complete authentication flow:
|
|
10554
|
+
* - Clears localStorage and cookies on logout
|
|
10555
|
+
* - Saves redirect path for post-auth return
|
|
10556
|
+
* - Handles iframe scenarios
|
|
10557
|
+
* - Syncs logout across multiple SPAs via cookies
|
|
10558
|
+
*
|
|
10559
|
+
* @param options - Configuration options for the redirect
|
|
10560
|
+
*
|
|
10561
|
+
* @example
|
|
10562
|
+
* ```tsx
|
|
10563
|
+
* // Basic usage
|
|
10564
|
+
* redirectToAuthSpa({
|
|
10565
|
+
* authUrl: 'https://auth.example.com',
|
|
10566
|
+
* appName: 'mentor',
|
|
10567
|
+
* });
|
|
10568
|
+
*
|
|
10569
|
+
* // With logout
|
|
10570
|
+
* redirectToAuthSpa({
|
|
10571
|
+
* authUrl: 'https://auth.example.com',
|
|
10572
|
+
* appName: 'mentor',
|
|
10573
|
+
* logout: true,
|
|
10574
|
+
* platformKey: 'my-tenant',
|
|
10575
|
+
* });
|
|
10576
|
+
*
|
|
10577
|
+
* // With custom redirect
|
|
10578
|
+
* redirectToAuthSpa({
|
|
10579
|
+
* authUrl: 'https://auth.example.com',
|
|
10580
|
+
* appName: 'mentor',
|
|
10581
|
+
* redirectTo: '/dashboard',
|
|
10582
|
+
* platformKey: 'my-tenant',
|
|
10583
|
+
* });
|
|
10584
|
+
* ```
|
|
10585
|
+
*/
|
|
10586
|
+
async function redirectToAuthSpa(options) {
|
|
10587
|
+
const { redirectTo, platformKey, logout = false, saveRedirect = true, authUrl, appName, queryParams = {
|
|
10588
|
+
app: "app",
|
|
10589
|
+
redirectTo: "redirect-to",
|
|
10590
|
+
tenant: "tenant",
|
|
10591
|
+
}, redirectPathStorageKey = "redirect_to", cookieNames = {
|
|
10592
|
+
currentTenant: "ibl_current_tenant",
|
|
10593
|
+
userData: "ibl_user_data",
|
|
10594
|
+
tenant: "ibl_tenant",
|
|
10595
|
+
logoutTimestamp: "ibl_logout_timestamp",
|
|
10596
|
+
}, } = options;
|
|
10597
|
+
// Clear localStorage
|
|
10598
|
+
localStorage.clear();
|
|
10599
|
+
if (logout) {
|
|
10600
|
+
// Delete authentication cookies for cross-SPA synchronization
|
|
10601
|
+
const currentDomain = window.location.hostname;
|
|
10602
|
+
if (cookieNames.currentTenant) {
|
|
10603
|
+
deleteCookieOnAllDomains(cookieNames.currentTenant, currentDomain);
|
|
10604
|
+
}
|
|
10605
|
+
if (cookieNames.userData) {
|
|
10606
|
+
deleteCookieOnAllDomains(cookieNames.userData, currentDomain);
|
|
10607
|
+
}
|
|
10608
|
+
if (cookieNames.tenant) {
|
|
10609
|
+
deleteCookieOnAllDomains(cookieNames.tenant, currentDomain);
|
|
10610
|
+
}
|
|
10611
|
+
// Set logout timestamp cookie to trigger logout on other SPAs
|
|
10612
|
+
if (cookieNames.logoutTimestamp) {
|
|
10613
|
+
setCookieForAuth(cookieNames.logoutTimestamp, Date.now().toString());
|
|
10614
|
+
}
|
|
10615
|
+
}
|
|
10616
|
+
// Handle iframe scenario
|
|
10617
|
+
if (isInIframe()) {
|
|
10618
|
+
console.log("[redirectToAuthSpa]: sending authExpired to parent");
|
|
10619
|
+
sendMessageToParentWebsite({ authExpired: true });
|
|
10620
|
+
return;
|
|
10621
|
+
}
|
|
10622
|
+
const redirectPath = redirectTo !== null && redirectTo !== void 0 ? redirectTo : `${window.location.pathname}${window.location.search}`;
|
|
10623
|
+
// Never save sso-login routes as redirect paths
|
|
10624
|
+
if (!redirectPath.startsWith("/sso-login") && saveRedirect) {
|
|
10625
|
+
window.localStorage.setItem(redirectPathStorageKey, redirectPath);
|
|
10626
|
+
}
|
|
10627
|
+
const platform = platformKey !== null && platformKey !== void 0 ? platformKey : getPlatformKey(redirectPath);
|
|
10628
|
+
const redirectToUrl = `${window.location.origin}`;
|
|
10629
|
+
let authRedirectUrl = `${authUrl}/login?${queryParams.app}=${appName}`;
|
|
10630
|
+
authRedirectUrl += `&${queryParams.redirectTo}=${redirectToUrl}`;
|
|
10631
|
+
if (platform) {
|
|
10632
|
+
authRedirectUrl += `&${queryParams.tenant}=${platform}`;
|
|
10633
|
+
}
|
|
10634
|
+
if (logout) {
|
|
10635
|
+
authRedirectUrl += "&logout=1";
|
|
10636
|
+
}
|
|
10637
|
+
// Small delay for any pending operations
|
|
10638
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
10639
|
+
// Redirect to auth SPA
|
|
10640
|
+
window.location.href = authRedirectUrl;
|
|
10641
|
+
}
|
|
10642
|
+
/**
|
|
10643
|
+
* Get the URL for joining a tenant (sign up flow)
|
|
10644
|
+
* @param authUrl - Auth SPA base URL
|
|
10645
|
+
* @param tenantKey - Tenant to join
|
|
10646
|
+
* @param redirectUrl - URL to redirect to after joining (defaults to current URL)
|
|
10647
|
+
* @returns The join URL
|
|
10648
|
+
*/
|
|
10649
|
+
function getAuthSpaJoinUrl(authUrl, tenantKey, redirectUrl) {
|
|
10650
|
+
const resolvedTenant = tenantKey || getPlatformKey(window.location.pathname) || "";
|
|
10651
|
+
if (!resolvedTenant) {
|
|
10652
|
+
return "";
|
|
10653
|
+
}
|
|
10654
|
+
const targetUrl = redirectUrl !== null && redirectUrl !== void 0 ? redirectUrl : window.location.href;
|
|
10655
|
+
const joinUrl = `${authUrl}/join?tenant=${encodeURIComponent(resolvedTenant)}&redirect-to=${encodeURIComponent(targetUrl)}`;
|
|
10656
|
+
return joinUrl;
|
|
10657
|
+
}
|
|
10658
|
+
/**
|
|
10659
|
+
* Redirect to auth SPA's join/signup page for a specific tenant
|
|
10660
|
+
* @param authUrl - Auth SPA base URL
|
|
10661
|
+
* @param tenantKey - Tenant to join
|
|
10662
|
+
* @param redirectUrl - URL to redirect to after joining (defaults to current URL)
|
|
10663
|
+
*/
|
|
10664
|
+
function redirectToAuthSpaJoinTenant(authUrl, tenantKey, redirectUrl) {
|
|
10665
|
+
const resolvedTenant = tenantKey || getPlatformKey(window.location.pathname) || "";
|
|
10666
|
+
if (!resolvedTenant) {
|
|
10667
|
+
console.log("[auth-redirect] Missing tenant key for join", {
|
|
10668
|
+
tenantKey,
|
|
10669
|
+
redirectUrl,
|
|
10670
|
+
});
|
|
10671
|
+
redirectToAuthSpa({
|
|
10672
|
+
authUrl,
|
|
10673
|
+
appName: "app", // Will need to be configured
|
|
10674
|
+
redirectTo: redirectUrl,
|
|
10675
|
+
});
|
|
10676
|
+
return;
|
|
10677
|
+
}
|
|
10678
|
+
const targetUrl = redirectUrl !== null && redirectUrl !== void 0 ? redirectUrl : window.location.href;
|
|
10679
|
+
const joinUrl = `${authUrl}/join?tenant=${encodeURIComponent(resolvedTenant)}&redirect-to=${encodeURIComponent(targetUrl)}`;
|
|
10680
|
+
window.location.href = joinUrl;
|
|
10681
|
+
}
|
|
10682
|
+
/**
|
|
10683
|
+
* Handle user logout
|
|
10684
|
+
*
|
|
10685
|
+
* This function:
|
|
10686
|
+
* - Clears localStorage (preserving tenant)
|
|
10687
|
+
* - Sets logout timestamp cookie for cross-SPA sync
|
|
10688
|
+
* - Clears all cookies
|
|
10689
|
+
* - Redirects to auth SPA logout endpoint
|
|
10690
|
+
*
|
|
10691
|
+
* @param options - Logout configuration options
|
|
10692
|
+
*
|
|
10693
|
+
* @example
|
|
10694
|
+
* ```tsx
|
|
10695
|
+
* handleLogout({
|
|
10696
|
+
* authUrl: 'https://auth.example.com',
|
|
10697
|
+
* redirectUrl: 'https://app.example.com',
|
|
10698
|
+
* });
|
|
10699
|
+
* ```
|
|
10700
|
+
*/
|
|
10701
|
+
function handleLogout(options) {
|
|
10702
|
+
const { redirectUrl = window.location.origin, authUrl, tenantStorageKey = "tenant", logoutTimestampCookie = "ibl_logout_timestamp", callback, } = options;
|
|
10703
|
+
const tenant = window.localStorage.getItem(tenantStorageKey);
|
|
10704
|
+
window.localStorage.clear();
|
|
10705
|
+
if (tenant) {
|
|
10706
|
+
window.localStorage.setItem(tenantStorageKey, tenant);
|
|
10707
|
+
}
|
|
10708
|
+
// Set logout timestamp cookie to trigger logout on other SPAs
|
|
10709
|
+
setCookieForAuth(logoutTimestampCookie, Date.now().toString());
|
|
10710
|
+
clearCookies();
|
|
10711
|
+
callback === null || callback === void 0 ? void 0 : callback();
|
|
10712
|
+
if (!isInIframe()) {
|
|
10713
|
+
window.location.href = `${authUrl}/logout?redirect-to=${redirectUrl}${tenant ? "&tenant=" + tenant : ""}`;
|
|
10714
|
+
}
|
|
10715
|
+
}
|
|
10716
|
+
|
|
10308
10717
|
const useExternalPricingPlan = ({ pricingModalData, userEmail, }) => {
|
|
10309
10718
|
const pricingBoxIframeRef = useRef(null);
|
|
10310
10719
|
const getIFrameReadyData = async () => {
|
|
@@ -10436,6 +10845,8 @@ function jwtDecode(token, options) {
|
|
|
10436
10845
|
* - Polls for cookie changes every 2 seconds
|
|
10437
10846
|
* - Refreshes the page when cross-SPA changes are detected
|
|
10438
10847
|
* - Listens for storage events to update cookies when localStorage changes
|
|
10848
|
+
* - Monitors logout timestamp cookie to trigger cross-SPA logout
|
|
10849
|
+
* - Automatically logs out when another SPA initiates logout
|
|
10439
10850
|
*
|
|
10440
10851
|
* For React Native:
|
|
10441
10852
|
* - Relies on AsyncStorage only (handled by StorageService)
|
|
@@ -10445,7 +10856,7 @@ function jwtDecode(token, options) {
|
|
|
10445
10856
|
/**
|
|
10446
10857
|
* Check if we're running in a web browser environment
|
|
10447
10858
|
*/
|
|
10448
|
-
const isWeb = () => {
|
|
10859
|
+
const isWeb$1 = () => {
|
|
10449
10860
|
return typeof window !== "undefined" && typeof document !== "undefined";
|
|
10450
10861
|
};
|
|
10451
10862
|
/**
|
|
@@ -10455,6 +10866,7 @@ const COOKIE_KEYS = {
|
|
|
10455
10866
|
CURRENT_TENANT: "ibl_current_tenant",
|
|
10456
10867
|
USER_DATA: "ibl_user_data",
|
|
10457
10868
|
TENANT: "ibl_tenant",
|
|
10869
|
+
LOGOUT_TIMESTAMP: "ibl_logout_timestamp",
|
|
10458
10870
|
};
|
|
10459
10871
|
/**
|
|
10460
10872
|
* Get the base domain for cookie sharing
|
|
@@ -10465,7 +10877,7 @@ const COOKIE_KEYS = {
|
|
|
10465
10877
|
*/
|
|
10466
10878
|
const getBaseDomain = () => {
|
|
10467
10879
|
// Must check for window existence before accessing it
|
|
10468
|
-
if (!isWeb())
|
|
10880
|
+
if (!isWeb$1())
|
|
10469
10881
|
return "";
|
|
10470
10882
|
const hostname = window.location.hostname;
|
|
10471
10883
|
// For localhost or IP addresses, use as-is
|
|
@@ -10494,7 +10906,7 @@ const CookieUtils = {
|
|
|
10494
10906
|
* Uses SameSite=None for cross-subdomain sharing and base domain
|
|
10495
10907
|
*/
|
|
10496
10908
|
set(name, value, days = 365) {
|
|
10497
|
-
if (!isWeb())
|
|
10909
|
+
if (!isWeb$1())
|
|
10498
10910
|
return;
|
|
10499
10911
|
const expires = new Date();
|
|
10500
10912
|
expires.setTime(expires.getTime() + days * 24 * 60 * 60 * 1000);
|
|
@@ -10506,7 +10918,7 @@ const CookieUtils = {
|
|
|
10506
10918
|
* Get a cookie value by name
|
|
10507
10919
|
*/
|
|
10508
10920
|
get(name) {
|
|
10509
|
-
if (!isWeb())
|
|
10921
|
+
if (!isWeb$1())
|
|
10510
10922
|
return null;
|
|
10511
10923
|
const nameEQ = name + "=";
|
|
10512
10924
|
const cookies = document.cookie.split(";");
|
|
@@ -10525,7 +10937,7 @@ const CookieUtils = {
|
|
|
10525
10937
|
* Uses same domain logic as set() to ensure proper deletion
|
|
10526
10938
|
*/
|
|
10527
10939
|
delete(name) {
|
|
10528
|
-
if (!isWeb())
|
|
10940
|
+
if (!isWeb$1())
|
|
10529
10941
|
return;
|
|
10530
10942
|
const baseDomain = getBaseDomain();
|
|
10531
10943
|
const domainAttr = baseDomain ? `;domain=${baseDomain}` : "";
|
|
@@ -10538,8 +10950,7 @@ const CookieUtils = {
|
|
|
10538
10950
|
* On React Native, this is a no-op
|
|
10539
10951
|
*/
|
|
10540
10952
|
async function syncAuthToCookies(storageService) {
|
|
10541
|
-
|
|
10542
|
-
if (!isWeb())
|
|
10953
|
+
if (!isWeb$1())
|
|
10543
10954
|
return;
|
|
10544
10955
|
try {
|
|
10545
10956
|
const currentTenant = await storageService.getItem(LOCAL_STORAGE_KEYS.CURRENT_TENANT);
|
|
@@ -10568,13 +10979,23 @@ async function syncAuthToCookies(storageService) {
|
|
|
10568
10979
|
console.error("[syncAuthToCookies] Error syncing to cookies:", error);
|
|
10569
10980
|
}
|
|
10570
10981
|
}
|
|
10982
|
+
/**
|
|
10983
|
+
* Clear current tenant cookie only (web only)
|
|
10984
|
+
* Should be called before tenant switching
|
|
10985
|
+
* On React Native, this is a no-op
|
|
10986
|
+
*/
|
|
10987
|
+
function clearCurrentTenantCookie() {
|
|
10988
|
+
if (!isWeb$1())
|
|
10989
|
+
return;
|
|
10990
|
+
CookieUtils.delete(COOKIE_KEYS.CURRENT_TENANT);
|
|
10991
|
+
}
|
|
10571
10992
|
/**
|
|
10572
10993
|
* Clear all authentication cookies (web only)
|
|
10573
10994
|
* Should be called on logout
|
|
10574
10995
|
* On React Native, this is a no-op
|
|
10575
10996
|
*/
|
|
10576
10997
|
function clearAuthCookies() {
|
|
10577
|
-
if (!isWeb())
|
|
10998
|
+
if (!isWeb$1())
|
|
10578
10999
|
return;
|
|
10579
11000
|
CookieUtils.delete(COOKIE_KEYS.CURRENT_TENANT);
|
|
10580
11001
|
CookieUtils.delete(COOKIE_KEYS.USER_DATA);
|
|
@@ -10586,14 +11007,30 @@ function clearAuthCookies() {
|
|
|
10586
11007
|
* On React Native, always returns false (no-op)
|
|
10587
11008
|
*/
|
|
10588
11009
|
async function syncCookiesToLocalStorage(storageService) {
|
|
10589
|
-
if (!isWeb())
|
|
11010
|
+
if (!isWeb$1())
|
|
10590
11011
|
return false;
|
|
10591
11012
|
try {
|
|
10592
11013
|
let needsRefresh = false;
|
|
10593
11014
|
// Check current_tenant
|
|
10594
11015
|
const cookieCurrentTenant = CookieUtils.get(COOKIE_KEYS.CURRENT_TENANT);
|
|
11016
|
+
console.log("[syncCookiesToLocalStorage] cookieCurrentTenant", cookieCurrentTenant);
|
|
10595
11017
|
const localCurrentTenant = await storageService.getItem(LOCAL_STORAGE_KEYS.CURRENT_TENANT);
|
|
10596
|
-
|
|
11018
|
+
console.log("[syncCookiesToLocalStorage] localCurrentTenant", localCurrentTenant);
|
|
11019
|
+
// Compare current tenant objects by key field only to avoid false positives from extra fields
|
|
11020
|
+
let currentTenantIsDifferent = false;
|
|
11021
|
+
if (cookieCurrentTenant && localCurrentTenant) {
|
|
11022
|
+
try {
|
|
11023
|
+
const cookieTenant = JSON.parse(cookieCurrentTenant);
|
|
11024
|
+
const localTenant = JSON.parse(localCurrentTenant);
|
|
11025
|
+
// Compare by the tenant key only
|
|
11026
|
+
currentTenantIsDifferent = cookieTenant.key !== localTenant.key;
|
|
11027
|
+
}
|
|
11028
|
+
catch (e) {
|
|
11029
|
+
// If parsing fails, fall back to string comparison
|
|
11030
|
+
currentTenantIsDifferent = cookieCurrentTenant !== localCurrentTenant;
|
|
11031
|
+
}
|
|
11032
|
+
}
|
|
11033
|
+
if (currentTenantIsDifferent) {
|
|
10597
11034
|
if (cookieCurrentTenant) {
|
|
10598
11035
|
await storageService.setItem(LOCAL_STORAGE_KEYS.CURRENT_TENANT, cookieCurrentTenant);
|
|
10599
11036
|
}
|
|
@@ -10605,8 +11042,24 @@ async function syncCookiesToLocalStorage(storageService) {
|
|
|
10605
11042
|
}
|
|
10606
11043
|
// Check userData
|
|
10607
11044
|
const cookieUserData = CookieUtils.get(COOKIE_KEYS.USER_DATA);
|
|
11045
|
+
console.log("[syncCookiesToLocalStorage] cookieUserData", cookieUserData);
|
|
10608
11046
|
const localUserData = await storageService.getItem(LOCAL_STORAGE_KEYS.USER_DATA);
|
|
10609
|
-
|
|
11047
|
+
console.log("[syncCookiesToLocalStorage] localUserData", localUserData);
|
|
11048
|
+
// Compare userData objects by user_id to avoid false positives from extra fields
|
|
11049
|
+
let userDataIsDifferent = false;
|
|
11050
|
+
if (cookieUserData && localUserData) {
|
|
11051
|
+
try {
|
|
11052
|
+
const cookieUser = JSON.parse(cookieUserData);
|
|
11053
|
+
const localUser = JSON.parse(localUserData);
|
|
11054
|
+
// Compare by user_id - the primary identifier
|
|
11055
|
+
userDataIsDifferent = cookieUser.user_id !== localUser.user_id;
|
|
11056
|
+
}
|
|
11057
|
+
catch (e) {
|
|
11058
|
+
// If parsing fails, fall back to string comparison
|
|
11059
|
+
userDataIsDifferent = cookieUserData !== localUserData;
|
|
11060
|
+
}
|
|
11061
|
+
}
|
|
11062
|
+
if (userDataIsDifferent) {
|
|
10610
11063
|
if (cookieUserData) {
|
|
10611
11064
|
await storageService.setItem(LOCAL_STORAGE_KEYS.USER_DATA, cookieUserData);
|
|
10612
11065
|
}
|
|
@@ -10618,8 +11071,37 @@ async function syncCookiesToLocalStorage(storageService) {
|
|
|
10618
11071
|
}
|
|
10619
11072
|
// Check tenant
|
|
10620
11073
|
const cookieTenant = CookieUtils.get(COOKIE_KEYS.TENANT);
|
|
11074
|
+
console.log("[syncCookiesToLocalStorage] cookieTenant", cookieTenant);
|
|
10621
11075
|
const localTenant = await storageService.getItem(LOCAL_STORAGE_KEYS.TENANTS);
|
|
10622
|
-
|
|
11076
|
+
console.log("[syncCookiesToLocalStorage] localTenant", localTenant);
|
|
11077
|
+
// Compare tenant arrays by checking length and tenant keys
|
|
11078
|
+
let tenantsAreDifferent = false;
|
|
11079
|
+
if (cookieTenant &&
|
|
11080
|
+
localTenant &&
|
|
11081
|
+
cookieTenant !== "[]" &&
|
|
11082
|
+
localTenant !== "[]") {
|
|
11083
|
+
try {
|
|
11084
|
+
const cookieTenantsArray = JSON.parse(cookieTenant);
|
|
11085
|
+
const localTenantsArray = JSON.parse(localTenant);
|
|
11086
|
+
// Check if arrays have same length
|
|
11087
|
+
if (cookieTenantsArray.length !== localTenantsArray.length) {
|
|
11088
|
+
tenantsAreDifferent = true;
|
|
11089
|
+
}
|
|
11090
|
+
else {
|
|
11091
|
+
// Check if all tenant keys match
|
|
11092
|
+
const cookieKeys = new Set(cookieTenantsArray.map((t) => t.key));
|
|
11093
|
+
const localKeys = new Set(localTenantsArray.map((t) => t.key));
|
|
11094
|
+
tenantsAreDifferent =
|
|
11095
|
+
cookieKeys.size !== localKeys.size ||
|
|
11096
|
+
[...cookieKeys].some((key) => !localKeys.has(key));
|
|
11097
|
+
}
|
|
11098
|
+
}
|
|
11099
|
+
catch (e) {
|
|
11100
|
+
// If parsing fails, fall back to string comparison
|
|
11101
|
+
tenantsAreDifferent = cookieTenant !== localTenant;
|
|
11102
|
+
}
|
|
11103
|
+
}
|
|
11104
|
+
if (tenantsAreDifferent) {
|
|
10623
11105
|
if (cookieTenant) {
|
|
10624
11106
|
await storageService.setItem(LOCAL_STORAGE_KEYS.TENANTS, cookieTenant);
|
|
10625
11107
|
}
|
|
@@ -10674,6 +11156,7 @@ async function isJwtTokenExpired(storageService) {
|
|
|
10674
11156
|
async function validateJwtToken(storageService) {
|
|
10675
11157
|
try {
|
|
10676
11158
|
const edxJwtToken = await storageService.getItem(LOCAL_STORAGE_KEYS.EDX_TOKEN_KEY);
|
|
11159
|
+
console.log("[AuthProvider] JWT token ", edxJwtToken);
|
|
10677
11160
|
const userData = await storageService.getItem(LOCAL_STORAGE_KEYS.USER_DATA);
|
|
10678
11161
|
if (!edxJwtToken || !userData) {
|
|
10679
11162
|
return false;
|
|
@@ -10696,7 +11179,9 @@ async function validateJwtToken(storageService) {
|
|
|
10696
11179
|
function useAuthProvider({ middleware = new Map(), onAuthSuccess, onAuthFailure, redirectToAuthSpa, hasNonExpiredAuthToken, username, pathname, storageService, skipAuthCheck, token, }) {
|
|
10697
11180
|
const [isAuthenticating, setIsAuthenticating] = useState(true);
|
|
10698
11181
|
const [userIsAccessingPublicRoute, setUserIsAccessingPublicRoute] = useState(false);
|
|
11182
|
+
const [initialSyncComplete, setInitialSyncComplete] = useState(false);
|
|
10699
11183
|
const cookieCheckIntervalRef = useRef(null);
|
|
11184
|
+
const lastLogoutTimestampRef = useRef(null);
|
|
10700
11185
|
// RTK Query hook for refreshing JWT token
|
|
10701
11186
|
const [refreshJwtToken] = useLazyRefreshJwtTokenQuery();
|
|
10702
11187
|
/**
|
|
@@ -10705,24 +11190,65 @@ function useAuthProvider({ middleware = new Map(), onAuthSuccess, onAuthFailure,
|
|
|
10705
11190
|
* On React Native, this is a no-op
|
|
10706
11191
|
*/
|
|
10707
11192
|
useEffect(() => {
|
|
10708
|
-
|
|
10709
|
-
|
|
11193
|
+
if (!storageService || !isWeb$1()) {
|
|
11194
|
+
// If no storage service or not web, mark sync as complete immediately
|
|
11195
|
+
setInitialSyncComplete(true);
|
|
10710
11196
|
return;
|
|
11197
|
+
}
|
|
10711
11198
|
// Initial sync on mount
|
|
10712
11199
|
async function initialSync() {
|
|
10713
|
-
|
|
10714
|
-
|
|
10715
|
-
|
|
10716
|
-
|
|
11200
|
+
try {
|
|
11201
|
+
// Initialize last known logout timestamp
|
|
11202
|
+
lastLogoutTimestampRef.current = CookieUtils.get(COOKIE_KEYS.LOGOUT_TIMESTAMP);
|
|
11203
|
+
const needsRefresh = await syncCookiesToLocalStorage(storageService);
|
|
11204
|
+
if (needsRefresh) {
|
|
11205
|
+
console.log("[auth-redirect] Cookie sync detected changes, refreshing page");
|
|
11206
|
+
redirectToAuthSpa(undefined, undefined, false, false);
|
|
11207
|
+
}
|
|
11208
|
+
else {
|
|
11209
|
+
await syncAuthToCookies(storageService);
|
|
11210
|
+
}
|
|
11211
|
+
}
|
|
11212
|
+
finally {
|
|
11213
|
+
// Mark initial sync as complete
|
|
11214
|
+
setInitialSyncComplete(true);
|
|
10717
11215
|
}
|
|
10718
11216
|
}
|
|
10719
11217
|
initialSync();
|
|
10720
11218
|
// Poll for cookie changes every 2 seconds to detect cross-SPA updates
|
|
10721
11219
|
cookieCheckIntervalRef.current = setInterval(async () => {
|
|
11220
|
+
// Skip cookie sync checks if user is completing SSO at /sso-login
|
|
11221
|
+
const completingSso = new RegExp("^\/sso-login").test(pathname);
|
|
11222
|
+
if (completingSso) {
|
|
11223
|
+
console.log("[AuthProvider] Skipping cookie sync check for public route");
|
|
11224
|
+
return;
|
|
11225
|
+
}
|
|
11226
|
+
// Check for logout timestamp changes (cross-SPA logout detection)
|
|
11227
|
+
const currentLogoutTimestamp = CookieUtils.get(COOKIE_KEYS.LOGOUT_TIMESTAMP);
|
|
11228
|
+
if (currentLogoutTimestamp &&
|
|
11229
|
+
lastLogoutTimestampRef.current &&
|
|
11230
|
+
currentLogoutTimestamp !== lastLogoutTimestampRef.current) {
|
|
11231
|
+
console.log("[auth-redirect] Logout detected from another SPA", {
|
|
11232
|
+
previousTimestamp: lastLogoutTimestampRef.current,
|
|
11233
|
+
newTimestamp: currentLogoutTimestamp,
|
|
11234
|
+
});
|
|
11235
|
+
lastLogoutTimestampRef.current = currentLogoutTimestamp;
|
|
11236
|
+
redirectToAuthSpa(undefined, undefined, true);
|
|
11237
|
+
return;
|
|
11238
|
+
}
|
|
10722
11239
|
const needsRefresh = await syncCookiesToLocalStorage(storageService);
|
|
10723
11240
|
if (needsRefresh) {
|
|
10724
|
-
console.log("[
|
|
10725
|
-
|
|
11241
|
+
console.log("[auth-redirect] Cookie sync detected changes from another SPA, refreshing page");
|
|
11242
|
+
let cookieTenantKey;
|
|
11243
|
+
try {
|
|
11244
|
+
const cookieCurrentTenant = JSON.parse(CookieUtils.get(COOKIE_KEYS.CURRENT_TENANT));
|
|
11245
|
+
cookieTenantKey = cookieCurrentTenant.key;
|
|
11246
|
+
}
|
|
11247
|
+
catch (_a) { }
|
|
11248
|
+
redirectToAuthSpa(undefined, cookieTenantKey, false, false);
|
|
11249
|
+
}
|
|
11250
|
+
else {
|
|
11251
|
+
await syncAuthToCookies(storageService);
|
|
10726
11252
|
}
|
|
10727
11253
|
}, 2000);
|
|
10728
11254
|
// Cleanup interval on unmount
|
|
@@ -10731,15 +11257,14 @@ function useAuthProvider({ middleware = new Map(), onAuthSuccess, onAuthFailure,
|
|
|
10731
11257
|
clearInterval(cookieCheckIntervalRef.current);
|
|
10732
11258
|
}
|
|
10733
11259
|
};
|
|
10734
|
-
}, [storageService]);
|
|
11260
|
+
}, [storageService, middleware, pathname]);
|
|
10735
11261
|
/**
|
|
10736
11262
|
* Listen for storage events from other tabs/windows (web only)
|
|
10737
11263
|
* This catches same-SPA changes in different tabs
|
|
10738
11264
|
* On React Native, this is a no-op
|
|
10739
11265
|
*/
|
|
10740
11266
|
useEffect(() => {
|
|
10741
|
-
|
|
10742
|
-
if (!storageService || !isWeb())
|
|
11267
|
+
if (!storageService || !isWeb$1())
|
|
10743
11268
|
return;
|
|
10744
11269
|
const handleStorageChange = async (event) => {
|
|
10745
11270
|
// Only handle changes to our auth-related keys
|
|
@@ -10770,14 +11295,15 @@ function useAuthProvider({ middleware = new Map(), onAuthSuccess, onAuthFailure,
|
|
|
10770
11295
|
if (!hasNonExpiredAuthToken()) {
|
|
10771
11296
|
const reason = "Auth token expired";
|
|
10772
11297
|
onAuthFailure === null || onAuthFailure === void 0 ? void 0 : onAuthFailure(reason);
|
|
10773
|
-
|
|
11298
|
+
console.log("[auth-redirect] Auth token expired");
|
|
11299
|
+
redirectToAuthSpa(undefined, undefined, true);
|
|
10774
11300
|
return;
|
|
10775
11301
|
}
|
|
10776
11302
|
// Check JWT token expiry and force logout for protected routes
|
|
10777
11303
|
if (isProtectedRoute && storageService) {
|
|
10778
11304
|
const jwtExpired = await isJwtTokenExpired(storageService);
|
|
10779
11305
|
if (jwtExpired) {
|
|
10780
|
-
console.log("[
|
|
11306
|
+
console.log("[auth-redirect] JWT token has expired, forcing logout");
|
|
10781
11307
|
// Clear all auth-related storage keys
|
|
10782
11308
|
clearAuthCookies();
|
|
10783
11309
|
const reason = "JWT token expired";
|
|
@@ -10799,11 +11325,18 @@ function useAuthProvider({ middleware = new Map(), onAuthSuccess, onAuthFailure,
|
|
|
10799
11325
|
console.log("[AuthProvider] JWT token refreshed successfully");
|
|
10800
11326
|
}
|
|
10801
11327
|
else if (result.error) {
|
|
10802
|
-
console.
|
|
11328
|
+
console.log("[auth-redirect] Failed to refresh JWT token", {
|
|
11329
|
+
error: result.error,
|
|
11330
|
+
});
|
|
11331
|
+
redirectToAuthSpa(undefined, undefined, true);
|
|
11332
|
+
return;
|
|
10803
11333
|
}
|
|
10804
11334
|
}
|
|
10805
11335
|
catch (refreshError) {
|
|
10806
|
-
console.
|
|
11336
|
+
console.log("[auth-redirect] JWT token refresh error", {
|
|
11337
|
+
error: refreshError,
|
|
11338
|
+
});
|
|
11339
|
+
redirectToAuthSpa(undefined, undefined, true);
|
|
10807
11340
|
// Continue with auth check even if JWT refresh fails
|
|
10808
11341
|
}
|
|
10809
11342
|
}
|
|
@@ -10815,12 +11348,18 @@ function useAuthProvider({ middleware = new Map(), onAuthSuccess, onAuthFailure,
|
|
|
10815
11348
|
setIsAuthenticating(false);
|
|
10816
11349
|
}
|
|
10817
11350
|
catch (error) {
|
|
11351
|
+
console.error("[AuthProvider] Error performing auth check:", error);
|
|
10818
11352
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
10819
11353
|
onAuthFailure === null || onAuthFailure === void 0 ? void 0 : onAuthFailure(`Unexpected error: ${errorMessage}`);
|
|
10820
11354
|
redirectToAuthSpa();
|
|
10821
11355
|
}
|
|
10822
11356
|
}
|
|
10823
11357
|
useEffect(() => {
|
|
11358
|
+
// Wait for initial sync to complete before performing auth check
|
|
11359
|
+
if (!initialSyncComplete) {
|
|
11360
|
+
console.log("[useAuthProvider] Waiting for initial sync to complete...");
|
|
11361
|
+
return;
|
|
11362
|
+
}
|
|
10824
11363
|
async function checkAuth() {
|
|
10825
11364
|
setIsAuthenticating(true);
|
|
10826
11365
|
const authRequired = (await determineAuthRequired(middleware, pathname)) && !token;
|
|
@@ -10841,7 +11380,7 @@ function useAuthProvider({ middleware = new Map(), onAuthSuccess, onAuthFailure,
|
|
|
10841
11380
|
}
|
|
10842
11381
|
console.log("[useAuthProvider] performing auth check for ", username);
|
|
10843
11382
|
checkAuth();
|
|
10844
|
-
}, [username]);
|
|
11383
|
+
}, [username, initialSyncComplete]);
|
|
10845
11384
|
return {
|
|
10846
11385
|
isAuthenticating,
|
|
10847
11386
|
userIsAccessingPublicRoute,
|
|
@@ -10909,7 +11448,7 @@ function AuthProvider({ children, fallback, middleware = new Map(), onAuthSucces
|
|
|
10909
11448
|
if (isAuthenticating) {
|
|
10910
11449
|
return fallback;
|
|
10911
11450
|
}
|
|
10912
|
-
return (jsx(AuthContextProvider, { value: { userIsAccessingPublicRoute, setUserIsAccessingPublicRoute }, children:
|
|
11451
|
+
return (jsx(AuthContextProvider, { value: { userIsAccessingPublicRoute, setUserIsAccessingPublicRoute }, children: children }));
|
|
10913
11452
|
}
|
|
10914
11453
|
|
|
10915
11454
|
/**
|
|
@@ -10922,6 +11461,12 @@ function AuthProvider({ children, fallback, middleware = new Map(), onAuthSucces
|
|
|
10922
11461
|
* - Tenant metadata retrieval
|
|
10923
11462
|
* - User-tenant relationship verification
|
|
10924
11463
|
*/
|
|
11464
|
+
/**
|
|
11465
|
+
* Check if we're running in a web browser environment
|
|
11466
|
+
*/
|
|
11467
|
+
const isWeb = () => {
|
|
11468
|
+
return typeof window !== "undefined" && typeof document !== "undefined";
|
|
11469
|
+
};
|
|
10925
11470
|
const TenantContext = createContext(undefined);
|
|
10926
11471
|
/**
|
|
10927
11472
|
* Context Provider component that wraps children with tenant context
|
|
@@ -10942,7 +11487,7 @@ const useTenantContext = () => useContext(TenantContext);
|
|
|
10942
11487
|
* 4. Handles tenant-specific domain redirects
|
|
10943
11488
|
* 5. Maintains tenant access state
|
|
10944
11489
|
*/
|
|
10945
|
-
function TenantProvider({ children, fallback, onAuthSuccess, onAuthFailure, currentTenant, requestedTenant, saveCurrentTenant, saveUserTenants, handleTenantSwitch, saveVisitingTenant, removeVisitingTenant, saveUserTokens, saveTenant, onAutoJoinUserToTenant, }) {
|
|
11490
|
+
function TenantProvider({ children, fallback, onAuthSuccess, onAuthFailure, currentTenant, requestedTenant, saveCurrentTenant, saveUserTenants, handleTenantSwitch, saveVisitingTenant, removeVisitingTenant, saveUserTokens, saveTenant, onAutoJoinUserToTenant, redirectToAuthSpa, }) {
|
|
10946
11491
|
const { userIsAccessingPublicRoute, setUserIsAccessingPublicRoute } = useAuthContext();
|
|
10947
11492
|
const [determineUserPath, setDetermineUserPath] = useState(false);
|
|
10948
11493
|
const [isLoading, setIsLoading] = React__default.useState(true);
|
|
@@ -10953,6 +11498,26 @@ function TenantProvider({ children, fallback, onAuthSuccess, onAuthFailure, curr
|
|
|
10953
11498
|
const [getAppToken] = useGetAppTokensMutation();
|
|
10954
11499
|
const [tenantKey, setTenantKey] = useState(currentTenant);
|
|
10955
11500
|
const [metadata, setMetadata] = useState({});
|
|
11501
|
+
// Fetch custom domain to get platform_key
|
|
11502
|
+
const currentDomain = typeof window !== "undefined" ? window.location.hostname : "";
|
|
11503
|
+
const { data: customDomainData, isLoading: isLoadingCustomDomain, isError: isCustomDomainError, } = useGetCustomDomainsQuery({ params: { domain: currentDomain } }, { skip: !isWeb() || !currentDomain });
|
|
11504
|
+
// Extract platform_key from custom domain response to use as requestedTenant
|
|
11505
|
+
const customDomainPlatformKey = React__default.useMemo(() => {
|
|
11506
|
+
if (customDomainData &&
|
|
11507
|
+
typeof customDomainData === "object" &&
|
|
11508
|
+
"custom_domains" in customDomainData) {
|
|
11509
|
+
const domains = customDomainData.custom_domains;
|
|
11510
|
+
if (Array.isArray(domains) &&
|
|
11511
|
+
domains.length > 0 &&
|
|
11512
|
+
domains[0].platform_key) {
|
|
11513
|
+
console.log("[TenantProvider] Using platform_key from custom domain:", domains[0].platform_key);
|
|
11514
|
+
return domains[0].platform_key;
|
|
11515
|
+
}
|
|
11516
|
+
}
|
|
11517
|
+
return undefined;
|
|
11518
|
+
}, [customDomainData]);
|
|
11519
|
+
// Use custom domain platform_key as requested tenant if available, otherwise use provided requestedTenant
|
|
11520
|
+
const effectiveRequestedTenant = customDomainPlatformKey || requestedTenant;
|
|
10956
11521
|
/**
|
|
10957
11522
|
* Helper function to enhance tenants with platform metadata from user apps
|
|
10958
11523
|
*/
|
|
@@ -11012,24 +11577,32 @@ function TenantProvider({ children, fallback, onAuthSuccess, onAuthFailure, curr
|
|
|
11012
11577
|
const otherTenant = tenants.find((t) => t.key !== MAIN_TENANT_KEY);
|
|
11013
11578
|
if (mainTenant &&
|
|
11014
11579
|
otherTenant &&
|
|
11015
|
-
|
|
11016
|
-
|
|
11580
|
+
effectiveRequestedTenant &&
|
|
11581
|
+
effectiveRequestedTenant !== otherTenant.key) {
|
|
11017
11582
|
handleTenantSwitch(otherTenant.key, true);
|
|
11018
11583
|
console.log("TenantProvider: Triggering tenant switch to newly joined tenant:", otherTenant.key);
|
|
11019
11584
|
}
|
|
11020
11585
|
}
|
|
11021
|
-
// Enhance tenants with platform
|
|
11586
|
+
// Enhance tenants with platform metadatax
|
|
11022
11587
|
let enhancedTenants = await enhanceTenants(tenants);
|
|
11588
|
+
// if there is customDomainPlatformKey and user doesn't belong to that tenant, trigger logout
|
|
11589
|
+
if (customDomainPlatformKey) {
|
|
11590
|
+
if (!(enhancedTenants === null || enhancedTenants === void 0 ? void 0 : enhancedTenants.find((tenant) => tenant.key === customDomainPlatformKey))) {
|
|
11591
|
+
redirectToAuthSpa === null || redirectToAuthSpa === void 0 ? void 0 : redirectToAuthSpa(undefined, undefined, true);
|
|
11592
|
+
return;
|
|
11593
|
+
}
|
|
11594
|
+
}
|
|
11023
11595
|
saveUserTenants(enhancedTenants);
|
|
11024
11596
|
// Find requested tenant or fallback to current tenant
|
|
11025
|
-
console.log("checking requested tenant", {
|
|
11026
|
-
|
|
11027
|
-
|
|
11597
|
+
console.log("checking requested tenant", JSON.stringify({
|
|
11598
|
+
requestedTenant: effectiveRequestedTenant,
|
|
11599
|
+
customDomainPlatformKey,
|
|
11600
|
+
}));
|
|
11601
|
+
let tenant = enhancedTenants === null || enhancedTenants === void 0 ? void 0 : enhancedTenants.find((tenant) => tenant.key === effectiveRequestedTenant);
|
|
11602
|
+
console.log("requested tenant", tenant);
|
|
11028
11603
|
if (!tenant && !userIsAccessingPublicRoute) {
|
|
11029
11604
|
setDetermineUserPath(true);
|
|
11030
|
-
console.log("no requested tenant, checking current tenant",
|
|
11031
|
-
currentTenant,
|
|
11032
|
-
});
|
|
11605
|
+
console.log("no requested tenant, checking current tenant", currentTenant);
|
|
11033
11606
|
tenant = enhancedTenants === null || enhancedTenants === void 0 ? void 0 : enhancedTenants.find((tenant) => tenant.key === currentTenant);
|
|
11034
11607
|
console.log("current tenant", { tenant });
|
|
11035
11608
|
}
|
|
@@ -11041,7 +11614,7 @@ function TenantProvider({ children, fallback, onAuthSuccess, onAuthFailure, curr
|
|
|
11041
11614
|
tenant = enhancedTenants[0];
|
|
11042
11615
|
}
|
|
11043
11616
|
// Fetch and validate tenant metadata
|
|
11044
|
-
console.log("fetching tenant metadata",
|
|
11617
|
+
console.log("fetching tenant metadata", JSON.stringify(tenant));
|
|
11045
11618
|
const { data: tenantMetadata } = await fetchTenantMetadata([
|
|
11046
11619
|
{
|
|
11047
11620
|
org: (tenant === null || tenant === void 0 ? void 0 : tenant.key) || currentTenant,
|
|
@@ -11183,28 +11756,44 @@ function TenantProvider({ children, fallback, onAuthSuccess, onAuthFailure, curr
|
|
|
11183
11756
|
}
|
|
11184
11757
|
// Effect to handle tenant determination when auth state changes
|
|
11185
11758
|
React__default.useEffect(() => {
|
|
11759
|
+
// Wait for custom domain query to complete (unless skipped or error)
|
|
11760
|
+
const customDomainQuerySkipped = !isWeb() || !currentDomain;
|
|
11761
|
+
if (!customDomainQuerySkipped && isLoadingCustomDomain) {
|
|
11762
|
+
console.log("[TenantProvider] Waiting for custom domain query to complete...");
|
|
11763
|
+
return;
|
|
11764
|
+
}
|
|
11765
|
+
if (isCustomDomainError) {
|
|
11766
|
+
console.warn("[TenantProvider] Custom domain query failed, proceeding without custom domain platform_key");
|
|
11767
|
+
}
|
|
11186
11768
|
// NOTE: currentTenant comes from local storage.
|
|
11187
11769
|
// it's the tenant the user is currently signed into.
|
|
11188
11770
|
console.log("determineWhichTenantToUse", {
|
|
11189
11771
|
userIsAccessingPublicRoute,
|
|
11190
11772
|
currentTenant,
|
|
11191
11773
|
requestedTenant,
|
|
11774
|
+
effectiveRequestedTenant,
|
|
11775
|
+
customDomainPlatformKey,
|
|
11192
11776
|
});
|
|
11193
|
-
if (userIsAccessingPublicRoute &&
|
|
11777
|
+
if (userIsAccessingPublicRoute && effectiveRequestedTenant.length === 0) {
|
|
11194
11778
|
setIsLoading(false);
|
|
11195
11779
|
return;
|
|
11196
11780
|
}
|
|
11197
|
-
if (userIsAccessingPublicRoute &&
|
|
11781
|
+
if (userIsAccessingPublicRoute && effectiveRequestedTenant.length > 0) {
|
|
11198
11782
|
setIsLoading(true);
|
|
11199
|
-
setTenantKey(
|
|
11200
|
-
handleLoadTenantMetadata(
|
|
11783
|
+
setTenantKey(effectiveRequestedTenant);
|
|
11784
|
+
handleLoadTenantMetadata(effectiveRequestedTenant);
|
|
11201
11785
|
}
|
|
11202
11786
|
else {
|
|
11203
11787
|
setIsLoading(true);
|
|
11204
11788
|
removeVisitingTenant === null || removeVisitingTenant === void 0 ? void 0 : removeVisitingTenant();
|
|
11205
11789
|
determineWhichTenantToUse();
|
|
11206
11790
|
}
|
|
11207
|
-
}, [
|
|
11791
|
+
}, [
|
|
11792
|
+
userIsAccessingPublicRoute,
|
|
11793
|
+
effectiveRequestedTenant,
|
|
11794
|
+
isLoadingCustomDomain,
|
|
11795
|
+
isCustomDomainError,
|
|
11796
|
+
]);
|
|
11208
11797
|
// Show fallback component during tenant determination
|
|
11209
11798
|
if (isLoading) {
|
|
11210
11799
|
return fallback;
|
|
@@ -11240,21 +11829,14 @@ function TenantProvider({ children, fallback, onAuthSuccess, onAuthFailure, curr
|
|
|
11240
11829
|
function MentorProvider({ children, fallback, onAuthSuccess, onAuthFailure, redirectToAuthSpa, redirectToMentor, onLoadMentorsPermissions, redirectToNoMentorsPage, redirectToCreateMentor, username, isAdmin, mainTenantKey, requestedMentorId, handleMentorNotFound, forceDetermineMentor = false, }) {
|
|
11241
11830
|
const [isLoading, setIsLoading] = useState(true);
|
|
11242
11831
|
const { userIsAccessingPublicRoute } = useAuthContext();
|
|
11243
|
-
const { determineUserPath, tenantKey } = useTenantContext();
|
|
11832
|
+
const { determineUserPath, tenantKey, metadata } = useTenantContext();
|
|
11244
11833
|
const isMainTenant = tenantKey === mainTenantKey;
|
|
11245
|
-
console.log("MentorProvider initialized",
|
|
11246
|
-
username,
|
|
11247
|
-
isAdmin,
|
|
11248
|
-
tenantKey,
|
|
11249
|
-
mainTenantKey,
|
|
11250
|
-
isMainTenant,
|
|
11251
|
-
requestedMentorId,
|
|
11252
|
-
determineUserPath,
|
|
11253
|
-
});
|
|
11834
|
+
console.log("MentorProvider initialized", username, isAdmin, tenantKey, mainTenantKey, isMainTenant, requestedMentorId, determineUserPath);
|
|
11254
11835
|
const [fetchMentors] = useLazyGetMentorsQuery();
|
|
11255
11836
|
const [fetchSeedMentors] = useLazySeedMentorsQuery();
|
|
11256
11837
|
const [getMentorPublicSettings] = useLazyGetMentorPublicSettingsQuery();
|
|
11257
11838
|
const [getRbacPermissions] = useGetRbacPermissionsMutation();
|
|
11839
|
+
const [getRecentlyAccessedMentors] = useLazyGetRecentlyAccessedMentorsQuery();
|
|
11258
11840
|
const QUERY_LIMIT = 10;
|
|
11259
11841
|
const loadMentorsPermissions = async (mentorDbId) => {
|
|
11260
11842
|
try {
|
|
@@ -11281,21 +11863,69 @@ function MentorProvider({ children, fallback, onAuthSuccess, onAuthFailure, redi
|
|
|
11281
11863
|
*/
|
|
11282
11864
|
async function determineMentorToRedirectTo() {
|
|
11283
11865
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s;
|
|
11284
|
-
console.log("starting mentor determination process", {
|
|
11866
|
+
console.log("starting mentor determination process", JSON.stringify({
|
|
11285
11867
|
tenantKey,
|
|
11286
11868
|
username,
|
|
11287
11869
|
isAdmin,
|
|
11288
11870
|
isMainTenant,
|
|
11289
|
-
});
|
|
11871
|
+
}));
|
|
11872
|
+
console.log("###################### metadata is ", metadata, typeof metadata);
|
|
11290
11873
|
setIsLoading(true);
|
|
11291
11874
|
try {
|
|
11292
11875
|
// Get the user's recent mentors
|
|
11293
|
-
console.log("fetching recent mentors", {
|
|
11876
|
+
console.log("fetching recent mentors", JSON.stringify({
|
|
11294
11877
|
tenantKey,
|
|
11295
11878
|
username,
|
|
11296
11879
|
limit: QUERY_LIMIT,
|
|
11297
11880
|
orderBy: "recently_accessed_at",
|
|
11881
|
+
}));
|
|
11882
|
+
if (typeof metadata === "object" &&
|
|
11883
|
+
metadata.skills_embedded_mentor_name) {
|
|
11884
|
+
const defaultMentor = JSON.parse(metadata.skills_embedded_mentor_name);
|
|
11885
|
+
if (defaultMentor === null || defaultMentor === void 0 ? void 0 : defaultMentor.unique_id) {
|
|
11886
|
+
if (defaultMentor === null || defaultMentor === void 0 ? void 0 : defaultMentor.id) {
|
|
11887
|
+
const rbacPermissions = await loadMentorsPermissions(defaultMentor === null || defaultMentor === void 0 ? void 0 : defaultMentor.id);
|
|
11888
|
+
onLoadMentorsPermissions === null || onLoadMentorsPermissions === void 0 ? void 0 : onLoadMentorsPermissions(rbacPermissions);
|
|
11889
|
+
}
|
|
11890
|
+
redirectToMentor(tenantKey, defaultMentor.unique_id);
|
|
11891
|
+
onAuthSuccess === null || onAuthSuccess === void 0 ? void 0 : onAuthSuccess();
|
|
11892
|
+
return;
|
|
11893
|
+
}
|
|
11894
|
+
}
|
|
11895
|
+
// Check for recently accessed mentors (second choice)
|
|
11896
|
+
console.log("checking recently accessed mentors", JSON.stringify({
|
|
11897
|
+
tenantKey,
|
|
11898
|
+
username,
|
|
11899
|
+
}));
|
|
11900
|
+
const recentlyAccessedResult = await getRecentlyAccessedMentors({
|
|
11901
|
+
org: tenantKey,
|
|
11902
|
+
// @ts-ignore
|
|
11903
|
+
userId: username,
|
|
11298
11904
|
});
|
|
11905
|
+
// @ts-ignore
|
|
11906
|
+
const recentlyAccessedMentors = recentlyAccessedResult.data;
|
|
11907
|
+
const starredMentors = recentlyAccessedMentors === null || recentlyAccessedMentors === void 0 ? void 0 : recentlyAccessedMentors.starred_mentors;
|
|
11908
|
+
let recentMentors = recentlyAccessedMentors === null || recentlyAccessedMentors === void 0 ? void 0 : recentlyAccessedMentors.recent_mentors;
|
|
11909
|
+
if (starredMentors.length > 0) {
|
|
11910
|
+
const starredMentor = starredMentors[0];
|
|
11911
|
+
if (starredMentor === null || starredMentor === void 0 ? void 0 : starredMentor.unique_id) {
|
|
11912
|
+
const rbacPermissions = await loadMentorsPermissions(starredMentor === null || starredMentor === void 0 ? void 0 : starredMentor.id);
|
|
11913
|
+
onLoadMentorsPermissions === null || onLoadMentorsPermissions === void 0 ? void 0 : onLoadMentorsPermissions(rbacPermissions);
|
|
11914
|
+
redirectToMentor(tenantKey, starredMentor.unique_id);
|
|
11915
|
+
onAuthSuccess === null || onAuthSuccess === void 0 ? void 0 : onAuthSuccess();
|
|
11916
|
+
return;
|
|
11917
|
+
}
|
|
11918
|
+
}
|
|
11919
|
+
if (recentMentors.length > 0) {
|
|
11920
|
+
const recentMentor = recentMentors[0];
|
|
11921
|
+
if (recentMentor === null || recentMentor === void 0 ? void 0 : recentMentor.unique_id) {
|
|
11922
|
+
const rbacPermissions = await loadMentorsPermissions(recentMentor === null || recentMentor === void 0 ? void 0 : recentMentor.id);
|
|
11923
|
+
onLoadMentorsPermissions === null || onLoadMentorsPermissions === void 0 ? void 0 : onLoadMentorsPermissions(rbacPermissions);
|
|
11924
|
+
redirectToMentor(tenantKey, recentMentor.unique_id);
|
|
11925
|
+
onAuthSuccess === null || onAuthSuccess === void 0 ? void 0 : onAuthSuccess();
|
|
11926
|
+
return;
|
|
11927
|
+
}
|
|
11928
|
+
}
|
|
11299
11929
|
const recentMentorsResult = await fetchMentors({
|
|
11300
11930
|
// @ts-ignore
|
|
11301
11931
|
org: tenantKey,
|
|
@@ -11303,20 +11933,20 @@ function MentorProvider({ children, fallback, onAuthSuccess, onAuthFailure, redi
|
|
|
11303
11933
|
limit: QUERY_LIMIT,
|
|
11304
11934
|
orderBy: "recently_accessed_at",
|
|
11305
11935
|
});
|
|
11306
|
-
|
|
11307
|
-
console.log("recent mentors fetched", {
|
|
11936
|
+
recentMentors = ((_a = recentMentorsResult.data) === null || _a === void 0 ? void 0 : _a.results) || [];
|
|
11937
|
+
console.log("recent mentors fetched", JSON.stringify({
|
|
11308
11938
|
count: recentMentors.length,
|
|
11309
11939
|
mentorIds: recentMentors.map((m) => m === null || m === void 0 ? void 0 : m.unique_id),
|
|
11310
|
-
});
|
|
11940
|
+
}));
|
|
11311
11941
|
// Check if we found recent mentors for the tenant
|
|
11312
11942
|
if (recentMentors.length > 0) {
|
|
11313
11943
|
const selectedMentorId = ((_b = recentMentors[0]) === null || _b === void 0 ? void 0 : _b.unique_id) || "";
|
|
11314
11944
|
const selectedMentorDbId = ((_c = recentMentors[0]) === null || _c === void 0 ? void 0 : _c.id) || "";
|
|
11315
|
-
console.log("redirecting to recent mentor", {
|
|
11945
|
+
console.log("redirecting to recent mentor", JSON.stringify({
|
|
11316
11946
|
selectedMentorId,
|
|
11317
11947
|
mentorName: (_d = recentMentors[0]) === null || _d === void 0 ? void 0 : _d.name,
|
|
11318
11948
|
tenantKey,
|
|
11319
|
-
});
|
|
11949
|
+
}));
|
|
11320
11950
|
const rbacPermissions = await loadMentorsPermissions(selectedMentorDbId);
|
|
11321
11951
|
// Select the most recent mentor as current mentor
|
|
11322
11952
|
onLoadMentorsPermissions === null || onLoadMentorsPermissions === void 0 ? void 0 : onLoadMentorsPermissions(rbacPermissions);
|
|
@@ -11518,6 +12148,9 @@ function MentorProvider({ children, fallback, onAuthSuccess, onAuthFailure, redi
|
|
|
11518
12148
|
isAdmin,
|
|
11519
12149
|
});
|
|
11520
12150
|
onAuthFailure === null || onAuthFailure === void 0 ? void 0 : onAuthFailure(`Unexpected error: ${errorMessage}`);
|
|
12151
|
+
console.log("[auth-redirect] Unexpected error in mentor provider", {
|
|
12152
|
+
error: errorMessage,
|
|
12153
|
+
});
|
|
11521
12154
|
redirectToAuthSpa();
|
|
11522
12155
|
}
|
|
11523
12156
|
finally {
|
|
@@ -11525,11 +12158,7 @@ function MentorProvider({ children, fallback, onAuthSuccess, onAuthFailure, redi
|
|
|
11525
12158
|
}
|
|
11526
12159
|
}
|
|
11527
12160
|
async function mentorExistsInTenant(tenantKey, requestedMentorId) {
|
|
11528
|
-
console.log("checking if mentor exists in tenant",
|
|
11529
|
-
tenantKey,
|
|
11530
|
-
requestedMentorId,
|
|
11531
|
-
username,
|
|
11532
|
-
});
|
|
12161
|
+
console.log("checking if mentor exists in tenant", tenantKey, requestedMentorId, username);
|
|
11533
12162
|
try {
|
|
11534
12163
|
const response = await getMentorPublicSettings({
|
|
11535
12164
|
// @ts-ignore
|
|
@@ -11558,11 +12187,7 @@ function MentorProvider({ children, fallback, onAuthSuccess, onAuthFailure, redi
|
|
|
11558
12187
|
// Effect to handle mentor determination when tenant path is determined
|
|
11559
12188
|
React__default.useEffect(() => {
|
|
11560
12189
|
async function checkMentor() {
|
|
11561
|
-
console.log("starting mentor check process",
|
|
11562
|
-
determineUserPath,
|
|
11563
|
-
requestedMentorId,
|
|
11564
|
-
tenantKey,
|
|
11565
|
-
});
|
|
12190
|
+
console.log("starting mentor check process", determineUserPath, requestedMentorId, tenantKey);
|
|
11566
12191
|
if (userIsAccessingPublicRoute && !requestedMentorId) {
|
|
11567
12192
|
setIsLoading(false);
|
|
11568
12193
|
return;
|
|
@@ -11582,11 +12207,11 @@ function MentorProvider({ children, fallback, onAuthSuccess, onAuthFailure, redi
|
|
|
11582
12207
|
const [mentorExists, requestedMentorDbId] = await mentorExistsInTenant(tenantKey, requestedMentorId);
|
|
11583
12208
|
// If the mentor does not exist in the tenant, redirect the user to the correct mentor
|
|
11584
12209
|
if (!mentorExists) {
|
|
11585
|
-
console.log("requested mentor not found in tenant", {
|
|
12210
|
+
console.log("requested mentor not found in tenant", JSON.stringify({
|
|
11586
12211
|
requestedMentorId,
|
|
11587
12212
|
tenantKey,
|
|
11588
12213
|
hasCustomHandler: !!handleMentorNotFound,
|
|
11589
|
-
});
|
|
12214
|
+
}));
|
|
11590
12215
|
if (handleMentorNotFound) {
|
|
11591
12216
|
console.log("calling custom mentor not found handler");
|
|
11592
12217
|
await handleMentorNotFound();
|
|
@@ -11598,9 +12223,7 @@ function MentorProvider({ children, fallback, onAuthSuccess, onAuthFailure, redi
|
|
|
11598
12223
|
return;
|
|
11599
12224
|
}
|
|
11600
12225
|
else {
|
|
11601
|
-
console.log("requested mentor exists in tenant, proceeding",
|
|
11602
|
-
requestedMentorId,
|
|
11603
|
-
});
|
|
12226
|
+
console.log("requested mentor exists in tenant, proceeding", requestedMentorId);
|
|
11604
12227
|
if (requestedMentorDbId) {
|
|
11605
12228
|
const rbacPermissions = await loadMentorsPermissions(requestedMentorDbId);
|
|
11606
12229
|
onLoadMentorsPermissions === null || onLoadMentorsPermissions === void 0 ? void 0 : onLoadMentorsPermissions(rbacPermissions);
|
|
@@ -12072,7 +12695,7 @@ function formatProdErrorMessage(code) {
|
|
|
12072
12695
|
|
|
12073
12696
|
const defaultSessionIds = Object.fromEntries(Object.keys(advancedTabsProperties).map((key) => [key, ""]));
|
|
12074
12697
|
const defaultChatState = Object.fromEntries(Object.keys(advancedTabsProperties).map((key) => [key, []]));
|
|
12075
|
-
const initialState = {
|
|
12698
|
+
const initialState$1 = {
|
|
12076
12699
|
chats: defaultChatState,
|
|
12077
12700
|
isTyping: false,
|
|
12078
12701
|
streaming: false,
|
|
@@ -12100,7 +12723,7 @@ const initialState = {
|
|
|
12100
12723
|
};
|
|
12101
12724
|
const chatSlice = createSlice({
|
|
12102
12725
|
name: "chatSliceShared",
|
|
12103
|
-
initialState,
|
|
12726
|
+
initialState: initialState$1,
|
|
12104
12727
|
reducers: {
|
|
12105
12728
|
setChats: (state, action) => {
|
|
12106
12729
|
state.chats = action.payload;
|
|
@@ -12204,6 +12827,25 @@ const chatSlice = createSlice({
|
|
|
12204
12827
|
setShowingSharedChat: (state, action) => {
|
|
12205
12828
|
state.showingSharedChat = action.payload;
|
|
12206
12829
|
},
|
|
12830
|
+
updateFileUrlInMessage: (state, action) => {
|
|
12831
|
+
const { fileId, fileUrl } = action.payload;
|
|
12832
|
+
// Update all tabs' messages that have this file
|
|
12833
|
+
Object.keys(state.chats).forEach((tab) => {
|
|
12834
|
+
state.chats[tab] = state.chats[tab].map((message) => {
|
|
12835
|
+
if (message.fileAttachments && message.fileAttachments.length > 0) {
|
|
12836
|
+
const updatedAttachments = message.fileAttachments.map((attachment) => {
|
|
12837
|
+
// Match by fileId
|
|
12838
|
+
if (attachment.fileId === fileId) {
|
|
12839
|
+
return { ...attachment, uploadUrl: fileUrl };
|
|
12840
|
+
}
|
|
12841
|
+
return attachment;
|
|
12842
|
+
});
|
|
12843
|
+
return { ...message, fileAttachments: updatedAttachments };
|
|
12844
|
+
}
|
|
12845
|
+
return message;
|
|
12846
|
+
});
|
|
12847
|
+
});
|
|
12848
|
+
},
|
|
12207
12849
|
},
|
|
12208
12850
|
});
|
|
12209
12851
|
const chatActions = chatSlice.actions;
|
|
@@ -12487,6 +13129,61 @@ function useTimeTrackerNative(config) {
|
|
|
12487
13129
|
};
|
|
12488
13130
|
}
|
|
12489
13131
|
|
|
13132
|
+
const initialState = {
|
|
13133
|
+
attachedFiles: [],
|
|
13134
|
+
};
|
|
13135
|
+
const filesSlice = createSlice({
|
|
13136
|
+
name: "files",
|
|
13137
|
+
initialState,
|
|
13138
|
+
reducers: {
|
|
13139
|
+
addFiles: (state, action) => {
|
|
13140
|
+
state.attachedFiles = [...state.attachedFiles, ...action.payload];
|
|
13141
|
+
},
|
|
13142
|
+
updateFileProgress: (state, action) => {
|
|
13143
|
+
state.attachedFiles = state.attachedFiles.map((file) => file.id === action.payload.id
|
|
13144
|
+
? { ...file, uploadProgress: action.payload.progress }
|
|
13145
|
+
: file);
|
|
13146
|
+
},
|
|
13147
|
+
updateFileStatus: (state, action) => {
|
|
13148
|
+
state.attachedFiles = state.attachedFiles.map((file) => file.id === action.payload.id
|
|
13149
|
+
? { ...file, uploadStatus: action.payload.status }
|
|
13150
|
+
: file);
|
|
13151
|
+
},
|
|
13152
|
+
updateFileUrl: (state, action) => {
|
|
13153
|
+
state.attachedFiles = state.attachedFiles.map((file) => file.id === action.payload.id
|
|
13154
|
+
? { ...file, uploadUrl: action.payload.uploadUrl }
|
|
13155
|
+
: file);
|
|
13156
|
+
},
|
|
13157
|
+
updateFileMetadata: (state, action) => {
|
|
13158
|
+
state.attachedFiles = state.attachedFiles.map((file) => file.id === action.payload.id
|
|
13159
|
+
? {
|
|
13160
|
+
...file,
|
|
13161
|
+
fileKey: action.payload.fileKey,
|
|
13162
|
+
fileId: action.payload.fileId,
|
|
13163
|
+
}
|
|
13164
|
+
: file);
|
|
13165
|
+
},
|
|
13166
|
+
updateFileRetryCount: (state, action) => {
|
|
13167
|
+
state.attachedFiles = state.attachedFiles.map((file) => file.id === action.payload.id
|
|
13168
|
+
? { ...file, retryCount: action.payload.retryCount }
|
|
13169
|
+
: file);
|
|
13170
|
+
},
|
|
13171
|
+
updateFileUrlFromWebSocket: (state, action) => {
|
|
13172
|
+
state.attachedFiles = state.attachedFiles.map((file) => file.fileId === action.payload.fileId
|
|
13173
|
+
? { ...file, fileUrl: action.payload.fileUrl }
|
|
13174
|
+
: file);
|
|
13175
|
+
},
|
|
13176
|
+
removeFile: (state, action) => {
|
|
13177
|
+
state.attachedFiles = state.attachedFiles.filter((file) => file.id !== action.payload);
|
|
13178
|
+
},
|
|
13179
|
+
clearFiles: (state) => {
|
|
13180
|
+
state.attachedFiles = [];
|
|
13181
|
+
},
|
|
13182
|
+
},
|
|
13183
|
+
});
|
|
13184
|
+
const { addFiles, removeFile, clearFiles, updateFileProgress, updateFileStatus, updateFileUrl, updateFileMetadata, updateFileRetryCount, updateFileUrlFromWebSocket, } = filesSlice.actions;
|
|
13185
|
+
const filesReducer = filesSlice.reducer;
|
|
13186
|
+
|
|
12490
13187
|
const useChat = ({ wsUrl, wsToken, flowConfig, sessionId, stopGenerationWsUrl, enableHaptics = false, hapticFeedback, store, errorHandler, onStatusChange, onStreamingChange, onStreamingMessageUpdate, WebSocketImpl = WebSocket, redirectToAuthSpa, sendMessageToParentWebsite, on402Error, }) => {
|
|
12491
13188
|
const dispatch = useDispatch();
|
|
12492
13189
|
const isWebSocketPaused = useRef(false);
|
|
@@ -12522,8 +13219,8 @@ const useChat = ({ wsUrl, wsToken, flowConfig, sessionId, stopGenerationWsUrl, e
|
|
|
12522
13219
|
}
|
|
12523
13220
|
if (!wsToken) {
|
|
12524
13221
|
if (!userIsAccessingPublicRoute) {
|
|
12525
|
-
console.
|
|
12526
|
-
redirectToAuthSpa();
|
|
13222
|
+
console.log("[auth-redirect] WebSocket token is missing for non-anonymous user");
|
|
13223
|
+
redirectToAuthSpa(undefined, undefined, true);
|
|
12527
13224
|
return;
|
|
12528
13225
|
}
|
|
12529
13226
|
}
|
|
@@ -12578,6 +13275,16 @@ const useChat = ({ wsUrl, wsToken, flowConfig, sessionId, stopGenerationWsUrl, e
|
|
|
12578
13275
|
onStreamingChange(!!messageData.isTyping);
|
|
12579
13276
|
return;
|
|
12580
13277
|
}
|
|
13278
|
+
// Handle file processing success
|
|
13279
|
+
if (messageData.type === "file_processing_success") {
|
|
13280
|
+
console.log("🟣 File processing success:", messageData);
|
|
13281
|
+
dispatch(chatActions.updateFileUrlInMessage({
|
|
13282
|
+
fileId: messageData.file_id,
|
|
13283
|
+
fileName: messageData.file_name,
|
|
13284
|
+
fileUrl: messageData.file_url,
|
|
13285
|
+
}));
|
|
13286
|
+
return;
|
|
13287
|
+
}
|
|
12581
13288
|
// Handle start of new streaming session
|
|
12582
13289
|
if (messageData.generation_id &&
|
|
12583
13290
|
!messageData.data &&
|
|
@@ -12673,19 +13380,31 @@ const useChat = ({ wsUrl, wsToken, flowConfig, sessionId, stopGenerationWsUrl, e
|
|
|
12673
13380
|
};
|
|
12674
13381
|
};
|
|
12675
13382
|
const sendMessage = useCallback(async (tab, text, options) => {
|
|
12676
|
-
var _a;
|
|
13383
|
+
var _a, _b;
|
|
12677
13384
|
dispatch(chatActions.setShowingSharedChat(false));
|
|
12678
|
-
if
|
|
13385
|
+
// Allow sending if there's text OR file references
|
|
13386
|
+
if (!text.trim() &&
|
|
13387
|
+
(!(options === null || options === void 0 ? void 0 : options.fileReferences) || options.fileReferences.length === 0)) {
|
|
12679
13388
|
return;
|
|
13389
|
+
}
|
|
12680
13390
|
onStatusChange("pending");
|
|
12681
13391
|
isWebSocketPaused.current = false;
|
|
12682
13392
|
await triggerHapticFeedback();
|
|
13393
|
+
// Create file attachments array from file references if present
|
|
13394
|
+
const fileAttachments = (_a = options === null || options === void 0 ? void 0 : options.fileReferences) === null || _a === void 0 ? void 0 : _a.map((ref) => ({
|
|
13395
|
+
fileName: ref.file_name,
|
|
13396
|
+
fileType: ref.content_type,
|
|
13397
|
+
fileSize: ref.file_size,
|
|
13398
|
+
uploadUrl: ref.upload_url, // Include URL for display
|
|
13399
|
+
fileId: ref.file_id, // Include fileId for matching WebSocket updates
|
|
13400
|
+
}));
|
|
12683
13401
|
const userMessage = {
|
|
12684
13402
|
id: `user-${Date.now()}`,
|
|
12685
13403
|
role: "user",
|
|
12686
13404
|
content: text,
|
|
12687
13405
|
timestamp: new Date().toISOString(),
|
|
12688
|
-
visible: (
|
|
13406
|
+
visible: (_b = options === null || options === void 0 ? void 0 : options.visible) !== null && _b !== void 0 ? _b : true,
|
|
13407
|
+
fileAttachments,
|
|
12689
13408
|
};
|
|
12690
13409
|
// Notify parent to add user message
|
|
12691
13410
|
// onAddUserMessage?.(tab, userMessage);
|
|
@@ -12697,8 +13416,14 @@ const useChat = ({ wsUrl, wsToken, flowConfig, sessionId, stopGenerationWsUrl, e
|
|
|
12697
13416
|
flow: flowConfig,
|
|
12698
13417
|
session_id: sessionId,
|
|
12699
13418
|
token: wsToken,
|
|
12700
|
-
prompt: text,
|
|
13419
|
+
prompt: text || "", // Allow empty prompt when sending files
|
|
12701
13420
|
};
|
|
13421
|
+
if ((options === null || options === void 0 ? void 0 : options.fileReferences) && options.fileReferences.length > 0) {
|
|
13422
|
+
messageData = {
|
|
13423
|
+
...messageData,
|
|
13424
|
+
file_references: options.fileReferences,
|
|
13425
|
+
};
|
|
13426
|
+
}
|
|
12702
13427
|
if (iframeContext.pageContent) {
|
|
12703
13428
|
messageData = {
|
|
12704
13429
|
...messageData,
|
|
@@ -12883,7 +13608,7 @@ function useMentorSettings({ mentorId, tenantKey, username }) {
|
|
|
12883
13608
|
};
|
|
12884
13609
|
}
|
|
12885
13610
|
|
|
12886
|
-
function useAdvancedChat({ tenantKey, mentorId, username = ANONYMOUS_USERNAME, token, wsUrl, stopGenerationWsUrl, redirectToAuthSpa, errorHandler, sendMessageToParentWebsite, isPreviewMode, mentorShareableToken, on402Error, }) {
|
|
13611
|
+
function useAdvancedChat({ tenantKey, mentorId, username = ANONYMOUS_USERNAME, token, wsUrl, stopGenerationWsUrl, redirectToAuthSpa, errorHandler, sendMessageToParentWebsite, isPreviewMode, mentorShareableToken, on402Error, cachedSessionId, onStartNewChat, }) {
|
|
12887
13612
|
var _a, _b, _c, _d;
|
|
12888
13613
|
const dispatch = useDispatch();
|
|
12889
13614
|
const [createSessionId, { isLoading: isLoadingSessionIds }] = useCreateSessionIdMutation();
|
|
@@ -12922,7 +13647,7 @@ function useAdvancedChat({ tenantKey, mentorId, username = ANONYMOUS_USERNAME, t
|
|
|
12922
13647
|
username,
|
|
12923
13648
|
pathway: mentorId,
|
|
12924
13649
|
},
|
|
12925
|
-
sessionId: sessionIds[activeTab],
|
|
13650
|
+
sessionId: cachedSessionId !== null && cachedSessionId !== void 0 ? cachedSessionId : sessionIds[activeTab],
|
|
12926
13651
|
activeTab,
|
|
12927
13652
|
wsToken: token,
|
|
12928
13653
|
errorHandler,
|
|
@@ -12933,6 +13658,49 @@ function useAdvancedChat({ tenantKey, mentorId, username = ANONYMOUS_USERNAME, t
|
|
|
12933
13658
|
onStatusChange,
|
|
12934
13659
|
on402Error,
|
|
12935
13660
|
});
|
|
13661
|
+
const [getSessionChats, { isError: isErrorGettingSessionChats }] = useLazyGetSessionIdQuery();
|
|
13662
|
+
useEffect(() => {
|
|
13663
|
+
const getChats = async () => {
|
|
13664
|
+
var _a;
|
|
13665
|
+
try {
|
|
13666
|
+
const { data } = await getSessionChats({
|
|
13667
|
+
sessionId: cachedSessionId !== null && cachedSessionId !== void 0 ? cachedSessionId : "",
|
|
13668
|
+
org: tenantKey,
|
|
13669
|
+
share: true,
|
|
13670
|
+
});
|
|
13671
|
+
let previousChats = [];
|
|
13672
|
+
try {
|
|
13673
|
+
previousChats =
|
|
13674
|
+
((_a = data === null || data === void 0 ? void 0 : data.results) === null || _a === void 0 ? void 0 : _a.map((result) => {
|
|
13675
|
+
return {
|
|
13676
|
+
...result,
|
|
13677
|
+
role: result.type === "human" ? "user" : "assistant",
|
|
13678
|
+
visible: true,
|
|
13679
|
+
id: new Date().getTime(),
|
|
13680
|
+
content: typeof result.content === "object" &&
|
|
13681
|
+
result.content.length > 0
|
|
13682
|
+
? result.content[0].text
|
|
13683
|
+
: result.content,
|
|
13684
|
+
};
|
|
13685
|
+
}).reverse()) || [];
|
|
13686
|
+
}
|
|
13687
|
+
catch (error) {
|
|
13688
|
+
console.error(JSON.stringify(error));
|
|
13689
|
+
}
|
|
13690
|
+
dispatch(chatActions.setNewMessages(previousChats));
|
|
13691
|
+
}
|
|
13692
|
+
catch (error) {
|
|
13693
|
+
console.error(JSON.stringify(error));
|
|
13694
|
+
}
|
|
13695
|
+
};
|
|
13696
|
+
if (cachedSessionId) {
|
|
13697
|
+
dispatch(chatActions.updateSessionIds(cachedSessionId));
|
|
13698
|
+
getChats();
|
|
13699
|
+
}
|
|
13700
|
+
}, [cachedSessionId]);
|
|
13701
|
+
useEffect(() => {
|
|
13702
|
+
}, [isErrorGettingSessionChats]);
|
|
13703
|
+
useEffect(() => { }, []);
|
|
12936
13704
|
const startNewChat = React__default.useCallback(async () => {
|
|
12937
13705
|
// Reset all chat state
|
|
12938
13706
|
if (!showingSharedChat) {
|
|
@@ -12942,6 +13710,7 @@ function useAdvancedChat({ tenantKey, mentorId, username = ANONYMOUS_USERNAME, t
|
|
|
12942
13710
|
dispatch(chatActions.setStreaming(false));
|
|
12943
13711
|
dispatch(chatActions.resetCurrentStreamingMessage(undefined));
|
|
12944
13712
|
dispatch(chatActions.setTools([]));
|
|
13713
|
+
dispatch(clearFiles(undefined));
|
|
12945
13714
|
if (isPreviewMode) {
|
|
12946
13715
|
return;
|
|
12947
13716
|
}
|
|
@@ -12952,6 +13721,7 @@ function useAdvancedChat({ tenantKey, mentorId, username = ANONYMOUS_USERNAME, t
|
|
|
12952
13721
|
// @ts-ignore
|
|
12953
13722
|
requestBody["shareable_link_token"] = mentorShareableToken;
|
|
12954
13723
|
}
|
|
13724
|
+
console.log("[startNewChat] requestBody", tenantKey, username, JSON.stringify(requestBody));
|
|
12955
13725
|
const response = await createSessionId({
|
|
12956
13726
|
org: tenantKey,
|
|
12957
13727
|
// @ts-ignore
|
|
@@ -12959,10 +13729,12 @@ function useAdvancedChat({ tenantKey, mentorId, username = ANONYMOUS_USERNAME, t
|
|
|
12959
13729
|
requestBody,
|
|
12960
13730
|
}).unwrap();
|
|
12961
13731
|
dispatch(chatActions.updateSessionIds(response.session_id));
|
|
12962
|
-
|
|
13732
|
+
onStartNewChat === null || onStartNewChat === void 0 ? void 0 : onStartNewChat(response.session_id);
|
|
12963
13733
|
}
|
|
12964
13734
|
catch (error) {
|
|
12965
|
-
errorHandler === null || errorHandler === void 0 ? void 0 : errorHandler(
|
|
13735
|
+
errorHandler === null || errorHandler === void 0 ? void 0 : errorHandler(`Failed to start new chat: ${JSON.stringify(error)}`);
|
|
13736
|
+
console.log("[auth-redirect] Failed to start new chat", JSON.stringify({ error }));
|
|
13737
|
+
redirectToAuthSpa(undefined, undefined, true);
|
|
12966
13738
|
}
|
|
12967
13739
|
}, [
|
|
12968
13740
|
isPreviewMode,
|
|
@@ -12976,7 +13748,7 @@ function useAdvancedChat({ tenantKey, mentorId, username = ANONYMOUS_USERNAME, t
|
|
|
12976
13748
|
]);
|
|
12977
13749
|
React__default.useEffect(() => {
|
|
12978
13750
|
if (!showingSharedChat || mentorSettings.allowAnonymous) {
|
|
12979
|
-
if (mentorSettings.allowAnonymous !== undefined) {
|
|
13751
|
+
if (mentorSettings.allowAnonymous !== undefined && !cachedSessionId) {
|
|
12980
13752
|
startNewChat();
|
|
12981
13753
|
}
|
|
12982
13754
|
}
|
|
@@ -13028,7 +13800,7 @@ function useAdvancedChat({ tenantKey, mentorId, username = ANONYMOUS_USERNAME, t
|
|
|
13028
13800
|
}
|
|
13029
13801
|
}
|
|
13030
13802
|
catch (error) {
|
|
13031
|
-
errorHandler === null || errorHandler === void 0 ? void 0 : errorHandler(
|
|
13803
|
+
errorHandler === null || errorHandler === void 0 ? void 0 : errorHandler(`Failed to start new chat: ${error}`);
|
|
13032
13804
|
return;
|
|
13033
13805
|
}
|
|
13034
13806
|
}
|
|
@@ -17918,5 +18690,123 @@ const tenantSchema = z.object({
|
|
|
17918
18690
|
});
|
|
17919
18691
|
const tenantKeySchema = z.string().min(1);
|
|
17920
18692
|
|
|
17921
|
-
|
|
18693
|
+
/**
|
|
18694
|
+
* Chat area size constants
|
|
18695
|
+
*/
|
|
18696
|
+
const CHAT_AREA_SIZE = {
|
|
18697
|
+
DEFAULT: 672,
|
|
18698
|
+
MIN: 672,
|
|
18699
|
+
MAX: 1024,
|
|
18700
|
+
};
|
|
18701
|
+
|
|
18702
|
+
/**
|
|
18703
|
+
* Extract file information from File object
|
|
18704
|
+
*/
|
|
18705
|
+
function getFileInfo(file) {
|
|
18706
|
+
const fileName = file.name;
|
|
18707
|
+
const contentType = file.type || "application/octet-stream";
|
|
18708
|
+
const fileSize = file.size;
|
|
18709
|
+
return {
|
|
18710
|
+
fileName,
|
|
18711
|
+
contentType,
|
|
18712
|
+
fileSize,
|
|
18713
|
+
};
|
|
18714
|
+
}
|
|
18715
|
+
/**
|
|
18716
|
+
* Request presigned S3 URL from backend
|
|
18717
|
+
* Note: This function should be called via the RTK Query mutation hook
|
|
18718
|
+
* This is a helper to show the data flow
|
|
18719
|
+
*/
|
|
18720
|
+
async function requestPresignedUrl(sessionId, file, getUploadUrlFn, org, userId) {
|
|
18721
|
+
const { fileName, contentType, fileSize } = getFileInfo(file);
|
|
18722
|
+
return await getUploadUrlFn({
|
|
18723
|
+
org,
|
|
18724
|
+
userId,
|
|
18725
|
+
requestBody: {
|
|
18726
|
+
session_id: sessionId,
|
|
18727
|
+
file_name: fileName,
|
|
18728
|
+
content_type: contentType,
|
|
18729
|
+
file_size: fileSize,
|
|
18730
|
+
},
|
|
18731
|
+
});
|
|
18732
|
+
}
|
|
18733
|
+
/**
|
|
18734
|
+
* Upload file directly to S3 using presigned URL
|
|
18735
|
+
* Uses axios for cross-platform support (web + React Native)
|
|
18736
|
+
*/
|
|
18737
|
+
async function uploadToS3(presignedUrl, file, contentType, onProgress) {
|
|
18738
|
+
await axios.put(presignedUrl, file, {
|
|
18739
|
+
headers: {
|
|
18740
|
+
"Content-Type": contentType,
|
|
18741
|
+
},
|
|
18742
|
+
onUploadProgress: (progressEvent) => {
|
|
18743
|
+
if (onProgress && progressEvent.total) {
|
|
18744
|
+
const progress = Math.round((progressEvent.loaded / progressEvent.total) * 100);
|
|
18745
|
+
onProgress(progress);
|
|
18746
|
+
}
|
|
18747
|
+
},
|
|
18748
|
+
});
|
|
18749
|
+
}
|
|
18750
|
+
/**
|
|
18751
|
+
* Complete file upload flow:
|
|
18752
|
+
* 1. Request presigned URL
|
|
18753
|
+
* 2. Upload to S3
|
|
18754
|
+
* 3. Return file reference
|
|
18755
|
+
*/
|
|
18756
|
+
async function createFileReference(file, sessionId, getUploadUrlFn, org, userId, onProgress) {
|
|
18757
|
+
// Step 1: Get presigned URL
|
|
18758
|
+
const presignedResponse = await requestPresignedUrl(sessionId, file, getUploadUrlFn, org, userId);
|
|
18759
|
+
// Step 2: Upload to S3
|
|
18760
|
+
const { fileName, contentType, fileSize } = getFileInfo(file);
|
|
18761
|
+
await uploadToS3(presignedResponse.upload_url, file, contentType, onProgress);
|
|
18762
|
+
// Step 3: Return file reference
|
|
18763
|
+
return {
|
|
18764
|
+
file_id: presignedResponse.file_id,
|
|
18765
|
+
file_key: presignedResponse.file_key,
|
|
18766
|
+
file_name: fileName,
|
|
18767
|
+
content_type: contentType,
|
|
18768
|
+
file_size: fileSize,
|
|
18769
|
+
};
|
|
18770
|
+
}
|
|
18771
|
+
/**
|
|
18772
|
+
* Upload multiple files and return their references
|
|
18773
|
+
*/
|
|
18774
|
+
async function createMultipleFileReferences(files, sessionId, getUploadUrlFn, org, userId, onFileProgress) {
|
|
18775
|
+
const fileReferences = [];
|
|
18776
|
+
for (let i = 0; i < files.length; i++) {
|
|
18777
|
+
const file = files[i];
|
|
18778
|
+
const onProgress = onFileProgress
|
|
18779
|
+
? (progress) => onFileProgress(i, progress)
|
|
18780
|
+
: undefined;
|
|
18781
|
+
const fileReference = await createFileReference(file, sessionId, getUploadUrlFn, org, userId, onProgress);
|
|
18782
|
+
fileReferences.push(fileReference);
|
|
18783
|
+
}
|
|
18784
|
+
return fileReferences;
|
|
18785
|
+
}
|
|
18786
|
+
/**
|
|
18787
|
+
* Validate file before upload
|
|
18788
|
+
* Returns error message if invalid, null if valid
|
|
18789
|
+
*/
|
|
18790
|
+
function validateFile(file, maxSizeBytes, allowedTypes) {
|
|
18791
|
+
var _a;
|
|
18792
|
+
// Check file size
|
|
18793
|
+
if (maxSizeBytes && file.size > maxSizeBytes) {
|
|
18794
|
+
const maxSizeMB = (maxSizeBytes / (1024 * 1024)).toFixed(2);
|
|
18795
|
+
const fileSizeMB = (file.size / (1024 * 1024)).toFixed(2);
|
|
18796
|
+
return `File size (${fileSizeMB}MB) exceeds maximum allowed size (${maxSizeMB}MB)`;
|
|
18797
|
+
}
|
|
18798
|
+
// Check file type
|
|
18799
|
+
if (allowedTypes && allowedTypes.length > 0) {
|
|
18800
|
+
const fileExtension = (_a = file.name.split(".").pop()) === null || _a === void 0 ? void 0 : _a.toLowerCase();
|
|
18801
|
+
const fileType = file.type.toLowerCase();
|
|
18802
|
+
const isTypeAllowed = allowedTypes.some((allowed) => fileType.includes(allowed.toLowerCase()) ||
|
|
18803
|
+
fileExtension === allowed.toLowerCase().replace(".", ""));
|
|
18804
|
+
if (!isTypeAllowed) {
|
|
18805
|
+
return `File type not allowed. Allowed types: ${allowedTypes.join(", ")}`;
|
|
18806
|
+
}
|
|
18807
|
+
}
|
|
18808
|
+
return null;
|
|
18809
|
+
}
|
|
18810
|
+
|
|
18811
|
+
export { ALPHANUMERIC_32_REGEX, ANONYMOUS_USERNAME, AuthContext, AuthContextProvider, AuthProvider, CHAT_AREA_SIZE, LOCAL_STORAGE_KEYS, MAX_INITIAL_WEBSOCKET_CONNECTION_ATTEMPTS, MENTOR_CHAT_DOCUMENTS_EXTENSIONS, METADATAS, MentorProvider, SUBSCRIPTION_MESSAGES, SUBSCRIPTION_PACKAGES, SUBSCRIPTION_TRIGGERS, SUBSCRIPTION_V2_TRIGGERS, SubscriptionFlow, SubscriptionFlowV2, TOOLS, TenantContext, TenantContextProvider, TenantProvider, TimeTracker, addFiles, addProtocolToUrl, advancedTabs, advancedTabsProperties, chatActions, chatSliceReducerShared, clearAuthCookies, clearCookies, clearCurrentTenantCookie, clearFiles, createFileReference, createMultipleFileReferences, defaultSessionIds, deleteCookie, deleteCookieOnAllDomains, filesReducer, filesSlice, formatRelativeTime, getAuthSpaJoinUrl, getDomainParts, getFileInfo, getInitials, getParentDomain, getPlatformKey, getTimeAgo, getUserName, handleLogout, isAlphaNumeric32, isInIframe, isJSON, isLoggedIn, loadMetadataConfig, redirectToAuthSpa, redirectToAuthSpaJoinTenant, removeFile, requestPresignedUrl, selectActiveChatMessages, selectActiveTab, selectChats, selectCurrentStreamingMessage, selectDocumentFilter, selectIframeContext, selectIsError, selectIsPending, selectIsStopped, selectIsTyping, selectNumberOfActiveChatMessages, selectSessionId, selectSessionIds, selectShouldStartNewChat, selectShowingSharedChat, selectStatus, selectStreaming, selectToken, selectTokenEnabled, selectTools, sendMessageToParentWebsite, setCookieForAuth, syncAuthToCookies, tenantKeySchema, tenantSchema, translatePrompt, updateFileMetadata, updateFileProgress, updateFileRetryCount, updateFileStatus, updateFileUrl, updateFileUrlFromWebSocket, uploadToS3, useAdvancedChat, useAuthContext, useAuthProvider, useChat, useDayJs, useExternalPricingPlan, useMentorSettings, useMentorTools, useProfileImageUpload, useSubscriptionHandler, useSubscriptionHandlerV2, useTenantContext, useTenantMetadata, useTimeTracker, useTimeTrackerNative, useUserProfileUpdate, userDataSchema, validateFile };
|
|
17922
18812
|
//# sourceMappingURL=index.esm.js.map
|