mirror of
https://github.com/MarekWo/mc-webui.git
synced 2026-08-07 01:03:15 +02:00
feat: Auto-detect device name from meshcli prompt
Bridge now detects device name from meshcli prompt ("DeviceName|*")
and exposes it via /health endpoint. mc-webui fetches this at startup
and uses RuntimeConfig for dynamic device name throughout the app.
Fallback chain: prompt detection → .infos command → MC_DEVICE_NAME env var
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -5,6 +5,7 @@ MeshCore CLI wrapper - executes meshcli commands via HTTP bridge
|
||||
import logging
|
||||
import re
|
||||
import json
|
||||
import time
|
||||
import requests
|
||||
from pathlib import Path
|
||||
from typing import Tuple, Optional, List, Dict
|
||||
@@ -965,3 +966,50 @@ def set_manual_add_contacts(enabled: bool) -> Tuple[bool, str]:
|
||||
return False, 'Cannot connect to meshcore-bridge service'
|
||||
except Exception as e:
|
||||
return False, str(e)
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Device Name Detection
|
||||
# =============================================================================
|
||||
|
||||
def fetch_device_name_from_bridge(max_retries: int = 3, retry_delay: float = 2.0) -> Tuple[Optional[str], str]:
|
||||
"""
|
||||
Fetch detected device name from meshcore-bridge /health endpoint.
|
||||
|
||||
The bridge auto-detects device name from meshcli prompt ("DeviceName|*")
|
||||
and exposes it via /health endpoint.
|
||||
|
||||
Args:
|
||||
max_retries: Number of retry attempts if bridge is unavailable
|
||||
retry_delay: Delay between retries in seconds
|
||||
|
||||
Returns:
|
||||
Tuple of (device_name, source)
|
||||
- device_name: Detected name or fallback from config
|
||||
- source: "detected", "config", or "fallback"
|
||||
"""
|
||||
bridge_health_url = config.MC_BRIDGE_URL.replace('/cli', '/health')
|
||||
|
||||
for attempt in range(max_retries):
|
||||
try:
|
||||
response = requests.get(bridge_health_url, timeout=5)
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
if data.get('status') == 'healthy':
|
||||
device_name = data.get('device_name')
|
||||
source = data.get('device_name_source', 'unknown')
|
||||
if device_name:
|
||||
logger.info(f"Got device name from bridge: {device_name} (source: {source})")
|
||||
return device_name, source
|
||||
except requests.exceptions.ConnectionError:
|
||||
logger.warning(f"Bridge not reachable, attempt {attempt + 1}/{max_retries}")
|
||||
except requests.exceptions.Timeout:
|
||||
logger.warning(f"Bridge timeout, attempt {attempt + 1}/{max_retries}")
|
||||
except Exception as e:
|
||||
logger.warning(f"Attempt {attempt + 1}/{max_retries} failed: {e}")
|
||||
|
||||
if attempt < max_retries - 1:
|
||||
time.sleep(retry_delay)
|
||||
|
||||
logger.warning(f"Using fallback device name: {config.MC_DEVICE_NAME}")
|
||||
return config.MC_DEVICE_NAME, "fallback"
|
||||
|
||||
@@ -9,7 +9,7 @@ import time
|
||||
from pathlib import Path
|
||||
from typing import List, Dict, Optional, Tuple
|
||||
from datetime import datetime, timedelta
|
||||
from app.config import config
|
||||
from app.config import config, runtime_config
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -48,7 +48,7 @@ def parse_message(line: Dict, allowed_channels: Optional[List[int]] = None) -> O
|
||||
# Extract sender name
|
||||
if is_own:
|
||||
# For sent messages, use 'sender' field (meshcore-cli 1.3.12+)
|
||||
sender = line.get('sender', config.MC_DEVICE_NAME)
|
||||
sender = line.get('sender', runtime_config.get_device_name())
|
||||
content = text
|
||||
else:
|
||||
# For received messages, extract sender from "SenderName: message" format
|
||||
@@ -91,7 +91,7 @@ def read_messages(limit: Optional[int] = None, offset: int = 0, archive_date: Op
|
||||
if archive_date:
|
||||
return read_archive_messages(archive_date, limit, offset, channel_idx)
|
||||
|
||||
msgs_file = config.msgs_file_path
|
||||
msgs_file = runtime_config.get_msgs_file_path()
|
||||
|
||||
if not msgs_file.exists():
|
||||
logger.warning(f"Messages file not found: {msgs_file}")
|
||||
@@ -268,7 +268,7 @@ def delete_channel_messages(channel_idx: int) -> bool:
|
||||
Returns:
|
||||
True if successful, False otherwise
|
||||
"""
|
||||
msgs_file = config.msgs_file_path
|
||||
msgs_file = runtime_config.get_msgs_file_path()
|
||||
|
||||
if not msgs_file.exists():
|
||||
logger.warning(f"Messages file not found: {msgs_file}")
|
||||
@@ -338,7 +338,7 @@ def _cleanup_old_dm_sent_log() -> None:
|
||||
return
|
||||
|
||||
try:
|
||||
dm_log_file = Path(config.MC_CONFIG_DIR) / f"{config.MC_DEVICE_NAME}_dm_sent.jsonl"
|
||||
dm_log_file = Path(config.MC_CONFIG_DIR) / f"{runtime_config.get_device_name()}_dm_sent.jsonl"
|
||||
if dm_log_file.exists():
|
||||
dm_log_file.unlink()
|
||||
logger.info(f"Cleaned up old DM sent log: {dm_log_file}")
|
||||
@@ -420,7 +420,7 @@ def _parse_sent_msg(line: Dict) -> Optional[Dict]:
|
||||
timestamp = line.get('timestamp', 0)
|
||||
# Use 'recipient' field (added in meshcore-cli 1.3.12), fallback to 'name'
|
||||
recipient = line.get('recipient', line.get('name', 'Unknown'))
|
||||
sender = line.get('sender', config.MC_DEVICE_NAME)
|
||||
sender = line.get('sender', runtime_config.get_device_name())
|
||||
|
||||
# Generate conversation ID from recipient name
|
||||
conversation_id = f"name_{recipient}"
|
||||
@@ -471,7 +471,7 @@ def read_dm_messages(
|
||||
_cleanup_old_dm_sent_log()
|
||||
|
||||
# --- Read DM messages from .msgs file ---
|
||||
msgs_file = config.msgs_file_path
|
||||
msgs_file = runtime_config.get_msgs_file_path()
|
||||
if msgs_file.exists():
|
||||
try:
|
||||
with open(msgs_file, 'r', encoding='utf-8') as f:
|
||||
|
||||
Reference in New Issue
Block a user