ecloudocs

Le système de l'API en profondeur

Cette page explique comment l'API fonctionne réellement, de la requête HTTP jusqu'au conteneur Docker qui héberge ton bot. Utile pour intégrer l'API sereinement — et pour comprendre pourquoi elle se comporte comme elle le fait.

Vue d'ensemble

L'API est un service Fastify (Node.js + TypeScript) qui sert de cerveau métier. Elle expose deux surfaces distinctes :

  • API interne (BFF) — consommée par le panel, authentifiée par session (cookie Redis, OAuth Discord).
  • API publique /api/v1 — consommée par tes intégrations, authentifiée par clé d'API. C'est celle documentée ici.

Les deux surfaces partagent les mêmes services métier et la même base de données : une action via clé d'API produit exactement le même effet que la même action dans le panel.

Le cycle d'une requête

Une requête sur la surface publique traverse toujours les mêmes étapes :

Client
  │  Authorization: Bearer ecl_…
  ▼
[1] Rate-limit (120 req/min/IP, Redis)
  ▼
[2] authenticateApiKey — hache la clé, la retrouve en base, vérifie scope
  ▼
[3] Validation Zod du corps / des paramètres
  ▼
[4] Service métier (server.service, offer.service…)
  ▼
[5] SDK Pterodactyl  →  Panel  →  Wings (Docker)
  ▼
Réponse JSON

[1] Limitation de débit

Un plugin de rate-limit adossé à Redis compte les requêtes par IP sur une fenêtre glissante d'une minute. Au-delà de 120, la requête est rejetée en 429 avant même d'atteindre l'authentification — cela protège la base de données et Pterodactyl des pics.

[2] Authentification par clé

C'est le cœur du système. Le décorateur Fastify authenticateApiKey :

  1. Lit l'en-tête Authorization et exige le schéma Bearer .
  2. Vérifie que le jeton commence par le préfixe ecl_.
  3. Calcule le SHA-256 du jeton reçu.
  4. Cherche en base une clé dont le hashedKey correspond.
  5. Rejette si la clé est absente, révoquée, ou si la comparaison échoue.
  6. Peuple req.user et req.apiScopes, puis met à jour lastUsedAt en tâche de fond.

Sécurité — la clé n'est jamais stockée en clair

Seul le hash SHA-256 de la clé (hashedKey) et un préfixe lisible (ecl_ab12cd34, pour l'affichage) sont conservés. Même en cas de fuite de la base, les clés ne peuvent pas être reconstituées. La comparaison des hash utilise timingSafeEqual (comparaison à temps constant) pour ne pas laisser fuiter d'information via le temps de réponse (attaque temporelle).

Le format d'une clé :

ecl_<48 caractères hexadécimaux>
└┬─┘ └──────────┬──────────────┘
préfixe      secret (randomBytes(24))

Scope write

Les routes d'écriture passent par authenticateApiKeyWrite : il exécute d'abord l'authentification standard, puis vérifie que write figure dans les scopes de la clé. Sinon, 403 — la clé est en lecture seule.

[3] Validation des entrées (Zod)

Chaque corps de requête est validé par un schéma Zod avant tout traitement. Exemple pour la création d'un serveur :

z.object({
  name:         z.string().min(3).max(48),
  runtime:      z.enum(["NODEJS", "PYTHON", "JAVA"]),
  ram:          z.number().int().min(128).max(8192),   // Mo
  disk:         z.number().int().min(512).max(51200),  // Mo
  cpu:          z.number().int().min(25).max(400),     // %
  withDatabase: z.boolean().optional(),
})

Un corps non conforme est rejeté en 400 avec le détail des champs fautifs — la logique métier n'est jamais atteinte avec des données invalides.

[4] Services métier & quotas

Les routes sont volontairement minces : elles délèguent aux services(server.service, offer.service…). Avant de provisionner, le service vérifie que la demande respecte le quota de ton offre (RAM, nombre de serveurs). En cas de dépassement, une QuotaExceededError est traduite en 403.

Les services vérifient aussi la propriété : tu ne peux lire ou piloter que tes serveurs. Une ressource appartenant à un autre compte renvoie 404 (et non 403), pour ne pas révéler son existence.

[5] Pont vers Pterodactyl

Pour l'hébergement réel, l'API parle à Pterodactyl via un SDK typé, avec deux clés :

  • Application (ptla_…) — créer/administrer les serveurs.
  • Client (ptlc_…) — power, fichiers, backups, console.

Provisioning asynchrone

Créer un serveur peut prendre plusieurs secondes (allocation d'un node, création du conteneur, installation de l'egg). L'API ne bloque pas la requête HTTP : elle enregistre le serveur en base (statut INSTALLING) et confie le provisioning à une file BullMQ (Redis). L'endpoint répond immédiatement en 201 ; tu suis l'avancement via GET /servers/:id.

Idempotence & bonnes pratiques

  • Les lectures (GET) sont sûres et peuvent être répétées librement.
  • POST /servers/:id/power est idempotent en pratique : renvoyer start à un serveur déjà démarré est sans effet néfaste.
  • Sur 429, respecte Retry-After et applique un backoff.
  • Traite INSTALLING comme un état transitoire : sonde /servers/:id.

Résumé

L'API publique est une couche mince, sécurisée et validée au-dessus des mêmes services que le panel : clés hachées et comparées à temps constant, scopes explicites, quotas appliqués, provisioning asynchrone, et Pterodactyl comme moteur d'exécution. Passe à la référence des endpoints pour l'implémentation.