fix(geoloc): server switch + proper auth throttling

This commit is contained in:
devthejo 2025-05-04 15:09:24 +02:00
parent 2f3db5adc0
commit af477ce835

View file

@ -5,6 +5,8 @@ import { BACKGROUND_SCOPES } from "~/lib/logger/scopes";
import jwtDecode from "jwt-decode"; import jwtDecode from "jwt-decode";
import { initEmulatorMode } from "./emulatorService"; import { initEmulatorMode } from "./emulatorService";
import throttle from "lodash.throttle";
import { import {
getAuthState, getAuthState,
subscribeAuthState, subscribeAuthState,
@ -65,31 +67,17 @@ export default async function trackLocation() {
feature: "tracking", feature: "tracking",
}); });
// Track the last time we handled auth changes to prevent rapid successive calls // Log the geolocation sync URL for debugging
let lastAuthHandleTime = 0; locationLogger.info("Geolocation sync URL configuration", {
const AUTH_HANDLE_COOLDOWN = 3000; // 3 seconds cooldown url: env.GEOLOC_SYNC_URL,
isStaging: env.IS_STAGING,
});
// Track the last time we triggered an auth reload to prevent rapid successive calls // Throttling configuration for auth reload only
let lastAuthReloadTime = 0; const AUTH_RELOAD_THROTTLE = 5000; // 5 seconds throttle
const AUTH_RELOAD_COOLDOWN = 5000; // 5 seconds cooldown
// Handle auth function - no throttling or cooldown
async function handleAuth(userToken) { async function handleAuth(userToken) {
// Implement debouncing for auth state changes
const now = Date.now();
const timeSinceLastHandle = now - lastAuthHandleTime;
if (timeSinceLastHandle < AUTH_HANDLE_COOLDOWN) {
locationLogger.info(
"Auth state change handled too recently, debouncing",
{
timeSinceLastHandle,
cooldown: AUTH_HANDLE_COOLDOWN,
},
);
return;
}
lastAuthHandleTime = now;
locationLogger.info("Handling auth token update", { locationLogger.info("Handling auth token update", {
hasToken: !!userToken, hasToken: !!userToken,
}); });
@ -102,10 +90,40 @@ export default async function trackLocation() {
// unsub(); // unsub();
locationLogger.debug("Updating background geolocation config"); locationLogger.debug("Updating background geolocation config");
await BackgroundGeolocation.setConfig({ await BackgroundGeolocation.setConfig({
url: env.GEOLOC_SYNC_URL, // Update the sync URL for when it's changed for staging
headers: { headers: {
Authorization: `Bearer ${userToken}`, Authorization: `Bearer ${userToken}`,
}, },
}); });
// Log the authorization header that was set
locationLogger.debug(
"Set Authorization header for background geolocation",
{
headerSet: true,
tokenPrefix: userToken ? userToken.substring(0, 10) + "..." : null,
},
);
// Verify the current configuration
try {
const currentConfig = await BackgroundGeolocation.getConfig();
locationLogger.debug("Current background geolocation config", {
hasHeaders: !!currentConfig.headers,
headerKeys: currentConfig.headers
? Object.keys(currentConfig.headers)
: [],
authHeader: currentConfig.headers?.Authorization
? currentConfig.headers.Authorization.substring(0, 15) + "..."
: "Not set",
url: currentConfig.url,
});
} catch (error) {
locationLogger.error("Failed to get background geolocation config", {
error: error.message,
});
}
const state = await BackgroundGeolocation.getState(); const state = await BackgroundGeolocation.getState();
try { try {
const decodedToken = jwtDecode(userToken); const decodedToken = jwtDecode(userToken);
@ -160,7 +178,20 @@ export default async function trackLocation() {
} }
}); });
// The core auth reload function that will be throttled
function _reloadAuth() {
locationLogger.info("Refreshing authentication token");
authActions.reload(); // should retriger sync in handleAuth via subscribeAuthState when done
}
// Create throttled version of auth reload with lodash
const reloadAuth = throttle(_reloadAuth, AUTH_RELOAD_THROTTLE, {
leading: true,
trailing: true,
});
BackgroundGeolocation.onHttp((response) => { BackgroundGeolocation.onHttp((response) => {
// Log the full response including headers if available
locationLogger.debug("HTTP response received", { locationLogger.debug("HTTP response received", {
status: response?.status, status: response?.status,
success: response?.success, success: response?.success,
@ -168,11 +199,18 @@ export default async function trackLocation() {
url: response?.url, url: response?.url,
method: response?.method, method: response?.method,
isSync: response?.isSync, isSync: response?.isSync,
requestHeaders:
response?.request?.headers || "Headers not available in response",
});
// Log the current auth token for comparison
const { userToken } = getAuthState();
locationLogger.debug("Current auth state token", {
tokenAvailable: !!userToken,
tokenPrefix: userToken ? userToken.substring(0, 10) + "..." : null,
}); });
const statusCode = response?.status; const statusCode = response?.status;
const now = Date.now();
const timeSinceLastReload = now - lastAuthReloadTime;
switch (statusCode) { switch (statusCode) {
case 410: case 410:
@ -181,18 +219,28 @@ export default async function trackLocation() {
authActions.logout(); authActions.logout();
break; break;
case 401: case 401:
// Unauthorized, check cooldown before triggering reload // Unauthorized, use throttled reload
if (timeSinceLastReload < AUTH_RELOAD_COOLDOWN) { locationLogger.info("Unauthorized (401), attempting to refresh token");
locationLogger.info("Auth reload requested too soon, skipping", {
timeSinceLastReload, // Add more detailed logging of the error response
cooldown: AUTH_RELOAD_COOLDOWN, try {
const errorBody = response?.responseText
? JSON.parse(response.responseText)
: null;
locationLogger.debug("Unauthorized error details", {
errorBody,
errorType: errorBody?.error?.type,
errorMessage: errorBody?.error?.message,
errorPath: errorBody?.error?.errors?.[0]?.path,
});
} catch (e) {
locationLogger.debug("Failed to parse error response", {
error: e.message,
responseText: response?.responseText,
}); });
return;
} }
locationLogger.info("Refreshing authentication token"); reloadAuth();
lastAuthReloadTime = now;
authActions.reload(); // should retriger sync in handleAuth via subscribeAuthState when done
break; break;
} }
}); });
@ -246,10 +294,14 @@ export default async function trackLocation() {
if (count > 0) { if (count > 0) {
locationLogger.info(`Found ${count} pending records, forcing sync`); locationLogger.info(`Found ${count} pending records, forcing sync`);
try { try {
const records = await BackgroundGeolocation.sync(); const { userToken } = getAuthState();
locationLogger.debug("Forced sync result", { const state = await BackgroundGeolocation.getState();
recordsCount: records?.length || 0, if (userToken && state.enabled) {
}); const records = await BackgroundGeolocation.sync();
locationLogger.debug("Forced sync result", {
recordsCount: records?.length || 0,
});
}
} catch (error) { } catch (error) {
locationLogger.error("Forced sync failed", { locationLogger.error("Forced sync failed", {
error: error, error: error,