@@ -253,9 +253,9 @@ class ApiService {
253253 };
254254 }
255255
256- // Log the full request payload
257- debugLog ( '[HEARTBEAT] POST $ wardriveEndpoint ' ) ;
258- debugLog ('[HEARTBEAT] Payload : ${ json . encode ( payload )} ' );
256+ // Log heartbeat request (hide sensitive payload details)
257+ final hasCoords = lat != null && lon != null ;
258+ debugLog ('[HEARTBEAT] POST (with GPS : $hasCoords ) ' );
259259
260260 final response = await _client.post (
261261 Uri .parse (wardriveEndpoint),
@@ -282,6 +282,50 @@ class ApiService {
282282 }
283283 }
284284
285+ /// Check if session is still valid by sending a heartbeat
286+ /// Use this before starting any wardrive action (Send Ping, Active Mode, Passive Mode)
287+ /// Returns (isValid, errorReason, errorMessage)
288+ Future <({bool isValid, String ? reason, String ? message})> checkSessionValid ({
289+ double ? lat,
290+ double ? lon,
291+ }) async {
292+ if (_sessionId == null ) {
293+ debugWarn ('[SESSION] No session to validate' );
294+ return (isValid: false , reason: 'no_session' , message: 'No active session' );
295+ }
296+
297+ debugLog ('[SESSION] Checking session validity via heartbeat...' );
298+ final result = await sendHeartbeat (lat: lat, lon: lon);
299+
300+ if (result == null ) {
301+ debugWarn ('[SESSION] Session check failed: no response' );
302+ return (isValid: false , reason: 'no_response' , message: 'Server did not respond' );
303+ }
304+
305+ if (result['success' ] == true ) {
306+ debugLog ('[SESSION] Session is valid (expires_at: ${result ['expires_at' ]})' );
307+ return (isValid: true , reason: null , message: null );
308+ }
309+
310+ // Session is invalid - check reason
311+ final reason = result['reason' ] as String ? ;
312+ final message = result['message' ] as String ? ;
313+ debugWarn ('[SESSION] Session invalid: $reason - $message ' );
314+
315+ // Trigger session error callback for critical errors
316+ const criticalErrors = {
317+ 'session_expired' , 'session_invalid' , 'session_revoked' , 'bad_session' ,
318+ 'invalid_key' , 'unauthorized' , 'bad_key' ,
319+ 'outside_zone' , 'zone_full' ,
320+ };
321+ if (criticalErrors.contains (reason)) {
322+ _clearSession ();
323+ onSessionError? .call (reason, message);
324+ }
325+
326+ return (isValid: false , reason: reason, message: message);
327+ }
328+
285329 /// Enable heartbeat mode (called when auto mode starts)
286330 /// Heartbeat is scheduled based on expires_at from API responses
287331 /// @param gpsProvider Callback to get current GPS coordinates for heartbeat
0 commit comments