Commit Graph

894 Commits

Author SHA1 Message Date
github-actions 6fd4270835 ci: update test badges [skip ci] 2026-03-26 19:55:10 +00:00
Kpa-clawbot 4cfdd85063 fix: resolve 4 issues + optimize E2E test performance
Issues fixed:
- #127: Firefox copy URL - shared copyToClipboard() with execCommand fallback
- #125: Dismiss packet detail pane - close button with keyboard support
- #124: Customize window scrollbar - flex layout fix for overflow
- #122: Last Activity stale times - use last_heard || last_seen

Test improvements:
- E2E perf: replace 19 networkidle waits, cut navigations 14->7, remove 11 sleeps
- 8 new unit tests for copyToClipboard helper (47->55 in test-frontend-helpers)
- 1 new E2E test for packet pane dismiss

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-26 12:41:25 -07:00
Kpa-clawbot 521ee21fce fix(test): move browser.close() after all E2E tests complete
browser.close() on line 274 was firing before tests 14-16 executed,
causing them to crash with 'Target page, context or browser has been
closed'. Moved to after test 16, just before the summary block.

Fixes 3 of 4 E2E failures (remaining 2 are data-dependent map tests).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-26 12:41:25 -07:00
Kpa-clawbot dfea71a9ea chore(squad): log Kobayashi E2E performance audit
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-26 12:41:25 -07:00
github-actions ef41a6ad6c ci: update test badges [skip ci] 2026-03-26 16:18:37 +00:00
you e2f9dd6f1e fix: track repeater last_heard from relay path hops
Repeaters that actively relay packets showed stale 'last seen' times
because last_seen only updates on adverts (every 12h) and last_heard
only tracked sender/recipient appearances, not relay hops.

- Add lastPathSeenMap: full pubkey → ISO timestamp for path hop sightings
- updatePathSeenTimestamps() resolves hop prefixes via hopPrefixToKey cache
- /api/nodes uses max(pktStore timestamp, path hop timestamp) for last_heard
- 4 new tests: hop-only nodes, stale vs fresh, pktStore priority, cache invalidation
2026-03-26 16:04:39 +00:00
you e1a776bd34 fix: move CI deploy paths to /opt — no personal info in logs
Runner moved to /opt/actions-runner/
Config/Caddyfile served from /opt/meshcore-deploy/
Data symlinked to /opt/meshcore-deploy/data/
Zero $HOME references in deploy workflow
2026-03-26 03:59:47 +00:00
you 262875435a fix: CI deploy reads config/Caddyfile from deployment dir, not CI checkout
CI runs from actions-runner/_work/ which doesn't have config.json or
caddy-config/. These files live in $HOME/meshcore-analyzer/ which is
the persistent deployment directory.
2026-03-26 03:52:50 +00:00
github-actions 9a6bc40d60 ci: update test badges [skip ci] 2026-03-26 03:30:07 +00:00
you 49b3648cbd fix: CI deploy uses correct Caddyfile path, dynamic ports, health check
- Config from repo dir, not hardcoded home path
- Caddyfile from caddy-config/ (was missing the subdirectory)
- Dynamic port mapping derived from Caddyfile content
- Auto-detect existing host data directory for bind mount
- Health check waits for /api/stats after deploy
- Read-only mounts for config and Caddyfile
2026-03-26 03:16:21 +00:00
you dee4a19986 fix: simplify HTTPS setup options, allow custom HTTP port 2026-03-26 01:44:16 +00:00
you 5d3f0e5642 fix: manage.sh deployment safety improvements
- Config protection: never overwrite existing config.json, warn on placeholder values
- Port mapping validation: start/restart check if container ports match Caddyfile,
  offer to recreate if mismatched
- Data volume detection: detect existing DB in $HOME/meshcore-data/ or ./data/,
  use bind mount instead of named volume (never hardcodes paths)
- Real health verification: wait for /api/stats response, check HTTPS if domain
  configured, scan logs for MQTT errors
