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}")