Files
refboard-ayon/README.md

178 lines
9.3 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# RefBoard AYON
Kollaborative Referenz-Bildwand (PureRef-on-the-web), umgebaut für den
AYON-only-Betrieb. Fork von [metalfinger/refboard](https://github.com/metalfinger/refboard)
(vollständige History erhalten).
**Das Besondere:** Es gibt keinen RefBoard-Login mehr. Nutzer öffnen Boards
aus dem AYON-Launcher heraus und sind automatisch mit ihrer AYON-Identität
eingeloggt. Boards gehören zu AYON-Tasks (`<Projekt>/<Task-Pfad>`), nicht zu
Personen. Alle Kollegen mit Projektzugriff arbeiten kollaborativ am selben
Board (Echtzeit, Socket.IO).
## Architektur
```
Launcher (Task ausgewählt)
→ "RefBoard öffnen" (Server-Addon-Action)
→ POST /api/addons/refboard/<ver>/session (AYON-Auth via Bearer)
→ Addon prüft Projektzugriff, legt Single-Use-Ticket an (60 s TTL)
← { url: "https://<refboard>/b?ticket=…&project=…&task=…" }
→ Launcher öffnet Browser auf dieser URL
→ RefBoard-Frontend (Route /b) ruft POST /api/auth/ayon/exchange
→ RefBoard-Backend ⇄ Addon /exchange (server-to-server, X-API-Key)
→ Ticket wird atomar verbraucht (single-use) → Identität
→ RefBoard-Session-JWT wird gesetzt → weiter zum Task-Board
```
Beteiligte Komponenten:
| Komponente | Repo | Aufgabe |
|---|---|---|
| RefBoard AYON (dieses Repo) | `Hermes/refboard-ayon` | Web-App: Canvas + Ticket-Auth + Task-Boards |
| AYON Server-Addon | `Hermes/refboard-addon` (ab v0.2.0) | Ticket-Ausstellung, `/exchange`, Launcher-Action |
| AYON Server (private Instanz) | — | Identitätsquelle, Token-Prüfung, Access-Groups |
### Warum ein Ticket statt des AYON-Tokens in der URL?
Der AYON-Session-Token ist langlebig und dürfte nie in Browser-History/Logs
landen. Das Ticket ist ein 32-Byte-Zufallswert, der genau einmal eingelöst
werden kann und nach 60 s verfällt — die OAuth-"Code"-Äquivalenz. Die
Identität kommt ausschließlich vom Addon (`/exchange`), niemals aus der URL.
### Task-Boards
- Adresse/Name: `<project>/<task_path>` (z. B. `lumenfjord/assets/vegetation/fir/lookdev`)
- Board entsteht lazy beim ersten Öffnen in der Sammlung `AYON`
- `users`-Tabelle bleibt intern bestehen (SQLite-Fremdschlüssel), wird aber
nicht mehr als Login angezeigt; AYON-Nutzer bekommen Einträge
`<username>@ayon.local` mit unbenutzbarem Passwort-Sentinel
- Legacy-Login/Register-Routen sind noch vorhanden (für Alt-Instanz), im
AYON-Betrieb jedoch ungenutzt
## Deployment (orange)
- Container: `refboard-ayon`, Host-Port **8003** → 8000
- Stack-Ordner: `/media/orange/RocketChat/refboard-ayon/` (NVMe)
- `repo/` — dieser Code (Branch `ayon-integration`)
- `compose.yaml` — Stack (inkl. `ayon-private_default` external network:
RefBoard erreicht AYON unter `http://ayon-private-server-1:5000`)
- `.env``JWT_SECRET`, `REFBOARD_API_KEY`, `AYON_EXCHANGE_URL`
- `data/` — SQLite + FS-Storage
- nginx: `/etc/nginx/sites-enabled/refboard.conf``refboard.niklashmotion.art`
(WebSocket-Upgrade, 250 M Upload)
- Wichtig nach `docker compose up -d`: das `ayon-private_default`-Netz ist
als `external: true` deklariert und überlebt Rebuilds dadurch
### Environment
| Variable | Bedeutung |
|---|---|
| `JWT_SECRET` | Signatur-Secret der RefBoard-Sessions |
| `REFBOARD_API_KEY` | Shared Secret für `/exchange` (muss mit dem AYON-Secret `refboard_api_key` übereinstimmen) |
| `AYON_EXCHANGE_URL` | z. B. `http://ayon-private-server-1:5000/api/addons/refboard/0.2.0/exchange` |
| `STORAGE_BACKEND` | `fs` (FS-Storage) |
| `CORS_ORIGIN` | CORS-Origin (Default `*`) |
## Setup in AYON (Schritt für Schritt)
> Ausführen, sobald `refboard-0.2.0.zip` und der A-Record existieren.
### 1. Addon installieren (im AYON-Klon, NICHT im Original)
```bash
# zip in den Server-Container entpacken
ssh orange@niklashmotion.art 'docker exec -i ayon-private-server-1 python3 -c "
import zipfile,io,os,sys
z=zipfile.ZipFile(io.BytesIO(sys.stdin.buffer.read()))
b=\"/addons/refboard/0.2.0\"
os.makedirs(b,exist_ok=True)
[open(os.path.join(b,n),\"wb\").write(z.read(n)) for n in z.namelist() if not n.endswith(\"/\")]
"' < refboard-0.2.0.zip
# AYON-Server neu starten (scannt /addons nur beim Start)
ssh orange@niklashmotion.art 'docker restart ayon-private-server-1'
```
### 2. Bundle (WebUI: Studio Settings → Bundles)
Neues Bundle anlegen, das die Addons des aktuellen Produktions-Bundles
enthält, aber `refboard: 0.2.0`, und als Produktion aktivieren.
### 3. Settings prüfen (WebUI: Studio Settings → Addons → RefBoard)
- `refboard_url`: `https://refboard.niklashmotion.art`
- `api_key`: `refboard_api_key` (Secret existiert bereits im AYON-Secret-Store
und muss denselben Wert wie `REFBOARD_API_KEY` in der RefBoard-`.env` haben)
### 4. Benutzen
1. AYON-Launcher starten und ein Projekt öffnen
2. Task auswählen → Kontext-/Aktionsmenü → **„RefBoard öffnen"**
3. Browser geht auf, automatisch eingeloggt als der eigene AYON-User, im
Board der Task
4. Teilen: Kollegen wählen dieselbe Task → „RefBoard öffnen" → alle landen
im selben Board und arbeiten in Echtzeit zusammen
Hinweise:
- Der Board-Name ist `<Projekt>/<Task-Pfad>`; im Editor oben wird er als
Titel angezeigt
- Ohne Task-Auswahl gibt es keinen Action-Eintrag (Bewusst so gebaut:
Boards sind taskbezogen)
- Tickets sind einmalig und 60 s gültig — bei „Invalid or expired ticket"
einfach im Launcher erneut klicken
## AYON-REST-API des Addons (v0.2.0)
Alle Routen unter `/api/addons/refboard/0.2.0/`:
| Endpoint | Methode | Auth | Zweck |
|---|---|---|---|
| `/health` | GET | AYON-User | Status + konfigurierte URL |
| `/session` | POST | AYON-User | `{project_name, task_id?}``{url}` (Ticket-URL) |
| `/exchange` | POST | `X-API-Key` | `{ticket}``{ayon_user, display_name, project, task}` (single-use) |
## Lokale Entwicklung / Tests
```bash
# Image bauen (ARM64-nativ auf orange getestet)
docker build -t refboard-ayon:latest .
# Container + Mock-Addon (Test-Double statt echtem AYON-Addon):
# /media/orange/RocketChat/refboard-ayon/mock-exchange.js (container :9000)
# /media/orange/RocketChat/refboard-ayon/e2e.sh (Container-interner E2E)
```
Der E2E (Ticket-Ausstellung → Exchange → Board-Zugriff → Single-Use →
2. Nutzer am selben Board) ist mit dem Mock grün getestet.
## Status: PRODUKTIV (seit 05.09.2026)
- DNS + Let's-Encrypt-Zertifikat aktiv (`refboard.niklashmotion.art`)
- Launcher-Klick → eingeloggt im Task-Board — produktiv im Einsatz
## Fehler & Lektionen (Session 04.05.09.2026)
| # | Fehler | Ursache | Fix/Lektion |
|---|---|---|---|
| 1 | Launcher zeigte fast keine Actions mehr (`KeyError: 'url'` in `_get_webactions`) | `SimpleActionManifest(icon=…)` erzeugte `{"type":"url"}` **ohne** `url`-Feld; eine einzige kaputte Action kippte **alle** Webactions im Client (try/except um den ganzen Fetch) | Icon-Feld weggelassen; Lektion: Action-Icons brauchen `{"type":"material-symbols","name":…}` oder vollständige URL |
| 2 | `/session` → 500 `AttributeError: 'UserAttribModel' object has no attribute 'name'` | AYON-User-attrib heißt `fullName`, nicht `name` | `user.attrib.fullName` |
| 3 | Exchange → 500 `uuidv4 is not defined` | Import im neuen db.js-Code vergessen | Lektion: py_compile fängt undefined names nicht — Smoke-Test im Container statt nur Syntax-Check |
| 4 | RefBoard konnte AYON nicht erreichen nach `compose up` | Externes Docker-Netz nur top-level deklariert, Service nicht attached; manuelles `docker network connect` überlebt kein Recreate | Netz **im Service-Block** als `external: true` deklarieren |
| 5 | Exchange → 403 trotz „richtigem" Secret | `REFBOARD_API_KEY`-Wert enthält `=`; Extraktion mit `cut -d= -f2` bricht am ersten `=` → falscher (gekürzter) Wert in AYON-Secret | Werte mit `sed "s/^KEY=//"` extrahieren; Lektion: Secret-Sync immer per Längen-/Hash-Vergleich verifizieren |
| 6 | Settings-POST (204) änderte die DB **nicht** | AYON speichert Settings als Ganzes, filtert aber „identity"-Felder (Werte == Defaults) heraus — ein Feld, das dem Default entspricht, ist nie speicherbar | Lektion: Settings-API ist Full-Replace + Identity-Filter; gezielte Feld-Fixe über die API sind nicht immer möglich |
| 7 | App-Kacheln fehlten in jeder Task (Launcher leer bis auf 3 Actions) | **Ynput-Bug in applications 1.4.4:** Runtime liest Pydantic-Defaults (`profiles=[]`), nicht `DEFAULT_VALUES` (mit `all_applications`-Profil); das UI zeigt die Defaults trotzdem — Diskrepanz UI/Runtime | Umgehend: Profil per API explizit setzen (Full-Replace aus Backup + Profil-Feld); langfristig: Ynput-Issue |
| 8 | Ticket-Wiederverwendung zeigte 502 statt 401 | RefBoard behandelte 401/403 vom Addon als Infrastruktur-Fehler | 401/403 aus `/exchange` → sauberes 401 „Invalid or expired ticket" |
| 9 | Nach Secret-Löschung + Neuanlage Exchange-Fehler | AYON-Secret-Wert ≠ Container-Env (Container läuft mit altem Env bis Recreate) | Nach Secret-Änderungen: `docker compose up -d --force-recreate` |
## Noch offen
- ~~A-Record~~ ✓ erledigt (05.09.)
- ~~Zertifikat~~ ✓ erledigt (05.09.)
- Nuke-17.0-Pfad-Mismatch in den Applications-Settings
(`/usr/local/Nuke17.0v1` erwartet, installiert ist `/opt/Nuke17.0v3`) — Fix im
Studio-Settings-WebUI: Executable auf `/usr/local/bin/nuke-ayon` setzen
- Alt-Container `refboard` (:8002, standalone) stilllegen, sobald Bestands-
Boards nicht mehr gebraucht werden
- Ynput-Issue zu #7 (applications 1.4.4 Runtime-Defaults) optional melden