- Restart recreates container with correct ports when mappings changed
- Status command: shows MQTT errors, port mismatch warnings
- Update command: uses shared recreate_container helper
- Extracted helpers: get_data_mount_args, get_required_ports, check_port_match,
  recreate_container, verify_health, check_config_placeholders
2026-03-26 01:38:50 +00:00
github-actions 05d787efa1 ci: update test badges [skip ci] 2026-03-25 22:47:37 +00:00
you e340949253 feat: optimize observations table — 478MB → 141MB
Schema v3 migration:
- Replace observer_id TEXT (64-char hex) with observer_idx INTEGER FK
- Drop redundant hash, observer_name, created_at columns
- Store timestamp as epoch integer instead of ISO string
- In-memory dedup Set replaces expensive unique index lookups
- Auto-migration on startup with timestamped backup (never overwrites)
- Detects already-migrated DBs via pragma user_version + column inspection

Fixes:
- disambiguateHops: restore 'known' field dropped during refactor (fba5649)
- Skip MQTT connections when NODE_ENV=test
- e2e test: encodeURIComponent for # channel hashes in URLs
- VACUUM + TRUNCATE checkpoint after migration (not just VACUUM)
- Daily TRUNCATE checkpoint at 2:00 AM UTC to reclaim WAL space

Observability:
- SQLite stats in /api/perf (DB size, WAL size, freelist, row counts, busy pages)
- Rendered in perf dashboard with color-coded thresholds

Tests: 839 pass (89 db + 30 migration + 70 helpers + 200 routes + 34 packet-store + 52 decoder + 255 decoder-spec + 62 filter + 47 e2e)
2026-03-25 22:33:39 +00:00
you 629606bbdd feat: optimize observations table schema (v3 migration)
- Replace observer_id TEXT (64-char hex) + observer_name TEXT with observer_idx INTEGER (FK to observers rowid)
- Remove redundant hash TEXT and created_at TEXT columns from observations
- Store timestamp as INTEGER epoch seconds instead of ISO text string
- Auto-migrate old schema on startup: backup DB, migrate data, rebuild indexes, VACUUM
- Migration is safe: backup first, abort on failure, schema_version marker prevents re-runs
- Backward-compatible packets_v view: JOINs observers table, converts epoch→ISO for consumers
- In-memory observer_id→rowid Map for fast lookups during ingestion
- In-memory dedup Set with 5-min TTL to prevent duplicate INSERT attempts
- packet-store.js: detect v3 schema and use appropriate JOIN query
- Tests: 29 migration tests (old→new, idempotency, backup failure, ingestion, dedup)
- Tests: 19 new v3 schema tests in test-db.js (columns, types, view compat, ingestion)

Expected savings on 947K-row prod DB:
- observer_id: 61 bytes → 4 bytes per row (57 bytes saved)
- observer_name: ~15 bytes → 0 (resolved via JOIN)
- hash: 16 bytes → 0 (redundant with transmission_id)
- timestamp: 25 bytes → 4 bytes (21 bytes saved)
- created_at: 25 bytes → 0 (redundant)
- Dedup index: much smaller (integers vs text)
- Estimated ~118 bytes saved per row = ~112MB total + massive index savings
2026-03-25 19:13:22 +00:00
you 63f2f7c995 refactor: unify live page packet rendering into renderPacketTree()
Major refactor of live.js data flow:

- Replaced animatePacket() and animateRealisticPropagation() with
  single renderPacketTree(packets, isReplay) function
- All paths use the same function: WS arrival, VCR replay, DB load,
  feed card replay button
- VCR fetches use expand=observations to get full observation data
- expandToBufferEntries() extracts per-observer paths from observations
- startReplay() pre-aggregates VCR buffer by hash before playback
- Feed dedup accumulates observation packets for full tree replay
- Longest path shown in feed (scans all observations, not just first)
- Replay button uses full observation set for starburst animation

Server changes:
- WS broadcast includes path_json per observation
- packet-store insert() uses longest path for display (was earliest)

DB changes:
- Removed seed() function and synthetic test data

Not pushed to prod — local testing only.
2026-03-25 06:02:48 +00:00
you 78e56f064f fix: use printf instead of echo -e for portable escape code rendering
echo -e doesn't work in all shells (sh on Ubuntu ignores -e).
printf '%b\n' handles escape sequences portably.
2026-03-25 01:56:47 +00:00
you 83af808162 feat: backup/restore now includes config, Caddyfile, and theme
Backup creates a directory with meshcore.db + config.json + Caddyfile +
theme.json (if present). Restore accepts a backup directory (restores
all files) or a single .db file (DB only). Lists available backups
when called without arguments.
2026-03-25 00:47:36 +00:00
you 77adc311cb docs: simplify backup/update sections — use manage.sh, drop volume inspect
Backup section now shows manage.sh commands instead of raw docker
volume inspect paths. Update section is one command.
2026-03-25 00:46:04 +00:00
you 60fde176d0 docs: update README Quick Start to use manage.sh
Replaced manual docker build/run commands with ./manage.sh setup.
Removed examples that exposed port 1883 by default.
Added link to DEPLOYMENT.md for full guide.
2026-03-25 00:43:31 +00:00
you dc95968a2c chore: gitignore .setup-state 2026-03-25 00:42:13 +00:00
you e169f9c7b2 feat: rewrite manage.sh as idempotent setup wizard
- State tracking (.setup-state) — resume from where you left off
- Every step checks what's already done and skips it
- Safe to Ctrl+C and re-run at any point
- Auto-generates random API key on first config creation
- HTTPS choice menu: domain (auto), Cloudflare/proxy (HTTP), or later
- DNS validation with IP comparison
- Port 80 conflict detection
- Status shows packet/node counts, DB size, per-service health
- Backup auto-creates ./backups/ dir, restore backs up current first
- Reset command (keeps data + config, removes container + image)
- .setup-state in .gitignore
2026-03-25 00:42:04 +00:00
you eaa42ed097 feat: add manage.sh helper script for setup and management
Interactive setup: checks Docker, creates config, prompts for domain,
validates DNS, builds and runs the container.

Commands: setup, start, stop, restart, status, logs, update, backup,
restore, mqtt-test. Colored output, error handling, confirmations
for destructive operations.

Updated DEPLOYMENT.md to show manage.sh as the primary quick start.
2026-03-25 00:40:08 +00:00
you 3f7fa89acb docs: full rewrite of deployment guide
- Added HTTPS Options section: auto (Caddy), bring your own cert,
  Cloudflare Tunnel, behind existing proxy, HTTP-only
- Expanded MQTT Security into its own section with 3 options + recommendation
- Fixed DB backup to use volume path not docker cp
- Added restore instructions
- Expanded troubleshooting table (rate limits → use own cert or different subdomain)
- Clarified that MQTT 1883 is NOT exposed by default in quick start
- Added tip to save docker run as a script
- Restructured for cleaner TOC
- Removed condescension, kept clarity
2026-03-25 00:38:06 +00:00
you 3cb6c43c52 docs: fix backup instructions — DB is on volume, not inside container 2026-03-25 00:35:32 +00:00
you 3ada1f1c3d docs: replace ASCII art with Mermaid diagrams in deployment guide
- Traffic flow: browser/observers → Caddy/Mosquitto → Node.js → SQLite
- Container internals: supervisord → services
- Data flow sequence: LoRa → observer → MQTT → server → browser
- Quick start flow: 5-step overview
2026-03-25 00:32:05 +00:00
you 69182828b3 docs: add table of contents to deployment guide 2026-03-25 00:31:00 +00:00
you 9b80cc5923 docs: remove condescending tone from deployment guide 2026-03-25 00:30:22 +00:00
you ef1e84e3d9 docs: rewrite deployment guide for complete beginners
Added: what is Docker, how to install it, what is a server,
where to get a domain, how to open ports. Every command explained.
Assumes zero DevOps knowledge.
2026-03-25 00:28:46 +00:00
you b053d14874 docs: add deployment guide for Docker + auto HTTPS
Step-by-step for users with limited DevOps experience. Covers:
- Quick start (5 minutes to running)
- Connecting observers (public broker vs your own)
- Common gotchas: port 80 for ACME, MQTT security, DB backups,
  DNS before container, read-only config, skip internal HTTPS
- Customization and branding
- Troubleshooting table
- Architecture diagram
2026-03-25 00:27:09 +00:00
github-actions 97b78731ff ci: update test badges [skip ci] 2026-03-24 22:33:07 +00:00
you fba5649979 refactor: consolidate hop disambiguation — remove 3 duplicate implementations
- server.js disambiguateHops() now delegates to server-helpers.js
  (was a full copy of the same algorithm, ~70 lines removed)
- live.js resolveHopPositions() now delegates to shared HopResolver
  (was a standalone reimplementation, ~50 lines removed)
- HopResolver.init() called when live page loads/updates node data
- Net -106 lines, same behavior, single source of truth

All unit tests pass (241). E2E 13/16 (3 pre-existing Chromium crashes).
2026-03-24 22:19:16 +00:00
you 5182ea69a2 docs: add XP practices to AGENTS.md
Test-First, YAGNI, Refactor Mercilessly, Simple Design,
Pair Programming (subagent→review→push), CI as gate not cleanup,
10-Minute Build, Collective Code Ownership, Small Releases.

Each with concrete examples from today's failures.
2026-03-24 21:06:04 +00:00
you 3ccefb0fe8 docs: add engineering principles to AGENTS.md
DRY, SOLID, code reuse, dependency injection, testability,
type safety, performance. These were being violated repeatedly
(5 implementations of disambiguation, .toFixed on strings, etc).
Now explicitly codified as rules.
2026-03-24 21:03:07 +00:00
you 459d51f5a5 fix: re-run decollision on zoom regardless of hash labels
zoomend handler was gated on filters.hashLabels — decollision only
re-ran on zoom when hash labels were enabled. Now always re-renders
markers on zoom so pixel offsets stay correct at every zoom level.
2026-03-24 20:57:45 +00:00
you 863ee604be fix: re-run marker decollision on map resize
Added map.on('resize') handler that re-renders markers, recalculating
pixel-based decollision offsets for the new container size. Previously
only zoomend triggered re-render — resize left stale offsets.

Added E2E test verifying markers survive a viewport resize.
2026-03-24 20:55:39 +00:00
you ae7010ae0c docs: move diagrams to exec summary, compact horizontal layout
Smaller circle nodes, horizontal flowcharts, clear color coding
(red=ambiguous, green=known, blue=just resolved). Removed duplicate
diagrams from section 2.
2026-03-24 20:52:06 +00:00
you 75cf855a48 docs: add Mermaid diagrams for forward/backward pass disambiguation
Visual step-by-step showing why two passes are needed — forward
pass can't resolve hops at the start of the path, backward pass
catches them by anchoring from the right.
2026-03-24 20:49:29 +00:00
you af79428f4c docs: add hash prefix disambiguation documentation
Comprehensive documentation of how MeshCore Analyzer resolves
truncated hash prefixes (1-3 bytes) to node identities across
the entire codebase. Covers firmware encoding, server-side
disambiguation (3 implementations), client-side HopResolver,
live feed's independent implementation, and consistency analysis.

Notable findings:
- /api/resolve-hops has regional filtering that disambiguateHops() lacks
- live.js reimplements disambiguation independently without HopResolver
- Inline resolveHop() in analytics resolves hops without path context
- These are not bugs but worth knowing about for future refactoring
2026-03-24 20:42:58 +00:00
github-actions 19c90c5cf0 ci: update test badges [skip ci] 2026-03-24 20:34:30 +00:00
you 305da30b88 fix: run map.invalidateSize before marker decollision on every render
On SPA navigation, the map container may not have its final dimensions
when markers render, causing latLngToLayerPoint to return incorrect
pixel coordinates for decollision. This resulted in overlapping markers
that only resolved on a full page refresh.

