Revert "chore: try to use sync to refresh"
This reverts commit e47a33bcd8
.
This commit is contained in:
parent
e47a33bcd8
commit
0001a50a5f
6 changed files with 268 additions and 377 deletions
85
index.js
85
index.js
|
@ -20,7 +20,6 @@ import onMessageReceived from "~/notifications/onMessageReceived";
|
||||||
import { createLogger } from "~/lib/logger";
|
import { createLogger } from "~/lib/logger";
|
||||||
import * as Sentry from "@sentry/react-native";
|
import * as Sentry from "@sentry/react-native";
|
||||||
import AsyncStorage from "@react-native-async-storage/async-storage";
|
import AsyncStorage from "@react-native-async-storage/async-storage";
|
||||||
import { handleHttpResponse } from "~/lib/geolocation/httpResponseHandler";
|
|
||||||
|
|
||||||
// setup notification, this have to stay in index.js
|
// setup notification, this have to stay in index.js
|
||||||
notifee.onBackgroundEvent(notificationBackgroundEvent);
|
notifee.onBackgroundEvent(notificationBackgroundEvent);
|
||||||
|
@ -34,8 +33,7 @@ registerRootComponent(App);
|
||||||
// Constants for persistence
|
// Constants for persistence
|
||||||
const LAST_SYNC_TIME_KEY = "@geolocation_last_sync_time";
|
const LAST_SYNC_TIME_KEY = "@geolocation_last_sync_time";
|
||||||
// const FORCE_SYNC_INTERVAL = 24 * 60 * 60 * 1000;
|
// const FORCE_SYNC_INTERVAL = 24 * 60 * 60 * 1000;
|
||||||
// const FORCE_SYNC_INTERVAL = 60 * 60 * 1000; // DEBUGGING
|
const FORCE_SYNC_INTERVAL = 60 * 60 * 1000; // DEBUGGING
|
||||||
const FORCE_SYNC_INTERVAL = 5 * 60 * 1000; // DEBUGGING
|
|
||||||
|
|
||||||
// Helper functions for persisting sync time
|
// Helper functions for persisting sync time
|
||||||
const getLastSyncTime = async () => {
|
const getLastSyncTime = async () => {
|
||||||
|
@ -331,47 +329,54 @@ const HeadlessTask = async (event) => {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "http":
|
case "http":
|
||||||
try {
|
// Validate HTTP parameters
|
||||||
// Use shared HTTP response handler for headless context
|
if (!params || typeof params !== "object" || !params.response) {
|
||||||
await handleHttpResponse(params, "headless");
|
geolocBgLogger.warn("Invalid HTTP params", { params });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// Update last sync time on successful HTTP response (keep existing logic)
|
const httpStatus = params.response?.status;
|
||||||
const httpStatus = params.status;
|
const isHttpSuccess = httpStatus === 200;
|
||||||
if (httpStatus === 200) {
|
|
||||||
try {
|
|
||||||
const now = Date.now();
|
|
||||||
await setLastSyncTime(now);
|
|
||||||
|
|
||||||
Sentry.addBreadcrumb({
|
Sentry.addBreadcrumb({
|
||||||
message: "Last sync time updated (HTTP success)",
|
message: "HTTP response received",
|
||||||
category: "headless-task",
|
category: "headless-task",
|
||||||
level: "info",
|
level: isHttpSuccess ? "info" : "warning",
|
||||||
data: { newSyncTime: new Date(now).toISOString() },
|
data: {
|
||||||
});
|
status: httpStatus,
|
||||||
} catch (syncTimeError) {
|
success: params.response?.success,
|
||||||
geolocBgLogger.error("Failed to update sync time", {
|
hasResponse: !!params.response,
|
||||||
error: syncTimeError,
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
Sentry.captureException(syncTimeError, {
|
geolocBgLogger.debug("HTTP response received", {
|
||||||
tags: {
|
response: params.response,
|
||||||
module: "headless-task",
|
});
|
||||||
operation: "update-sync-time-http",
|
|
||||||
},
|
// Update last sync time on successful HTTP response
|
||||||
});
|
if (isHttpSuccess) {
|
||||||
}
|
try {
|
||||||
|
const now = Date.now();
|
||||||
|
await setLastSyncTime(now);
|
||||||
|
|
||||||
|
Sentry.addBreadcrumb({
|
||||||
|
message: "Last sync time updated (HTTP success)",
|
||||||
|
category: "headless-task",
|
||||||
|
level: "info",
|
||||||
|
data: { newSyncTime: new Date(now).toISOString() },
|
||||||
|
});
|
||||||
|
} catch (syncTimeError) {
|
||||||
|
geolocBgLogger.error("Failed to update sync time", {
|
||||||
|
error: syncTimeError,
|
||||||
|
});
|
||||||
|
|
||||||
|
Sentry.captureException(syncTimeError, {
|
||||||
|
tags: {
|
||||||
|
module: "headless-task",
|
||||||
|
operation: "update-sync-time-http",
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} catch (httpHandlerError) {
|
|
||||||
geolocBgLogger.error("HTTP response handler failed", {
|
|
||||||
error: httpHandlerError,
|
|
||||||
});
|
|
||||||
|
|
||||||
Sentry.captureException(httpHandlerError, {
|
|
||||||
tags: {
|
|
||||||
module: "headless-task",
|
|
||||||
operation: "http-response-handler",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
|
@ -1,225 +0,0 @@
|
||||||
import { createLogger } from "~/lib/logger";
|
|
||||||
import { BACKGROUND_SCOPES } from "~/lib/logger/scopes";
|
|
||||||
import * as Sentry from "@sentry/react-native";
|
|
||||||
import BackgroundGeolocation from "react-native-background-geolocation";
|
|
||||||
import throttle from "lodash.throttle";
|
|
||||||
|
|
||||||
import { authActions, getAuthState } from "~/stores";
|
|
||||||
import { setLastSyncTime } from "~/lib/geolocation/syncTimeManager";
|
|
||||||
|
|
||||||
// Create logger for HTTP response handling
|
|
||||||
const httpLogger = createLogger({
|
|
||||||
module: BACKGROUND_SCOPES.GEOLOCATION,
|
|
||||||
feature: "http-handler",
|
|
||||||
});
|
|
||||||
|
|
||||||
// Throttling configuration for auth reload
|
|
||||||
const AUTH_RELOAD_THROTTLE = 5000; // 5 seconds throttle
|
|
||||||
|
|
||||||
// The core auth reload function that will be throttled
|
|
||||||
async function _reloadAuth() {
|
|
||||||
httpLogger.info("Refreshing authentication token via sync endpoint");
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Get current auth state to check if we have an auth token
|
|
||||||
const { authToken, userToken } = getAuthState();
|
|
||||||
|
|
||||||
if (!authToken) {
|
|
||||||
httpLogger.warn("No auth token available for refresh");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
httpLogger.debug(
|
|
||||||
"Auth token available, updating BackgroundGeolocation config",
|
|
||||||
);
|
|
||||||
|
|
||||||
// Update BackgroundGeolocation config to include X-Auth-Token header
|
|
||||||
await BackgroundGeolocation.setConfig({
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${userToken}`, // Keep existing user token (may be expired)
|
|
||||||
"X-Auth-Token": authToken, // Add auth token for refresh
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// Trigger sync to refresh token
|
|
||||||
await BackgroundGeolocation.changePace(true);
|
|
||||||
await BackgroundGeolocation.sync();
|
|
||||||
|
|
||||||
httpLogger.info("Token refresh sync triggered successfully");
|
|
||||||
} catch (error) {
|
|
||||||
httpLogger.error("Failed to refresh authentication token", {
|
|
||||||
error: error.message,
|
|
||||||
stack: error.stack,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create throttled version of auth reload with lodash
|
|
||||||
const reloadAuth = throttle(_reloadAuth, AUTH_RELOAD_THROTTLE, {
|
|
||||||
leading: true,
|
|
||||||
trailing: false, // Prevent trailing calls to avoid duplicate refreshes
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Shared HTTP response handler for both foreground and headless contexts
|
|
||||||
* @param {Object} response - The HTTP response object from BackgroundGeolocation
|
|
||||||
* @param {string} context - Either 'foreground' or 'headless'
|
|
||||||
*/
|
|
||||||
export const handleHttpResponse = async (response, context = "foreground") => {
|
|
||||||
// Log the full response including headers if available
|
|
||||||
httpLogger.debug("HTTP response received", {
|
|
||||||
status: response?.status,
|
|
||||||
success: response?.success,
|
|
||||||
responseText: response?.responseText,
|
|
||||||
url: response?.url,
|
|
||||||
method: response?.method,
|
|
||||||
isSync: response?.isSync,
|
|
||||||
context,
|
|
||||||
requestHeaders:
|
|
||||||
response?.request?.headers || "Headers not available in response",
|
|
||||||
});
|
|
||||||
|
|
||||||
// Add Sentry breadcrumb for HTTP responses
|
|
||||||
Sentry.addBreadcrumb({
|
|
||||||
message: `Background geolocation HTTP response (${context})`,
|
|
||||||
category: "geolocation-http",
|
|
||||||
level: response?.status === 200 ? "info" : "warning",
|
|
||||||
data: {
|
|
||||||
status: response?.status,
|
|
||||||
success: response?.success,
|
|
||||||
url: response?.url,
|
|
||||||
isSync: response?.isSync,
|
|
||||||
recordCount: response?.count,
|
|
||||||
context,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const statusCode = response?.status;
|
|
||||||
|
|
||||||
// Log status code and response
|
|
||||||
httpLogger.debug("Processing HTTP response", {
|
|
||||||
status: statusCode,
|
|
||||||
responseText: response?.responseText,
|
|
||||||
context,
|
|
||||||
});
|
|
||||||
|
|
||||||
switch (statusCode) {
|
|
||||||
case 200:
|
|
||||||
await handleSuccessResponse(response, context);
|
|
||||||
break;
|
|
||||||
case 410:
|
|
||||||
await handleAuthTokenNotFound(response, context);
|
|
||||||
break;
|
|
||||||
case 401:
|
|
||||||
await handleUnauthorized(response, context);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
httpLogger.debug("Unhandled HTTP status code", {
|
|
||||||
status: statusCode,
|
|
||||||
context,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle successful HTTP response (status 200)
|
|
||||||
*/
|
|
||||||
const handleSuccessResponse = async (response, context) => {
|
|
||||||
try {
|
|
||||||
const responseBody = response?.responseText
|
|
||||||
? JSON.parse(response.responseText)
|
|
||||||
: null;
|
|
||||||
|
|
||||||
if (responseBody?.userBearerJwt) {
|
|
||||||
httpLogger.info("Token refresh successful, updating stored token", {
|
|
||||||
context,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Use auth action to update both in-memory and persistent storage
|
|
||||||
await authActions.setUserToken(responseBody.userBearerJwt);
|
|
||||||
|
|
||||||
// Update BackgroundGeolocation config with new token and remove X-Auth-Token header
|
|
||||||
await BackgroundGeolocation.setConfig({
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${responseBody.userBearerJwt}`,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
httpLogger.debug(
|
|
||||||
"Updated BackgroundGeolocation with refreshed token and removed X-Auth-Token header",
|
|
||||||
{ context },
|
|
||||||
);
|
|
||||||
|
|
||||||
Sentry.addBreadcrumb({
|
|
||||||
message: "Token refreshed successfully",
|
|
||||||
category: "geolocation-auth",
|
|
||||||
level: "info",
|
|
||||||
data: { context },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
httpLogger.debug("Failed to parse successful response", {
|
|
||||||
error: e.message,
|
|
||||||
responseText: response?.responseText,
|
|
||||||
context,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle auth token expired (status 410)
|
|
||||||
*/
|
|
||||||
const handleAuthTokenNotFound = async (response, context) => {
|
|
||||||
httpLogger.info("Auth token not found (410), logging out", { context });
|
|
||||||
|
|
||||||
Sentry.addBreadcrumb({
|
|
||||||
message: "Auth token expired - logging out",
|
|
||||||
category: "geolocation-auth",
|
|
||||||
level: "warning",
|
|
||||||
data: { context },
|
|
||||||
});
|
|
||||||
|
|
||||||
authActions.logout();
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle unauthorized request (status 401)
|
|
||||||
*/
|
|
||||||
const handleUnauthorized = async (response, context) => {
|
|
||||||
httpLogger.info("Unauthorized (401), attempting to refresh token", {
|
|
||||||
context,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Add more detailed logging of the error response
|
|
||||||
try {
|
|
||||||
const errorBody = response?.responseText
|
|
||||||
? JSON.parse(response.responseText)
|
|
||||||
: null;
|
|
||||||
|
|
||||||
httpLogger.debug("Unauthorized error details", {
|
|
||||||
errorBody,
|
|
||||||
errorType: errorBody?.error?.type,
|
|
||||||
errorMessage: errorBody?.error?.message,
|
|
||||||
errorPath: errorBody?.error?.errors?.[0]?.path,
|
|
||||||
context,
|
|
||||||
});
|
|
||||||
|
|
||||||
Sentry.addBreadcrumb({
|
|
||||||
message: "Unauthorized - refreshing token",
|
|
||||||
category: "geolocation-auth",
|
|
||||||
level: "warning",
|
|
||||||
data: {
|
|
||||||
errorType: errorBody?.error?.type,
|
|
||||||
errorMessage: errorBody?.error?.message,
|
|
||||||
context,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
httpLogger.debug("Failed to parse error response", {
|
|
||||||
error: e.message,
|
|
||||||
responseText: response?.responseText,
|
|
||||||
context,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
await reloadAuth();
|
|
||||||
};
|
|
|
@ -1,67 +0,0 @@
|
||||||
import { memoryAsyncStorage } from "~/lib/memoryAsyncStorage";
|
|
||||||
import { createLogger } from "~/lib/logger";
|
|
||||||
import { BACKGROUND_SCOPES } from "~/lib/logger/scopes";
|
|
||||||
import * as Sentry from "@sentry/react-native";
|
|
||||||
|
|
||||||
// Constants for persistence
|
|
||||||
const LAST_SYNC_TIME_KEY = "@geolocation_last_sync_time";
|
|
||||||
|
|
||||||
// Create logger for sync time management
|
|
||||||
const syncTimeLogger = createLogger({
|
|
||||||
module: BACKGROUND_SCOPES.GEOLOCATION,
|
|
||||||
feature: "sync-time",
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the last sync time from storage
|
|
||||||
* @returns {Promise<number>} The last sync time in milliseconds, or current time if not found
|
|
||||||
*/
|
|
||||||
export const getLastSyncTime = async () => {
|
|
||||||
try {
|
|
||||||
const value = await memoryAsyncStorage.getItem(LAST_SYNC_TIME_KEY);
|
|
||||||
const lastSyncTime = value ? parseInt(value, 10) : Date.now();
|
|
||||||
|
|
||||||
syncTimeLogger.debug("Retrieved last sync time", {
|
|
||||||
value,
|
|
||||||
lastSyncTime,
|
|
||||||
isDefault: !value,
|
|
||||||
});
|
|
||||||
|
|
||||||
return lastSyncTime;
|
|
||||||
} catch (error) {
|
|
||||||
syncTimeLogger.error("Failed to get last sync time", {
|
|
||||||
error: error.message,
|
|
||||||
});
|
|
||||||
|
|
||||||
Sentry.captureException(error, {
|
|
||||||
tags: { module: "sync-time-manager", operation: "get-last-sync-time" },
|
|
||||||
});
|
|
||||||
|
|
||||||
// Return current time as fallback
|
|
||||||
return Date.now();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the last sync time in storage
|
|
||||||
* @param {number} time - The sync time in milliseconds
|
|
||||||
*/
|
|
||||||
export const setLastSyncTime = async (time) => {
|
|
||||||
try {
|
|
||||||
await memoryAsyncStorage.setItem(LAST_SYNC_TIME_KEY, time.toString());
|
|
||||||
|
|
||||||
syncTimeLogger.debug("Set last sync time", {
|
|
||||||
time,
|
|
||||||
timeISO: new Date(time).toISOString(),
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
syncTimeLogger.error("Failed to set last sync time", {
|
|
||||||
error: error.message,
|
|
||||||
time,
|
|
||||||
});
|
|
||||||
|
|
||||||
Sentry.captureException(error, {
|
|
||||||
tags: { module: "sync-time-manager", operation: "set-last-sync-time" },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -152,20 +152,19 @@ export const memoryAsyncStorage = {
|
||||||
storageLogger.debug("Set in memory cache", { key });
|
storageLogger.debug("Set in memory cache", { key });
|
||||||
|
|
||||||
// Try to persist to AsyncStorage
|
// Try to persist to AsyncStorage
|
||||||
(async () => {
|
try {
|
||||||
try {
|
await AsyncStorage.setItem(key, value);
|
||||||
await AsyncStorage.setItem(key, value);
|
storageLogger.debug("Persisted to AsyncStorage", { key });
|
||||||
storageLogger.debug("Persisted to AsyncStorage", { key });
|
} catch (error) {
|
||||||
} catch (error) {
|
storageLogger.warn(
|
||||||
storageLogger.warn(
|
"Failed to persist to AsyncStorage, kept in memory only",
|
||||||
"Failed to persist to AsyncStorage, kept in memory only",
|
{
|
||||||
{
|
key,
|
||||||
key,
|
error: error.message,
|
||||||
error: error.message,
|
},
|
||||||
},
|
);
|
||||||
);
|
// Continue - value is at least in memory
|
||||||
}
|
}
|
||||||
})();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -179,18 +178,16 @@ export const memoryAsyncStorage = {
|
||||||
storageLogger.debug("Deleted from memory cache", { key });
|
storageLogger.debug("Deleted from memory cache", { key });
|
||||||
|
|
||||||
// Try to delete from AsyncStorage
|
// Try to delete from AsyncStorage
|
||||||
(async () => {
|
try {
|
||||||
try {
|
await AsyncStorage.removeItem(key);
|
||||||
await AsyncStorage.removeItem(key);
|
storageLogger.debug("Deleted from AsyncStorage", { key });
|
||||||
storageLogger.debug("Deleted from AsyncStorage", { key });
|
} catch (error) {
|
||||||
} catch (error) {
|
storageLogger.warn("Failed to delete from AsyncStorage", {
|
||||||
storageLogger.warn("Failed to delete from AsyncStorage", {
|
key,
|
||||||
key,
|
error: error.message,
|
||||||
error: error.message,
|
});
|
||||||
});
|
// Continue - at least removed from memory
|
||||||
// Continue - at least removed from memory
|
}
|
||||||
}
|
|
||||||
})();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -245,16 +242,14 @@ export const memoryAsyncStorage = {
|
||||||
storageLogger.info("Cleared memory cache");
|
storageLogger.info("Cleared memory cache");
|
||||||
|
|
||||||
// Try to clear AsyncStorage
|
// Try to clear AsyncStorage
|
||||||
(async () => {
|
try {
|
||||||
try {
|
await AsyncStorage.clear();
|
||||||
await AsyncStorage.clear();
|
storageLogger.info("Cleared AsyncStorage");
|
||||||
storageLogger.info("Cleared AsyncStorage");
|
} catch (error) {
|
||||||
} catch (error) {
|
storageLogger.warn("Failed to clear AsyncStorage", {
|
||||||
storageLogger.warn("Failed to clear AsyncStorage", {
|
error: error.message,
|
||||||
error: error.message,
|
});
|
||||||
});
|
}
|
||||||
}
|
|
||||||
})();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -18,7 +18,6 @@ import {
|
||||||
|
|
||||||
import setLocationState from "~/location/setLocationState";
|
import setLocationState from "~/location/setLocationState";
|
||||||
import { storeLocation } from "~/utils/location/storage";
|
import { storeLocation } from "~/utils/location/storage";
|
||||||
import { handleHttpResponse } from "~/lib/geolocation/httpResponseHandler";
|
|
||||||
|
|
||||||
import env from "~/env";
|
import env from "~/env";
|
||||||
|
|
||||||
|
@ -77,6 +76,53 @@ export default async function trackLocation() {
|
||||||
isStaging: env.IS_STAGING,
|
isStaging: env.IS_STAGING,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Throttling configuration for auth reload only
|
||||||
|
const AUTH_RELOAD_THROTTLE = 5000; // 5 seconds throttle
|
||||||
|
|
||||||
|
// The core auth reload function that will be throttled
|
||||||
|
async function _reloadAuth() {
|
||||||
|
locationLogger.info("Refreshing authentication token via sync endpoint");
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Get current auth state to check if we have an auth token
|
||||||
|
const { authToken, userToken } = getAuthState();
|
||||||
|
|
||||||
|
if (!authToken) {
|
||||||
|
locationLogger.warn("No auth token available for refresh");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
locationLogger.debug(
|
||||||
|
"Auth token available, updating BackgroundGeolocation config",
|
||||||
|
);
|
||||||
|
|
||||||
|
// Update BackgroundGeolocation config to include X-Auth-Token header
|
||||||
|
await BackgroundGeolocation.setConfig({
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${userToken}`, // Keep existing user token (may be expired)
|
||||||
|
"X-Auth-Token": authToken, // Add auth token for refresh
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Trigger sync to refresh token
|
||||||
|
await BackgroundGeolocation.changePace(true);
|
||||||
|
await BackgroundGeolocation.sync();
|
||||||
|
|
||||||
|
locationLogger.info("Token refresh sync triggered successfully");
|
||||||
|
} catch (error) {
|
||||||
|
locationLogger.error("Failed to refresh authentication token", {
|
||||||
|
error: error.message,
|
||||||
|
stack: error.stack,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create throttled version of auth reload with lodash
|
||||||
|
const reloadAuth = throttle(_reloadAuth, AUTH_RELOAD_THROTTLE, {
|
||||||
|
leading: true,
|
||||||
|
trailing: false, // Prevent trailing calls to avoid duplicate refreshes
|
||||||
|
});
|
||||||
|
|
||||||
// Handle auth function - no throttling or cooldown
|
// Handle auth function - no throttling or cooldown
|
||||||
async function handleAuth(userToken) {
|
async function handleAuth(userToken) {
|
||||||
locationLogger.info("Handling auth token update", {
|
locationLogger.info("Handling auth token update", {
|
||||||
|
@ -106,6 +152,25 @@ export default async function trackLocation() {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
@ -175,8 +240,132 @@ export default async function trackLocation() {
|
||||||
});
|
});
|
||||||
|
|
||||||
BackgroundGeolocation.onHttp(async (response) => {
|
BackgroundGeolocation.onHttp(async (response) => {
|
||||||
// Use shared HTTP response handler for foreground context
|
// Log the full response including headers if available
|
||||||
await handleHttpResponse(response, "foreground");
|
locationLogger.debug("HTTP response received", {
|
||||||
|
status: response?.status,
|
||||||
|
success: response?.success,
|
||||||
|
responseText: response?.responseText,
|
||||||
|
url: response?.url,
|
||||||
|
method: response?.method,
|
||||||
|
isSync: response?.isSync,
|
||||||
|
requestHeaders:
|
||||||
|
response?.request?.headers || "Headers not available in response",
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add Sentry breadcrumb for HTTP responses
|
||||||
|
Sentry.addBreadcrumb({
|
||||||
|
message: "Background geolocation HTTP response",
|
||||||
|
category: "geolocation-http",
|
||||||
|
level: response?.status === 200 ? "info" : "warning",
|
||||||
|
data: {
|
||||||
|
status: response?.status,
|
||||||
|
success: response?.success,
|
||||||
|
url: response?.url,
|
||||||
|
isSync: response?.isSync,
|
||||||
|
recordCount: response?.count,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
// log status code and response
|
||||||
|
locationLogger.debug("HTTP response received", {
|
||||||
|
status: statusCode,
|
||||||
|
responseText: response?.responseText,
|
||||||
|
});
|
||||||
|
|
||||||
|
switch (statusCode) {
|
||||||
|
case 200:
|
||||||
|
// Successful response, check for token refresh
|
||||||
|
try {
|
||||||
|
const responseBody = response?.responseText
|
||||||
|
? JSON.parse(response.responseText)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
if (responseBody?.userBearerJwt) {
|
||||||
|
locationLogger.info(
|
||||||
|
"Token refresh successful, updating stored token",
|
||||||
|
);
|
||||||
|
|
||||||
|
// Use auth action to update both in-memory and persistent storage
|
||||||
|
await authActions.setUserToken(responseBody.userBearerJwt);
|
||||||
|
|
||||||
|
// Update BackgroundGeolocation config with new token
|
||||||
|
await BackgroundGeolocation.setConfig({
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${responseBody.userBearerJwt}`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
locationLogger.debug(
|
||||||
|
"Updated BackgroundGeolocation with refreshed token and removed X-Auth-Token header",
|
||||||
|
);
|
||||||
|
|
||||||
|
Sentry.addBreadcrumb({
|
||||||
|
message: "Token refreshed successfully",
|
||||||
|
category: "geolocation-auth",
|
||||||
|
level: "info",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
locationLogger.debug("Failed to parse successful response", {
|
||||||
|
error: e.message,
|
||||||
|
responseText: response?.responseText,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 410:
|
||||||
|
// Auth token expired, logout
|
||||||
|
locationLogger.info("Auth token expired (410), logging out");
|
||||||
|
Sentry.addBreadcrumb({
|
||||||
|
message: "Auth token expired - logging out",
|
||||||
|
category: "geolocation-auth",
|
||||||
|
level: "warning",
|
||||||
|
});
|
||||||
|
authActions.logout();
|
||||||
|
break;
|
||||||
|
case 401:
|
||||||
|
// Unauthorized: User token expired, refresh using throttled reload
|
||||||
|
locationLogger.info("Unauthorized (401), attempting to refresh token");
|
||||||
|
|
||||||
|
// Add more detailed logging of the error response
|
||||||
|
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,
|
||||||
|
});
|
||||||
|
|
||||||
|
Sentry.addBreadcrumb({
|
||||||
|
message: "Unauthorized - refreshing token",
|
||||||
|
category: "geolocation-auth",
|
||||||
|
level: "warning",
|
||||||
|
data: {
|
||||||
|
errorType: errorBody?.error?.type,
|
||||||
|
errorMessage: errorBody?.error?.message,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
locationLogger.debug("Failed to parse error response", {
|
||||||
|
error: e.message,
|
||||||
|
responseText: response?.responseText,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
reloadAuth();
|
||||||
|
break;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -71,7 +71,6 @@ export default createAtom(({ get, merge, getActions }) => {
|
||||||
await secureStore.setItemAsync("userToken", userToken);
|
await secureStore.setItemAsync("userToken", userToken);
|
||||||
endLoading({
|
endLoading({
|
||||||
userToken,
|
userToken,
|
||||||
authToken,
|
|
||||||
});
|
});
|
||||||
sessionActions.loadSessionFromJWT(userToken);
|
sessionActions.loadSessionFromJWT(userToken);
|
||||||
return { userToken };
|
return { userToken };
|
||||||
|
@ -111,7 +110,6 @@ export default createAtom(({ get, merge, getActions }) => {
|
||||||
} else {
|
} else {
|
||||||
endLoading({
|
endLoading({
|
||||||
userToken,
|
userToken,
|
||||||
authToken,
|
|
||||||
});
|
});
|
||||||
sessionActions.loadSessionFromJWT(jwtData);
|
sessionActions.loadSessionFromJWT(jwtData);
|
||||||
return;
|
return;
|
||||||
|
@ -124,7 +122,6 @@ export default createAtom(({ get, merge, getActions }) => {
|
||||||
authLogger.info("Successfully registered new user");
|
authLogger.info("Successfully registered new user");
|
||||||
authToken = res.authToken;
|
authToken = res.authToken;
|
||||||
await secureStore.setItemAsync("authToken", authToken);
|
await secureStore.setItemAsync("authToken", authToken);
|
||||||
merge({ authToken });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!userToken && authToken) {
|
if (!userToken && authToken) {
|
||||||
|
@ -263,8 +260,6 @@ export default createAtom(({ get, merge, getActions }) => {
|
||||||
]);
|
]);
|
||||||
merge({
|
merge({
|
||||||
userOffMode: true,
|
userOffMode: true,
|
||||||
authToken: null,
|
|
||||||
userToken: null,
|
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -308,7 +303,6 @@ export default createAtom(({ get, merge, getActions }) => {
|
||||||
return {
|
return {
|
||||||
default: {
|
default: {
|
||||||
userToken: null,
|
userToken: null,
|
||||||
authToken: null,
|
|
||||||
loading: true,
|
loading: true,
|
||||||
initialized: false,
|
initialized: false,
|
||||||
onReload: false,
|
onReload: false,
|
||||||
|
|
Loading…
Add table
Reference in a new issue