mirror of
https://github.com/ipnet-mesh/meshcore-hub.git
synced 2026-07-21 09:02:24 +02:00
feat: raw packet capture, browse, and classification (v0.13.0)
Add a first-class Raw Packets feature that captures every inbound MeshCore
packet from the LetsMesh `packets` feed exactly as received, independent of
how the collector later classifies it.
Capture & storage
- New `RawPacket` model + migration (raw_packets table) with single and
composite indexes for the dominant filter-then-sort-by-newest queries.
- Collector-side `RAW_PACKET_CAPTURE_ENABLED` flag (default off); capture hook
reuses the decoder's per-hex cache (no second decode), one row per observer
reception, never blocks event dispatch.
- Separate `RAW_PACKET_RETENTION_DAYS` (falls back to DATA_RETENTION_DAYS);
cleanup runs regardless of capture so disabling drains the table. Raw-packet
observers retained in the is_observer recompute union.
API
- `GET /packets` and `/packets/{id}` with rich filtering, role-aware Redis
cache key, and channel-visibility redaction (restricted-channel packets are
returned metadata-only, not hidden, so pagination counts stay stable).
Web
- `FEATURE_PACKETS` flag (default off). Responsive Packets page (table desktop,
cards mobile) plus a Packet Detail page (breadcrumb nav, raw hex + decoded).
- Nav entry after Messages on all three surfaces; home.js reordered so Map
precedes Members; new packets icon + colour.
Finer-grained classification
- Replace the single `letsmesh_packet` catch-all with per-payload-type event
types (req, ack, encrypted_direct, encrypted_channel, grp_data, multipart,
control, raw_custom, ...); letsmesh_packet kept only as the unresolved-type
safety net.
Link from structured tables
- Add `packet_hash` to advertisements and messages (populated at ingest);
exact `packet_hash` filter on /packets; cube-icon link on the Adverts and
Messages lists -> /packets?packet_hash=<hash>, shown only when the feature is
on and the row has a stored hash.
Docs/config: .env.example, docker-compose (collector + web), AGENTS.md,
SCHEMAS.md, docs/letsmesh.md, docs/upgrading.md (## v0.13.0), en/nl i18n, and a
plan/tasks doc under docs/plans/.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -6,7 +6,7 @@ import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from meshcore_hub.web.app import create_app
|
||||
from tests.test_web.conftest import MockHttpClient
|
||||
from tests.test_web.conftest import ALL_FEATURES_ENABLED, MockHttpClient
|
||||
|
||||
|
||||
class TestFeatureFlagsConfig:
|
||||
@@ -193,6 +193,47 @@ class TestFeatureFlagsSEO:
|
||||
)
|
||||
|
||||
|
||||
class TestPacketsFeatureFlag:
|
||||
"""Test the packets feature flag (off by default)."""
|
||||
|
||||
def _make_app(self, mock_http_client: MockHttpClient, packets: bool):
|
||||
features = dict(ALL_FEATURES_ENABLED)
|
||||
features["packets"] = packets
|
||||
app = create_app(
|
||||
api_url="http://localhost:8000",
|
||||
api_key="test-api-key",
|
||||
network_name="Test Network",
|
||||
features=features,
|
||||
)
|
||||
app.state.http_client = mock_http_client
|
||||
return TestClient(app, raise_server_exceptions=True)
|
||||
|
||||
def test_packets_nav_hidden_when_disabled(
|
||||
self, mock_http_client: MockHttpClient
|
||||
) -> None:
|
||||
"""The packets nav link is absent when the feature is off."""
|
||||
client = self._make_app(mock_http_client, packets=False)
|
||||
html = client.get("/").text
|
||||
assert 'href="/packets"' not in html
|
||||
# Messages still shows (ordering sanity)
|
||||
assert 'href="/messages"' in html
|
||||
|
||||
def test_packets_nav_shown_when_enabled(
|
||||
self, mock_http_client: MockHttpClient
|
||||
) -> None:
|
||||
"""The packets nav link appears when the feature is on."""
|
||||
client = self._make_app(mock_http_client, packets=True)
|
||||
html = client.get("/").text
|
||||
assert 'href="/packets"' in html
|
||||
|
||||
def test_packets_disabled_by_default_in_settings(self) -> None:
|
||||
"""The declared default for feature_packets is False (env-independent)."""
|
||||
from meshcore_hub.common.config import WebSettings
|
||||
|
||||
# Check the field default directly so a local .env cannot mask it.
|
||||
assert WebSettings.model_fields["feature_packets"].default is False
|
||||
|
||||
|
||||
class TestFeatureFlagsIndividual:
|
||||
"""Test individual feature flags."""
|
||||
|
||||
|
||||
Reference in New Issue
Block a user