From d49cfefca2d35640fa4d30b15661dc5e1f779363 Mon Sep 17 00:00:00 2001 From: MarekWo Date: Mon, 29 Dec 2025 13:11:21 +0100 Subject: [PATCH] debug(contacts): Add detailed logging to diagnose last_seen matching issue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add comprehensive debug logging to trace last_seen data flow: cli.py (get_contacts_with_last_seen): - Log apply_to command success/failure with error details - Log number of bytes returned by command - Log number of contacts parsed from JSON - Log sample contact with public_key prefix and last_advert value - Log raw output on JSON parse errors api.py (get_contacts_detailed_api): - Log number of detailed contacts retrieved - Log number of matched contacts after merging - Fix case sensitivity: normalize both prefix and full_key to lowercase This will help identify why all contacts show "Last seen: Unknown". 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 --- app/meshcore/cli.py | 10 ++++++++++ app/routes/api.py | 10 ++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/app/meshcore/cli.py b/app/meshcore/cli.py index 55a7549..7ec6eb5 100644 --- a/app/meshcore/cli.py +++ b/app/meshcore/cli.py @@ -553,27 +553,37 @@ def get_contacts_with_last_seen() -> Tuple[bool, Dict[str, Dict], str]: success, stdout, stderr = _run_command(['apply_to', 't=1,t=2,t=3,t=4', 'contact_info']) if not success: + logger.error(f"apply_to contact_info failed: {stderr}") return False, {}, stderr or 'Failed to get contact details' + logger.info(f"apply_to contact_info returned {len(stdout)} bytes") + # Parse JSON output try: # The output should be a JSON array contact_list = json.loads(stdout) if not isinstance(contact_list, list): + logger.error(f"Unexpected response type: {type(contact_list)}") return False, {}, 'Unexpected response format (expected JSON array)' + logger.info(f"Parsed {len(contact_list)} contacts from JSON") + # Build dictionary indexed by public_key for easy lookup contacts_dict = {} for contact in contact_list: if 'public_key' in contact: # Use full public key as index contacts_dict[contact['public_key']] = contact + # Log first contact for debugging + if len(contacts_dict) == 1: + logger.info(f"Sample contact: public_key={contact['public_key'][:12]}..., last_advert={contact.get('last_advert', 'MISSING')}") return True, contacts_dict, "" except json.JSONDecodeError as e: logger.error(f"Failed to parse contact_info JSON: {e}") + logger.error(f"Raw output (first 500 chars): {stdout[:500]}") return False, {}, f'JSON parse error: {str(e)}' except Exception as e: diff --git a/app/routes/api.py b/app/routes/api.py index 14cf849..5c49810 100644 --- a/app/routes/api.py +++ b/app/routes/api.py @@ -1245,17 +1245,23 @@ def get_contacts_detailed_api(): success_detailed, contacts_detailed, error_detailed = cli.get_contacts_with_last_seen() if success_detailed: + logger.info(f"Got {len(contacts_detailed)} detailed contacts") + # Merge last_advert data with contacts # Match by public_key_prefix (first 12 chars of full public_key) + matched_count = 0 for contact in contacts: - prefix = contact.get('public_key_prefix', '') + prefix = contact.get('public_key_prefix', '').lower() # Find matching contact in detailed data for full_key, details in contacts_detailed.items(): - if full_key.startswith(prefix): + if full_key.lower().startswith(prefix): # Add last_seen timestamp contact['last_seen'] = details.get('last_advert', None) + matched_count += 1 break + + logger.info(f"Matched {matched_count} out of {len(contacts)} contacts with last_seen data") else: # If detailed fetch failed, log warning but still return contacts without last_seen logger.warning(f"Failed to get last_seen data: {error_detailed}")