73e4fc005e
Server UI gains Update on stack cards, ComfyUI Models tab with workflow scan and downloads, and centralized comfyui_config. Model catalog and download scripts move from stacks/comfyui to server-ui so ComfyUI stays a minimal Docker wrapper for easier image updates.
141 lines
4.7 KiB
Markdown
141 lines
4.7 KiB
Markdown
# Server UI — CLI (terminal PTY)
|
|
|
|
**Data:** 2026-07-05
|
|
**Status:** wdrożone (bugfix FitAddon: 2026-07-05; pamięć sesji: 2026-07-05)
|
|
|
|
Zakładka **CLI** w panelu Server UI (`:8091`) — interaktywny shell bash w przeglądarce (PTY + xterm.js).
|
|
|
|
## UI
|
|
|
|
- Zakładka **CLI** (pierwsza, przed Pliki)
|
|
- Hash URL: `http://<host>:8091/#cli`
|
|
- Wymaga **API Key** w toolbarze (WebSocket nie obsługuje nagłówka `X-API-Key`)
|
|
|
|
## Architektura
|
|
|
|
```mermaid
|
|
sequenceDiagram
|
|
participant Browser
|
|
participant xterm as xterm_js
|
|
participant WS as WS_api_cli_ws
|
|
participant PTY as cli_pty_py
|
|
participant Shell as bash
|
|
|
|
Browser->>xterm: klawiatura
|
|
xterm->>WS: dane terminala
|
|
WS->>PTY: zapis master_fd
|
|
PTY->>Shell: stdin
|
|
Shell->>PTY: stdout/stderr
|
|
PTY->>WS: odczyt master_fd
|
|
WS->>xterm: output
|
|
```
|
|
|
|
## WebSocket
|
|
|
|
| Parametr | Wartość |
|
|
|----------|---------|
|
|
| Ścieżka | `/api/cli/ws` |
|
|
| Auth | `?api_key=<API_KEY>` (query string) |
|
|
| Reattach | `?session_id=<uuid>` (opcjonalnie, z `sessionStorage`) |
|
|
| Protokół I/O | surowe bajty terminala (text/binary frames) |
|
|
| Resize | JSON: `{"type":"resize","cols":120,"rows":40}` |
|
|
| Zamknij sesję | JSON: `{"type":"close"}` (klient → serwer) |
|
|
|
|
### Handshake sesji (serwer → klient)
|
|
|
|
| Payload | Znaczenie |
|
|
|---------|-----------|
|
|
| `{"type":"session","id":"<uuid>"}` | Nowa sesja — ID zapisane w `sessionStorage` (`server-ui-cli-session-id`) |
|
|
| `{"type":"replay","complete":true}` | Koniec replay bufora po reattach |
|
|
|
|
### Przykład (wscat)
|
|
|
|
```bash
|
|
API_KEY=$(sudo grep ^API_KEY= /opt/control-plane/.env | cut -d= -f2)
|
|
|
|
# wymaga: npm i -g wscat
|
|
wscat -c "ws://127.0.0.1:8091/api/cli/ws?api_key=${API_KEY}"
|
|
```
|
|
|
|
Bez klucza na bind LAN → połączenie zamknięte z kodem `1008`.
|
|
|
|
## Konfiguracja
|
|
|
|
W [`/opt/control-plane/.env`](/opt/control-plane/.env) (opcjonalnie):
|
|
|
|
| Zmienna | Domyślnie | Opis |
|
|
|---------|-----------|------|
|
|
| `CLI_ENABLED` | `1` | `0` = WebSocket odrzucany |
|
|
| `CLI_SHELL` | `/bin/bash` | Shell PTY (np. `/bin/bash -l`) |
|
|
| `CLI_DEFAULT_CWD` | `$HOME` usługi | Katalog startowy |
|
|
| `CLI_MAX_SESSIONS` | `5` | Limit równoległych sesji PTY (bash) |
|
|
| `CLI_SESSION_TTL` | `300` | Sekundy utrzymania odłączonej sesji przed kill |
|
|
|
|
## Pamięć sesji
|
|
|
|
- **Przełączanie zakładek** — WebSocket i bash pozostają aktywne (bez `disconnectCli`)
|
|
- **F5 / reload** — `session_id` z `sessionStorage` → reattach do tego samego bash (bufor ~256 KB replay)
|
|
- **Połącz ponownie** — wysyła `{"type":"close"}`, czyści `sessionStorage`, nowy bash
|
|
- **Zamknięcie karty** — serwer utrzymuje PTY przez `CLI_SESSION_TTL`, potem zwalnia slot
|
|
|
|
```mermaid
|
|
sequenceDiagram
|
|
participant Browser
|
|
participant SS as sessionStorage
|
|
participant WS as WebSocket
|
|
participant Mgr as CliSessionManager
|
|
participant Bash as bash
|
|
|
|
Browser->>WS: connect api_key
|
|
WS->>Mgr: create_session
|
|
Mgr->>Bash: spawn PTY
|
|
Mgr-->>Browser: session id
|
|
Browser->>SS: zapisz session_id
|
|
Note over Browser,Bash: przełączenie zakładki — WS żyje
|
|
Browser->>Browser: F5 reload
|
|
Browser->>SS: odczyt session_id
|
|
Browser->>WS: connect session_id
|
|
WS->>Mgr: attach_session
|
|
Mgr-->>Browser: replay + live output
|
|
```
|
|
|
|
## Uprawnienia POSIX
|
|
|
|
Server UI działa jako **`tomasz-syn-grzegorza`** (`server-ui.service`):
|
|
|
|
- `docker ps`, edycja repo, `/data/apps/` — OK
|
|
- `sudo` — interaktywnie (hasło użytkownika w terminalu)
|
|
- root-only pliki — odmowa jak w SSH
|
|
|
|
## Kod
|
|
|
|
| Plik | Rola |
|
|
|------|------|
|
|
| [`stacks/server-ui/cli_pty.py`](../stacks/server-ui/cli_pty.py) | `CliSessionManager` — PTY, detach/attach, bufor outputu |
|
|
| [`stacks/server-ui/app.py`](../stacks/server-ui/app.py) | WebSocket `/api/cli/ws`, mount `/static` |
|
|
| [`stacks/server-ui/static/index.html`](../stacks/server-ui/static/index.html) | Zakładka CLI |
|
|
| [`stacks/server-ui/static/vendor/xterm/`](../stacks/server-ui/static/vendor/xterm/) | xterm.js + FitAddon (vendored; UMD: `new FitAddon.FitAddon()`) |
|
|
|
|
## Bezpieczeństwo
|
|
|
|
- Pełny shell przez LAN = **wysokie ryzyko** — wymagany `API_KEY` przy `SERVER_UI_HOST=0.0.0.0`
|
|
- `CLI_ENABLED=0` jako kill switch
|
|
- `CLI_MAX_SESSIONS` ogranicza liczbę równoległych bashów (PTY, nie połączeń WS)
|
|
- Brak whitelisty komend (właściciel serwera)
|
|
- Jawne zamknięcie: przycisk **Połącz ponownie** lub `{"type":"close"}`
|
|
- Odłączenie WS (reload) → detach + TTL; reattach w tej samej karcie przeglądarki
|
|
|
|
## Deploy
|
|
|
|
```bash
|
|
sudo rsync -a --exclude '.venv' --exclude '__pycache__' \
|
|
stacks/server-ui/ /opt/server-ui/
|
|
sudo systemctl restart server-ui
|
|
```
|
|
|
|
Tutorial użytkownika: [`manual-tutorial/10-server-ui-cli.md`](../manual-tutorial/10-server-ui-cli.md)
|
|
|
|
## Znane problemy / bugfix
|
|
|
|
Pusty terminal przy działającym API Key: [`SERVER-UI-CLI-BUGFIX.md`](SERVER-UI-CLI-BUGFIX.md)
|