@optifye/dashboard-core 6.11.21 → 6.11.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +268 -44
- package/dist/index.mjs +268 -45
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2232,6 +2232,61 @@ var clearAuthSnapshot = () => {
|
|
|
2232
2232
|
safeStorageRemoveItem(AUTH_SNAPSHOT_STORAGE_KEY);
|
|
2233
2233
|
};
|
|
2234
2234
|
|
|
2235
|
+
// src/lib/auth/authAuditLog.ts
|
|
2236
|
+
var TAB_ID = typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID().slice(0, 8) : Math.random().toString(36).slice(2, 10);
|
|
2237
|
+
var FLUSH_INTERVAL_MS = 3e4;
|
|
2238
|
+
var MAX_QUEUE_SIZE = 50;
|
|
2239
|
+
var queue = [];
|
|
2240
|
+
var flushTimer = null;
|
|
2241
|
+
var apiBaseUrl = "";
|
|
2242
|
+
var getAccessToken = null;
|
|
2243
|
+
var initAuthAuditLog = (baseUrl, tokenGetter) => {
|
|
2244
|
+
apiBaseUrl = baseUrl;
|
|
2245
|
+
getAccessToken = tokenGetter;
|
|
2246
|
+
if (typeof window === "undefined") return;
|
|
2247
|
+
if (!flushTimer) {
|
|
2248
|
+
flushTimer = setInterval(flushAuthAuditLog, FLUSH_INTERVAL_MS);
|
|
2249
|
+
window.addEventListener("visibilitychange", () => {
|
|
2250
|
+
if (document.visibilityState === "hidden") {
|
|
2251
|
+
void flushAuthAuditLog();
|
|
2252
|
+
}
|
|
2253
|
+
});
|
|
2254
|
+
}
|
|
2255
|
+
};
|
|
2256
|
+
var logAuthEvent = (eventType, details = {}) => {
|
|
2257
|
+
queue.push({
|
|
2258
|
+
event_type: eventType,
|
|
2259
|
+
tab_id: TAB_ID,
|
|
2260
|
+
details: {
|
|
2261
|
+
...details,
|
|
2262
|
+
ts: (/* @__PURE__ */ new Date()).toISOString()
|
|
2263
|
+
}
|
|
2264
|
+
});
|
|
2265
|
+
if (queue.length >= MAX_QUEUE_SIZE) {
|
|
2266
|
+
void flushAuthAuditLog();
|
|
2267
|
+
}
|
|
2268
|
+
};
|
|
2269
|
+
var flushAuthAuditLog = async () => {
|
|
2270
|
+
if (queue.length === 0 || !apiBaseUrl || !getAccessToken) return;
|
|
2271
|
+
const events = queue.splice(0, MAX_QUEUE_SIZE);
|
|
2272
|
+
try {
|
|
2273
|
+
const token = await getAccessToken();
|
|
2274
|
+
if (!token) return;
|
|
2275
|
+
const body = JSON.stringify({ events });
|
|
2276
|
+
const url = `${apiBaseUrl}/api/auth/audit`;
|
|
2277
|
+
await fetch(url, {
|
|
2278
|
+
method: "POST",
|
|
2279
|
+
headers: {
|
|
2280
|
+
"Content-Type": "application/json",
|
|
2281
|
+
Authorization: `Bearer ${token}`
|
|
2282
|
+
},
|
|
2283
|
+
body,
|
|
2284
|
+
keepalive: document.visibilityState === "hidden"
|
|
2285
|
+
});
|
|
2286
|
+
} catch {
|
|
2287
|
+
}
|
|
2288
|
+
};
|
|
2289
|
+
|
|
2235
2290
|
// src/lib/auth/session.ts
|
|
2236
2291
|
var DEFAULT_MIN_VALIDITY_MS = 6e4;
|
|
2237
2292
|
var refreshPromises = /* @__PURE__ */ new WeakMap();
|
|
@@ -2241,6 +2296,24 @@ var isInvalidRefreshTokenError = (error) => {
|
|
|
2241
2296
|
if (!message.includes("refresh token")) return false;
|
|
2242
2297
|
return message.includes("invalid") || message.includes("not found") || message.includes("revoked");
|
|
2243
2298
|
};
|
|
2299
|
+
var getSessionFromStorage = () => {
|
|
2300
|
+
if (typeof window === "undefined") return null;
|
|
2301
|
+
try {
|
|
2302
|
+
for (let i = 0; i < localStorage.length; i++) {
|
|
2303
|
+
const key = localStorage.key(i);
|
|
2304
|
+
if (key?.startsWith("sb-") && key?.endsWith("-auth-token")) {
|
|
2305
|
+
const raw = localStorage.getItem(key);
|
|
2306
|
+
if (!raw) continue;
|
|
2307
|
+
const parsed = JSON.parse(raw);
|
|
2308
|
+
if (parsed?.access_token && parsed?.refresh_token && parsed?.expires_at) {
|
|
2309
|
+
return parsed;
|
|
2310
|
+
}
|
|
2311
|
+
}
|
|
2312
|
+
}
|
|
2313
|
+
} catch {
|
|
2314
|
+
}
|
|
2315
|
+
return null;
|
|
2316
|
+
};
|
|
2244
2317
|
var isSessionValid = (session, minValidityMs = 0) => {
|
|
2245
2318
|
if (!session) return false;
|
|
2246
2319
|
if (!session.expires_at) return true;
|
|
@@ -2297,6 +2370,18 @@ var refreshSessionSingleFlight = async (supabase) => {
|
|
|
2297
2370
|
}
|
|
2298
2371
|
const invalidRefreshToken = isInvalidRefreshTokenError(refreshError);
|
|
2299
2372
|
if (invalidRefreshToken) {
|
|
2373
|
+
const storedSession = getSessionFromStorage();
|
|
2374
|
+
if (storedSession && isSessionValid(storedSession, 0)) {
|
|
2375
|
+
console.log("[Auth] Recovered session from localStorage after invalid refresh token (another tab refreshed)");
|
|
2376
|
+
try {
|
|
2377
|
+
await supabase.auth.setSession({
|
|
2378
|
+
access_token: storedSession.access_token,
|
|
2379
|
+
refresh_token: storedSession.refresh_token
|
|
2380
|
+
});
|
|
2381
|
+
} catch {
|
|
2382
|
+
}
|
|
2383
|
+
return { session: storedSession, error: null, invalidRefreshToken: false };
|
|
2384
|
+
}
|
|
2300
2385
|
try {
|
|
2301
2386
|
await supabase.auth.signOut({ scope: "local" });
|
|
2302
2387
|
} catch (error) {
|
|
@@ -6606,7 +6691,17 @@ function captureSentryException(error, extras) {
|
|
|
6606
6691
|
}
|
|
6607
6692
|
|
|
6608
6693
|
// src/lib/services/mixpanelService.ts
|
|
6694
|
+
var ROOT_DASHBOARD_EVENT_NAMES = {
|
|
6695
|
+
operations_overview: "Operations Overview Page Clicked",
|
|
6696
|
+
monitor: "Live Monitor Clicked"
|
|
6697
|
+
};
|
|
6698
|
+
var MIXPANEL_EVENT_NAME_ALIASES = {
|
|
6699
|
+
"Operations Overview clicked": ROOT_DASHBOARD_EVENT_NAMES.operations_overview,
|
|
6700
|
+
"monitor page clicked": ROOT_DASHBOARD_EVENT_NAMES.monitor
|
|
6701
|
+
};
|
|
6702
|
+
var MAX_QUEUED_MIXPANEL_ACTIONS = 200;
|
|
6609
6703
|
var isMixpanelInitialized = false;
|
|
6704
|
+
var queuedMixpanelActions = [];
|
|
6610
6705
|
var currentUserProperties;
|
|
6611
6706
|
var MIXPANEL_WARNING_RATE_LIMIT = {
|
|
6612
6707
|
windowMs: 10 * 60 * 1e3,
|
|
@@ -6634,6 +6729,52 @@ var reportMixpanelError = (key, error, extras) => {
|
|
|
6634
6729
|
if (!shouldReportMixpanel(key, true)) return;
|
|
6635
6730
|
captureSentryException(error, { ...baseMixpanelExtras(), ...extras });
|
|
6636
6731
|
};
|
|
6732
|
+
var normalizeCoreEventName = (eventName) => {
|
|
6733
|
+
const canonicalName = MIXPANEL_EVENT_NAME_ALIASES[eventName] || eventName;
|
|
6734
|
+
if (canonicalName !== eventName) {
|
|
6735
|
+
reportMixpanelWarning(`event_alias:${eventName}`, "Mixpanel event alias rewritten to canonical name", {
|
|
6736
|
+
operation: "track_event",
|
|
6737
|
+
originalEventName: eventName,
|
|
6738
|
+
canonicalEventName: canonicalName
|
|
6739
|
+
});
|
|
6740
|
+
}
|
|
6741
|
+
return canonicalName;
|
|
6742
|
+
};
|
|
6743
|
+
var queueMixpanelAction = (action) => {
|
|
6744
|
+
if (queuedMixpanelActions.length >= MAX_QUEUED_MIXPANEL_ACTIONS) {
|
|
6745
|
+
queuedMixpanelActions.shift();
|
|
6746
|
+
reportMixpanelWarning("queue_overflow", "Mixpanel queue overflow, dropping oldest queued action", {
|
|
6747
|
+
operation: "queue",
|
|
6748
|
+
maxQueuedActions: MAX_QUEUED_MIXPANEL_ACTIONS
|
|
6749
|
+
});
|
|
6750
|
+
}
|
|
6751
|
+
queuedMixpanelActions.push(action);
|
|
6752
|
+
};
|
|
6753
|
+
var flushQueuedMixpanelActions = () => {
|
|
6754
|
+
if (!isMixpanelInitialized || queuedMixpanelActions.length === 0) return;
|
|
6755
|
+
const pendingActions = queuedMixpanelActions.splice(0, queuedMixpanelActions.length);
|
|
6756
|
+
pendingActions.forEach((action) => {
|
|
6757
|
+
try {
|
|
6758
|
+
if (action.type === "identify") {
|
|
6759
|
+
mixpanel__default.default.identify(action.userId);
|
|
6760
|
+
if (action.userProperties) {
|
|
6761
|
+
mixpanel__default.default.people.set(action.userProperties);
|
|
6762
|
+
}
|
|
6763
|
+
return;
|
|
6764
|
+
}
|
|
6765
|
+
if (action.type === "page_view") {
|
|
6766
|
+
mixpanel__default.default.track("Page View", { page: action.pageName, ...action.properties || {} });
|
|
6767
|
+
return;
|
|
6768
|
+
}
|
|
6769
|
+
mixpanel__default.default.track(action.eventName, action.properties || {});
|
|
6770
|
+
} catch (err) {
|
|
6771
|
+
reportMixpanelError("flush_queued_action_failed", err, {
|
|
6772
|
+
operation: "flush_queue",
|
|
6773
|
+
actionType: action.type
|
|
6774
|
+
});
|
|
6775
|
+
}
|
|
6776
|
+
});
|
|
6777
|
+
};
|
|
6637
6778
|
var initializeCoreMixpanel = (token, debugOrOptions, trackPageViewArg) => {
|
|
6638
6779
|
if (!token) {
|
|
6639
6780
|
console.warn("Mixpanel token not provided for initialization. Mixpanel will not be enabled.");
|
|
@@ -6691,6 +6832,7 @@ var initializeCoreMixpanel = (token, debugOrOptions, trackPageViewArg) => {
|
|
|
6691
6832
|
try {
|
|
6692
6833
|
mixpanel__default.default.init(token, initOptions);
|
|
6693
6834
|
isMixpanelInitialized = true;
|
|
6835
|
+
flushQueuedMixpanelActions();
|
|
6694
6836
|
} catch (err) {
|
|
6695
6837
|
reportMixpanelError("init_failed", err, {
|
|
6696
6838
|
operation: "init",
|
|
@@ -6706,7 +6848,12 @@ var initializeCoreMixpanel = (token, debugOrOptions, trackPageViewArg) => {
|
|
|
6706
6848
|
};
|
|
6707
6849
|
var trackCorePageView = (pageName, properties) => {
|
|
6708
6850
|
if (!isMixpanelInitialized) {
|
|
6709
|
-
|
|
6851
|
+
queueMixpanelAction({
|
|
6852
|
+
type: "page_view",
|
|
6853
|
+
pageName,
|
|
6854
|
+
properties
|
|
6855
|
+
});
|
|
6856
|
+
reportMixpanelWarning("track_pageview_queued", "Mixpanel not initialized yet: page view queued", {
|
|
6710
6857
|
operation: "track_pageview",
|
|
6711
6858
|
pageName
|
|
6712
6859
|
});
|
|
@@ -6722,10 +6869,20 @@ var trackCorePageView = (pageName, properties) => {
|
|
|
6722
6869
|
}
|
|
6723
6870
|
};
|
|
6724
6871
|
var trackCoreEvent = (eventName, properties) => {
|
|
6872
|
+
const normalizedEventName = normalizeCoreEventName(eventName);
|
|
6725
6873
|
if (!isMixpanelInitialized) {
|
|
6726
|
-
|
|
6874
|
+
const mergedProps2 = {
|
|
6875
|
+
...currentUserProperties || {},
|
|
6876
|
+
...properties || {}
|
|
6877
|
+
};
|
|
6878
|
+
queueMixpanelAction({
|
|
6879
|
+
type: "track",
|
|
6880
|
+
eventName: normalizedEventName,
|
|
6881
|
+
properties: mergedProps2
|
|
6882
|
+
});
|
|
6883
|
+
reportMixpanelWarning("track_event_queued", "Mixpanel not initialized yet: event queued", {
|
|
6727
6884
|
operation: "track_event",
|
|
6728
|
-
eventName
|
|
6885
|
+
eventName: normalizedEventName
|
|
6729
6886
|
});
|
|
6730
6887
|
return;
|
|
6731
6888
|
}
|
|
@@ -6736,11 +6893,11 @@ var trackCoreEvent = (eventName, properties) => {
|
|
|
6736
6893
|
...properties || {}
|
|
6737
6894
|
};
|
|
6738
6895
|
try {
|
|
6739
|
-
mixpanel__default.default.track(
|
|
6896
|
+
mixpanel__default.default.track(normalizedEventName, mergedProps);
|
|
6740
6897
|
} catch (err) {
|
|
6741
6898
|
reportMixpanelError("track_event_failed", err, {
|
|
6742
6899
|
operation: "track_event",
|
|
6743
|
-
eventName
|
|
6900
|
+
eventName: normalizedEventName
|
|
6744
6901
|
});
|
|
6745
6902
|
}
|
|
6746
6903
|
};
|
|
@@ -6791,8 +6948,14 @@ var getCoreSessionReplayUrl = () => {
|
|
|
6791
6948
|
return null;
|
|
6792
6949
|
};
|
|
6793
6950
|
var identifyCoreUser = (userId, userProperties) => {
|
|
6951
|
+
currentUserProperties = userProperties ? { ...userProperties } : void 0;
|
|
6794
6952
|
if (!isMixpanelInitialized) {
|
|
6795
|
-
|
|
6953
|
+
queueMixpanelAction({
|
|
6954
|
+
type: "identify",
|
|
6955
|
+
userId,
|
|
6956
|
+
userProperties: currentUserProperties
|
|
6957
|
+
});
|
|
6958
|
+
reportMixpanelWarning("identify_queued", "Mixpanel not initialized yet: identify queued", {
|
|
6796
6959
|
operation: "identify",
|
|
6797
6960
|
userIdPresent: Boolean(userId),
|
|
6798
6961
|
hasUserProperties: Boolean(userProperties)
|
|
@@ -6812,17 +6975,20 @@ var identifyCoreUser = (userId, userProperties) => {
|
|
|
6812
6975
|
});
|
|
6813
6976
|
return;
|
|
6814
6977
|
}
|
|
6815
|
-
currentUserProperties = { ...userProperties };
|
|
6978
|
+
currentUserProperties = userProperties ? { ...userProperties } : void 0;
|
|
6816
6979
|
};
|
|
6817
6980
|
var resetCoreMixpanel = () => {
|
|
6818
|
-
if (
|
|
6819
|
-
|
|
6820
|
-
|
|
6821
|
-
|
|
6822
|
-
|
|
6823
|
-
|
|
6824
|
-
|
|
6981
|
+
if (isMixpanelInitialized) {
|
|
6982
|
+
try {
|
|
6983
|
+
mixpanel__default.default.reset();
|
|
6984
|
+
} catch (err) {
|
|
6985
|
+
reportMixpanelError("reset_failed", err, {
|
|
6986
|
+
operation: "reset"
|
|
6987
|
+
});
|
|
6988
|
+
}
|
|
6825
6989
|
}
|
|
6990
|
+
currentUserProperties = void 0;
|
|
6991
|
+
queuedMixpanelActions.splice(0, queuedMixpanelActions.length);
|
|
6826
6992
|
};
|
|
6827
6993
|
|
|
6828
6994
|
// src/lib/services/sseClient.ts
|
|
@@ -10379,6 +10545,15 @@ var AuthProvider = ({ children }) => {
|
|
|
10379
10545
|
const [error, setError] = React142.useState(null);
|
|
10380
10546
|
const [authStatus, setAuthStatus] = React142.useState("loading");
|
|
10381
10547
|
const [showOnboarding, setShowOnboarding] = React142.useState(false);
|
|
10548
|
+
const auditInitRef = React142.useRef(false);
|
|
10549
|
+
if (!auditInitRef.current && typeof window !== "undefined") {
|
|
10550
|
+
const backendUrl = process.env.NEXT_PUBLIC_BACKEND_URL || "http://localhost:8000";
|
|
10551
|
+
initAuthAuditLog(backendUrl, async () => {
|
|
10552
|
+
const { data: { session: s } } = await supabase.auth.getSession();
|
|
10553
|
+
return s?.access_token ?? null;
|
|
10554
|
+
});
|
|
10555
|
+
auditInitRef.current = true;
|
|
10556
|
+
}
|
|
10382
10557
|
const isFetchingRef = React142.useRef(false);
|
|
10383
10558
|
const lastProcessedSessionRef = React142.useRef(null);
|
|
10384
10559
|
const hasAuthenticatedRef = React142.useRef(false);
|
|
@@ -10542,6 +10717,7 @@ var AuthProvider = ({ children }) => {
|
|
|
10542
10717
|
resetRecoveryState();
|
|
10543
10718
|
if (refreshResult.invalidRefreshToken) {
|
|
10544
10719
|
console.warn("[AuthContext] Refresh token invalid, redirecting to login");
|
|
10720
|
+
logAuthEvent("forced_logout", { reason: "invalid_refresh_token", trigger: "fetchSession" });
|
|
10545
10721
|
setAuthStatus("failed");
|
|
10546
10722
|
clearSessionState();
|
|
10547
10723
|
await handleAuthRequired(supabase, "session_expired");
|
|
@@ -10641,6 +10817,7 @@ var AuthProvider = ({ children }) => {
|
|
|
10641
10817
|
const signOut = React142.useCallback(async () => {
|
|
10642
10818
|
try {
|
|
10643
10819
|
console.log("[AuthContext] Signing out");
|
|
10820
|
+
logAuthEvent("sign_out_initiated", { trigger: "user_action" });
|
|
10644
10821
|
setAuthStatus("loading");
|
|
10645
10822
|
resetRecoveryState();
|
|
10646
10823
|
clearAuthSnapshot();
|
|
@@ -10665,6 +10842,22 @@ var AuthProvider = ({ children }) => {
|
|
|
10665
10842
|
return;
|
|
10666
10843
|
}
|
|
10667
10844
|
const monitorTokenExpiry = async () => {
|
|
10845
|
+
const storedSession = getSessionFromStorage();
|
|
10846
|
+
if (storedSession && isSessionValid(storedSession, 5 * 60 * 1e3)) {
|
|
10847
|
+
if (storedSession.access_token !== session.access_token) {
|
|
10848
|
+
console.log("[AuthContext] Adopting session refreshed by another tab");
|
|
10849
|
+
logAuthEvent("cross_tab_refresh_adopted", { source: "monitorTokenExpiry" });
|
|
10850
|
+
setTrackedSession(storedSession);
|
|
10851
|
+
try {
|
|
10852
|
+
await supabase.auth.setSession({
|
|
10853
|
+
access_token: storedSession.access_token,
|
|
10854
|
+
refresh_token: storedSession.refresh_token
|
|
10855
|
+
});
|
|
10856
|
+
} catch {
|
|
10857
|
+
}
|
|
10858
|
+
}
|
|
10859
|
+
return;
|
|
10860
|
+
}
|
|
10668
10861
|
const expiresAt = session.expires_at;
|
|
10669
10862
|
if (!expiresAt) {
|
|
10670
10863
|
console.warn("[AuthContext] Session has no expiry time");
|
|
@@ -10677,13 +10870,16 @@ var AuthProvider = ({ children }) => {
|
|
|
10677
10870
|
console.log(`[AuthContext] Token expires in ${minutesUntilExpiry} minutes`);
|
|
10678
10871
|
if (minutesUntilExpiry < 5 && timeUntilExpiry > 0) {
|
|
10679
10872
|
console.warn("[AuthContext] Token expiring soon, attempting refresh...");
|
|
10873
|
+
logAuthEvent("token_refresh_started", { reason: "expiring_soon", minutesUntilExpiry });
|
|
10680
10874
|
const refreshResult = await refreshSessionSingleFlight(supabase);
|
|
10681
10875
|
if (isSessionValid(refreshResult.session, 0)) {
|
|
10682
10876
|
setTrackedSession(refreshResult.session);
|
|
10877
|
+
logAuthEvent("token_refresh_succeeded", { expiresAt: refreshResult.session?.expires_at });
|
|
10683
10878
|
console.log("[AuthContext] Token refreshed successfully");
|
|
10684
10879
|
return;
|
|
10685
10880
|
}
|
|
10686
10881
|
if (refreshResult.invalidRefreshToken) {
|
|
10882
|
+
logAuthEvent("token_refresh_failed", { reason: "invalid_refresh_token", trigger: "proactive" });
|
|
10687
10883
|
clearAuthSnapshot();
|
|
10688
10884
|
resetRecoveryState();
|
|
10689
10885
|
console.error("[AuthContext] Refresh token invalid during proactive refresh");
|
|
@@ -10692,13 +10888,16 @@ var AuthProvider = ({ children }) => {
|
|
|
10692
10888
|
}
|
|
10693
10889
|
if (timeUntilExpiry <= 0) {
|
|
10694
10890
|
console.warn("[AuthContext] Token has expired, attempting refresh...");
|
|
10891
|
+
logAuthEvent("token_refresh_started", { reason: "expired", minutesUntilExpiry });
|
|
10695
10892
|
const refreshResult = await refreshSessionSingleFlight(supabase);
|
|
10696
10893
|
if (isSessionValid(refreshResult.session, 0)) {
|
|
10697
10894
|
setTrackedSession(refreshResult.session);
|
|
10895
|
+
logAuthEvent("token_refresh_succeeded", { expiresAt: refreshResult.session?.expires_at });
|
|
10698
10896
|
console.log("[AuthContext] Token refreshed after expiry");
|
|
10699
10897
|
return;
|
|
10700
10898
|
}
|
|
10701
10899
|
if (refreshResult.invalidRefreshToken) {
|
|
10900
|
+
logAuthEvent("token_refresh_failed", { reason: "invalid_refresh_token", trigger: "expired" });
|
|
10702
10901
|
clearAuthSnapshot();
|
|
10703
10902
|
resetRecoveryState();
|
|
10704
10903
|
console.error("[AuthContext] Refresh token invalid after expiry");
|
|
@@ -10750,6 +10949,29 @@ var AuthProvider = ({ children }) => {
|
|
|
10750
10949
|
window.addEventListener("rbac:refresh-scope", handleScopeRefresh);
|
|
10751
10950
|
return () => window.removeEventListener("rbac:refresh-scope", handleScopeRefresh);
|
|
10752
10951
|
}, [fetchSession, session]);
|
|
10952
|
+
React142.useEffect(() => {
|
|
10953
|
+
if (typeof window === "undefined") return;
|
|
10954
|
+
const handleStorageChange = (e) => {
|
|
10955
|
+
if (!e.key?.startsWith("sb-") || !e.key?.endsWith("-auth-token")) return;
|
|
10956
|
+
if (!e.newValue || !session) return;
|
|
10957
|
+
try {
|
|
10958
|
+
const stored = JSON.parse(e.newValue);
|
|
10959
|
+
if (stored?.access_token && stored?.refresh_token && stored?.expires_at && stored.access_token !== session.access_token) {
|
|
10960
|
+
console.log("[AuthContext] Cross-tab token update detected, syncing session");
|
|
10961
|
+
logAuthEvent("cross_tab_refresh_adopted", { source: "storage_event" });
|
|
10962
|
+
setTrackedSession(stored);
|
|
10963
|
+
supabase.auth.setSession({
|
|
10964
|
+
access_token: stored.access_token,
|
|
10965
|
+
refresh_token: stored.refresh_token
|
|
10966
|
+
}).catch(() => {
|
|
10967
|
+
});
|
|
10968
|
+
}
|
|
10969
|
+
} catch {
|
|
10970
|
+
}
|
|
10971
|
+
};
|
|
10972
|
+
window.addEventListener("storage", handleStorageChange);
|
|
10973
|
+
return () => window.removeEventListener("storage", handleStorageChange);
|
|
10974
|
+
}, [session, setTrackedSession, supabase]);
|
|
10753
10975
|
React142.useEffect(() => {
|
|
10754
10976
|
if (typeof window === "undefined" || authStatus !== "recovering") {
|
|
10755
10977
|
return;
|
|
@@ -10841,6 +11063,7 @@ var AuthProvider = ({ children }) => {
|
|
|
10841
11063
|
}
|
|
10842
11064
|
if (event === "SIGNED_OUT") {
|
|
10843
11065
|
console.log("[AuthContext] User signed out");
|
|
11066
|
+
logAuthEvent("signed_out", { trigger: "auth_state_change" });
|
|
10844
11067
|
resetRecoveryState();
|
|
10845
11068
|
clearAuthSnapshot();
|
|
10846
11069
|
clearSessionState();
|
|
@@ -11117,7 +11340,7 @@ function useSessionTracking(options = {}) {
|
|
|
11117
11340
|
const trackerRef = React142.useRef(null);
|
|
11118
11341
|
const isTrackingRef = React142.useRef(false);
|
|
11119
11342
|
const initRef = React142.useRef(false);
|
|
11120
|
-
const
|
|
11343
|
+
const getAccessToken2 = React142.useCallback(async () => {
|
|
11121
11344
|
return session?.access_token || null;
|
|
11122
11345
|
}, [session?.access_token]);
|
|
11123
11346
|
React142.useEffect(() => {
|
|
@@ -11125,14 +11348,14 @@ function useSessionTracking(options = {}) {
|
|
|
11125
11348
|
return;
|
|
11126
11349
|
}
|
|
11127
11350
|
initRef.current = true;
|
|
11128
|
-
const
|
|
11129
|
-
if (!
|
|
11351
|
+
const apiBaseUrl2 = config?.apiBaseUrl || process.env.NEXT_PUBLIC_API_BASE_URL || "";
|
|
11352
|
+
if (!apiBaseUrl2) {
|
|
11130
11353
|
console.warn("[useSessionTracking] No API base URL configured, session tracking disabled");
|
|
11131
11354
|
return;
|
|
11132
11355
|
}
|
|
11133
11356
|
const tracker = createSessionTracker({
|
|
11134
|
-
apiBaseUrl,
|
|
11135
|
-
getAccessToken,
|
|
11357
|
+
apiBaseUrl: apiBaseUrl2,
|
|
11358
|
+
getAccessToken: getAccessToken2,
|
|
11136
11359
|
onSessionStart: (sessionId) => {
|
|
11137
11360
|
isTrackingRef.current = true;
|
|
11138
11361
|
onSessionStart?.(sessionId);
|
|
@@ -11153,7 +11376,7 @@ function useSessionTracking(options = {}) {
|
|
|
11153
11376
|
initRef.current = false;
|
|
11154
11377
|
isTrackingRef.current = false;
|
|
11155
11378
|
};
|
|
11156
|
-
}, [enabled, isAuthenticated, user?.id, session?.access_token, config?.apiBaseUrl,
|
|
11379
|
+
}, [enabled, isAuthenticated, user?.id, session?.access_token, config?.apiBaseUrl, getAccessToken2, onSessionStart, onSessionEnd, user]);
|
|
11157
11380
|
React142.useEffect(() => {
|
|
11158
11381
|
if (!isAuthenticated && trackerRef.current && isTrackingRef.current) {
|
|
11159
11382
|
trackerRef.current.endSession("logout");
|
|
@@ -19734,9 +19957,9 @@ function useUserUsage(userId, options = {}) {
|
|
|
19734
19957
|
const [data, setData] = React142.useState(null);
|
|
19735
19958
|
const [isLoading, setIsLoading] = React142.useState(true);
|
|
19736
19959
|
const [error, setError] = React142.useState(null);
|
|
19737
|
-
const
|
|
19960
|
+
const apiBaseUrl2 = config?.apiBaseUrl || process.env.NEXT_PUBLIC_API_BASE_URL || "";
|
|
19738
19961
|
const fetchData = React142.useCallback(async () => {
|
|
19739
|
-
if (!userId || !
|
|
19962
|
+
if (!userId || !apiBaseUrl2 || !session?.access_token) {
|
|
19740
19963
|
setIsLoading(false);
|
|
19741
19964
|
return;
|
|
19742
19965
|
}
|
|
@@ -19746,7 +19969,7 @@ function useUserUsage(userId, options = {}) {
|
|
|
19746
19969
|
const params = new URLSearchParams();
|
|
19747
19970
|
if (startDate) params.append("start_date", startDate);
|
|
19748
19971
|
if (endDate) params.append("end_date", endDate);
|
|
19749
|
-
const url = `${
|
|
19972
|
+
const url = `${apiBaseUrl2}/api/usage/user/${userId}${params.toString() ? `?${params}` : ""}`;
|
|
19750
19973
|
const response = await fetch(url, {
|
|
19751
19974
|
headers: {
|
|
19752
19975
|
"Authorization": `Bearer ${session.access_token}`
|
|
@@ -19764,7 +19987,7 @@ function useUserUsage(userId, options = {}) {
|
|
|
19764
19987
|
} finally {
|
|
19765
19988
|
setIsLoading(false);
|
|
19766
19989
|
}
|
|
19767
|
-
}, [userId,
|
|
19990
|
+
}, [userId, apiBaseUrl2, session?.access_token, startDate, endDate]);
|
|
19768
19991
|
React142.useEffect(() => {
|
|
19769
19992
|
if (enabled) {
|
|
19770
19993
|
fetchData();
|
|
@@ -19786,12 +20009,12 @@ function useCompanyUsersUsage(companyId, options = {}) {
|
|
|
19786
20009
|
const [isLoading, setIsLoading] = React142.useState(true);
|
|
19787
20010
|
const [isTodayLoading, setIsTodayLoading] = React142.useState(true);
|
|
19788
20011
|
const [error, setError] = React142.useState(null);
|
|
19789
|
-
const
|
|
20012
|
+
const apiBaseUrl2 = config?.apiBaseUrl || process.env.NEXT_PUBLIC_BACKEND_URL || "";
|
|
19790
20013
|
const userRole = user?.role || user?.role_level;
|
|
19791
20014
|
const canAccess = userRole === "owner" || userRole === "optifye";
|
|
19792
|
-
const isEnabled = enabled && canAccess && !!
|
|
20015
|
+
const isEnabled = enabled && canAccess && !!apiBaseUrl2;
|
|
19793
20016
|
const fetchOwnerReport = React142.useCallback(async () => {
|
|
19794
|
-
if (!
|
|
20017
|
+
if (!apiBaseUrl2 || !session?.access_token || !canAccess || !isEnabled) {
|
|
19795
20018
|
setIsLoading(false);
|
|
19796
20019
|
return;
|
|
19797
20020
|
}
|
|
@@ -19806,7 +20029,7 @@ function useCompanyUsersUsage(companyId, options = {}) {
|
|
|
19806
20029
|
if (startDate) params.append("start_date", startDate);
|
|
19807
20030
|
if (endDate) params.append("end_date", endDate);
|
|
19808
20031
|
if (roleFilter) params.append("role_filter", roleFilter);
|
|
19809
|
-
const url = `${
|
|
20032
|
+
const url = `${apiBaseUrl2}/api/usage/owner-report${params.toString() ? `?${params}` : ""}`;
|
|
19810
20033
|
const response = await fetch(url, {
|
|
19811
20034
|
headers: {
|
|
19812
20035
|
"Authorization": `Bearer ${session.access_token}`
|
|
@@ -19824,15 +20047,15 @@ function useCompanyUsersUsage(companyId, options = {}) {
|
|
|
19824
20047
|
} finally {
|
|
19825
20048
|
setIsLoading(false);
|
|
19826
20049
|
}
|
|
19827
|
-
}, [
|
|
20050
|
+
}, [apiBaseUrl2, session?.access_token, canAccess, isEnabled, startDate, endDate, roleFilter]);
|
|
19828
20051
|
const fetchTodayUsage = React142.useCallback(async () => {
|
|
19829
|
-
if (!
|
|
20052
|
+
if (!apiBaseUrl2 || !session?.access_token || !canAccess || !isEnabled) {
|
|
19830
20053
|
setIsTodayLoading(false);
|
|
19831
20054
|
return;
|
|
19832
20055
|
}
|
|
19833
20056
|
setIsTodayLoading(true);
|
|
19834
20057
|
try {
|
|
19835
|
-
const url = `${
|
|
20058
|
+
const url = `${apiBaseUrl2}/api/usage/today`;
|
|
19836
20059
|
const response = await fetch(url, {
|
|
19837
20060
|
headers: {
|
|
19838
20061
|
"Authorization": `Bearer ${session.access_token}`
|
|
@@ -19848,7 +20071,7 @@ function useCompanyUsersUsage(companyId, options = {}) {
|
|
|
19848
20071
|
} finally {
|
|
19849
20072
|
setIsTodayLoading(false);
|
|
19850
20073
|
}
|
|
19851
|
-
}, [
|
|
20074
|
+
}, [apiBaseUrl2, session?.access_token, canAccess, isEnabled]);
|
|
19852
20075
|
const fetchAll = React142.useCallback(async () => {
|
|
19853
20076
|
await Promise.all([
|
|
19854
20077
|
fetchOwnerReport(),
|
|
@@ -19918,9 +20141,9 @@ function useCompanyClipsCost() {
|
|
|
19918
20141
|
const hasFetchedOnceRef = React142.useRef(false);
|
|
19919
20142
|
const canViewClipsCost = user?.role_level === "owner" || user?.role_level === "optifye";
|
|
19920
20143
|
const companyId = user?.properties?.company_id || user?.company_id || entityConfig.companyId;
|
|
19921
|
-
const
|
|
20144
|
+
const apiBaseUrl2 = config?.apiBaseUrl || process.env.NEXT_PUBLIC_API_BASE_URL || "";
|
|
19922
20145
|
const fetchData = React142.useCallback(async () => {
|
|
19923
|
-
if (!canViewClipsCost || !companyId || !supabase || !
|
|
20146
|
+
if (!canViewClipsCost || !companyId || !supabase || !apiBaseUrl2 || !session?.access_token) {
|
|
19924
20147
|
setIsLoading(false);
|
|
19925
20148
|
setData(null);
|
|
19926
20149
|
hasFetchedOnceRef.current = false;
|
|
@@ -19932,7 +20155,7 @@ function useCompanyClipsCost() {
|
|
|
19932
20155
|
setError(null);
|
|
19933
20156
|
try {
|
|
19934
20157
|
const [statsResponse, linesResult] = await Promise.all([
|
|
19935
|
-
fetch(`${
|
|
20158
|
+
fetch(`${apiBaseUrl2}/api/classification/company-stats?company_id=${encodeURIComponent(companyId)}`, {
|
|
19936
20159
|
headers: {
|
|
19937
20160
|
"Authorization": `Bearer ${session.access_token}`
|
|
19938
20161
|
}
|
|
@@ -19967,7 +20190,7 @@ function useCompanyClipsCost() {
|
|
|
19967
20190
|
hasFetchedOnceRef.current = true;
|
|
19968
20191
|
setIsLoading(false);
|
|
19969
20192
|
}
|
|
19970
|
-
}, [canViewClipsCost, companyId, supabase,
|
|
20193
|
+
}, [canViewClipsCost, companyId, supabase, apiBaseUrl2, session?.access_token]);
|
|
19971
20194
|
React142.useEffect(() => {
|
|
19972
20195
|
fetchData();
|
|
19973
20196
|
}, [fetchData]);
|
|
@@ -23503,11 +23726,11 @@ function createRenderStep(runNextFrame) {
|
|
|
23503
23726
|
*/
|
|
23504
23727
|
schedule: (callback, keepAlive = false, immediate = false) => {
|
|
23505
23728
|
const addToCurrentFrame = immediate && isProcessing;
|
|
23506
|
-
const
|
|
23729
|
+
const queue2 = addToCurrentFrame ? thisFrame : nextFrame;
|
|
23507
23730
|
if (keepAlive)
|
|
23508
23731
|
toKeepAlive.add(callback);
|
|
23509
|
-
if (!
|
|
23510
|
-
|
|
23732
|
+
if (!queue2.has(callback))
|
|
23733
|
+
queue2.add(callback);
|
|
23511
23734
|
return callback;
|
|
23512
23735
|
},
|
|
23513
23736
|
/**
|
|
@@ -52675,7 +52898,7 @@ var SideNavBar = React142.memo(({
|
|
|
52675
52898
|
transition-all duration-200 ease-out focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2`;
|
|
52676
52899
|
}, [pathname]);
|
|
52677
52900
|
const buildDashboardSurfaceTrackingEvent = React142.useCallback((source, destinationPath, dashboardSurface) => ({
|
|
52678
|
-
name: dashboardSurface
|
|
52901
|
+
name: ROOT_DASHBOARD_EVENT_NAMES[dashboardSurface],
|
|
52679
52902
|
properties: {
|
|
52680
52903
|
source,
|
|
52681
52904
|
destination_path: destinationPath,
|
|
@@ -53956,7 +54179,7 @@ var AwardNotificationManager = () => {
|
|
|
53956
54179
|
const supabase = useSupabase();
|
|
53957
54180
|
const { user } = useAuth();
|
|
53958
54181
|
const router$1 = router.useRouter();
|
|
53959
|
-
const [
|
|
54182
|
+
const [queue2, setQueue] = React142.useState([]);
|
|
53960
54183
|
const [activeNotification, setActiveNotification] = React142.useState(null);
|
|
53961
54184
|
const lastUserIdRef = React142.useRef(null);
|
|
53962
54185
|
React142.useEffect(() => {
|
|
@@ -53977,10 +54200,10 @@ var AwardNotificationManager = () => {
|
|
|
53977
54200
|
loadNotifications();
|
|
53978
54201
|
}, [user, supabase]);
|
|
53979
54202
|
React142.useEffect(() => {
|
|
53980
|
-
if (!activeNotification &&
|
|
53981
|
-
setActiveNotification(
|
|
54203
|
+
if (!activeNotification && queue2.length > 0) {
|
|
54204
|
+
setActiveNotification(queue2[0]);
|
|
53982
54205
|
}
|
|
53983
|
-
}, [
|
|
54206
|
+
}, [queue2, activeNotification]);
|
|
53984
54207
|
const dismissActive = async () => {
|
|
53985
54208
|
if (!activeNotification) return;
|
|
53986
54209
|
if (!activeNotification.id.startsWith("mock-")) {
|
|
@@ -79730,6 +79953,7 @@ exports.PrefetchEvents = PrefetchEvents;
|
|
|
79730
79953
|
exports.PrefetchStatus = PrefetchStatus;
|
|
79731
79954
|
exports.PrefetchTimeoutError = PrefetchTimeoutError;
|
|
79732
79955
|
exports.ProfileView = ProfileView_default;
|
|
79956
|
+
exports.ROOT_DASHBOARD_EVENT_NAMES = ROOT_DASHBOARD_EVENT_NAMES;
|
|
79733
79957
|
exports.RegistryProvider = RegistryProvider;
|
|
79734
79958
|
exports.RoleBadge = RoleBadge;
|
|
79735
79959
|
exports.S3ClipsService = S3ClipsSupabaseService;
|