Files
blekin/doc/plan/implementation-feature-port-mapping.md

122 lines
5.2 KiB
Markdown

# KVM port configuration and switching
## Context
The Belkin OmniView fronts an Avocent KVM switch with up to 16 ports. The legacy web interface at `http://10.3.0.130` allows port naming, hotkey assignment, visibility toggling, and active port switching. Users currently need to visit that interface separately. We want blekin to fully replace it — starting with port management, with a navigation structure that supports adding more settings pages later.
## Belkin device API (from HTML form inspection)
**GET /kvm.asp** — returns HTML form with current port config:
- `ECG_kvm_nr_ports`: port count (1,2,4,8,12,16,24,32,48,64)
- `ECG_key_pause_duration`: ms (default 100)
- Per port N (0-indexed): `ECG_kvm_portname_N`, `ECG_kvm_hotkey_N`, `ECG_kvm_show_in_rc_N`
- `kvm_active_port_0`: currently selected port
**POST /kvm.asp** — save port config (same fields + `action_apply=Apply`)
**POST /home2.asp** — switch active port: `kvm_active_port_0=<0-15>&action_switch_0=Switch`
All requests require `pp_session_id` cookie.
## Changes
### Backend: persist session cookie
Modify `AppState` in `main.rs` to hold the device session cookie:
```rust
pub session_cookie: Arc<tokio::sync::RwLock<Option<String>>>,
```
Update `login.rs` to store the cookie into shared state after successful auth.
### Backend: new REST endpoints (`crates/ericrfb-proxy/src/kvm.rs`)
| Endpoint | Method | Purpose |
|----------|--------|---------|
| `/api/kvm/ports` | GET | Fetch port config (scrape kvm.asp, return JSON) |
| `/api/kvm/ports` | PUT | Save port config (build form, POST to kvm.asp) |
| `/api/kvm/switch` | POST | Switch active port (POST to home2.asp) |
**GET /api/kvm/ports response:**
```json
{
"port_count": 16,
"key_pause_duration": 100,
"active_port": 0,
"ports": [
{ "index": 0, "name": "oolon", "hotkey": "", "show_in_rc": true },
...
]
}
```
HTML scraping: simple string/regex matching on the predictable firmware HTML (extract `value="..."` from named inputs, `selected` from dropdowns, `checked` from checkboxes).
### Frontend: app shell with navigation (`crates/ericrfb-frontend/src/shell.ts`)
After login, render a shell layout:
```
+----------+----------------------------------+
| sidebar | content area |
| | |
| Console | (active page rendered here) |
| Ports | |
| | |
+----------+----------------------------------+
```
Each page module exports `mount(container)` and `unmount()`. The shell swaps them on nav clicks. This pattern supports adding Virtual Media, Users, Device Settings etc. later without restructuring.
### Frontend: refactor console into page module
Move `console.ts` logic into a `pages/console.ts` with mount/unmount pattern:
- `mount(el, session)`: creates toolbar + canvas, connects WebSocket
- `unmount()`: tears down WebSocket, removes event listeners
Add port switcher `<select>` to the console toolbar (fetches port list from `/api/kvm/ports`, calls `/api/kvm/switch` on change, triggers reconnect).
### Frontend: port config page (`crates/ericrfb-frontend/src/pages/ports.ts`)
Editable table of ports with name, hotkey, show-in-console fields. Port count dropdown and key-pause input at the top. Save button PUTs to `/api/kvm/ports`. Each row has a Switch button that POSTs to `/api/kvm/switch`. Active port highlighted.
## Files
### New
| File | Purpose |
|------|---------|
| `crates/ericrfb-proxy/src/kvm.rs` | GET/PUT ports, POST switch, HTML scraping |
| `crates/ericrfb-frontend/src/shell.ts` | App shell, sidebar nav, page routing |
| `crates/ericrfb-frontend/src/pages/console.ts` | Console page (refactored from console.ts) |
| `crates/ericrfb-frontend/src/pages/ports.ts` | Port configuration page |
### Modified
| File | Changes |
|------|---------|
| `crates/ericrfb-proxy/src/main.rs` | session_cookie in AppState, new routes, `mod kvm` |
| `crates/ericrfb-proxy/src/login.rs` | Store cookie in shared state after auth |
| `crates/ericrfb-proxy/Cargo.toml` | Add `regex = "1"` |
| `crates/ericrfb-frontend/src/main.ts` | After login → mountShell() instead of startConsole() |
| `crates/ericrfb-frontend/src/login.ts` | Success callback → shell mount |
| `crates/ericrfb-frontend/src/style.css` | Shell layout, sidebar, ports page styles |
| `crates/ericrfb-frontend/src/console.ts` | Delete or re-export from pages/console.ts |
## Implementation order
1. Backend: session cookie persistence in AppState + login.rs
2. Backend: kvm.rs with three endpoints, test with curl
3. Frontend: shell.ts with sidebar navigation
4. Frontend: refactor console.ts → pages/console.ts with mount/unmount
5. Frontend: pages/ports.ts wired to API
6. Frontend: port switcher dropdown in console toolbar
## Verification
- `cargo test && cargo clippy` pass
- `npx tsc --noEmit` passes
- `curl /api/kvm/ports` returns correct JSON matching device state
- `curl -X POST /api/kvm/switch -d '{"port":0}'` switches the active port
- `curl -X PUT /api/kvm/ports` with config JSON updates port names on device
- Frontend: navigate between Console and Ports pages without breaking WebSocket
- Frontend: switch port from console toolbar, console reconnects to new port
- Frontend: edit port names in Ports page, save, verify on device