359afb3a59
Infrastructure configs for GMKtec K11 (Docker, vLLM, LocalAI, ComfyUI, control-plane, gpu-fan agent, Server UI with CLI/file explorer/GPU fan curve). Co-authored-by: Cursor <cursoragent@cursor.com>
174 lines
4.6 KiB
Markdown
174 lines
4.6 KiB
Markdown
# Server UI — edycja portów stacków
|
||
|
||
**Data:** 2026-07-05
|
||
**Host:** gmktec-k11
|
||
**Kontekst:** Linki „Otwórz :PORT” w Server UI brały port z `stacks.yaml`, podczas gdy Docker mapował port z osobnego `stacks/<name>/.env`. Wdrożono jeden source of truth w `.env` stacku + edycję w UI.
|
||
|
||
**Powiązane:**
|
||
- [`stacks/server-ui/`](../stacks/server-ui/)
|
||
- [`coding-agent/SERVER-UI-ARCHITECTURE-RESEARCH.md`](SERVER-UI-ARCHITECTURE-RESEARCH.md)
|
||
- [`coding-agent/CONVENTIONS.md`](CONVENTIONS.md)
|
||
|
||
---
|
||
|
||
## 1. Executive summary
|
||
|
||
Server UI odczytuje i zapisuje port hosta w pliku `stacks/<name>/.env` (np. `LOCALAI_PORT=8070`). Karty stacków mają pole numeryczne i przycisk **Zapisz port**; po zapisie uruchamiany jest `docker compose up -d --force-recreate` gdy stack działa. Nowa usługa wymaga tylko wpisu w `stacks.yaml` z polami `port_env`, `port_default`, `port_editable`.
|
||
|
||
---
|
||
|
||
## 2. As-is / to-be
|
||
|
||
| Aspekt | Przed | Po |
|
||
|--------|-------|-----|
|
||
| Źródło portu w linku UI | `stacks.yaml` → `ui_port` (statyczny) | `stacks/<name>/.env` → `port_env` |
|
||
| Edycja portu | ręcznie w `.env` + restart compose | UI + `PATCH /api/stacks/{id}/port` |
|
||
| Nowa usługa | hardcode w JS | metadane w `stacks.yaml` |
|
||
| NPMPlus | link :81 | read-only (host network) |
|
||
|
||
```mermaid
|
||
flowchart LR
|
||
yaml[stacks.yaml port_env metadata]
|
||
env[stack .env]
|
||
api[Server UI API]
|
||
ui[Stack card port input]
|
||
compose[docker compose force-recreate]
|
||
yaml --> api
|
||
env --> api
|
||
api --> ui
|
||
api -->|PATCH| env
|
||
env --> compose
|
||
```
|
||
|
||
---
|
||
|
||
## 3. Konfiguracja stacków (`stacks.yaml`)
|
||
|
||
| Stack | `port_env` | `port_default` | `port_editable` |
|
||
|-------|------------|----------------|-----------------|
|
||
| localai | `LOCALAI_PORT` | 8080 | true |
|
||
| comfyui | `COMFYUI_PORT` | 8188 | true |
|
||
| vllm | `VLLM_PORT` | 8000 | true |
|
||
| npmplus | — | 81 | false |
|
||
|
||
**Nowa usługa** — dodaj wpis:
|
||
|
||
```yaml
|
||
- id: myservice
|
||
name: My Service
|
||
compose_dir: myservice
|
||
profile: myservice
|
||
container: myservice
|
||
ui_port: 9000 # fallback gdy brak w .env
|
||
port_env: MYSERVICE_PORT
|
||
port_default: 9000
|
||
port_editable: true
|
||
gpu: false
|
||
```
|
||
|
||
W `docker-compose.yml` stacku: `"${MYSERVICE_PORT:-9000}:9000"`.
|
||
|
||
---
|
||
|
||
## 4. API
|
||
|
||
### `GET /api/stacks`
|
||
|
||
Każdy stack zawiera dodatkowo:
|
||
|
||
```json
|
||
{
|
||
"ui_port": 8070,
|
||
"port_env": "LOCALAI_PORT",
|
||
"port_editable": true,
|
||
"port_default": 8080,
|
||
"published_port": 8070,
|
||
"port_pending_restart": false
|
||
}
|
||
```
|
||
|
||
`port_pending_restart`: kontener działa, ale opublikowany port ≠ wartość w `.env`.
|
||
|
||
### `PATCH /api/stacks/{stack_id}/port`
|
||
|
||
**Auth:** `X-API-Key` (jak start/stop)
|
||
|
||
**Body:**
|
||
```json
|
||
{ "port": 8070, "recreate": true }
|
||
```
|
||
|
||
**Odpowiedź:**
|
||
```json
|
||
{
|
||
"ok": true,
|
||
"stack_id": "localai",
|
||
"port": 8070,
|
||
"port_env": "LOCALAI_PORT",
|
||
"requires_restart": false,
|
||
"recreated": true,
|
||
"running": true
|
||
}
|
||
```
|
||
|
||
**Błędy:**
|
||
- `400` — stack bez edycji portu (npmplus)
|
||
- `409` — port zajęty / poza zakresem 1024–65535
|
||
- `500` — błąd docker compose
|
||
|
||
**Zarezerwowane porty:** 80, 443, 8090 (gpu-fan), 18090 (gpu-fan agent), `SERVER_UI_PORT` (8091).
|
||
|
||
---
|
||
|
||
## 5. Pliki zmienione
|
||
|
||
| Plik | Zmiana |
|
||
|------|--------|
|
||
| `stacks/server-ui/stacks.yaml` | `port_env`, `port_default`, `port_editable` |
|
||
| `stacks/server-ui/compose_runner.py` | read/write `.env`, walidacja, recreate |
|
||
| `stacks/server-ui/app.py` | `PATCH /api/stacks/{id}/port` |
|
||
| `stacks/server-ui/static/index.html` | input portu w kartach stacków |
|
||
|
||
---
|
||
|
||
## 6. Weryfikacja
|
||
|
||
```bash
|
||
cd ~/cursor/ubuntu-bare-metal/stacks/server-ui
|
||
|
||
# Lista stacków z portami
|
||
curl -s http://127.0.0.1:8091/api/stacks | python3 -m json.tool
|
||
|
||
# Zmiana portu (wymaga API_KEY z .env)
|
||
curl -s -X PATCH http://127.0.0.1:8091/api/stacks/localai/port \
|
||
-H "X-API-Key: $API_KEY" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"port":8070,"recreate":true}'
|
||
|
||
grep LOCALAI_PORT ../localai/.env
|
||
```
|
||
|
||
**UI:** zakładka Stacki → karta LocalAI → pole portu → **Zapisz port** → link „Otwórz” się aktualizuje.
|
||
|
||
**Prod:** `sudo scripts/install.sh && sudo systemctl restart server-ui`
|
||
|
||
---
|
||
|
||
## 7. Uwagi operacyjne
|
||
|
||
- Po zmianie `LOCALAI_PORT` zaktualizuj **upstream w NPMPlus** ręcznie (proxy host → nowy port).
|
||
- `docker compose restart` **nie** zmienia mapowania portów — wymagany recreate (`recreate: true` w API).
|
||
- NPMPlus (`network_mode: host`) — port 81 tylko do odczytu w UI.
|
||
|
||
---
|
||
|
||
## 8. Checklist wdrożenia
|
||
|
||
- [x] Metadane portów w `stacks.yaml`
|
||
- [x] Backend: odczyt/zapis `.env`, walidacja, recreate
|
||
- [x] API `PATCH /port`
|
||
- [x] UI: input + Zapisz w kartach
|
||
- [x] NPMPlus read-only
|
||
- [ ] `install.sh` + restart server-ui na produkcji
|
||
- [ ] Test zmiany portu LocalAI na hoście
|