Selaa lähdekoodia

README: rewrite to feature dashboard, add HTTP API table, default creds warning

Gogs 2 kuukautta sitten
vanhempi
sitoutus
a2e4a87d0c
1 muutettua tiedostoa jossa 189 lisäystä ja 80 poistoa
  1. 189 80
      README.md

+ 189 - 80
README.md

@@ -1,54 +1,80 @@
 # client2server
 
-> **Lightweight bi-directional event forwarder for OpenWrt routers**
+> **Bi-directional event forwarder + Apple-style dashboard for OpenWrt router fleets**
 
 [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
 
+Lightweight OpenWrt event forwarder with a real-time web dashboard.
+Routers stream DHCP, WiFi, and WAN events over WebSocket; the Go server
+fans them out to Redpanda and persists them to SQLite; a React/TypeScript
+SPA gives you live visibility and command control from any browser.
+
 ## Overview
 
 ```
-┌─────────────────────────────────────────────────────────────┐
-│                    client2server Architecture               │
-├─────────────────────────────────────────────────────────────┤
-│                                                              │
-│   OpenWrt Routers                                             │
-│       │                                                      │
-│       ▼ WebSocket :3843                                      │
-│   ┌──────────────────────────────────────────┐               │
-│   │   Caddy Load Balancer                    │               │
-│   │   - Health checks                       │               │
-│   │   - Auto-failover                       │               │
-│   └──────────────────┬───────────────────┘               │
-│                      │                                    │
-│          ┌───────────┴───────────┐                      │
-│          ▼                     ▼                      │
-│   ┌─────────────┐       ┌─────────────┐               │
-│   │  server1   │       │  server2   │               │
-│   │  (Go)      │       │  (Go)      │               │
-│   └─────┬───────┘       └─────┬───────┘               │
-│         │                  │                        │
-│         └────────┬─────────┘                        │
-│                  │                                 │
-│                  ▼                                 │
-│         ┌─────────────────┐                       │
-│         │   Redpanda     │  Events stored          │
-│         │   (Kafka)      │  Persisted             │
-│         └───────────────┘                       │
-│                                                             │
-└─────────────────────────────────────────────────────────────┘
+┌────────────────────────────────────────────────────────────────┐
+│                    client2server Architecture                   │
+├────────────────────────────────────────────────────────────────┤
+│                                                                 │
+│   OpenWrt Routers                                               │
+│       │  WebSocket :3843  +  Hotplug (curl)                    │
+│       ▼                                                          │
+│   ┌──────────────┐                                              │
+│   │ Caddy LB     │  ────────────────┐                          │
+│   │  :80/443 UI  │  :3843 API/WS   │                          │
+│   └──────┬───────┘  └──────┬────────┘                          │
+│          │                  │                                    │
+│          ▼                  ▼                                    │
+│   ┌─────────────┐    ┌─────────────┐    ┌─────────────┐      │
+│   │  Dashboard  │    │  server1   │    │  server2    │      │
+│   │  (React)    │    │  (Go)      │    │  (Go)       │      │
+│   │  /login     │    │  SQLite    │    │  SQLite     │      │
+│   │  /routers   │    │  JWT       │    │  JWT        │      │
+│   │  /events    │    │  SSE       │    │  SSE        │      │
+│   │  /commands  │    │  WebSocket │    │  WebSocket  │      │
+│   │  /alerts    │    └──────┬──────┘    └──────┬──────┘      │
+│   └─────────────┘            │                  │              │
+│                              └────────┬─────────┘              │
+│                                       │                          │
+│                                       ▼                          │
+│                              ┌─────────────────┐               │
+│                              │   Redpanda     │  Kafka protocol │
+│                              │   :9092        │  Persisted     │
+│                              └─────────────────┘               │
+│                                                                 │
+└────────────────────────────────────────────────────────────────┘
 ```
 
 ## Features
 
-- ✅ **WebSocket connection** with auto-reconnect
-- ✅ **Local buffer** (store-and-forward while offline)
-- ✅ **DHCP lease events** (new/expire)
-- ✅ **WAN link state** (up/down monitoring)
-- ✅ **WAN IP changes** (new ISP detection)
-- ✅ **Bidirectional** (receive commands from server)
-- ✅ **Command executor** (uci_set, shell, reboot, status)
-- ✅ **Load balancer** ready (Caddy)
-- ✅ **Event persistence** (Redpanda)
+### Server (Go)
+- ✅ **WebSocket** connection with auto-reconnect from routers
+- ✅ **HTTP POST** fallback for hotplug scripts
+- ✅ **Redpanda** (Kafka) event fan-out for downstream consumers
+- ✅ **SQLite** persistence (events, commands, alerts, users)
+- ✅ **JWT auth** with role-based access (system_admin / project_admin / user)
+- ✅ **Server-Sent Events** for real-time dashboard feed
+- ✅ **Time-series metrics** (1-min buckets, 24h retention)
+- ✅ **Alerts** for offline routers (auto-clear on reconnect)
+- ✅ **Command queue** for offline routers (auto-flush on reconnect)
+- ✅ **Idempotency** (5-min TTL on command IDs)
+- ✅ **Load balanced** behind Caddy (2× Go servers, health checks)
+
+### Router client (Lua)
+- ✅ **Hotplug-driven** WiFi connect/disconnect + DHCP lease events (instant, no polling)
+- ✅ **luv-based** async state-diff for WAN link, DHCP renew, IP changes (≤1s latency)
+- ✅ **UCI-configurable** server URL, token, check intervals
+- ✅ **Event buffer** for store-and-forward while offline
+- ✅ **Bidirectional** command execution (uci_set, shell, reboot, wifi_restart, status)
+
+### Dashboard (React)
+- ✅ **Apple-style** dark theme (frosted glass surfaces, gradient hero, tabular stat numbers)
+- ✅ **Live event feed** via Server-Sent Events
+- ✅ **Real-time charts** of events and commands over time
+- ✅ **Router list** with online/offline status and queued command count
+- ✅ **Command console** with target router picker and one-click reboot/wifi_restart/status
+- ✅ **Alerts inbox** with one-click acknowledgement
+- ✅ **Event history** with router-id and event-type filters
 
 ## Quick Start
 
@@ -59,8 +85,18 @@
 git clone https://git3.techno-world.net/lrosales/client2server.git
 cd client2server
 
+# Set secrets (use any 32+ byte strings)
+cat > .env <<EOF
+TOKEN=replace-with-router-shared-secret
+JWT_SECRET=$(openssl rand -hex 32)
+EOF
+
 # Start all services
-TOKEN=your-secret-token docker-compose up -d
+docker-compose up -d
+
+# Open the dashboard
+open http://localhost/
+# Login: admin / admin  (CHANGE IN PRODUCTION)
 
 # Check status
 docker-compose ps
@@ -69,12 +105,22 @@ docker-compose ps
 ### Option 2: Manual (Development)
 
 ```bash
-# Go server
+# 1. Go server (with Redpanda running locally)
 cd server
 go build -o server .
-REDPANDA_BROKERS=localhost:9092 TOKEN=*** ./server
-
-# On OpenWrt router (copy all files)
+REDPANDA_BROKERS=localhost:9092 \
+  TOKEN=replace-me \
+  JWT_SECRET=replace-me \
+  PORT=3843 \
+  ./server
+
+# 2. Dashboard (Vite dev server)
+cd dashboard
+npm install
+VITE_API_TARGET=http://localhost:3843 npm run dev
+# → http://localhost:5173
+
+# 3. On OpenWrt router (copy all files)
 scp package/src/client2server-unified.lua root@router:/usr/sbin/
 scp package/files/etc/init.d/client2server root@router:/etc/init.d/
 scp package/files/etc/config/client2server root@router:/etc/config/
@@ -88,17 +134,21 @@ ssh root@router "/etc/init.d/client2server enable && /etc/init.d/client2server s
 
 | Service | Port | Protocol |
 |----------|------|----------|
-| WebSocket + HTTP | 3843 | WS + HTTP (unified) |
-| Redpanda | 9092 | Kafka (Redpanda) |
+| Dashboard (web UI) | 80 / 443 | HTTP(S) |
+| WebSocket + HTTP API | 3843 | WS + HTTP (unified) |
+| Redpanda Kafka | 9092 | Kafka (Redpanda) |
+| Dashboard dev (Vite) | 5173 | HTTP (local dev only) |
 
 ## Configuration
 
 ### Environment Variables
 
 ```bash
-TOKEN=***                # Authentication token
-REDPANDA_BROKERS=redpanda:9092   # Redpanda address
-PORT=3843               # Server port
+TOKEN=replace-with-shared-secret      # Legacy router/hotplug shared token
+JWT_SECRET=replace-with-32+bytes     # JWT signing key for dashboard auth
+REDPANDA_BROKERS=redpanda:9092        # Comma-separated Redpanda addresses
+PORT=3843                            # Server listen port
+DB_PATH=/var/lib/c2s/client2server.db # SQLite path
 ```
 
 ### UCI Config (on Router)
@@ -110,43 +160,63 @@ config client2server 'general'
 
 config server
     option url 'wss://your-server.com:3843'
-    option token '***'
+    option token 'replace-me'
 
 config wan
     option interface 'wan'
     option device 'eth0'
 ```
 
+## HTTP API
+
+All endpoints on `:3843`. The dashboard uses JWT (from `/api/auth/login`); routers use the legacy shared `TOKEN` directly.
+
+| Method | Path | Auth | Purpose |
+|--------|------|------|---------|
+| GET  | `/health` | none | Liveness + router stats |
+| POST | `/api/auth/login` | none | `{username,password}` → JWT |
+| GET  | `/api/auth/me` | JWT | Current user info |
+| POST | `/api/events` | token or JWT | Ingest event from router/hotplug |
+| GET  | `/api/events/list` | JWT | Historical events (filterable) |
+| GET  | `/api/events/stream` | token or JWT | Server-Sent Events live feed |
+| GET  | `/api/routers` | none | Known routers |
+| POST | `/api/command` | token or JWT | Send command, awaits result |
+| GET  | `/api/commands` | JWT | Command history |
+| GET  | `/api/metrics?since=1h` | JWT | Time-series metrics |
+| GET  | `/api/alerts?unack=1` | JWT | Alerts list |
+| POST | `/api/alerts/{id}/ack` | JWT | Acknowledge alert |
+| GET  | `/ws` | token | WebSocket from router |
+
 ## Commands
 
 Send from server to router via WebSocket or HTTP API:
 
 ```bash
-# UCI set
+# Reboot
 curl -X POST http://localhost:3843/api/command \
   -H "Authorization: Bearer TOKEN" \
-  -d '{"router_id":"router1","command":"uci_set","args":{"config":"network","section":"lan","option":"ipaddr","value":"192.168.1.1"}}'
+  -d '{"router_id":"router1","command":"reboot"}'
 
-# Shell
+# UCI set
 curl -X POST http://localhost:3843/api/command \
   -H "Authorization: Bearer TOKEN" \
-  -d '{"router_id":"router1","command":"shell","args":{"command":"reboot"}}'
+  -d '{"router_id":"router1","command":"uci_set","args":{"config":"network","section":"lan","option":"ipaddr","value":"192.168.1.1"}}'
 
-# Reboot
+# Shell
 curl -X POST http://localhost:3843/api/command \
   -H "Authorization: Bearer TOKEN" \
-  -d '{"router_id":"router1","command":"reboot"}'
+  -d '{"router_id":"router1","command":"shell","args":{"command":"uptime"}}'
 ```
 
 ### Available Commands
 
-| Command | Description | Arguments |
-|---------|-------------|-----------|
-| `uci_set` | Set UCI config value | `config`, `section`, `option`, `value` |
-| `shell` | Run shell command | `command` |
-| `reboot` | Reboot router | - |
-| `wifi_restart` | Restart WiFi | - |
-| `status` | Get router status | - |
+| Command | Description | Arguments | Dangerous |
+|---------|-------------|-----------|-----------|
+| `reboot` | Reboot router | — | ⚠️ |
+| `wifi_restart` | Restart WiFi | — | |
+| `status` | Get router status | — | |
+| `shell` | Run shell command | `command` | ⚠️ |
+| `uci_set` | Set UCI config value | `config,section,option,value` | ⚠️ |
 
 ## Events
 
@@ -168,24 +238,39 @@ Router sends these events to server:
 ```
 client2server/
 ├── ARCHITECTURE.md         # Architecture docs
-├── README.md             # This file
-├── MEMORY.md             # Project memory (AI/agent context)
-├── Caddyfile            # Caddy load balancer
-├── docker-compose.yml   # Full stack
-├── package/
-│   ├── Makefile         # IPK build
+├── README.md               # This file
+├── MEMORY.md               # Project memory (AI/agent context)
+├── Caddyfile               # Caddy load balancer
+├── docker-compose.yml      # Full stack
+├── package/                # OpenWrt IPK build
+│   ├── Makefile
 │   ├── src/
 │   │   └── client2server-unified.lua  # Router script (canonical)
 │   ├── files/
 │   │   ├── etc/init.d/client2server
 │   │   └── etc/config/client2server
 │   └── hotplug/
-│       ├── 01-wifi      # /etc/hotplug.d/wireless/ - instant WiFi events
-│       └── 02-dhcp      # /etc/hotplug.d/dhcp/     - instant DHCP events
-└── server/
-    ├── main.go
-    ├── go.mod
-    └── Dockerfile
+│       ├── 01-wifi         # /etc/hotplug.d/wireless/ - instant WiFi events
+│       └── 02-dhcp         # /etc/hotplug.d/dhcp/     - instant DHCP events
+├── server/                 # Go server
+│   ├── main.go             # WS handler + HTTP API + ingestEvent
+│   ├── auth.go             # JWT + scrypt + login + middleware
+│   ├── consumer.go         # Redpanda → SQLite consumer
+│   ├── metrics.go          # 1-min buckets, 24h retention
+│   ├── sse.go              # SSE broadcaster
+│   ├── store.go            # SQLite (users, events, commands, alerts)
+│   ├── go.mod
+│   └── Dockerfile
+└── dashboard/              # React SPA (Apple-style)
+    ├── src/
+    │   ├── App.tsx                 # Routes + auth gate
+    │   ├── styles.css              # Apple design tokens
+    │   ├── lib/{api,types,sse}.ts
+    │   ├── components/{Shell,ui}.tsx
+    │   └── pages/{Login,Overview,Routers,Events,Commands,Alerts}.tsx
+    ├── screenshots/                # Visual reference (6 retina PNGs)
+    ├── Dockerfile                  # Node builder + Caddy runtime
+    └── Caddyfile.production        # /api + /ws proxy, SPA fallback
 ```
 
 ## Building IPK
@@ -201,6 +286,16 @@ make package/client2server-unified/ipk
 # bin/packages/*/client2server-unified_*.ipk
 ```
 
+## Building Dashboard
+
+```bash
+cd dashboard
+npm install
+npm run build       # → dist/ (static files)
+# Or development server:
+VITE_API_TARGET=http://localhost:3843 npm run dev
+```
+
 ## Monitoring
 
 ```bash
@@ -218,13 +313,27 @@ curl http://localhost:3843/api/routers
 curl http://localhost:3843/api/events
 ```
 
+## Default Credentials
+
+The Go server creates a default admin user on first run:
+
+- **Username:** `admin`
+- **Password:** `admin`
+
+**Change this immediately in production.** Connect to the SQLite database
+and replace the password hash, or extend the `/api/auth` endpoints to
+expose a user-management UI.
+
 ## Security
 
-- Token-based auth on both WS and HTTP
-- Use TLS (wss://) in production
-- Firewalls: Only allow port 3843 from router network
-- Redpanda: Enable auth for production
+- **JWT auth** for dashboard access (HS256, 24h tokens)
+- **Legacy shared token** for router/hotplug access (simpler on minimal OpenWrt)
+- **Use TLS** (`wss://`, `https://`) in production
+- **Firewalls**: Only allow port 3843 from router network
+- **Redpanda**: Enable auth for production
+- **JWT_SECRET**: Must be 32+ random bytes in production
+- **Bind Caddy to a reverse proxy** (Cloudflare, nginx) for HTTPS termination
 
 ## License
 
-MIT - Luis Rosales 2026
+MIT - Luis Rosales 2026