maj
This commit is contained in:
537
docs/03_api_backend.md
Normal file
537
docs/03_api_backend.md
Normal file
@@ -0,0 +1,537 @@
|
||||
|
||||
# 03 – Spécification API Backend (FastAPI)
|
||||
|
||||
Objectif : définir précisément les endpoints backend, les schémas JSON échangés et les règles métier associées.
|
||||
|
||||
L’API sera exposée sous `/api`.
|
||||
|
||||
---
|
||||
|
||||
## 1. Contenu attendu de ce fichier
|
||||
|
||||
Ce document doit servir de référence au développeur backend. Il décrit :
|
||||
|
||||
1. Le format du payload JSON envoyé par le script de benchmark (`bench.sh`) vers le backend.
|
||||
2. Les endpoints REST nécessaires :
|
||||
- Réception d’un benchmark.
|
||||
- Consultation des devices.
|
||||
- Consultation de l’historique des benchmarks.
|
||||
- Gestion des liens constructeur.
|
||||
- Gestion des documents (PDF, images).
|
||||
3. Les règles d’authentification (token simple).
|
||||
4. Les codes d’erreur principaux et attentes de validation.
|
||||
|
||||
---
|
||||
|
||||
## 2. Schéma JSON – payload d’un benchmark
|
||||
|
||||
Endpoint cible : `POST /api/benchmark`
|
||||
|
||||
Le script client envoie un JSON de la forme :
|
||||
|
||||
```json
|
||||
{
|
||||
"device_identifier": "elitedesk-800g3",
|
||||
"bench_script_version": "1.0.0",
|
||||
"hardware": {
|
||||
"cpu": {
|
||||
"vendor": "Intel",
|
||||
"model": "Intel(R) Core(TM) i7-6700 CPU @ 3.40GHz",
|
||||
"microarchitecture": "Skylake",
|
||||
"cores": 4,
|
||||
"threads": 8,
|
||||
"base_freq_ghz": 3.4,
|
||||
"max_freq_ghz": 4.0,
|
||||
"cache_l1_kb": 256,
|
||||
"cache_l2_kb": 1024,
|
||||
"cache_l3_kb": 8192,
|
||||
"flags": ["avx", "avx2", "aes"],
|
||||
"tdp_w": 65
|
||||
},
|
||||
"ram": {
|
||||
"total_mb": 65536,
|
||||
"slots_total": 4,
|
||||
"slots_used": 4,
|
||||
"ecc": false,
|
||||
"layout": [
|
||||
{
|
||||
"slot": "DIMM_A1",
|
||||
"size_mb": 16384,
|
||||
"type": "DDR4",
|
||||
"speed_mhz": 2133,
|
||||
"vendor": "Corsair",
|
||||
"part_number": "XYZ"
|
||||
}
|
||||
]
|
||||
},
|
||||
"gpu": {
|
||||
"vendor": "Intel",
|
||||
"model": "Intel HD Graphics 530",
|
||||
"driver_version": "xxx",
|
||||
"memory_dedicated_mb": null,
|
||||
"memory_shared_mb": 512,
|
||||
"api_support": ["OpenGL 4.5"]
|
||||
},
|
||||
"storage": {
|
||||
"devices": [
|
||||
{
|
||||
"name": "/dev/nvme0n1",
|
||||
"type": "NVMe",
|
||||
"interface": "PCIe 3.0 x4",
|
||||
"capacity_gb": 1000,
|
||||
"vendor": "Samsung",
|
||||
"model": "970 EVO Plus",
|
||||
"smart_health": "PASSED",
|
||||
"temperature_c": 42
|
||||
}
|
||||
],
|
||||
"partitions": [
|
||||
{
|
||||
"name": "/dev/nvme0n1p1",
|
||||
"mount_point": "/",
|
||||
"fs_type": "ext4",
|
||||
"used_gb": 50,
|
||||
"total_gb": 100
|
||||
}
|
||||
]
|
||||
},
|
||||
"network": {
|
||||
"interfaces": [
|
||||
{
|
||||
"name": "eth0",
|
||||
"type": "ethernet",
|
||||
"mac": "aa:bb:cc:dd:ee:ff",
|
||||
"ip": "10.0.0.10",
|
||||
"speed_mbps": 1000,
|
||||
"driver": "e1000e"
|
||||
}
|
||||
]
|
||||
},
|
||||
"motherboard": {
|
||||
"vendor": "HP",
|
||||
"model": "HP 8054",
|
||||
"bios_version": "P01 Ver. 02.48",
|
||||
"bios_date": "2023-05-10"
|
||||
},
|
||||
"os": {
|
||||
"name": "Debian",
|
||||
"version": "12 (bookworm)",
|
||||
"kernel_version": "6.1.0-xx-amd64",
|
||||
"architecture": "x86_64",
|
||||
"virtualization_type": "none"
|
||||
},
|
||||
"sensors": {
|
||||
"cpu_temp_c": 45,
|
||||
"disk_temps_c": {
|
||||
"/dev/nvme0n1": 42
|
||||
}
|
||||
},
|
||||
"raw_info": {
|
||||
"lscpu": "raw text…",
|
||||
"lsblk": "raw text…"
|
||||
}
|
||||
},
|
||||
"results": {
|
||||
"cpu": {
|
||||
"events_per_sec": 12000,
|
||||
"duration_s": 10,
|
||||
"score": 90
|
||||
},
|
||||
"memory": {
|
||||
"throughput_mib_s": 20000,
|
||||
"score": 95
|
||||
},
|
||||
"disk": {
|
||||
"read_mb_s": 1200,
|
||||
"write_mb_s": 1000,
|
||||
"iops_read": 50000,
|
||||
"iops_write": 45000,
|
||||
"latency_ms": 1.2,
|
||||
"score": 94
|
||||
},
|
||||
"network": {
|
||||
"upload_mbps": 930,
|
||||
"download_mbps": 940,
|
||||
"ping_ms": 1.2,
|
||||
"jitter_ms": 0.3,
|
||||
"packet_loss_percent": 0.0,
|
||||
"score": 88
|
||||
},
|
||||
"gpu": {
|
||||
"glmark2_score": null,
|
||||
"score": null
|
||||
},
|
||||
"global_score": 92
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Remarques importantes :
|
||||
|
||||
- Certains champs peuvent être `null` ou absents en fonction de la machine (ex: pas de GPU dédié).
|
||||
- Le backend doit être robuste à l’absence de certains blocs (ex: pas de `network` si aucun test réseau).
|
||||
|
||||
---
|
||||
|
||||
## 3. Logique de traitement côté backend
|
||||
|
||||
1. **Auth**
|
||||
- Lecture du header `Authorization: Bearer <TOKEN>`.
|
||||
- Comparaison avec une valeur stockée dans la config/ENV (`API_TOKEN`).
|
||||
- Si token invalide ou absent : réponse 401.
|
||||
|
||||
2. **Résolution du device**
|
||||
- Chercher un enregistrement `devices` avec `hostname == device_identifier`.
|
||||
- Si trouvé :
|
||||
- Utiliser `device_id` existant.
|
||||
- Sinon :
|
||||
- Créer un nouveau `device` minimal avec :
|
||||
- `hostname = device_identifier`
|
||||
- `created_at`, `updated_at` = maintenant.
|
||||
|
||||
3. **Création d’un hardware_snapshot**
|
||||
- Mapper le contenu `hardware` vers la table `hardware_snapshots` telle que définie dans `02_model_donnees.md`.
|
||||
- Exemples :
|
||||
- `hardware.cpu.vendor` -> `cpu_vendor`
|
||||
- `hardware.ram.total_mb` -> `ram_total_mb`
|
||||
- `hardware.storage.devices` -> JSON dans `storage_devices_json`
|
||||
- etc.
|
||||
- `captured_at` = `now()` (ou `run_at` du benchmark si fourni).
|
||||
|
||||
4. **Création d’un benchmark**
|
||||
- `run_at` = `now()` (ou fourni explicitement plus tard).
|
||||
- `bench_script_version` = champ du payload.
|
||||
- Scores :
|
||||
- `cpu_score` = `results.cpu.score` (si présent).
|
||||
- `memory_score` = `results.memory.score`
|
||||
- `disk_score` = `results.disk.score`
|
||||
- `network_score` = `results.network.score`
|
||||
- `gpu_score` = `results.gpu.score`
|
||||
- `global_score` = `results.global_score`
|
||||
- `details_json` = JSON complet de `results` (ou éventuellement de `results` + métriques brutes).
|
||||
|
||||
5. **Réponse**
|
||||
- En cas de succès, renvoyer :
|
||||
```json
|
||||
{
|
||||
"status": "ok",
|
||||
"device_id": 1,
|
||||
"benchmark_id": 42
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Endpoints détaillés
|
||||
|
||||
### 4.1. POST /api/benchmark
|
||||
|
||||
- Rôle : point d’entrée unique des résultats du script client.
|
||||
- Auth : header `Authorization: Bearer <TOKEN>`.
|
||||
- Body : JSON (voir schéma ci-dessus).
|
||||
- Codes de réponse :
|
||||
- `200` – succès.
|
||||
- `400` – JSON invalide / validation Pydantic échouée.
|
||||
- `401` – token invalide ou manquant.
|
||||
- `500` – erreur interne.
|
||||
|
||||
---
|
||||
|
||||
### 4.2. GET /api/devices
|
||||
|
||||
Objectif : lister les devices avec un résumé de leur dernier benchmark.
|
||||
|
||||
Paramètres query :
|
||||
- `page` (int, optionnel, défaut 1)
|
||||
- `page_size` (int, optionnel, défaut 20)
|
||||
- `search` (string, optionnel – filtre sur hostname/description/tags)
|
||||
|
||||
Réponse (exemple simplifié) :
|
||||
|
||||
```json
|
||||
{
|
||||
"items": [
|
||||
{
|
||||
"id": 1,
|
||||
"hostname": "elitedesk-800g3",
|
||||
"description": "HP EliteDesk 800 G3 SFF",
|
||||
"location": "Bureau",
|
||||
"tags": ["lab", "dev"],
|
||||
"last_benchmark": {
|
||||
"id": 42,
|
||||
"run_at": "2025-12-07T10:32:00Z",
|
||||
"global_score": 92,
|
||||
"cpu_score": 90,
|
||||
"memory_score": 95,
|
||||
"disk_score": 94,
|
||||
"network_score": 88,
|
||||
"gpu_score": null
|
||||
}
|
||||
}
|
||||
],
|
||||
"total": 1,
|
||||
"page": 1,
|
||||
"page_size": 20
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4.3. GET /api/devices/{device_id}
|
||||
|
||||
Objectif : récupérer le détail d’un device.
|
||||
|
||||
Réponse (exemple) :
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 1,
|
||||
"hostname": "elitedesk-800g3",
|
||||
"fqdn": "elitedesk-800g3.maison43",
|
||||
"description": "HP EliteDesk 800 G3 SFF (serveur dev)",
|
||||
"asset_tag": "LAB-001",
|
||||
"location": "Bureau",
|
||||
"owner": "Gilles",
|
||||
"tags": ["lab", "proxmox"],
|
||||
"created_at": "2025-12-01T10:00:00Z",
|
||||
"updated_at": "2025-12-07T10:32:00Z",
|
||||
"last_hardware_snapshot": {
|
||||
"captured_at": "2025-12-07T10:32:00Z",
|
||||
"cpu_vendor": "Intel",
|
||||
"cpu_model": "Intel(R) Core(TM) i7-6700 CPU @ 3.40GHz",
|
||||
"cpu_cores": 4,
|
||||
"cpu_threads": 8,
|
||||
"ram_total_mb": 65536,
|
||||
"storage_summary": "NVMe 1TB + HDD 2TB",
|
||||
"gpu_summary": "Intel HD Graphics 530",
|
||||
"os_name": "Debian",
|
||||
"os_version": "12 (bookworm)",
|
||||
"kernel_version": "6.1.0-xx-amd64"
|
||||
},
|
||||
"last_benchmark": {
|
||||
"id": 42,
|
||||
"run_at": "2025-12-07T10:32:00Z",
|
||||
"global_score": 92,
|
||||
"cpu_score": 90,
|
||||
"memory_score": 95,
|
||||
"disk_score": 94,
|
||||
"network_score": 88,
|
||||
"gpu_score": null
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4.4. GET /api/devices/{device_id}/benchmarks
|
||||
|
||||
Objectif : récupérer l’historique de benchmarks d’un device.
|
||||
|
||||
Paramètres :
|
||||
- `limit` (int, défaut 20)
|
||||
- `offset` (int, défaut 0)
|
||||
|
||||
Réponse :
|
||||
|
||||
```json
|
||||
{
|
||||
"items": [
|
||||
{
|
||||
"id": 42,
|
||||
"run_at": "2025-12-07T10:32:00Z",
|
||||
"global_score": 92,
|
||||
"cpu_score": 90,
|
||||
"memory_score": 95,
|
||||
"disk_score": 94,
|
||||
"network_score": 88,
|
||||
"gpu_score": null
|
||||
},
|
||||
{
|
||||
"id": 35,
|
||||
"run_at": "2025-12-05T09:10:00Z",
|
||||
"global_score": 89,
|
||||
"cpu_score": 88,
|
||||
"memory_score": 92,
|
||||
"disk_score": 90,
|
||||
"network_score": 85,
|
||||
"gpu_score": null
|
||||
}
|
||||
],
|
||||
"total": 2,
|
||||
"limit": 20,
|
||||
"offset": 0
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4.5. GET /api/benchmarks/{benchmark_id}
|
||||
|
||||
Objectif : récupérer les détails complets d’un benchmark.
|
||||
|
||||
Réponse :
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 42,
|
||||
"device_id": 1,
|
||||
"hardware_snapshot_id": 10,
|
||||
"run_at": "2025-12-07T10:32:00Z",
|
||||
"bench_script_version": "1.0.0",
|
||||
"global_score": 92,
|
||||
"cpu_score": 90,
|
||||
"memory_score": 95,
|
||||
"disk_score": 94,
|
||||
"network_score": 88,
|
||||
"gpu_score": null,
|
||||
"details": {
|
||||
"cpu": {
|
||||
"events_per_sec": 12000,
|
||||
"duration_s": 10,
|
||||
"score": 90
|
||||
},
|
||||
"memory": {
|
||||
"throughput_mib_s": 20000,
|
||||
"score": 95
|
||||
},
|
||||
"disk": {
|
||||
"read_mb_s": 1200,
|
||||
"write_mb_s": 1000,
|
||||
"iops_read": 50000,
|
||||
"iops_write": 45000,
|
||||
"latency_ms": 1.2,
|
||||
"score": 94
|
||||
},
|
||||
"network": {
|
||||
"upload_mbps": 930,
|
||||
"download_mbps": 940,
|
||||
"ping_ms": 1.2,
|
||||
"jitter_ms": 0.3,
|
||||
"packet_loss_percent": 0,
|
||||
"score": 88
|
||||
},
|
||||
"gpu": {
|
||||
"glmark2_score": null,
|
||||
"score": null
|
||||
},
|
||||
"global_score": 92
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Note : `details` reflète `details_json` de la base.
|
||||
|
||||
---
|
||||
|
||||
### 4.6. Liens constructeur
|
||||
|
||||
#### GET /api/devices/{device_id}/links
|
||||
|
||||
Réponse :
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": 1,
|
||||
"label": "Support HP",
|
||||
"url": "https://support.hp.com/..."
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"label": "Page produit",
|
||||
"url": "https://www.hp.com/..."
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
#### POST /api/devices/{device_id}/links
|
||||
|
||||
Body :
|
||||
|
||||
```json
|
||||
{
|
||||
"label": "Support HP",
|
||||
"url": "https://support.hp.com/..."
|
||||
}
|
||||
```
|
||||
|
||||
Réponse : l’objet créé avec `id`.
|
||||
|
||||
#### PUT /api/links/{id}
|
||||
|
||||
Body idem POST (label + url).
|
||||
|
||||
#### DELETE /api/links/{id}
|
||||
|
||||
- Réponse : `204 No Content` en cas de succès.
|
||||
|
||||
---
|
||||
|
||||
### 4.7. Documents
|
||||
|
||||
#### GET /api/devices/{device_id}/docs
|
||||
|
||||
Réponse :
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": 1,
|
||||
"doc_type": "manual",
|
||||
"filename": "manual_elitedesk.pdf",
|
||||
"mime_type": "application/pdf",
|
||||
"size_bytes": 3456789,
|
||||
"uploaded_at": "2025-05-01T10:00:00Z"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
#### POST /api/devices/{device_id}/docs
|
||||
|
||||
- Type : `multipart/form-data`
|
||||
- Champs :
|
||||
- `file` (fichier binaire)
|
||||
- `doc_type` (string)
|
||||
|
||||
Réponse : métadonnées du document créé.
|
||||
|
||||
#### GET /api/docs/{doc_id}/download
|
||||
|
||||
- Sert le fichier binaire avec le bon `Content-Type`.
|
||||
|
||||
---
|
||||
|
||||
## 5. Authentification
|
||||
|
||||
MVP : token simple.
|
||||
|
||||
- Variable d’env `API_TOKEN` côté backend.
|
||||
- Chaque requête `POST /api/benchmark` doit inclure :
|
||||
|
||||
```http
|
||||
Authorization: Bearer <API_TOKEN>
|
||||
```
|
||||
|
||||
- Les endpoints de lecture (GET) peuvent être :
|
||||
- soit publics sur le LAN,
|
||||
- soit protégés par un autre mécanisme (reverse proxy) – hors scope de cette spec.
|
||||
|
||||
---
|
||||
|
||||
## 6. Gestion des erreurs et validations
|
||||
|
||||
- Utiliser des modèles Pydantic pour :
|
||||
- valider la structure de `hardware` et `results` autant que possible sans être bloquant.
|
||||
- autoriser des champs optionnels (None / manquants).
|
||||
- Codes d’erreur génériques :
|
||||
- 400 : données invalides (schema).
|
||||
- 401 : auth.
|
||||
- 404 : ressources inexistantes (device, benchmark, link, doc).
|
||||
- 413 : payload trop volumineux (facultatif selon config).
|
||||
- 500 : erreur interne.
|
||||
|
||||
Logs :
|
||||
- Logguer les erreurs de parsing JSON.
|
||||
- Logguer l’origine (IP) des appels `POST /api/benchmark`.
|
||||
|
||||
---
|
||||
Reference in New Issue
Block a user