Fix: call map.invalidateSize() at the start of every renderMarkers()
call, ensuring correct container dimensions before deconfliction runs.
2026-03-24 20:24:26 +00:00
you 383219b4cf test: add Number() casting tests for snr/rssi toFixed
6 tests covering string, number, null, negative, and integer
values through the Number(x).toFixed() pattern used across
observer-detail, home, traces, and live pages.
2026-03-24 20:18:51 +00:00
you d6ea3dd9fd fix: cast snr/rssi to Number before toFixed() — fixes crash on string values
Observer detail, home health timeline, and traces all called
.toFixed() on snr/rssi values that may be strings from the DB.
Wrapping in Number() matches what live.js already does.
2026-03-24 20:17:41 +00:00
you 14ff1821d6 fix: hash-based packet deduplication in Live feed
Root cause: addFeedItem had no dedup logic — each WS message created
a new feed entry regardless of hash. Dedup only worked when the
'Realistic propagation' toggle was ON (which buffers by hash before
calling animateRealisticPropagation). Default mode called animatePacket
directly for every observation, producing duplicate feed entries.

Fix: Added feedHashMap (hash -> {element, count, pkt, addedAt}) that
tracks recent feed items by packet hash. When a packet with a known
hash arrives within 30s, the existing feed item is updated in-place:
- Observation count badge incremented
- Item flashed and moved to top of feed
- No duplicate DOM element created

Also adds data-hash attribute to feed items for testability.

Tests: 5 new Playwright tests in test-live-dedup.js covering:
- Same hash different observers → single entry
- Different hashes → separate entries
- 5 rapid sequential duplicates → single entry with count 5
- Same hash same observer → still deduplicates
- Packets without hash → not deduplicated
2026-03-24 19:35:28 +00:00
you 1bdf41a631 fix: separate heatmap opacity controls for Map and Live pages
- Live page showHeatMap() now reads meshcore-live-heatmap-opacity from
  localStorage and applies it to the canvas element (was hardcoded 0.3)
- Customizer now has two clearly labeled sliders:
  🗺️ Nodes Map — controls the static map page heatmap
  📡 Live Map — controls the live page heatmap
- Each uses its own localStorage key (meshcore-heatmap-opacity vs
  meshcore-live-heatmap-opacity)
- Added E2E tests for live opacity persistence and dual slider existence
- 13/15 E2E tests pass locally (2 fail due to ARM chromium OOM after
  heavy live page tests — CI on x64 will handle them)

Closes #119 properly this time.
2026-03-24 19:25:28 +00:00
you 52d52af6ec fix: force-enable heat toggle when matrix mode is off
Recover from stale localStorage state where heat checkbox stayed
disabled even after matrix/ghosts mode was turned off. Explicitly
sets ht.disabled = false in the else branch of matrix init.

13/13 E2E tests pass locally.
2026-03-24 19:17:17 +00:00
github-actions 91f28e9fda ci: update test badges [skip ci] 2026-03-24 18:12:20 +00:00
you 16eb7ef07d fix: persist live page heat checkbox + add E2E test
Live page liveHeatToggle now saves to localStorage (meshcore-live-heatmap).
Map page was already fixed but live page was missed.
Added E2E test that verifies persistence across reload.

13/13 E2E tests pass locally.
2026-03-24 17:57:17 +00:00
you 325fdbe50e fix: heatmap opacity flash on new packet arrival
When new data arrived, toggleHeatmap() destroyed and recreated the
heat layer, causing a brief flash at full opacity before the CSS
opacity was applied via setTimeout. Now reuses the existing layer
via setLatLngs() for data updates, and hooks the 'add' event for
immediate opacity on first creation. No more flash.

All 12 E2E tests pass locally.
2026-03-24 17:53:29 +00:00
you dbb792bcbb test: add E2E tests for heat checkbox persistence and heatmap opacity
- Heat checkbox persists in localStorage across reload
- Heat checkbox is clickable (not disabled)
- Live page heat disabled when ghosts/matrix mode active
- Heatmap opacity value persists and applies to canvas

4 new tests, all verified locally before push.
2026-03-24 17:50:58 +00:00