178 lines
9.3 KiB
Markdown
178 lines
9.3 KiB
Markdown
# 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
|