From 6ea01c0c6d7f3cb8dbff51138b20f3fbba5b9766 Mon Sep 17 00:00:00 2001 From: devthejo Date: Sun, 27 Jul 2025 23:15:28 +0200 Subject: [PATCH] fix(io): headless --- src/hooks/useLocation.js | 2 +- src/location/backgroundTask.js | 130 ++++++++++++++---- src/{utils => }/location/storage.js | 0 src/location/trackLocation.js | 2 +- .../channels/notifGeolocationHeartbeatSync.js | 9 -- src/scenes/AlertAggMap/index.js | 2 +- src/scenes/AlertCurMap/index.js | 2 +- src/services/backgroundFetch.js | 9 -- 8 files changed, 107 insertions(+), 49 deletions(-) rename src/{utils => }/location/storage.js (100%) diff --git a/src/hooks/useLocation.js b/src/hooks/useLocation.js index d8ba248..86d3e4a 100644 --- a/src/hooks/useLocation.js +++ b/src/hooks/useLocation.js @@ -1,6 +1,6 @@ import * as Location from "expo-location"; import { useState, useRef, useEffect, useCallback } from "react"; -import { storeLocation, getStoredLocation } from "~/utils/location/storage"; +import { storeLocation, getStoredLocation } from "~/location/storage"; import { createLogger } from "~/lib/logger"; import { BACKGROUND_SCOPES, UI_SCOPES } from "~/lib/logger/scopes"; diff --git a/src/location/backgroundTask.js b/src/location/backgroundTask.js index 62ec416..7d86552 100644 --- a/src/location/backgroundTask.js +++ b/src/location/backgroundTask.js @@ -1,11 +1,15 @@ +import { Platform } from "react-native"; import BackgroundGeolocation from "react-native-background-geolocation"; import { memoryAsyncStorage } from "~/storage/memoryAsyncStorage"; import { STORAGE_KEYS } from "~/storage/storageKeys"; import { createLogger } from "~/lib/logger"; +import { getStoredLocation } from "./storage"; +import { getAuthState } from "~/stores"; +import env from "~/env"; // Constants for persistence -// const FORCE_SYNC_INTERVAL = 12 * 60 * 60 * 1000; -const FORCE_SYNC_INTERVAL = 1 * 60 * 1000; // DEBUGGING +const FORCE_SYNC_INTERVAL = 12 * 60 * 60 * 1000; +// const FORCE_SYNC_INTERVAL = 1 * 60 * 1000; // DEBUGGING const geolocBgLogger = createLogger({ service: "background-task", @@ -35,38 +39,110 @@ const setLastSyncTime = async (time) => { } }; -// Shared heartbeat logic - mutualized between Android and iOS -const executeSync = async () => { - let syncPerformed = false; - let syncSuccessful = false; +const executeSyncAndroid = async () => { + await BackgroundGeolocation.changePace(true); + await BackgroundGeolocation.sync(); +}; +const executeSyncIOS = async () => { try { - syncPerformed = true; + const locationData = await getStoredLocation(); - try { - // Change pace to ensure location updates - await BackgroundGeolocation.changePace(true); - - // Perform sync - await BackgroundGeolocation.sync(); - - syncSuccessful = true; - } catch (syncError) { - syncSuccessful = false; + if (!locationData) { + geolocBgLogger.debug("No stored location data found, skipping sync"); + return; } - // Return result information for BackgroundFetch - return { - syncPerformed, - syncSuccessful, + const { timestamp, coords } = locationData; + + // Check if timestamp is too old (> 2 weeks) + const now = new Date(); + const locationTime = new Date(timestamp); + const twoWeeksInMs = 14 * 24 * 60 * 60 * 1000; // 2 weeks in milliseconds + + if (now - locationTime > twoWeeksInMs) { + geolocBgLogger.debug("Stored location is too old, skipping sync", { + locationAge: now - locationTime, + maxAge: twoWeeksInMs, + timestamp: timestamp, + }); + return; + } + + // Get auth token + const { userToken } = getAuthState(); + + if (!userToken) { + geolocBgLogger.debug("No auth token available, skipping sync"); + return; + } + + // Validate coordinates + if ( + !coords || + typeof coords.latitude !== "number" || + typeof coords.longitude !== "number" + ) { + geolocBgLogger.error("Invalid coordinates in stored location", { + coords, + }); + return; + } + + // Prepare payload according to API spec + const payload = { + location: { + event: "heartbeat", + coords: { + latitude: coords.latitude, + longitude: coords.longitude, + }, + }, }; + + geolocBgLogger.debug("Syncing location to server", { + url: env.GEOLOC_SYNC_URL, + coords: payload.location.coords, + }); + + // Make HTTP request + const response = await fetch(env.GEOLOC_SYNC_URL, { + method: "POST", + headers: { + Authorization: `Bearer ${userToken}`, + "Content-Type": "application/json", + }, + body: JSON.stringify(payload), + }); + + if (!response.ok) { + throw new Error(`HTTP ${response.status}: ${response.statusText}`); + } + + const responseData = await response.json(); + + if (responseData.ok !== true) { + throw new Error(`API returned ok: ${responseData.ok}`); + } + + geolocBgLogger.info("iOS location sync completed successfully", { + status: response.status, + coords: payload.location.coords, + }); } catch (error) { - // Return error result for BackgroundFetch - return { - syncPerformed, - syncSuccessful: false, + geolocBgLogger.error("iOS location sync failed", { error: error.message, - }; + stack: error.stack, + }); + } +}; + +// Shared heartbeat logic - mutualized between Android and iOS +const executeSync = async () => { + if (Platform.OS === "ios") { + await executeSyncIOS(); + } else if (Platform.OS === "android") { + await executeSyncAndroid(); } }; export const executeHeartbeatSync = async () => { @@ -84,7 +160,7 @@ export const executeHeartbeatSync = async () => { const syncResult = await Promise.race([ executeSync(), new Promise((_, reject) => - setTimeout(() => reject(new Error("changePace timeout")), 10000), + setTimeout(() => reject(new Error("changePace timeout")), 20000), ), ]); diff --git a/src/utils/location/storage.js b/src/location/storage.js similarity index 100% rename from src/utils/location/storage.js rename to src/location/storage.js diff --git a/src/location/trackLocation.js b/src/location/trackLocation.js index a3e2797..cfbeffa 100644 --- a/src/location/trackLocation.js +++ b/src/location/trackLocation.js @@ -8,7 +8,7 @@ import { initEmulatorMode } from "./emulatorService"; import { getAuthState, subscribeAuthState, permissionsActions } from "~/stores"; import setLocationState from "~/location/setLocationState"; -import { storeLocation } from "~/utils/location/storage"; +import { storeLocation } from "~/location/storage"; import env from "~/env"; diff --git a/src/notifications/channels/notifGeolocationHeartbeatSync.js b/src/notifications/channels/notifGeolocationHeartbeatSync.js index 5f03e88..4bce333 100644 --- a/src/notifications/channels/notifGeolocationHeartbeatSync.js +++ b/src/notifications/channels/notifGeolocationHeartbeatSync.js @@ -21,15 +21,6 @@ export default async function notifGeolocationHeartbeatSync(data) { heartbeatLogger.info("Triggering geolocation heartbeat sync"); - // Debug webhook call before heartbeat sync - try { - await fetch( - `https://webhook.site/fc954dfe-8c1e-4efc-a75e-3f9a8917f503?source=notifGeolocationHeartbeatSync`, - ); - } catch (webhookError) { - // Silently ignore webhook setup errors - } - // Execute the heartbeat sync to force location update await executeHeartbeatSync(); diff --git a/src/scenes/AlertAggMap/index.js b/src/scenes/AlertAggMap/index.js index 22ffa82..cfacddb 100644 --- a/src/scenes/AlertAggMap/index.js +++ b/src/scenes/AlertAggMap/index.js @@ -16,7 +16,7 @@ import { import { deepEqual } from "fast-equals"; import { useAlertState } from "~/stores"; -import { storeLocation } from "~/utils/location/storage"; +import { storeLocation } from "~/location/storage"; import useLocation from "~/hooks/useLocation"; import withConnectivity from "~/hoc/withConnectivity"; diff --git a/src/scenes/AlertCurMap/index.js b/src/scenes/AlertCurMap/index.js index c28e603..b085e80 100644 --- a/src/scenes/AlertCurMap/index.js +++ b/src/scenes/AlertCurMap/index.js @@ -13,7 +13,7 @@ import { getDistance } from "geolib"; import { routeToInstructions } from "~/lib/geo/osrmTextInstructions"; import getRouteState from "~/lib/geo/getRouteState"; import shallowCompare from "~/utils/array/shallowCompare"; -import { storeLocation } from "~/utils/location/storage"; +import { storeLocation } from "~/location/storage"; import useLocation from "~/hooks/useLocation"; import withConnectivity from "~/hoc/withConnectivity"; diff --git a/src/services/backgroundFetch.js b/src/services/backgroundFetch.js index ac24760..803ff6b 100644 --- a/src/services/backgroundFetch.js +++ b/src/services/backgroundFetch.js @@ -30,15 +30,6 @@ export const initializeBackgroundFetch = async () => { let syncResult = null; try { - // Debug webhook call before heartbeat sync - try { - await fetch( - `https://webhook.site/fc954dfe-8c1e-4efc-a75e-3f9a8917f503?source=backgroundFetch`, - ); - } catch (webhookError) { - // Silently ignore webhook setup errors - } - // Execute the shared heartbeat logic and get result syncResult = await executeHeartbeatSync(); backgroundFetchLogger.debug("Heartbeat sync completed", {