# Console Web et Web Push

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

La console navigateur est accessible à `https://remote.ivjn.us/console/`. Elle prend en charge l'inscription avec vérification par e-mail, la connexion par compte/mot de passe ou jeton opérateur, les appareils par espace de travail, les commandes en flux continu, un PTY xterm, les captures d’écran Android, les appuis, la navigation et la saisie de texte, les métriques, l'audit, la création/suppression d'appareils et les notifications facultatives. Les comptes en lecture seule ne peuvent ni exécuter des commandes ni consulter les écrans Android. Les appareils hors ligne restent visibles.

Les appareils annonçant la capacité `ubus` affichent un onglet **ubus** pour saisir un objet, une méthode et des paramètres JSON facultatifs (un champ vide envoie `null`). Les appels utilisent des sessions chiffrées et exigent un appareil activé, en ligne et une autorisation de contrôle. Annuler termine l’attente sans annuler une méthode déjà exécutée. Changer d’appareil ou perdre les droits efface les appels en attente et les résultats.

L'inscription crée un espace de travail distinct. L'adresse e-mail doit être vérifiée avec son code à huit chiffres avant la connexion ; la console permet de renvoyer les codes et de reprendre la vérification après rechargement. Exchange doit disposer d'une configuration SMTP (`SMTP_HOST`, `SMTP_PORT`, `SMTP_FROM` et paramètres d'authentification/sécurité de `.env.example`). Sans service de messagerie disponible, l'inscription signale une erreur sans contourner la vérification.

Les identifiants restent en mémoire dans la page. Un rechargement exige une nouvelle connexion. La déconnexion ferme les sessions, tente de supprimer l'abonnement push de ce navigateur et révoque les sessions ouvertes par mot de passe. Les jetons opérateur principaux conservent leur durée de validité existante. Un clic sur une notification active une console déjà ouverte sans recharger ses identifiants.

## Compiler et servir

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

Copiez `dist/` dans le répertoire `console/` du site. Gardez `website/assets/` accessible à `/assets/` pour les polices et le symbole partagés. Servez `index.html`, `sw.js` et le manifeste avec revalidation/no-cache. Les ressources portant un hachage peuvent être mises en cache de manière immuable. Ne mettez pas `/api/*` ni `/ws/*` en cache.

La console utilise `/api/*` et `/ws/browser` sur la même origine. Transmettez-les à Exchange par proxy inverse en conservant Host et en autorisant les mises à niveau WebSocket. Ne supprimez pas les préfixes `/api` et `/ws`. Le serveur de développement Vite transmet API/WS vers `127.0.0.1:18082`. Rust Exchange autorise par défaut les navigateurs de même origine. Configurez les origines exactes avec `REMOTE_CORS_ORIGINS` ; seul un `*` explicite autorise toutes les origines. Worker utilise `CORS_ORIGINS` et ses propres valeurs par défaut. Authentification Bearer et tickets WebSocket à usage unique restent obligatoires ; les cookies interorigines sont désactivés.

L'authentification navigateur utilise `POST /api/ws-ticket` avec autorisation Bearer, puis `GET /ws/browser` et une première trame JSON `{"ticket":"..."}`. Les tickets expirent après 30 secondes, sont à usage unique et revérifient à leur consommation l'état actif de l'opérateur et la validité des identifiants. Les identifiants durables ne sont jamais placés dans les URL WebSocket. Les clients natifs continuent d'utiliser `/ws/operator`.

Toutes les opérations sur les appareils utilisent des sessions WebSocket scellées natives. HTTP fournit l’authentification, les métadonnées des appareils, les annuaires des clés et autorisations, ainsi que l’administration des sessions du relais ; voir l’[API HTTP pour les opérations Agent](/docs/fr/http-api.md).

## Activer Web Push

Définissez ces deux variables dans l'environnement d'Exchange (leur absence à toutes deux désactive le push) :

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

Générez une clé privée avec `python scripts/generate-vapid-key.py`. Conservez-la hors de Git dans un fichier d'environnement à accès restreint, stable entre redémarrages, et sauvegardez-la de manière sécurisée. La clé publique est dérivée automatiquement. Changer de clé oblige les navigateurs à se réabonner.

`WEB_PUSH_ALLOWED_ORIGINS` contient par défaut les origines de confiance exactes `https://fcm.googleapis.com,https://updates.push.services.mozilla.com,https://web.push.apple.com`. N'ajoutez un autre fournisseur de navigateur qu'après vérification de son origine push HTTPS. Les adresses fournies par les clients ne peuvent pas étendre cette liste. Les redirections ne sont jamais suivies.

Routes authentifiées :

