# Webkonsole und Web Push

> HTML: https://remote.ivjn.us/docs/de/web-console/

Die Browserkonsole ist unter `https://remote.ivjn.us/console/` verfügbar. Sie unterstützt Registrierung mit E-Mail-Verifizierung, Konto-/Passwort- oder Operatortoken-Anmeldung, Workspace-bezogene Geräte, gestreamte Befehle, ein xterm-PTY, Android-Screenshots, Tippen, Navigation und Texteingabe, Metriken, Auditprotokolle, Geräteerstellung und -löschung sowie freiwillige Benachrichtigungen. Schreibgeschützte Konten können weder Befehle ausführen noch Android-Bildschirme betrachten. Offlinegeräte bleiben sichtbar.

Geräte mit der Fähigkeit `ubus` zeigen einen Tab **ubus** für Objekt, Methode und optionale JSON-Objektparameter (leer sendet `null`). Aufrufe nutzen versiegelte Sitzungen und erfordern ein aktives, erreichbares Gerät mit Bedienberechtigung. Abbrechen beendet das Warten, macht ausgeführte Methoden aber nicht rückgängig. Gerätewechsel und Berechtigungsverlust verwerfen ausstehende Aufrufe und Ergebnisse.

Bei Registrierung entsteht ein eigener Workspace. Vor der Anmeldung muss die E-Mail-Adresse mit ihrem achtstelligen Code bestätigt werden. Codes lassen sich erneut senden; nach Neuladen kann die Verifizierung fortgesetzt werden. Exchange benötigt SMTP-Konfiguration (`SMTP_HOST`, `SMTP_PORT`, `SMTP_FROM` sowie Authentifizierungs- und Sicherheitseinstellungen in `.env.example`). Ohne verfügbaren Maildienst meldet die Registrierung einen Fehler, statt die Verifizierung zu umgehen.

Zugangsdaten bleiben im Seitenspeicher. Nach Neuladen ist eine erneute Anmeldung nötig. Abmelden schließt Sitzungen, versucht das Push-Abonnement dieses Browsers zu entfernen und widerruft Passwort-Anmeldesitzungen. Primäre Operatortokens behalten ihre bisherige Gültigkeitsdauer. Ein Klick auf eine Benachrichtigung fokussiert eine offene Konsole, ohne deren Zugangsdaten neu zu laden.

## Bauen und bereitstellen

```sh
cd website/console
npm ci
npm test
npm run build
```

Kopieren Sie `dist/` in das `console/`-Verzeichnis der Website. Stellen Sie die bestehenden `website/assets/` für gemeinsame Schriften und das Logo unter `/assets/` bereit. Liefern Sie `index.html`, `sw.js` und das Manifest mit Revalidierung/no-cache aus. Assets mit Hash im Dateinamen dürfen mit unveränderlichem Cache-Status ausgeliefert werden. Cachen Sie weder `/api/*` noch `/ws/*`.

Die Konsole verwendet gleichursprüngliche `/api/*`- und `/ws/browser`-Pfade. Leiten Sie sie per Reverse Proxy an Exchange weiter, behalten Sie den Host-Header bei und erlauben Sie WebSocket-Upgrades. Entfernen Sie die Präfixe `/api` und `/ws` nicht. Der Vite-Entwicklungsserver leitet API/WS an `127.0.0.1:18082`. Rust Exchange erlaubt standardmäßig Browserzugriff vom gleichen Ursprung. Exakte Ursprünge werden mit `REMOTE_CORS_ORIGINS` konfiguriert; nur ein ausdrückliches `*` erlaubt alle Ursprünge. Worker verwendet `CORS_ORIGINS` und eigene Standardwerte. Bearer-Authentifizierung und einmalige WebSocket-Tickets bleiben erforderlich; ursprungsübergreifende Cookies sind deaktiviert.

