mirror of
https://github.com/MeshCore-Beacon/beacon-web.git
synced 2026-10-23 15:42:38 +00:00
d5deb1e042ae8b455483c2ebb6f24d827c044744
The donut suits node types after all — few slices, and the total reads better in the ring than on three bars. The center text is a title block with textAlign center now, which ECharts actually centers (the old graphic text anchored its left edge at the ring's x and clipped); TitleComponent registered in the tree-shaken setup. Payload types stays a bar chart. Leaderboard names (top nodes/observers, radio presets) are left-aligned at the card edge and ellipsized instead of running off the grid's left side.
BEACON Web
Real-time LoRa mesh packet analyzer. Desktop-first, dark-mode-primary, dense information display for radio hobbyists.
Built with React 19, TypeScript, Tailwind CSS 4, TanStack Query, and TanStack Virtual.
Deployment
1. Copy the docker/ folder to your server
scp -r docker/ user@your-server:/opt/docker/beacon-web
2. Create a .env file
cd /opt/docker/beacon-web
cat > .env << 'EOF'
DOMAIN=dev.meshcore.ca
VITE_API_BASE=https://dev.meshcore.ca/api/v1
VITE_WS_URL=wss://dev.meshcore.ca/ws
EOF
| Variable | Description |
|---|---|
DOMAIN |
Domain for HTTPS (Caddy auto-provisions Let's Encrypt certs) |
VITE_API_BASE |
Backend REST API base URL |
VITE_WS_URL |
Backend WebSocket URL |
3. Authenticate with GitHub Container Registry
docker login ghcr.io -u YOUR_GITHUB_USERNAME
Use a Personal Access Token (classic) with read:packages scope as the password. You only need to do this once.
4. Start the services
docker compose up -d
Caddy will automatically obtain a TLS certificate for your domain. Ensure DNS is pointed at your server before starting.
Local Development
npm install
cp .env.example .env # edit with your backend URLs
npm run dev # starts Vite dev server at http://localhost:5173
Commands
| Command | Description |
|---|---|
npm run dev |
Start dev server |
npm run build |
Type-check and build for production |
npm run preview |
Preview production build locally |
npm run lint |
Run ESLint |
npx vitest run |
Run tests |
npx tsc --noEmit |
Type-check without emitting |
Project Structure
docker/
docker-compose.yml # production deployment compose file
Caddyfile # internal Caddy config (static file serving)
Caddyfile.proxy # reverse proxy config (HTTPS termination)
docker-entrypoint.sh # runtime env var injection
Dockerfile # multi-stage build (Node + Caddy)
src/
api/
client.ts # typed REST client (fetch wrapper)
ws-manager.ts # WebSocket connection, reconnect, subscription management
components/ # shared UI components
features/ # feature modules (packets, nodes, channels, map, stats)
hooks/ # React hooks (region, theme, WebSocket)
lib/ # constants, formatters, theme utilities
types/ # TypeScript types and enums
App.tsx # providers + routing + WS init
main.tsx # entry point
index.css # Tailwind setup, theme tokens, animations
Architecture
- Region-driven: All data queries and WS subscriptions are scoped to an IATA region code. Changing region resets the cache and resubscribes.
- Live + historical merge: WebSocket pushes live packets into a
LivePacketStorebuffer (capped at 500). Historical data comes from cursor-paginated REST viauseInfiniteQuery(max 20 pages). Both are merged and deduped at render time. - Client-side filtering: Filters are not part of the query key. The cache holds all packets for the current region; filters are applied via
useMemo. Toggling a filter is instant with no refetch. - Reconnect with jitter: Exponential backoff with +/-25% random jitter prevents thundering herd on server bounce.
Contributing
See CONTRIBUTING.md. All contributors are welcome — please also read the Code of Conduct. To report a security issue, see SECURITY.md.
License
Licensed under the GNU Affero General Public License v3.0 or later (AGPL-3.0-or-later). See LICENSE for the full text and CONTRIBUTORS.md for acknowledgements.
Languages
TypeScript
57.3%
HTML
42.2%
CSS
0.2%
Shell
0.2%
JavaScript
0.1%