diff --git a/CHANGELOG.md b/CHANGELOG.md index afd58ab..ee740f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,14 +8,20 @@ und dieses Projekt folgt [Semantic Versioning](https://semver.org/lang/de/). ## [Unveröffentlicht] ### Hinzugefügt +- CLI `proxdeploy` (Go, ein Binary für Linux/macOS/Windows × amd64/arm64): `login`, `servers`, `distros`, `profiles`, `presets`, `vm`, `lxc`, `template list|build`, `batch`, `status`, `logs --follow`, `cancel`; Ausgabe im Aufbau der Vorgabe mit 24-bit-/256-Farben, `--ascii`, `--quiet`, `--json`, `--dry-run` (#013) +- API-Tokens für die CLI: anlegen und widerrufen in den Einstellungen, `proxdeploy login` tauscht Benutzername und Passwort gegen ein Token; Anmeldung per `Authorization: Bearer` (#013) +- Kurze Anfragen: fehlen Storage, Bridge, Node, Benutzer oder SSH-Schlüssel, nimmt der Server die Standards (Server-Einstellungen, Verbindungstest, eigener Benutzername, hinterlegte Schlüssel) – auch beim Template-Bau (#013) +- OpenAPI-Beschreibung unter `/api/v1/openapi.json`, aus denselben zod-Schemas wie die API (#013) ### Geändert +- Die Abschlussbox (Name, ID, IP, Profile, SSH bzw. Phase, Ursache, Hinweis) berechnet der Server; Web-GUI und CLI zeigen dieselben Zeilen (#013) ### Behoben ### Entfernt ### Sicherheit +- API-Tokens werden nur als SHA-256 gespeichert und genau einmal angezeigt; ein ungültiges Token gilt als nicht angemeldet, auch wenn zusätzlich ein Sitzungscookie mitkommt (#013) ## [0.3.0] – 2026-10-12 diff --git a/CLAUDE.md b/CLAUDE.md index 28afc1b..624dc4e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -38,6 +38,7 @@ src/app/api/v1/* Route Handler: Recht prüfen, zod, Service. Sonst nichts src/components/* Darstellung, keine Geschäftslogik. src/lib/* Framework-freie Helfer; was Client und Server teilen, liegt hier. src/worker/* Hintergrundprozess (pg-boss). +cli/ CLI in Go; nur REST-API. Typen aus cli/api/openapi.json (oapi-codegen). ``` - **Web-GUI und CLI nutzen dieselbe REST-API.** Keine Logik nur für eine Oberfläche. @@ -61,6 +62,11 @@ Node 24 über nvm (`export NVM_DIR="$HOME/.nvm"; . "$NVM_DIR/nvm.sh"`), pnpm 12. `pino@10.4.0`. Obergrenzen: **eslint 9.x** (eslint-plugin-react aus eslint-config-next kann 10 nicht), **typescript ~6.0.3** (typescript-eslint 8 erlaubt < 6.1). +CLI: **Go 1.27.2** ohne sudo unter `~/.local/go` (`export PATH="$HOME/.local/go/bin:$PATH"`), +`cobra 1.10.2`, `BurntSushi/toml 1.6.0`, `x/term 0.46.0`, `go-runewidth 0.0.30`, +`go.yaml.in/yaml/v3 3.0.5`, `oapi-codegen 2.8.0` (als `go tool`). Antwortschemas der API stehen in +`src/lib/schemas/api.ts`; nach einer Änderung `pnpm openapi` und `go generate ./...` in `cli/`. + Next 16: `proxy.ts` statt middleware; `cookies()`, `headers()`, `params`, `searchParams` sind async. Prisma 7: Client aus `@/generated/prisma/client`, Enum-Werte aus `@/generated/prisma/enums`, Zugriff über den Lazy-Proxy `src/lib/db.ts`. @@ -70,6 +76,7 @@ Prisma 7: Client aus `@/generated/prisma/client`, Enum-Werte aus `@/generated/pr ```bash pnpm db:up pnpm lint && pnpm typecheck && pnpm format:pruefen && pnpm test +pnpm cli:pruefen # wenn API-Schemas oder cli/ betroffen sind ``` Ohne laufende Datenbank überspringt `pnpm test` die Datenbanktests und sagt es auf stderr. diff --git a/README.md b/README.md index 38520de..aecb0da 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ In Entwicklung. Version und Fortschritt stehen im [CHANGELOG](CHANGELOG.md), der | 1 · v0.1.0 | Server-Verwaltung, Fingerprint-Pinning, Verbindungstest mit Fähigkeitserkennung | ✓ fertig, auf PVE 9.2 geprüft | | 2 · v0.2.0 | Katalog, Template-Bau, Debian-13-VM, Live-Fortschritt, Assistent | ✓ fertig, auf PVE 9.2 geprüft | | 3 · v0.3.0 | LXC, Ubuntu, Profile, Vorlagen, Profile im Template, SSH-Schlüssel | ✓ fertig, auf PVE 9.2 geprüft (Bau-Adresse #021: Abnahme offen) | -| 4 · v0.4.0 | CLI `proxdeploy` (Go) | ◐ als Nächstes | +| 4 · v0.4.0 | CLI `proxdeploy` (Go), API-Tokens | ◐ in Arbeit | | 5 · v0.5.0 | Verlauf, Audit-Log, Rollen, OIDC | ○ geplant | | 6 · v0.6.0 | Branding, README mit Screenshots, Installer | ○ geplant | @@ -37,6 +37,72 @@ Maßgeblich ist die Proxmox-API (geprüft gegen PVE 8.x und 9.2): Das API-Token braucht eine eigene Rolle; die Befehle stehen in [docs/proxmox-rechte.md](docs/proxmox-rechte.md). +## CLI + +`proxdeploy` ist ein einzelnes Binary (Linux, macOS, Windows; amd64 und arm64) und spricht nur die +REST-API – dieselbe wie die Web-GUI, mit denselben Phasentexten. + +```bash +proxdeploy login https://proxdeploy.example # fragt Benutzer und Passwort, speichert ein Token +proxdeploy servers +proxdeploy distros --type lxc +proxdeploy profiles +proxdeploy vm web01 --server rz1 --distro debian-13 --profile base,docker --ip 10.0.20.15/24 --gw 10.0.20.1 --vlan 20 +proxdeploy lxc dns01 --server rz1 --distro ubuntu-24.04 --dry-run +proxdeploy template build --server rz1 --distro debian-13 --profile base,docker +proxdeploy batch gaeste.yaml +proxdeploy status a-7f3k +proxdeploy logs a-7f3k --follow +``` + +``` +⬡ ProxDeploy v0.4 · Server rz1 · Node pve01 · Debian 13 (Template 9013) + +✓ Prüfungen VMID 120 frei · local-zfs · vmbr0 VLAN 20 +✓ Klonen full clone 9013 → 120 · Disk auf 40G +✓ cloud-init user florian · SSH-Key · Profile base, docker +✓ Start VM 120 läuft +✓ Guest-Agent antwortet nach 18s +◐ Provisionierung cloud-init läuft … 1m 12s +``` + +- Was nicht angegeben ist, füllt der Server: Storage, Bridge und Node aus den Server-Einstellungen + bzw. dem Verbindungstest, Benutzer = eigener Benutzername, SSH-Schlüssel = die in den Einstellungen + hinterlegten. `--preset` belegt aus einer Vorlage vor; Flags gehen vor. +- Konfiguration in `~/.config/proxdeploy/config.toml` (Rechte 0600): `url`, `token`, optional + `server` als Standard für `--server` und `[instances.]` für weitere Instanzen + (`--instance`). `PROXDEPLOY_URL`, `PROXDEPLOY_TOKEN`, `PROXDEPLOY_INSTANCE` gehen vor. +- Tokens für Skripte und CI: Web-GUI → Einstellungen → API-Tokens, dann `proxdeploy login --token pd_…`. +- Ausgabe: Farben (24-bit, sonst 256) nur im Terminal und ohne `NO_COLOR`/`--no-color`; `--ascii` + ersetzt Symbole durch `[OK]`, `[..]`, `[!!]`; `--quiet` zeigt nur die Abschlussbox; `--json` ist + maschinenlesbar. Ohne Terminal (Pipe, CI) erscheint jede Phase als eine Zeile, sobald sie fertig ist. +- Exit-Codes: `0` Erfolg, `1` Auftrag fehlgeschlagen, `2` Bedienfehler, `3` Verbindung/Anmeldung. +- `Strg+C` hängt nur ab – der Auftrag läuft weiter (`proxdeploy status `); abbrechen mit + `proxdeploy cancel `. + +`batch` rollt mehrere Gäste aus; die Schlüssel heißen wie die Flags. Die Datei wird vollständig +geprüft, bevor der erste Auftrag startet: + +```yaml +defaults: + server: rz1 + distro: debian-13 + profile: [base] +guests: + - name: web01 + type: vm + profile: [base, docker] + ip: 10.0.20.15/24 + gw: 10.0.20.1 + memory: 4096 + - name: mon01 + type: lxc + profile: [base, zabbix-agent] + vars: { zabbix-agent: { server: zbx.example } } +``` + +Bauen: `cli/bauen.sh 0.4.0` legt die sechs Binaries und `SHA256SUMS` unter `cli/bin/` ab (Go 1.27). + ## Entwicklung Voraussetzungen: Node 24 (z. B. über nvm), pnpm 12 (corepack), Docker für PostgreSQL. @@ -54,8 +120,12 @@ Prüfen vor jedem Commit: ```bash pnpm lint && pnpm typecheck && pnpm format:pruefen && pnpm test pnpm test:e2e # Browser-Abläufe gegen den gebauten Stand (Desktop und 390 px) +pnpm cli:pruefen # openapi.json und Go-Typen aktuell, go vet, go test (Golden-Files) ``` +Ändert sich ein Schema der API, erst `pnpm openapi`, dann in `cli/` `go generate ./...` – der +Go-Client nutzt die aus `cli/api/openapi.json` erzeugten Typen. + Konventionen (deutsche Bezeichner, Schichtung, Versionen) stehen in [CLAUDE.md](CLAUDE.md). ## Betrieb diff --git a/docs/architektur.md b/docs/architektur.md index 2b94684..7104c19 100644 --- a/docs/architektur.md +++ b/docs/architektur.md @@ -500,9 +500,19 @@ TTY-Erkennung, 24-bit-Farben und SSE. Der Nachteil (keine geteilten TS-Typen) wi `openapi.json` generierten Client (`oapi-codegen`) aufgefangen; ein CI-Schritt prüft, dass der generierte Code aktuell ist. Bibliotheken: `cobra` (Befehle), `BurntSushi/toml`, `golang.org/x/term`. -- Konfig `~/.config/proxdeploy/config.toml` (`url`, `token`, optional `[profiles.]` für mehrere - Instanzen), Rechte 0600; `PROXDEPLOY_URL`/`PROXDEPLOY_TOKEN` überschreiben. -- `login ` fragt Benutzername/Passwort ab, legt ein Token an und speichert es (oder `--token`). +- Konfig `~/.config/proxdeploy/config.toml` (`url`, `token`, optional `server` als Standard und + `[instances.]` für mehrere Instanzen – nicht `profiles`, das hieße wie `--profile`), Rechte + 0600; `PROXDEPLOY_URL`/`PROXDEPLOY_TOKEN`/`PROXDEPLOY_INSTANCE` überschreiben. +- **Kurze Anfragen:** Die CLI schickt nur gesetzte Flags. Der Server füllt nach der Vorlage Storage, + Bridge, Node (Server-Standards bzw. Verbindungstest), Benutzer (eigener Name) und SSH-Schlüssel + (hinterlegte) – `anfrageErgaenzen` in `src/server/services/ausrollen.ts`. +- **Abschlussbox** (`abschluss` im Auftrag) berechnet der Server; Web und CLI zeigen dieselben Zeilen. +- **Generierter Code:** Antwortschemas als zod in `src/lib/schemas/api.ts`, daraus `openapi.json` + (`pnpm openapi`) und daraus die Go-Typen (oapi-codegen, nur Modelle). Anfragen gehen als Map hinaus + (sonst schickte Go leere Pflichtfelder statt sie wegzulassen). `pnpm cli:pruefen` schlägt fehl, + wenn etwas veraltet ist; `tests/api-schemas.test.ts` prüft echte Antworten gegen die Schemas. +- `login ` fragt Benutzername/Passwort ab, tauscht sie über `POST /api/v1/auth/token` gegen ein + Token und speichert es (oder `--token`). Tokens: `pd_` + 256 Bit, gespeichert nur als SHA-256. - Ausgabe exakt nach Vorgabe; Renderer in `cli/internal/ausgabe/` mit Golden-File-Tests für Farbe, `--no-color`, `--ascii`, `--quiet`, `--json`, `--dry-run`. Breite der Phasenspalte aus dem längsten Phasennamen, Box-Breite aus dem Inhalt (Unicode-Breite beachtet). diff --git a/docs/issues/013-cli.md b/docs/issues/013-cli.md index f321259..99aa837 100644 --- a/docs/issues/013-cli.md +++ b/docs/issues/013-cli.md @@ -1,5 +1,5 @@ # #013 CLI proxdeploy (Go) -**Status:** offen +**Status:** in Arbeit **Meilenstein:** v0.4.0 **Labels:** feature, cli **Geschätzt:** 12 h **Tatsächlich:** – @@ -8,16 +8,28 @@ Alle Befehle aus der Vorgabe, Ausgabe exakt im Format der Vorgabe. ## Aufgaben -- [ ] OpenAPI aus zod, Go-Client per oapi-codegen, Prüfung auf Aktualität -- [ ] login, servers, distros, profiles, vm, lxc, template build, batch, status, logs --follow -- [ ] Renderer: Farbe 24-bit/256/aus, --ascii, --quiet, --json, --dry-run; Golden-Tests -- [ ] Release-Builds für linux/darwin/windows × amd64/arm64 +- [x] API-Tokens (`pd_…`, nur SHA-256 gespeichert, Ablauf, Widerruf) mit Bearer-Anmeldung, `POST /auth/token` für `login`, Verwaltung in den Einstellungen +- [x] Kurze Anfragen: Server füllt Storage, Bridge, Node, Benutzer, Schlüssel (`anfrageErgaenzen`), auch beim Template-Bau +- [x] Abschlussbox serverseitig (`abschluss`), Web und CLI zeigen dieselben Zeilen +- [x] OpenAPI aus zod (`/api/v1/openapi.json`, `pnpm openapi`), Go-Typen per oapi-codegen, `pnpm cli:pruefen` +- [x] login, servers, distros, profiles, presets, vm, lxc, template list/build, batch, status, logs --follow, cancel +- [x] Renderer: Farbe 24-bit/256/aus, --ascii, --quiet, --json, --dry-run; Golden-Tests +- [x] Release-Builds für linux/darwin/windows × amd64/arm64 (`cli/bauen.sh`) +- [ ] Abnahme auf pve-demo (VM + LXC per batch) ## Akzeptanzkriterien -- [ ] Ausgabe entspricht zeichengenau dem Mockup (Golden-File) +- [x] Ausgabe entspricht zeichengenau dem Mockup (`cli/internal/ausgabe/testdata/mockup.txt`, von Hand aus der Vorgabe übernommen) ## Abhängigkeiten #008, #012 ## Notizen / Entscheidungen -– +- **Anfragen als Map, Antworten typisiert.** oapi-codegen macht Pflichtfelder zu Nicht-Zeigern; Go + schickte dann `"storage": ""` und überschriebe Vorlagenwerte. Deshalb gehen nur gesetzte Flags hinaus. +- **`[instances.]`** statt `[profiles.]` in der Konfiguration – „profile“ ist schon `--profile`. +- **Strg+C hängt nur ab**, der Auftrag läuft weiter; Abbruch ist `proxdeploy cancel`. +- **Ohne Terminal** (Pipe, CI) kein Neuzeichnen: jede Phase eine Zeile, sobald fertig; die Kopfzeile + nach der ersten Phase, weil Node und Template erst dann feststehen. +- **SSE-Ausfall:** Wiederverbinden mit Last-Event-ID; nach drei Fehlschlägen ohne Stand Polling alle 2 s. +- **Go 1.27.2** ohne sudo unter `~/.local/go`; Bibliotheken mit abgelagerten Versionen (x/term 0.46.0, + go-runewidth 0.0.30 statt der wenige Tage alten Nachfolger). diff --git a/docs/issues/README.md b/docs/issues/README.md index d61c1f8..dbe1b56 100644 --- a/docs/issues/README.md +++ b/docs/issues/README.md @@ -16,7 +16,7 @@ Details und Architektur: [../architektur.md](../architektur.md). Zeiten werden a | [#010](010-ubuntu.md) | Ubuntu 22.04, 24.04 und 26.04 | erledigt | v0.3.0 | 3 h | 0 h 05 min | | [#011](011-profile-docker-zabbix.md) | Profile docker und zabbix-agent, Variablen, Editor | erledigt | v0.3.0 | 6 h | 0 h 10 min | | [#012](012-vorlagen.md) | Vorlagen (Presets) | erledigt | v0.3.0 | 3 h | 0 h 11 min | -| [#013](013-cli.md) | CLI proxdeploy (Go) | offen | v0.4.0 | 12 h | – | +| [#013](013-cli.md) | CLI proxdeploy (Go) | in Arbeit | v0.4.0 | 12 h | – | | [#014](014-verlauf.md) | Verlauf und Wiederholen | offen | v0.5.0 | 4 h | – | | [#015](015-rollen-audit.md) | Rollen, Benutzer, Tokens, Audit-Log | offen | v0.5.0 | 6 h | – | | [#016](016-oidc.md) | Anmeldung per OIDC | offen | v0.5.0 | 4 h | – |