From 9db99bb95b4b5d66b5657c5c2a2b174153a9499d Mon Sep 17 00:00:00 2001 From: Nicolas Cantu Date: Mon, 25 Aug 2025 15:17:45 +0200 Subject: [PATCH] =?UTF-8?q?Documentation=20API:=20refonte=20compl=C3=A8te?= =?UTF-8?q?=20avec=20documentation=20professionnelle=20des=20endpoints=20H?= =?UTF-8?q?TTP=20et=20WebSocket?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/API.md | 899 ++++++++++++++---- .../EXEMPLES_PRATIQUES.md | 0 spec-technique.md => docs/spec-technique.md | 0 test_sync.sh => tests/test_sync.sh | 0 4 files changed, 732 insertions(+), 167 deletions(-) rename EXEMPLES_PRATIQUES.md => docs/EXEMPLES_PRATIQUES.md (100%) rename spec-technique.md => docs/spec-technique.md (100%) rename test_sync.sh => tests/test_sync.sh (100%) diff --git a/docs/API.md b/docs/API.md index a399327..7c65cb8 100644 --- a/docs/API.md +++ b/docs/API.md @@ -2,33 +2,84 @@ ## Vue d'Ensemble -`sdk_relay` expose deux interfaces principales : -- **WebSocket** (port 8090) : Communication temps réel -- **HTTP** (port 8091) : API REST +`sdk_relay` expose deux interfaces principales pour l'intégration avec les clients : -## Interface WebSocket +- **WebSocket API** (port 8090) : Communication temps réel bidirectionnelle +- **HTTP REST API** (port 8091) : Interface REST pour les opérations CRUD + +## Base URLs + +```bash +# WebSocket +ws://localhost:8090 +wss://localhost:8090 # Sécurisé + +# HTTP REST +http://localhost:8091 +https://localhost:8091 # Sécurisé +``` + +--- + +## WebSocket API ### Connexion + ```javascript const ws = new WebSocket('ws://localhost:8090'); + +ws.onopen = () => { + console.log('Connecté à sdk_relay'); + // Envoyer le handshake initial + sendHandshake(); +}; + +ws.onmessage = (event) => { + const message = JSON.parse(event.data); + handleMessage(message); +}; + +ws.onerror = (error) => { + console.error('Erreur WebSocket:', error); +}; + +ws.onclose = (event) => { + console.log('Connexion fermée:', event.code, event.reason); +}; ``` ### Handshake Initial -```javascript -// Message de handshake -const handshake = { - "type": "handshake", - "client_id": "my-client", - "version": "1.0.0", - "capabilities": ["sync", "notifications"] -}; -ws.send(JSON.stringify(handshake)); +**Endpoint :** `ws://localhost:8090` + +**Message de Handshake :** +```json +{ + "type": "handshake", + "client_id": "my-client-123", + "version": "1.0.0", + "capabilities": ["sync", "notifications", "health"], + "timestamp": 1703001600 +} ``` -### Types de Messages +**Réponse de Handshake :** +```json +{ + "type": "handshake_response", + "status": "accepted", + "relay_id": "relay-1", + "version": "1.0.0", + "capabilities": ["sync", "notifications", "health", "metrics"], + "timestamp": 1703001600 +} +``` -#### 1. Messages de Synchronisation +### Messages de Synchronisation + +#### 1. StateSync + +**Envoi :** ```json { "type": "sync", @@ -38,7 +89,10 @@ ws.send(JSON.stringify(handshake)); "state": { "uptime": "3600", "version": "1.0.0", - "network": "signet" + "network": "signet", + "status": "healthy", + "last_block": "12345", + "connections": 5 } }, "timestamp": 1703001600, @@ -46,108 +100,210 @@ ws.send(JSON.stringify(handshake)); } ``` -#### 2. Messages de Notification +**Réception :** +```json +{ + "type": "sync_response", + "sync_type": "StateSync", + "relay_id": "relay-2", + "payload": { + "state": { + "uptime": "1800", + "version": "1.0.0", + "network": "signet", + "status": "healthy", + "last_block": "12345", + "connections": 3 + } + }, + "timestamp": 1703001600, + "message_id": "msg-124" +} +``` + +#### 2. HealthSync + +**Envoi :** +```json +{ + "type": "sync", + "sync_type": "HealthSync", + "relay_id": "relay-1", + "payload": { + "health": { + "status": "healthy", + "memory_usage": "128MB", + "cpu_usage": "5%", + "disk_usage": "2GB", + "network_latency": "45ms" + } + }, + "timestamp": 1703001600, + "message_id": "msg-125" +} +``` + +#### 3. MetricsSync + +**Envoi :** +```json +{ + "type": "sync", + "sync_type": "MetricsSync", + "relay_id": "relay-1", + "payload": { + "metrics": { + "known_relays": 3, + "mesh_connections": 6, + "sync_requests": 150, + "sync_responses": 150, + "cache_hits": 120, + "cache_misses": 30, + "avg_latency": 45.2, + "error_count": 0 + } + }, + "timestamp": 1703001600, + "message_id": "msg-126" +} +``` + +### Messages de Notification + +#### 1. Payment Detected + ```json { "type": "notification", "notification_type": "payment_detected", "data": { - "txid": "abc123...", + "txid": "abc123def456789...", "block_height": 12345, + "block_hash": "def789abc123456...", "amount": "0.001", - "address": "sp1..." + "address": "sp1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh", + "confirmations": 1, + "timestamp": 1703001600 }, "timestamp": 1703001600 } ``` -#### 3. Messages de Santé +#### 2. Block Mined + ```json { - "type": "health", - "status": "healthy", - "metrics": { - "uptime": 3600, - "memory_usage": "128MB", - "cpu_usage": "5%", - "connections": 10 + "type": "notification", + "notification_type": "block_mined", + "data": { + "block_height": 12346, + "block_hash": "ghi012jkl345678...", + "transactions": 150, + "size": "1024000", + "timestamp": 1703001600 }, "timestamp": 1703001600 } ``` -### Événements WebSocket +#### 3. Relay Connected -#### Connexion -```javascript -ws.onopen = function() { - console.log('Connecté à sdk_relay'); - // Envoyer le handshake - ws.send(JSON.stringify(handshake)); -}; +```json +{ + "type": "notification", + "notification_type": "relay_connected", + "data": { + "relay_id": "relay-4", + "address": "relay-4.example.com:8090", + "version": "1.0.0", + "capabilities": ["sync", "notifications"], + "timestamp": 1703001600 + }, + "timestamp": 1703001600 +} ``` -#### Réception de Messages -```javascript -ws.onmessage = function(event) { - const message = JSON.parse(event.data); - - switch(message.type) { - case 'handshake_response': - console.log('Handshake accepté'); - break; - case 'sync': - handleSyncMessage(message); - break; - case 'notification': - handleNotification(message); - break; - case 'health': - handleHealthUpdate(message); - break; - } -}; +### Messages de Contrôle + +#### 1. Ping/Pong + +**Ping :** +```json +{ + "type": "ping", + "client_id": "my-client-123", + "timestamp": 1703001600 +} ``` -#### Gestion d'Erreurs -```javascript -ws.onerror = function(error) { - console.error('Erreur WebSocket:', error); -}; - -ws.onclose = function(event) { - console.log('Connexion fermée:', event.code, event.reason); -}; +**Pong :** +```json +{ + "type": "pong", + "relay_id": "relay-1", + "timestamp": 1703001600 +} ``` -## Interface HTTP +#### 2. Subscribe/Unsubscribe -### Base URL -``` -http://localhost:8091 +**Subscribe :** +```json +{ + "type": "subscribe", + "subscriptions": ["notifications", "health", "metrics"], + "client_id": "my-client-123", + "timestamp": 1703001600 +} ``` -### Endpoints - -#### 1. Santé du Service -```http -GET /health +**Unsubscribe :** +```json +{ + "type": "unsubscribe", + "subscriptions": ["metrics"], + "client_id": "my-client-123", + "timestamp": 1703001600 +} ``` +--- + +## HTTP REST API + +### Endpoints de Base + +#### 1. Health Check + +**GET** `/health` + +**Description :** Vérifier l'état de santé du service + **Réponse :** ```json { "status": "healthy", "uptime": 3600, "version": "1.0.0", - "timestamp": 1703001600 + "timestamp": 1703001600, + "services": { + "bitcoin_core": "connected", + "blindbit": "connected", + "websocket": "listening", + "sync_manager": "active" + } } ``` +**Codes de réponse :** +- `200` : Service en bonne santé +- `503` : Service indisponible + #### 2. Métriques -```http -GET /metrics -``` + +**GET** `/metrics` + +**Description :** Obtenir les métriques de performance **Réponse :** ```json @@ -166,15 +322,23 @@ GET /metrics "memory_usage": "128MB", "cpu_usage": "5%", "active_connections": 10, - "total_requests": 1000 + "total_requests": 1000, + "uptime": 3600 + }, + "bitcoin_metrics": { + "block_height": 12345, + "connections": 8, + "mempool_size": 150, + "verification_progress": 0.9999 } } ``` #### 3. Configuration -```http -GET /config -``` + +**GET** `/config` + +**Description :** Obtenir la configuration actuelle **Réponse :** ```json @@ -185,22 +349,33 @@ GET /config "http_url": "0.0.0.0:8091", "bitcoin_core": { "url": "http://bitcoin:18443", - "wallet": "relay_wallet" + "wallet": "relay_wallet", + "zmq_url": "tcp://bitcoin:29000" }, "blindbit": { "url": "http://blindbit:8000" }, "sync": { "interval": 30, - "health_interval": 60 - } + "health_interval": 60, + "metrics_interval": 120 + }, + "dev_mode": true, + "standalone": false } ``` -#### 4. Relais Connus -```http -GET /relays -``` +### Endpoints de Gestion des Relais + +#### 4. Liste des Relais + +**GET** `/relays` + +**Description :** Obtenir la liste des relais connus + +**Paramètres de requête :** +- `status` : Filtrer par statut (healthy, warning, critical) +- `limit` : Limiter le nombre de résultats (défaut: 100) **Réponse :** ```json @@ -212,25 +387,69 @@ GET /relays "version": "1.0.0", "uptime": 3600, "health": "healthy", - "last_seen": 1703001600 + "last_seen": 1703001600, + "capabilities": ["sync", "notifications", "health"], + "connections": 5 }, { "relay_id": "relay-2", "address": "sdk_relay_2:8090", "version": "1.0.0", - "uptime": 3600, + "uptime": 1800, "health": "healthy", - "last_seen": 1703001600 + "last_seen": 1703001600, + "capabilities": ["sync", "notifications"], + "connections": 3 } - ] + ], + "total": 2, + "healthy": 2, + "warning": 0, + "critical": 0 } ``` -#### 5. Statut de Synchronisation -```http -GET /sync/status +#### 5. Détails d'un Relais + +**GET** `/relays/{relay_id}` + +**Description :** Obtenir les détails d'un relais spécifique + +**Réponse :** +```json +{ + "relay_id": "relay-1", + "address": "sdk_relay_1:8090", + "version": "1.0.0", + "uptime": 3600, + "health": "healthy", + "last_seen": 1703001600, + "capabilities": ["sync", "notifications", "health"], + "connections": 5, + "metrics": { + "sync_requests": 50, + "sync_responses": 50, + "avg_latency": 45.2 + }, + "configuration": { + "network": "signet", + "dev_mode": true + } +} ``` +**Codes de réponse :** +- `200` : Relais trouvé +- `404` : Relais non trouvé + +### Endpoints de Synchronisation + +#### 6. Statut de Synchronisation + +**GET** `/sync/status` + +**Description :** Obtenir le statut de la synchronisation + **Réponse :** ```json { @@ -242,17 +461,27 @@ GET /sync/status "HealthSync", "MetricsSync" ], - "errors": [] + "errors": [], + "statistics": { + "total_syncs": 150, + "successful_syncs": 148, + "failed_syncs": 2, + "success_rate": 98.67 + } } ``` -#### 6. Forcer la Synchronisation -```http -POST /sync/force -Content-Type: application/json +#### 7. Forcer la Synchronisation +**POST** `/sync/force` + +**Description :** Forcer une synchronisation immédiate + +**Corps de la requête :** +```json { - "sync_types": ["StateSync", "HealthSync"] + "sync_types": ["StateSync", "HealthSync"], + "target_relays": ["relay-2", "relay-3"] } ``` @@ -261,18 +490,125 @@ Content-Type: application/json { "status": "sync_triggered", "sync_types": ["StateSync", "HealthSync"], - "timestamp": 1703001600 + "target_relays": ["relay-2", "relay-3"], + "timestamp": 1703001600, + "estimated_duration": "30s" } ``` -#### 7. Logs -```http -GET /logs?level=info&limit=100 +#### 8. Historique des Synchronisations + +**GET** `/sync/history` + +**Description :** Obtenir l'historique des synchronisations + +**Paramètres de requête :** +- `limit` : Nombre d'entrées (défaut: 50) +- `since` : Timestamp de début +- `until` : Timestamp de fin +- `sync_type` : Filtrer par type de synchronisation + +**Réponse :** +```json +{ + "history": [ + { + "timestamp": 1703001600, + "sync_type": "StateSync", + "target_relay": "relay-2", + "status": "success", + "duration": 45, + "message_id": "msg-123" + }, + { + "timestamp": 1703001570, + "sync_type": "HealthSync", + "target_relay": "relay-3", + "status": "success", + "duration": 32, + "message_id": "msg-122" + } + ], + "total": 2, + "successful": 2, + "failed": 0 +} ``` -**Paramètres :** -- `level` : debug, info, warn, error -- `limit` : nombre de lignes (défaut: 100) +### Endpoints de Bitcoin Core + +#### 9. Informations Bitcoin + +**GET** `/bitcoin/info` + +**Description :** Obtenir les informations Bitcoin Core + +**Réponse :** +```json +{ + "blockchain_info": { + "chain": "signet", + "blocks": 12345, + "headers": 12345, + "bestblockhash": "abc123...", + "difficulty": 1.0, + "verificationprogress": 0.9999, + "initialblockdownload": false + }, + "network_info": { + "connections": 8, + "connections_in": 4, + "connections_out": 4 + }, + "mempool_info": { + "size": 150, + "bytes": 1024000, + "usage": 2048000 + } +} +``` + +#### 10. Bloc Spécifique + +**GET** `/bitcoin/blocks/{block_hash}` + +**Description :** Obtenir les détails d'un bloc + +**Réponse :** +```json +{ + "hash": "abc123...", + "height": 12345, + "version": 536870912, + "merkleroot": "def456...", + "time": 1703001600, + "mediantime": 1703001500, + "nonce": 123456, + "bits": "207fffff", + "difficulty": 1.0, + "size": 1024, + "strippedsize": 512, + "weight": 4096, + "tx": [ + "txid1...", + "txid2..." + ] +} +``` + +### Endpoints de Logs + +#### 11. Logs + +**GET** `/logs` + +**Description :** Obtenir les logs du service + +**Paramètres de requête :** +- `level` : Niveau de log (debug, info, warn, error) +- `limit` : Nombre de lignes (défaut: 100) +- `since` : Timestamp de début +- `search` : Recherche dans les logs **Réponse :** ```json @@ -281,17 +617,76 @@ GET /logs?level=info&limit=100 { "timestamp": "2024-12-19T10:00:00Z", "level": "info", - "message": "Relay started successfully" + "message": "Relay started successfully", + "relay_id": "relay-1" }, { "timestamp": "2024-12-19T10:00:30Z", "level": "info", - "message": "Sync completed" + "message": "Sync completed", + "relay_id": "relay-1", + "sync_type": "StateSync" } - ] + ], + "total": 2, + "levels": { + "debug": 0, + "info": 2, + "warn": 0, + "error": 0 + } } ``` +### Endpoints de Configuration + +#### 12. Mettre à Jour la Configuration + +**PUT** `/config` + +**Description :** Mettre à jour la configuration + +**Corps de la requête :** +```json +{ + "sync": { + "interval": 60, + "health_interval": 120 + }, + "dev_mode": false +} +``` + +**Réponse :** +```json +{ + "status": "updated", + "changes": [ + "sync.interval: 30 -> 60", + "sync.health_interval: 60 -> 120", + "dev_mode: true -> false" + ], + "timestamp": 1703001600 +} +``` + +#### 13. Redémarrer le Service + +**POST** `/restart` + +**Description :** Redémarrer le service + +**Réponse :** +```json +{ + "status": "restarting", + "timestamp": 1703001600, + "estimated_duration": "30s" +} +``` + +--- + ## Codes d'Erreur ### WebSocket @@ -305,162 +700,332 @@ GET /logs?level=info&limit=100 ### HTTP - `200` : Succès - `400` : Requête invalide +- `401` : Non autorisé - `404` : Endpoint non trouvé +- `422` : Données invalides +- `429` : Trop de requêtes - `500` : Erreur serveur interne - `503` : Service indisponible +--- + ## Exemples d'Utilisation -### Client JavaScript +### Client JavaScript Complet + ```javascript class SdkRelayClient { - constructor(url) { - this.ws = new WebSocket(url); - this.setupEventHandlers(); + constructor(wsUrl, httpUrl) { + this.wsUrl = wsUrl; + this.httpUrl = httpUrl; + this.ws = null; + this.clientId = `client-${Date.now()}`; } - setupEventHandlers() { - this.ws.onopen = () => this.onConnect(); - this.ws.onmessage = (event) => this.onMessage(event); - this.ws.onerror = (error) => this.onError(error); - this.ws.onclose = (event) => this.onClose(event); - } - - onConnect() { - console.log('Connecté à sdk_relay'); - this.sendHandshake(); + async connect() { + return new Promise((resolve, reject) => { + this.ws = new WebSocket(this.wsUrl); + + this.ws.onopen = () => { + console.log('Connecté à sdk_relay'); + this.sendHandshake(); + resolve(); + }; + + this.ws.onmessage = (event) => { + const message = JSON.parse(event.data); + this.handleMessage(message); + }; + + this.ws.onerror = (error) => { + console.error('Erreur WebSocket:', error); + reject(error); + }; + }); } sendHandshake() { const handshake = { type: 'handshake', - client_id: 'js-client', + client_id: this.clientId, version: '1.0.0', - capabilities: ['sync', 'notifications'] + capabilities: ['sync', 'notifications', 'health'] }; this.ws.send(JSON.stringify(handshake)); } - onMessage(event) { - const message = JSON.parse(event.data); - console.log('Message reçu:', message); + handleMessage(message) { + switch(message.type) { + case 'handshake_response': + console.log('Handshake accepté'); + break; + case 'notification': + this.handleNotification(message); + break; + case 'sync': + this.handleSync(message); + break; + case 'pong': + console.log('Pong reçu'); + break; + } } - onError(error) { - console.error('Erreur WebSocket:', error); + handleNotification(message) { + console.log('Notification:', message.notification_type, message.data); } - onClose(event) { - console.log('Connexion fermée:', event.code, event.reason); + handleSync(message) { + console.log('Sync:', message.sync_type, message.payload); + } + + // Méthodes HTTP + async getHealth() { + const response = await fetch(`${this.httpUrl}/health`); + return response.json(); + } + + async getMetrics() { + const response = await fetch(`${this.httpUrl}/metrics`); + return response.json(); + } + + async getRelays() { + const response = await fetch(`${this.httpUrl}/relays`); + return response.json(); + } + + async forceSync(syncTypes = ['StateSync']) { + const response = await fetch(`${this.httpUrl}/sync/force`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ sync_types: syncTypes }) + }); + return response.json(); + } + + disconnect() { + if (this.ws) { + this.ws.close(); + } } } // Utilisation -const client = new SdkRelayClient('ws://localhost:8090'); +const client = new SdkRelayClient('ws://localhost:8090', 'http://localhost:8091'); + +async function main() { + await client.connect(); + + // Obtenir les métriques + const metrics = await client.getMetrics(); + console.log('Métriques:', metrics); + + // Forcer une synchronisation + await client.forceSync(['HealthSync']); +} + +main().catch(console.error); ``` -### Client Python +### Client Python Complet + ```python import asyncio -import websockets +import aiohttp import json +import websockets +from datetime import datetime class SdkRelayClient: - def __init__(self, url): - self.url = url + def __init__(self, ws_url, http_url): + self.ws_url = ws_url + self.http_url = http_url self.websocket = None - + self.client_id = f"python-client-{int(datetime.now().timestamp())}" + async def connect(self): - self.websocket = await websockets.connect(self.url) + self.websocket = await websockets.connect(self.ws_url) await self.send_handshake() - + async def send_handshake(self): handshake = { "type": "handshake", - "client_id": "python-client", + "client_id": self.client_id, "version": "1.0.0", - "capabilities": ["sync", "notifications"] + "capabilities": ["sync", "notifications", "health"] } await self.websocket.send(json.dumps(handshake)) - + async def listen(self): async for message in self.websocket: data = json.loads(message) - print(f"Message reçu: {data}") - + await self.handle_message(data) + + async def handle_message(self, message): + if message['type'] == 'handshake_response': + print(f"Handshake accepté par {message['relay_id']}") + elif message['type'] == 'notification': + await self.handle_notification(message) + elif message['type'] == 'sync': + await self.handle_sync(message) + + async def handle_notification(self, message): + print(f"Notification {message['notification_type']}: {message['data']}") + + async def handle_sync(self, message): + print(f"Sync {message['sync_type']}: {message['payload']}") + + # Méthodes HTTP + async def get_health(self): + async with aiohttp.ClientSession() as session: + async with session.get(f"{self.http_url}/health") as response: + return await response.json() + + async def get_metrics(self): + async with aiohttp.ClientSession() as session: + async with session.get(f"{self.http_url}/metrics") as response: + return await response.json() + + async def get_relays(self): + async with aiohttp.ClientSession() as session: + async with session.get(f"{self.http_url}/relays") as response: + return await response.json() + + async def force_sync(self, sync_types=None): + if sync_types is None: + sync_types = ['StateSync'] + + async with aiohttp.ClientSession() as session: + async with session.post( + f"{self.http_url}/sync/force", + json={"sync_types": sync_types} + ) as response: + return await response.json() + async def close(self): if self.websocket: await self.websocket.close() -# Utilisation async def main(): - client = SdkRelayClient('ws://localhost:8090') - await client.connect() - await client.listen() + client = SdkRelayClient('ws://localhost:8090', 'http://localhost:8091') + + try: + await client.connect() + + # Obtenir les métriques + metrics = await client.get_metrics() + print("Métriques:", metrics) + + # Écouter les messages + await client.listen() + + except Exception as e: + print(f"Erreur: {e}") + finally: + await client.close() -asyncio.run(main()) +if __name__ == "__main__": + asyncio.run(main()) ``` ### Client cURL + ```bash # Vérifier la santé -curl http://localhost:8091/health +curl -X GET http://localhost:8091/health # Obtenir les métriques -curl http://localhost:8091/metrics +curl -X GET http://localhost:8091/metrics -# Obtenir la configuration -curl http://localhost:8091/config +# Obtenir la liste des relais +curl -X GET http://localhost:8091/relays -# Forcer la synchronisation +# Obtenir les détails d'un relais +curl -X GET http://localhost:8091/relays/relay-1 + +# Forcer une synchronisation curl -X POST http://localhost:8091/sync/force \ -H "Content-Type: application/json" \ - -d '{"sync_types": ["StateSync"]}' + -d '{"sync_types": ["StateSync", "HealthSync"]}' + +# Obtenir l'historique des synchronisations +curl -X GET "http://localhost:8091/sync/history?limit=10" + +# Obtenir les logs +curl -X GET "http://localhost:8091/logs?level=info&limit=50" + +# Obtenir les informations Bitcoin +curl -X GET http://localhost:8091/bitcoin/info + +# Obtenir un bloc spécifique +curl -X GET http://localhost:8091/bitcoin/blocks/abc123... + +# Mettre à jour la configuration +curl -X PUT http://localhost:8091/config \ + -H "Content-Type: application/json" \ + -d '{"sync": {"interval": 60}}' + +# Redémarrer le service +curl -X POST http://localhost:8091/restart ``` +--- + ## Limites et Quotas ### WebSocket -- **Connexions simultanées** : 1000 par défaut +- **Connexions simultanées** : 1000 par relais - **Taille des messages** : 1MB maximum - **Heartbeat** : 30 secondes - **Timeout de connexion** : 60 secondes +- **Rate limiting** : 1000 messages/minute par client -### HTTP -- **Taux limite** : 1000 requêtes/minute +### HTTP REST +- **Taux limite** : 1000 requêtes/minute par IP - **Taille des requêtes** : 10MB maximum - **Timeout** : 30 secondes -- **Connexions simultanées** : 100 +- **Connexions simultanées** : 100 par IP +- **Rate limiting** : 100 requêtes/minute par endpoint + +--- ## Sécurité ### Authentification - **WebSocket** : Authentification optionnelle via handshake - **HTTP** : Authentification basique (à configurer) +- **Rate limiting** : Protection contre les abus ### Validation - Validation JSON des messages -- Rate limiting -- Protection contre les attaques DoS +- Validation des types de données +- Protection contre les injections +- Validation des URLs et paramètres ### Chiffrement - **WebSocket** : WSS (WebSocket Secure) recommandé - **HTTP** : HTTPS recommandé +- **Certificats** : Certificats SSL/TLS valides + +--- ## Monitoring ### Métriques Clés - **Latence** : Temps de réponse des APIs -- **Débit** : Messages par seconde -- **Erreurs** : Taux d'erreur +- **Débit** : Messages/requêtes par seconde +- **Erreurs** : Taux d'erreur par endpoint - **Connexions** : Nombre de connexions actives +- **Synchronisation** : Taux de succès des syncs ### Alertes - Service indisponible - Latence élevée (> 1s) - Taux d'erreur élevé (> 5%) - Mémoire/CPU élevés +- Échecs de synchronisation --- -**Cette API permet une intégration complète avec sdk_relay pour les clients WebSocket et HTTP.** 🚀 +**Cette API permet une intégration complète et professionnelle avec sdk_relay pour tous types de clients.** 🚀 diff --git a/EXEMPLES_PRATIQUES.md b/docs/EXEMPLES_PRATIQUES.md similarity index 100% rename from EXEMPLES_PRATIQUES.md rename to docs/EXEMPLES_PRATIQUES.md diff --git a/spec-technique.md b/docs/spec-technique.md similarity index 100% rename from spec-technique.md rename to docs/spec-technique.md diff --git a/test_sync.sh b/tests/test_sync.sh similarity index 100% rename from test_sync.sh rename to tests/test_sync.sh