# BEACON Web

[![CodeQL](https://github.com/MeshCore-Beacon/beacon-web/actions/workflows/codeql.yml/badge.svg)](https://github.com/MeshCore-Beacon/beacon-web/actions/workflows/codeql.yml)
[![CI](https://github.com/MeshCore-Beacon/beacon-web/actions/workflows/ci.yml/badge.svg)](https://github.com/MeshCore-Beacon/beacon-web/actions/workflows/ci.yml)
[![Docker](https://github.com/MeshCore-Beacon/beacon-web/actions/workflows/docker-publish.yml/badge.svg)](https://github.com/MeshCore-Beacon/beacon-web/actions/workflows/docker-publish.yml)

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

```bash
scp -r docker/ user@your-server:/opt/docker/beacon-web
```

### 2. Create a `.env` file

```bash
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. Start the services

```bash
docker compose up -d
```

The images are public on GitHub Container Registry — no `docker login` required.
If a pull fails with `403 Forbidden`, the package visibility has regressed to
Private; a maintainer needs to set it back to Public (see the troubleshooting note
in [beacon-docs](https://github.com/MeshCore-Beacon/beacon-docs)).

Caddy will automatically obtain a TLS certificate for your domain. Ensure DNS is pointed at your server before starting.

## Local Development

```bash
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 `LivePacketStore` buffer (capped at 500). Historical data comes from cursor-paginated REST via `useInfiniteQuery` (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](CONTRIBUTING.md). All contributors are welcome — please
also read the [Code of Conduct](CODE_OF_CONDUCT.md). To report a security issue,
see [SECURITY.md](SECURITY.md).

## License

Licensed under the GNU Affero General Public License v3.0 or later
(AGPL-3.0-or-later). See [LICENSE](LICENSE) for the full text and
[CONTRIBUTORS.md](CONTRIBUTORS.md) for acknowledgements.