Die Browserauthentifizierung verwendet `POST /api/ws-ticket` mit Bearer-Autorisierung, anschließend `GET /ws/browser` und den ersten JSON-Frame `{"ticket":"..."}`. Tickets laufen nach 30 Sekunden ab, sind einmalig nutzbar und prüfen beim Einlösen erneut Kontofreigabe und Gültigkeit der Zugangsdaten. Langlebige Zugangsdaten stehen niemals in WebSocket-URLs. Native Clients verwenden weiter `/ws/operator`.

Alle Geräteoperationen verwenden native versiegelte WebSocket-Sitzungen. HTTP stellt Authentifizierung, Gerätemetadaten, Schlüssel- und Grant-Verzeichnisse sowie Relay-Sitzungsverwaltung bereit; siehe [HTTP-API für Agent-Operationen](/docs/de/http-api.md).

## Web Push aktivieren

Setzen Sie beide Variablen in der Exchange-Umgebung; fehlen beide, bleibt Push deaktiviert:

```text
WEB_PUSH_VAPID_PRIVATE_KEY=<base64url-unpadded-32-byte-P256-private-key>
WEB_PUSH_VAPID_SUBJECT=https://remote.ivjn.us
```

Erzeugen Sie den privaten Schlüssel mit `python scripts/generate-vapid-key.py`. Bewahren Sie ihn außerhalb von Git in einer zugriffsbeschränkten Umgebungsdatei auf, behalten Sie ihn über Neustarts bei und sichern Sie ihn sicher. Der öffentliche Schlüssel wird automatisch abgeleitet. Nach Schlüsselwechsel müssen Browser erneut abonnieren.

`WEB_PUSH_ALLOWED_ORIGINS` enthält standardmäßig die exakt vertrauenswürdigen Origins `https://fcm.googleapis.com,https://updates.push.services.mozilla.com,https://web.push.apple.com`. Ergänzen Sie weitere Browseranbieter erst nach Prüfung ihres HTTPS-Push-Origins. Vom Client übermittelte Endpunkte erweitern diese Positivliste nicht. Weiterleitungen werden nie verfolgt.

Authentifizierte Routen:

| Methode und Pfad | Zweck |
| --- | --- |
| GET `/api/webpush/config` | Aktivierungszustand und öffentlicher Application-Server-Schlüssel |
| GET/POST `/api/webpush/subscriptions` | Eigene Metadaten auflisten oder abonnieren/aktualisieren |
| DELETE `/api/webpush/subscriptions/{id}` | Eigenes Abonnement entfernen |
| POST `/api/webpush/test` | Sichtbaren Test für Abonnements des Aufrufers einreihen |
| POST `/api/webpush/messages` | An sich selbst oder als Administrator an ein anderes Konto desselben Workspace senden |
| POST `/api/webpush/devices/{id}/messages` | Abonnierte Administratoren benachrichtigen, die das authentifizierte Gerät verwalten |

Abonnements verwenden `PushSubscription.toJSON()` des Browsers, `application_server_key` und `triggers`. `triggers` enthält höchstens je ein `online`- und `offline`-Objekt. Jedes Objekt besitzt `kind`, `tick_count` (Standard 3, Bereich 1–100) und optional `device_ids`. `offline` meldet nach `tick_count` aufeinanderfolgenden Fehlschlägen, `online` nach `tick_count` aufeinanderfolgenden Erfolgen. `device_ids` ist eine Geräte-ID als String, eine Liste von IDs oder fehlt/ist `null` für alle Geräte des Workspace. Ein leeres `triggers`-Array abonniert den Browser ohne automatische Online-/Offline-Meldungen. `missed_count` und `success_count` werden nicht akzeptiert. Nachrichten verwenden optional `operator_id` und `notification` mit `title`, `body` sowie optional gleichursprünglicher relativer `url` und `tag`. `/webpush/` ist ein minimaler Referenzclient; die Produktionskonsole verwendet einen eigenen Worker mit Geltungsbereich `/console/`.

