# 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 (`/`), 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//session (AYON-Auth via Bearer) → Addon prüft Projektzugriff, legt Single-Use-Ticket an (60 s TTL) ← { url: "https:///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: `/` (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 `@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 `/`; 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. ## Noch offen - A-Record `refboard` → `89.57.47.213` (IONOS-Panel, einzeln anlegen) - danach: `certbot --nginx -d refboard.niklashmotion.art` - Addon v0.2.0 installieren + Bundle (siehe oben)