fix(geoloc): server switch + proper auth throttling
This commit is contained in:
parent
2f3db5adc0
commit
af477ce835
1 changed files with 89 additions and 37 deletions
|
@ -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,
|
||||||
|
|
Loading…
Add table
Reference in a new issue