| Méthode et chemin | Fonction |
| --- | --- |
| GET `/api/webpush/config` | État d'activation et clé publique du serveur d'application |
| GET/POST `/api/webpush/subscriptions` | Lister les métadonnées de ses abonnements, ou créer/mettre à jour un abonnement |
| DELETE `/api/webpush/subscriptions/{id}` | Supprimer un de ses abonnements |
| POST `/api/webpush/test` | Mettre en file un test visible pour les abonnements de l'appelant |
| POST `/api/webpush/messages` | Envoyer à soi-même ou, comme administrateur, à un autre compte du même espace |
| POST `/api/webpush/devices/{id}/messages` | Notifier les administrateurs abonnés qui gèrent cet appareil authentifié |

Les abonnements utilisent `PushSubscription.toJSON()` du navigateur, `application_server_key` et `triggers`. `triggers` contient au plus un objet `online` et un objet `offline`. Chaque objet possède `kind`, `tick_count` (3 par défaut, de 1 à 100) et éventuellement `device_ids`. `offline` notifie après `tick_count` échecs consécutifs ; `online` après `tick_count` réussites consécutives. `device_ids` est un identifiant d'appareil (chaîne), une liste d'identifiants, ou absent/`null` pour tous les appareils de l'espace de travail. Un tableau `triggers` vide abonne le navigateur sans alertes automatiques en ligne/hors ligne. `missed_count` et `success_count` sont refusés. Les messages utilisent éventuellement `operator_id` et `notification` avec `title`, `body`, ainsi que `url` relatif de même origine et `tag` facultatifs. `/webpush/` est un client de référence minimal ; la console de production utilise son propre worker limité à `/console/`.

La migration `0005_web_push.sql` ajoute les abonnements et la boîte d'envoi durable. `0008_online_offline_triggers.sql` transfère les notifications d'appareils vers `triggers` et supprime `missed_count` / `success_count`. `202` signifie mis en file, sans garantir livraison ni affichage. Les tâches expirent après 15 minutes. Les erreurs réseau, 408, 429 et 5xx sont réessayées jusqu'à cinq tentatives ; 404/410 suppriment la révision correspondante de l'abonnement. Un bail de 90 secondes permet la reprise après défaillance d'un worker. La livraison suit une sémantique « au moins une fois » ; l'affichage dépend du fournisseur, des autorisations et de la politique du système d'exploitation. Avant mise en file durable, les événements d'appareil sont traités au mieux en cas de surcharge ; ceux déjà en file survivent aux redémarrages. Limites : 20 abonnements/compte, 32 messages en file/abonnement et 30 modifications/minute/compte.

iOS/iPadOS peut exiger l'ajout de la console à l'écran d'accueil avant l'activation des notifications. L'accessibilité du service Push du navigateur est indépendante de la connexion à Exchange.

## Validation

Exécutez `cargo test --workspace`, `cargo clippy --workspace --all-targets -- -D warnings` et les tests/la compilation npm de la console. Pour le test d'intégration PostgreSQL/HTTPS, installez `psycopg[binary]`, définissez `WEB_PUSH_TEST_ADMIN_URL` sur une connexion PostgreSQL de test dotée du droit CREATE DATABASE, puis lancez `python scripts/test-webpush.py`. Il crée puis supprime une base jetable ; ne lui donnez jamais un rôle de base de données possédant des privilèges involontaires.

## Envoyer depuis chaque client

Web Console propose un formulaire dans les notifications du navigateur. Les paramètres de la Console mobile proposent l’envoi Web Push, un test et un lien vers Web Console. Les paramètres locaux de l’Agent de bureau, ceux de l’Agent Android et la page de configuration de l’extension utilisent les identifiants de l’appareil. Les Agents sans interface/OpenWrt utilisent la commande suivante. Activez les notifications dans Web Console/PWA pour les recevoir ; aucun récepteur natif FCM/APNs n’est ajouté.

```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` utilise l’authentification Bearer de l’opérateur. Sans `operator_id`, le destinataire est votre compte ; un autre compte exige des droits administrateur et le même espace de travail. Le JSON ci-dessus fonctionne aussi avec `remote-cli webpush send --json-file message.json`. Des déclencheurs d’état vides désactivent les alertes automatiques en ligne/hors ligne tout en conservant les messages généraux.

POST `/api/webpush/devices/{id}/messages` utilise le jeton Bearer de cet appareil et accepte directement un objet notification (`title`, `body`, `tag` facultatif). Seuls les abonnements actifs des administrateurs activés qui gèrent cet appareil sont ciblés. Le serveur fixe `device_id`, `event=device.message`, `occurred_at` et `/console/` ; l’expéditeur ne choisit ni compte ni autre appareil. Les appareils partagent 30 requêtes/minute/espace, avec 256 abonnements maximum par envoi. Les limites des comptes et files restent applicables. Sans destinataire actif, la réponse est `202 {"queued":0}` ; `queued` compte les abonnements, pas les livraisons.
