Skip to content

feat(meshcore): display device channels in MeshCoreChannelsView (phase 2/3, relanded)#3038

Merged
Yeraze merged 1 commit into
mainfrom
feat/meshcore-channels-phase-2-relanded
May 16, 2026
Merged

feat(meshcore): display device channels in MeshCoreChannelsView (phase 2/3, relanded)#3038
Yeraze merged 1 commit into
mainfrom
feat/meshcore-channels-phase-2-relanded

Conversation

@Yeraze
Copy link
Copy Markdown
Owner

@Yeraze Yeraze commented May 16, 2026

Re-land of PR #3036, which was inadvertently merged into the phase-1 branch (now orphaned) instead of main. The retarget-base step before merging didn't take.

This is the same commit, cherry-picked onto a fresh branch off main. Full vitest suite passes locally (4949/0).

Summary (from #3036)

Reads the channel list synced by phase 1 and renders one tab per channel. Replaces the hardcoded "Public" entry that was the only channel surface in the UI before. Extends the send path so messages sent on a non-channel-0 tab actually go to the right channel.

Display

  • MeshCoreChannelsView.tsx fetches /api/channels/all?sourceId=<src> and renders one tab per row.
  • Per-channel filter handles received (fromPublicKey === 'channel-${idx}') and locally-sent (toPublicKey === 'channel-${idx}') messages, with a back-compat clause for pre-phase-2 channel-0 local messages.

Send path extension

  • meshcoreNativeBackend.send_message now accepts channel_idx.
  • MeshCoreManager.sendMessage(text, toPublicKey?, channelIdx?) — third arg passes through and stamps the locally-stored copy with toPublicKey = 'channel-${idx}'.
  • /api/meshcore/messages/send accepts channelIdx in the body with range validation (0..255).
  • useMeshCore.sendMessage and the view pass the active channel idx.

Test plan

🤖 Generated with Claude Code

…e 2/3) (#3036)

Reads the channel list synced by phase 1 and renders one tab per
channel. Replaces the hardcoded "Public" entry that was the only
channel surface in the UI before.

Display
- MeshCoreChannelsView fetches /api/channels/all?sourceId=<src>
  (the /all endpoint bypasses the legacy 0..7 Meshtastic filter so
  MeshCore rows with higher indices show up). Synthesises a single
  "Public" entry as a fallback while a freshly-connected source is
  still mid-sync.
- Per-channel filter: a message belongs to channel X when its
  fromPublicKey is `channel-${X}` (received) or its toPublicKey is
  `channel-${X}` (locally-sent, phase-2 tagging). A legacy
  back-compat clause keeps pre-phase-2 outbound channel-0 messages
  visible in the Public tab without a migration.

Send path (small extension required for multi-channel UX)
- meshcoreNativeBackend.send_message now accepts `channel_idx` and
  routes the broadcast through sendChannelTextMessage(idx, text)
  when no DM recipient is given. Channel 0 stays the default for
  backward-compatible callers.
- MeshCoreManager.sendMessage(text, toPublicKey?, channelIdx?) —
  third arg passes through to the backend and stamps the locally-
  stored copy with toPublicKey = `channel-${idx}` so the filter
  works symmetrically.
- /api/meshcore/messages/send accepts channelIdx in the body with
  range validation (0..255).
- useMeshCore.sendMessage and the MeshCoreActions type expose the
  third arg; MeshCoreChannelsView passes the active channel idx.

Tests (+6 frontend)
- MeshCoreChannelsView.test.tsx: rendering each channel as a tab,
  fallback when the list is empty, blank-name -> "Channel N",
  per-channel filter (channel 0 incl. legacy local, channel 1
  incl. local toPublicKey), and that Send passes the active idx.

Phase 2 of 3 (see docs/meshcore-channels-plan.md). Phase 3 will add
the Configuration UI for create/edit/delete.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 16, 2026

Claude finished @Yeraze's task —— View job


Code Review Complete

I've thoroughly reviewed this PR implementing MeshCore channels display functionality (phase 2/3). Here's my comprehensive feedback:

Code Quality Assessment ✅

Strengths:

  • Excellent documentation: Every component has clear JSDoc comments explaining purpose and behavior
  • Consistent architecture: Follows established patterns from the codebase (sourceId scoping, async/await, permission gates)
  • Type safety: Proper TypeScript interfaces and type annotations throughout
  • Error handling: Comprehensive error handling with user-friendly fallbacks
  • Clean separation of concerns: UI logic separated from data fetching and state management

Code Style:

  • Follows React functional component patterns with proper hooks usage
  • Consistent naming conventions (camelCase, descriptive variable names)
  • Proper use of useMemo and useCallback for performance optimization
  • Clean JSX structure with semantic class names

Security Analysis ✅

No security concerns identified:

  • ✅ Proper input validation on channelIdx (0-255 range check)
  • ✅ URL encoding for sourceId parameter (encodeURIComponent)
  • ✅ CSRF protection via useCsrfFetch hook
  • ✅ Permission checks (requirePermission('messages', 'write'))
  • ✅ Source isolation enforced at router level
  • ✅ No SQL injection risks (using Drizzle ORM patterns)

Performance Considerations ✅

Well optimized:

  • ✅ Efficient message filtering with useMemo preventing unnecessary re-renders
  • ✅ Proper cleanup in useEffect with cancellation tokens
  • ✅ Debounced channel switching without excessive API calls
  • ✅ Smart fallback rendering (synthetic Channel 0) prevents loading states
  • ✅ Status dependency triggers re-fetch only on connection changes

Test Coverage Assessment ✅

Comprehensive test suite (266 lines):

  • ✅ Tests channel list rendering with API integration
  • ✅ Tests fallback behavior when no channels available
  • ✅ Tests per-channel message filtering (received vs sent)
  • ✅ Tests legacy message compatibility (pre-phase-2)
  • ✅ Tests send path integration with channel selection
  • ✅ Mock setup follows repository patterns
  • ✅ Good use of waitFor for async operations

Technical Implementation Review ✅

Backend Changes (meshcoreManager.ts:972-1019):

  • ✅ Clean extension of sendMessage signature with optional channelIdx
  • ✅ Proper channel key synthesis (channel-${idx}) for message tagging
  • ✅ Backwards compatibility maintained for existing callers
  • ✅ Appropriate validation and error handling

API Changes (meshcoreRoutes.ts):

  • ✅ Proper validation of channelIdx parameter (0-255 range)
  • ✅ Clean integration with existing send endpoint
  • ✅ Maintains existing authentication/permission patterns

Native Backend (meshcoreNativeBackend.ts):

  • ✅ Proper parameter passing to underlying sendChannelTextMessage
  • ✅ Input validation and error handling
  • ✅ Maintains wire protocol compatibility

Potential Issues & Recommendations ⚠️

Minor concerns:

  1. Message count calculation: The channel row shows message count via messages.filter(filter).length which re-filters on every render. Consider memoizing this calculation for better performance with large message lists.

  2. API error handling: The channel fetch only logs to console. Consider showing user-friendly error states in the UI when channel sync fails.

  3. Channel refresh logic: Re-fetch is triggered on status?.connected changes, but not on explicit user actions. Consider exposing a manual refresh capability.

Architecture Compliance ✅

  • ✅ Follows multi-source architecture (proper sourceId scoping)
  • ✅ Adheres to per-source permission model
  • ✅ Uses established database patterns (no raw SQL)
  • ✅ Follows async-first patterns
  • ✅ Maintains backend-frontend separation

Final Recommendation ✅ APPROVE

This is high-quality code that properly implements the phase 2 channels functionality. The implementation is:

  • Well-tested with comprehensive coverage
  • Follows established architectural patterns
  • Maintains security and performance standards
  • Has excellent documentation
  • Shows careful consideration of backwards compatibility

The minor performance suggestions above are optimizations rather than blocking issues. This PR is ready for merge.

Tested Features:

  • ✅ Channel list display and fallback behavior
  • ✅ Per-channel message filtering and display
  • ✅ Send path with channel index routing
  • ✅ Legacy message compatibility
  • ✅ API validation and security

@Yeraze Yeraze merged commit 24db654 into main May 16, 2026
15 checks passed
@github-actions
Copy link
Copy Markdown

System Test Results

MeshMonitor System Test Results

Test Run: 2026-05-15 20:31:00 EDT

Test Summary

Test Suite Result
Configuration Import ✅ PASSED
Quick Start Test ✅ PASSED
Security Test ✅ PASSED
V1 API Test ✅ PASSED
Reverse Proxy Test ✅ PASSED
Reverse Proxy + OIDC ✅ PASSED
Virtual Node CLI Test ✅ PASSED
Backup & Restore Test ✅ PASSED
Database Migration Test ✅ PASSED
DB Backing Consistency ✅ PASSED
API Exercise (3 DBs) ✅ PASSED

✅ Overall Result: PASSED

All deployment configurations are working correctly!

Test Details

Configuration Import:

  • Tests configuration import and device reboot cycle
  • Verifies channel roles, PSKs, and LoRa configuration
  • Note: Channel name verification skipped due to architectural limitation

Quick Start Test:

  • Zero-config deployment (no SESSION_SECRET or COOKIE_SECURE required)
  • HTTP access without HSTS
  • Auto-generated admin user with default credentials
  • Session cookies work over HTTP
  • Meshtastic node connection and message exchange verified

Security Test:

  • Verifies Node IP address hidden from anonymous users in API responses
  • Verifies MQTT configuration hidden from anonymous users
  • Verifies Node IP address visible to authenticated users
  • Verifies MQTT configuration visible to authenticated users
  • Verifies protected endpoints require authentication

V1 API Test:

  • Tests v1 REST API endpoints with Bearer token authentication
  • Verifies Bearer token requests bypass CSRF protection
  • Verifies POST/PUT/DELETE work without CSRF token when using Bearer auth
  • Verifies session-based requests still require CSRF token

Reverse Proxy Test:

  • Production deployment with COOKIE_SECURE=true
  • HTTPS-ready configuration
  • Trust proxy enabled for reverse proxy compatibility
  • CORS configured for HTTPS domain
  • Meshtastic node connection and message exchange verified

Reverse Proxy + OIDC Test:

  • OIDC authentication integration
  • Mock OIDC provider health checks
  • Authorization flow and session creation
  • Hybrid mode (OIDC + local auth)
  • Meshtastic node connection verified

Virtual Node CLI Test:

  • Virtual Node Server enabled on TCP port 4404
  • Meshtastic Python client successfully connects
  • Node data download and synchronization verified
  • Test message sent on gauntlet channel (index 3)
  • Message delivery confirmed via Web UI API
  • Virtual Node Server connection logging verified

Backup & Restore Test:

  • System backup created from running dev container
  • New container spun up with RESTORE_FROM_BACKUP env var
  • Data integrity verified (node count, message count, settings)
  • Restore event logged in audit log
  • Dev container unaffected by restore test

Database Migration Test:

  • SQLite to PostgreSQL migration verified
  • SQLite to MySQL migration verified
  • Data integrity confirmed for both target databases
  • Row counts match between source and target

DB Backing Consistency Test:

  • SQLite, PostgreSQL, and MySQL backends tested with same device
  • Node counts within ±10 across all three backends
  • Favorite counts identical across all backends
  • Key station verified as favorite on all backends

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant