From 01ccec21c4b4ab824e2dc12775e787ee576ecbb5 Mon Sep 17 00:00:00 2001 From: DataArchitectPro Date: Thu, 2 Jul 2026 01:40:14 +0500 Subject: [PATCH 1/2] Forward offline wardrive batches to Custom API after upload After a successful offline batch upload to MeshMapper, mirror the online queue behavior by forwarding the same pings to the configured Custom API endpoint. Adds detailed [CUSTOM API] / [OFFLINE] debug logging for tracing. Co-authored-by: Cursor --- lib/providers/app_state_provider.dart | 33 +++++++++++++++++++++ lib/services/custom_api_service.dart | 41 +++++++++++++++++++++------ pubspec.lock | 24 ++++++++-------- pubspec.yaml | 2 +- 4 files changed, 79 insertions(+), 21 deletions(-) diff --git a/lib/providers/app_state_provider.dart b/lib/providers/app_state_provider.dart index 059dda5..c903f00 100644 --- a/lib/providers/app_state_provider.dart +++ b/lib/providers/app_state_provider.dart @@ -5100,6 +5100,7 @@ class AppStateProvider extends ChangeNotifier with WidgetsBindingObserver { // Extract session_id into local variable — never stored in shared state final offlineSessionId = effectiveAuth!['session_id'] as String?; + final uploadPublicKey = publicKey!; if (offlineSessionId == null) { debugError('[OFFLINE] Auth succeeded but no session_id in response'); return OfflineUploadResult.authFailed; @@ -5179,6 +5180,12 @@ class AppStateProvider extends ChangeNotifier with WidgetsBindingObserver { if (result == UploadResult.success) { uploadedCount += batch.length; debugLog('[OFFLINE] Uploaded batch $batchNum: ${batch.length} pings'); + _forwardOfflineBatchToCustomApi( + batch, + batchNum: batchNum, + publicKey: uploadPublicKey, + auth: effectiveAuth, + ); continue; } @@ -5234,6 +5241,32 @@ class AppStateProvider extends ChangeNotifier with WidgetsBindingObserver { } } + /// Forward a successfully uploaded offline batch to the Custom API endpoint. + void _forwardOfflineBatchToCustomApi( + List> batch, { + required int batchNum, + required String publicKey, + required Map auth, + }) { + final contact = publicKey.length >= 8 + ? publicKey.substring(0, 8).toUpperCase() + : null; + var iata = zoneCode ?? _preferences.iataCode; + final zone = auth['zone']; + if (zone is Map && zone['code'] != null) { + iata = zone['code'].toString(); + } + debugLog( + '[OFFLINE] Custom API forward for batch $batchNum ' + '(${batch.length} pings, contact=$contact, iata=$iata)'); + _customApiService.forwardPings( + batch, + contactOverride: contact, + iataOverride: iata, + source: 'offline', + ); + } + /// Delete an offline session without uploading Future deleteOfflineSession(String filename) async { await _offlineSessionService.deleteSession(filename); diff --git a/lib/services/custom_api_service.dart b/lib/services/custom_api_service.dart index f5ba0c7..4615921 100644 --- a/lib/services/custom_api_service.dart +++ b/lib/services/custom_api_service.dart @@ -41,17 +41,42 @@ class CustomApiService { _client = client ?? http.Client(); /// Fire-and-forget: forward pings to the custom endpoint. - /// Called after successful MeshMapper upload. Never throws. - void forwardPings(List> pings) { + /// Called after successful MeshMapper upload (online queue or offline session). + /// Never throws. + void forwardPings( + List> pings, { + String? contactOverride, + String? iataOverride, + String source = 'online', + }) { + if (pings.isEmpty) return; + final prefs = _prefsGetter(); - if (!prefs.customApiEnabled) return; - if (prefs.customApiUrl == null || prefs.customApiUrl!.isEmpty) return; - if (prefs.customApiKey == null || prefs.customApiKey!.isEmpty) return; + debugLog( + '[CUSTOM API] Forward requested ($source): ${pings.length} item(s)'); + + if (!prefs.customApiEnabled) { + debugLog('[CUSTOM API] Forward skipped ($source): Custom API disabled'); + return; + } + if (prefs.customApiUrl == null || prefs.customApiUrl!.isEmpty) { + debugLog('[CUSTOM API] Forward skipped ($source): URL not configured'); + return; + } + if (prefs.customApiKey == null || prefs.customApiKey!.isEmpty) { + debugLog('[CUSTOM API] Forward skipped ($source): API key not configured'); + return; + } // Enrich with contact and iata (custom API only — never sent to MeshMapper) - final contact = - prefs.customApiIncludeContact ? contactGetter?.call() : null; - final iata = iataGetter?.call(); + final contact = prefs.customApiIncludeContact + ? (contactOverride ?? contactGetter?.call()) + : null; + final iata = iataOverride ?? iataGetter?.call(); + + debugLog( + '[CUSTOM API] Forwarding ($source) → ${prefs.customApiUrl} ' + '(contact=${contact ?? "off"}, iata=${iata ?? "none"})'); final enriched = pings.map((ping) { final enrichedPing = Map.from(ping); diff --git a/pubspec.lock b/pubspec.lock index c63f0b2..5874ba9 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -133,10 +133,10 @@ packages: dependency: transitive description: name: characters - sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 + sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.4.1" checked_yaml: dependency: transitive description: @@ -468,10 +468,10 @@ packages: dependency: "direct main" description: name: font_awesome_flutter - sha256: b9011df3a1fa02993630b8fb83526368cf2206a711259830325bab2f1d2a4eb0 + sha256: "09dcde8ab90ffae1a7d65ff2ef96fc62a17ad9d0ce7c127b317ded676b0d5935" url: "https://pub.dev" source: hosted - version: "10.12.0" + version: "11.0.0" frontend_server_client: dependency: transitive description: @@ -731,26 +731,26 @@ packages: dependency: transitive description: name: matcher - sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861 url: "https://pub.dev" source: hosted - version: "0.12.17" + version: "0.12.19" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec + sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b" url: "https://pub.dev" source: hosted - version: "0.11.1" + version: "0.13.0" meta: dependency: transitive description: name: meta - sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" + sha256: "1741988757a65eb6b36abe716829688cf01910bbf91c34354ff7ec1c3de2b349" url: "https://pub.dev" source: hosted - version: "1.17.0" + version: "1.18.0" mime: dependency: transitive description: @@ -1160,10 +1160,10 @@ packages: dependency: transitive description: name: test_api - sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55 + sha256: "949a932224383300f01be9221c39180316445ecb8e7547f70a41a35bf421fb9e" url: "https://pub.dev" source: hosted - version: "0.7.7" + version: "0.7.11" timezone: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index adba300..0902ab8 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -35,7 +35,7 @@ dependencies: just_audio: ^0.9.40 audio_session: ^0.1.21 dio: ^5.0.0 - font_awesome_flutter: ^10.7.0 + font_awesome_flutter: ^11.0.0 fl_chart: ^0.69.0 web: ^1.1.0 disk_space_plus: ^0.2.3 From 298dbd8ea0a8b70ff83675c0be69d88420348496 Mon Sep 17 00:00:00 2001 From: DataArchitectPro Date: Thu, 2 Jul 2026 02:36:11 +0500 Subject: [PATCH 2/2] Revert pubspec font_awesome bump (build-only, not part of feature) Keeps upstream PR limited to offline Custom API forward changes. Co-authored-by: Cursor --- pubspec.lock | 24 ++++++++++++------------ pubspec.yaml | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index 5874ba9..c63f0b2 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -133,10 +133,10 @@ packages: dependency: transitive description: name: characters - sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b + sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 url: "https://pub.dev" source: hosted - version: "1.4.1" + version: "1.4.0" checked_yaml: dependency: transitive description: @@ -468,10 +468,10 @@ packages: dependency: "direct main" description: name: font_awesome_flutter - sha256: "09dcde8ab90ffae1a7d65ff2ef96fc62a17ad9d0ce7c127b317ded676b0d5935" + sha256: b9011df3a1fa02993630b8fb83526368cf2206a711259830325bab2f1d2a4eb0 url: "https://pub.dev" source: hosted - version: "11.0.0" + version: "10.12.0" frontend_server_client: dependency: transitive description: @@ -731,26 +731,26 @@ packages: dependency: transitive description: name: matcher - sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861 + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 url: "https://pub.dev" source: hosted - version: "0.12.19" + version: "0.12.17" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b" + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec url: "https://pub.dev" source: hosted - version: "0.13.0" + version: "0.11.1" meta: dependency: transitive description: name: meta - sha256: "1741988757a65eb6b36abe716829688cf01910bbf91c34354ff7ec1c3de2b349" + sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" url: "https://pub.dev" source: hosted - version: "1.18.0" + version: "1.17.0" mime: dependency: transitive description: @@ -1160,10 +1160,10 @@ packages: dependency: transitive description: name: test_api - sha256: "949a932224383300f01be9221c39180316445ecb8e7547f70a41a35bf421fb9e" + sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55 url: "https://pub.dev" source: hosted - version: "0.7.11" + version: "0.7.7" timezone: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 0902ab8..adba300 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -35,7 +35,7 @@ dependencies: just_audio: ^0.9.40 audio_session: ^0.1.21 dio: ^5.0.0 - font_awesome_flutter: ^11.0.0 + font_awesome_flutter: ^10.7.0 fl_chart: ^0.69.0 web: ^1.1.0 disk_space_plus: ^0.2.3