Migration `0005_web_push.sql` ergänzt Abonnements und die dauerhafte Outbox. `0008_online_offline_triggers.sql` stellt Gerätebenachrichtigungen auf `triggers` um und entfernt `missed_count` / `success_count`. `202` bedeutet eingereiht, nicht zugestellt oder angezeigt. Aufträge verfallen nach 15 Minuten. Bei Netzwerkfehlern, 408, 429 und 5xx erfolgen bis zu fünf Versuche; 404/410 entfernt die passende Abonnementrevision. Eine 90-Sekunden-Lease ermöglicht die Wiederaufnahme nach Worker-Ausfall. Die Zustellung erfolgt mindestens einmal; Browseranzeige hängt von Anbieterzustellung, Berechtigungen und Betriebssystemrichtlinien ab. Geräteereignisse vor dauerhaftem Einreihen werden bei Überlast nur bestmöglich verarbeitet; eingereihte Ereignisse überleben Neustarts. Grenzen: 20 Abonnements je Konto, 32 eingereihte Nachrichten je Abonnement und 30 Änderungen je Minute und Konto.

iOS/iPadOS verlangt möglicherweise das Hinzufügen der Konsole zum Home-Bildschirm vor Aktivierung der Benachrichtigungen. Die Erreichbarkeit des Browser-Push-Dienstes ist unabhängig von der Exchange-Verbindung.

## Validierung

Führen Sie `cargo test --workspace`, `cargo clippy --workspace --all-targets -- -D warnings` und die npm-Tests sowie den Build der Konsole aus. Für den PostgreSQL-/HTTPS-Integrationstest installieren Sie `psycopg[binary]`, setzen `WEB_PUSH_TEST_ADMIN_URL` auf eine Test-PostgreSQL-Verbindung mit CREATE-DATABASE-Rechten und starten `python scripts/test-webpush.py`. Der Test erstellt und entfernt eine Wegwerfdatenbank; verwenden Sie niemals eine Datenbankrolle mit unbeabsichtigten Berechtigungen.

## Von allen Clients senden

Web Console bietet ein Nachrichtenformular unter Browserbenachrichtigungen. Die mobile Console bietet unter Einstellungen Web-Push-Senden, Tests und einen Link zu Web Console. Desktop-Agent-Einstellungen, Android-Agent-Einstellungen und Browsererweiterung können mit Geräteanmeldedaten senden. Headless/OpenWrt verwendet den folgenden Befehl. Empfangen wird durch Aktivieren der Benachrichtigungen in Web Console/PWA; native FCM/APNs-Empfänger werden nicht hinzugefügt.

```sh
remote-cli webpush status
remote-cli webpush subscriptions
remote-cli webpush test
remote-cli webpush send --title "Backup ready" --body "Completed"
remote-cli webpush send --operator-id ACCOUNT_UUID --title "Notice" --body "Hello"
remote-agent --config /path/to/config.json webpush --title "Device ready" --body "Completed"
```

```json
{"notification":{"title":"Backup ready","body":"Completed","url":"/console/","device_id":"edge-1"}}
```

POST `/api/webpush/messages` verwendet Operator-Bearer-Authentifizierung. Ohne `operator_id` geht die Nachricht an das eigene Konto. Andere Empfänger erfordern Administratorrechte und denselben Arbeitsbereich. Das obige JSON funktioniert auch mit `remote-cli webpush send --json-file message.json`. Leere Statustrigger deaktivieren automatische Online-/Offline-Meldungen, allgemeine Nachrichten bleiben aktiv.

POST `/api/webpush/devices/{id}/messages` verwendet das Bearer-Token dieses Geräts und nimmt ein Benachrichtigungsobjekt direkt an (`title`, `body`, optional `tag`). Nur aktive Abonnements aktivierter Administratoren mit Verwaltungsrechten für dieses Gerät erhalten Nachrichten. Der Server setzt `device_id`, `event=device.message`, `occurred_at` und `/console/`; Absender können kein Konto oder fremdes Gerät wählen. Geräte teilen ein Limit von 30 Anfragen pro Minute und Arbeitsbereich, mit höchstens 256 Abonnements pro Versand. Konto- und Warteschlangenlimits gelten weiterhin. Ohne Empfänger folgt `202 {"queued":0}`; `queued` zählt Abonnements, keine Zustellungen.
