Documentation J+SERVICES Guides Référence API

Contrat API — Ports d’accès distant & partage de passerelle

Destinataire : développeur de l’application client (mobile) Base : https://live.jmoai.net · auth Authorization: Bearer <jwt client> Enveloppe : { success: boolean, data: …, error?: { code, message } }

Déployé en production le 2026-08-10. Le paramètre port, le champ access_mode et la notification DELEGATED_ROUTER_ACCESS sont actifs.


1. Le principe

Un seul appel renvoie tous les ports d’un routeur. L’application n’a jamais besoin de demander « le port SSH » séparément : elle récupère la matrice entière et y pioche.

Deux familles de ports coexistent, et les confondre est l’erreur la plus fréquente :

   App  ──────►  vpn.mikhmoai.com : 23000        ──────►  routeur : 22
                 └── vpnRedirection.ssh_port              └── local_ports.ssh
                     (port PUBLIC sur le relais)              (port NATIF du routeur)

2. GET /api/v1/clients/remote-access/ports

Requête

Paramètre Requis Rôle
router_serial numéro de série du routeur (alias acceptés : router_sn, sn)
port un port de redirection actif du routeur. Requis uniquement en accès partagé (§3). Alias : current_port, active_port
GET /api/v1/clients/remote-access/ports?router_serial=HXX0EXEMPLE01
GET /api/v1/clients/remote-access/ports?router_serial=HXX0EXEMPLE01&port=17000

Réponse — succès

{
  "success": true,
  "data": {
    "found": true,
    "router_serial": "HXX0EXEMPLE01",
    "allocation_id": "…",
    "access_mode": "OWNER", // ou "DELEGATED" — voir §3
    "online": true,
    "ports_active": true, // tunnel EN LIGNE *et* relais publié
    "tunnel_status": "CONNECTED",
    "health_status": "…",
    "relay_status": "PUBLISHED",
    "last_seen_at": "2026-08-10T…Z",

    "vpnRedirection": {
      "public_host": "vpn.mikhmoai.com",
      "webfig_port": 17000,
      "rest_port": 17000, // ⚠️ identique à webfig_port — voir §5
      "winbox_port": 19000,
      "api_port": 22000,
      "ssh_port": 23000,
    },

    // ⚠️ Valeurs de CONVENTION, pas une mesure du routeur — voir §5
    "local_ports": { "webfig": 8081, "winbox": 8291, "api": 8728, "ssh": 22 },

    "urls": {/* URLs prêtes à ouvrir */},
  },
}

Réponse — routeur inconnu ou non autorisé

HTTP 200, pas une erreur :

{
  "success": true,
  "data": {
    "found": false,
    "online": false,
    "router_serial": "…",
    "message": "Aucun équipement provisionné pour ce numéro de série.",
  },
}

Erreur

Code HTTP Cause
ROUTER_SERIAL_REQUIRED 400 router_serial absent ou vide

3. Partage de passerelle (QR) — l’accès délégué

Le problème résolu

Quand un client partage sa passerelle avec un technicien, l’application ne transmet ni compte ni adresse e-mail : seulement un hôte, un port et des identifiants. Côté serveur, le technicien n’est propriétaire d’aucun équipement — l’appel lui renvoyait donc une matrice vide, et l’application ne pouvait proposer ni SSH, ni SFTP, ni mise à jour du portail captif.

Ce que l’application doit faire

Joindre le port qu’elle détient déjà :

GET /remote-access/ports?router_serial=<SN>&port=<le port reçu par le partage>

Le couple (numéro de série + port actif) vaut preuve d’accès : c’est exactement ce que le partageur a remis volontairement.

Règle d’implémentation : envoyez port dès que vous l’avez, sans chercher à deviner si l’utilisateur est propriétaire. Le paramètre est ignoré quand il l’est.

Lire access_mode

Valeur Sens Usage suggéré
OWNER le routeur est dans le parc de l’utilisateur affichage normal
DELEGATED accès obtenu par partage signaler « accès partagé » dans l’interface

4. Ce que le serveur garantit

L’application n’a donc rien à réimplémenter de ces protections.


5. Pièges à ne pas contourner


6. Quota — à lire avant d’implémenter la boucle de rafraîchissement

100 requêtes / 15 min par IP, partagées par TOUTES les routes /api.

Ce n’est pas théorique : une vue qui sondait une route toutes les 15 secondes a consommé à elle seule la totalité du quota, et ce sont les autres appels — licence, VPN — qui ont échoué en 429. La route fautive, elle, semblait fonctionner.

Pour cet endpoint :


7. Voir aussi