diff --git a/.env.example b/.env.example
old mode 100644
new mode 100755
diff --git a/.gitignore b/.gitignore
old mode 100644
new mode 100755
diff --git a/AJOUT_CHAMPS_MANQUANTS.md b/AJOUT_CHAMPS_MANQUANTS.md
old mode 100644
new mode 100755
diff --git a/AMELIORATIONS_SCRIPT.md b/AMELIORATIONS_SCRIPT.md
old mode 100644
new mode 100755
diff --git a/ANALYSE_CHAMPS_BASE_DONNEES.md b/ANALYSE_CHAMPS_BASE_DONNEES.md
old mode 100644
new mode 100755
diff --git a/ANALYSE_DONNEES final.md b/ANALYSE_DONNEES final.md
old mode 100644
new mode 100755
diff --git a/ANALYSE_DONNEES.md b/ANALYSE_DONNEES.md
old mode 100644
new mode 100755
diff --git a/BUGFIXES_2025-12-13.md b/BUGFIXES_2025-12-13.md
old mode 100644
new mode 100755
diff --git a/BUG_9_COLLECTE_RESEAU.md b/BUG_9_COLLECTE_RESEAU.md
old mode 100644
new mode 100755
diff --git a/CHANGELOG.md b/CHANGELOG.md
old mode 100644
new mode 100755
diff --git a/CHANGELOG_2025-12-13.md b/CHANGELOG_2025-12-13.md
old mode 100644
new mode 100755
diff --git a/CHANGELOG_2025-12-14.md b/CHANGELOG_2025-12-14.md
old mode 100644
new mode 100755
diff --git a/COMMAND_CURL_FIX.md b/COMMAND_CURL_FIX.md
new file mode 100644
index 0000000..3664309
--- /dev/null
+++ b/COMMAND_CURL_FIX.md
@@ -0,0 +1,92 @@
+# Fix: Erreur "jq: invalid JSON text passed to --argjson"
+
+## Problème identifié
+
+L'erreur `jq: invalid JSON text passed to --argjson` à l'étape [8/8] est causée par une variable JSON vide ou invalide passée à `jq`.
+
+## Corrections appliquées
+
+### 1. **Parsing des arguments CLI** (Ligne 1570-1607)
+Ajout d'une fonction `parse_args()` pour gérer les arguments `--server`, `--token`, `--iperf-server`, et `--debug`.
+
+```bash
+parse_args() {
+ while [[ $# -gt 0 ]]; do
+ case "$1" in
+ --server) SERVER_URL="$2"; shift 2 ;;
+ --token) API_TOKEN="$2"; shift 2 ;;
+ --iperf-server) IPERF_SERVER="$2"; shift 2 ;;
+ --debug) DEBUG_PAYLOAD=1; shift ;;
+ --help|-h) # affiche l'aide
+ esac
+ done
+}
+```
+
+### 2. **Amélioration de la collecte GPU** (Ligne 553-589)
+Ajout de validations pour éviter de capturer les messages d'erreur de `nvidia-smi`:
+
+```bash
+# Vérifier que nvidia-smi fonctionne avant d'extraire les infos
+if nvidia-smi &>/dev/null; then
+ nvidia_model=$(nvidia-smi --query-gpu=name --format=csv,noheader 2>/dev/null | head -1 | tr -d '\n')
+
+ # Ne remplacer que si les valeurs sont non-vides et valides
+ if [[ -n "$nvidia_model" && ! "$nvidia_model" =~ (failed|error|Error) ]]; then
+ gpu_model="$nvidia_model"
+ fi
+fi
+```
+
+### 3. **Validation des variables JSON** (Ligne 1326-1357)
+Ajout de validations pour garantir qu'aucune variable n'est vide avant envoi.
+
+### 4. **Mode debug amélioré** (Ligne 1328-1344)
+Quand `DEBUG_PAYLOAD=1` ou `--debug`, affiche l'état de toutes les variables JSON.
+
+## Commandes de diagnostic sur le poste distant
+
+### 1. Vérifier la détection GPU
+```bash
+lspci | grep -iE 'vga|3d'
+
+if command -v nvidia-smi &>/dev/null; then
+ if nvidia-smi &>/dev/null; then
+ echo "nvidia-smi fonctionne"
+ nvidia-smi --query-gpu=name --format=csv,noheader
+ else
+ echo "nvidia-smi échoue - driver non chargé"
+ fi
+fi
+```
+
+### 2. Tester le script complet avec debug
+```bash
+curl -fsSL http://10.0.1.97:8087/scripts/bench.sh | sudo bash -s -- \
+ --server http://10.0.1.97:8007 \
+ --token "29855796dacf5cfe75ff9b02d6adf3dd0f9c52db5b53e7abfb4c0df7ece1be0a" \
+ --iperf-server 10.0.1.97 \
+ --debug
+```
+
+Le flag `--debug` va:
+- Afficher l'état de validation de toutes les variables JSON
+- Sauvegarder le payload complet dans `/tmp/bench_payload_YYYYMMDD_HHMMSS.json`
+- Demander confirmation avant l'envoi
+
+### 3. Examiner un payload sauvegardé
+```bash
+ls -lht /tmp/bench_payload_*.json | head -1
+jq '.' /tmp/bench_payload_*.json | less
+```
+
+## Prochaines étapes
+
+1. Redémarrer le conteneur nginx pour servir la nouvelle version
+2. Tester avec `--debug` sur le poste ASUS
+3. Examiner le payload sauvegardé
+4. Corriger si nécessaire
+
+## Fichiers modifiés
+
+- frontend/scripts/bench.sh - Script principal de benchmark client
diff --git a/CORRECTIFS_FINAUX_2025-12-14.md b/CORRECTIFS_FINAUX_2025-12-14.md
old mode 100644
new mode 100755
diff --git a/CORRECTIFS_RESEAU_SMART.md b/CORRECTIFS_RESEAU_SMART.md
old mode 100644
new mode 100755
diff --git a/DEBUG_NETWORK_BENCH.md b/DEBUG_NETWORK_BENCH.md
old mode 100644
new mode 100755
diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md
old mode 100644
new mode 100755
diff --git a/DEPLOYMENT_GUIDE.md b/DEPLOYMENT_GUIDE.md
old mode 100644
new mode 100755
index 1665fe8..cce736d
--- a/DEPLOYMENT_GUIDE.md
+++ b/DEPLOYMENT_GUIDE.md
@@ -34,13 +34,17 @@ cd /home/gilles/Documents/vscode/serv_benchmark
docker-compose down
```
-### Étape 2: Appliquer la Migration Base de Données
+### Étape 2: Appliquer les Migrations Base de Données
-Si la base de données existe déjà :
+Si la base de données existe déjà, appliquez les scripts dans l'ordre :
```bash
cd backend
python3 apply_migration.py
+python3 apply_migration_002.py
+python3 apply_migration_003.py
+python3 apply_migration_004.py
+python3 apply_migration_005.py
```
**OU** si vous préférez partir de zéro (⚠️ PERTE DE DONNÉES) :
diff --git a/FIXES_APPLIED.md b/FIXES_APPLIED.md
old mode 100644
new mode 100755
diff --git a/FIX_DEBUG_PAYLOAD.md b/FIX_DEBUG_PAYLOAD.md
new file mode 100644
index 0000000..f26acad
--- /dev/null
+++ b/FIX_DEBUG_PAYLOAD.md
@@ -0,0 +1,116 @@
+# Fix: Script bench.sh bloqué en mode non-interactif
+
+## Problème
+
+Lorsque le script `bench.sh` était exécuté via curl pipe bash :
+
+```bash
+curl -fsSL http://10.0.1.97:8087/scripts/bench.sh | sudo bash -s -- ...
+```
+
+Le script s'arrêtait après l'affichage du payload JSON et ne continuait pas l'envoi au serveur.
+
+## Cause Racine
+
+1. **DEBUG_PAYLOAD activé par défaut** (ligne 37) :
+ ```bash
+ DEBUG_PAYLOAD="${DEBUG_PAYLOAD:-1}" # Par défaut: 1 (activé)
+ ```
+
+2. **Attente input interactive** (ligne 1493) :
+ ```bash
+ read -p "Appuyez sur Entrée pour continuer l'envoi ou Ctrl+C pour annuler..."
+ ```
+
+3. **Pas de TTY en mode pipe** : Quand le script est exécuté via `curl | bash`, il n'y a pas de terminal interactif (`stdin` n'est pas un TTY), donc le `read -p` bloque indéfiniment en attendant un input qui ne viendra jamais.
+
+## Solution Implémentée
+
+### 1. Désactiver DEBUG_PAYLOAD par défaut
+
+```bash
+# Avant
+DEBUG_PAYLOAD="${DEBUG_PAYLOAD:-1}" # Par défaut: 1 (activé)
+
+# Après
+DEBUG_PAYLOAD="${DEBUG_PAYLOAD:-0}" # Par défaut: 0 (désactivé)
+```
+
+### 2. Détection du mode interactif
+
+Ajout d'un test `[[ -t 0 ]]` pour vérifier si stdin est un terminal :
+
+```bash
+# Demander confirmation seulement si on a un terminal interactif
+if [[ -t 0 ]]; then
+ read -p "Appuyez sur Entrée pour continuer l'envoi ou Ctrl+C pour annuler..."
+else
+ log_warn "Mode non-interactif détecté - envoi automatique dans 2 secondes..."
+ sleep 2
+fi
+```
+
+## Comportement Après Fix
+
+### Mode Normal (via curl pipe)
+```bash
+curl -fsSL http://10.0.1.97:8087/scripts/bench.sh | sudo bash -s -- \
+ --server http://10.0.1.97:8007 \
+ --token "..." \
+ --iperf-server 10.0.1.97
+```
+- DEBUG_PAYLOAD = 0 (pas d'affichage du payload)
+- Envoi automatique au serveur
+- ✅ **Fonctionne correctement**
+
+### Mode Debug Local
+```bash
+sudo DEBUG_PAYLOAD=1 bash scripts/bench.sh
+```
+- Affiche le payload complet
+- Sauvegarde dans `/tmp/bench_payload_YYYYMMDD_HHMMSS.json`
+- Demande confirmation avant envoi (mode interactif détecté)
+
+### Mode Debug via Curl
+```bash
+curl -fsSL http://10.0.1.97:8087/scripts/bench.sh | DEBUG_PAYLOAD=1 sudo bash -s -- ...
+```
+- Affiche le payload complet
+- Sauvegarde dans `/tmp/bench_payload_YYYYMMDD_HHMMSS.json`
+- Message : "Mode non-interactif détecté - envoi automatique dans 2 secondes..."
+- Envoi après 2 secondes
+- ✅ **Fonctionne correctement**
+
+## Test de Validation
+
+```bash
+# Test 1 : Mode normal (doit envoyer au serveur)
+curl -fsSL http://10.0.1.97:8087/scripts/bench.sh | sudo bash -s -- \
+ --server http://10.0.1.97:8007 \
+ --token "29855796dacf5cfe75ff9b02d6adf3dd0f9c52db5b53e7abfb4c0df7ece1be0a" \
+ --iperf-server 10.0.1.97
+
+# Test 2 : Mode debug local (doit demander confirmation)
+cd /home/gilles/Documents/vscode/serv_benchmark
+sudo DEBUG_PAYLOAD=1 bash scripts/bench.sh
+
+# Test 3 : Mode debug via curl (doit envoyer après 2s)
+curl -fsSL http://10.0.1.97:8087/scripts/bench.sh | DEBUG_PAYLOAD=1 sudo bash -s -- \
+ --server http://10.0.1.97:8007 \
+ --token "29855796dacf5cfe75ff9b02d6adf3dd0f9c52db5b53e7abfb4c0df7ece1be0a" \
+ --iperf-server 10.0.1.97
+```
+
+## Fichiers Modifiés
+
+- `scripts/bench.sh` (lignes 37 et 1493-1499)
+
+## Statut
+
+✅ **CORRIGÉ** - Le script s'exécute maintenant correctement en mode non-interactif et envoie le payload au serveur.
+
+---
+
+**Date**: 2025-12-18
+**Issue**: Script bloqué en mode curl pipe bash
+**Root Cause**: DEBUG_PAYLOAD=1 par défaut + read -p sans détection TTY
diff --git a/FRONTEND_IMPROVEMENTS_2025-12-13.md b/FRONTEND_IMPROVEMENTS_2025-12-13.md
old mode 100644
new mode 100755
diff --git a/FRONTEND_RESTRUCTURE_2025-12-14.md b/FRONTEND_RESTRUCTURE_2025-12-14.md
old mode 100644
new mode 100755
diff --git a/FRONTEND_UPDATES.md b/FRONTEND_UPDATES.md
old mode 100644
new mode 100755
index 4afbe5e..2b77186
--- a/FRONTEND_UPDATES.md
+++ b/FRONTEND_UPDATES.md
@@ -144,14 +144,55 @@ ALTER TABLE benchmarks ADD COLUMN network_results_json TEXT;
**Script d'application** : `backend/apply_migration_002.py`
+### 4. Migration SQL (nouveau score CPU)
+
+**Fichier** : `backend/migrations/003_add_cpu_scores.sql`
+```sql
+ALTER TABLE benchmarks ADD COLUMN cpu_score_single FLOAT;
+ALTER TABLE benchmarks ADD COLUMN cpu_score_multi FLOAT;
+```
+
+**Script d'application** : `backend/apply_migration_003.py`
+
+### 5. Migration SQL (métadonnées hardware supplémentaires)
+
+**Fichier** : `backend/migrations/004_add_snapshot_details.sql`
+```sql
+ALTER TABLE hardware_snapshots ADD COLUMN hostname VARCHAR(255);
+ALTER TABLE hardware_snapshots ADD COLUMN desktop_environment VARCHAR(100);
+ALTER TABLE hardware_snapshots ADD COLUMN pci_devices_json TEXT;
+ALTER TABLE hardware_snapshots ADD COLUMN usb_devices_json TEXT;
+```
+
+**Script d'application** : `backend/apply_migration_004.py`
+
+### 6. Migration SQL (session Wayland/X11, batterie, uptime)
+
+**Fichier** : `backend/migrations/005_add_os_display_and_battery.sql`
+```sql
+ALTER TABLE hardware_snapshots ADD COLUMN screen_resolution VARCHAR(50);
+ALTER TABLE hardware_snapshots ADD COLUMN display_server VARCHAR(50);
+ALTER TABLE hardware_snapshots ADD COLUMN session_type VARCHAR(50);
+ALTER TABLE hardware_snapshots ADD COLUMN last_boot_time VARCHAR(50);
+ALTER TABLE hardware_snapshots ADD COLUMN uptime_seconds INTEGER;
+ALTER TABLE hardware_snapshots ADD COLUMN battery_percentage FLOAT;
+ALTER TABLE hardware_snapshots ADD COLUMN battery_status VARCHAR(50);
+ALTER TABLE hardware_snapshots ADD COLUMN battery_health VARCHAR(50);
+```
+
+**Script d'application** : `backend/apply_migration_005.py`
+
## Déploiement
Pour appliquer les mises à jour :
-### 1. Appliquer la migration 002
+### 1. Appliquer les migrations 002, 003, 004 et 005
```bash
cd backend
python3 apply_migration_002.py
+python3 apply_migration_003.py
+python3 apply_migration_004.py
+python3 apply_migration_005.py
```
### 2. Redémarrer le backend
diff --git a/HOTFIX_BACKEND_SMARTCTL.md b/HOTFIX_BACKEND_SMARTCTL.md
old mode 100644
new mode 100755
diff --git a/HOTFIX_BENCH_IMPROVEMENTS.md b/HOTFIX_BENCH_IMPROVEMENTS.md
old mode 100644
new mode 100755
diff --git a/HOTFIX_NETWORK_BENCH.md b/HOTFIX_NETWORK_BENCH.md
old mode 100644
new mode 100755
diff --git a/HOTFIX_SCORE_VALIDATION.md b/HOTFIX_SCORE_VALIDATION.md
old mode 100644
new mode 100755
diff --git a/IMPLEMENTATION_STATUS.md b/IMPLEMENTATION_STATUS.md
old mode 100644
new mode 100755
diff --git a/INSTRUCTIONS_BENCHMARK.md b/INSTRUCTIONS_BENCHMARK.md
old mode 100644
new mode 100755
diff --git a/NETWORK_SETUP.md b/NETWORK_SETUP.md
old mode 100644
new mode 100755
diff --git a/PROJECT_SUMMARY.md b/PROJECT_SUMMARY.md
old mode 100644
new mode 100755
diff --git a/QUICKSTART.md b/QUICKSTART.md
old mode 100644
new mode 100755
diff --git a/QUICKTEST.md b/QUICKTEST.md
old mode 100644
new mode 100755
diff --git a/README.md b/README.md
old mode 100644
new mode 100755
diff --git a/README_MISE_A_JOUR.md b/README_MISE_A_JOUR.md
old mode 100644
new mode 100755
diff --git a/RESUME_FINAL_CORRECTIONS.md b/RESUME_FINAL_CORRECTIONS.md
old mode 100644
new mode 100755
diff --git a/RESUME_RESTRUCTURATION.md b/RESUME_RESTRUCTURATION.md
old mode 100644
new mode 100755
diff --git a/SESSION_2025-12-18.md b/SESSION_2025-12-18.md
new file mode 100644
index 0000000..71f2d16
--- /dev/null
+++ b/SESSION_2025-12-18.md
@@ -0,0 +1,237 @@
+# Session de Développement - 2025-12-18
+
+## Contexte
+
+Reprise du développement du projet **Linux BenchTools** après la session du 2025-12-14 qui avait corrigé 8 bugs majeurs.
+
+L'utilisateur a signalé que la commande bash curl n'apparaissait pas dans le dashboard frontend.
+
+## Problèmes Identifiés
+
+### 1. ❌ Commande curl manquante dans le dashboard
+**Symptôme** : La section "Quick Bench Script" affichait "Chargement..." au lieu de la vraie commande.
+
+**Cause** : Le token API était hardcodé à `YOUR_TOKEN` dans le code JavaScript au lieu d'être récupéré dynamiquement depuis le backend.
+
+### 2. ❌ Script bench.sh bloqué en mode non-interactif
+**Symptôme** : Lors de l'exécution via `curl | bash`, le script s'arrêtait après l'affichage du payload JSON et n'envoyait rien au serveur.
+
+**Cause** :
+- `DEBUG_PAYLOAD=1` par défaut
+- `read -p` qui attendait un input sans vérifier si stdin était un terminal interactif
+
+## Solutions Implémentées
+
+### Fix #1 : Endpoint API pour configuration frontend
+
+#### Backend - Nouvel endpoint `/api/config`
+
+**Fichier** : `backend/app/main.py`
+
+```python
+@app.get(f"{settings.API_PREFIX}/config")
+async def get_config():
+ """Get frontend configuration (API token, server URLs, etc.)"""
+ return {
+ "api_token": settings.API_TOKEN,
+ "iperf_server": "10.0.1.97"
+ }
+```
+
+**URL** : http://10.0.1.97:8007/api/config
+
+**Réponse** :
+```json
+{
+ "api_token": "29855796dacf5cfe75ff9b02d6adf3dd0f9c52db5b53e7abfb4c0df7ece1be0a",
+ "iperf_server": "10.0.1.97"
+}
+```
+
+#### Frontend - Chargement dynamique du token
+
+**Fichier** : `frontend/js/dashboard.js`
+
+Ajout de variables globales et fonction de chargement :
+```javascript
+let apiToken = null;
+let iperfServer = null;
+
+async function loadBackendConfig() {
+ const response = await fetch(`${window.BenchConfig.backendApiUrl}/config`);
+ if (response.ok) {
+ const config = await response.json();
+ apiToken = config.api_token;
+ iperfServer = config.iperf_server || '10.0.1.97';
+ updateBenchCommandDisplay();
+ }
+}
+```
+
+Modification de la génération de commande :
+```javascript
+function buildBenchCommand() {
+ const token = apiToken || 'LOADING...';
+ const backendUrl = backendBase.replace(/\/api$/, '');
+
+ return `curl -fsSL ${frontendBase}${scriptPath} | sudo bash -s -- --server ${backendUrl} --token "${token}" --iperf-server ${iperf}`;
+}
+```
+
+Initialisation au chargement de la page :
+```javascript
+document.addEventListener('DOMContentLoaded', async () => {
+ await loadBackendConfig(); // Charge le token en premier
+ loadDashboard();
+ // ...
+});
+```
+
+**Fichier** : `frontend/js/settings.js` - Modifications similaires
+
+#### Docker - Volume de développement
+
+**Fichier** : `docker-compose.yml`
+
+Ajout du volume pour faciliter les modifications sans rebuild :
+```yaml
+backend:
+ volumes:
+ - ./backend/data:/app/data
+ - ./uploads:/app/uploads
+ - ./backend/app:/app/app # ← Nouveau
+```
+
+### Fix #2 : Mode non-interactif pour bench.sh
+
+**Fichier** : `scripts/bench.sh`
+
+#### Changement 1 : DEBUG_PAYLOAD désactivé par défaut (ligne 37)
+```bash
+# Avant
+DEBUG_PAYLOAD="${DEBUG_PAYLOAD:-1}" # Par défaut: 1 (activé)
+
+# Après
+DEBUG_PAYLOAD="${DEBUG_PAYLOAD:-0}" # Par défaut: 0 (désactivé)
+```
+
+#### Changement 2 : Détection du mode interactif (lignes 1493-1499)
+```bash
+# Demander confirmation seulement si on a un terminal interactif
+if [[ -t 0 ]]; then
+ read -p "Appuyez sur Entrée pour continuer l'envoi ou Ctrl+C pour annuler..."
+else
+ log_warn "Mode non-interactif détecté - envoi automatique dans 2 secondes..."
+ sleep 2
+fi
+```
+
+## Commande Finale Générée
+
+La commande affichée dans le dashboard est maintenant :
+
+```bash
+curl -fsSL http://10.0.1.97:8087/scripts/bench.sh | sudo bash -s -- \
+ --server http://10.0.1.97:8007 \
+ --token "29855796dacf5cfe75ff9b02d6adf3dd0f9c52db5b53e7abfb4c0df7ece1be0a" \
+ --iperf-server 10.0.1.97
+```
+
+## Fichiers Modifiés
+
+| Fichier | Lignes | Changement |
+|---------|--------|------------|
+| `backend/app/main.py` | 97-104 | ✅ Ajout endpoint `/api/config` |
+| `frontend/js/dashboard.js` | 9-25, 229-235, 305-320 | ✅ Chargement dynamique token |
+| `frontend/js/settings.js` | 6-29, 149-167 | ✅ Chargement dynamique token |
+| `docker-compose.yml` | 12 | ✅ Volume backend app |
+| `scripts/bench.sh` | 37, 1493-1499 | ✅ DEBUG_PAYLOAD=0 + détection TTY |
+
+## Documentation Créée
+
+- ✅ `COMMAND_CURL_FIX.md` - Fix de la commande curl manquante
+- ✅ `FIX_DEBUG_PAYLOAD.md` - Fix du blocage en mode non-interactif
+- ✅ `SESSION_2025-12-18.md` - Ce document (résumé de session)
+
+## Tests de Validation
+
+### Test 1 : API Config
+```bash
+curl http://10.0.1.97:8007/api/config
+# ✅ Retourne le token et iperf_server
+```
+
+### Test 2 : Dashboard Frontend
+1. Ouvrir http://10.0.1.97:8087
+2. Section "⚡ Quick Bench Script"
+3. ✅ La commande complète s'affiche avec le vrai token
+4. ✅ Le bouton "Copier" fonctionne
+
+### Test 3 : Page Settings
+1. Ouvrir http://10.0.1.97:8087/settings.html
+2. Section "📋 Commande Générée"
+3. ✅ La commande s'affiche avec le vrai token
+4. ✅ Le token est visible dans la section "🔑 Informations API"
+
+### Test 4 : Exécution du script via curl
+```bash
+curl -fsSL http://10.0.1.97:8087/scripts/bench.sh | sudo bash -s -- \
+ --server http://10.0.1.97:8007 \
+ --token "29855796dacf5cfe75ff9b02d6adf3dd0f9c52db5b53e7abfb4c0df7ece1be0a" \
+ --iperf-server 10.0.1.97
+```
+✅ Le script s'exécute de bout en bout sans blocage
+✅ Le payload est envoyé au serveur
+✅ Le benchmark apparaît dans le dashboard
+
+## Statut Final
+
+| Composant | Statut | Remarques |
+|-----------|--------|-----------|
+| Backend API | ✅ Fonctionnel | Endpoint `/api/config` opérationnel |
+| Frontend Dashboard | ✅ Fonctionnel | Commande curl complète affichée |
+| Frontend Settings | ✅ Fonctionnel | Token et commande affichés |
+| Script bench.sh | ✅ Fonctionnel | Mode non-interactif OK |
+| Docker Compose | ✅ Fonctionnel | Volumes de dev ajoutés |
+
+## Prochaines Étapes Recommandées
+
+### Phase 2 - Améliorations UX (Roadmap)
+1. Tri avancé sur les colonnes du dashboard
+2. Filtres par tags/type de machine
+3. Icônes pour types de machines et OS
+4. Pagination améliorée
+
+### Phase 3 - Graphiques d'historique
+1. Intégration Chart.js
+2. Graphiques d'évolution des scores
+3. Comparaison de benchmarks
+
+### Phase 4 - Détection de régressions
+1. Calcul de baseline par device
+2. Alertes sur régression de performance
+3. Webhooks pour notifications
+
+## Résumé Technique
+
+**Problème Initial** : Impossible d'utiliser la commande curl du dashboard car le token n'était pas affiché.
+
+**Solution** :
+- Backend : Nouvel endpoint REST pour exposer la config
+- Frontend : Chargement asynchrone du token au démarrage
+- Script : Détection du mode non-interactif pour éviter les blocages
+
+**Impact** :
+- ✅ Workflow complet fonctionnel de bout en bout
+- ✅ Aucune modification manuelle requise
+- ✅ Expérience utilisateur améliorée
+
+---
+
+**Session du** : 2025-12-18
+**Durée** : ~2 heures
+**Bugs corrigés** : 2
+**Fichiers modifiés** : 5
+**Documentation** : 3 fichiers
+**Lignes de code** : ~150
+**Statut** : ✅ **SUCCÈS COMPLET**
diff --git a/SESSION_COMPLETE_2025-12-14.md b/SESSION_COMPLETE_2025-12-14.md
old mode 100644
new mode 100755
diff --git a/SMART_GUIDE.md b/SMART_GUIDE.md
old mode 100644
new mode 100755
diff --git a/STATUS_FINAL.txt b/STATUS_FINAL.txt
old mode 100644
new mode 100755
diff --git a/STRUCTURE.md b/STRUCTURE.md
old mode 100644
new mode 100755
diff --git a/TESTING.md b/TESTING.md
old mode 100644
new mode 100755
diff --git a/TEST_BENCH.md b/TEST_BENCH.md
old mode 100644
new mode 100755
diff --git a/TEST_FRONTEND_RESTRUCTURE.md b/TEST_FRONTEND_RESTRUCTURE.md
old mode 100644
new mode 100755
diff --git a/TEST_RAPIDE.md b/TEST_RAPIDE.md
old mode 100644
new mode 100755
diff --git a/USAGE_DEBUG.md b/USAGE_DEBUG.md
old mode 100644
new mode 100755
diff --git a/VERIFICATION_FINALE_BENCHMARK.md b/VERIFICATION_FINALE_BENCHMARK.md
old mode 100644
new mode 100755
diff --git a/analyse_chatgpt.md b/analyse_chatgpt.md
old mode 100644
new mode 100755
diff --git a/backend/Dockerfile b/backend/Dockerfile
old mode 100644
new mode 100755
diff --git a/backend/README.md b/backend/README.md
old mode 100644
new mode 100755
diff --git a/backend/app/api/benchmark.py b/backend/app/api/benchmark.py
index 2038e8e..281a46d 100644
--- a/backend/app/api/benchmark.py
+++ b/backend/app/api/benchmark.py
@@ -9,11 +9,24 @@ from datetime import datetime
from app.db.session import get_db
from app.core.security import verify_token
-from app.schemas.benchmark import BenchmarkPayload, BenchmarkResponse, BenchmarkDetail, BenchmarkSummary
+from app.schemas.benchmark import (
+ BenchmarkPayload,
+ BenchmarkResponse,
+ BenchmarkDetail,
+ BenchmarkSummary,
+ BenchmarkUpdate,
+)
from app.models.device import Device
from app.models.hardware_snapshot import HardwareSnapshot
from app.models.benchmark import Benchmark
-from app.utils.scoring import calculate_global_score
+from app.utils.scoring import (
+ calculate_global_score,
+ calculate_cpu_score,
+ calculate_memory_score,
+ calculate_disk_score,
+ calculate_network_score,
+ calculate_gpu_score
+)
router = APIRouter()
@@ -91,7 +104,7 @@ async def submit_benchmark(
snapshot.ram_slots_total = hw.ram.slots_total if hw.ram else None
snapshot.ram_slots_used = hw.ram.slots_used if hw.ram else None
snapshot.ram_ecc = hw.ram.ecc if hw.ram else None
- snapshot.ram_layout_json = json.dumps([slot.dict() for slot in hw.ram.layout]) if hw.ram and hw.ram.layout else None
+ snapshot.ram_layout_json = json.dumps([slot.model_dump() for slot in hw.ram.layout]) if hw.ram and hw.ram.layout else None
# GPU
snapshot.gpu_summary = f"{hw.gpu.vendor} {hw.gpu.model}" if hw.gpu and hw.gpu.model else None
@@ -104,11 +117,12 @@ async def submit_benchmark(
# Storage
snapshot.storage_summary = f"{len(hw.storage.devices)} device(s)" if hw.storage and hw.storage.devices else None
- snapshot.storage_devices_json = json.dumps([d.dict() for d in hw.storage.devices]) if hw.storage and hw.storage.devices else None
- snapshot.partitions_json = json.dumps([p.dict() for p in hw.storage.partitions]) if hw.storage and hw.storage.partitions else None
+ snapshot.storage_devices_json = json.dumps([d.model_dump() for d in hw.storage.devices]) if hw.storage and hw.storage.devices else None
+ snapshot.partitions_json = json.dumps([p.model_dump() for p in hw.storage.partitions]) if hw.storage and hw.storage.partitions else None
# Network
- snapshot.network_interfaces_json = json.dumps([i.dict() for i in hw.network.interfaces]) if hw.network and hw.network.interfaces else None
+ snapshot.network_interfaces_json = json.dumps([i.model_dump() for i in hw.network.interfaces]) if hw.network and hw.network.interfaces else None
+ snapshot.network_shares_json = json.dumps([share.model_dump() for share in hw.network_shares]) if hw.network_shares else None
# OS / Motherboard
snapshot.os_name = hw.os.name if hw.os else None
@@ -116,15 +130,29 @@ async def submit_benchmark(
snapshot.kernel_version = hw.os.kernel_version if hw.os else None
snapshot.architecture = hw.os.architecture if hw.os else None
snapshot.virtualization_type = hw.os.virtualization_type if hw.os else None
+ snapshot.screen_resolution = hw.os.screen_resolution if hw.os else None
+ snapshot.display_server = hw.os.display_server if hw.os else None
+ snapshot.session_type = hw.os.session_type if hw.os else None
+ snapshot.last_boot_time = hw.os.last_boot_time if hw.os else None
+ snapshot.uptime_seconds = hw.os.uptime_seconds if hw.os else None
+ snapshot.battery_percentage = hw.os.battery_percentage if hw.os else None
+ snapshot.battery_status = hw.os.battery_status if hw.os else None
+ snapshot.battery_health = hw.os.battery_health if hw.os else None
+ snapshot.hostname = hw.os.hostname if hw.os else None
+ snapshot.desktop_environment = hw.os.desktop_environment if hw.os else None
snapshot.motherboard_vendor = hw.motherboard.vendor if hw.motherboard else None
snapshot.motherboard_model = hw.motherboard.model if hw.motherboard else None
snapshot.bios_vendor = hw.motherboard.bios_vendor if hw.motherboard and hasattr(hw.motherboard, 'bios_vendor') else None
snapshot.bios_version = hw.motherboard.bios_version if hw.motherboard else None
snapshot.bios_date = hw.motherboard.bios_date if hw.motherboard else None
+ # PCI and USB Devices
+ snapshot.pci_devices_json = json.dumps([d.model_dump(by_alias=True) for d in hw.pci_devices]) if hw.pci_devices else None
+ snapshot.usb_devices_json = json.dumps([d.model_dump() for d in hw.usb_devices]) if hw.usb_devices else None
+
# Misc
- snapshot.sensors_json = json.dumps(hw.sensors.dict()) if hw.sensors else None
- snapshot.raw_info_json = json.dumps(hw.raw_info.dict()) if hw.raw_info else None
+ snapshot.sensors_json = json.dumps(hw.sensors.model_dump()) if hw.sensors else None
+ snapshot.raw_info_json = json.dumps(hw.raw_info.model_dump()) if hw.raw_info else None
# Add to session only if it's a new snapshot
if not existing_snapshot:
@@ -135,18 +163,61 @@ async def submit_benchmark(
# 3. Create benchmark
results = payload.results
- # Calculate global score if not provided or recalculate
- global_score = calculate_global_score(
- cpu_score=results.cpu.score if results.cpu else None,
- memory_score=results.memory.score if results.memory else None,
- disk_score=results.disk.score if results.disk else None,
- network_score=results.network.score if results.network else None,
- gpu_score=results.gpu.score if results.gpu else None
- )
+ # Recalculate scores from raw metrics using new formulas
+ cpu_score = None
+ cpu_score_single = None
+ cpu_score_multi = None
- # Use provided global_score if available and valid
- if results.global_score is not None:
- global_score = results.global_score
+ if results.cpu:
+ # Use scores from script if available (preferred), otherwise calculate
+ if results.cpu.score_single is not None:
+ cpu_score_single = results.cpu.score_single
+ elif results.cpu.events_per_sec_single:
+ cpu_score_single = calculate_cpu_score(results.cpu.events_per_sec_single)
+
+ if results.cpu.score_multi is not None:
+ cpu_score_multi = results.cpu.score_multi
+ elif results.cpu.events_per_sec_multi:
+ cpu_score_multi = calculate_cpu_score(results.cpu.events_per_sec_multi)
+
+ # Use score from script if available, otherwise calculate
+ if results.cpu.score is not None:
+ cpu_score = results.cpu.score
+ elif results.cpu.events_per_sec_multi:
+ cpu_score = cpu_score_multi
+ elif results.cpu.events_per_sec:
+ cpu_score = calculate_cpu_score(results.cpu.events_per_sec)
+
+ memory_score = None
+ if results.memory and results.memory.throughput_mib_s:
+ memory_score = calculate_memory_score(results.memory.throughput_mib_s)
+
+ disk_score = None
+ if results.disk:
+ disk_score = calculate_disk_score(
+ read_mb_s=results.disk.read_mb_s,
+ write_mb_s=results.disk.write_mb_s
+ )
+
+ network_score = None
+ if results.network:
+ network_score = calculate_network_score(
+ upload_mbps=results.network.upload_mbps,
+ download_mbps=results.network.download_mbps
+ )
+
+ gpu_score = None
+ if results.gpu and results.gpu.glmark2_score:
+ gpu_score = calculate_gpu_score(results.gpu.glmark2_score)
+
+ # Calculate global score from recalculated component scores
+ global_score = calculate_global_score(
+ cpu_score=cpu_score,
+ memory_score=memory_score,
+ disk_score=disk_score,
+ network_score=network_score,
+ gpu_score=gpu_score
+ )
# Extract network results for easier frontend access
network_results = None
@@ -155,7 +226,7 @@ async def submit_benchmark(
"upload_mbps": results.network.upload_mbps if hasattr(results.network, 'upload_mbps') else None,
"download_mbps": results.network.download_mbps if hasattr(results.network, 'download_mbps') else None,
"ping_ms": results.network.ping_ms if hasattr(results.network, 'ping_ms') else None,
- "score": results.network.score
+ "score": network_score
}
benchmark = Benchmark(
@@ -165,11 +236,13 @@ async def submit_benchmark(
bench_script_version=payload.bench_script_version,
global_score=global_score,
- cpu_score=results.cpu.score if results.cpu else None,
- memory_score=results.memory.score if results.memory else None,
- disk_score=results.disk.score if results.disk else None,
- network_score=results.network.score if results.network else None,
- gpu_score=results.gpu.score if results.gpu else None,
+ cpu_score=cpu_score,
+ cpu_score_single=cpu_score_single,
+ cpu_score_multi=cpu_score_multi,
+ memory_score=memory_score,
+ disk_score=disk_score,
+ network_score=network_score,
+ gpu_score=gpu_score,
details_json=json.dumps(results.dict()),
network_results_json=json.dumps(network_results) if network_results else None
@@ -210,9 +283,54 @@ async def get_benchmark(
bench_script_version=benchmark.bench_script_version,
global_score=benchmark.global_score,
cpu_score=benchmark.cpu_score,
+ cpu_score_single=benchmark.cpu_score_single,
+ cpu_score_multi=benchmark.cpu_score_multi,
memory_score=benchmark.memory_score,
disk_score=benchmark.disk_score,
network_score=benchmark.network_score,
gpu_score=benchmark.gpu_score,
- details=json.loads(benchmark.details_json)
+ details=json.loads(benchmark.details_json),
+ notes=benchmark.notes
+ )
+
+
+@router.patch("/benchmarks/{benchmark_id}", response_model=BenchmarkSummary)
+async def update_benchmark_entry(
+ benchmark_id: int,
+ payload: BenchmarkUpdate,
+ db: Session = Depends(get_db)
+):
+ """
+ Update editable benchmark fields (currently only notes).
+ """
+ benchmark = db.query(Benchmark).filter(Benchmark.id == benchmark_id).first()
+
+ if not benchmark:
+ raise HTTPException(
+ status_code=status.HTTP_404_NOT_FOUND,
+ detail=f"Benchmark {benchmark_id} not found"
+ )
+
+ update_data = payload.model_dump(exclude_unset=True)
+
+ if "notes" in update_data:
+ benchmark.notes = update_data["notes"]
+
+ db.add(benchmark)
+ db.commit()
+ db.refresh(benchmark)
+
+ return BenchmarkSummary(
+ id=benchmark.id,
+ run_at=benchmark.run_at.isoformat(),
+ global_score=benchmark.global_score,
+ cpu_score=benchmark.cpu_score,
+ cpu_score_single=benchmark.cpu_score_single,
+ cpu_score_multi=benchmark.cpu_score_multi,
+ memory_score=benchmark.memory_score,
+ disk_score=benchmark.disk_score,
+ network_score=benchmark.network_score,
+ gpu_score=benchmark.gpu_score,
+ bench_script_version=benchmark.bench_script_version,
+ notes=benchmark.notes
)
diff --git a/backend/app/api/devices.py b/backend/app/api/devices.py
index 43c0e6c..1a313a0 100644
--- a/backend/app/api/devices.py
+++ b/backend/app/api/devices.py
@@ -3,7 +3,7 @@ Linux BenchTools - Devices API
"""
import json
-from fastapi import APIRouter, Depends, HTTPException, status, Query
+from fastapi import APIRouter, Depends, HTTPException, status, Query, Response
from sqlalchemy.orm import Session
from typing import List
@@ -68,7 +68,8 @@ async def get_devices(
disk_score=last_bench.disk_score,
network_score=last_bench.network_score,
gpu_score=last_bench.gpu_score,
- bench_script_version=last_bench.bench_script_version
+ bench_script_version=last_bench.bench_script_version,
+ notes=last_bench.notes
)
items.append(DeviceSummary(
@@ -80,6 +81,9 @@ async def get_devices(
location=device.location,
owner=device.owner,
tags=device.tags,
+ purchase_store=device.purchase_store,
+ purchase_date=device.purchase_date,
+ purchase_price=device.purchase_price,
created_at=device.created_at.isoformat(),
updated_at=device.updated_at.isoformat(),
last_benchmark=last_bench_summary
@@ -125,7 +129,8 @@ async def get_device(
disk_score=last_bench.disk_score,
network_score=last_bench.network_score,
gpu_score=last_bench.gpu_score,
- bench_script_version=last_bench.bench_script_version
+ bench_script_version=last_bench.bench_script_version,
+ notes=last_bench.notes
)
# Get last hardware snapshot
@@ -146,20 +151,40 @@ async def get_device(
cpu_base_freq_ghz=last_snapshot.cpu_base_freq_ghz,
cpu_max_freq_ghz=last_snapshot.cpu_max_freq_ghz,
ram_total_mb=last_snapshot.ram_total_mb,
+ ram_used_mb=last_snapshot.ram_used_mb,
+ ram_free_mb=last_snapshot.ram_free_mb,
+ ram_shared_mb=last_snapshot.ram_shared_mb,
ram_slots_total=last_snapshot.ram_slots_total,
ram_slots_used=last_snapshot.ram_slots_used,
gpu_summary=last_snapshot.gpu_summary,
gpu_model=last_snapshot.gpu_model,
storage_summary=last_snapshot.storage_summary,
storage_devices_json=last_snapshot.storage_devices_json,
+ partitions_json=last_snapshot.partitions_json,
network_interfaces_json=last_snapshot.network_interfaces_json,
+ network_shares_json=last_snapshot.network_shares_json,
os_name=last_snapshot.os_name,
os_version=last_snapshot.os_version,
kernel_version=last_snapshot.kernel_version,
architecture=last_snapshot.architecture,
virtualization_type=last_snapshot.virtualization_type,
+ screen_resolution=last_snapshot.screen_resolution,
+ display_server=last_snapshot.display_server,
+ session_type=last_snapshot.session_type,
+ last_boot_time=last_snapshot.last_boot_time,
+ uptime_seconds=last_snapshot.uptime_seconds,
+ battery_percentage=last_snapshot.battery_percentage,
+ battery_status=last_snapshot.battery_status,
+ battery_health=last_snapshot.battery_health,
+ hostname=last_snapshot.hostname,
+ desktop_environment=last_snapshot.desktop_environment,
motherboard_vendor=last_snapshot.motherboard_vendor,
- motherboard_model=last_snapshot.motherboard_model
+ motherboard_model=last_snapshot.motherboard_model,
+ bios_vendor=last_snapshot.bios_vendor,
+ bios_version=last_snapshot.bios_version,
+ bios_date=last_snapshot.bios_date,
+ pci_devices_json=last_snapshot.pci_devices_json,
+ usb_devices_json=last_snapshot.usb_devices_json
)
# Get documents for this device
@@ -189,6 +214,9 @@ async def get_device(
location=device.location,
owner=device.owner,
tags=device.tags,
+ purchase_store=device.purchase_store,
+ purchase_date=device.purchase_date,
+ purchase_price=device.purchase_price,
created_at=device.created_at.isoformat(),
updated_at=device.updated_at.isoformat(),
last_benchmark=last_bench_summary,
@@ -232,7 +260,8 @@ async def get_device_benchmarks(
disk_score=b.disk_score,
network_score=b.network_score,
gpu_score=b.gpu_score,
- bench_script_version=b.bench_script_version
+ bench_script_version=b.bench_script_version,
+ notes=b.notes
)
for b in benchmarks
]
@@ -276,3 +305,25 @@ async def update_device(
# Return updated device (reuse get_device logic)
return await get_device(device_id, db)
+
+
+@router.delete("/devices/{device_id}", status_code=status.HTTP_204_NO_CONTENT)
+async def delete_device(
+ device_id: int,
+ db: Session = Depends(get_db)
+):
+ """
+ Delete a device and all related data
+ """
+ device = db.query(Device).filter(Device.id == device_id).first()
+
+ if not device:
+ raise HTTPException(
+ status_code=status.HTTP_404_NOT_FOUND,
+ detail=f"Device {device_id} not found"
+ )
+
+ db.delete(device)
+ db.commit()
+
+ return Response(status_code=status.HTTP_204_NO_CONTENT)
diff --git a/backend/app/main.py b/backend/app/main.py
index b01bf8d..0a22944 100644
--- a/backend/app/main.py
+++ b/backend/app/main.py
@@ -94,6 +94,16 @@ async def get_stats(db: Session = Depends(get_db)):
}
+# Config endpoint (for frontend to get API token and server info)
+@app.get(f"{settings.API_PREFIX}/config")
+async def get_config():
+ """Get frontend configuration (API token, server URLs, etc.)"""
+ return {
+ "api_token": settings.API_TOKEN,
+ "iperf_server": "10.0.1.97"
+ }
+
+
if __name__ == "__main__":
import uvicorn
uvicorn.run("app.main:app", host="0.0.0.0", port=8007, reload=True)
diff --git a/backend/app/models/benchmark.py b/backend/app/models/benchmark.py
index 664fc67..0c58305 100644
--- a/backend/app/models/benchmark.py
+++ b/backend/app/models/benchmark.py
@@ -23,6 +23,8 @@ class Benchmark(Base):
# Scores
global_score = Column(Float, nullable=False)
cpu_score = Column(Float, nullable=True)
+ cpu_score_single = Column(Float, nullable=True) # Monocore CPU score
+ cpu_score_multi = Column(Float, nullable=True) # Multicore CPU score
memory_score = Column(Float, nullable=True)
disk_score = Column(Float, nullable=True)
network_score = Column(Float, nullable=True)
diff --git a/backend/app/models/device.py b/backend/app/models/device.py
index e073700..a1d5b5a 100644
--- a/backend/app/models/device.py
+++ b/backend/app/models/device.py
@@ -2,7 +2,7 @@
Linux BenchTools - Device Model
"""
-from sqlalchemy import Column, Integer, String, DateTime, Text
+from sqlalchemy import Column, Integer, String, DateTime, Text, Float
from sqlalchemy.orm import relationship
from datetime import datetime
from app.db.base import Base
@@ -22,6 +22,9 @@ class Device(Base):
location = Column(String(255), nullable=True)
owner = Column(String(100), nullable=True)
tags = Column(Text, nullable=True) # JSON or comma-separated
+ purchase_store = Column(String(255), nullable=True)
+ purchase_date = Column(String(50), nullable=True)
+ purchase_price = Column(Float, nullable=True)
created_at = Column(DateTime, nullable=False, default=datetime.utcnow)
updated_at = Column(DateTime, nullable=False, default=datetime.utcnow, onupdate=datetime.utcnow)
diff --git a/backend/app/models/hardware_snapshot.py b/backend/app/models/hardware_snapshot.py
index 55691d2..fe7c2c0 100644
--- a/backend/app/models/hardware_snapshot.py
+++ b/backend/app/models/hardware_snapshot.py
@@ -58,6 +58,7 @@ class HardwareSnapshot(Base):
# Network
network_interfaces_json = Column(Text, nullable=True) # JSON array
+ network_shares_json = Column(Text, nullable=True) # JSON array
# OS / Motherboard
os_name = Column(String(100), nullable=True)
@@ -65,11 +66,25 @@ class HardwareSnapshot(Base):
kernel_version = Column(String(100), nullable=True)
architecture = Column(String(50), nullable=True)
virtualization_type = Column(String(50), nullable=True)
+ screen_resolution = Column(String(50), nullable=True)
+ display_server = Column(String(50), nullable=True)
+ session_type = Column(String(50), nullable=True)
+ last_boot_time = Column(String(50), nullable=True)
+ uptime_seconds = Column(Integer, nullable=True)
+ battery_percentage = Column(Float, nullable=True)
+ battery_status = Column(String(50), nullable=True)
+ battery_health = Column(String(50), nullable=True)
motherboard_vendor = Column(String(100), nullable=True)
motherboard_model = Column(String(255), nullable=True)
bios_vendor = Column(String(100), nullable=True)
bios_version = Column(String(100), nullable=True)
bios_date = Column(String(50), nullable=True)
+ hostname = Column(String(255), nullable=True)
+ desktop_environment = Column(String(100), nullable=True)
+
+ # PCI and USB Devices
+ pci_devices_json = Column(Text, nullable=True) # JSON array
+ usb_devices_json = Column(Text, nullable=True) # JSON array
# Misc
sensors_json = Column(Text, nullable=True) # JSON object
diff --git a/backend/app/schemas/benchmark.py b/backend/app/schemas/benchmark.py
index e9ac06d..7176cff 100644
--- a/backend/app/schemas/benchmark.py
+++ b/backend/app/schemas/benchmark.py
@@ -10,8 +10,12 @@ from app.schemas.hardware import HardwareData
class CPUResults(BaseModel):
"""CPU benchmark results"""
events_per_sec: Optional[float] = Field(None, ge=0)
+ events_per_sec_single: Optional[float] = Field(None, ge=0) # Monocore
+ events_per_sec_multi: Optional[float] = Field(None, ge=0) # Multicore
duration_s: Optional[float] = Field(None, ge=0)
score: Optional[float] = Field(None, ge=0, le=10000)
+ score_single: Optional[float] = Field(None, ge=0, le=10000) # Monocore score
+ score_multi: Optional[float] = Field(None, ge=0, le=10000) # Multicore score
class MemoryResults(BaseModel):
@@ -82,12 +86,15 @@ class BenchmarkDetail(BaseModel):
global_score: float
cpu_score: Optional[float] = None
+ cpu_score_single: Optional[float] = None
+ cpu_score_multi: Optional[float] = None
memory_score: Optional[float] = None
disk_score: Optional[float] = None
network_score: Optional[float] = None
gpu_score: Optional[float] = None
details: dict # details_json parsed
+ notes: Optional[str] = None
class Config:
from_attributes = True
@@ -99,11 +106,19 @@ class BenchmarkSummary(BaseModel):
run_at: str
global_score: float
cpu_score: Optional[float] = None
+ cpu_score_single: Optional[float] = None
+ cpu_score_multi: Optional[float] = None
memory_score: Optional[float] = None
disk_score: Optional[float] = None
network_score: Optional[float] = None
gpu_score: Optional[float] = None
bench_script_version: Optional[str] = None
+ notes: Optional[str] = None
class Config:
from_attributes = True
+
+
+class BenchmarkUpdate(BaseModel):
+ """Fields allowed when updating a benchmark"""
+ notes: Optional[str] = None
diff --git a/backend/app/schemas/device.py b/backend/app/schemas/device.py
index 4203746..d6763a8 100644
--- a/backend/app/schemas/device.py
+++ b/backend/app/schemas/device.py
@@ -18,6 +18,9 @@ class DeviceBase(BaseModel):
location: Optional[str] = None
owner: Optional[str] = None
tags: Optional[str] = None
+ purchase_store: Optional[str] = None
+ purchase_date: Optional[str] = None
+ purchase_price: Optional[float] = None
class DeviceCreate(DeviceBase):
@@ -34,6 +37,9 @@ class DeviceUpdate(BaseModel):
location: Optional[str] = None
owner: Optional[str] = None
tags: Optional[str] = None
+ purchase_store: Optional[str] = None
+ purchase_date: Optional[str] = None
+ purchase_price: Optional[float] = None
class DeviceSummary(DeviceBase):
diff --git a/backend/app/schemas/hardware.py b/backend/app/schemas/hardware.py
index 733d263..e7d8d46 100644
--- a/backend/app/schemas/hardware.py
+++ b/backend/app/schemas/hardware.py
@@ -2,7 +2,7 @@
Linux BenchTools - Hardware Schemas
"""
-from pydantic import BaseModel
+from pydantic import BaseModel, ConfigDict, Field
from typing import Optional, List
@@ -73,6 +73,7 @@ class Partition(BaseModel):
fs_type: Optional[str] = None
used_gb: Optional[float] = None
total_gb: Optional[float] = None
+ free_gb: Optional[float] = None
class StorageInfo(BaseModel):
@@ -89,6 +90,7 @@ class NetworkInterface(BaseModel):
ip: Optional[str] = None
speed_mbps: Optional[int] = None
driver: Optional[str] = None
+ ssid: Optional[str] = None
wake_on_lan: Optional[bool] = None
@@ -97,6 +99,18 @@ class NetworkInfo(BaseModel):
interfaces: Optional[List[NetworkInterface]] = None
+class NetworkShare(BaseModel):
+ """Mounted network share information"""
+ protocol: Optional[str] = None
+ source: Optional[str] = None
+ mount_point: Optional[str] = None
+ fs_type: Optional[str] = None
+ options: Optional[str] = None
+ total_gb: Optional[float] = None
+ used_gb: Optional[float] = None
+ free_gb: Optional[float] = None
+
+
class MotherboardInfo(BaseModel):
"""Motherboard information schema"""
vendor: Optional[str] = None
@@ -113,6 +127,34 @@ class OSInfo(BaseModel):
kernel_version: Optional[str] = None
architecture: Optional[str] = None
virtualization_type: Optional[str] = None
+ hostname: Optional[str] = None
+ desktop_environment: Optional[str] = None
+ session_type: Optional[str] = None
+ display_server: Optional[str] = None
+ screen_resolution: Optional[str] = None
+ last_boot_time: Optional[str] = None
+ uptime_seconds: Optional[int] = None
+ battery_percentage: Optional[float] = None
+ battery_status: Optional[str] = None
+ battery_health: Optional[str] = None
+
+
+class PCIDevice(BaseModel):
+ """PCI device information"""
+ model_config = ConfigDict(populate_by_name=True)
+ slot: str
+ class_: Optional[str] = Field(default=None, alias="class")
+ vendor: Optional[str] = None
+ device: Optional[str] = None
+
+
+class USBDevice(BaseModel):
+ """USB device information"""
+ bus: str
+ device: str
+ vendor_id: Optional[str] = None
+ product_id: Optional[str] = None
+ name: Optional[str] = None
class SensorsInfo(BaseModel):
@@ -135,10 +177,13 @@ class HardwareData(BaseModel):
gpu: Optional[GPUInfo] = None
storage: Optional[StorageInfo] = None
network: Optional[NetworkInfo] = None
+ network_shares: Optional[List[NetworkShare]] = None
motherboard: Optional[MotherboardInfo] = None
os: Optional[OSInfo] = None
sensors: Optional[SensorsInfo] = None
raw_info: Optional[RawInfo] = None
+ pci_devices: Optional[List[PCIDevice]] = None
+ usb_devices: Optional[List[USBDevice]] = None
class HardwareSnapshotResponse(BaseModel):
@@ -157,6 +202,9 @@ class HardwareSnapshotResponse(BaseModel):
# RAM
ram_total_mb: Optional[int] = None
+ ram_used_mb: Optional[int] = None
+ ram_free_mb: Optional[int] = None
+ ram_shared_mb: Optional[int] = None
ram_slots_total: Optional[int] = None
ram_slots_used: Optional[int] = None
@@ -167,18 +215,37 @@ class HardwareSnapshotResponse(BaseModel):
# Storage
storage_summary: Optional[str] = None
storage_devices_json: Optional[str] = None
+ partitions_json: Optional[str] = None
# Network
network_interfaces_json: Optional[str] = None
+ network_shares_json: Optional[str] = None
- # OS / Motherboard
+ # OS / Motherboard / BIOS
os_name: Optional[str] = None
os_version: Optional[str] = None
kernel_version: Optional[str] = None
architecture: Optional[str] = None
virtualization_type: Optional[str] = None
+ hostname: Optional[str] = None
+ desktop_environment: Optional[str] = None
+ screen_resolution: Optional[str] = None
+ display_server: Optional[str] = None
+ session_type: Optional[str] = None
+ last_boot_time: Optional[str] = None
+ uptime_seconds: Optional[int] = None
+ battery_percentage: Optional[float] = None
+ battery_status: Optional[str] = None
+ battery_health: Optional[str] = None
motherboard_vendor: Optional[str] = None
motherboard_model: Optional[str] = None
+ bios_vendor: Optional[str] = None
+ bios_version: Optional[str] = None
+ bios_date: Optional[str] = None
+
+ # PCI and USB Devices
+ pci_devices_json: Optional[str] = None
+ usb_devices_json: Optional[str] = None
class Config:
from_attributes = True
diff --git a/backend/app/utils/scoring.py b/backend/app/utils/scoring.py
index e5995ec..0ac63bb 100644
--- a/backend/app/utils/scoring.py
+++ b/backend/app/utils/scoring.py
@@ -1,10 +1,103 @@
"""
Linux BenchTools - Scoring Utilities
+
+New normalized scoring formulas (0-100 scale):
+- CPU: events_per_second / 100
+- Memory: throughput_mib_s / 1000
+- Disk: (read_mb_s + write_mb_s) / 20
+- Network: (upload_mbps + download_mbps) / 20
+- GPU: glmark2_score / 50
"""
from app.core.config import settings
+def calculate_cpu_score(events_per_second: float = None) -> float:
+ """
+ Calculate CPU score from sysbench events per second.
+
+ Formula: events_per_second / 100
+ Range: 0-100 (capped)
+
+ Example: 3409.87 events/s → 34.1 score
+ """
+ if events_per_second is None or events_per_second <= 0:
+ return 0.0
+
+ score = events_per_second / 100.0
+ return min(100.0, max(0.0, score))
+
+
+def calculate_memory_score(throughput_mib_s: float = None) -> float:
+ """
+ Calculate Memory score from sysbench throughput.
+
+ Formula: throughput_mib_s / 1000
+ Range: 0-100 (capped)
+
+ Example: 13806.03 MiB/s → 13.8 score
+ """
+ if throughput_mib_s is None or throughput_mib_s <= 0:
+ return 0.0
+
+ score = throughput_mib_s / 1000.0
+ return min(100.0, max(0.0, score))
+
+
+def calculate_disk_score(read_mb_s: float = None, write_mb_s: float = None) -> float:
+ """
+ Calculate Disk score from fio read/write bandwidth.
+
+ Formula: (read_mb_s + write_mb_s) / 20
+ Range: 0-100 (capped)
+
+ Example: (695 + 695) MB/s → 69.5 score
+ """
+ if read_mb_s is None and write_mb_s is None:
+ return 0.0
+
+ read = read_mb_s if read_mb_s is not None and read_mb_s > 0 else 0.0
+ write = write_mb_s if write_mb_s is not None and write_mb_s > 0 else 0.0
+
+ score = (read + write) / 20.0
+ return min(100.0, max(0.0, score))
+
+
+def calculate_network_score(upload_mbps: float = None, download_mbps: float = None) -> float:
+ """
+ Calculate Network score from iperf3 upload/download speeds.
+
+ Formula: (upload_mbps + download_mbps) / 20
+ Range: 0-100 (capped)
+
+ Example: (484.67 + 390.13) Mbps → 43.7 score
+ """
+ if upload_mbps is None and download_mbps is None:
+ return 0.0
+
+ upload = upload_mbps if upload_mbps is not None and upload_mbps > 0 else 0.0
+ download = download_mbps if download_mbps is not None and download_mbps > 0 else 0.0
+
+ score = (upload + download) / 20.0
+ return min(100.0, max(0.0, score))
+
+
+def calculate_gpu_score(glmark2_score: int = None) -> float:
+ """
+ Calculate GPU score from glmark2 benchmark.
+
+ Formula: glmark2_score / 50
+ Range: 0-100 (capped)
+
+ Example: 2500 glmark2 → 50.0 score
+ """
+ if glmark2_score is None or glmark2_score <= 0:
+ return 0.0
+
+ score = glmark2_score / 50.0
+ return min(100.0, max(0.0, score))
+
+
def calculate_global_score(
cpu_score: float = None,
memory_score: float = None,
diff --git a/backend/apply_migration.py b/backend/apply_migration.py
old mode 100755
new mode 100644
diff --git a/backend/apply_migration_003.py b/backend/apply_migration_003.py
new file mode 100644
index 0000000..50fb4b2
--- /dev/null
+++ b/backend/apply_migration_003.py
@@ -0,0 +1,115 @@
+#!/usr/bin/env python3
+"""
+Apply SQL migration 003 to existing database
+Migration 003: Add cpu_score_single and cpu_score_multi columns to benchmarks table
+Usage: python apply_migration_003.py
+"""
+
+import os
+import sqlite3
+from typing import Dict, List, Tuple
+
+# Database path
+DB_PATH = os.path.join(os.path.dirname(__file__), "data", "data.db")
+MIGRATION_PATH = os.path.join(
+ os.path.dirname(__file__), "migrations", "003_add_cpu_scores.sql"
+)
+
+# (column_name, human description)
+COLUMNS_TO_ADD: List[Tuple[str, str]] = [
+ ("cpu_score_single", "Score CPU monocœur"),
+ ("cpu_score_multi", "Score CPU multicœur"),
+]
+
+
+def _load_statements() -> Dict[str, str]:
+ """Load SQL statements mapped by column name from the migration file."""
+ with open(MIGRATION_PATH, "r", encoding="utf-8") as f:
+ raw_sql = f.read()
+
+ # Remove comments and blank lines for easier parsing
+ filtered_lines = []
+ for line in raw_sql.splitlines():
+ stripped = line.strip()
+ if not stripped or stripped.startswith("--"):
+ continue
+ filtered_lines.append(line)
+
+ statements = {}
+ for statement in "\n".join(filtered_lines).split(";"):
+ stmt = statement.strip()
+ if not stmt:
+ continue
+ for column_name, _ in COLUMNS_TO_ADD:
+ if column_name in stmt:
+ statements[column_name] = stmt
+ break
+
+ return statements
+
+
+def apply_migration():
+ """Apply the SQL migration 003."""
+
+ if not os.path.exists(DB_PATH):
+ print(f"❌ Database not found at {DB_PATH}")
+ print(" The database will be created automatically on first run.")
+ return
+
+ if not os.path.exists(MIGRATION_PATH):
+ print(f"❌ Migration file not found at {MIGRATION_PATH}")
+ return
+
+ print(f"📂 Database: {DB_PATH}")
+ print(f"📄 Migration: {MIGRATION_PATH}")
+ print()
+
+ conn = sqlite3.connect(DB_PATH)
+ cursor = conn.cursor()
+
+ try:
+ cursor.execute("PRAGMA table_info(benchmarks)")
+ existing_columns = {row[1] for row in cursor.fetchall()}
+
+ missing_columns = [
+ col for col, _ in COLUMNS_TO_ADD if col not in existing_columns
+ ]
+ if not missing_columns:
+ print("⚠️ Migration 003 already applied (CPU score columns exist)")
+ print("✅ Database is up to date")
+ return
+
+ statements = _load_statements()
+
+ print("🔄 Applying migration 003...")
+ for column_name, description in COLUMNS_TO_ADD:
+ if column_name not in missing_columns:
+ print(f"⏩ Column {column_name} already present, skipping")
+ continue
+
+ statement = statements.get(column_name)
+ if not statement:
+ raise RuntimeError(
+ f"No SQL statement found for column '{column_name}' in migration file"
+ )
+
+ print(f"➕ Adding {description} ({column_name})...")
+ cursor.execute(statement)
+
+ conn.commit()
+
+ print("✅ Migration 003 applied successfully!")
+ print("New columns added to benchmarks table:")
+ for column_name, description in COLUMNS_TO_ADD:
+ if column_name in missing_columns:
+ print(f" - {column_name}: {description}")
+
+ except (sqlite3.Error, RuntimeError) as e:
+ print(f"❌ Error applying migration: {e}")
+ conn.rollback()
+ finally:
+ conn.close()
+
+
+if __name__ == "__main__":
+ apply_migration()
diff --git a/backend/apply_migration_004.py b/backend/apply_migration_004.py
new file mode 100644
index 0000000..8636bfd
--- /dev/null
+++ b/backend/apply_migration_004.py
@@ -0,0 +1,107 @@
+#!/usr/bin/env python3
+"""
+Apply SQL migration 004 to existing database.
+Migration 004: Add hostname/desktop environment/PCI/USB columns to hardware_snapshots.
+Usage: python apply_migration_004.py
+"""
+
+import os
+import sqlite3
+from typing import Dict, List, Tuple
+
+# Database path
+DB_PATH = os.path.join(os.path.dirname(__file__), "data", "data.db")
+MIGRATION_PATH = os.path.join(
+ os.path.dirname(__file__), "migrations", "004_add_snapshot_details.sql"
+)
+
+COLUMNS_TO_ADD: List[Tuple[str, str]] = [
+ ("hostname", "Nom d'hôte du snapshot"),
+ ("desktop_environment", "Environnement de bureau détecté"),
+ ("pci_devices_json", "Liste PCI en JSON"),
+ ("usb_devices_json", "Liste USB en JSON"),
+]
+
+
+def _load_statements() -> Dict[str, str]:
+ """Return ALTER TABLE statements indexed by column name."""
+ with open(MIGRATION_PATH, "r", encoding="utf-8") as f:
+ filtered = []
+ for line in f:
+ stripped = line.strip()
+ if not stripped or stripped.startswith("--"):
+ continue
+ filtered.append(line.rstrip("\n"))
+
+ statements: Dict[str, str] = {}
+ for statement in "\n".join(filtered).split(";"):
+ stmt = statement.strip()
+ if not stmt:
+ continue
+ for column, _ in COLUMNS_TO_ADD:
+ if column in stmt:
+ statements[column] = stmt
+ break
+ return statements
+
+
+def apply_migration():
+ """Apply the SQL migration 004."""
+ if not os.path.exists(DB_PATH):
+ print(f"❌ Database not found at {DB_PATH}")
+ print(" The database will be created automatically on first run.")
+ return
+
+ if not os.path.exists(MIGRATION_PATH):
+ print(f"❌ Migration file not found at {MIGRATION_PATH}")
+ return
+
+ print(f"📂 Database: {DB_PATH}")
+ print(f"📄 Migration: {MIGRATION_PATH}")
+ print()
+
+ conn = sqlite3.connect(DB_PATH)
+ cursor = conn.cursor()
+
+ try:
+ cursor.execute("PRAGMA table_info(hardware_snapshots)")
+ existing_columns = {row[1] for row in cursor.fetchall()}
+
+ missing = [col for col, _ in COLUMNS_TO_ADD if col not in existing_columns]
+ if not missing:
+ print("⚠️ Migration 004 already applied (columns exist)")
+ print("✅ Database is up to date")
+ return
+
+ statements = _load_statements()
+
+ print("🔄 Applying migration 004...")
+ for column, description in COLUMNS_TO_ADD:
+ if column not in missing:
+ print(f"⏩ Column {column} already present, skipping")
+ continue
+ statement = statements.get(column)
+ if not statement:
+ raise RuntimeError(
+ f"No SQL statement found for column '{column}' in migration file"
+ )
+ print(f"➕ Adding {description} ({column})...")
+ cursor.execute(statement)
+
+ conn.commit()
+
+ print("✅ Migration 004 applied successfully!")
+ print("New columns added to hardware_snapshots:")
+ for column, description in COLUMNS_TO_ADD:
+ if column in missing:
+ print(f" - {column}: {description}")
+
+ except (sqlite3.Error, RuntimeError) as exc:
+ print(f"❌ Error applying migration: {exc}")
+ conn.rollback()
+ finally:
+ conn.close()
+
+
+if __name__ == "__main__":
+ apply_migration()
diff --git a/backend/apply_migration_005.py b/backend/apply_migration_005.py
new file mode 100644
index 0000000..43d2cc1
--- /dev/null
+++ b/backend/apply_migration_005.py
@@ -0,0 +1,112 @@
+#!/usr/bin/env python3
+"""
+Apply SQL migration 005 to existing database.
+Migration 005: Add OS/display/battery metadata columns to hardware_snapshots.
+Usage: python apply_migration_005.py
+"""
+
+import os
+import sqlite3
+from typing import Dict, List, Tuple
+
+DB_PATH = os.path.join(os.path.dirname(__file__), "data", "data.db")
+MIGRATION_PATH = os.path.join(
+ os.path.dirname(__file__), "migrations", "005_add_os_display_and_battery.sql"
+)
+
+COLUMNS_TO_ADD: List[Tuple[str, str]] = [
+ ("screen_resolution", "Résolution écran"),
+ ("display_server", "Serveur d'affichage"),
+ ("session_type", "Type de session"),
+ ("last_boot_time", "Dernier boot"),
+ ("uptime_seconds", "Uptime en secondes"),
+ ("battery_percentage", "Pourcentage batterie"),
+ ("battery_status", "Statut batterie"),
+ ("battery_health", "Santé batterie"),
+]
+
+
+def _load_statements() -> Dict[str, str]:
+ """Load ALTER statements from migration file keyed by column name."""
+ with open(MIGRATION_PATH, "r", encoding="utf-8") as fh:
+ filtered = []
+ for line in fh:
+ stripped = line.strip()
+ if not stripped or stripped.startswith("--"):
+ continue
+ filtered.append(line.rstrip("\n"))
+
+ statements: Dict[str, str] = {}
+ for statement in "\n".join(filtered).split(";"):
+ stmt = statement.strip()
+ if not stmt:
+ continue
+ for column, _ in COLUMNS_TO_ADD:
+ if column in stmt:
+ statements[column] = stmt
+ break
+ return statements
+
+
+def apply_migration():
+ """Apply migration 005 to the SQLite database."""
+ if not os.path.exists(DB_PATH):
+ print(f"❌ Database not found at {DB_PATH}")
+ print(" The database will be created automatically on first run.")
+ return
+
+ if not os.path.exists(MIGRATION_PATH):
+ print(f"❌ Migration file not found at {MIGRATION_PATH}")
+ return
+
+ print(f"📂 Database: {DB_PATH}")
+ print(f"📄 Migration: {MIGRATION_PATH}")
+ print()
+
+ conn = sqlite3.connect(DB_PATH)
+ cursor = conn.cursor()
+
+ try:
+ cursor.execute("PRAGMA table_info(hardware_snapshots)")
+ existing_columns = {row[1] for row in cursor.fetchall()}
+
+ missing = [col for col, _ in COLUMNS_TO_ADD if col not in existing_columns]
+ if not missing:
+ print("⚠️ Migration 005 already applied (columns exist)")
+ print("✅ Database is up to date")
+ return
+
+ statements = _load_statements()
+
+ print("🔄 Applying migration 005...")
+ for column, description in COLUMNS_TO_ADD:
+ if column not in missing:
+ print(f"⏩ Column {column} already present, skipping")
+ continue
+
+ statement = statements.get(column)
+ if not statement:
+ raise RuntimeError(
+ f"No SQL statement found for column '{column}' in migration file"
+ )
+
+ print(f"➕ Adding {description} ({column})...")
+ cursor.execute(statement)
+
+ conn.commit()
+
+ print("✅ Migration 005 applied successfully!")
+ print("New columns added to hardware_snapshots:")
+ for column, description in COLUMNS_TO_ADD:
+ if column in missing:
+ print(f" - {column}: {description}")
+
+ except (sqlite3.Error, RuntimeError) as exc:
+ print(f"❌ Error applying migration: {exc}")
+ conn.rollback()
+ finally:
+ conn.close()
+
+
+if __name__ == "__main__":
+ apply_migration()
diff --git a/backend/apply_migration_006.py b/backend/apply_migration_006.py
new file mode 100644
index 0000000..60550cc
--- /dev/null
+++ b/backend/apply_migration_006.py
@@ -0,0 +1,61 @@
+#!/usr/bin/env python3
+"""
+Apply SQL migration 006 to existing database
+Migration 006: Add purchase metadata fields to devices table
+"""
+
+import os
+import sqlite3
+
+DB_PATH = os.path.join(os.path.dirname(__file__), "data", "data.db")
+MIGRATION_PATH = os.path.join(
+ os.path.dirname(__file__), "migrations", "006_add_purchase_fields.sql"
+)
+
+COLUMNS = ["purchase_store", "purchase_date", "purchase_price"]
+
+
+def apply_migration():
+ if not os.path.exists(DB_PATH):
+ print(f"❌ Database not found at {DB_PATH}")
+ print(" It will be created automatically on first backend start.")
+ return
+
+ if not os.path.exists(MIGRATION_PATH):
+ print(f"❌ Migration file not found at {MIGRATION_PATH}")
+ return
+
+ conn = sqlite3.connect(DB_PATH)
+ cursor = conn.cursor()
+
+ try:
+ cursor.execute("PRAGMA table_info(devices)")
+ existing_columns = {row[1] for row in cursor.fetchall()}
+
+ missing = [col for col in COLUMNS if col not in existing_columns]
+ if not missing:
+ print("⚠️ Migration 006 already applied (purchase columns exist)")
+ return
+
+ print("🔄 Applying migration 006 (purchase fields)...")
+ with open(MIGRATION_PATH, "r", encoding="utf-8") as f:
+ statements = [
+ stmt.strip()
+ for stmt in f.read().split(";")
+ if stmt.strip()
+ ]
+
+ for stmt in statements:
+ cursor.execute(stmt)
+
+ conn.commit()
+ print("✅ Migration 006 applied successfully.")
+ except sqlite3.Error as exc:
+ conn.rollback()
+ print(f"❌ Error applying migration 006: {exc}")
+ finally:
+ conn.close()
+
+
+if __name__ == "__main__":
+ apply_migration()
diff --git a/backend/migrations/001_add_ram_stats_and_smart.sql b/backend/migrations/001_add_ram_stats_and_smart.sql
old mode 100644
new mode 100755
diff --git a/backend/migrations/002_add_network_results.sql b/backend/migrations/002_add_network_results.sql
old mode 100644
new mode 100755
diff --git a/backend/migrations/003_add_cpu_scores.sql b/backend/migrations/003_add_cpu_scores.sql
new file mode 100755
index 0000000..c0ce6b7
--- /dev/null
+++ b/backend/migrations/003_add_cpu_scores.sql
@@ -0,0 +1,5 @@
+-- Migration 003: Add CPU subscore columns to benchmarks table
+-- Date: 2025-12-15
+
+ALTER TABLE benchmarks ADD COLUMN cpu_score_single FLOAT;
+ALTER TABLE benchmarks ADD COLUMN cpu_score_multi FLOAT;
diff --git a/backend/migrations/004_add_snapshot_details.sql b/backend/migrations/004_add_snapshot_details.sql
new file mode 100755
index 0000000..e9345f9
--- /dev/null
+++ b/backend/migrations/004_add_snapshot_details.sql
@@ -0,0 +1,7 @@
+-- Migration 004: Add extra hardware snapshot metadata columns
+-- Date: 2025-12-17
+
+ALTER TABLE hardware_snapshots ADD COLUMN hostname VARCHAR(255);
+ALTER TABLE hardware_snapshots ADD COLUMN desktop_environment VARCHAR(100);
+ALTER TABLE hardware_snapshots ADD COLUMN pci_devices_json TEXT;
+ALTER TABLE hardware_snapshots ADD COLUMN usb_devices_json TEXT;
diff --git a/backend/migrations/005_add_os_display_and_battery.sql b/backend/migrations/005_add_os_display_and_battery.sql
new file mode 100755
index 0000000..61f87fd
--- /dev/null
+++ b/backend/migrations/005_add_os_display_and_battery.sql
@@ -0,0 +1,11 @@
+-- Migration 005: Extend hardware_snapshots with OS/display/battery metadata
+-- Date: 2025-12-17
+
+ALTER TABLE hardware_snapshots ADD COLUMN screen_resolution VARCHAR(50);
+ALTER TABLE hardware_snapshots ADD COLUMN display_server VARCHAR(50);
+ALTER TABLE hardware_snapshots ADD COLUMN session_type VARCHAR(50);
+ALTER TABLE hardware_snapshots ADD COLUMN last_boot_time VARCHAR(50);
+ALTER TABLE hardware_snapshots ADD COLUMN uptime_seconds INTEGER;
+ALTER TABLE hardware_snapshots ADD COLUMN battery_percentage FLOAT;
+ALTER TABLE hardware_snapshots ADD COLUMN battery_status VARCHAR(50);
+ALTER TABLE hardware_snapshots ADD COLUMN battery_health VARCHAR(50);
diff --git a/backend/migrations/006_add_purchase_fields.sql b/backend/migrations/006_add_purchase_fields.sql
new file mode 100755
index 0000000..8a37a9d
--- /dev/null
+++ b/backend/migrations/006_add_purchase_fields.sql
@@ -0,0 +1,4 @@
+-- Add purchase metadata columns to devices table
+ALTER TABLE devices ADD COLUMN purchase_store TEXT;
+ALTER TABLE devices ADD COLUMN purchase_date TEXT;
+ALTER TABLE devices ADD COLUMN purchase_price REAL;
diff --git a/backend/migrations/add_bios_vendor.sql b/backend/migrations/add_bios_vendor.sql
old mode 100644
new mode 100755
diff --git a/backend/requirements.txt b/backend/requirements.txt
old mode 100644
new mode 100755
diff --git a/docker-compose.yml b/docker-compose.yml
old mode 100644
new mode 100755
index 5d03e6a..c081951
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -9,6 +9,7 @@ services:
volumes:
- ./backend/data:/app/data
- ./uploads:/app/uploads
+ - ./backend/app:/app/app
environment:
- API_TOKEN=${API_TOKEN:-CHANGE_ME_GENERATE_RANDOM_TOKEN}
- DATABASE_URL=sqlite:////app/data/data.db
@@ -24,6 +25,7 @@ services:
- "${FRONTEND_PORT:-8087}:80"
volumes:
- ./frontend:/usr/share/nginx/html:ro
+ - ./scripts/bench.sh:/usr/share/nginx/html/scripts/bench.sh:ro
restart: unless-stopped
networks:
- benchtools
diff --git a/docs/01_vision_fonctionnelle.md b/docs/01_vision_fonctionnelle.md
old mode 100644
new mode 100755
diff --git a/docs/02_model_donnees.md b/docs/02_model_donnees.md
old mode 100644
new mode 100755
diff --git a/docs/03_api_backend.md b/docs/03_api_backend.md
old mode 100644
new mode 100755
diff --git a/docs/04_bench_script_client.md b/docs/04_bench_script_client.md
old mode 100644
new mode 100755
diff --git a/docs/05_webui_design.md b/docs/05_webui_design.md
old mode 100644
new mode 100755
diff --git a/docs/06_backend_architecture.md b/docs/06_backend_architecture.md
old mode 100644
new mode 100755
diff --git a/docs/08_installation_bootstrap.md b/docs/08_installation_bootstrap.md
old mode 100644
new mode 100755
diff --git a/docs/09_tests_qualite.md b/docs/09_tests_qualite.md
old mode 100644
new mode 100755
diff --git a/docs/10_roadmap_evolutions.md b/docs/10_roadmap_evolutions.md
old mode 100644
new mode 100755
diff --git a/frontend/config.js b/frontend/config.js
new file mode 100755
index 0000000..607462f
--- /dev/null
+++ b/frontend/config.js
@@ -0,0 +1,28 @@
+// Frontend configuration (can be overridden by defining window.BenchConfig before loading this file)
+(function() {
+ window.BenchConfig = window.BenchConfig || {};
+
+ const origin = window.location.origin;
+ const protocol = window.location.protocol;
+ const hostname = window.location.hostname;
+
+ if (!window.BenchConfig.frontendBaseUrl) {
+ window.BenchConfig.frontendBaseUrl = origin;
+ }
+
+ if (!window.BenchConfig.backendApiUrl) {
+ window.BenchConfig.backendApiUrl = `${protocol}//${hostname}:8007/api`;
+ }
+
+ if (!window.BenchConfig.benchScriptPath) {
+ window.BenchConfig.benchScriptPath = '/scripts/bench.sh';
+ }
+
+ if (!window.BenchConfig.apiTokenPlaceholder) {
+ window.BenchConfig.apiTokenPlaceholder = 'YOUR_TOKEN';
+ }
+
+ if (!window.BenchConfig.iperfServer) {
+ window.BenchConfig.iperfServer = '10.0.1.97';
+ }
+})();
diff --git a/frontend/css/components.css b/frontend/css/components.css
old mode 100644
new mode 100755
index f8398b0..ec9583a
--- a/frontend/css/components.css
+++ b/frontend/css/components.css
@@ -475,6 +475,9 @@
color: var(--text-secondary);
font-size: 0.75rem;
border: 1px solid var(--bg-tertiary);
+ display: inline-flex;
+ align-items: center;
+ gap: 0.25rem;
}
.tag-primary {
@@ -483,6 +486,16 @@
border-color: var(--color-info);
}
+.tag .remove-tag {
+ background: transparent;
+ border: none;
+ color: inherit;
+ cursor: pointer;
+ font-size: 0.8rem;
+ padding: 0;
+ line-height: 1;
+}
+
/* Alert Component */
.alert {
padding: var(--spacing-md);
diff --git a/frontend/css/main.css b/frontend/css/main.css
old mode 100644
new mode 100755
index 709e327..ed7d977
--- a/frontend/css/main.css
+++ b/frontend/css/main.css
@@ -16,6 +16,7 @@
--color-info: #66d9ef;
--color-purple: #ae81ff;
--color-yellow: #e6db74;
+ --border-color: #444444;
/* Spacing */
--spacing-xs: 0.25rem;
@@ -28,6 +29,12 @@
--radius-sm: 4px;
--radius-md: 8px;
--radius-lg: 12px;
+
+ /* Icon sizing (customisable) */
+ --section-icon-size: 32px;
+ --button-icon-size: 24px;
+ --icon-btn-size: 42px;
+ --icon-btn-icon-size: 26px;
}
/* Reset & Base */
@@ -198,6 +205,264 @@ td {
color: var(--text-primary);
}
+/* Device sections */
+.device-section {
+ background: var(--bg-secondary);
+ border: 1px solid var(--bg-tertiary);
+ border-radius: var(--radius-md);
+ padding: var(--spacing-md);
+ margin-bottom: var(--spacing-md);
+}
+
+.device-section .section-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ gap: var(--spacing-md);
+ margin-bottom: var(--spacing-sm);
+ border-bottom: 1px solid var(--bg-tertiary);
+ padding-bottom: var(--spacing-sm);
+}
+
+.device-section .section-header h3 {
+ margin: 0;
+ font-size: 1.05rem;
+ color: var(--color-info);
+ display: flex;
+ align-items: center;
+ gap: var(--spacing-sm);
+}
+
+.section-icon-wrap {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.section-icon {
+ width: var(--section-icon-size);
+ height: var(--section-icon-size);
+ object-fit: contain;
+ filter: drop-shadow(0 0 2px rgba(0,0,0,0.4));
+}
+
+.section-title {
+ line-height: 1.2;
+}
+
+.section-actions {
+ display: flex;
+ align-items: center;
+ gap: var(--spacing-sm);
+}
+
+.icon-btn {
+ background: var(--bg-primary);
+ border: 1px solid var(--bg-tertiary);
+ border-radius: 50%;
+ width: 38px;
+ height: 38px;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+ padding: 0;
+ transition: transform 0.2s ease, border-color 0.2s ease;
+ text-decoration: none;
+ color: var(--text-primary);
+}
+
+.icon-btn img {
+ width: var(--button-icon-size);
+ height: var(--button-icon-size);
+ object-fit: contain;
+}
+
+.icon-btn:hover {
+ transform: scale(1.05);
+ border-color: var(--color-info);
+}
+
+.icon-btn.danger {
+ border-color: var(--color-danger);
+}
+
+.icon-btn.success {
+ border-color: var(--color-success);
+}
+
+.doc-actions {
+ display: flex;
+ gap: var(--spacing-xs);
+}
+
+.doc-actions .icon-btn {
+ width: 32px;
+ height: 32px;
+}
+
+.device-preamble {
+ border: 1px solid var(--bg-tertiary);
+ border-radius: var(--radius-md);
+ padding: var(--spacing-md);
+ margin-bottom: var(--spacing-md);
+ background: var(--bg-secondary);
+}
+
+.preamble-content {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: var(--spacing-lg);
+ align-items: start;
+}
+
+.preamble-left {
+ display: flex;
+ flex-direction: column;
+ gap: var(--spacing-sm);
+}
+
+.preamble-right {
+ display: flex;
+ flex-direction: column;
+}
+
+@media (max-width: 768px) {
+ .preamble-content {
+ grid-template-columns: 1fr;
+ }
+}
+
+.header-row {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ gap: var(--spacing-lg);
+ margin-bottom: var(--spacing-sm);
+}
+
+.header-label {
+ color: var(--text-secondary);
+ font-size: 0.8rem;
+ text-transform: uppercase;
+ letter-spacing: 0.05em;
+}
+
+.header-value {
+ font-size: 1.1rem;
+ font-weight: 600;
+ color: var(--text-primary);
+}
+
+.header-meta {
+ font-size: 0.8rem;
+ color: var(--text-secondary);
+ margin-top: 0.25rem;
+}
+
+.header-stat {
+ text-align: right;
+}
+
+.usage-pill {
+ display: inline-block;
+ padding: 0.2rem 0.6rem;
+ border-radius: 999px;
+ font-size: 0.8rem;
+ font-weight: 600;
+}
+
+.usage-pill.ok {
+ background: rgba(166, 226, 46, 0.2);
+ color: var(--color-success);
+}
+
+.usage-pill.medium {
+ background: rgba(253, 151, 31, 0.2);
+ color: var(--color-warning);
+}
+
+.usage-pill.high {
+ background: rgba(249, 38, 114, 0.2);
+ color: var(--color-danger);
+}
+
+.usage-pill.muted {
+ background: var(--bg-tertiary);
+ color: var(--text-secondary);
+}
+
+.inline-form,
+.links-form,
+.tag-form {
+ display: flex;
+ gap: var(--spacing-sm);
+ align-items: center;
+ margin-bottom: var(--spacing-sm);
+}
+
+.inline-form input,
+.links-form input,
+.tag-form input {
+ flex: 1;
+ padding: 0.5rem 0.75rem;
+ border: 1px solid var(--border-color);
+ border-radius: var(--radius-sm);
+ background: var(--bg-primary);
+ color: var(--text-primary);
+}
+
+.link-item {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ gap: var(--spacing-sm);
+ padding: 0.75rem;
+ border: 1px solid var(--border-color);
+ border-radius: var(--radius-sm);
+ background: var(--bg-primary);
+}
+
+/* Device list actions */
+.device-list-item {
+ position: relative;
+}
+
+.device-list-delete {
+ background: transparent;
+ border: none;
+ color: var(--color-danger);
+ cursor: pointer;
+ font-size: 0.9rem;
+ padding: 0.2rem;
+ transition: transform 0.2s ease;
+ position: relative;
+ z-index: 10;
+ pointer-events: auto;
+}
+
+.device-list-delete:hover {
+ transform: scale(1.2);
+ filter: brightness(1.3);
+}
+
+/* Markdown blocks */
+.markdown-block {
+ background: var(--bg-primary);
+ border: 1px solid var(--bg-tertiary);
+ border-radius: var(--radius-sm);
+ padding: var(--spacing-sm);
+ white-space: pre-wrap;
+ line-height: 1.5;
+}
+
+.markdown-block code {
+ background: rgba(0,0,0,0.3);
+ padding: 0 0.25rem;
+ border-radius: 4px;
+ font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
+}
+
tbody tr {
transition: background-color 0.2s;
}
diff --git a/frontend/device_detail.html b/frontend/device_detail.html
old mode 100644
new mode 100755
index 686e53c..512e164
--- a/frontend/device_detail.html
+++ b/frontend/device_detail.html
@@ -6,6 +6,8 @@
Device Detail - Linux BenchTools
+
+
@@ -32,12 +34,15 @@
-
+
diff --git a/frontend/devices.html b/frontend/devices.html
old mode 100644
new mode 100755
index 3ba6248..286010b
--- a/frontend/devices.html
+++ b/frontend/devices.html
@@ -6,6 +6,8 @@
Devices - Linux BenchTools
+
+
@@ -58,7 +60,24 @@
© 2025 Linux BenchTools - Self-hosted benchmarking tool
+
+
+
+
diff --git a/frontend/icons/favicon/icons8-devices-3d-fluency-16.png b/frontend/icons/favicon/icons8-devices-3d-fluency-16.png
new file mode 100755
index 0000000..b4ded81
Binary files /dev/null and b/frontend/icons/favicon/icons8-devices-3d-fluency-16.png differ
diff --git a/frontend/icons/favicon/icons8-devices-3d-fluency-32.png b/frontend/icons/favicon/icons8-devices-3d-fluency-32.png
new file mode 100755
index 0000000..cab0586
Binary files /dev/null and b/frontend/icons/favicon/icons8-devices-3d-fluency-32.png differ
diff --git a/frontend/icons/favicon/icons8-devices-3d-fluency-57.png b/frontend/icons/favicon/icons8-devices-3d-fluency-57.png
new file mode 100755
index 0000000..715a675
Binary files /dev/null and b/frontend/icons/favicon/icons8-devices-3d-fluency-57.png differ
diff --git a/frontend/icons/favicon/icons8-devices-3d-fluency-60.png b/frontend/icons/favicon/icons8-devices-3d-fluency-60.png
new file mode 100755
index 0000000..2f5a16b
Binary files /dev/null and b/frontend/icons/favicon/icons8-devices-3d-fluency-60.png differ
diff --git a/frontend/icons/favicon/icons8-devices-3d-fluency-70.png b/frontend/icons/favicon/icons8-devices-3d-fluency-70.png
new file mode 100755
index 0000000..de863a9
Binary files /dev/null and b/frontend/icons/favicon/icons8-devices-3d-fluency-70.png differ
diff --git a/frontend/icons/favicon/icons8-devices-3d-fluency-72.png b/frontend/icons/favicon/icons8-devices-3d-fluency-72.png
new file mode 100755
index 0000000..6403f95
Binary files /dev/null and b/frontend/icons/favicon/icons8-devices-3d-fluency-72.png differ
diff --git a/frontend/icons/favicon/icons8-devices-3d-fluency-76.png b/frontend/icons/favicon/icons8-devices-3d-fluency-76.png
new file mode 100755
index 0000000..e30def0
Binary files /dev/null and b/frontend/icons/favicon/icons8-devices-3d-fluency-76.png differ
diff --git a/frontend/icons/favicon/icons8-devices-3d-fluency-96.png b/frontend/icons/favicon/icons8-devices-3d-fluency-96.png
new file mode 100755
index 0000000..a51b0d9
Binary files /dev/null and b/frontend/icons/favicon/icons8-devices-3d-fluency-96.png differ
diff --git a/frontend/icons/icons8-benchmark-64.png b/frontend/icons/icons8-benchmark-64.png
new file mode 100755
index 0000000..ee24655
Binary files /dev/null and b/frontend/icons/icons8-benchmark-64.png differ
diff --git a/frontend/icons/icons8-bios-94.png b/frontend/icons/icons8-bios-94.png
new file mode 100755
index 0000000..e296061
Binary files /dev/null and b/frontend/icons/icons8-bios-94.png differ
diff --git a/frontend/icons/icons8-check-mark-48.png b/frontend/icons/icons8-check-mark-48.png
new file mode 100755
index 0000000..a198782
Binary files /dev/null and b/frontend/icons/icons8-check-mark-48.png differ
diff --git a/frontend/icons/icons8-close-48.png b/frontend/icons/icons8-close-48.png
new file mode 100755
index 0000000..1f4caa2
Binary files /dev/null and b/frontend/icons/icons8-close-48.png differ
diff --git a/frontend/icons/icons8-debian-48.png b/frontend/icons/icons8-debian-48.png
new file mode 100755
index 0000000..c59aa27
Binary files /dev/null and b/frontend/icons/icons8-debian-48.png differ
diff --git a/frontend/icons/icons8-delete-48.png b/frontend/icons/icons8-delete-48.png
new file mode 100755
index 0000000..a1d2731
Binary files /dev/null and b/frontend/icons/icons8-delete-48.png differ
diff --git a/frontend/icons/icons8-done-48.png b/frontend/icons/icons8-done-48.png
new file mode 100755
index 0000000..7ca088a
Binary files /dev/null and b/frontend/icons/icons8-done-48.png differ
diff --git a/frontend/icons/icons8-edit-pencil-48.png b/frontend/icons/icons8-edit-pencil-48.png
new file mode 100755
index 0000000..0952e0c
Binary files /dev/null and b/frontend/icons/icons8-edit-pencil-48.png differ
diff --git a/frontend/icons/icons8-ethernet-on-94.png b/frontend/icons/icons8-ethernet-on-94.png
new file mode 100755
index 0000000..97fe259
Binary files /dev/null and b/frontend/icons/icons8-ethernet-on-94.png differ
diff --git a/frontend/icons/icons8-gpu-64.png b/frontend/icons/icons8-gpu-64.png
new file mode 100755
index 0000000..81c990f
Binary files /dev/null and b/frontend/icons/icons8-gpu-64.png differ
diff --git a/frontend/icons/icons8-hardware-64.png b/frontend/icons/icons8-hardware-64.png
new file mode 100755
index 0000000..9bab239
Binary files /dev/null and b/frontend/icons/icons8-hardware-64.png differ
diff --git a/frontend/icons/icons8-hdd-94.png b/frontend/icons/icons8-hdd-94.png
new file mode 100755
index 0000000..2c41411
Binary files /dev/null and b/frontend/icons/icons8-hdd-94.png differ
diff --git a/frontend/icons/icons8-laptop-50.png b/frontend/icons/icons8-laptop-50.png
new file mode 100755
index 0000000..b2b02cf
Binary files /dev/null and b/frontend/icons/icons8-laptop-50.png differ
diff --git a/frontend/icons/icons8-memory-slot-94.png b/frontend/icons/icons8-memory-slot-94.png
new file mode 100755
index 0000000..81c6f5d
Binary files /dev/null and b/frontend/icons/icons8-memory-slot-94.png differ
diff --git a/frontend/icons/icons8-motherboard-94.png b/frontend/icons/icons8-motherboard-94.png
new file mode 100755
index 0000000..941bfb2
Binary files /dev/null and b/frontend/icons/icons8-motherboard-94.png differ
diff --git a/frontend/icons/icons8-network-cable-94.png b/frontend/icons/icons8-network-cable-94.png
new file mode 100755
index 0000000..50333cb
Binary files /dev/null and b/frontend/icons/icons8-network-cable-94.png differ
diff --git a/frontend/icons/icons8-operating-system-64.png b/frontend/icons/icons8-operating-system-64.png
new file mode 100755
index 0000000..3a21dcc
Binary files /dev/null and b/frontend/icons/icons8-operating-system-64.png differ
diff --git a/frontend/icons/icons8-pcie-48.png b/frontend/icons/icons8-pcie-48.png
new file mode 100755
index 0000000..1b2acf6
Binary files /dev/null and b/frontend/icons/icons8-pcie-48.png differ
diff --git a/frontend/icons/icons8-picture-48.png b/frontend/icons/icons8-picture-48.png
new file mode 100755
index 0000000..4aa0aad
Binary files /dev/null and b/frontend/icons/icons8-picture-48.png differ
diff --git a/frontend/icons/icons8-processor-64.png b/frontend/icons/icons8-processor-64.png
new file mode 100755
index 0000000..99ffae7
Binary files /dev/null and b/frontend/icons/icons8-processor-64.png differ
diff --git a/frontend/icons/icons8-processor-94.png b/frontend/icons/icons8-processor-94.png
new file mode 100755
index 0000000..8df55ea
Binary files /dev/null and b/frontend/icons/icons8-processor-94.png differ
diff --git a/frontend/icons/icons8-ram-64.png b/frontend/icons/icons8-ram-64.png
new file mode 100755
index 0000000..22f4b7b
Binary files /dev/null and b/frontend/icons/icons8-ram-64.png differ
diff --git a/frontend/icons/icons8-save-48.png b/frontend/icons/icons8-save-48.png
new file mode 100755
index 0000000..b551de1
Binary files /dev/null and b/frontend/icons/icons8-save-48.png differ
diff --git a/frontend/icons/icons8-server-94.png b/frontend/icons/icons8-server-94.png
new file mode 100755
index 0000000..8aff138
Binary files /dev/null and b/frontend/icons/icons8-server-94.png differ
diff --git a/frontend/icons/icons8-setting-48.png b/frontend/icons/icons8-setting-48.png
new file mode 100755
index 0000000..06fc5cb
Binary files /dev/null and b/frontend/icons/icons8-setting-48.png differ
diff --git a/frontend/icons/icons8-shared-folder-94.png b/frontend/icons/icons8-shared-folder-94.png
new file mode 100755
index 0000000..88f2ffa
Binary files /dev/null and b/frontend/icons/icons8-shared-folder-94.png differ
diff --git a/frontend/icons/icons8-ssd-94.png b/frontend/icons/icons8-ssd-94.png
new file mode 100755
index 0000000..344ead7
Binary files /dev/null and b/frontend/icons/icons8-ssd-94.png differ
diff --git a/frontend/icons/icons8-usb-memory-stick-94.png b/frontend/icons/icons8-usb-memory-stick-94.png
new file mode 100755
index 0000000..0c5c5b6
Binary files /dev/null and b/frontend/icons/icons8-usb-memory-stick-94.png differ
diff --git a/frontend/icons/icons8-windows-11-48.png b/frontend/icons/icons8-windows-11-48.png
new file mode 100755
index 0000000..2549981
Binary files /dev/null and b/frontend/icons/icons8-windows-11-48.png differ
diff --git a/frontend/icons/icons8-workstation-94.png b/frontend/icons/icons8-workstation-94.png
new file mode 100755
index 0000000..cc706f2
Binary files /dev/null and b/frontend/icons/icons8-workstation-94.png differ
diff --git a/frontend/index.html b/frontend/index.html
old mode 100644
new mode 100755
index c1be6a2..d805422
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -6,6 +6,8 @@
Linux BenchTools - Dashboard
+
+
@@ -77,7 +79,7 @@
Copier
- curl -s http://VOTRE_SERVEUR/scripts/bench.sh | bash -s -- --server http://VOTRE_SERVEUR:8007/api/benchmark --token YOUR_TOKEN
+ Chargement...
@@ -99,6 +101,7 @@
+
diff --git a/frontend/js/api.js b/frontend/js/api.js
old mode 100644
new mode 100755
index 2f960c2..0a286e6
--- a/frontend/js/api.js
+++ b/frontend/js/api.js
@@ -1,6 +1,7 @@
// Linux BenchTools - API Client
-const API_BASE_URL = window.location.protocol + '//' + window.location.hostname + ':8007/api';
+const API_BASE_URL = (window.BenchConfig && window.BenchConfig.backendApiUrl)
+ || `${window.location.protocol}//${window.location.hostname}:8007/api`;
class BenchAPI {
constructor(baseURL = API_BASE_URL) {
@@ -143,6 +144,14 @@ class BenchAPI {
return this.get(`/benchmarks/${benchmarkId}`);
}
+ // Update benchmark fields
+ async updateBenchmark(benchmarkId, data) {
+ return this.request(`/benchmarks/${benchmarkId}`, {
+ method: 'PATCH',
+ body: JSON.stringify(data)
+ });
+ }
+
// Get all benchmarks
async getAllBenchmarks(params = {}) {
return this.get('/benchmarks', params);
diff --git a/frontend/js/dashboard.js b/frontend/js/dashboard.js
old mode 100644
new mode 100755
index 336172b..4419bc5
--- a/frontend/js/dashboard.js
+++ b/frontend/js/dashboard.js
@@ -6,6 +6,23 @@ const api = window.BenchAPI;
// Global state
let allDevices = [];
let isLoading = false;
+let apiToken = null;
+let iperfServer = null;
+
+// Load backend config (API token, etc.)
+async function loadBackendConfig() {
+ try {
+ const response = await fetch(`${window.BenchConfig.backendApiUrl}/config`);
+ if (response.ok) {
+ const config = await response.json();
+ apiToken = config.api_token;
+ iperfServer = config.iperf_server || '10.0.1.97';
+ updateBenchCommandDisplay();
+ }
+ } catch (error) {
+ console.error('Failed to load backend config:', error);
+ }
+}
// Load dashboard data
async function loadDashboard() {
@@ -83,7 +100,7 @@ async function loadStats() {
}
});
- const avgScore = scoreCount > 0 ? Math.round(scoreSum / scoreCount) : 0;
+ const avgScore = scoreCount > 0 ? Math.ceil(scoreSum / scoreCount) : 0;
// Update UI
document.getElementById('totalDevices').textContent = totalDevices;
@@ -221,6 +238,26 @@ function createDeviceRow(device, rank) {
`;
}
+function buildBenchCommand() {
+ const cfg = window.BenchConfig || {};
+ const frontendBase = (cfg.frontendBaseUrl || window.location.origin).replace(/\/$/, '');
+ const scriptPath = cfg.benchScriptPath || '/scripts/bench.sh';
+ const backendBase = (cfg.backendApiUrl || `${window.location.protocol}//${window.location.hostname}:8007/api`).replace(/\/$/, '');
+ const token = apiToken || 'LOADING...';
+ const iperf = iperfServer || '10.0.1.97';
+
+ // Extract backend URL without /api suffix
+ const backendUrl = backendBase.replace(/\/api$/, '');
+
+ return `curl -fsSL ${frontendBase}${scriptPath} | sudo bash -s -- --server ${backendUrl} --token "${token}" --iperf-server ${iperf}`;
+}
+
+function updateBenchCommandDisplay() {
+ const element = document.getElementById('benchCommand');
+ if (!element) return;
+ element.textContent = buildBenchCommand();
+}
+
// Copy bench command to clipboard
async function copyBenchCommand() {
const command = document.getElementById('benchCommand').textContent;
@@ -282,7 +319,11 @@ function refreshDashboard() {
}
// Initialize dashboard on page load
-document.addEventListener('DOMContentLoaded', () => {
+document.addEventListener('DOMContentLoaded', async () => {
+ // Load backend config first to get API token
+ await loadBackendConfig();
+
+ // Then load dashboard data
loadDashboard();
// Setup search input listener
diff --git a/frontend/js/device_detail.js b/frontend/js/device_detail.js
old mode 100644
new mode 100755
index 1f4420c..e374f1c
--- a/frontend/js/device_detail.js
+++ b/frontend/js/device_detail.js
@@ -1,6 +1,6 @@
// Linux BenchTools - Device Detail Logic
-const { formatDate, formatRelativeTime, formatFileSize, createScoreBadge, getScoreBadgeText, escapeHtml, showError, showEmptyState, formatTags, initTabs, openModal, showToast, formatHardwareInfo } = window.BenchUtils;
+const { formatDate, formatRelativeTime, formatFileSize, createScoreBadge, getScoreBadgeText, escapeHtml, showError, showEmptyState, formatTags, initTabs, openModal, showToast, formatHardwareInfo, formatDuration, formatStorage } = window.BenchUtils;
const api = window.BenchAPI;
let currentDeviceId = null;
@@ -46,6 +46,11 @@ async function loadDeviceDetail() {
await loadDocuments();
await loadLinks();
+ const deleteBtn = document.getElementById('deleteDeviceBtn');
+ if (deleteBtn) {
+ deleteBtn.addEventListener('click', handleDeleteDevice);
+ }
+
} catch (error) {
console.error('Failed to load device:', error);
document.getElementById('loadingState').innerHTML =
@@ -300,7 +305,7 @@ function renderStorageDetails() {
${disk.capacity_gb ? `
- Capacité: ${disk.capacity_gb} GB
+ Capacité: ${formatStorage(disk.capacity_gb)}
` : ''}
${disk.type ? `
@@ -331,7 +336,114 @@ function renderStorageDetails() {
console.error('Failed to parse storage devices:', e);
html = '
Erreur lors du parsing des données de stockage
';
}
- } else {
+ }
+
+ // Parse partitions
+ if (snapshot.partitions_json) {
+ try {
+ const partitions = typeof snapshot.partitions_json === 'string'
+ ? JSON.parse(snapshot.partitions_json)
+ : snapshot.partitions_json;
+
+ if (Array.isArray(partitions) && partitions.length > 0) {
+ html += `
+
+
Partitions et volumes
+
+
+
+
+ Partition
+ Montage
+ Type
+ Utilisé
+ Libre
+ Total
+
+
+
+ ${partitions.map(part => {
+ const used = typeof part.used_gb === 'number' ? formatStorage(part.used_gb) : 'N/A';
+ const free = typeof part.free_gb === 'number'
+ ? formatStorage(part.free_gb)
+ : (typeof part.total_gb === 'number' && typeof part.used_gb === 'number'
+ ? formatStorage(part.total_gb - part.used_gb)
+ : 'N/A');
+ const total = typeof part.total_gb === 'number' ? formatStorage(part.total_gb) : 'N/A';
+
+ return `
+
+ ${escapeHtml(part.name || 'N/A')}
+ ${part.mount_point ? escapeHtml(part.mount_point) : 'Non monté '}
+ ${part.fs_type ? escapeHtml(part.fs_type) : 'N/A'}
+ ${used}
+ ${free}
+ ${total}
+
+ `;
+ }).join('')}
+
+
+
+
+ `;
+ }
+ } catch (error) {
+ console.error('Failed to parse partitions:', error);
+ html += '
Erreur lors de la lecture des partitions
';
+ }
+ }
+
+ if (snapshot.network_shares_json) {
+ try {
+ const shares = typeof snapshot.network_shares_json === 'string'
+ ? JSON.parse(snapshot.network_shares_json)
+ : snapshot.network_shares_json;
+
+ if (Array.isArray(shares) && shares.length > 0) {
+ html += `
+
+
Partages réseau montés
+
+
+
+
+ Source
+ Montage
+ Protocole
+ Type
+ Utilisé
+ Libre
+ Total
+ Options
+
+
+
+ ${shares.map(share => `
+
+ ${escapeHtml(share.source || 'N/A')}
+ ${escapeHtml(share.mount_point || 'N/A')}
+ ${escapeHtml(share.protocol || share.fs_type || 'N/A')}
+ ${share.fs_type ? escapeHtml(share.fs_type) : 'N/A'}
+ ${typeof share.used_gb === 'number' ? formatStorage(share.used_gb) : 'N/A'}
+ ${typeof share.free_gb === 'number' ? formatStorage(share.free_gb) : 'N/A'}
+ ${typeof share.total_gb === 'number' ? formatStorage(share.total_gb) : 'N/A'}
+ ${share.options ? escapeHtml(share.options) : 'N/A '}
+
+ `).join('')}
+
+
+
+
+ `;
+ }
+ } catch (error) {
+ console.error('Failed to parse network shares:', error);
+ html += '
Erreur lors de la lecture des partages réseau
';
+ }
+ }
+
+ if (!html) {
html = '
Aucune information de stockage disponible
';
}
@@ -407,11 +519,25 @@ function renderOSDetails() {
return;
}
+ const displayServer = snapshot.display_server || snapshot.session_type || 'N/A';
+ const resolution = snapshot.screen_resolution || 'N/A';
+ const lastBoot = snapshot.last_boot_time || 'N/A';
+ const uptime = snapshot.uptime_seconds != null ? formatDuration(snapshot.uptime_seconds) : 'N/A';
+ const battery = snapshot.battery_percentage != null
+ ? `${snapshot.battery_percentage}%${snapshot.battery_status ? ` (${snapshot.battery_status})` : ''}`
+ : 'N/A';
+
const items = [
{ label: 'Nom', value: snapshot.os_name || 'N/A' },
{ label: 'Version', value: snapshot.os_version || 'N/A' },
{ label: 'Kernel', value: snapshot.kernel_version || 'N/A' },
{ label: 'Architecture', value: snapshot.architecture || 'N/A' },
+ { label: 'Environnement', value: snapshot.desktop_environment || 'N/A' },
+ { label: 'Session', value: displayServer },
+ { label: 'Résolution écran', value: resolution },
+ { label: 'Dernier boot', value: lastBoot },
+ { label: 'Uptime', value: uptime },
+ { label: 'Batterie', value: battery },
{ label: 'Virtualisation', value: snapshot.virtualization_type || 'none' }
];
@@ -427,6 +553,26 @@ function renderOSDetails() {
`;
}
+async function handleDeleteDevice() {
+ if (!currentDevice) return;
+
+ const confirmed = confirm(`Voulez-vous vraiment supprimer le device "${currentDevice.hostname}" ? Cette action est définitive.`);
+ if (!confirmed) {
+ return;
+ }
+
+ try {
+ await api.deleteDevice(currentDevice.id);
+ showToast('Device supprimé avec succès', 'success');
+ setTimeout(() => {
+ window.location.href = 'devices.html';
+ }, 800);
+ } catch (error) {
+ console.error('Failed to delete device:', error);
+ showToast(error.message || 'Impossible de supprimer le device', 'error');
+ }
+}
+
// Render Benchmark Results
function renderBenchmarkResults() {
const bench = currentDevice.last_benchmark;
diff --git a/frontend/js/devices.js b/frontend/js/devices.js
old mode 100644
new mode 100755
index 051b9d6..489100a
--- a/frontend/js/devices.js
+++ b/frontend/js/devices.js
@@ -10,6 +10,38 @@ let allDevices = [];
let selectedDeviceId = null;
let isEditing = false;
let currentDevice = null;
+let editingNotes = false;
+let editingUpgradeNotes = false;
+let editingPurchase = false;
+
+const SECTION_ICON_PATHS = {
+ motherboard: 'icons/icons8-motherboard-94.png',
+ cpu: 'icons/icons8-processor-94.png',
+ ram: 'icons/icons8-memory-slot-94.png',
+ storage: 'icons/icons8-ssd-94.png',
+ gpu: 'icons/icons8-gpu-64.png',
+ network: 'icons/icons8-network-cable-94.png',
+ usb: 'icons/icons8-usb-memory-stick-94.png',
+ pci: 'icons/icons8-pcie-48.png',
+ os: 'icons/icons8-operating-system-64.png',
+ shares: 'icons/icons8-shared-folder-94.png',
+ benchmarks: 'icons/icons8-benchmark-64.png',
+ metadata: 'icons/icons8-hardware-64.png',
+ images: 'icons/icons8-picture-48.png',
+ pdf: 'icons/icons8-bios-94.png',
+ links: 'icons/icons8-server-94.png',
+ tags: 'icons/icons8-check-mark-48.png',
+ notes: 'icons/icons8-edit-pencil-48.png',
+ purchase: 'icons/icons8-laptop-50.png',
+ upgrade: 'icons/icons8-workstation-94.png'
+};
+
+function getSectionIcon(key, altText) {
+ const src = SECTION_ICON_PATHS[key];
+ if (!src) return '';
+ const safeAlt = utils.escapeHtml(altText || key);
+ return `
`;
+}
// Load devices
async function loadDevices() {
@@ -67,10 +99,9 @@ function renderDeviceList() {
listContainer.innerHTML = allDevices.map(device => {
const globalScore = device.last_benchmark?.global_score;
const isSelected = device.id === selectedDeviceId;
+ const hostnameEscaped = (device.hostname || '').replace(/'/g, "\\'").replace(/"/g, '\\"');
- const scoreText = globalScore !== null && globalScore !== undefined
- ? Math.round(globalScore)
- : 'N/A';
+ const scoreText = formatScoreValue(globalScore);
const scoreClass = globalScore !== null && globalScore !== undefined
? utils.getScoreBadgeClass(globalScore)
@@ -94,11 +125,14 @@ function renderDeviceList() {
>
- ${utils.escapeHtml(device.hostname)}
+ ${utils.escapeHtml(device.hostname || 'N/A')}
+
+
+
+ ${scoreText}
+
+ 🗑️
-
- ${scoreText}
-
${device.last_benchmark?.run_at ? `
@@ -175,6 +209,253 @@ async function saveDevice() {
}
}
+async function deleteCurrentDevice() {
+ if (!currentDevice) return;
+
+ const confirmed = confirm(`Voulez-vous vraiment supprimer le device "${currentDevice.hostname}" ? Cette action supprimera également ses benchmarks et documents.`);
+ if (!confirmed) {
+ return;
+ }
+
+ try {
+ await apiClient.deleteDevice(currentDevice.id);
+ utils.showToast('Device supprimé', 'success');
+
+ currentDevice = null;
+ selectedDeviceId = null;
+
+ document.getElementById('deviceDetailsContainer').innerHTML = renderEmptyDetailsPlaceholder();
+
+ await loadDevices();
+ } catch (error) {
+ console.error('❌ Failed to delete device:', error);
+ alert('Suppression impossible: ' + (error.message || 'Erreur inconnue'));
+ }
+}
+
+async function deleteDeviceFromList(event, deviceId, hostname) {
+ event.stopPropagation();
+
+ const label = hostname || 'ce device';
+ const confirmed = confirm(`Supprimer définitivement "${label}" ? Toutes les données associées seront perdues.`);
+ if (!confirmed) {
+ return;
+ }
+
+ try {
+ await apiClient.deleteDevice(deviceId);
+ utils.showToast('Device supprimé', 'success');
+
+ if (currentDevice && currentDevice.id === deviceId) {
+ currentDevice = null;
+ selectedDeviceId = null;
+ document.getElementById('deviceDetailsContainer').innerHTML = renderEmptyDetailsPlaceholder();
+ }
+
+ await loadDevices();
+ } catch (error) {
+ console.error('❌ Failed to delete device from list:', error);
+ alert('Suppression impossible: ' + (error.message || 'Erreur inconnue'));
+ }
+}
+
+async function reloadCurrentDevice() {
+ if (!currentDevice) return;
+ const refreshed = await apiClient.getDevice(currentDevice.id);
+ currentDevice = refreshed;
+ renderDeviceDetails(refreshed);
+}
+
+function startNotesEdit() {
+ if (!currentDevice) return;
+ editingNotes = true;
+ renderDeviceDetails(currentDevice);
+}
+
+function cancelNotesEdit() {
+ editingNotes = false;
+ renderDeviceDetails(currentDevice);
+}
+
+async function saveNotes() {
+ if (!currentDevice) return;
+ const textarea = document.getElementById('notes-editor');
+ const value = textarea ? textarea.value.trim() : '';
+ try {
+ await apiClient.updateDevice(currentDevice.id, { description: value || null });
+ editingNotes = false;
+ await reloadCurrentDevice();
+ utils.showToast('Notes sauvegardées', 'success');
+ } catch (error) {
+ console.error('Failed to save notes:', error);
+ utils.showToast(error.message || 'Échec de la sauvegarde des notes', 'error');
+ }
+}
+
+async function clearNotes() {
+ if (!currentDevice) return;
+ if (!confirm('Supprimer les notes ?')) return;
+ try {
+ await apiClient.updateDevice(currentDevice.id, { description: null });
+ editingNotes = false;
+ await reloadCurrentDevice();
+ utils.showToast('Notes supprimées', 'success');
+ } catch (error) {
+ console.error('Failed to clear notes:', error);
+ utils.showToast(error.message || 'Suppression impossible', 'error');
+ }
+}
+
+function startUpgradeNotesEdit() {
+ if (!currentDevice) return;
+ editingUpgradeNotes = true;
+ renderDeviceDetails(currentDevice);
+}
+
+function cancelUpgradeNotesEdit() {
+ editingUpgradeNotes = false;
+ renderDeviceDetails(currentDevice);
+}
+
+async function saveUpgradeNotes() {
+ if (!currentDevice || !currentDevice.last_benchmark) return;
+ const textarea = document.getElementById('upgrade-notes-editor');
+ const value = textarea ? textarea.value.trim() : '';
+ try {
+ await apiClient.updateBenchmark(currentDevice.last_benchmark.id, { notes: value || null });
+ editingUpgradeNotes = false;
+ await reloadCurrentDevice();
+ utils.showToast('Upgrade notes sauvegardées', 'success');
+ } catch (error) {
+ console.error('Failed to save upgrade notes:', error);
+ utils.showToast(error.message || 'Échec de la sauvegarde des upgrade notes', 'error');
+ }
+}
+
+async function clearUpgradeNotes() {
+ if (!currentDevice || !currentDevice.last_benchmark) return;
+ if (!confirm('Supprimer les upgrade notes ?')) return;
+ try {
+ await apiClient.updateBenchmark(currentDevice.last_benchmark.id, { notes: null });
+ editingUpgradeNotes = false;
+ await reloadCurrentDevice();
+ utils.showToast('Upgrade notes supprimées', 'success');
+ } catch (error) {
+ console.error('Failed to clear upgrade notes:', error);
+ utils.showToast(error.message || 'Suppression impossible', 'error');
+ }
+}
+
+function startPurchaseEdit() {
+ if (!currentDevice) return;
+ editingPurchase = true;
+ renderDeviceDetails(currentDevice);
+}
+
+function cancelPurchaseEdit() {
+ editingPurchase = false;
+ renderDeviceDetails(currentDevice);
+}
+
+async function savePurchaseInfo() {
+ if (!currentDevice) return;
+ const storeInput = document.getElementById('purchase-store-input');
+ const dateInput = document.getElementById('purchase-date-input');
+ const priceInput = document.getElementById('purchase-price-input');
+
+ const store = storeInput ? storeInput.value.trim() : '';
+ const date = dateInput ? dateInput.value.trim() : '';
+ const priceValue = priceInput ? priceInput.value.trim() : '';
+ const price = priceValue !== '' && !Number.isNaN(Number(priceValue)) ? Number(priceValue) : null;
+
+ try {
+ await apiClient.updateDevice(currentDevice.id, {
+ purchase_store: store || null,
+ purchase_date: date || null,
+ purchase_price: price
+ });
+ editingPurchase = false;
+ await reloadCurrentDevice();
+ utils.showToast('Informations d’achat mises à jour', 'success');
+ } catch (error) {
+ console.error('Failed to save purchase info:', error);
+ utils.showToast(error.message || 'Échec de la sauvegarde', 'error');
+ }
+}
+
+async function addTag() {
+ if (!currentDevice) return;
+ const input = document.getElementById(`new-tag-input-${currentDevice.id}`);
+ if (!input) return;
+ const value = input.value.trim();
+ if (!value) return;
+
+ const tags = utils.parseTags(currentDevice.tags);
+ if (tags.includes(value)) {
+ utils.showToast('Tag déjà présent', 'warning');
+ return;
+ }
+
+ tags.push(value);
+ try {
+ await apiClient.updateDevice(currentDevice.id, { tags: tags.join(',') });
+ input.value = '';
+ await reloadCurrentDevice();
+ } catch (error) {
+ console.error('Failed to add tag:', error);
+ utils.showToast(error.message || 'Ajout impossible', 'error');
+ }
+}
+
+async function removeTag(tagValue) {
+ if (!currentDevice) return;
+ const tags = utils.parseTags(currentDevice.tags).filter(tag => tag !== tagValue);
+ try {
+ await apiClient.updateDevice(currentDevice.id, { tags: tags.length ? tags.join(',') : null });
+ await reloadCurrentDevice();
+ } catch (error) {
+ console.error('Failed to remove tag:', error);
+ utils.showToast(error.message || 'Suppression du tag impossible', 'error');
+ }
+}
+
+async function addDeviceLinkEntry(deviceId) {
+ const labelInput = document.getElementById(`link-label-${deviceId}`);
+ const urlInput = document.getElementById(`link-url-${deviceId}`);
+ if (!labelInput || !urlInput) return;
+
+ const label = labelInput.value.trim();
+ const url = urlInput.value.trim();
+
+ if (!label || !url) {
+ utils.showToast('Libellé et URL requis', 'warning');
+ return;
+ }
+
+ try {
+ await apiClient.addDeviceLink(deviceId, { label, url });
+ labelInput.value = '';
+ urlInput.value = '';
+ utils.showToast('Lien ajouté', 'success');
+ loadLinksSection(deviceId);
+ } catch (error) {
+ console.error('Failed to add link:', error);
+ utils.showToast(error.message || 'Ajout du lien impossible', 'error');
+ }
+}
+
+async function deleteDeviceLink(linkId, deviceId) {
+ if (!confirm('Supprimer ce lien ?')) return;
+ try {
+ await apiClient.deleteLink(linkId);
+ utils.showToast('Lien supprimé', 'success');
+ loadLinksSection(deviceId);
+ } catch (error) {
+ console.error('Failed to delete link:', error);
+ utils.showToast(error.message || 'Suppression impossible', 'error');
+ }
+}
+
// Upload image for device
async function uploadImage() {
if (!currentDevice) return;
@@ -285,23 +566,37 @@ function renderImageDocuments(documents) {
return '
🖼️ Aucune image ';
}
- const imageDoc = documents.find(doc => doc.doc_type === 'image');
+ const imageDocs = documents.filter(doc => doc.doc_type === 'image');
- if (!imageDoc) {
+ if (imageDocs.length === 0) {
return '
🖼️ Aucune image ';
}
- const downloadUrl = apiClient.getDocumentDownloadUrl(imageDoc.id);
-
return `
-
-
-
-
- 📎 ${utils.escapeHtml(imageDoc.filename)}
-
-
🗑️ Supprimer
-
+
+ ${imageDocs.map(imageDoc => {
+ const downloadUrl = apiClient.getDocumentDownloadUrl(imageDoc.id);
+ const safeFilenameArg = utils.escapeHtml(JSON.stringify(imageDoc.filename || ''));
+ return `
+
+
+
+
+ 📎 ${utils.escapeHtml(imageDoc.filename.substring(0, 15))}${imageDoc.filename.length > 15 ? '...' : ''}
+
+
+
+
+
+
+ `;
+ }).join('')}
`;
}
@@ -333,9 +628,16 @@ function renderPDFDocuments(documents) {
Uploadé le ${uploadDate}
-
@@ -343,285 +645,751 @@ function renderPDFDocuments(documents) {
}).join('');
}
+// Helper: Render Motherboard Details
+function renderMotherboardDetails(snapshot) {
+ if (!snapshot) {
+ return '
Aucune information disponible
';
+ }
+
+ const cleanValue = (val) => {
+ if (!val || (typeof val === 'string' && val.trim() === '')) return 'N/A';
+ return val;
+ };
+
+ const items = [
+ { label: 'Fabricant', value: cleanValue(snapshot.motherboard_vendor) },
+ { label: 'Modèle', value: cleanValue(snapshot.motherboard_model) },
+ { label: 'BIOS Vendor', value: cleanValue(snapshot.bios_vendor) },
+ { label: 'Version BIOS', value: cleanValue(snapshot.bios_version) },
+ { label: 'Date BIOS', value: cleanValue(snapshot.bios_date) },
+ { label: 'Slots RAM', value: `${snapshot.ram_slots_used || '?'} utilisés / ${snapshot.ram_slots_total || '?'} total` }
+ ];
+
+ return `
+
+ ${items.map(item => `
+
+
${item.label}
+
${utils.escapeHtml(String(item.value))}
+
+ `).join('')}
+
+ `;
+}
+
+// Helper: Render CPU Details
+function renderCPUDetails(snapshot) {
+ if (!snapshot) {
+ return '
Aucune information disponible
';
+ }
+
+ const items = [
+ { label: 'Fabricant', value: snapshot.cpu_vendor || 'N/A', tooltip: snapshot.cpu_model },
+ { label: 'Modèle', value: snapshot.cpu_model || 'N/A', tooltip: snapshot.cpu_microarchitecture ? `Architecture: ${snapshot.cpu_microarchitecture}` : null },
+ { label: 'Microarchitecture', value: snapshot.cpu_microarchitecture || 'N/A' },
+ { label: 'Cores', value: snapshot.cpu_cores != null ? snapshot.cpu_cores : 'N/A', tooltip: snapshot.cpu_threads ? `${snapshot.cpu_threads} threads disponibles` : null },
+ { label: 'Threads', value: snapshot.cpu_threads != null ? snapshot.cpu_threads : 'N/A', tooltip: snapshot.cpu_cores ? `${snapshot.cpu_cores} cores physiques` : null },
+ { label: 'Fréquence de base', value: snapshot.cpu_base_freq_ghz ? `${snapshot.cpu_base_freq_ghz} GHz` : 'N/A', tooltip: snapshot.cpu_max_freq_ghz ? `Max: ${snapshot.cpu_max_freq_ghz} GHz` : null },
+ { label: 'Fréquence max', value: snapshot.cpu_max_freq_ghz ? `${snapshot.cpu_max_freq_ghz} GHz` : 'N/A', tooltip: snapshot.cpu_base_freq_ghz ? `Base: ${snapshot.cpu_base_freq_ghz} GHz` : null },
+ { label: 'TDP', value: snapshot.cpu_tdp_w ? `${snapshot.cpu_tdp_w} W` : 'N/A', tooltip: 'Thermal Design Power - Consommation thermique typique' },
+ { label: 'Cache L1', value: snapshot.cpu_cache_l1_kb ? utils.formatCache(snapshot.cpu_cache_l1_kb) : 'N/A', tooltip: 'Cache de niveau 1 - Le plus rapide' },
+ { label: 'Cache L2', value: snapshot.cpu_cache_l2_kb ? utils.formatCache(snapshot.cpu_cache_l2_kb) : 'N/A', tooltip: 'Cache de niveau 2 - Intermédiaire' },
+ { label: 'Cache L3', value: snapshot.cpu_cache_l3_kb ? utils.formatCache(snapshot.cpu_cache_l3_kb) : 'N/A', tooltip: 'Cache de niveau 3 - Partagé entre les cores' }
+ ];
+
+ let html = `
+
+ ${items.map(item => `
+
+
${item.label}
+
${utils.escapeHtml(String(item.value))}
+
+ `).join('')}
+
+ `;
+
+ // CPU Flags
+ if (snapshot.cpu_flags) {
+ try {
+ const flags = typeof snapshot.cpu_flags === 'string' ? JSON.parse(snapshot.cpu_flags) : snapshot.cpu_flags;
+ if (Array.isArray(flags) && flags.length > 0) {
+ html += `
+
+
Instructions supportées:
+
+ ${flags.slice(0, 30).map(flag => `${utils.escapeHtml(flag)} `).join('')}
+ ${flags.length > 30 ? `+${flags.length - 30} autres... ` : ''}
+
+
+ `;
+ }
+ } catch (e) {
+ console.warn('Failed to parse CPU flags:', e);
+ }
+ }
+
+ return html;
+}
+
+// Helper: Render Memory Details
+function renderMemoryDetails(snapshot) {
+ if (!snapshot) {
+ return '
Aucune information disponible
';
+ }
+
+ const items = [
+ { label: 'RAM totale', value: snapshot.ram_total_mb ? utils.formatMemory(snapshot.ram_total_mb) : 'N/A' },
+ { label: 'RAM utilisée', value: snapshot.ram_used_mb ? utils.formatMemory(snapshot.ram_used_mb) : 'N/A' },
+ { label: 'RAM libre', value: snapshot.ram_free_mb ? utils.formatMemory(snapshot.ram_free_mb) : 'N/A' },
+ { label: 'RAM partagée', value: snapshot.ram_shared_mb ? utils.formatMemory(snapshot.ram_shared_mb) : 'N/A' },
+ { label: 'Slots utilisés', value: snapshot.ram_slots_used != null ? snapshot.ram_slots_used : 'N/A' },
+ { label: 'Slots total', value: snapshot.ram_slots_total != null ? snapshot.ram_slots_total : 'N/A' },
+ { label: 'ECC', value: snapshot.ram_ecc != null ? (snapshot.ram_ecc ? 'Oui' : 'Non') : 'N/A' }
+ ];
+
+ let html = `
+
+ ${items.map(item => `
+
+
${item.label}
+
${utils.escapeHtml(String(item.value))}
+
+ `).join('')}
+
+ `;
+
+ // RAM Layout (if available)
+ if (snapshot.ram_layout_json) {
+ try {
+ const layout = typeof snapshot.ram_layout_json === 'string' ? JSON.parse(snapshot.ram_layout_json) : snapshot.ram_layout_json;
+ if (Array.isArray(layout) && layout.length > 0) {
+ html += `
+
+
Configuration des barrettes:
+
+ ${layout.map(slot => `
+
+ ${utils.escapeHtml(slot.slot || 'N/A')} :
+ ${utils.formatMemory(slot.size_mb || 0)}
+ ${slot.type ? `(${utils.escapeHtml(slot.type)})` : ''}
+ ${slot.speed_mhz ? `@ ${slot.speed_mhz} MHz` : ''}
+
+ `).join('')}
+
+
+ `;
+ }
+ } catch (e) {
+ console.warn('Failed to parse RAM layout:', e);
+ }
+ }
+
+ return html;
+}
+
+// Helper: Render Storage Details
+function renderStorageDetails(snapshot) {
+ if (!snapshot) {
+ return '
Aucune information disponible
';
+ }
+
+ let html = '';
+
+ try {
+ if (snapshot.storage_devices_json) {
+ const devices = typeof snapshot.storage_devices_json === 'string'
+ ? JSON.parse(snapshot.storage_devices_json)
+ : snapshot.storage_devices_json;
+
+ if (Array.isArray(devices) && devices.length > 0) {
+ html += `
+
+ ${devices.map(disk => {
+ const typeIcon = disk.type === 'SSD' ? '💾' : '💿';
+ const healthColor = disk.smart_health === 'PASSED' ? 'var(--color-success)' :
+ disk.smart_health === 'FAILED' ? 'var(--color-danger)' :
+ 'var(--text-secondary)';
+
+ return `
+
+
+
+
+ ${typeIcon} ${utils.escapeHtml(disk.name || disk.device || 'N/A')}
+
+
+ ${utils.escapeHtml(disk.model || 'Unknown model')}
+
+
+ ${disk.smart_health ? `
+
+ ${utils.escapeHtml(disk.smart_health)}
+
+ ` : ''}
+
+
+ ${disk.capacity_gb ? `
Capacité: ${utils.formatStorage(disk.capacity_gb)}
` : ''}
+ ${disk.type ? `
Type: ${utils.escapeHtml(disk.type)}
` : ''}
+ ${disk.interface ? `
Interface: ${utils.escapeHtml(disk.interface)}
` : ''}
+ ${disk.temperature_c ? `
Température: ${utils.formatTemperature(disk.temperature_c)}
` : ''}
+
+
+ `;
+ }).join('')}
+
+ `;
+ } else {
+ html += '
Aucun disque détecté
';
+ }
+ }
+ } catch (e) {
+ console.error('Failed to parse storage devices:', e);
+ html += '
Erreur lors du parsing des données de stockage
';
+ }
+
+ if (snapshot.partitions_json) {
+ try {
+ const partitions = typeof snapshot.partitions_json === 'string'
+ ? JSON.parse(snapshot.partitions_json)
+ : snapshot.partitions_json;
+
+ if (Array.isArray(partitions) && partitions.length > 0) {
+ html += `
+
+
Partitions
+
+
+
+
+ Partition
+ Montage
+ Type
+ Utilisé
+ Libre
+ Total
+ Usage
+
+
+
+ ${partitions.map(part => {
+ const used = typeof part.used_gb === 'number' ? utils.formatStorage(part.used_gb) : 'N/A';
+ const free = typeof part.free_gb === 'number'
+ ? utils.formatStorage(part.free_gb)
+ : (typeof part.total_gb === 'number' && typeof part.used_gb === 'number'
+ ? utils.formatStorage(part.total_gb - part.used_gb)
+ : 'N/A');
+ const total = typeof part.total_gb === 'number' ? utils.formatStorage(part.total_gb) : 'N/A';
+
+ return `
+
+ ${utils.escapeHtml(part.name || 'N/A')}
+ ${part.mount_point ? utils.escapeHtml(part.mount_point) : 'Non monté '}
+ ${part.fs_type ? utils.escapeHtml(part.fs_type) : 'N/A'}
+ ${used}
+ ${free}
+ ${total}
+ ${renderUsageBadge(part.used_gb, part.total_gb)}
+
+ `;
+ }).join('')}
+
+
+
+
+ `;
+ }
+ } catch (error) {
+ console.error('Failed to parse partitions:', error);
+ html += '
Erreur lors de la lecture des partitions
';
+ }
+ }
+
+ if (!html) {
+ html = '
Aucune information disponible
';
+ }
+
+ return html;
+}
+
+// Helper: Render GPU Details
+function renderGPUDetails(snapshot) {
+ if (!snapshot) {
+ return '
Aucune information disponible
';
+ }
+
+ if (!snapshot.gpu_vendor && !snapshot.gpu_model && !snapshot.gpu_summary) {
+ return '
Aucun GPU détecté
';
+ }
+
+ const items = [
+ { label: 'Fabricant', value: snapshot.gpu_vendor || 'N/A' },
+ { label: 'Modèle', value: snapshot.gpu_model || snapshot.gpu_summary || 'N/A' }
+ ];
+
+ return `
+
+ ${items.map(item => `
+
+
${item.label}
+
${utils.escapeHtml(String(item.value))}
+
+ `).join('')}
+
+ `;
+}
+
+// Helper: Render OS Details
+function renderOSDetails(snapshot) {
+ if (!snapshot) {
+ return '
Aucune information disponible
';
+ }
+
+ const displayServer = snapshot.display_server || snapshot.session_type || 'N/A';
+ const resolution = snapshot.screen_resolution || 'N/A';
+ const lastBoot = snapshot.last_boot_time || 'N/A';
+ const uptime = snapshot.uptime_seconds != null ? utils.formatDuration(snapshot.uptime_seconds) : 'N/A';
+ const battery = snapshot.battery_percentage != null
+ ? `${snapshot.battery_percentage}%${snapshot.battery_status ? ` (${snapshot.battery_status})` : ''}`
+ : 'N/A';
+
+ const items = [
+ { label: 'Hostname', value: snapshot.hostname || 'N/A' },
+ { label: 'Distribution', value: snapshot.os_name || 'N/A' },
+ { label: 'Version', value: snapshot.os_version || 'N/A' },
+ { label: 'Kernel', value: snapshot.kernel_version || 'N/A' },
+ { label: 'Architecture', value: snapshot.architecture || 'N/A' },
+ { label: 'Environnement', value: snapshot.desktop_environment || 'N/A' },
+ { label: 'Session / Display', value: displayServer },
+ { label: 'Résolution écran', value: resolution },
+ { label: 'Dernier boot', value: lastBoot },
+ { label: 'Uptime', value: uptime },
+ { label: 'Batterie', value: battery },
+ { label: 'Virtualisation', value: snapshot.virtualization_type || 'none' }
+ ];
+
+ return `
+
+ ${items.map(item => `
+
+
${item.label}
+
${utils.escapeHtml(String(item.value))}
+
+ `).join('')}
+
+ `;
+}
+
+// Helper: Render PCI Devices
+function renderPCIDetails(snapshot) {
+ if (!snapshot || !snapshot.pci_devices_json) {
+ return '
Aucune information disponible
';
+ }
+
+ try {
+ const devices = typeof snapshot.pci_devices_json === 'string'
+ ? JSON.parse(snapshot.pci_devices_json)
+ : snapshot.pci_devices_json;
+
+ if (!Array.isArray(devices) || devices.length === 0) {
+ return '
Aucun périphérique PCI détecté
';
+ }
+
+ return `
+
+ ${devices.map(dev => `
+
+
+
Slot:
+
${utils.escapeHtml(dev.slot || 'N/A')}
+
+
Class:
+
${utils.escapeHtml(dev.class || 'N/A')}
+
+
Vendor:
+
${utils.escapeHtml(dev.vendor || 'N/A')}
+
+
Device:
+
${utils.escapeHtml(dev.device || 'N/A')}
+
+
+ `).join('')}
+
+ `;
+ } catch (e) {
+ console.error('Failed to parse PCI devices:', e);
+ return '
Erreur lors du parsing des données PCI
';
+ }
+}
+
+// Helper: Render USB Devices
+function renderUSBDetails(snapshot) {
+ if (!snapshot || !snapshot.usb_devices_json) {
+ return '
Aucune information disponible
';
+ }
+
+ try {
+ const devices = typeof snapshot.usb_devices_json === 'string'
+ ? JSON.parse(snapshot.usb_devices_json)
+ : snapshot.usb_devices_json;
+
+ if (!Array.isArray(devices) || devices.length === 0) {
+ return '
Aucun périphérique USB détecté
';
+ }
+
+ return `
+
+ ${devices.map(dev => `
+
+
+ ${utils.escapeHtml(dev.name || 'Unknown USB Device')}
+
+
+
Bus/Device:
+
${utils.escapeHtml(dev.bus || 'N/A')} / ${utils.escapeHtml(dev.device || 'N/A')}
+
+
Vendor ID:
+
${utils.escapeHtml(dev.vendor_id || 'N/A')}
+
+
Product ID:
+
${utils.escapeHtml(dev.product_id || 'N/A')}
+
+
+ `).join('')}
+
+ `;
+ } catch (e) {
+ console.error('Failed to parse USB devices:', e);
+ return '
Erreur lors du parsing des données USB
';
+ }
+}
+
+// Helper: View benchmark details
+async function viewBenchmarkDetails(benchmarkId) {
+ const modalBody = document.getElementById('benchmarkModalBody');
+ utils.openModal('benchmarkModal');
+
+ try {
+ const benchmark = await apiClient.getBenchmark(benchmarkId);
+
+ modalBody.innerHTML = `
+
+
${JSON.stringify(benchmark.details || benchmark, null, 2)}
+
+ `;
+
+ } catch (error) {
+ console.error('Failed to load benchmark details:', error);
+ modalBody.innerHTML = `
Erreur: ${utils.escapeHtml(error.message)}
`;
+ }
+}
+
// Render device details (right panel)
function renderDeviceDetails(device) {
+ const previousDeviceId = currentDevice?.id;
currentDevice = device;
+ if (previousDeviceId !== device.id) {
+ editingNotes = false;
+ editingUpgradeNotes = false;
+ editingPurchase = false;
+ }
const detailsContainer = document.getElementById('deviceDetailsContainer');
const snapshot = device.last_hardware_snapshot;
const bench = device.last_benchmark;
- // Hardware summary
- const cpuModel = snapshot?.cpu_model || 'N/A';
- const cpuCores = snapshot?.cpu_cores || '?';
- const cpuThreads = snapshot?.cpu_threads || '?';
- const ramTotalGB = Math.round((snapshot?.ram_total_mb || 0) / 1024);
- const ramUsedMB = snapshot?.ram_used_mb || 0;
- const ramFreeMB = snapshot?.ram_free_mb || 0;
- const ramSharedMB = snapshot?.ram_shared_mb || 0;
- const gpuSummary = snapshot?.gpu_summary || 'N/A';
- const storage = snapshot?.storage_summary || 'N/A';
- const osName = snapshot?.os_name || 'N/A';
- const kernelVersion = snapshot?.kernel_version || 'N/A';
+ const metaParts = [];
+ if (device.location) metaParts.push(`📍 ${utils.escapeHtml(device.location)}`);
+ if (device.owner) metaParts.push(`👤 ${utils.escapeHtml(device.owner)}`);
+ if (device.asset_tag) metaParts.push(`🏷️ ${utils.escapeHtml(device.asset_tag)}`);
- // RAM usage calculation
- let ramUsageHtml = `${ramTotalGB} GB`;
- if (ramUsedMB > 0 || ramFreeMB > 0) {
- const usagePercent = ramTotalGB > 0 ? Math.round((ramUsedMB / (snapshot.ram_total_mb || 1)) * 100) : 0;
- ramUsageHtml = `
- ${ramTotalGB} GB (${usagePercent}% utilisé)
-
- Utilisée: ${Math.round(ramUsedMB / 1024)}GB •
- Libre: ${Math.round(ramFreeMB / 1024)}GB${ramSharedMB > 0 ? ` • Partagée: ${Math.round(ramSharedMB / 1024)}GB` : ''}
-
- `;
- }
+ const brand = snapshot?.motherboard_vendor || snapshot?.os_name || 'N/A';
+ const model = snapshot?.motherboard_model || snapshot?.os_version || 'N/A';
+ const imageCount = (device.documents || []).filter(doc => doc.doc_type === 'image').length;
- // Benchmark scores
- const globalScore = bench?.global_score;
- const cpuScore = bench?.cpu_score;
- const memScore = bench?.memory_score;
- const diskScore = bench?.disk_score;
- const netScore = bench?.network_score;
- const gpuScore = bench?.gpu_score;
+ const imagesContent = renderImageDocuments(device.documents);
- const globalScoreHtml = globalScore !== null && globalScore !== undefined
- ? `
${utils.getScoreBadgeText(globalScore)} `
- : '
N/A ';
-
- // Network details
- let networkHtml = '';
- if (snapshot?.network_interfaces_json) {
- try {
- const interfaces = JSON.parse(snapshot.network_interfaces_json);
- networkHtml = interfaces.map(iface => {
- const typeIcon = iface.type === 'ethernet' ? '🔌' : '📡';
- const wolBadge = iface.wake_on_lan === true
- ? '
WoL ✓ '
- : '
WoL ✗ ';
-
- return `
-
-
- ${typeIcon} ${utils.escapeHtml(iface.name)} (${iface.type})${wolBadge}
-
-
- IP: ${iface.ip || 'N/A'} • MAC: ${iface.mac || 'N/A'}
- Vitesse: ${iface.speed_mbps ? iface.speed_mbps + ' Mbps' : 'N/A'}
- ${iface.driver ? ` • Driver: ${iface.driver}` : ''}
+ const headerHtml = `
+
+
+
+
- `;
- }).join('');
- } catch (e) {
- networkHtml = '
Erreur de parsing JSON
';
- }
- }
-
- // Network benchmark results (iperf3)
- let netBenchHtml = '';
- if (bench?.network_results_json) {
- try {
- const netResults = JSON.parse(bench.network_results_json);
- netBenchHtml = `
-
-
📈 Résultats Benchmark Réseau (iperf3)
-
+
+
- `;
- } catch (e) {
- console.error('Error parsing network results:', e);
- }
- }
-
- detailsContainer.innerHTML = `
-
-
-
-
${utils.escapeHtml(device.hostname)}
- ${isEditing ? `
-
- ` : `
-
- ${utils.escapeHtml(device.description || 'Aucune description')}
-
- `}
-
-
- ${globalScoreHtml}
- ${!isEditing ? `
- ✏️ Edit
- ` : `
- ✖️ Annuler
- 💾 Save
- `}
-
-
-
-
-
-
-
-
-
Caractéristiques
-
- ${createFormRow('CPU', utils.escapeHtml(cpuModel))}
- ${createFormRow('Cores / Threads', `${cpuCores} / ${cpuThreads}`)}
- ${createFormRow('RAM Total', `${ramTotalGB} GB`)}
- ${createFormRow('RAM Utilisée', ramUsedMB > 0 ? `${Math.round(ramUsedMB / 1024)} GB (${Math.round((ramUsedMB / (snapshot.ram_total_mb || 1)) * 100)}%)` : 'N/A')}
- ${createFormRow('RAM Libre', ramFreeMB > 0 ? `${Math.round(ramFreeMB / 1024)} GB` : 'N/A')}
- ${ramSharedMB > 0 ? createFormRow('RAM Partagée', `${Math.round(ramSharedMB / 1024)} GB`) : ''}
- ${createFormRow('GPU', utils.escapeHtml(gpuSummary))}
- ${createFormRow('Storage', utils.escapeHtml(storage))}
- ${createFormRow('OS', utils.escapeHtml(osName))}
- ${createFormRow('Kernel', utils.escapeHtml(kernelVersion))}
- ${isEditing ? `
-
-
-
-
- ` : `
- ${device.location ? createFormRow('Location', utils.escapeHtml(device.location)) : ''}
- ${device.owner ? createFormRow('Propriétaire', utils.escapeHtml(device.owner)) : ''}
- ${device.asset_tag ? createFormRow('Asset Tag', utils.escapeHtml(device.asset_tag)) : ''}
- ${device.tags ? createFormRow('Tags', utils.escapeHtml(device.tags)) : ''}
- `}
- ${createFormRow('Créé le', new Date(device.created_at).toLocaleDateString('fr-FR', { year: 'numeric', month: 'long', day: 'numeric' }))}
-
-
-
-
-
-
-
-
-
Image
- 📤 Upload
-
-
- ${renderImageDocuments(device.documents)}
-
-
-
-
-
-
-
Notice PDF
- 📤 Upload
-
-
- ${renderPDFDocuments(device.documents)}
-
+
+ ${createSection(
+ 'section-images-header',
+ getSectionIcon('images', 'Images'),
+ 'Images',
+ imagesContent,
+ {
+ actionsHtml: `
+
+ `
+ }
+ )}
-
-
- ${bench ? `
-
-
📊 Benchmark Scores
-
- ${createFormRow('CPU Score', cpuScore !== null && cpuScore !== undefined ? Math.round(cpuScore) : 'N/A', true)}
- ${createFormRow('RAM Score', memScore !== null && memScore !== undefined ? Math.round(memScore) : 'N/A', true)}
- ${createFormRow('Disk Score', diskScore !== null && diskScore !== undefined ? Math.round(diskScore) : 'N/A', true)}
- ${createFormRow('Network Score', netScore !== null && netScore !== undefined ? Math.round(netScore) : 'N/A', true)}
- ${createFormRow('GPU Score', gpuScore !== null && gpuScore !== undefined ? Math.round(gpuScore) : 'N/A', true)}
-
-
- ⏱️ Dernier benchmark: ${bench.run_at ? utils.formatRelativeTime(bench.run_at) : 'N/A'}
-
-
- ` : ''}
-
-
- ${networkHtml || netBenchHtml ? `
-
-
🌐 Détails Réseau
- ${networkHtml}
- ${netBenchHtml}
-
- ` : ''}
-
-
-
`;
- // Attach event listeners for edit/save/upload buttons
- setTimeout(() => {
- const btnEdit = document.getElementById('btn-edit');
- const btnSave = document.getElementById('btn-save');
- const btnCancel = document.getElementById('btn-cancel');
- const btnUploadImage = document.getElementById('btn-upload-image');
- const btnUploadPDF = document.getElementById('btn-upload-pdf');
+ const metadataActions = isEditing
+ ? `
+
+
+
+
+
+
+ `
+ : `
+
+
+
+ `;
+ const notesActions = editingNotes
+ ? `
+
+
+
+
+
+
+ `
+ : `
+
+
+
+ ${device.description ? `
+
+
+
+ ` : ''}
+ `;
+ const upgradeActions = currentDevice?.last_benchmark
+ ? (editingUpgradeNotes
+ ? `
+
+
+
+
+
+
+ `
+ : `
+
+
+
+ ${currentDevice.last_benchmark.notes ? `
+
+
+
+ ` : ''}
+ `)
+ : '';
+ const purchaseActions = editingPurchase
+ ? `
+
+
+
+
+
+
+ `
+ : `
+
+
+
+ `;
- if (btnEdit) {
- btnEdit.addEventListener('click', toggleEditMode);
+ const sections = {
+ motherboard: createSection('section-motherboard', getSectionIcon('motherboard', 'Carte Mère'), 'Carte Mère', renderMotherboardDetails(snapshot)),
+ cpu: createSection('section-cpu', getSectionIcon('cpu', 'Processeur'), 'Processeur (CPU)', renderCPUDetails(snapshot)),
+ ram: createSection('section-ram', getSectionIcon('ram', 'Mémoire'), 'Mémoire (RAM)', renderMemoryDetails(snapshot)),
+ disks: createSection('section-disks', getSectionIcon('storage', 'Stockage'), 'Stockage (Disques)', renderStorageDetails(snapshot)),
+ gpu: createSection('section-gpu', getSectionIcon('gpu', 'Carte Graphique'), 'Carte Graphique (GPU)', renderGPUDetails(snapshot)),
+ network: createSection('section-network', getSectionIcon('network', 'Interfaces réseau'), 'Interfaces Réseau', renderNetworkBlock(snapshot, bench)),
+ usb: createSection('section-usb', getSectionIcon('usb', 'USB'), 'Périphériques USB', renderUSBDetails(snapshot)),
+ pci: createSection('section-pci', getSectionIcon('pci', 'PCI'), 'Périphériques PCI', renderPCIDetails(snapshot)),
+ os: createSection('section-os', getSectionIcon('os', 'Système'), 'Système d’exploitation', renderOSDetails(snapshot)),
+ shares: createSection('section-shares', getSectionIcon('shares', 'Partages réseau'), 'Partages réseau', renderNetworkSharesDetails(snapshot)),
+ benchmarks: createSection('section-benchmarks', getSectionIcon('benchmarks', 'Benchmarks'), 'Benchmarks', renderBenchmarkSection(device.id, bench)),
+ metadata: createSection('section-metadata', getSectionIcon('metadata', 'Métadonnées'), 'Métadonnées', renderMetadataSection(device, bench), { actionsHtml: metadataActions })
+ };
+
+ const pdfContent = renderPDFDocuments(device.documents);
+ sections.pdf = createSection(
+ 'section-pdf',
+ getSectionIcon('pdf', 'Notices PDF'),
+ 'Notices PDF',
+ pdfContent,
+ {
+ actionsHtml: `
+
+
+
+ `
+ }
+ );
+
+ sections.links = createSection('section-links', getSectionIcon('links', 'Liens'), 'URLs & Liens', renderLinksPlaceholder(device.id));
+ sections.tags = createSection('section-tags', getSectionIcon('tags', 'Tags'), 'Tags', renderTagsSection(device));
+ sections.notes = createSection('section-notes', getSectionIcon('notes', 'Notes'), 'Notes (Markdown)', renderNotesSection(device), { actionsHtml: notesActions });
+ sections.purchase = createSection('section-purchase', getSectionIcon('purchase', 'Informations d’achat'), 'Informations d’achat', renderPurchaseSection(device), { actionsHtml: purchaseActions });
+ sections.upgrades = createSection('section-upgrades', getSectionIcon('upgrade', 'Upgrade Notes'), 'Upgrade Notes', renderUpgradeSection(bench), { actionsHtml: upgradeActions });
+
+ const sectionsOrder = [
+ 'motherboard',
+ 'cpu',
+ 'ram',
+ 'disks',
+ 'gpu',
+ 'network',
+ 'os',
+ 'shares',
+ 'usb',
+ 'pci',
+ 'benchmarks',
+ 'metadata',
+ 'pdf',
+ 'links',
+ 'tags',
+ 'notes',
+ 'purchase',
+ 'upgrades'
+ ];
+
+ const orderedSections = sectionsOrder.map(key => sections[key] || '').join('');
+
+ detailsContainer.innerHTML = headerHtml + orderedSections;
+
+ bindDetailActions();
+ loadLinksSection(device.id);
+ loadBenchmarkHistorySection(device.id);
+}
+
+function bindDetailActions() {
+ const btnEdit = document.getElementById('btn-edit');
+ const btnSave = document.getElementById('btn-save');
+ const btnCancel = document.getElementById('btn-cancel');
+ const btnUploadImage = document.getElementById('btn-upload-image');
+ const btnUploadImageHeader = document.getElementById('btn-upload-image-header');
+ const btnUploadPDF = document.getElementById('btn-upload-pdf');
+ const btnDelete = document.getElementById('btn-delete');
+
+ if (btnEdit) btnEdit.addEventListener('click', toggleEditMode);
+ if (btnSave) btnSave.addEventListener('click', saveDevice);
+ if (btnCancel) {
+ btnCancel.addEventListener('click', () => {
+ isEditing = false;
+ renderDeviceDetails(currentDevice);
+ });
+ }
+ if (btnUploadImage) btnUploadImage.addEventListener('click', uploadImage);
+ if (btnUploadImageHeader) btnUploadImageHeader.addEventListener('click', uploadImage);
+ if (btnUploadPDF) btnUploadPDF.addEventListener('click', uploadPDF);
+ if (btnDelete) btnDelete.addEventListener('click', deleteCurrentDevice);
+}
+
+async function loadLinksSection(deviceId) {
+ const listContainer = document.getElementById(`links-list-${deviceId}`);
+ if (!listContainer) return;
+
+ listContainer.innerHTML = '
Chargement des liens...
';
+
+ try {
+ const links = await apiClient.getDeviceLinks(deviceId);
+ listContainer.innerHTML = renderLinksList(links, deviceId);
+ } catch (error) {
+ console.error('Failed to load links:', error);
+ listContainer.innerHTML = `
Erreur: ${utils.escapeHtml(error.message || 'Impossible de charger les liens')}
`;
+ }
+}
+
+async function loadBenchmarkHistorySection(deviceId) {
+ const container = document.getElementById(`benchmark-history-${deviceId}`);
+ if (!container) return;
+
+ try {
+ const data = await apiClient.getDeviceBenchmarks(deviceId, { limit: 20 });
+ if (!data.items || data.items.length === 0) {
+ container.innerHTML = '
Aucun benchmark dans l\'historique.
';
+ return;
}
- if (btnSave) {
- btnSave.addEventListener('click', saveDevice);
- }
-
- if (btnCancel) {
- btnCancel.addEventListener('click', () => {
- isEditing = false;
- renderDeviceDetails(currentDevice);
- });
- }
-
- if (btnUploadImage) {
- btnUploadImage.addEventListener('click', uploadImage);
- }
-
- if (btnUploadPDF) {
- btnUploadPDF.addEventListener('click', uploadPDF);
- }
- }, 0);
+ container.innerHTML = `
+
+
+
+
+ Date
+ Global
+ CPU
+ CPU Mono
+ CPU Multi
+ Mémoire
+ Disque
+ Réseau
+ GPU
+ Version
+
+
+
+ ${data.items.map(bench => `
+
+ ${utils.escapeHtml(utils.formatDate(bench.run_at))}
+ ${formatScoreValue(bench.global_score)}
+ ${formatScoreValue(bench.cpu_score)}
+ ${formatScoreValue(bench.cpu_score_single)}
+ ${formatScoreValue(bench.cpu_score_multi)}
+ ${formatScoreValue(bench.memory_score)}
+ ${formatScoreValue(bench.disk_score)}
+ ${formatScoreValue(bench.network_score)}
+ ${formatScoreValue(bench.gpu_score)}
+ ${utils.escapeHtml(bench.bench_script_version || 'N/A')}
+
+ `).join('')}
+
+
+
+ `;
+ } catch (error) {
+ console.error('Failed to load benchmark history:', error);
+ container.innerHTML = `
Erreur: ${utils.escapeHtml(error.message || 'Impossible de charger l\'historique')}
`;
+ }
}
// Create score card for display
function createScoreCard(score, label, icon) {
- const scoreValue = score !== null && score !== undefined ? Math.round(score) : 'N/A';
+ const scoreValue = formatScoreValue(score);
const badgeClass = score !== null && score !== undefined
? utils.getScoreBadgeClass(score)
: 'badge';
@@ -666,6 +1434,441 @@ function createFormRow(label, value, inline = false) {
`;
}
+function createSection(id, icon, title, content, options = {}) {
+ const iconHtml = icon ? `
${icon} ` : '';
+ const actionsHtml = options.actionsHtml
+ ? `
${options.actionsHtml}
`
+ : '';
+
+ return `
+
+ `;
+}
+
+function formatRawValue(value) {
+ if (value === null || value === undefined || value === '') return 'N/A';
+ if (typeof value === 'number') return value;
+ return utils.escapeHtml(String(value));
+}
+
+function formatScoreValue(value) {
+ if (value === null || value === undefined || value === '') return 'N/A';
+ const numeric = Number(value);
+ if (!Number.isFinite(numeric)) {
+ return utils.escapeHtml(String(value));
+ }
+ return Math.ceil(numeric);
+}
+
+function renderUsageBadge(used, total) {
+ if (typeof used !== 'number' || typeof total !== 'number' || total <= 0) {
+ return '
N/A ';
+ }
+ const percent = Math.min(100, Math.max(0, Math.round((used / total) * 100)));
+ const modifier = percent >= 85 ? 'high' : percent >= 60 ? 'medium' : 'ok';
+ return `
${percent}% `;
+}
+
+function formatPriceValue(value) {
+ if (value === null || value === undefined || value === '') return 'N/A';
+ const numeric = Number(value);
+ if (Number.isNaN(numeric)) return utils.escapeHtml(String(value));
+ return `${numeric.toFixed(2)} €`;
+}
+
+function renderMetadataInput(label, id, value, placeholder = '') {
+ return `
+
+ `;
+}
+
+function renderMetadataSection(device, bench) {
+ const rows = [];
+ if (isEditing) {
+ return `
+ ${renderMetadataInput('Location', 'edit-location', device.location, 'Ex: Datacenter Paris')}
+ ${renderMetadataInput('Propriétaire', 'edit-owner', device.owner, 'Responsable')}
+ ${renderMetadataInput('Asset Tag', 'edit-asset-tag', device.asset_tag, 'INV-00123')}
+ ${createFormRow('Créé le', utils.escapeHtml(new Date(device.created_at).toLocaleString('fr-FR')))}
+ ${createFormRow('Mis à jour', utils.escapeHtml(new Date(device.updated_at).toLocaleString('fr-FR')))}
+ ${createFormRow('Version bench', utils.escapeHtml(bench?.bench_script_version || 'N/A'))}
+ `;
+ }
+
+ if (device.location) rows.push(createFormRow('Location', utils.escapeHtml(device.location)));
+ if (device.owner) rows.push(createFormRow('Propriétaire', utils.escapeHtml(device.owner)));
+ if (device.asset_tag) rows.push(createFormRow('Asset Tag', utils.escapeHtml(device.asset_tag)));
+ rows.push(createFormRow('Créé le', utils.escapeHtml(new Date(device.created_at).toLocaleString('fr-FR'))));
+ rows.push(createFormRow('Mis à jour', utils.escapeHtml(new Date(device.updated_at).toLocaleString('fr-FR'))));
+ rows.push(createFormRow('Version bench', utils.escapeHtml(bench?.bench_script_version || 'N/A')));
+
+ return rows.join('');
+}
+
+function renderNetworkBlock(snapshot, bench) {
+ if (!snapshot || !snapshot.network_interfaces_json) {
+ return '
Aucune information réseau disponible
';
+ }
+
+ let html = '';
+
+ try {
+ const interfaces = typeof snapshot.network_interfaces_json === 'string'
+ ? JSON.parse(snapshot.network_interfaces_json)
+ : snapshot.network_interfaces_json;
+
+ if (!interfaces || interfaces.length === 0) {
+ return '
Aucune interface réseau détectée
';
+ }
+
+ const hostLabel = snapshot.hostname || currentDevice?.hostname || null;
+
+ html += '
';
+ interfaces.forEach(iface => {
+ const typeIcon = iface.type === 'ethernet' ? '🔌' : (iface.type === 'wifi' ? '📡' : '🌐');
+ const wol = iface.wake_on_lan;
+ const wolBadge = wol === true
+ ? '
WoL ✓ '
+ : (wol === false ? '
WoL ✗ ' : '');
+ const networkName = iface.network_name || iface.connection_name || iface.profile || iface.ssid || null;
+
+ html += `
+
+
+
+
${typeIcon} ${utils.escapeHtml(iface.name || 'N/A')}
+
${utils.escapeHtml(iface.type || 'unknown')}
+
+
${wolBadge}
+
+
+ ${hostLabel ? `
Hostname: ${utils.escapeHtml(hostLabel)}
` : ''}
+ ${networkName ? `
Nom du réseau: ${utils.escapeHtml(networkName)}
` : ''}
+ ${iface.ip ? `
Adresse IP: ${utils.escapeHtml(iface.ip)}
` : ''}
+ ${iface.mac ? `
MAC: ${utils.escapeHtml(iface.mac)}
` : ''}
+ ${iface.speed_mbps ? `
Vitesse: ${iface.speed_mbps} Mbps
` : ''}
+ ${iface.driver ? `
Driver: ${utils.escapeHtml(iface.driver)}
` : ''}
+ ${iface.ssid ? `
SSID: ${utils.escapeHtml(iface.ssid)}
` : ''}
+
+
+ `;
+ });
+ html += '
';
+ } catch (error) {
+ console.error('Failed to parse network interfaces:', error);
+ html = '
Erreur lors du parsing des données réseau
';
+ }
+
+ if (bench?.network_results_json) {
+ try {
+ const netResults = typeof bench.network_results_json === 'string'
+ ? JSON.parse(bench.network_results_json)
+ : bench.network_results_json;
+
+ html += `
+
+
📈 Résultats iperf3
+
+
+
${formatRawValue(netResults.upload_mbps)}
+
Upload Mbps
+
+
+
${formatRawValue(netResults.download_mbps)}
+
Download Mbps
+
+
+
${formatRawValue(netResults.ping_ms)}
+
Ping ms
+
+
+
${formatScoreValue(netResults.score)}
+
Score
+
+
+
+ `;
+ } catch (error) {
+ console.error('Failed to parse network benchmark results:', error);
+ }
+ }
+
+ return html;
+}
+
+function renderNetworkSharesDetails(snapshot) {
+ if (!snapshot || !snapshot.network_shares_json) {
+ return '
Aucun partage réseau détecté
';
+ }
+
+ try {
+ const shares = typeof snapshot.network_shares_json === 'string'
+ ? JSON.parse(snapshot.network_shares_json)
+ : snapshot.network_shares_json;
+
+ if (!Array.isArray(shares) || shares.length === 0) {
+ return '
Aucun partage réseau monté
';
+ }
+
+ return `
+
+
+
+
+ Source
+ Montage
+ Protocole
+ Type
+ Utilisé
+ Libre
+ Total
+ Options
+
+
+
+ ${shares.map(share => `
+
+ ${utils.escapeHtml(share.source || 'N/A')}
+ ${utils.escapeHtml(share.mount_point || 'N/A')}
+ ${utils.escapeHtml(share.protocol || share.fs_type || 'N/A')}
+ ${share.fs_type ? utils.escapeHtml(share.fs_type) : 'N/A'}
+ ${typeof share.used_gb === 'number' ? utils.formatStorage(share.used_gb, 'GB') : 'N/A'}
+ ${typeof share.free_gb === 'number' ? utils.formatStorage(share.free_gb, 'GB') : 'N/A'}
+ ${typeof share.total_gb === 'number' ? utils.formatStorage(share.total_gb, 'GB') : 'N/A'}
+ ${share.options ? utils.escapeHtml(share.options) : 'N/A'}
+
+ `).join('')}
+
+
+
+ `;
+ } catch (error) {
+ console.error('Failed to parse network shares:', error);
+ return '
Erreur lors de la lecture des partages réseau
';
+ }
+}
+
+function renderBenchmarkSection(deviceId, bench) {
+ if (!bench) {
+ return '
Aucun benchmark disponible pour ce device.
';
+ }
+
+ const rows = [
+ { label: 'Score global', value: formatScoreValue(bench.global_score) },
+ { label: 'CPU score', value: formatScoreValue(bench.cpu_score) },
+ { label: 'CPU single', value: formatScoreValue(bench.cpu_score_single) },
+ { label: 'CPU multi', value: formatScoreValue(bench.cpu_score_multi) },
+ { label: 'Mémoire', value: formatScoreValue(bench.memory_score) },
+ { label: 'Disque', value: formatScoreValue(bench.disk_score) },
+ { label: 'Réseau', value: formatScoreValue(bench.network_score) },
+ { label: 'GPU', value: formatScoreValue(bench.gpu_score) },
+ { label: 'Exécuté le', value: utils.escapeHtml(utils.formatDate(bench.run_at)) }
+ ];
+
+ return `
+
+ ${rows.map(row => `
+
+
${row.label}
+
${row.value}
+
+ `).join('')}
+
+
+ 📋 Voir les détails JSON
+
+
+
Chargement de l'historique...
+
+ `;
+}
+
+function renderTagsSection(device) {
+ const tags = utils.parseTags(device.tags);
+ const chips = tags.length
+ ? `
+
+ ${tags.map(tag => `
+
+ ${utils.escapeHtml(tag)}
+ ×
+
+ `).join('')}
+
+ `
+ : '
Aucun tag configuré
';
+
+ return `
+ ${chips}
+
+ `;
+}
+
+function renderNotesSection(device) {
+ if (editingNotes) {
+ return `
+
+ `;
+ }
+
+ return utils.renderMarkdown(device.description || 'Aucune note enregistrée.');
+}
+
+function renderPurchaseSection(device) {
+ const infoRows = [
+ { label: 'Boutique', value: device.purchase_store || 'Non renseigné' },
+ { label: 'Date d\'achat', value: device.purchase_date || 'Non renseigné' },
+ { label: 'Prix', value: formatPriceValue(device.purchase_price) }
+ ];
+
+ let infoHtml = '';
+ if (editingPurchase) {
+ infoHtml = `
+
+
+
+
+
+ `;
+ } else {
+ infoHtml = `
+
+ ${infoRows.map(row => `
+
+
${row.label}
+
${utils.escapeHtml(String(row.value))}
+
+ `).join('')}
+
+ `;
+ }
+
+ const docs = device.documents || [];
+ const invoices = docs.filter(doc => ['invoice', 'warranty'].includes(doc.doc_type));
+ const docsHtml = invoices.length
+ ? `
+
+ `
+ : '
Aucune facture ou garantie uploadée.
';
+
+ return infoHtml + docsHtml;
+}
+
+function renderUpgradeSection(bench) {
+ if (!bench) {
+ return '
Aucun benchmark disponible.
';
+ }
+
+ if (editingUpgradeNotes) {
+ return `
+
+ `;
+ }
+
+ if (bench.notes) {
+ return utils.renderMarkdown(bench.notes);
+ }
+
+ return '
Aucune note d\'upgrade enregistrée sur le dernier benchmark.
';
+}
+
+function renderLinksPlaceholder(deviceId) {
+ return `
+
+
Chargement des liens...
+ `;
+}
+
+function renderLinksList(links, deviceId) {
+ if (!links || links.length === 0) {
+ return '
Aucun lien enregistré.
';
+ }
+
+ return `
+
+ ${links.map(link => `
+
+
+
${utils.escapeHtml(link.label)}
+
+
+
+
+
+
+ `).join('')}
+
+ `;
+}
+
+function renderEmptyDetailsPlaceholder() {
+ return `
+
+
📊
+
Sélectionnez un device dans la liste pour afficher ses détails
+
+ `;
+}
+
// Initialize devices page
document.addEventListener('DOMContentLoaded', () => {
loadDevices();
@@ -674,8 +1877,77 @@ document.addEventListener('DOMContentLoaded', () => {
setInterval(loadDevices, 30000);
});
+// Preview image in modal
+function previewImage(docId, filename) {
+ const downloadUrl = apiClient.getDocumentDownloadUrl(docId);
+
+ const modalHtml = `
+
+
×
+
+
${utils.escapeHtml(filename)}
+
+ `;
+
+ document.body.insertAdjacentHTML('beforeend', modalHtml);
+}
+
+function closeImagePreview() {
+ const modal = document.getElementById('imagePreviewModal');
+ if (modal) {
+ modal.remove();
+ }
+}
+
+// Preview PDF in modal
+function previewPDF(docId, filename) {
+ const downloadUrl = apiClient.getDocumentDownloadUrl(docId);
+
+ const modalHtml = `
+
+
+
+
📄 ${utils.escapeHtml(filename)}
+ ✖ Fermer
+
+
+
+
+ `;
+
+ document.body.insertAdjacentHTML('beforeend', modalHtml);
+}
+
+function closePDFPreview() {
+ const modal = document.getElementById('pdfPreviewModal');
+ if (modal) {
+ modal.remove();
+ }
+}
+
// Make functions available globally for onclick handlers
window.selectDevice = selectDevice;
window.deleteDocument = deleteDocument;
+window.viewBenchmarkDetails = viewBenchmarkDetails;
+window.previewImage = previewImage;
+window.closeImagePreview = closeImagePreview;
+window.previewPDF = previewPDF;
+window.closePDFPreview = closePDFPreview;
+window.deleteDeviceFromList = deleteDeviceFromList;
+window.startNotesEdit = startNotesEdit;
+window.cancelNotesEdit = cancelNotesEdit;
+window.saveNotes = saveNotes;
+window.clearNotes = clearNotes;
+window.startUpgradeNotesEdit = startUpgradeNotesEdit;
+window.cancelUpgradeNotesEdit = cancelUpgradeNotesEdit;
+window.saveUpgradeNotes = saveUpgradeNotes;
+window.clearUpgradeNotes = clearUpgradeNotes;
+window.startPurchaseEdit = startPurchaseEdit;
+window.cancelPurchaseEdit = cancelPurchaseEdit;
+window.savePurchaseInfo = savePurchaseInfo;
+window.addTag = addTag;
+window.removeTag = removeTag;
+window.addDeviceLinkEntry = addDeviceLinkEntry;
+window.deleteDeviceLink = deleteDeviceLink;
})(); // End of IIFE
diff --git a/frontend/js/settings.js b/frontend/js/settings.js
old mode 100644
new mode 100755
index 2351bd0..72876e7
--- a/frontend/js/settings.js
+++ b/frontend/js/settings.js
@@ -3,14 +3,103 @@
const { copyToClipboard, showToast, escapeHtml } = window.BenchUtils;
let tokenVisible = false;
-const API_TOKEN = 'YOUR_API_TOKEN_HERE'; // Will be replaced by actual token or fetched from backend
+let API_TOKEN = 'LOADING...';
+
+// Load backend config (API token, etc.)
+async function loadBackendConfig() {
+ try {
+ const backendApiUrl = window.BenchConfig?.backendApiUrl || `${window.location.protocol}//${window.location.hostname}:8007/api`;
+ const response = await fetch(`${backendApiUrl}/config`);
+ if (response.ok) {
+ const config = await response.json();
+ API_TOKEN = config.api_token;
+ document.getElementById('apiToken').value = API_TOKEN;
+ generateBenchCommand();
+ }
+ } catch (error) {
+ console.error('Failed to load backend config:', error);
+ API_TOKEN = 'ERROR_LOADING_TOKEN';
+ }
+}
// Initialize settings page
-document.addEventListener('DOMContentLoaded', () => {
+document.addEventListener('DOMContentLoaded', async () => {
+ loadDisplayPreferences();
loadSettings();
- generateBenchCommand();
+ await loadBackendConfig();
});
+// Load display preferences
+function loadDisplayPreferences() {
+ const memoryUnit = localStorage.getItem('displayPref_memoryUnit') || 'GB';
+ const storageUnit = localStorage.getItem('displayPref_storageUnit') || 'GB';
+ const cacheUnit = localStorage.getItem('displayPref_cacheUnit') || 'KB';
+ const temperatureUnit = localStorage.getItem('displayPref_temperatureUnit') || 'C';
+ const sectionIconSize = localStorage.getItem('displayPref_sectionIconSize') || '32';
+ const buttonIconSize = localStorage.getItem('displayPref_buttonIconSize') || '24';
+
+ document.getElementById('memoryUnit').value = memoryUnit;
+ document.getElementById('storageUnit').value = storageUnit;
+ document.getElementById('cacheUnit').value = cacheUnit;
+ document.getElementById('temperatureUnit').value = temperatureUnit;
+ document.getElementById('sectionIconSize').value = sectionIconSize;
+ document.getElementById('buttonIconSize').value = buttonIconSize;
+
+ // Apply icon sizes
+ applyIconSizes(sectionIconSize, buttonIconSize);
+}
+
+// Apply icon sizes dynamically
+function applyIconSizes(sectionIconSize, buttonIconSize) {
+ document.documentElement.style.setProperty('--section-icon-size', `${sectionIconSize}px`);
+ document.documentElement.style.setProperty('--button-icon-size', `${buttonIconSize}px`);
+}
+
+// Save display preferences
+function saveDisplayPreferences() {
+ const memoryUnit = document.getElementById('memoryUnit').value;
+ const storageUnit = document.getElementById('storageUnit').value;
+ const cacheUnit = document.getElementById('cacheUnit').value;
+ const temperatureUnit = document.getElementById('temperatureUnit').value;
+ const sectionIconSize = document.getElementById('sectionIconSize').value;
+ const buttonIconSize = document.getElementById('buttonIconSize').value;
+
+ localStorage.setItem('displayPref_memoryUnit', memoryUnit);
+ localStorage.setItem('displayPref_storageUnit', storageUnit);
+ localStorage.setItem('displayPref_cacheUnit', cacheUnit);
+ localStorage.setItem('displayPref_temperatureUnit', temperatureUnit);
+ localStorage.setItem('displayPref_sectionIconSize', sectionIconSize);
+ localStorage.setItem('displayPref_buttonIconSize', buttonIconSize);
+
+ // Apply icon sizes immediately
+ applyIconSizes(sectionIconSize, buttonIconSize);
+
+ showToast('Préférences enregistrées ! Rechargez la page pour voir les changements.', 'success');
+}
+
+// Reset display preferences to defaults
+function resetDisplayPreferences() {
+ localStorage.removeItem('displayPref_memoryUnit');
+ localStorage.removeItem('displayPref_storageUnit');
+ localStorage.removeItem('displayPref_cacheUnit');
+ localStorage.removeItem('displayPref_temperatureUnit');
+ localStorage.removeItem('displayPref_sectionIconSize');
+ localStorage.removeItem('displayPref_buttonIconSize');
+
+ // Reset form to defaults
+ document.getElementById('memoryUnit').value = 'GB';
+ document.getElementById('storageUnit').value = 'GB';
+ document.getElementById('cacheUnit').value = 'KB';
+ document.getElementById('temperatureUnit').value = 'C';
+ document.getElementById('sectionIconSize').value = '32';
+ document.getElementById('buttonIconSize').value = '24';
+
+ // Apply default icon sizes
+ applyIconSizes('32', '24');
+
+ showToast('Préférences réinitialisées aux valeurs par défaut', 'success');
+}
+
// Load settings
function loadSettings() {
// In a real scenario, these would be fetched from backend or localStorage
@@ -22,8 +111,7 @@ function loadSettings() {
document.getElementById('iperfServer').value = savedIperfServer;
document.getElementById('benchMode').value = savedBenchMode;
- // Set API token (in production, this should be fetched securely)
- document.getElementById('apiToken').value = API_TOKEN;
+ // API token will be loaded asynchronously from backend
// Add event listeners for auto-generation
document.getElementById('backendUrl').addEventListener('input', () => {
@@ -74,8 +162,8 @@ function generateBenchCommand() {
const scriptUrl = `${backendUrl.replace(':8007', ':8087')}/scripts/bench.sh`;
// Build command parts
- let command = `curl -s ${scriptUrl} | bash -s -- \\
- --server ${backendUrl}/api/benchmark \\
+ let command = `curl -s ${scriptUrl} | sudo bash -s -- \\
+ --server ${backendUrl} \\
--token "${API_TOKEN}"`;
if (iperfServer) {
@@ -143,3 +231,5 @@ window.generateBenchCommand = generateBenchCommand;
window.copyGeneratedCommand = copyGeneratedCommand;
window.toggleTokenVisibility = toggleTokenVisibility;
window.copyToken = copyToken;
+window.saveDisplayPreferences = saveDisplayPreferences;
+window.resetDisplayPreferences = resetDisplayPreferences;
diff --git a/frontend/js/utils.js b/frontend/js/utils.js
old mode 100644
new mode 100755
index 9530837..22b569d
--- a/frontend/js/utils.js
+++ b/frontend/js/utils.js
@@ -40,6 +40,26 @@ function formatFileSize(bytes) {
return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + ' ' + sizes[i];
}
+// Format duration (seconds) into human readable form
+function formatDuration(seconds) {
+ if (seconds === null || seconds === undefined) return 'N/A';
+ const totalSeconds = Number(seconds);
+ if (!Number.isFinite(totalSeconds) || totalSeconds < 0) return 'N/A';
+
+ const days = Math.floor(totalSeconds / 86400);
+ const hours = Math.floor((totalSeconds % 86400) / 3600);
+ const minutes = Math.floor((totalSeconds % 3600) / 60);
+ const secs = Math.floor(totalSeconds % 60);
+
+ const parts = [];
+ if (days > 0) parts.push(`${days} j`);
+ if (hours > 0) parts.push(`${hours} h`);
+ if (minutes > 0) parts.push(`${minutes} min`);
+ if (parts.length === 0) parts.push(`${secs} s`);
+
+ return parts.join(' ');
+}
+
// Get score badge class based on value
function getScoreBadgeClass(score) {
if (score === null || score === undefined) return 'score-badge';
@@ -51,7 +71,9 @@ function getScoreBadgeClass(score) {
// Get score badge text
function getScoreBadgeText(score) {
if (score === null || score === undefined) return '--';
- return Math.round(score);
+ const numeric = Number(score);
+ if (!Number.isFinite(numeric)) return '--';
+ return Math.ceil(numeric);
}
// Create score badge HTML
@@ -299,8 +321,21 @@ function closeModal(modalId) {
}
}
-// Initialize modal close buttons
+// Apply icon size preferences from localStorage
+function applyIconSizePreferences() {
+ const sectionIconSize = localStorage.getItem('displayPref_sectionIconSize') || '32';
+ const buttonIconSize = localStorage.getItem('displayPref_buttonIconSize') || '24';
+
+ document.documentElement.style.setProperty('--section-icon-size', `${sectionIconSize}px`);
+ document.documentElement.style.setProperty('--button-icon-size', `${buttonIconSize}px`);
+}
+
+// Initialize modal close buttons and apply icon preferences
document.addEventListener('DOMContentLoaded', () => {
+ // Apply icon size preferences on all pages
+ applyIconSizePreferences();
+
+ // Initialize modal close buttons
document.querySelectorAll('.modal').forEach(modal => {
modal.addEventListener('click', (e) => {
if (e.target === modal) {
@@ -318,10 +353,90 @@ document.addEventListener('DOMContentLoaded', () => {
});
// Export functions for use in other files
+// Unit conversion functions
+function getDisplayPreferences() {
+ return {
+ memoryUnit: localStorage.getItem('displayPref_memoryUnit') || 'GB',
+ storageUnit: localStorage.getItem('displayPref_storageUnit') || 'GB',
+ cacheUnit: localStorage.getItem('displayPref_cacheUnit') || 'KB',
+ temperatureUnit: localStorage.getItem('displayPref_temperatureUnit') || 'C'
+ };
+}
+
+// Convert memory from MB to preferred unit
+function formatMemory(mb, forceUnit = null) {
+ if (!mb || mb === 0) return '0 MB';
+
+ const prefs = getDisplayPreferences();
+ const unit = forceUnit || prefs.memoryUnit;
+
+ if (unit === 'GB') {
+ return (mb / 1024).toFixed(2) + ' GB';
+ }
+ return mb.toFixed(0) + ' MB';
+}
+
+// Convert storage from GB to preferred unit
+function formatStorage(gb, forceUnit = null) {
+ if (!gb || gb === 0) return '0 GB';
+
+ const prefs = getDisplayPreferences();
+ const unit = forceUnit || prefs.storageUnit;
+
+ if (unit === 'TB') {
+ return (gb / 1024).toFixed(2) + ' TB';
+ } else if (unit === 'MB') {
+ return (gb * 1024).toFixed(0) + ' MB';
+ }
+ return gb.toFixed(2) + ' GB';
+}
+
+// Convert cache from KB to preferred unit
+function formatCache(kb, forceUnit = null) {
+ if (!kb || kb === 0) return '0 KB';
+
+ const prefs = getDisplayPreferences();
+ const unit = forceUnit || prefs.cacheUnit;
+
+ if (unit === 'MB') {
+ return (kb / 1024).toFixed(2) + ' MB';
+ }
+ return kb.toFixed(0) + ' KB';
+}
+
+// Convert temperature to preferred unit
+function formatTemperature(celsius, forceUnit = null) {
+ if (celsius === null || celsius === undefined) return 'N/A';
+
+ const prefs = getDisplayPreferences();
+ const unit = forceUnit || prefs.temperatureUnit;
+
+ if (unit === 'F') {
+ const fahrenheit = (celsius * 9/5) + 32;
+ return fahrenheit.toFixed(1) + '°F';
+ }
+ return celsius.toFixed(1) + '°C';
+}
+
+function renderMarkdown(text) {
+ if (!text || !text.trim()) {
+ return '
Aucune note disponible
';
+ }
+
+ let html = escapeHtml(text);
+ html = html.replace(/\*\*(.+?)\*\*/g, '
$1 ');
+ html = html.replace(/\*(.+?)\*/g, '
$1 ');
+ html = html.replace(/`([^`]+)`/g, '
$1');
+ html = html.replace(/\n/g, '
');
+
+ return `
${html}
`;
+}
+
window.BenchUtils = {
formatDate,
formatRelativeTime,
formatFileSize,
+ formatDuration,
getScoreBadgeClass,
getScoreBadgeText,
createScoreBadge,
@@ -340,5 +455,11 @@ window.BenchUtils = {
formatHardwareInfo,
initTabs,
openModal,
- closeModal
+ closeModal,
+ getDisplayPreferences,
+ formatMemory,
+ formatStorage,
+ formatCache,
+ formatTemperature,
+ renderMarkdown
};
diff --git a/frontend/scripts/bench.sh b/frontend/scripts/bench.sh
index bea92eb..f36c3b5 100755
--- a/frontend/scripts/bench.sh
+++ b/frontend/scripts/bench.sh
@@ -2,82 +2,1611 @@
#
# Linux BenchTools - Client Benchmark Script
-# Version: 1.0.0
+# Version: 1.3.0
#
-# This script collects hardware information and runs benchmarks on Linux machines,
-# then sends the results to the BenchTools backend API.
+# Collecte les informations hardware, exécute les benchmarks
+# puis envoie les résultats au serveur backend (payload JSON).
#
set -e
-# Script version
-BENCH_SCRIPT_VERSION="1.0.0"
+#=========================================================
+# Version / variables globales
+#=========================================================
-# Default values
-SERVER_URL=""
-API_TOKEN=""
-DEVICE_IDENTIFIER=""
-IPERF_SERVER=""
-SHORT_MODE=false
-SKIP_CPU=false
-SKIP_MEMORY=false
-SKIP_DISK=false
-SKIP_NETWORK=false
-SKIP_GPU=false
+BENCH_SCRIPT_VERSION="1.3.1"
-# Colors for output
-RED='\033[0;31m'
+# Couleurs
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
+RED='\033[0;31m'
+BLUE='\033[0;34m'
NC='\033[0m' # No Color
-# Logging functions
+TOTAL_STEPS=8
+CURRENT_STEP=0
+
+# Paramètres réseau / API (peuvent être surchargés par variables d'environnement)
+SERVER_URL="${SERVER_URL:-10.0.1.97:8007}" # ajouter le port ou le schéma dans send_benchmark_payload si besoin
+API_TOKEN="${API_TOKEN:-29855796dacf5cfe75ff9b02d6adf3dd0f9c52db5b53e7abfb4c0df7ece1be0a}"
+IPERF_SERVER="${IPERF_SERVER:-10.0.1.97}"
+
+# Mode DEBUG
+# Mettre à 1 pour afficher le payload JSON complet avant envoi
+# Mettre à 0 pour désactiver le debug
+DEBUG_PAYLOAD="${DEBUG_PAYLOAD:-0}" # Par défaut: 0 (désactivé)
+ # Peut être activé via: DEBUG_PAYLOAD=1 bash bench.sh
+
+# Forcer locale en anglais pour parsing
+export LC_ALL=C
+
+# Ajouter /usr/sbin au PATH pour dmidecode, smartctl, ethtool
+export PATH="/usr/sbin:/sbin:$PATH"
+
+# Variables JSON globales
+SYSTEM_INFO="null"
+CPU_INFO="null"
+RAM_INFO="null"
+GPU_INFO="null"
+MOTHERBOARD_INFO="null"
+STORAGE_INFO="[]"
+PARTITION_INFO="[]"
+NETWORK_INFO="[]"
+PCI_INFO="[]"
+USB_INFO="[]"
+NETWORK_SHARES_INFO="[]"
+BENCHMARK_RESULTS="null"
+
+SMARTCTL_CMD="sudo smartctl"
+if command -v timeout &>/dev/null; then
+ SMARTCTL_CMD="timeout 10s sudo smartctl"
+fi
+
+#=========================================================
+# Affichage / logs
+#=========================================================
+
+log_step() {
+ CURRENT_STEP=$((CURRENT_STEP + 1))
+ echo -e "${BLUE}[${CURRENT_STEP}/${TOTAL_STEPS}]${NC} $1"
+}
+
log_info() {
- echo -e "${GREEN}[INFO]${NC} $1"
+ echo -e " ${GREEN}✓${NC} $1"
}
log_warn() {
- echo -e "${YELLOW}[WARN]${NC} $1"
+ echo -e " ${YELLOW}⚠${NC} $1"
}
log_error() {
- echo -e "${RED}[ERROR]${NC} $1"
+ echo -e " ${RED}✗${NC} $1"
}
-# Usage information
-show_usage() {
- cat <
--token [OPTIONS]
+check_sudo() {
+ echo -e "${BLUE}════════════════════════════════════════════════════════${NC}"
+ echo -e " Linux BenchTools - Client Benchmark Script"
+ echo -e " Version ${BENCH_SCRIPT_VERSION}"
+ echo -e "${BLUE}════════════════════════════════════════════════════════${NC}"
+ echo ""
+ echo "Ce script nécessite les permissions sudo pour :"
+ echo " - dmidecode (infos RAM, carte mère, BIOS)"
+ echo " - smartctl (infos disques)"
+ echo " - ethtool (vitesse réseau)"
+ echo ""
-Required:
- --server Backend API URL (e.g., http://server:8007/api/benchmark)
- --token API authentication token
+ if ! sudo -v; then
+ log_error "Permissions sudo requises. Relance le script avec sudo."
+ exit 1
+ fi
-Optional:
- --device Device identifier (default: hostname)
- --iperf-server iperf3 server for network tests
- --short Run quick tests (reduced duration)
- --skip-cpu Skip CPU benchmark
- --skip-memory Skip memory benchmark
- --skip-disk Skip disk benchmark
- --skip-network Skip network benchmark
- --skip-gpu Skip GPU benchmark
- --help Show this help message
+ # Garder sudo actif
+ while true; do sudo -n true; sleep 50; kill -0 "$$" || exit; done 2>/dev/null &
-Example:
- $0 --server http://192.168.1.100:8007/api/benchmark \\
- --token YOUR_TOKEN \\
- --iperf-server 192.168.1.100
-
-EOF
+ log_info "Permissions sudo OK"
+ echo ""
}
-# Parse command line arguments
+check_dependencies() {
+ local missing=()
+ local bench_missing=()
+
+ echo -e "${BLUE}Vérification des dépendances...${NC}"
+
+ for tool in curl jq lscpu free lsblk ip bc smartctl; do
+ command -v "$tool" &>/dev/null || missing+=("$tool")
+ done
+
+ for tool in sysbench fio iperf3; do
+ command -v "$tool" &>/dev/null || bench_missing+=("$tool")
+ done
+
+ if [[ ${#missing[@]} -eq 0 && ${#bench_missing[@]} -eq 0 ]]; then
+ log_info "Toutes les dépendances de base sont présentes"
+ log_info "Vérification des dépendances terminée"
+ echo ""
+ return
+ fi
+
+ echo ""
+ if [[ ${#missing[@]} -gt 0 ]]; then
+ log_warn "Outils systèmes manquants: ${missing[*]}"
+ fi
+ if [[ ${#bench_missing[@]} -gt 0 ]]; then
+ log_warn "Outils de benchmark manquants: ${bench_missing[*]}"
+ fi
+
+ if ! command -v apt-get &>/dev/null; then
+ log_warn "apt-get non disponible, installation automatique impossible."
+ log_info "Vérification des dépendances terminée"
+ echo ""
+ return
+ fi
+
+ declare -A pkg_map=(
+ [curl]="curl"
+ [jq]="jq"
+ [lscpu]="util-linux"
+ [free]="procps"
+ [lsblk]="util-linux"
+ [ip]="iproute2"
+ [bc]="bc"
+ [smartctl]="smartmontools"
+ [sysbench]="sysbench"
+ [fio]="fio"
+ [iperf3]="iperf3"
+ )
+
+ local to_install=()
+ for t in "${missing[@]}" "${bench_missing[@]}"; do
+ [[ -n "${pkg_map[$t]}" ]] && to_install+=("${pkg_map[$t]}")
+ done
+
+ if [[ ${#to_install[@]} -gt 0 ]]; then
+ mapfile -t to_install < <(printf '%s\n' "${to_install[@]}" | sort -u)
+ echo -e "${BLUE}Installation automatique des paquets manquants...${NC}"
+ echo " Paquets: ${to_install[*]}"
+ echo ""
+ echo "► apt-get update..."
+ if ! sudo apt-get update -qq 2>&1 | grep -v "Policy will reject signature"; then
+ log_warn "Warning durant apt-get update (on continue)"
+ fi
+ echo "► apt-get install..."
+ if sudo apt-get install -y "${to_install[@]}"; then
+ log_info "Installation des dépendances OK"
+ else
+ log_error "Échec de l'installation des dépendances"
+ exit 1
+ fi
+ fi
+
+ log_info "Vérification des dépendances terminée"
+ echo ""
+}
+
+#=========================================================
+# Utilitaire bc robuste
+#=========================================================
+
+safe_bc() {
+ local expr="$1"
+ local out
+ out=$(echo "$expr" | bc 2>/dev/null) || out="0"
+ echo "$out"
+}
+
+bytes_to_gb() {
+ local bytes="$1"
+ if [[ -z "$bytes" || "$bytes" == "0" ]]; then
+ echo ""
+ else
+ safe_bc "scale=2; $bytes / (1024*1024*1024)"
+ fi
+}
+
+# Detect the current graphical session type (wayland/x11/tty)
+detect_session_type() {
+ local session_type="${XDG_SESSION_TYPE:-}"
+
+ if [[ -z "$session_type" && -n "$USER" && $(command -v loginctl) ]]; then
+ local session_id
+ session_id=$(loginctl 2>/dev/null | awk -v user="$USER" '$3==user {print $1; exit}')
+ if [[ -n "$session_id" ]]; then
+ session_type=$(loginctl show-session "$session_id" -p Type 2>/dev/null | awk -F= '/Type=/ {print $2}')
+ fi
+ fi
+
+ if [[ -z "$session_type" && -n "$WAYLAND_DISPLAY" ]]; then
+ session_type="wayland"
+ elif [[ -z "$session_type" && -n "$DISPLAY" ]]; then
+ session_type="x11"
+ fi
+
+ echo "$session_type"
+}
+
+# Try to detect the primary screen resolution
+detect_screen_resolution() {
+ local resolution=""
+
+ if command -v xrandr &>/dev/null && [[ -n "$DISPLAY" ]]; then
+ resolution=$(xrandr --current 2>/dev/null | awk '/\*/ {print $1; exit}')
+ fi
+
+ if [[ -z "$resolution" && -n "$DISPLAY" ]] && command -v xdpyinfo &>/dev/null; then
+ resolution=$(xdpyinfo 2>/dev/null | awk '/dimensions:/ {print $2; exit}')
+ fi
+
+ if [[ -z "$resolution" ]] && command -v swaymsg &>/dev/null; then
+ resolution=$(swaymsg -t get_outputs 2>/dev/null | jq -r '
+ [.[] | select(.active == true) | "\(.current_mode.width)x\(.current_mode.height)"] | .[0]'
+ )
+ [[ "$resolution" == "null" ]] && resolution=""
+ fi
+
+ echo "$resolution"
+}
+
+#=========================================================
+# Étape 1 : Système
+#=========================================================
+
+collect_system_info() {
+ log_step "Collecte des informations système de base"
+
+ local hostname os_name os_version kernel arch
+ local session_type screen_resolution last_boot uptime_seconds
+ local battery_percentage="" battery_status="" battery_health=""
+
+ hostname=$(hostname)
+ os_name=$(grep '^ID=' /etc/os-release | cut -d= -f2 | tr -d '"')
+ os_version=$(grep '^VERSION=' /etc/os-release | cut -d= -f2 | tr -d '"')
+ kernel=$(uname -r)
+ arch=$(uname -m)
+ session_type=$(detect_session_type)
+ screen_resolution=$(detect_screen_resolution)
+
+ if command -v who &>/dev/null; then
+ last_boot=$(who -b 2>/dev/null | awk '{print $3 " " $4}')
+ fi
+
+ if [[ -r /proc/uptime ]]; then
+ uptime_seconds=$(awk '{print int($1)}' /proc/uptime)
+ fi
+
+ for bat in /sys/class/power_supply/BAT*; do
+ [[ -d "$bat" ]] || continue
+ if [[ -f "$bat/present" ]]; then
+ local present
+ present=$(cat "$bat/present")
+ [[ "$present" != "1" ]] && continue
+ fi
+
+ if [[ -z "$battery_percentage" && -f "$bat/capacity" ]]; then
+ battery_percentage=$(cat "$bat/capacity" | tr -dc '0-9.')
+ fi
+ if [[ -z "$battery_status" && -f "$bat/status" ]]; then
+ battery_status=$(cat "$bat/status")
+ fi
+ if [[ -z "$battery_health" ]]; then
+ if [[ -f "$bat/health" ]]; then
+ battery_health=$(cat "$bat/health")
+ elif [[ -f "$bat/uevent" ]]; then
+ battery_health=$(grep -m1 'POWER_SUPPLY_HEALTH' "$bat/uevent" 2>/dev/null | cut -d= -f2)
+ fi
+ fi
+ break
+ done
+
+ SYSTEM_INFO=$(jq -n \
+ --arg hostname "$hostname" \
+ --arg os_name "$os_name" \
+ --arg os_version "$os_version" \
+ --arg kernel "$kernel" \
+ --arg arch "$arch" \
+ --arg session "$session_type" \
+ --arg resolution "$screen_resolution" \
+ --arg last_boot "$last_boot" \
+ --arg uptime "$uptime_seconds" \
+ --arg battery_pct "$battery_percentage" \
+ --arg battery_status "$battery_status" \
+ --arg battery_health "$battery_health" \
+ '{
+ hostname: $hostname,
+ os: {
+ name: $os_name,
+ version: $os_version,
+ kernel_version: $kernel,
+ architecture: $arch,
+ session_type: (if $session != "" then $session else null end),
+ display_server: (if $session != "" then $session else null end),
+ screen_resolution: (if $resolution != "" then $resolution else null end),
+ last_boot_time: (if $last_boot != "" then $last_boot else null end),
+ uptime_seconds: (if $uptime != "" then ($uptime | tonumber?) else null end),
+ battery_percentage: (if $battery_pct != "" then ($battery_pct | tonumber?) else null end),
+ battery_status: (if $battery_status != "" then $battery_status else null end),
+ battery_health: (if $battery_health != "" then $battery_health else null end)
+ }
+ }')
+
+ log_info "Hostname: $hostname"
+ log_info "OS: $os_name $os_version"
+ log_info "Kernel: $kernel"
+ [[ -n "$session_type" ]] && log_info "Session: $session_type"
+ [[ -n "$screen_resolution" ]] && log_info "Résolution écran: $screen_resolution"
+ [[ -n "$last_boot" ]] && log_info "Dernier boot: $last_boot"
+ if [[ -n "$battery_percentage" ]]; then
+ log_info "Batterie: ${battery_percentage}% ${battery_status:+($battery_status)}"
+ fi
+ echo ""
+}
+
+#=========================================================
+# Étape 2 : CPU
+#=========================================================
+
+collect_cpu_info() {
+ log_step "Collecte des informations CPU"
+
+ local vendor model cores threads
+ vendor=$(lscpu | awk -F: '/Vendor ID/ {gsub(/^[ \t]+/,"",$2); print $2}')
+ model=$(lscpu | awk -F: '/Model name/ {gsub(/^[ \t]+/,"",$2); print $2}')
+
+ # Calcul du nombre de cores physiques: Core(s) per socket × Socket(s)
+ local cores_per_socket sockets
+ cores_per_socket=$(lscpu | awk -F: '/Core\(s\) per socket/ {gsub(/^[ \t]+/,"",$2); gsub(/[^0-9]/,"",$2); print $2}')
+ sockets=$(lscpu | awk -F: '/Socket\(s\)/ {gsub(/^[ \t]+/,"",$2); gsub(/[^0-9]/,"",$2); print $2}')
+
+ # S'assurer que les valeurs sont des nombres valides
+ [[ -z "$cores_per_socket" || "$cores_per_socket" == "0" ]] && cores_per_socket=1
+ [[ -z "$sockets" || "$sockets" == "0" ]] && sockets=1
+
+ cores=$((cores_per_socket * sockets))
+
+ threads=$(nproc)
+
+ local cpu_mhz cpu_max_mhz base_freq_ghz max_freq_ghz
+ cpu_mhz=$(lscpu | awk -F: '/CPU MHz/ {gsub(/^[ \t]+/,"",$2); print $2}')
+ cpu_max_mhz=$(lscpu | awk -F: '/CPU max MHz/ {gsub(/^[ \t]+/,"",$2); print $2}')
+
+ base_freq_ghz="null"
+ max_freq_ghz="null"
+ if [[ -n "$cpu_mhz" ]]; then
+ base_freq_ghz=$(safe_bc "scale=2; $cpu_mhz / 1000")
+ fi
+ if [[ -n "$cpu_max_mhz" ]]; then
+ max_freq_ghz=$(safe_bc "scale=2; $cpu_max_mhz / 1000")
+ fi
+
+ local cache_l1_kb cache_l2_kb cache_l3_kb
+ # L1 cache = L1d + L1i
+ # Parser format: "384 KiB (12 instances)" ou "6 MiB (12 instances)"
+ # Extraire le premier nombre + unité, ignorer "(X instances)"
+ # Utilisation de sed pour extraire "nombre unité" puis awk pour convertir MiB en KB
+ local cache_l1d cache_l1i
+ cache_l1d=$(lscpu | grep 'L1d cache' | sed -n 's/.*:\s*\([0-9]\+\)\s*\(KiB\|MiB\).*/\1 \2/p' | awk '{if ($2 == "MiB") print $1*1024; else print $1}')
+ cache_l1i=$(lscpu | grep 'L1i cache' | sed -n 's/.*:\s*\([0-9]\+\)\s*\(KiB\|MiB\).*/\1 \2/p' | awk '{if ($2 == "MiB") print $1*1024; else print $1}')
+ cache_l1_kb=$((${cache_l1d:-0} + ${cache_l1i:-0}))
+
+ cache_l2_kb=$(lscpu | grep 'L2 cache' | sed -n 's/.*:\s*\([0-9]\+\)\s*\(KiB\|MiB\).*/\1 \2/p' | awk '{if ($2 == "MiB") print $1*1024; else print $1}')
+ cache_l3_kb=$(lscpu | grep 'L3 cache' | sed -n 's/.*:\s*\([0-9]\+\)\s*\(KiB\|MiB\).*/\1 \2/p' | awk '{if ($2 == "MiB") print $1*1024; else print $1}')
+
+ local flags
+ flags=$(lscpu | awk -F: '/Flags/ {gsub(/^[ \t]+/,"",$2); print $2}')
+ local flags_array
+ flags_array=$(printf '%s\n' $flags | jq -R . | jq -s .)
+
+ # Important: on passe les numériques en --arg (string) puis on cast dans jq
+ CPU_INFO=$(jq -n \
+ --arg vendor "$vendor" \
+ --arg model "$model" \
+ --arg cores "$cores" \
+ --arg threads "$threads" \
+ --arg base_freq "$base_freq_ghz" \
+ --arg max_freq "$max_freq_ghz" \
+ --arg l1 "$cache_l1_kb" \
+ --arg l2 "$cache_l2_kb" \
+ --arg l3 "$cache_l3_kb" \
+ --argjson flags "$flags_array" \
+ '{
+ vendor: $vendor,
+ model: $model,
+ cores: ($cores | tonumber? // 0),
+ threads: ($threads | tonumber? // 0),
+ base_freq_ghz: (if $base_freq == "null" or $base_freq == "" then null else ($base_freq | tonumber?) end),
+ max_freq_ghz: (if $max_freq == "null" or $max_freq == "" then null else ($max_freq | tonumber?) end),
+ cache_l1_kb: ($l1 | tonumber? // 0),
+ cache_l2_kb: ($l2 | tonumber? // 0),
+ cache_l3_kb: ($l3 | tonumber? // 0),
+ flags: $flags
+ }')
+
+ log_info "CPU: $model"
+ log_info "Cores: ${cores:-0}, Threads: ${threads:-0}"
+ echo ""
+}
+
+#=========================================================
+# Étape 3 : RAM
+#=========================================================
+
+collect_ram_info() {
+ log_step "Collecte des informations RAM"
+
+ local mem_total_kb mem_used_kb mem_free_kb mem_shared_kb
+ mem_total_kb=$(free -k | awk '/^Mem:/ {print $2}')
+ mem_used_kb=$(free -k | awk '/^Mem:/ {print $3}')
+ mem_free_kb=$(free -k | awk '/^Mem:/ {print $4}')
+ mem_shared_kb=$(free -k | awk '/^Mem:/ {print $5}')
+
+ local total_mb used_mb free_mb shared_mb
+ total_mb=$((mem_total_kb / 1024))
+ used_mb=$((mem_used_kb / 1024))
+ free_mb=$((mem_free_kb / 1024))
+ shared_mb=$((mem_shared_kb / 1024))
+
+ local slots_total=0 slots_used=0 ecc=false
+ local dimm_layout="[]"
+
+ if command -v dmidecode &>/dev/null; then
+ log_info "[DEBUG] Vérification dmidecode..."
+ if sudo dmidecode -t 16 &>/dev/null; then
+ log_info "[DEBUG] dmidecode trouvé: $(command -v dmidecode)"
+ else
+ log_warn "dmidecode accessible mais aucune info DMI"
+ fi
+
+ local slots
+ slots=$(sudo dmidecode -t 16 2>/dev/null | awk -F: '/Number Of Devices/ {gsub(/^[ \t]+/,"",$2); print $2}' | head -1)
+ [[ -n "$slots" ]] && slots_total="$slots"
+
+ if sudo dmidecode -t memory 2>/dev/null | grep -q 'Error Correction Type'; then
+ if sudo dmidecode -t memory 2>/dev/null | grep 'Error Correction Type' | grep -q 'None'; then
+ ecc=false
+ else
+ ecc=true
+ fi
+ fi
+
+ local dimm_data
+ dimm_data=$(sudo dmidecode -t 17 | grep -E 'Locator:|Size:|Type:|Speed:|Manufacturer:' | \
+ awk '
+ /Locator:/ && !/Bank/ { slot=$2; gsub(/_/, "", slot) }
+ /Size:/ && /[0-9]+ [GM]B/ {
+ size=$2;
+ if ($3 == "GB") size=size*1024;
+ if ($3 == "MB") size=size;
+ }
+ /Type:/ && !/Detail/ { type=$2 }
+ /Speed:/ && /MHz/ { speed=$2 }
+ /Manufacturer:/ {
+ manu=$2;
+ printf "%s;%s;%s;%s;%s\n", slot, size, type, speed, manu
+ }')
+
+ if [[ -n "$dimm_data" ]]; then
+ while IFS=';' read -r slot size type speed manu; do
+ [[ -z "$slot" ]] && continue
+ slots_used=$((slots_used + 1))
+ local entry
+ entry=$(jq -n \
+ --arg slot "$slot" \
+ --arg size_mb "$size" \
+ --arg type "$type" \
+ --arg speed_mhz "$speed" \
+ --arg manu "$manu" \
+ '{
+ slot: $slot,
+ size_mb: ($size_mb | tonumber? // 0),
+ type: $type,
+ speed_mhz: ($speed_mhz | tonumber? // 0),
+ manufacturer: $manu
+ }')
+ dimm_layout=$(echo "$dimm_layout" | jq --argjson e "$entry" '. + [$e]')
+ done <<< "$dimm_data"
+ fi
+ else
+ log_warn "dmidecode non disponible - layout RAM détaillé ignoré"
+ fi
+
+ RAM_INFO=$(jq -n \
+ --arg total_mb "$total_mb" \
+ --arg used_mb "$used_mb" \
+ --arg free_mb "$free_mb" \
+ --arg shared_mb "$shared_mb" \
+ --arg slots_total "$slots_total" \
+ --arg slots_used "$slots_used" \
+ --arg ecc_str "$([[ "$ecc" = true ]] && echo true || echo false)" \
+ --argjson layout "$dimm_layout" \
+ '{
+ total_mb: ($total_mb | tonumber? // 0),
+ used_mb: ($used_mb | tonumber? // 0),
+ free_mb: ($free_mb | tonumber? // 0),
+ shared_mb: ($shared_mb | tonumber? // 0),
+ slots_total: ($slots_total | tonumber? // 0),
+ slots_used: ($slots_used | tonumber? // 0),
+ ecc: (if $ecc_str == "true" then true else false end),
+ layout: $layout
+ }')
+
+ log_info "RAM Total: ${total_mb}MB (Utilisée: ${used_mb}MB, Libre: ${free_mb}MB)"
+ log_info "Slots: ${slots_used}/${slots_total}"
+ echo ""
+}
+
+#=========================================================
+# Étape 4 : GPU / Carte mère
+#=========================================================
+
+collect_hardware_info() {
+ log_step "Collecte GPU, Carte mère, BIOS"
+
+ local gpu_vendor="null"
+ local gpu_model="null"
+ local gpu_vram="null"
+ local gpu_driver="null"
+
+ if command -v lspci &>/dev/null; then
+ local gpu_line
+ gpu_line=$(lspci | grep -iE 'vga|3d' | head -1)
+ if [[ -n "$gpu_line" ]]; then
+ gpu_model=$(echo "$gpu_line" | sed 's/.*: //')
+ if echo "$gpu_line" | grep -qi 'nvidia'; then
+ gpu_vendor="NVIDIA"
+ # Essayer d'obtenir plus d'infos avec nvidia-smi
+ if command -v nvidia-smi &>/dev/null; then
+ local nvidia_model nvidia_vram nvidia_driver
+ # Vérifier que nvidia-smi fonctionne avant d'extraire les infos
+ if nvidia-smi &>/dev/null; then
+ nvidia_model=$(nvidia-smi --query-gpu=name --format=csv,noheader 2>/dev/null | head -1 | tr -d '\n')
+ nvidia_vram=$(nvidia-smi --query-gpu=memory.total --format=csv,noheader,nounits 2>/dev/null | head -1 | tr -d '\n')
+ nvidia_driver=$(nvidia-smi --query-gpu=driver_version --format=csv,noheader 2>/dev/null | head -1 | tr -d '\n')
+
+ # Ne remplacer que si les valeurs sont non-vides et valides
+ if [[ -n "$nvidia_model" && ! "$nvidia_model" =~ (failed|error|Error) ]]; then
+ gpu_model="$nvidia_model"
+ fi
+ if [[ -n "$nvidia_vram" && "$nvidia_vram" =~ ^[0-9]+$ ]]; then
+ gpu_vram="$nvidia_vram"
+ fi
+ if [[ -n "$nvidia_driver" && ! "$nvidia_driver" =~ (failed|error|Error) ]]; then
+ gpu_driver="$nvidia_driver"
+ fi
+ fi
+ fi
+ elif echo "$gpu_line" | grep -qi 'amd'; then
+ gpu_vendor="AMD"
+ elif echo "$gpu_line" | grep -qi 'intel'; then
+ gpu_vendor="Intel"
+ else
+ gpu_vendor="Unknown"
+ fi
+ fi
+ fi
+
+ GPU_INFO=$(jq -n \
+ --arg vendor "$gpu_vendor" \
+ --arg model "$gpu_model" \
+ --arg vram "$gpu_vram" \
+ --arg driver "$gpu_driver" \
+ '{
+ vendor: $vendor,
+ model: $model,
+ memory_dedicated_mb: (if $vram == "null" or $vram == "" then null else ($vram | tonumber?) end),
+ driver_version: (if $driver == "null" or $driver == "" then null else $driver end)
+ }')
+
+ if [[ "$gpu_model" != "null" ]]; then
+ if [[ "$gpu_vram" != "null" ]]; then
+ log_info "GPU: $gpu_model (${gpu_vram}MB VRAM)"
+ else
+ log_info "GPU: $gpu_model"
+ fi
+ else
+ log_warn "GPU non détecté"
+ fi
+
+ local mb_manufacturer="Unknown" mb_model="Unknown"
+ local bios_vendor="Unknown" bios_version="Unknown" bios_date="Unknown"
+
+ if command -v dmidecode &>/dev/null; then
+ mb_manufacturer=$(sudo dmidecode -s baseboard-manufacturer 2>/dev/null || echo "Unknown")
+ mb_model=$(sudo dmidecode -s baseboard-product-name 2>/dev/null || echo "Unknown")
+ bios_vendor=$(sudo dmidecode -s bios-vendor 2>/dev/null || echo "Unknown")
+ bios_version=$(sudo dmidecode -s bios-version 2>/dev/null || echo "Unknown")
+ bios_date=$(sudo dmidecode -s bios-release-date 2>/dev/null || echo "Unknown")
+ fi
+
+ MOTHERBOARD_INFO=$(jq -n \
+ --arg manu "$mb_manufacturer" \
+ --arg model "$mb_model" \
+ --arg bvend "$bios_vendor" \
+ --arg bver "$bios_version" \
+ --arg bdate "$bios_date" \
+ '{
+ manufacturer: $manu,
+ model: $model,
+ bios_vendor: $bvend,
+ bios_version: $bver,
+ bios_date: $bdate
+ }')
+
+ log_info "Motherboard: $mb_manufacturer $mb_model"
+ log_info "BIOS: $bios_version ($bios_date)"
+ echo ""
+
+ collect_pci_devices
+ collect_usb_devices
+}
+
+collect_pci_devices() {
+ PCI_INFO="[]"
+
+ if ! command -v lspci &>/dev/null; then
+ log_warn "lspci non disponible - périphériques PCI ignorés"
+ return
+ fi
+
+ local count=0
+ while IFS= read -r line; do
+ [[ -z "$line" ]] && continue
+
+ local slot class vendor device
+ slot=$(echo "$line" | awk '{print $1}')
+ class=$(echo "$line" | cut -d'"' -f2)
+ vendor=$(echo "$line" | cut -d'"' -f4)
+ device=$(echo "$line" | cut -d'"' -f6)
+
+ [[ -z "$slot" ]] && continue
+
+ local entry
+ entry=$(jq -n \
+ --arg slot "$slot" \
+ --arg class "$class" \
+ --arg vendor "$vendor" \
+ --arg device "$device" \
+ '{slot: $slot, class: $class, vendor: $vendor, device: $device}')
+
+ PCI_INFO=$(echo "$PCI_INFO" | jq --argjson e "$entry" '. + [$e]')
+ count=$((count + 1))
+ done < <(lspci -mm 2>/dev/null || true)
+
+ log_info "Périphériques PCI détectés: $count"
+}
+
+collect_usb_devices() {
+ USB_INFO="[]"
+
+ if ! command -v lsusb &>/dev/null; then
+ log_warn "lsusb non disponible - périphériques USB ignorés"
+ return
+ fi
+
+ local count=0
+ while IFS= read -r line; do
+ [[ -z "$line" ]] && continue
+ if [[ "$line" =~ Bus[[:space:]]([0-9]+)[[:space:]]Device[[:space:]]([0-9]+):[[:space:]]ID[[:space:]]([0-9a-fA-F]+):([0-9a-fA-F]+)[[:space:]](.*)$ ]]; then
+ local bus="${BASH_REMATCH[1]}"
+ local dev="${BASH_REMATCH[2]}"
+ local vendor_id="${BASH_REMATCH[3]}"
+ local product_id="${BASH_REMATCH[4]}"
+ local name="${BASH_REMATCH[5]}"
+
+ local entry
+ entry=$(jq -n \
+ --arg bus "$bus" \
+ --arg device "$dev" \
+ --arg vendor_id "$vendor_id" \
+ --arg product_id "$product_id" \
+ --arg name "$name" \
+ '{bus: $bus, device: $device, vendor_id: $vendor_id, product_id: $product_id, name: $name}')
+
+ USB_INFO=$(echo "$USB_INFO" | jq --argjson e "$entry" '. + [$e]')
+ count=$((count + 1))
+ fi
+ done < <(lsusb 2>/dev/null || true)
+
+ log_info "Périphériques USB détectés: $count"
+}
+
+#=========================================================
+# Étape 5 : Stockage
+#=========================================================
+
+collect_storage_info() {
+ log_step "Collecte des informations de stockage"
+
+ local storage_array="[]"
+ local disks
+ disks=$(lsblk -d -n -o NAME,TYPE | awk '$2=="disk"{print $1}')
+
+ for d in $disks; do
+ local size_h size_gb rota tran
+ size_h=$(lsblk -d -n -o SIZE "/dev/$d")
+ rota=$(lsblk -d -n -o ROTA "/dev/$d")
+ tran=$(lsblk -d -n -o TRAN "/dev/$d")
+
+ local disk_type="unknown"
+ [[ "$rota" = "0" ]] && disk_type="ssd" || disk_type="hdd"
+
+ local interface="unknown"
+ [[ -n "$tran" ]] && interface="$tran"
+
+ local model="Unknown" serial="Unknown" temperature="null" smart_health="null"
+ if command -v smartctl &>/dev/null; then
+ log_info "→ SMART (/dev/$d): collecte en cours (timeout 10s)"
+ if $SMARTCTL_CMD -i "/dev/$d" &>/dev/null; then
+ local s
+ s=$($SMARTCTL_CMD -i "/dev/$d" 2>/dev/null)
+ model=$(echo "$s" | awk -F: '/Device Model|Model Number/ {gsub(/^[ \t]+/,"",$2); print $2}' | head -1)
+ serial=$(echo "$s" | awk -F: '/Serial Number/ {gsub(/^[ \t]+/,"",$2); print $2}' | head -1)
+
+ # Essayer de récupérer la température et le statut SMART
+ local smart_all
+ smart_all=$($SMARTCTL_CMD -A "/dev/$d" 2>/dev/null || true)
+ # Température (diverses variantes selon le type de disque)
+ # SATA/HDD: Temperature_Celsius, Airflow_Temperature_Cel, Current Drive Temperature
+ # RAW_VALUE est après le "-" (colonne 8 dans la plupart des cas, mais peut varier)
+ # On extrait tout après le "-" puis prend le premier nombre
+ # NVMe: Temperature: XX Celsius (colonne 2)
+ temperature=$(echo "$smart_all" | awk '/Temperature_Celsius|Airflow_Temperature_Cel|Current Drive Temperature/ {for(i=1;i<=NF;i++) if($i=="-" && i/dev/null | awk '/SMART overall-health|SMART Health Status/ {print $NF}' | head -1)
+ [[ -n "$health" ]] && smart_health="$health" || smart_health="null"
+ fi
+ fi
+
+ size_gb=$(echo "$size_h" | sed 's/[^0-9.]//g')
+
+ local disk_json
+ disk_json=$(jq -n \
+ --arg device "$d" \
+ --arg model "$model" \
+ --arg size "$size_gb" \
+ --arg type "$disk_type" \
+ --arg interface "$interface" \
+ --arg serial "$serial" \
+ --arg temp "$temperature" \
+ --arg health "$smart_health" \
+ '{
+ device: $device,
+ model: $model,
+ size_gb: $size,
+ type: $type,
+ interface: $interface,
+ serial: $serial,
+ temperature_c: (if $temp == "null" or $temp == "" then null else ($temp | tonumber?) end),
+ smart_health: (if $health == "null" or $health == "" then null else $health end)
+ }')
+
+ storage_array=$(echo "$storage_array" | jq --argjson disk "$disk_json" '. + [$disk]')
+
+ if [[ "$temperature" != "null" ]]; then
+ log_info "Disque: /dev/$d - $model ($size_h, $disk_type, ${temperature}°C)"
+ else
+ log_info "Disque: /dev/$d - $model ($size_h, $disk_type)"
+ fi
+ done
+
+ STORAGE_INFO="$storage_array"
+
+ local partitions_json="[]"
+ if command -v lsblk &>/dev/null; then
+ log_info "→ Récupération des partitions via lsblk"
+ local lsblk_payload=""
+ local lsblk_fields="NAME,TYPE,MOUNTPOINT,SIZE,FSTYPE,FSUSED,FSAVAIL"
+ local lsblk_cmd="lsblk -b -J -o"
+
+ if command -v timeout &>/dev/null; then
+ lsblk_cmd="timeout 10s lsblk -b -J -o"
+ fi
+
+ if ! lsblk_payload=$(bash -c "$lsblk_cmd \"$lsblk_fields\" 2>/dev/null"); then
+ log_warn "lsblk avec colonnes étendues indisponible, tentative en mode simplifié"
+ lsblk_fields="NAME,TYPE,MOUNTPOINT,SIZE,FSTYPE"
+ lsblk_payload=$(bash -c "$lsblk_cmd \"$lsblk_fields\" 2>/dev/null" || true)
+ fi
+
+ if [[ -n "$lsblk_payload" ]]; then
+ if ! partitions_json=$(echo "$lsblk_payload" | jq '
+ def flatten(node):
+ [node]
+ + ( (node.children // []) | map(flatten(.)) | add );
+ (.blockdevices // [])
+ | map(flatten(.))
+ | add
+ | map(select(.type == "part"))
+ | map({
+ name: (if .name then "/dev/" + .name else null end),
+ mount_point: (if (.mountpoint // "") == "" then null else .mountpoint end),
+ fs_type: (.fstype // null),
+ total_gb: (if (.size? // null) then (((.size | tonumber) / (1024*1024*1024)) * 100 | round / 100) else null end),
+ used_gb: (if (.fsused? // null) then (((.fsused | tonumber) / (1024*1024*1024)) * 100 | round / 100) else null end),
+ free_gb: (if (.fsavail? // null) then (((.fsavail | tonumber) / (1024*1024*1024)) * 100 | round / 100) else null end)
+ })
+ ' 2>/dev/null); then
+ log_warn "Parsing JSON lsblk échoué"
+ partitions_json="[]"
+ fi
+ [[ -z "$partitions_json" ]] && partitions_json="[]"
+ else
+ log_warn "Impossible d'obtenir la sortie lsblk (timeout ?)"
+ fi
+ fi
+
+ PARTITION_INFO="$partitions_json"
+ if [[ "$partitions_json" != "[]" ]]; then
+ local partition_count
+ partition_count=$(echo "$partitions_json" | jq 'length')
+ log_info "Partitions détectées: $partition_count"
+ fi
+
+ collect_network_shares
+ echo ""
+}
+
+collect_network_shares() {
+ NETWORK_SHARES_INFO="[]"
+
+ if [[ ! -r /proc/mounts ]]; then
+ log_warn "Impossible de lire /proc/mounts - partages réseau ignorés"
+ return
+ fi
+
+ local count=0
+ while read -r raw_src raw_target raw_type raw_opts _; do
+ local fstype
+ fstype=$(printf '%b' "$raw_type" | tr '[:upper:]' '[:lower:]')
+ case "$fstype" in
+ nfs|nfs4|cifs|smbfs|fuse.cifs|fuse.smbfs|afp|afpfs|fuse.afpfs|sshfs|fuse.sshfs|ftpfs|curlftpfs|fuse.ftpfs|davfs|davfs2|fuse.webdavfs)
+ ;;
+ *)
+ continue
+ ;;
+ esac
+
+ local protocol="$fstype"
+ case "$fstype" in
+ cifs|smbfs|fuse.cifs|fuse.smbfs) protocol="smb" ;;
+ nfs|nfs4) protocol="nfs" ;;
+ afp|afpfs|fuse.afpfs) protocol="afp" ;;
+ sshfs|fuse.sshfs) protocol="sshfs" ;;
+ ftpfs|curlftpfs|fuse.ftpfs) protocol="ftp" ;;
+ davfs|davfs2|fuse.webdavfs) protocol="webdav" ;;
+ esac
+
+ local source target options
+ source=$(printf '%b' "$raw_src")
+ target=$(printf '%b' "$raw_target")
+ options=$(printf '%b' "$raw_opts")
+
+ local total_gb="" used_gb="" free_gb=""
+ if [[ -d "$target" ]]; then
+ local df_line
+ df_line=$(df -B1 "$target" 2>/dev/null | tail -1)
+ if [[ -n "$df_line" ]]; then
+ local total_bytes used_bytes free_bytes
+ total_bytes=$(echo "$df_line" | awk '{print $2}')
+ used_bytes=$(echo "$df_line" | awk '{print $3}')
+ free_bytes=$(echo "$df_line" | awk '{print $4}')
+ [[ -n "$total_bytes" ]] && total_gb=$(bytes_to_gb "$total_bytes")
+ [[ -n "$used_bytes" ]] && used_gb=$(bytes_to_gb "$used_bytes")
+ [[ -n "$free_bytes" ]] && free_gb=$(bytes_to_gb "$free_bytes")
+ fi
+ fi
+
+ local entry
+ entry=$(jq -n \
+ --arg protocol "$protocol" \
+ --arg source "$source" \
+ --arg mount "$target" \
+ --arg fs "$fstype" \
+ --arg opts "$options" \
+ --arg total "${total_gb:-}" \
+ --arg used "${used_gb:-}" \
+ --arg free "${free_gb:-}" \
+ '{
+ protocol: ( $protocol | select(. != "") ),
+ source: $source,
+ mount_point: $mount,
+ fs_type: $fs,
+ options: ( $opts | select(. != "") ),
+ total_gb: ( if $total == "" then null else ($total | tonumber?) end ),
+ used_gb: ( if $used == "" then null else ($used | tonumber?) end ),
+ free_gb: ( if $free == "" then null else ($free | tonumber?) end )
+ }')
+
+ NETWORK_SHARES_INFO=$(echo "$NETWORK_SHARES_INFO" | jq --argjson e "$entry" '. + [$e]')
+ count=$((count + 1))
+ done < /proc/mounts
+
+ if (( count > 0 )); then
+ log_info "Partages réseau détectés: $count"
+ else
+ log_info "Aucun partage réseau monté"
+ fi
+}
+
+#=========================================================
+# Étape 6 : Réseau
+#=========================================================
+
+collect_network_info() {
+ log_step "Collecte des informations réseau"
+
+ local network_array="[]"
+
+ local iface
+ for iface in $(ip -br link show | awk '$2 ~ /UP|UNKNOWN/ {print $1}'); do
+ [[ "$iface" =~ ^(lo|docker|br-|veth) ]] && continue
+
+ local mac
+ mac=$(ip link show "$iface" | awk '/link\/ether/ {print $2}')
+ [[ -z "$mac" ]] && continue
+
+ local type="unknown"
+ if [[ "$iface" =~ ^(eth|enp|eno) ]]; then
+ type="ethernet"
+ elif [[ "$iface" =~ ^(wlan|wl) ]]; then
+ type="wifi"
+ fi
+
+ local ip_addr
+ ip_addr=$(ip -4 addr show "$iface" | awk '/inet /{print $2}' | cut -d/ -f1 | head -1)
+
+ local speed="" wol_supported="" driver="" ssid=""
+ if [[ "$type" = "ethernet" && -x /usr/sbin/ethtool ]]; then
+ local e
+ e=$(sudo ethtool "$iface" 2>/dev/null || true)
+ local spd
+ # Extraire seulement le nombre de la vitesse (enlever "Mb/s")
+ spd=$(echo "$e" | awk -F: '/Speed:/ {gsub(/^[ \t]+/,"",$2); print $2}' | grep -oE '[0-9]+' | head -1)
+ [[ -n "$spd" ]] && speed="$spd"
+ local wol
+ # Extraire Wake-on-LAN et nettoyer (enlever retours chariot)
+ wol=$(echo "$e" | awk -F: '/Wake-on:/ {gsub(/^[ \t]+/,"",$2); print $2}' | tr -d '\n' | head -1)
+ if [[ -n "$wol" && "$wol" != "d" ]]; then
+ wol_supported="true"
+ elif [[ -n "$wol" ]]; then
+ wol_supported="false"
+ fi
+ fi
+
+ if [[ "$type" = "wifi" ]] && command -v iw &>/dev/null; then
+ local iw_info
+ iw_info=$(iw dev "$iface" link 2>/dev/null || true)
+ if echo "$iw_info" | grep -q "SSID"; then
+ ssid=$(echo "$iw_info" | awk -F': ' '/SSID/ {print $2; exit}' | sed 's/[[:space:]]*$//')
+ fi
+
+ local bitrate_line
+ bitrate_line=$(echo "$iw_info" | awk -F': ' '/tx bitrate/ {print $2; exit}' | xargs)
+ if [[ -n "$bitrate_line" ]]; then
+ local br_value=$(echo "$bitrate_line" | awk '{print $1}')
+ local br_unit=$(echo "$bitrate_line" | awk '{print $2}')
+ if [[ "$br_value" =~ ^[0-9]+(\.[0-9]+)?$ ]]; then
+ if [[ "$br_unit" =~ ^MBit/s$ ]]; then
+ speed=$(printf "%.0f" "$br_value")
+ elif [[ "$br_unit" =~ ^GBit/s$ ]]; then
+ speed=$(awk "BEGIN {printf \"%.0f\", $br_value * 1000}")
+ fi
+ fi
+ fi
+ fi
+
+ if command -v ethtool &>/dev/null; then
+ local ethtool_info
+ ethtool_info=$(sudo ethtool -i "$iface" 2>/dev/null || true)
+ if [[ -n "$ethtool_info" ]]; then
+ driver=$(echo "$ethtool_info" | awk -F: '/driver:/ {gsub(/^[ \t]+/,"",$2); print $2; exit}')
+ fi
+ fi
+ if [[ -z "$driver" && -L "/sys/class/net/$iface/device/driver" ]]; then
+ driver=$(basename "$(readlink -f "/sys/class/net/$iface/device/driver")" 2>/dev/null)
+ fi
+
+ # Convertir wol_supported en booléen ou null pour jq
+ local wol_json="null"
+ if [[ "$wol_supported" == "true" ]]; then
+ wol_json="true"
+ elif [[ "$wol_supported" == "false" ]]; then
+ wol_json="false"
+ fi
+
+ local net_json
+ net_json=$(jq -n \
+ --arg name "$iface" \
+ --arg type "$type" \
+ --arg mac "$mac" \
+ --arg ip "${ip_addr:-}" \
+ --arg speed "$speed" \
+ --arg driver "$driver" \
+ --arg ssid "$ssid" \
+ --argjson wol "$wol_json" \
+ '{
+ name: $name,
+ type: $type,
+ mac: $mac,
+ ip_address: ( $ip | select(. != "") ),
+ speed_mbps: ( ( $speed | tonumber? ) // null ),
+ driver: ( $driver | select(. != "") ),
+ ssid: ( $ssid | select(. != "") ),
+ wake_on_lan: $wol
+ }' 2>/dev/null || echo '{}')
+
+ # Vérifier que net_json est valide avant de l'ajouter
+ if [[ "$net_json" != "{}" ]] && echo "$net_json" | jq empty 2>/dev/null; then
+ network_array=$(echo "$network_array" | jq --argjson net "$net_json" '. + [$net]')
+ else
+ log_warn "Interface $iface: JSON invalide, ignorée"
+ fi
+
+ local log_line="Interface: $iface ($type) - IP: ${ip_addr:-N/A}"
+ if [[ -n "$speed" ]]; then
+ log_line+=" - Speed: ${speed}Mbps"
+ fi
+ if [[ "$type" = "wifi" && -n "$ssid" ]]; then
+ log_line+=" - SSID: $ssid"
+ fi
+ log_info "$log_line"
+ done
+
+ NETWORK_INFO="$network_array"
+ echo ""
+}
+
+#=========================================================
+# Étape 7 : Benchmarks
+#=========================================================
+
+run_benchmarks() {
+ log_step "Exécution des benchmarks (peut prendre plusieurs minutes)"
+
+ # CPU
+ local cpu_bench="null"
+ if command -v sysbench &>/dev/null; then
+ log_info "Benchmark CPU en cours..."
+
+ # Test single-core (1 thread)
+ log_info "→ Test single-core (1 thread)..."
+ local cpu_single
+ cpu_single=$(sysbench cpu --cpu-max-prime=20000 --threads=1 run 2>&1 || true)
+ local eps_single
+ eps_single=$(echo "$cpu_single" | awk '/events per second/ {print $4}')
+ [[ -z "$eps_single" ]] && eps_single=0
+ local cpu_score_single
+ cpu_score_single=$(safe_bc "scale=2; $eps_single / 100")
+ log_info " Single-core: ${eps_single} events/sec (score: ${cpu_score_single})"
+
+ # Test multi-core (tous les threads)
+ log_info "→ Test multi-core ($(nproc) threads)..."
+ local cpu_multi
+ cpu_multi=$(sysbench cpu --cpu-max-prime=20000 --threads="$(nproc)" run 2>&1 || true)
+ local eps_multi time_s
+ eps_multi=$(echo "$cpu_multi" | awk '/events per second/ {print $4}')
+ time_s=$(echo "$cpu_multi" | awk '/total time:/ {gsub(/s/,"",$3); print $3}')
+ [[ -z "$eps_multi" ]] && eps_multi=0
+ [[ -z "$time_s" ]] && time_s=0
+ local cpu_score_multi
+ cpu_score_multi=$(safe_bc "scale=2; $eps_multi / 100")
+ log_info " Multi-core: ${eps_multi} events/sec (score: ${cpu_score_multi})"
+
+ # Score global = moyenne des deux
+ local cpu_score
+ cpu_score=$(safe_bc "scale=2; ($cpu_score_single + $cpu_score_multi) / 2")
+
+ cpu_bench=$(jq -n \
+ --arg eps "$eps_multi" \
+ --arg eps_single "$eps_single" \
+ --arg eps_multi "$eps_multi" \
+ --arg time "$time_s" \
+ --arg score "$cpu_score" \
+ --arg score_single "$cpu_score_single" \
+ --arg score_multi "$cpu_score_multi" \
+ '{
+ events_per_sec: ($eps | tonumber? // 0),
+ events_per_sec_single: ($eps_single | tonumber? // 0),
+ events_per_sec_multi: ($eps_multi | tonumber? // 0),
+ duration_s: ($time | tonumber? // 0),
+ score: ($score | tonumber? // 0),
+ score_single: ($score_single | tonumber? // 0),
+ score_multi: ($score_multi | tonumber? // 0)
+ }')
+
+ log_info "CPU Global: score ${cpu_score} (single: ${cpu_score_single}, multi: ${cpu_score_multi})"
+ else
+ log_warn "sysbench non disponible - CPU bench ignoré"
+ fi
+
+ # Mémoire
+ local mem_bench="null"
+ if command -v sysbench &>/dev/null; then
+ log_info "Benchmark Mémoire en cours..."
+ local mem_res
+ mem_res=$(sysbench memory --memory-total-size=1G run 2>&1 || true)
+ local thr
+ # Extraire le throughput - essayer plusieurs patterns
+ thr=$(echo "$mem_res" | grep -oP '\d+\.\d+(?= MiB/sec)' | head -1)
+ [[ -z "$thr" ]] && thr=$(echo "$mem_res" | awk '/transferred/ {print $(NF-1)}' | head -1)
+ [[ -z "$thr" ]] && thr=0
+ local mem_score
+ mem_score=$(safe_bc "scale=2; $thr / 100")
+
+ mem_bench=$(jq -n \
+ --arg thr "$thr" \
+ --arg score "$mem_score" \
+ '{
+ throughput_mib_s: ($thr | tonumber? // 0),
+ score: ($score | tonumber? // 0)
+ }')
+
+ log_info "Mémoire: ${thr} MiB/s (score: ${mem_score})"
+ else
+ log_warn "sysbench non disponible - Memory bench ignoré"
+ fi
+
+ # Disque
+ local disk_bench="null"
+ if command -v fio &>/dev/null; then
+ log_info "Benchmark Disque en cours (2–3 minutes)..."
+ local tmpdir
+ tmpdir=$(mktemp -d /tmp/fio-bench-XXXX)
+ local fio_res
+ fio_res=$(fio --name=bench --ioengine=libaio --rw=randrw --bs=4k \
+ --size=500M --numjobs=1 --direct=1 --runtime=30 --time_based \
+ --filename="$tmpdir/testfile" --output-format=json 2>/dev/null || echo '{}')
+ rm -rf "$tmpdir"
+
+ if [[ "$fio_res" != "{}" ]]; then
+ local rbw wbw riops wiops lat_ns
+ rbw=$(echo "$fio_res" | jq '.jobs[0].read.bw // 0')
+ wbw=$(echo "$fio_res" | jq '.jobs[0].write.bw // 0')
+ riops=$(echo "$fio_res" | jq '.jobs[0].read.iops // 0' | cut -d. -f1)
+ wiops=$(echo "$fio_res" | jq '.jobs[0].write.iops // 0' | cut -d. -f1)
+ lat_ns=$(echo "$fio_res" | jq '.jobs[0].read.clat_ns.mean // 0')
+
+ local rmb wmb lat_ms
+ rmb=$(safe_bc "scale=2; $rbw / 1024")
+ wmb=$(safe_bc "scale=2; $wbw / 1024")
+ lat_ms=$(safe_bc "scale=3; $lat_ns / 1000000")
+
+ local disk_score
+ disk_score=$(safe_bc "scale=2; ($rmb + $wmb) / 20")
+
+ disk_bench=$(jq -n \
+ --arg rmb "$rmb" \
+ --arg wmb "$wmb" \
+ --arg riops "$riops" \
+ --arg wiops "$wiops" \
+ --arg lat_ms "$lat_ms" \
+ --arg score "$disk_score" \
+ '{
+ read_mb_s: ($rmb | tonumber? // 0),
+ write_mb_s: ($wmb | tonumber? // 0),
+ iops_read: ($riops | tonumber? // 0),
+ iops_write: ($wiops | tonumber? // 0),
+ latency_ms: ($lat_ms | tonumber? // 0),
+ score: ($score | tonumber? // 0)
+ }')
+
+ log_info "Disque: R=${rmb}MB/s W=${wmb}MB/s (score: ${disk_score})"
+ else
+ log_warn "Échec du benchmark disque (fio)"
+ fi
+ else
+ log_warn "fio non disponible - Disk bench ignoré"
+ fi
+
+ # Réseau (iperf3)
+ local net_bench="null"
+ if command -v iperf3 &>/dev/null; then
+ local target="$IPERF_SERVER"
+ [[ -z "$target" ]] && target="10.0.1.97"
+
+ log_info "Benchmark Réseau en cours (vers $target)..."
+
+ if ping -c 1 -W 1 "$target" &>/dev/null; then
+ if nc -z "$target" 5201 &>/dev/null; then
+ # Test bidirectionnel (upload + download simultanés)
+ local bidir_result=$(iperf3 -c "$target" -t 10 --bidir -J 2>/dev/null || echo '{}')
+
+ local upload_mbps="0"
+ local download_mbps="0"
+ local ping_ms="null"
+
+ if [[ "$bidir_result" != "{}" ]]; then
+ # Extraire upload (end.sum_sent) et download (end.sum_received)
+ local upload_bps=$(echo "$bidir_result" | jq -r '.end.sum_sent.bits_per_second // 0' | tr -d '\n')
+ local download_bps=$(echo "$bidir_result" | jq -r '.end.sum_received.bits_per_second // 0' | tr -d '\n')
+
+ upload_mbps=$(safe_bc "scale=2; $upload_bps / 1000000" | tr -d '\n')
+ download_mbps=$(safe_bc "scale=2; $download_bps / 1000000" | tr -d '\n')
+
+ # Mesurer le ping
+ local ping_output=$(ping -c 5 "$target" 2>/dev/null | grep 'avg' || echo "")
+ if [[ -n "$ping_output" ]]; then
+ ping_ms=$(echo "$ping_output" | awk -F'/' '{print $5}' | tr -d '\n')
+ fi
+
+ # Score réseau
+ local net_score=$(safe_bc "scale=2; ($upload_mbps + $download_mbps) / 20" | tr -d '\n')
+
+ # S'assurer que les valeurs sont valides pour jq
+ [[ -z "$upload_mbps" || "$upload_mbps" == "null" ]] && upload_mbps="0"
+ [[ -z "$download_mbps" || "$download_mbps" == "null" ]] && download_mbps="0"
+ [[ -z "$ping_ms" || "$ping_ms" == "null" ]] && ping_ms="0"
+ [[ -z "$net_score" || "$net_score" == "null" ]] && net_score="0"
+
+ net_bench=$(jq -n \
+ --argjson upload "$upload_mbps" \
+ --argjson download "$download_mbps" \
+ --argjson ping "$ping_ms" \
+ --argjson score "$net_score" \
+ '{upload_mbps: $upload, download_mbps: $download, ping_ms: $ping, score: $score}')
+
+ log_info "Réseau: ↑${upload_mbps}Mbps ↓${download_mbps}Mbps (ping: ${ping_ms}ms, score: ${net_score})"
+ else
+ log_warn "Test iperf3 bidirectionnel échoué - Network bench ignoré"
+ fi
+ else
+ log_warn "Port iperf3 (5201) fermé sur $target - Network bench ignoré"
+ fi
+ else
+ log_warn "Hôte $target non joignable - Network bench ignoré"
+ fi
+ else
+ log_warn "iperf3 non disponible - Network bench ignoré"
+ fi
+
+ # GPU bench non implémenté
+ local gpu_bench="null"
+ log_warn "GPU bench non implémenté - ignoré"
+
+ # Score global selon pondérations recommandées :
+ # CPU 30%, RAM 20%, Disque 25%, Réseau 15%, GPU 10%
+ local scores="" total_weight=0
+
+ if [[ "$cpu_bench" != "null" ]]; then
+ local cs
+ cs=$(echo "$cpu_bench" | jq '.score // 0')
+ scores="$scores + $cs * 0.30"
+ total_weight=$(safe_bc "$total_weight + 0.30")
+ fi
+
+ if [[ "$mem_bench" != "null" ]]; then
+ local ms
+ ms=$(echo "$mem_bench" | jq '.score // 0')
+ scores="$scores + $ms * 0.20"
+ total_weight=$(safe_bc "$total_weight + 0.20")
+ fi
+
+ if [[ "$disk_bench" != "null" ]]; then
+ local ds
+ ds=$(echo "$disk_bench" | jq '.score // 0')
+ scores="$scores + $ds * 0.25"
+ total_weight=$(safe_bc "$total_weight + 0.25")
+ fi
+
+ if [[ "$net_bench" != "null" ]]; then
+ local ns
+ ns=$(echo "$net_bench" | jq '.score // 0')
+ scores="$scores + $ns * 0.15"
+ total_weight=$(safe_bc "$total_weight + 0.15")
+ fi
+
+ if [[ "$gpu_bench" != "null" ]]; then
+ local gs
+ gs=$(echo "$gpu_bench" | jq '.score // 0')
+ scores="$scores + $gs * 0.10"
+ total_weight=$(safe_bc "$total_weight + 0.10")
+ fi
+
+ scores=$(echo "$scores" | sed -E 's/^[[:space:]]*\+ //')
+ local global_score=0
+ if [[ -n "$scores" && "$total_weight" != "0" ]]; then
+ global_score=$(safe_bc "scale=2; ($scores) / $total_weight")
+ fi
+ [[ -z "$global_score" ]] && global_score=0
+
+ BENCHMARK_RESULTS=$(jq -n \
+ --argjson cpu "$cpu_bench" \
+ --argjson mem "$mem_bench" \
+ --argjson disk "$disk_bench" \
+ --argjson net "$net_bench" \
+ --argjson gpu "$gpu_bench" \
+ --argjson global "$global_score" \
+ '{
+ cpu: $cpu,
+ memory: $mem,
+ disk: $disk,
+ network: $net,
+ gpu: $gpu,
+ global_score: $global
+ }')
+
+ log_info "Score global: ${global_score}/100"
+ echo ""
+}
+
+#=========================================================
+# Envoi du payload JSON
+#=========================================================
+
+send_benchmark_payload() {
+ log_step "Construction du payload JSON et envoi au serveur"
+
+ # Si SERVER_URL n'a pas de schéma, on préfixe par http://
+ local base_url="$SERVER_URL"
+ if [[ "$base_url" != http*://* ]]; then
+ base_url="http://$base_url"
+ fi
+ local api_url="${base_url%/}/api/benchmark"
+
+ # Validation des variables JSON avant envoi (éviter les variables vides qui causent des erreurs jq)
+ # Si DEBUG_PAYLOAD=1, on affiche les variables pour diagnostic
+ if [[ "${DEBUG_PAYLOAD:-0}" == "1" ]]; then
+ echo -e "${BLUE}════════════════════════════════════════════════════════${NC}"
+ echo -e "${YELLOW}DEBUG: Validation des variables JSON${NC}"
+ echo -e "${BLUE}════════════════════════════════════════════════════════${NC}"
+ for var in SYSTEM_INFO CPU_INFO RAM_INFO GPU_INFO MOTHERBOARD_INFO STORAGE_INFO PARTITION_INFO NETWORK_INFO PCI_INFO USB_INFO NETWORK_SHARES_INFO BENCHMARK_RESULTS; do
+ local val="${!var}"
+ if [[ -z "$val" ]]; then
+ echo -e "${RED}✗ $var est VIDE${NC}"
+ elif echo "$val" | jq empty 2>/dev/null; then
+ echo -e "${GREEN}✓ $var est JSON valide${NC} (${#val} chars)"
+ else
+ echo -e "${RED}✗ $var est JSON INVALIDE${NC}: ${val:0:100}..."
+ fi
+ done
+ echo -e "${BLUE}════════════════════════════════════════════════════════${NC}"
+ echo ""
+ fi
+
+ [[ -z "$SYSTEM_INFO" ]] && SYSTEM_INFO="null"
+ [[ -z "$CPU_INFO" ]] && CPU_INFO="null"
+ [[ -z "$RAM_INFO" ]] && RAM_INFO="null"
+ [[ -z "$GPU_INFO" ]] && GPU_INFO="null"
+ [[ -z "$MOTHERBOARD_INFO" ]] && MOTHERBOARD_INFO="null"
+ [[ -z "$STORAGE_INFO" ]] && STORAGE_INFO="[]"
+ [[ -z "$PARTITION_INFO" ]] && PARTITION_INFO="[]"
+ [[ -z "$NETWORK_INFO" ]] && NETWORK_INFO="[]"
+ [[ -z "$PCI_INFO" ]] && PCI_INFO="[]"
+ [[ -z "$USB_INFO" ]] && USB_INFO="[]"
+ [[ -z "$NETWORK_SHARES_INFO" ]] && NETWORK_SHARES_INFO="[]"
+ [[ -z "$BENCHMARK_RESULTS" ]] && BENCHMARK_RESULTS="null"
+
+ local payload
+ payload=$(
+ jq -n \
+ --arg version "$BENCH_SCRIPT_VERSION" \
+ --argjson system "$SYSTEM_INFO" \
+ --argjson cpu "$CPU_INFO" \
+ --argjson ram "$RAM_INFO" \
+ --argjson gpu "$GPU_INFO" \
+ --argjson mb "$MOTHERBOARD_INFO" \
+ --argjson storage "$STORAGE_INFO" \
+ --argjson partitions "$PARTITION_INFO" \
+ --argjson network "$NETWORK_INFO" \
+ --argjson pci "$PCI_INFO" \
+ --argjson usb "$USB_INFO" \
+ --argjson shares "$NETWORK_SHARES_INFO" \
+ --argjson bench "$BENCHMARK_RESULTS" \
+ '
+{
+ device_identifier: $system.hostname,
+ bench_script_version: $version,
+
+ hardware: {
+ cpu: ($cpu + {
+ microarchitecture: null,
+ tdp_w: null
+ }),
+
+ ram: (
+ $ram
+ | {
+ total_mb: .total_mb,
+ used_mb: .used_mb,
+ free_mb: .free_mb,
+ shared_mb: .shared_mb,
+ slots_total: .slots_total,
+ slots_used: .slots_used,
+ ecc: .ecc,
+ layout: [
+ .layout[]
+ | {
+ slot,
+ size_mb,
+ type,
+ speed_mhz,
+ vendor: .manufacturer,
+ part_number: null
+ }
+ ]
+ }
+ ),
+
+ gpu: (
+ if $gpu == null then
+ {
+ vendor: null,
+ model: null,
+ driver_version: null,
+ memory_dedicated_mb: null,
+ memory_shared_mb: null,
+ api_support: []
+ }
+ else
+ {
+ vendor: $gpu.vendor,
+ model: $gpu.model,
+ driver_version: null,
+ memory_dedicated_mb: null,
+ memory_shared_mb: null,
+ api_support: []
+ }
+ end
+ ),
+
+ storage: {
+ devices: [
+ $storage[]
+ | {
+ name: ("/dev/" + .device),
+ type: (.type | ascii_upcase),
+ interface,
+ capacity_gb: (.size_gb | tonumber? // .size_gb),
+ vendor: null,
+ model,
+ smart_health,
+ temperature_c
+ }
+ ],
+ partitions: $partitions
+ },
+
+ pci_devices: $pci,
+ usb_devices: $usb,
+ network_shares: $shares,
+
+ network: {
+ interfaces: [
+ $network[]
+ | {
+ name,
+ type,
+ mac,
+ ip: .ip_address,
+ speed_mbps,
+ driver: (.driver // null),
+ ssid: (.ssid // null),
+ wake_on_lan
+ }
+ ]
+ },
+
+ motherboard: {
+ vendor: $mb.manufacturer,
+ model: $mb.model,
+ bios_vendor: $mb.bios_vendor,
+ bios_version: $mb.bios_version,
+ bios_date: $mb.bios_date
+ },
+
+ os: (
+ $system.os
+ + {
+ hostname: $system.hostname,
+ virtualization_type: "none",
+ desktop_environment: (.session_type // .display_server // null)
+ }
+ ),
+
+ sensors: {
+ cpu_temp_c: null,
+ disk_temps_c: {}
+ },
+
+ raw_info: {
+ lscpu: null,
+ lsblk: null
+ }
+ },
+
+ results: (
+ $bench as $b
+ | {
+ cpu: $b.cpu,
+ memory: $b.memory,
+ disk: $b.disk,
+ network: (
+ $b.network
+ + {
+ jitter_ms: null,
+ packet_loss_percent: null
+ }
+ ),
+ gpu: (
+ if $b.gpu == null then
+ { glmark2_score: null, score: null }
+ else
+ $b.gpu
+ end
+ ),
+ global_score: $b.global_score
+ }
+ )
+}
+'
+ )
+
+ # Debug : afficher le payload complet si DEBUG_PAYLOAD=1
+ if [[ "${DEBUG_PAYLOAD:-0}" == "1" ]]; then
+ echo ""
+ echo -e "${BLUE}════════════════════════════════════════════════════════${NC}"
+ echo -e "${YELLOW}DEBUG: Payload JSON complet${NC}"
+ echo -e "${BLUE}════════════════════════════════════════════════════════${NC}"
+ echo "$payload" | jq '.'
+ echo -e "${BLUE}════════════════════════════════════════════════════════${NC}"
+ echo ""
+
+ # Sauvegarder le payload dans un fichier avec timestamp
+ local debug_file="/tmp/bench_payload_$(date +%Y%m%d_%H%M%S).json"
+ echo "$payload" | jq '.' > "$debug_file"
+ echo -e "${GREEN}✓${NC} Payload sauvegardé dans: ${debug_file}"
+ echo ""
+
+ # Demander confirmation seulement si on a un terminal interactif
+ if [[ -t 0 ]]; then
+ read -p "Appuyez sur Entrée pour continuer l'envoi ou Ctrl+C pour annuler..."
+ else
+ log_warn "Mode non-interactif détecté - envoi automatique dans 2 secondes..."
+ sleep 2
+ fi
+ fi
+
+ log_info "Envoi du payload vers: $api_url"
+
+ local tmp_resp http_code
+ tmp_resp=$(mktemp /tmp/bench_response_XXXXXX.json)
+
+ # IMPORTANT : on envoie le payload via stdin (pas de @<(...) foireux)
+ http_code=$(
+ printf '%s\n' "$payload" | curl -sS -o "$tmp_resp" -w "%{http_code}" \
+ -X POST "$api_url" \
+ -H "Content-Type: application/json" \
+ -H "Authorization: Bearer $API_TOKEN" \
+ --data-binary @-
+ )
+
+ if [[ "$http_code" != "200" && "$http_code" != "201" ]]; then
+ log_error "Erreur lors de l'envoi (HTTP $http_code)"
+ echo "Réponse serveur :"
+ cat "$tmp_resp"
+ rm -f "$tmp_resp"
+ exit 1
+ fi
+
+ log_info "Payload envoyé avec succès (HTTP $http_code)"
+ rm -f "$tmp_resp"
+ echo ""
+}
+
+#=========================================================
+# MAIN
+#=========================================================
+
parse_args() {
while [[ $# -gt 0 ]]; do
- case $1 in
+ case "$1" in
--server)
SERVER_URL="$2"
shift 2
@@ -86,400 +1615,55 @@ parse_args() {
API_TOKEN="$2"
shift 2
;;
- --device)
- DEVICE_IDENTIFIER="$2"
- shift 2
- ;;
--iperf-server)
IPERF_SERVER="$2"
shift 2
;;
- --short)
- SHORT_MODE=true
+ --debug)
+ DEBUG_PAYLOAD=1
shift
;;
- --skip-cpu)
- SKIP_CPU=true
- shift
- ;;
- --skip-memory)
- SKIP_MEMORY=true
- shift
- ;;
- --skip-disk)
- SKIP_DISK=true
- shift
- ;;
- --skip-network)
- SKIP_NETWORK=true
- shift
- ;;
- --skip-gpu)
- SKIP_GPU=true
- shift
- ;;
- --help)
- show_usage
+ --help|-h)
+ echo "Usage: $0 [OPTIONS]"
+ echo ""
+ echo "Options:"
+ echo " --server URL Backend server URL (default: $SERVER_URL)"
+ echo " --token TOKEN API authentication token"
+ echo " --iperf-server IP iperf3 server IP (default: $IPERF_SERVER)"
+ echo " --debug Enable debug mode (shows full JSON payload)"
+ echo " --help, -h Show this help message"
+ echo ""
exit 0
;;
*)
- log_error "Unknown option: $1"
- show_usage
- exit 1
+ log_warn "Option inconnue: $1 (ignorée)"
+ shift
;;
esac
done
-
- # Validate required parameters
- if [[ -z "$SERVER_URL" || -z "$API_TOKEN" ]]; then
- log_error "Missing required parameters: --server and --token"
- show_usage
- exit 1
- fi
-
- # Set device identifier to hostname if not provided
- if [[ -z "$DEVICE_IDENTIFIER" ]]; then
- DEVICE_IDENTIFIER=$(hostname)
- fi
}
-# Check dependencies (no automatic installation)
-check_dependencies() {
- log_info "Checking dependencies..."
-
- local missing_essential=()
- local missing_optional=()
-
- # Essential tools (required for hardware detection)
- for tool in curl jq; do
- if ! command -v $tool &> /dev/null; then
- missing_essential+=($tool)
- fi
- done
-
- # Optional hardware detection tools
- for tool in lscpu free dmidecode lsblk; do
- if ! command -v $tool &> /dev/null; then
- missing_optional+=($tool)
- fi
- done
-
- # Optional benchmark tools
- if [[ "$SKIP_CPU" == false ]] && ! command -v sysbench &> /dev/null; then
- missing_optional+=(sysbench)
- SKIP_CPU=true
- log_warn "sysbench not found - CPU benchmark will be skipped"
- fi
-
- if [[ "$SKIP_DISK" == false ]] && ! command -v fio &> /dev/null; then
- missing_optional+=(fio)
- SKIP_DISK=true
- log_warn "fio not found - Disk benchmark will be skipped"
- fi
-
- if [[ "$SKIP_NETWORK" == false && -n "$IPERF_SERVER" ]] && ! command -v iperf3 &> /dev/null; then
- missing_optional+=(iperf3)
- SKIP_NETWORK=true
- log_warn "iperf3 not found - Network benchmark will be skipped"
- fi
-
- # Check essential dependencies
- if [[ ${#missing_essential[@]} -gt 0 ]]; then
- log_error "Missing essential dependencies: ${missing_essential[*]}"
- log_error "Please install them manually. Example (Debian/Ubuntu):"
- log_error " sudo apt-get install ${missing_essential[*]}"
- exit 1
- fi
-
- # Info about optional dependencies
- if [[ ${#missing_optional[@]} -gt 0 ]]; then
- log_warn "Missing optional tools: ${missing_optional[*]}"
- log_info "Some hardware info or benchmarks may be limited"
- log_info "To install (Debian/Ubuntu): sudo apt-get install ${missing_optional[*]}"
- fi
-
- log_info "Dependency check completed"
-}
-
-# Collect CPU information
-collect_cpu_info() {
- local cpu_json="{}"
-
- cpu_json=$(jq -n \
- --arg vendor "$(lscpu | grep 'Vendor ID' | awk '{print $3}' || echo 'Unknown')" \
- --arg model "$(lscpu | grep 'Model name' | sed 's/Model name: *//')" \
- --argjson cores "$(lscpu | grep '^CPU(s):' | awk '{print $2}')" \
- --argjson threads "$(nproc)" \
- '{
- vendor: $vendor,
- model: $model,
- cores: $cores,
- threads: $threads
- }'
- )
-
- echo "$cpu_json"
-}
-
-# Collect RAM information
-collect_ram_info() {
- local ram_total_mb=$(free -m | grep '^Mem:' | awk '{print $2}')
-
- local ram_json=$(jq -n \
- --argjson total_mb "$ram_total_mb" \
- '{
- total_mb: $total_mb
- }'
- )
-
- echo "$ram_json"
-}
-
-# Collect OS information
-collect_os_info() {
- local os_name=$(grep '^ID=' /etc/os-release | cut -d= -f2 | tr -d '"')
- local os_version=$(grep '^VERSION=' /etc/os-release | cut -d= -f2 | tr -d '"')
- local kernel=$(uname -r)
- local arch=$(uname -m)
-
- local os_json=$(jq -n \
- --arg name "$os_name" \
- --arg version "$os_version" \
- --arg kernel_version "$kernel" \
- --arg architecture "$arch" \
- '{
- name: $name,
- version: $version,
- kernel_version: $kernel_version,
- architecture: $architecture
- }'
- )
-
- echo "$os_json"
-}
-
-# Run CPU benchmark
-run_cpu_benchmark() {
- if [[ "$SKIP_CPU" == true ]]; then
- echo "null"
- return
- fi
-
- log_info "Running CPU benchmark..."
-
- local prime=10000
- [[ "$SHORT_MODE" == false ]] && prime=20000
-
- local result=$(sysbench cpu --cpu-max-prime=$prime --threads=$(nproc) run 2>&1)
-
- local events_per_sec=$(echo "$result" | grep 'events per second' | awk '{print $4}')
- local score=$(echo "scale=2; $events_per_sec / 100" | bc)
-
- local cpu_result=$(jq -n \
- --argjson events_per_sec "${events_per_sec:-0}" \
- --argjson score "${score:-0}" \
- '{
- events_per_sec: $events_per_sec,
- score: $score
- }'
- )
-
- echo "$cpu_result"
-}
-
-# Run memory benchmark
-run_memory_benchmark() {
- if [[ "$SKIP_MEMORY" == true ]]; then
- echo "null"
- return
- fi
-
- log_info "Running memory benchmark..."
-
- local size="512M"
- [[ "$SHORT_MODE" == false ]] && size="2G"
-
- local result=$(sysbench memory --memory-total-size=$size --memory-oper=write run 2>&1)
-
- local throughput=$(echo "$result" | grep 'transferred' | awk '{print $4}')
- local score=$(echo "scale=2; $throughput / 200" | bc)
-
- local mem_result=$(jq -n \
- --argjson throughput_mib_s "${throughput:-0}" \
- --argjson score "${score:-0}" \
- '{
- throughput_mib_s: $throughput_mib_s,
- score: $score
- }'
- )
-
- echo "$mem_result"
-}
-
-# Run disk benchmark
-run_disk_benchmark() {
- if [[ "$SKIP_DISK" == true ]]; then
- echo "null"
- return
- fi
-
- log_info "Running disk benchmark..."
-
- local size="256M"
- [[ "$SHORT_MODE" == false ]] && size="1G"
-
- local tmpfile="/tmp/fio_benchfile_$$"
-
- fio --name=bench --rw=readwrite --bs=1M --size=$size --numjobs=1 \
- --iodepth=16 --filename=$tmpfile --direct=1 --group_reporting \
- --output-format=json > /tmp/fio_result_$$.json 2>&1
-
- local read_mb_s=$(jq -r '.jobs[0].read.bw_bytes / 1048576' /tmp/fio_result_$$.json 2>/dev/null || echo 0)
- local write_mb_s=$(jq -r '.jobs[0].write.bw_bytes / 1048576' /tmp/fio_result_$$.json 2>/dev/null || echo 0)
- local score=$(echo "scale=2; ($read_mb_s + $write_mb_s) / 20" | bc)
-
- rm -f $tmpfile /tmp/fio_result_$$.json
-
- local disk_result=$(jq -n \
- --argjson read_mb_s "${read_mb_s:-0}" \
- --argjson write_mb_s "${write_mb_s:-0}" \
- --argjson score "${score:-0}" \
- '{
- read_mb_s: $read_mb_s,
- write_mb_s: $write_mb_s,
- score: $score
- }'
- )
-
- echo "$disk_result"
-}
-
-# Run network benchmark
-run_network_benchmark() {
- if [[ "$SKIP_NETWORK" == true || -z "$IPERF_SERVER" ]]; then
- echo "null"
- return
- fi
-
- log_info "Running network benchmark..."
-
- local download=$(iperf3 -c "$IPERF_SERVER" -R -J 2>/dev/null | jq -r '.end.sum_received.bits_per_second / 1000000' 2>/dev/null || echo 0)
- local upload=$(iperf3 -c "$IPERF_SERVER" -J 2>/dev/null | jq -r '.end.sum_sent.bits_per_second / 1000000' 2>/dev/null || echo 0)
-
- local score=$(echo "scale=2; ($download + $upload) / 20" | bc)
-
- local net_result=$(jq -n \
- --argjson download_mbps "${download:-0}" \
- --argjson upload_mbps "${upload:-0}" \
- --argjson score "${score:-0}" \
- '{
- download_mbps: $download_mbps,
- upload_mbps: $upload_mbps,
- score: $score
- }'
- )
-
- echo "$net_result"
-}
-
-# Calculate global score
-calculate_global_score() {
- local cpu_score=$1
- local mem_score=$2
- local disk_score=$3
- local net_score=$4
-
- local global=$(echo "scale=2; ($cpu_score * 0.3) + ($mem_score * 0.2) + ($disk_score * 0.25) + ($net_score * 0.15)" | bc)
-
- echo "$global"
-}
-
-# Build and send JSON payload
-send_benchmark() {
- log_info "Collecting hardware information..."
-
- local cpu_info=$(collect_cpu_info)
- local ram_info=$(collect_ram_info)
- local os_info=$(collect_os_info)
-
- log_info "Running benchmarks..."
-
- local cpu_result=$(run_cpu_benchmark)
- local memory_result=$(run_memory_benchmark)
- local disk_result=$(run_disk_benchmark)
- local network_result=$(run_network_benchmark)
-
- # Extract scores
- local cpu_score=$(echo "$cpu_result" | jq -r '.score // 0' 2>/dev/null || echo 0)
- local mem_score=$(echo "$memory_result" | jq -r '.score // 0' 2>/dev/null || echo 0)
- local disk_score=$(echo "$disk_result" | jq -r '.score // 0' 2>/dev/null || echo 0)
- local net_score=$(echo "$network_result" | jq -r '.score // 0' 2>/dev/null || echo 0)
-
- # Calculate global score
- local global_score=$(calculate_global_score "$cpu_score" "$mem_score" "$disk_score" "$net_score")
-
- log_info "Building JSON payload..."
-
- local payload=$(jq -n \
- --arg device_identifier "$DEVICE_IDENTIFIER" \
- --arg bench_script_version "$BENCH_SCRIPT_VERSION" \
- --argjson cpu "$cpu_info" \
- --argjson ram "$ram_info" \
- --argjson os "$os_info" \
- --argjson cpu_result "$cpu_result" \
- --argjson memory_result "$memory_result" \
- --argjson disk_result "$disk_result" \
- --argjson network_result "$network_result" \
- --argjson global_score "$global_score" \
- '{
- device_identifier: $device_identifier,
- bench_script_version: $bench_script_version,
- hardware: {
- cpu: $cpu,
- ram: $ram,
- os: $os
- },
- results: {
- cpu: $cpu_result,
- memory: $memory_result,
- disk: $disk_result,
- network: $network_result,
- gpu: null,
- global_score: $global_score
- }
- }'
- )
-
- log_info "Sending results to server..."
-
- local response=$(curl -s -w "\n%{http_code}" \
- -X POST "$SERVER_URL" \
- -H "Content-Type: application/json" \
- -H "Authorization: Bearer $API_TOKEN" \
- -d "$payload")
-
- local http_code=$(echo "$response" | tail -n1)
- local body=$(echo "$response" | head -n-1)
-
- if [[ "$http_code" == "200" ]]; then
- log_info "Benchmark submitted successfully!"
- log_info "Response: $body"
- else
- log_error "Failed to submit benchmark (HTTP $http_code)"
- log_error "Response: $body"
- exit 1
- fi
-}
-
-# Main execution
main() {
- log_info "Linux BenchTools Client v${BENCH_SCRIPT_VERSION}"
-
parse_args "$@"
+ check_sudo
check_dependencies
- send_benchmark
- log_info "Benchmark completed!"
+ echo -e "${BLUE}Début de la collecte et des benchmarks...${NC}"
+ echo ""
+
+ collect_system_info
+ collect_cpu_info
+ collect_ram_info
+ collect_hardware_info
+ collect_storage_info
+ collect_network_info
+ run_benchmarks
+ send_benchmark_payload
+
+ echo -e "${GREEN}════════════════════════════════════════════════════════${NC}"
+ echo -e "${GREEN} Benchmark terminé avec succès !${NC}"
+ echo -e "${GREEN}════════════════════════════════════════════════════════${NC}"
+ echo ""
}
main "$@"
diff --git a/frontend/settings.html b/frontend/settings.html
old mode 100644
new mode 100755
index 110c1cd..f9ce033
--- a/frontend/settings.html
+++ b/frontend/settings.html
@@ -6,6 +6,8 @@
Settings - Linux BenchTools
+
+
@@ -25,6 +27,78 @@
+
+
+
+
+
+ Configurez les unités d'affichage pour les valeurs matérielles
+
+
+
+ Unité de mémoire (RAM)
+
+ Mégaoctets (MB)
+ Gigaoctets (GB)
+
+ Affiche la RAM en MB ou GB dans les sections matérielles
+
+
+
+ Unité de stockage (Disques)
+
+ Mégaoctets (MB)
+ Gigaoctets (GB)
+ Téraoctets (TB)
+
+ Affiche la capacité des disques en MB, GB ou TB
+
+
+
+ Unité de cache CPU
+
+ Kilooctets (KB)
+ Mégaoctets (MB)
+
+ Affiche les caches L1/L2/L3 en KB ou MB
+
+
+
+ Unité de température
+
+ Celsius (°C)
+ Fahrenheit (°F)
+
+ Affiche la température des composants en Celsius ou Fahrenheit
+
+
+
+ Taille des icônes de section
+
+ Petite (24px)
+ Moyenne (28px)
+ Grande (32px)
+ Très grande (36px)
+
+ Taille des icônes dans les titres de sections
+
+
+
+ Taille des icônes de bouton
+
+ Petite (18px)
+ Moyenne (22px)
+ Grande (24px)
+ Très grande (28px)
+
+ Taille des icônes dans les boutons d'action
+
+
+
💾 Enregistrer les préférences
+
🔄 Réinitialiser
+
+
+
@@ -185,6 +259,7 @@
+
diff --git a/hardinfo2_report.html b/hardinfo2_report.html
new file mode 100755
index 0000000..4f4f254
--- /dev/null
+++ b/hardinfo2_report.html
@@ -0,0 +1,4493 @@
+
+
+
HardInfo (2.2.10) System Report
+
+
+
+
Ordinateur Résumé
+Ordinateur
+Processeur Intel Core i5-6200U
+1 processeur physique; 2 coeurs; 4 threads
+Mémoire 8017MB (4405MB utilisées)
+Type de machine Convertible
+Systeme d'exploitation Debian GNU/Linux 13.1
+Utilisateur gilles (gilles)
+Date/Heure mer. 17 déc. 2025 19:37:14
+Affichage
+Résolution 3072x1728 pixels
+Display Adapter Intel Skylake GT2 [HD Graphics 520]
+OpenGL Rendu Mesa Intel(R) HD Graphics 520 (SKL GT2)
+Serveur d'affichage de session Wayland
+Matériels Audio
+Adapteur Audio HDA-Intel - HDA Intel PCH
+Adapteur Audio ThinkPad EC - ThinkPad Console Audio Control
+Périphériques d'entrée
+ Lid Switch Audio
+ Sleep Button Keyboard
+ Power Button Keyboard
+ AT Translated Set 2 keyboard Keyboard
+ ETPS/2 Elantech TrackPoint Mouse
+ ETPS/2 Elantech Touchpad Mouse
+ Wacom Pen and multitouch sensor Finger Mouse
+ Video Bus Keyboard
+ Wacom Pen and multitouch sensor Pen Mouse
+ PC Speaker Speaker
+ ThinkPad Extra Buttons Keyboard
+ HDA Intel PCH Mic Audio
+ HDA Intel PCH Headphone Audio
+ HDA Intel PCH HDMI/DP,pcm:3 Audio
+ HDA Intel PCH HDMI/DP,pcm:7 Audio
+ HDA Intel PCH HDMI/DP,pcm:8 Audio
+Imprimantes
+Aucune imprimante trouvée
+UDisks2
+sda Micron CT480BX500SSD1
+
Systeme d'exploitation
+Version
+Kernel Linux 6.17.11-1-liquorix-amd64 (x86_64)
+Command Line audit=0 intel_pstate=disable amd_pstate=disable BOOT_IMAGE=/boot/vmlinuz-6.17.11-1-liquorix-amd64 root=UUID=696c9d58-f384-441c-a64d-06a68b792144 ro quiet
+Version #1 ZEN SMP PREEMPT_DYNAMIC liquorix 6.17-13.1~trixie (2025-12-07
+C Library GNU C Library / (Debian GLIBC 2.41-12) 2.41
+Distribution Debian GNU/Linux 13.1 (trixie)
+Session en cours
+Nom de l'Ordinateur yoga14
+Utilisateur gilles (gilles)
+Langue fr_FR.UTF-8 (fr_FR.UTF-8)
+Dossier Home /home/gilles
+Environnement de bureau GNOME 48.4 on wayland
+Divers
+durée de fonctionnent 1 heure 10 minutes
+Nombre de chargements 0,82, 1,13, 1,39
+
Sécurité
+HardInfo2
+HardInfo2 running as User
+User System Type Single User System
+Health
+Entropie disponible dans /dev/random 256 bits (moyen)
+Hardening Features
+ASLR Fully enabled (mmap base+stack+VDSO base+heap)
+dmesg User access forbidden
+Linux Security Modules
+Modules available capability,yama,bpf
+SELinux status Not installed
+CPU Vulnerabilities
+gather_data_sampling Vulnerable: No microcode
+ghostwrite Not affected
+indirect_target_selection Not affected
+itlb_multihit KVM: Mitigation: Split huge pages
+l1tf Mitigation: PTE Inversion; VMX: conditional cache flushes, SMT vulnerable
+mds Mitigation: Clear CPU buffers; SMT vulnerable
+meltdown Mitigation: PTI
+mmio_stale_data Mitigation: Clear CPU buffers; SMT vulnerable
+old_microcode Not affected
+reg_file_data_sampling Not affected
+retbleed Mitigation: IBRS
+spec_rstack_overflow Not affected
+spec_store_bypass Mitigation: Speculative Store Bypass disabled via prctl
+spectre_v1 Mitigation: usercopy/swapgs barriers and __user pointer sanitization
+spectre_v2 Mitigation: IBRS; IBPB: conditional; STIBP: conditional; RSB filling; PBRSB-eIBRS: Not affected; BHI: Not affected
+srbds Mitigation: Microcode
+tsa Not affected
+tsx_async_abort Not affected
+vmscape Mitigation: IBPB before exit to userspace
+
Modules du kernel
+Modules chargés
+8250_dw Synopsys DesignWare 8250 serial port driver
+ac97_bus Legacy AC97 bus interface
+acpi_pad ACPI Processor Aggregator Driver
+aesni_intel AES cipher and modes, optimized with AES-NI or VAES instructions
+af_alg Crypto userspace interface
+af_packet Packet socket support (AF_PACKET)
+agpgart AGP GART driver
+algif_hash Userspace interface for hash algorithms
+algif_skcipher Userspace interface for skcipher algorithms
+at24 Driver for most I2C EEPROMs
+blake2b_generic BLAKE2b generic implementation
+bluetooth Bluetooth Core ver 2.22
+bnep Bluetooth BNEP ver 1.3
+bridge Ethernet bridge driver
+btbcm Bluetooth support for Broadcom devices ver 0.1
+btintel Bluetooth support for Intel devices ver 0.1
+btmtk Bluetooth support for MediaTek devices ver 0.1
+btrfs B-Tree File System (BTRFS)
+btrtl Bluetooth support for Realtek devices ver 0.1
+btusb Generic Bluetooth USB driver ver 0.8
+ccm Counter with CBC MAC
+cdrom Uniform CD-ROM driver
+cec Device node registration for cec drivers
+cfg80211 wireless configuration support
+cmac CMAC keyed hash algorithm
+coretemp Intel Core temperature monitor
+cpuid x86 generic CPUID driver
+cqhci Command Queue Host Controller Interface driver
+crc16 CRC16 calculations
+dm_mod device-mapper driver
+dmi_sysfs DMI sysfs support
+drm DRM bridge infrastructure
+drm_buddy DRM Buddy Allocator
+drm_client_lib In-kernel DRM clients
+drm_display_helper DRM display adapter helper
+drm_kms_helper DRM KMS helper
+e1000e Intel(R) PRO/1000 Network Driver
+ecdh_generic ECDH generic algorithm
+ee1004 Driver for EE1004-compliant DDR4 SPD EEPROMs
+efi_pstore EFI variable backend for pstore
+ext4 Fourth Extended Filesystem
+fat Core FAT filesystem support
+firmware_attributes_class Firmware attributes class helper module
+fuse Filesystem in Userspace
+ghash_clmulni_intel GHASH hash function, accelerated by PCLMULQDQ-NI
+hfs Apple Macintosh file system support
+hfsplus Extended Macintosh Filesystem
+hid_sensor_accel_3d HID Sensor Accel 3D
+hid_sensor_als HID Sensor ALS
+hid_sensor_gyro_3d HID Sensor Gyroscope 3D
+hid_sensor_hub HID Sensor Hub driver
+hid_sensor_iio_common HID Sensor common attribute processing
+hid_sensor_magn_3d HID Sensor Magnetometer 3D
+hid_sensor_trigger HID Sensor trigger processing
+i2c_algo_bit I2C-Bus bit-banging algorithm
+i2c_hid HID over I2C core driver
+i2c_hid_acpi HID over I2C ACPI driver
+i2c_i801 I801 SMBus driver
+i2c_mux I2C driver for multiplexed I2C busses
+i2c_smbus SMBus protocol extensions support
+i915 Intel Graphics
+iTCO_vendor_support Intel TCO Vendor Specific WatchDog Timer Driver Support
+iTCO_wdt Intel TCO WatchDog Timer Driver
+idma64 iDMA64 core driver
+industrialio Industrial I/O core
+industrialio_triggered_buffer IIO helper functions for setting up triggered buffers
+intel_gtt Intel GTT (Graphics Translation Table) routines
+intel_ish_ipc Intel(R) Integrated Sensor Hub PCI Device Driver
+intel_ishtp ISHTP bus driver
+intel_ishtp_hid ISH ISHTP HID client driver
+intel_lpss Intel LPSS core driver
+intel_lpss_pci Intel LPSS PCI driver
+intel_oc_wdt Intel OC Watchdog driver
+intel_pch_thermal Intel PCH Thermal driver
+intel_pmc_bxt Intel Broxton PMC driver
+intel_pmc_core Intel PMC Core Driver
+intel_pmc_ssram_telemetry Intel PMC SSRAM Telemetry driver
+intel_powerclamp Package Level C-state Idle Injection for Intel CPUs
+intel_rapl_common Intel Runtime Average Power Limit (RAPL) common code
+intel_rapl_msr Driver for Intel RAPL (Running Average Power Limit) control via MSR interface
+intel_tcc_cooling TCC offset cooling device Driver
+intel_vsec Intel Extended Capabilities auxiliary bus driver
+intel_wmi_thunderbolt Intel WMI Thunderbolt force power driver
+intel_xhci_usb_role_switch Intel XHCI USB role switch driver
+ip_set ip_set: protocol 7
+ip_tables IPv4 packet filter
+irqbypass IRQ bypass manager utility module
+iwlmvm The new Intel(R) wireless AGN driver for Linux
+iwlwifi Intel(R) Wireless WiFi driver for Linux
+jbd2 Generic filesystem journal-writing module
+jfs The Journaled Filesystem (JFS)
+joydev Joystick device interfaces
+kfifo_buf Industrial I/O buffering based on kfifo
+kvm Kernel-based Virtual Machine (KVM) Hypervisor
+kvm_intel KVM support for VMX (Intel VT-x) extensions
+libaescfb Generic AES-CFB library
+libarc4 ARC4 Cipher Algorithm
+llc LLC IEEE 802.2 core support
+lp Generic parallel printer driver
+mac80211 IEEE 802.11 subsystem
+mbcache Meta block cache (for extended attributes)
+mc Device node registration for media drivers
+mei Intel(R) Management Engine Interface
+mei_hdcp MEI HDCP
+mei_me Intel(R) Management Engine Interface
+mei_pxp MEI PXP
+minix Minix file system
+mmc_core MMC core driver
+mousedev Mouse (ExplorerPS/2) device interfaces
+msdos MS-DOS filesystem support
+msr x86 generic MSR driver
+nf_conntrack IPv4 and IPv6 connection tracking
+nf_conntrack_netlink List and change connection tracking table
+nf_defrag_ipv4 IPv4 defragmentation support
+nf_defrag_ipv6 IPv6 defragmentation support
+nf_nat Network address translation core
+nf_tables Framework for packet filtering and classification
+nfnetlink Netfilter messages via netlink socket
+nft_chain_nat nftables network address translation support
+nft_compat x_tables over nftables support
+nls_cp437 NLS Codepage 437 (United States, Canada)
+nls_ucs2_utils NLS UCS-2
+nls_utf8 NLS UTF-8
+nvram CMOS/NV-RAM driver for Linux
+overlay Overlay filesystem
+parport Parallel-port resource manager
+parport_pc PC-style parallel port driver
+pcspkr PC Speaker beeper driver
+pinctrl_sunrisepoint Intel Sunrisepoint PCH pinctrl/GPIO driver
+platform_profile ACPI platform profile sysfs interface
+pmt_class Intel PMT Class driver
+pmt_discovery Intel PMT Discovery driver
+pmt_telemetry Intel PMT Telemetry driver
+polyval_clmulni POLYVAL hash function accelerated by PCLMULQDQ-NI
+ppdev Support for user-space parallel port device drivers
+psmouse PS/2 mouse driver
+qnx4 QNX4 file system
+qrtr Qualcomm IPC-router driver
+raid6_pq RAID6 Q-syndrome calculations
+rc_core Remote Controller core module
+rfcomm Bluetooth RFCOMM ver 1.11
+rfkill RF switch support
+rng_core H/W Random Number Generator (RNG) driver
+sdhci Secure Digital Host Controller Interface core driver
+sdhci_pci Secure Digital Host Controller Interface PCI driver
+sdhci_uhs2 MMC UHS-II Support
+serio_raw Raw serio driver
+sg SCSI generic (sg) driver
+snd Advanced Linux Sound Architecture driver for soundcards.
+snd_compress ALSA Compressed offload framework
+snd_ctl_led ALSA control interface to LED trigger code.
+snd_hda_codec HDA codec core
+snd_hda_codec_conexant Conexant HD-audio codec
+snd_hda_codec_generic Generic HD-audio codec parser
+snd_hda_codec_hdmi Generic HDMI HD-audio codec
+snd_hda_codec_intelhdmi Intel HDMI HD-audio codec
+snd_hda_core HD-audio bus
+snd_hda_ext_core HDA extended core
+snd_hda_intel Intel HDA driver
+snd_hrtimer ALSA hrtimer backend
+snd_hwdep Hardware dependent layer
+snd_intel_dspcfg Intel DSP config driver
+snd_intel_sdw_acpi Intel Soundwire ACPI helpers
+snd_pcm Midlevel PCM code for ALSA.
+snd_pcm_dmaengine PCM dmaengine helper APIs
+snd_seq Advanced Linux Sound Architecture sequencer.
+snd_seq_device ALSA sequencer device management
+snd_seq_dummy ALSA sequencer MIDI-through client
+snd_soc_avs Intel cAVS sound driver
+snd_soc_core ALSA SoC Core
+snd_soc_hda_codec HD-Audio codec driver
+snd_timer ALSA timer interface
+soc_button_array Windows-compatible SoC Button Array driver
+soundcore Core sound module
+sparse_keymap Generic support for sparse keymaps
+spd5118 SPD 5118 driver
+stp SAP demux for IEEE 802.1D Spanning Tree Protocol (STP)
+tap Common library for drivers implementing the TAP interface
+think_lmi ThinkLMI Driver
+thinkpad_acpi ThinkPad ACPI Extras
+tpm TPM Driver
+tpm_crb TPM2 Driver
+tpm_tis TPM Driver
+tpm_tis_core TPM Driver
+ttm TTM memory manager subsystem (for DRM device)
+tun Universal TUN/TAP device driver
+ufs UFS Filesystem
+usbhid USB HID core driver
+uvc USB Video Class common code
+uvcvideo USB Video Class driver
+veth Virtual Ethernet Tunnel
+vfat VFAT filesystem support
+vhost Host kernel accelerator for virtio
+vhost_iotlb VHOST IOTLB
+vhost_net Host kernel accelerator for virtio net
+video ACPI Video Driver
+videobuf2_common Media buffer core framework
+videobuf2_memops common memory handling routines for videobuf2
+videobuf2_v4l2 Driver helper framework for Video for Linux 2
+videobuf2_vmalloc vmalloc memory handling routines for videobuf2
+videodev Video4Linux2 core driver
+wacom USB Wacom tablet driver
+wmi ACPI-WMI Mapping Driver
+wmi_bmof WMI embedded Binary MOF driver
+x86_pkg_temp_thermal X86 PKG TEMP Thermal Driver
+x_tables {ip,ip6,arp,eb}_tables backend module
+xfrm_algo XFRM Algorithm interface
+xfrm_user XFRM User interface
+xfs SGI XFS with ACLs, security attributes, realtime, scrub, repair, quota, debug enabled
+xor RAID-5 checksumming functions
+xt_CHECKSUM Xtables: checksum modification
+xt_MASQUERADE Xtables: automatic-address SNAT
+xt_addrtype Xtables: address type match
+xt_conntrack Xtables: connection tracking state match
+xt_multiport Xtables: multiple port matching for TCP, UDP, UDP-Lite, SCTP and DCCP
+xt_nat SNAT and DNAT targets support
+xt_set Xtables: IP set match and target module
+xt_tcpudp Xtables: TCP, UDP and UDP-Lite match
+
Boots
+Boots
+Wed Dec 17 19:26:06 2025 6.17.11-1-liquorix-amd64
+Wed Dec 17 19:20:37 2025 6.17.11-1-liquorix-amd64
+Sat Dec 13 14:06:32 2025 6.17.11-1-liquorix-amd64
+Fri Dec 12 20:47:26 2025 6.17.11-1-liquorix-amd64
+Thu Dec 11 22:25:46 2025 6.17.11-1-liquorix-amd64
+Sat Dec 6 20:46:50 2025 6.17.10-1-liquorix-amd64
+Sat Dec 6 09:24:52 2025 6.17.10-1-liquorix-amd64
+Tue Dec 2 05:57:08 2025 6.17.9-1-liquorix-amd64
+Tue Dec 2 05:54:50 2025 6.17.8-2-liquorix-amd64
+Sun Nov 30 14:59:40 2025 6.17.8-2-liquorix-amd64
+Tue Nov 18 19:58:45 2025 6.17.7-1-liquorix-amd64
+Tue Nov 18 19:29:46 2025 6.17.7-1-liquorix-amd64
+Thu Nov 13 04:13:16 2025 6.17.7-1-liquorix-amd64
+Thu Nov 13 04:11:59 2025 6.17.7-1-liquorix-amd64
+Tue Nov 11 06:54:33 2025 6.17.7-1-liquorix-amd64
+Mon Nov 10 23:13:21 2025 6.17.7-1-liquorix-amd64
+Sun Nov 9 03:42:26 2025 6.17.6-1-liquorix-amd64
+Sat Nov 1 20:24:15 2025 6.17.6-1-liquorix-amd64
+Sat Nov 1 20:20:33 2025 6.17.5-1-liquorix-amd64
+Tue Oct 28 23:39:01 2025 6.17.4-1-liquorix-amd64
+Tue Oct 28 20:35:06 2025 6.17.4-1-liquorix-amd64
+Wed Oct 22 05:27:29 2025 6.17.3-1-liquorix-amd64
+Tue Oct 21 20:43:39 2025 6.17.3-1-liquorix-amd64
+Mon Oct 20 18:52:53 2025 6.17.3-1-liquorix-amd64
+Sun Oct 12 17:08:25 2025 6.16.11-1-liquorix-amd64
+
Langues
+Langues disponibles
+aa_DJ Afar language locale for Djibouti (Cadu/Laaqo Dialects).
+aa_DJ.utf8 Afar language locale for Djibouti (Cadu/Laaqo Dialects).
+aa_ER Afar language locale for Eritrea (Cadu/Laaqo Dialects).
+aa_ET Afar language locale for Ethiopia (Cadu/Carra Dialects).
+af_ZA Afrikaans locale for South Africa
+af_ZA.utf8 Afrikaans locale for South Africa
+agr_PE Awajún (agr) locale for Peru
+ak_GH Akan locale for Ghana
+am_ET Amharic language locale for Ethiopia.
+an_ES Aragonese locale for Spain
+an_ES.utf8 Aragonese locale for Spain
+anp_IN Angika language locale for India
+ar_AE Arabic language locale for United Arab Emirates
+ar_AE.utf8 Arabic language locale for United Arab Emirates
+ar_BH Arabic language locale for Bahrain
+ar_BH.utf8 Arabic language locale for Bahrain
+ar_DZ Arabic language locale for Algeria
+ar_DZ.utf8 Arabic language locale for Algeria
+ar_EG Arabic language locale for Egypt
+ar_EG.utf8 Arabic language locale for Egypt
+ar_IN Arabic language locale for India
+ar_IQ Arabic language locale for Iraq
+ar_IQ.utf8 Arabic language locale for Iraq
+ar_JO Arabic language locale for Jordan
+ar_JO.utf8 Arabic language locale for Jordan
+ar_KW Arabic language locale for Kuwait
+ar_KW.utf8 Arabic language locale for Kuwait
+ar_LB Arabic language locale for Lebanon
+ar_LB.utf8 Arabic language locale for Lebanon
+ar_LY Arabic language locale for Libyan Arab Jamahiriya
+ar_LY.utf8 Arabic language locale for Libyan Arab Jamahiriya
+ar_MA Arabic language locale for Morocco
+ar_MA.utf8 Arabic language locale for Morocco
+ar_OM Arabic language locale for Oman
+ar_OM.utf8 Arabic language locale for Oman
+ar_QA Arabic language locale for Qatar
+ar_QA.utf8 Arabic language locale for Qatar
+ar_SA Arabic locale for Saudi Arabia
+ar_SA.utf8 Arabic locale for Saudi Arabia
+ar_SD Arabic language locale for Sudan
+ar_SD.utf8 Arabic language locale for Sudan
+ar_SS Arabic language locale for South Sudan
+ar_SY Arabic language locale for Syrian Arab Republic
+ar_SY.utf8 Arabic language locale for Syrian Arab Republic
+ar_TN Arabic language locale for Tunisia
+ar_TN.utf8 Arabic language locale for Tunisia
+ar_YE Arabic language locale for Yemen
+ar_YE.utf8 Arabic language locale for Yemen
+as_IN Assamese language locale for India
+ast_ES Asturian locale for Spain
+ast_ES.utf8 Asturian locale for Spain
+ayc_PE Aymara (ayc) locale for Peru
+az_AZ Azeri language locale for Azerbaijan (latin)
+az_IR South Azerbaijani language locale for Iran
+be_BY Belarusian locale for Belarus
+be_BY@latin Belarusian Latin-Script locale for Belarus
+be_BY.utf8 Belarusian locale for Belarus
+bem_ZM Bemba locale for Zambia
+ber_DZ Berber language locale for Algeria (latin)
+ber_MA Berber language locale for Morocco (tifinagh)
+bg_BG Bulgarian locale for Bulgaria
+bg_BG.utf8 Bulgarian locale for Bulgaria
+bhb_IN.utf8 Bhili(devanagari) language locale for India
+bho_IN Bhojpuri language locale for India
+bho_NP Bhojpuri language locale for Nepal
+bi_VU Bislama language locale for Vanuatu
+bn_BD Bangla language locale for Bangladesh
+bn_IN Bangla language locale for India
+bo_CN Tibetan language locale for P.R. of China
+bo_IN Tibetan language locale for India
+br_FR Breton language locale for France
+br_FR@euro Breton locale for France with Euro
+br_FR.utf8 Breton language locale for France
+brx_IN Bodo language locale for India
+bs_BA Bosnian language locale for Bosnia and Herzegowina
+bs_BA.utf8 Bosnian language locale for Bosnia and Herzegowina
+byn_ER Blin language locale for Eritrea
+ca_AD Catalan locale for Andorra
+ca_AD.utf8 Catalan locale for Andorra
+ca_ES Catalan locale for Spain
+ca_ES@euro Catalan locale for Catalonia with Euro
+ca_ES.utf8 Catalan locale for Spain
+ca_ES@valencia Valencian (southern Catalan) locale for Spain with Euro
+ca_FR Catalan locale for France
+ca_FR.utf8 Catalan locale for France
+ca_IT Catalan locale for Italy (L'Alguer)
+ca_IT.utf8 Catalan locale for Italy (L'Alguer)
+ce_RU Chechen locale for RUSSIAN FEDERATION
+chr_US Cherokee language locale for United States
+ckb_IQ Central Kurdish language locale for Iraq
+cmn_TW Mandarin Chinese locale for the Republic of China
+crh_RU Crimean Tatar language locale for Russia
+crh_UA Crimean Tatar (Crimean Turkish) language locale for Ukraine
+csb_PL Kashubian locale for Poland
+cs_CZ Czech locale for the Czech Republic
+cs_CZ.utf8 Czech locale for the Czech Republic
+C.utf8 C locale
+cv_RU Chuvash locale for Russia
+cy_GB Welsh language locale for Great Britain
+cy_GB.utf8 Welsh language locale for Great Britain
+da_DK Danish locale for Denmark
+da_DK.utf8 Danish locale for Denmark
+de_AT German locale for Austria
+de_AT@euro German locale for Austria with Euro
+de_AT.utf8 German locale for Austria
+de_BE German locale for Belgium
+de_BE@euro German locale for Belgium with Euro
+de_BE.utf8 German locale for Belgium
+de_CH German locale for Switzerland
+de_CH.utf8 German locale for Switzerland
+de_DE German locale for Germany
+de_DE@euro German locale for Germany with Euro
+de_DE.utf8 German locale for Germany
+de_IT German language locale for Italy
+de_IT.utf8 German language locale for Italy
+de_LI.utf8 German locale for Liechtenstein
+de_LU German locale for Luxemburg
+de_LU@euro German locale for Luxemburg with Euro
+de_LU.utf8 German locale for Luxemburg
+doi_IN Dogri language locale for India
+dsb_DE Lower Sorbian locale for Germany
+dv_MV Dhivehi Language Locale for Maldives
+dz_BT Dzongkha language locale for Bhutan
+el_CY Greek locale for Cyprus
+el_CY.utf8 Greek locale for Cyprus
+el_GR Greek locale for Greece
+el_GR@euro Greek locale for Greece with Euro
+el_GR.utf8 Greek locale for Greece
+en_AG English language locale for Antigua and Barbuda
+en_AU English locale for Australia
+en_AU.utf8 English locale for Australia
+en_BW English locale for Botswana
+en_BW.utf8 English locale for Botswana
+en_CA English locale for Canada
+en_CA.utf8 English locale for Canada
+en_DK English locale for Denmark
+en_DK.iso885915 English locale for Denmark
+en_DK.utf8 English locale for Denmark
+en_GB English locale for Britain
+en_GB.iso885915 English locale for Britain
+en_GB.utf8 English locale for Britain
+en_HK English locale for Hong Kong
+en_HK.utf8 English locale for Hong Kong
+en_IE English locale for Ireland
+en_IE@euro English locale for Ireland with Euro
+en_IE.utf8 English locale for Ireland
+en_IL English locale for Israel
+en_IN English language locale for India
+en_NG English locale for Nigeria
+en_NZ English locale for New Zealand
+en_NZ.utf8 English locale for New Zealand
+en_PH English language locale for Philippines
+en_PH.utf8 English language locale for Philippines
+en_SC.utf8 English locale for the Seychelles
+en_SG English language locale for Singapore
+en_SG.utf8 English language locale for Singapore
+en_US English locale for the USA
+en_US.iso885915 English locale for the USA
+en_US.utf8 English locale for the USA
+en_ZA English locale for South Africa
+en_ZA.utf8 English locale for South Africa
+en_ZM English locale for Zambia
+en_ZW English locale for Zimbabwe
+en_ZW.utf8 English locale for Zimbabwe
+eo Esperanto language locale
+es_AR Spanish locale for Argentina
+es_AR.utf8 Spanish locale for Argentina
+es_BO Spanish locale for Bolivia
+es_BO.utf8 Spanish locale for Bolivia
+es_CL Spanish locale for Chile
+es_CL.utf8 Spanish locale for Chile
+es_CO Spanish locale for Colombia
+es_CO.utf8 Spanish locale for Colombia
+es_CR Spanish locale for Costa Rica
+es_CR.utf8 Spanish locale for Costa Rica
+es_CU Spanish locale for Cuba
+es_DO Spanish locale for Dominican Republic
+es_DO.utf8 Spanish locale for Dominican Republic
+es_EC Spanish locale for Ecuador
+es_EC.utf8 Spanish locale for Ecuador
+es_ES Spanish locale for Spain
+es_ES@euro Spanish locale for Spain with Euro
+es_ES.utf8 Spanish locale for Spain
+es_GT Spanish locale for Guatemala
+es_GT.utf8 Spanish locale for Guatemala
+es_HN Spanish locale for Honduras
+es_HN.utf8 Spanish locale for Honduras
+es_MX Spanish locale for Mexico
+es_MX.utf8 Spanish locale for Mexico
+es_NI Spanish locale for Nicaragua
+es_NI.utf8 Spanish locale for Nicaragua
+es_PA Spanish locale for Panama
+es_PA.utf8 Spanish locale for Panama
+es_PE Spanish locale for Peru
+es_PE.utf8 Spanish locale for Peru
+es_PR Spanish locale for Puerto Rico
+es_PR.utf8 Spanish locale for Puerto Rico
+es_PY Spanish locale for Paraguay
+es_PY.utf8 Spanish locale for Paraguay
+es_SV Spanish locale for El Salvador
+es_SV.utf8 Spanish locale for El Salvador
+es_US Spanish locale for the USA
+es_US.utf8 Spanish locale for the USA
+es_UY Spanish locale for Uruguay
+es_UY.utf8 Spanish locale for Uruguay
+es_VE Spanish locale for Venezuela
+es_VE.utf8 Spanish locale for Venezuela
+et_EE Estonian locale for Estonia
+et_EE.iso885915 Estonian locale for Estonia
+et_EE.utf8 Estonian locale for Estonia
+eu_ES Basque locale for Spain
+eu_ES@euro Basque language locale for Spain with Euro
+eu_ES.utf8 Basque locale for Spain
+eu_FR Basque locale for France
+eu_FR@euro Basque locale for France
+eu_FR.utf8 Basque locale for France
+fa_IR Persian locale for Iran
+ff_SN Fulah locale for Senegal
+fi_FI Finnish locale for Finland
+fi_FI@euro Finnish locale for Finland with Euro
+fi_FI.utf8 Finnish locale for Finland
+fil_PH Filipino language locale for Philippines
+fo_FO Faroese locale for Faroe Islands
+fo_FO.utf8 Faroese locale for Faroe Islands
+fr_BE French locale for Belgium
+fr_BE@euro French locale for Belgium with Euro
+fr_BE.utf8 French locale for Belgium
+fr_CA French locale for Canada
+fr_CA.utf8 French locale for Canada
+fr_CH French locale for Switzerland
+fr_CH.utf8 French locale for Switzerland
+fr_FR French locale for France
+fr_FR@euro French locale for France with Euro
+fr_FR.utf8 French locale for France
+fr_LU French locale for Luxemburg
+fr_LU@euro French locale for Luxemburg with Euro
+fr_LU.utf8 French locale for Luxemburg
+fur_IT Furlan locale for Italy
+fy_DE Western Frisian locale for Germany
+fy_NL Frisian locale for the Netherlands
+ga_IE Irish locale for Ireland
+ga_IE@euro Irish locale for Ireland with Euro
+ga_IE.utf8 Irish locale for Ireland
+gbm_IN Garhwali language locale for India
+gd_GB Scots Gaelic language locale for Great Britain
+gd_GB.utf8 Scots Gaelic language locale for Great Britain
+gez_ER Ge'ez language locale for Eritrea.
+gez_ER@abegede Ge'ez language locale for Eritrea With Abegede Collation.
+gez_ET Ge'ez language locale for Ethiopia
+gez_ET@abegede Ge'ez language locale for Ethiopia With Abegede Collation
+gl_ES Galician locale for Spain
+gl_ES@euro Galician locale for Spain with Euro
+gl_ES.utf8 Galician locale for Spain
+gu_IN Gujarati Language Locale For India
+gv_GB Manx Gaelic locale for Britain
+gv_GB.utf8 Manx Gaelic locale for Britain
+hak_TW Hakka Chinese locale for the Republic of China
+ha_NG Hausa locale for Nigeria
+he_IL Hebrew locale for Israel
+he_IL.utf8 Hebrew locale for Israel
+hif_FJ Fiji Hindi (Latin) language locale for Fiji
+hi_IN Hindi language locale for India
+hne_IN Chhattisgarhi language locale for India
+hr_HR Croatian locale for Croatia
+hr_HR.utf8 Croatian locale for Croatia
+hsb_DE Upper Sorbian locale for Germany
+hsb_DE.utf8 Upper Sorbian locale for Germany
+ht_HT Kreyol locale for Haiti
+hu_HU Hungarian locale for Hungary
+hu_HU.utf8 Hungarian locale for Hungary
+hy_AM Armenian language locale for Armenia
+hy_AM.armscii8 Armenian language locale for Armenia
+ia_FR Interlingua locale for France
+id_ID Indonesian locale for Indonesia
+id_ID.utf8 Indonesian locale for Indonesia
+ig_NG Igbo locale for Nigeria
+ik_CA Inupiaq locale for Canada
+is_IS Icelandic locale for Iceland
+is_IS.utf8 Icelandic locale for Iceland
+it_CH Italian locale for Switzerland
+it_CH.utf8 Italian locale for Switzerland
+it_IT Italian locale for Italy
+it_IT@euro Italian locale for Italy with Euro
+it_IT.utf8 Italian locale for Italy
+iu_CA Inuktitut language locale for Nunavut, Canada
+ja_JP.eucjp Japanese language locale for Japan
+ja_JP.utf8 Japanese language locale for Japan
+kab_DZ Kabyle language locale for Algeria
+ka_GE Georgian language locale for Georgia
+ka_GE.utf8 Georgian language locale for Georgia
+kk_KZ Kazakh locale for Kazakhstan
+kk_KZ.rk1048 Kazakh locale for Kazakhstan
+kk_KZ.utf8 Kazakh locale for Kazakhstan
+kl_GL Greenlandic locale for Greenland
+kl_GL.utf8 Greenlandic locale for Greenland
+km_KH Khmer locale for Cambodia
+kn_IN Kannada language locale for India
+kok_IN Konkani language locale for India
+ko_KR.euckr Korean locale for Republic of Korea
+ko_KR.utf8 Korean locale for Republic of Korea
+ks_IN Kashmiri language locale for India
+ks_IN@devanagar Kashmiri(devanagari) language locale for India
+ku_TR.utf8 Kurdish (latin) locale for Türkiye
+kv_RU Komi locale for Russia
+kw_GB Cornish locale for Britain
+kw_GB.utf8 Cornish locale for Britain
+ky_KG Kyrgyz Language Locale for Kyrgyzstan
+lb_LU Luxembourgish locale for Luxembourg
+lg_UG Luganda locale for Uganda
+lg_UG.utf8 Luganda locale for Uganda
+li_BE Limburgish Language Locale for Belgium
+lij_IT Ligurian locale for Italy
+li_NL Limburgish Language Locale for the Netherlands
+ln_CD Lingala locale for Democratic Republic of the Congo
+lo_LA Lao locale for Laos
+ltg_LV.utf8 Latgalian locale for Latvia
+lt_LT Lithuanian locale for Lithuania
+lt_LT.utf8 Lithuanian locale for Lithuania
+lv_LV Latvian locale for Latvia
+lv_LV.utf8 Latvian locale for Latvia
+lzh_TW Literary Chinese locale for the Republic of China
+mag_IN Magahi language locale for India
+mai_IN Maithili language locale for India
+mai_NP Maithili language locale for Nepal
+mdf_RU Moksha language locale for Russia
+mfe_MU Morisyen locale for Mauritius
+mg_MG Malagasy locale for Madagascar
+mg_MG.utf8 Malagasy locale for Madagascar
+mhr_RU Mari locale for Russia
+mi_NZ Maori language locale for New Zealand
+mi_NZ.utf8 Maori language locale for New Zealand
+miq_NI Miskito language locale for Nicaragua
+mjw_IN Karbi language locale for India
+mk_MK Macedonian locale for Macedonia
+mk_MK.utf8 Macedonian locale for Macedonia
+ml_IN Malayalam language locale for India
+mni_IN Manipuri language locale for India
+mn_MN Mongolian locale for Mongolia
+mnw_MM Mon language locale for Myanmar
+mr_IN Marathi language locale for India
+ms_MY Malay language locale for Malaysia
+ms_MY.utf8 Malay language locale for Malaysia
+mt_MT Maltese language locale for Malta
+mt_MT.utf8 Maltese language locale for Malta
+my_MM Burmese language locale for Myanmar
+nan_TW Min Nan Chinese locale for the Republic of China
+nan_TW@latin Minnan language locale for Taiwan
+nb_NO.utf8 Norwegian (Bokmål) locale for Norway
+nds_DE Low(lands) Saxon Language Locale for Germany
+nds_NL Low(lands) Saxon Language Locale for the Netherlands
+ne_NP Nepali language locale for Nepal
+nhn_MX Central Nahuatl for Mexico
+niu_NU Niuean (Vagahau Niue) locale for Niue
+niu_NZ Niuean (Vagahau Niue) locale for New Zealand
+nl_AW Dutch language locale for Aruba
+nl_BE Dutch locale for Belgium
+nl_BE@euro Dutch locale for Belgium with Euro
+nl_BE.utf8 Dutch locale for Belgium
+nl_NL Dutch locale for the Netherlands
+nl_NL@euro Dutch locale for the Netherlands with Euro
+nl_NL.utf8 Dutch locale for the Netherlands
+nn_NO Nynorsk language locale for Norway
+nn_NO.utf8 Nynorsk language locale for Norway
+nr_ZA Southern Ndebele locale for South Africa
+nso_ZA Northern Sotho locale for South Africa
+oc_FR Occitan Language Locale for France
+oc_FR.utf8 Occitan Language Locale for France
+om_ET Oromo language locale for Ethiopia.
+om_KE Oromo language locale for Kenya.
+om_KE.utf8 Oromo language locale for Kenya.
+or_IN Odia language locale for India
+os_RU Ossetian locale for Russia
+pa_IN Punjabi language locale for Indian Punjabi(Gurmukhi)
+pap_AW Papiamento Language for Aruba
+pap_CW Papiamento language for Curaçao
+pa_PK Punjabi (Shahmukhi) Language Locale for Pakistan
+pl_PL Polish locale for Poland
+pl_PL.utf8 Polish locale for Poland
+ps_AF Pashto locale for Afghanistan
+pt_BR Portuguese locale for Brasil
+pt_BR.utf8 Portuguese locale for Brasil
+pt_PT Portuguese locale for Portugal
+pt_PT@euro Portuguese locale for Portugal with Euro
+pt_PT.utf8 Portuguese locale for Portugal
+quz_PE Cusco Quechua locale for Peru
+raj_IN Rajasthani language locale for India
+rif_MA Tarifit locale for Morocco
+ro_RO Romanian locale for Romania
+ro_RO.utf8 Romanian locale for Romania
+ru_RU Russian locale for Russia
+ru_RU.cp1251 Russian locale for Russia
+ru_RU.koi8r Russian locale for Russia
+ru_RU.utf8 Russian locale for Russia
+ru_UA Russian locale for Ukraine
+ru_UA.utf8 Russian locale for Ukraine
+rw_RW Kinyarwanda language locale for Rwanda
+sah_RU Sakha (Yakut) locale for Russian Federation
+sa_IN Sanskrit language locale for India
+sat_IN Santali language locale for India
+sc_IT Sardinian locale for Italy
+scn_IT Sicilian locale for Italy
+sd_IN Sindhi language locale for India
+sd_IN@devanagar Sindhi language locale for India
+se_NO Northern Saami language locale for Norway
+sgs_LT Samogitian language locale for Lithuania
+shn_MM Shan language locale for Myanmar
+shs_CA Shuswap locale for Canada
+sid_ET Sidama language locale for Ethiopia.
+si_LK Sinhala language locale for Sri Lanka
+sk_SK Slovak locale for Slovak
+sk_SK.utf8 Slovak locale for Slovak
+sl_SI Slovenian locale for Slovenia
+sl_SI.utf8 Slovenian locale for Slovenia
+sm_WS Samoan language locale for Samoa
+so_DJ Somali language locale for Djibouti.
+so_DJ.utf8 Somali language locale for Djibouti.
+so_ET Somali language locale for Ethiopia
+so_KE Somali language locale for Kenya
+so_KE.utf8 Somali language locale for Kenya
+so_SO Somali language locale for Somalia
+so_SO.utf8 Somali language locale for Somalia
+sq_AL Albanian language locale for Albania
+sq_AL.utf8 Albanian language locale for Albania
+sq_MK Albanian language locale for Macedonia
+sr_ME Serbian locale for Montenegro
+sr_RS Serbian locale for Serbia
+sr_RS@latin Serbian Latin locale for Serbia
+ssy_ER Saho language locale for Eritrea.
+ss_ZA Swati locale for South Africa
+st_ZA Sotho locale for South Africa
+st_ZA.utf8 Sotho locale for South Africa
+su_ID Sundanese locale for Indonesia
+sv_FI Swedish locale for Finland
+sv_FI@euro Swedish locale for Finland with Euro
+sv_FI.utf8 Swedish locale for Finland
+sv_SE Swedish locale for Sweden
+sv_SE.iso885915 Swedish locale for Sweden
+sv_SE.utf8 Swedish locale for Sweden
+sw_KE Swahili locale for Kenya
+sw_TZ Swahili locale for Tanzania
+syr Syriac language locale
+szl_PL Silesian locale for Poland
+ta_IN Tamil language locale for India
+ta_LK Tamil language locale for Sri Lanka
+tcy_IN.utf8 Tulu language locale for India
+te_IN Telugu language locale for India
+tg_TJ Tajik language locale for Tajikistan
+tg_TJ.utf8 Tajik language locale for Tajikistan
+the_NP Tharu language locale for Nepal
+th_TH Thai locale for Thailand
+th_TH.utf8 Thai locale for Thailand
+ti_ER Tigrigna language locale for Eritrea.
+ti_ET Tigrigna language locale for Ethiopia.
+tig_ER Tigre language locale for Eritrea
+tk_TM Turkmen locale for Turkmenistan
+tl_PH Tagalog language locale for Philippines
+tl_PH.utf8 Tagalog language locale for Philippines
+tn_ZA Tswana locale for South Africa
+tok Toki Pona language locale
+to_TO Tongan language locale for Tonga
+tpi_PG Tok Pisin language locale for Papua New Guinea
+tr_CY Turkish language locale for Cyprus
+tr_CY.utf8 Turkish language locale for Cyprus
+tr_TR.utf8 Turkish locale for Türkiye
+ts_ZA Tsonga locale for South Africa
+tt_RU Tatar language locale for Russia
+tt_RU@iqtelif Tatar language locale using IQTElif alphabet; for Tatarstan, Russian Federation
+ug_CN Uyghur locale for China
+uk_UA Ukrainian Language Locale for Ukraine
+uk_UA.utf8 Ukrainian Language Locale for Ukraine
+unm_US Unami Delaware locale for the USA
+ur_IN Urdu language locale for India
+ur_PK Urdu Language Locale for Pakistan
+uz_UZ Uzbek (latin) locale for Uzbekistan
+uz_UZ@cyrillic Uzbek (cyrillic) locale for Uzbekistan
+uz_UZ.utf8 Uzbek (latin) locale for Uzbekistan
+ve_ZA Venda locale for South Africa
+vi_VN Vietnamese language locale for Vietnam
+wa_BE Walloon Language Locale for Belgium
+wa_BE@euro Walloon locale for Belgium with Euro
+wa_BE.utf8 Walloon Language Locale for Belgium
+wae_CH Walser locale for Switzerland
+wal_ET Walaita language locale for Ethiopia.
+wo_SN Wolof locale for Senegal
+xh_ZA Xhosa locale for South Africa
+xh_ZA.utf8 Xhosa locale for South Africa
+yi_US Yiddish Language locale for the USA
+yi_US.utf8 Yiddish Language locale for the USA
+yo_NG Yoruba locale for Nigeria
+yue_HK Yue Chinese (Cantonese) language locale for Hong Kong
+yuw_PG YauNungon locale for Papua New Guinea
+zgh_MA Tamazight language locale for Morocco
+zh_CN Chinese locale for Peoples Republic of China
+zh_CN.gb18030 Chinese locale for Peoples Republic of China
+zh_CN.gbk Chinese locale for Peoples Republic of China
+zh_CN.utf8 Chinese locale for Peoples Republic of China
+zh_HK Chinese language locale for Hong Kong
+zh_HK.utf8 Chinese language locale for Hong Kong
+zh_SG Chinese language locale for Singapore
+zh_SG.gbk Chinese language locale for Singapore
+zh_SG.utf8 Chinese language locale for Singapore
+zh_TW Chinese locale for Taiwan R.O.C.
+zh_TW.euctw Chinese locale for Taiwan R.O.C.
+zh_TW.utf8 Chinese locale for Taiwan R.O.C.
+zu_ZA Zulu locale for South Africa
+zu_ZA.utf8 Zulu locale for South Africa
+
Usage de Mémoire
+Memory
+Active(anon) 2590764 KiB|Anonymous memory used more recently and not swapped out
+Active(file) 673484 KiB|Pagecache memory been used more recently and not reclaimed
+Active 3264248 KiB|Memory used more recently and not swapped out or reclaimed
+AnonHugePages 0 KiB|Non-file backed huge pages mapped into userspace page tables
+AnonPages 3811592 KiB|Non-file backed pages mapped into userspace page tables
+Balloon 0 KiB|Balloon
+Bounce 0 KiB|Memory used for block device bounce buffers
+Buffers 65992 KiB|Memory in buffer cache, temporary storage for raw disk blocks
+Cached 3226012 KiB|Memory in the page cache (diskcache, shared memory, tmpfs and shmem)
+CmaFree 0 KiB|Free remaining memory in the CMA reserves
+CmaTotal 0 KiB|Memory reserved for the Contiguous Memory Allocator (CMA)
+CommitLimit 13383660 KiB|Total memory currently available to be allocated on the system
+Committed_AS 29531036 KiB|The amount of memory presently allocated on the system
+DirectMap1G 1048576 KiB|Breakdown of page table sizes of 1G size
+DirectMap2M 6848512 KiB|Breakdown of page table sizes of 2M size
+DirectMap4k 372180 KiB|Breakdown of page table sizes of 4k size
+Dirty 952 KiB|Memory waiting to be written back to disk
+FileHugePages 8192 KiB|Memory used for fs data (page cache) allocated with huge pages
+FilePmdMapped 0 KiB|Page cache mapped into userspace with huge pages
+HardwareCorrupted 0 KiB|Amount of memory the kernel identifies as corrupted
+HugePages_Free 0|Number of huge pages available
+HugePages_Rsvd 0|Number of huge pages reserved
+HugePages_Surp 0|Number of huge pages surplus
+HugePages_Total 0|Size of the pool of huge pages
+Hugepagesize 2048 KiB|Huge page size, used in conjunction with hugepages parameter
+Hugetlb 0 KiB|Total amount of memory consumed by huge pages of all sizes
+Inactive(anon) 1911860 KiB|Anonymous memory not been used and can be swapped out
+Inactive(file) 864824 KiB|Pagecache memory reclaimable without huge performance impact
+Inactive 2776684 KiB|Memory not been used recently and can be swapped out or reclaimed
+KReclaimable 162840 KiB|Kernel allocations reclaimable under memory pressure
+KernelStack 31728 KiB|Memory consumed by the kernel stacks of all tasks
+Mapped 755796 KiB|Files which have been mmapped, such as libraries
+MemAvailable 1648152 KiB|Available memory for allocation to any process, without swapping
+MemFree 251672 KiB|Free memory which is not used for anything
+MemTotal 8017888 KiB|Total physical memory usable by the system
+Mlocked 1800 KiB|Pages locked to memory using the mlock() system call
+NFS_Unstable 0 KiB|Previous counted pages which had been written to the server
+PageTables 91252 KiB|Memory consumed by userspace page tables
+Percpu 3072 KiB|Memory allocated to the percpu allocator
+SReclaimable 162840 KiB|Part of Slab, that might be reclaimed, such as caches
+SUnreclaim 154348 KiB|Part of Slab, that cannot be reclaimed on memory pressure
+SecPageTables 2056 KiB|Memory consumed by secondary page tables
+Shmem 1754208 KiB|Total memory used by shared memory (shmem) and tmpfs
+ShmemHugePages 0 KiB|Memory used by shmem and tmpfs allocated with huge pages
+ShmemPmdMapped 0 KiB|Shared memory mapped into userspace with huge pages
+Slab 317188 KiB|In-kernel data structures cache
+SwapCached 302388 KiB|Memory present within main memory and in the swapfile
+SwapFree 6792768 KiB|Virtual memory, remaining swap space available
+SwapTotal 9374716 KiB|Virtual memory, total swap space available
+Unaccepted 0 KiB|Amount of unaccepted memory usable by the kernel
+Unevictable 1071616 KiB|Unevictable pages can't be swapped out for a variety of reasons
+VmallocChunk 0 KiB|Largest contiguous block of vmalloc area which is free
+VmallocTotal -1 KiB|Total size of vmalloc virtual address space
+VmallocUsed 90644 KiB|Amount of vmalloc area which is used
+Writeback 0 KiB|Memory which is actively being written back to disk
+WritebackTmp 0 KiB|Memory used by FUSE for temporary writeback buffers
+Zswap 0 KiB|Memory consumed by the zswap backend (compressed size)
+Zswapped 0 KiB|Amount of anonymous memory stored in zswap (original size)
+
Fichiers Système
+Systèmes de fichiers montés
+
+
+udev /dev 0,00 % (3,7 GiB of 3,7 GiB)
+
+
+tmpfs /run 0,71 % (777,4 MiB of 783,0 MiB)
+
+
+/dev/sda3 / 57,34 % (77,8 GiB of 182,3 GiB)
+
+
+tmpfs /dev/shm 2,03 % (3,7 GiB of 3,8 GiB)
+
+
+efivarfs /sys/firmware/efi/efivars 59,88 % (61,7 KiB of 153,9 KiB)
+
+
+tmpfs /run/lock 0,23 % (5,0 MiB of 5,0 MiB)
+
+
+tmpfs🔒 /run/credentials/systemd-journald.service 0,00 % (1,0 MiB of 1,0 MiB)
+
+
+tmpfs /tmp 0,04 % (3,8 GiB of 3,8 GiB)
+
+
+/dev/sda1 /boot/efi 7,22 % (474,1 MiB of 511,0 MiB)
+
+
+tmpfs /run/user/1000 0,52 % (778,9 MiB of 783,0 MiB)
+
Affichage
+Session
+
+
+Type wayland
+Wayland
+
+
+Nom de l'affichage actuel wayland-0
+X Server
+
+
+Nom de l'affichage actuel :0
+
+
+Fabricant The X.Org Foundation
+
+
+Version 24.1.6
+
+
+Numéro de version 12401006
+Ecrans
+
+
+Screen 0 3072x1728 pixels
+Outputs (XRandR)
+
+
+eDP-1 Connecté; 3072x1728 pixels, offset (0, 0)
+OpenGL (GLX)
+
+
+Fabricant Intel
+
+
+Moteur de rendu Mesa Intel(R) HD Graphics 520 (SKL GT2)
+
+
+Direct Rendering Oui
+
+
+Version (compatible) 4.6 (Compatibility Profile) Mesa 25.0.7-2
+
+
+Shading Language Version (Compatibilité) 4.60
+
+
+Version (Coeur) 4.6 (Core Profile) Mesa 25.0.7-2
+
+
+Shading Language Version (Core) 4.60
+
+
+Version (ES) OpenGL ES 3.2 Mesa 25.0.7-2
+
+
+Shading Language Version (ES) OpenGL ES GLSL ES 3.20
+
+
+GLX Version 1.4
+Vulkan
+
+
+Instance Version 1.4.309
+
+
+Api Version 1.4.305
+
+
+Driver Version 25.0.7
+
+
+Fabricant Intel
+
+
+Device Type PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU
+
+
+Device Name Intel(R) HD Graphics 520 (SKL GT2)
+
+
+Driver Name Intel open-source Mesa driver
+
+
+Driver Info Mesa 25.0.7-2
+
+
+Conformance Version 1.4.0.0
+
Variables d'environnement
+Variables d'environnement
+DBUS_SESSION_BUS_ADDRESS unix:path=/run/user/1000/bus
+DESKTOP_SESSION gnome
+DISPLAY :0
+GDMSESSION gnome
+GDM_LANG fr_FR.UTF-8
+GIO_LAUNCHED_DESKTOP_FILE /usr/share/applications/hardinfo2.desktop
+GIO_LAUNCHED_DESKTOP_FILE_PID 77995
+GNOME_DESKTOP_SESSION_ID this-is-deprecated
+GNOME_SETUP_DISPLAY :1
+GPG_AGENT_INFO /run/user/1000/gnupg/S.gpg-agent:0:1
+GTK_MODULES gail:atk-bridge
+HOME /home/gilles
+IM_CONFIG_PHASE 1
+INVOCATION_ID 7db15225fc734b43a671b0e309ec75b2
+JOURNAL_STREAM 9:9119
+LANG fr_FR.UTF-8
+LOGNAME gilles
+MANAGERPID 818
+PATH /home/gilles/.local/bin
/usr/src/linux-headers-6.17.11-1-liquorix-amd64/tools/power/x86/x86_energy_perf_
policy:/usr/src/linux-headers-6.17.11-1-liquorix-amd64/tools/power/x86/turbostat
/usr/src/linux-headers-6.17.11-1-liquorix-amd64/tools/power/cpupower
/usr/src/linux-headers-6.17.11-1-liquorix-amd64/tools/perf:/usr/local/bin
/usr/bin:/bin:/usr/local/games:/usr/games
+PWD /home/gilles
+QT_ACCESSIBILITY 1
+QT_IM_MODULE ibus
+SESSION_MANAGER local/yoga14:@/tmp/.ICE-unix/986,unix/yoga14:/tmp/.ICE-unix/986
+SHELL /bin/bash
+SHLVL 0
+SSH_AUTH_SOCK /run/user/1000/keyring/ssh
+SYSTEMD_EXEC_PID 986
+USER gilles
+USERNAME gilles
+WAYLAND_DISPLAY wayland-0
+XAUTHORITY /run/user/1000/.mutter-Xwaylandauth.2RC0H3
+XDG_ACTIVATION_TOKEN 8d664c89-1e81-4963-b32c-bda16128c2e7_TIME0
+XDG_CURRENT_DESKTOP GNOME
+XDG_DATA_DIRS /usr/share/gnome:/home/gilles/.local/share/flatpak/exports/share
/var/lib/flatpak/exports/share:/usr/local/share/:/usr/share/
+XDG_MENU_PREFIX gnome-
+XDG_RUNTIME_DIR /run/user/1000
+XDG_SESSION_CLASS user
+XDG_SESSION_DESKTOP gnome
+XDG_SESSION_TYPE wayland
+XMODIFIERS @im=ibus
+
Developpement
+Langage de script
+Gambas3 (gbr3) Non trouvé
+Python (default) Non trouvé
+Python2 Non trouvé
+Python3 3.13.5
+Perl 5.40.1
+Rakudo (Perl6) Non trouvé
+PHP Non trouvé
+Ruby Non trouvé
+Bash 5.2.37
+JavaScript (Node.js) 20.19.2
+awk 5.2.1
+Compilateurs
+C (GCC) 14.2.0
+C (Clang) Non trouvé
+D (dmd) Non trouvé
+Gambas3 (gbc3) Non trouvé
+Java Non trouvé
+.NET Non trouvé
+Vala Non trouvé
+Haskell (GHC) Non trouvé
+FreePascal Non trouvé
+Go Non trouvé
+Rust Non trouvé
+Outils
+make 4.4
+ninja Non trouvé
+GDB Non trouvé
+LLDB Non trouvé
+strace Non trouvé
+valgrind Non trouvé
+QMake Non trouvé
+CMake Non trouvé
+Gambas3 IDE Non trouvé
+Radare2 Non trouvé
+ltrace Non trouvé
+Powershell Non trouvé
+
Utilisateurs
+Utilisateurs
+Debian-gdm Gnome Display Manager
+_apt
+_flatpak Flatpak system-wide installation helper
+avahi Avahi mDNS daemon
+backup backup
+bin bin
+colord colord colour management daemon
+cups-pk-helper user for cups-pk-helper service
+daemon daemon
+dhcpcd DHCP Client Daemon
+dnsmasq dnsmasq
+fwupd-refresh Firmware update daemon
+games games
+geoclue
+gilles gilles
+gnome-remote-desktop GNOME Remote Desktop
+iperf3
+irc ircd
+list Mailing List Manager
+lp lp
+mail mail
+man man
+messagebus System Message Bus
+news news
+nobody nobody
+ollama
+polkitd User for polkitd
+proxy proxy
+root root
+rtkit RealtimeKit
+saned
+speech-dispatcher Speech Dispatcher
+sshd sshd user
+sync sync
+sys sys
+systemd-network systemd Network Management
+systemd-timesync systemd Time Synchronization
+tss TPM software stack
+usbmux usbmux daemon
+uucp uucp
+wsdd2 Dynamic User
+www-data www-data
+
Groupes
+Group
+Debian-gdm 114
+_flatpak 115
+_ssh 104
+adm 4
+audio 29
+avahi 107
+backup 34
+bin 2
+bluetooth 106
+cdrom 24
+clock 994
+colord 113
+crontab 997
+daemon 1
+dialout 20
+dip 30
+disk 6
+docker 984
+fax 21
+floppy 25
+fwupd-refresh 988
+games 60
+geoclue 110
+gilles 1000
+gnome-remote-desktop 987
+hardinfo2 1001
+input 996
+iperf3 117
+irc 39
+kmem 15
+kvm 993
+list 38
+lp 7
+lpadmin 108
+mail 8
+man 12
+messagebus 990
+netdev 101
+news 9
+nogroup 65534
+ollama 983
+operator 37
+pipewire 109
+plugdev 46
+polkitd 986
+proxy 13
+rdma 116
+render 992
+root 0
+rtkit 112
+sambashare 989
+saned 111
+sasl 45
+scanner 102
+sgx 995
+shadow 42
+src 40
+ssl-cert 105
+staff 50
+sudo 27
+sys 3
+systemd-journal 999
+systemd-network 998
+systemd-timesync 991
+tape 26
+tss 103
+tty 5
+users 100
+utmp 43
+uucp 10
+video 44
+voice 22
+winbindd_priv 985
+wsdd2 61623
+www-data 33
+
Périphériques System DMI
+Produit
+Nom 20ELS0C100
+Famille ThinkPad Yoga 460
+Fabricant LENOVO
+Version ThinkPad Yoga 460
+Numéro de série (Non disponible)
+SKU LENOVO_MT_20EL_BU_Think_FM_ThinkPad Yoga 460
+BIOS
+Date 05/14/2019
+Fabricant LENOVO
+Version R05ET81W (1.59)
+Carte
+Nom 20ELS0C100
+Fabricant LENOVO
+Version SDK0J40697 WIN
+Numéro de série (Non disponible)
+Asset Tag Not Available
+Châssis
+Fabricant LENOVO
+Type [31] Convertible
+Version None
+Numéro de série (Non disponible)
+Asset Tag No Asset Information
+
Processeur
+Processors
+all |Summary
+
+Package Information
+Nom Intel Core i5-6200U
+Topologie 1 processeur physique; 2 coeurs; 4 threads
+Logical CPU Config 4x 2300,00 MHz
+Clocks
+400,00-2300,00 MHz 1x
+Caches
+Level 1 (Data) 2x 32KB (64KB), 8-way set-associative, 64 sets
+Level 1 (Instruction) 2x 32KB (64KB), 8-way set-associative, 64 sets
+Level 2 (Unified) 2x 256KB (512KB), 4-way set-associative, 1024 sets
+Level 3 (Unified) 1x 3072KB (3072KB), 12-way set-associative, 4096 sets
+Socket Information
+Résultat (Not available; Perhaps try running hardinfo2 as root.)
+
+cpu0 2300,00 MHz| Intel Core i5-6200U|0:0
+cpu1 2300,00 MHz| Intel Core i5-6200U|0:1
+cpu2 2300,00 MHz| Intel Core i5-6200U|0:0
+cpu3 2300,00 MHz| Intel Core i5-6200U|0:1
+
Processeurs Graphiques
+GPUs
+
+
+card0 Lenovo Intel Skylake GT2 [HD Graphics 520]
+
+Information matériel
+
+
+GPU Mesa Intel HD Graphics 520
+
+
+Location PCI/0000:00:02.0
+
+
+DRM Device /dev/dri/card0
+
+
+Class [0300] VGA compatible controller
+
+
+Fabricant [8086] Intel Corporation
+
+
+Périphérique [1916] Skylake GT2 [HD Graphics 520]
+
+
+SVendor [17aa] Lenovo
+
+
+SDevice [504c] (Inconnu)
+
+
+Révision 07
+Clocks
+
+
+Coeur 300,00-1000,00 MHz
+
+
+Mémoire (Inconnu)
+Driver
+
+
+En cours i915
+
+
+Modules du kernel i915
+
+
Monitors
+Monitors
+card0-eDP-1 BOE 13,9″ BOE DT
+
+Connection
+DRM card0-eDP-1
+Etats connected enabled
+Signal Type Digital
+Interface [5] DisplayPort
+Bits per Color Channel 6
+Speaker Allocation (Unspecified)
+Output (Max)
+VESA EDID 0x0@0Hz 31,0x17,0cm (13,9") progressive normal
+VESA EDID DTD 1920x1080@59Hz 30,9x17,3cm (13,9") progressive normal
+EDID Device
+Fabricant [PNP:BOE] BOE
+Nom BOE DT
+Modèle [06ac-00000000] 1708-0
+Serial NV140FHM-N45
+Manufacture Date Week 3 of 2015
+EDID Meta
+Data Size 128 bytes
+Version 1.4
+Extension Blocks 0
+Extended to (Aucun)
+Checksum Ok
+EDID Descriptors
+descriptor0 ([00] detailed timing descriptor) {...}
+descriptor1 ([00] detailed timing descriptor) {...}
+descriptor2 ([fe] unspecified text) BOE DT
+descriptor3 ([fe] unspecified text) NV140FHM-N45
+Detailed Timing Descriptors (DTD)
+dtd0 1920x1080@59Hz, 30,9x17,3cm (13,9") progressive normal (VESA EDID DTD)
+Established Timings Bitmap (ETB)
+(Empty List)
+Standard Timings (STD)
+(Empty List)
+E-EDID Extension Blocks
+(Empty List)
+EIA/CEA-861 Data Blocks
+(Empty List)
+EIA/CEA-861 Short Audio Descriptors
+(Empty List)
+EIA/CEA-861 Short Video Descriptors
+(Empty List)
+DisplayID Timings
+(Empty List)
+DisplayID Strings
+(Empty List)
+Hex Dump
+Data 00ffffffffffff0009e5ac0600000000
+03190104951f1178029d409a5d558d28
+1e505400000001010101010101010101
+010101010101bc398018713828403020
+350035ad1000001a0000000000000000
+0000000000000000001a000000fe0042
+4f452044540a202020202020000000fe
+004e5631343046484d2d4e34350a00b7
+
+
+
Memory Devices
+Memory Device List
+Mainboard DDR3 SDRAM|8 / 8 GiB
+
+Memory Array
+DMI Handle 0x8
+Locator Mainboard
+Use System Memory
+Error Correction Type None
+Size (Present / Max) 8 / 8 GiB
+Devices (Populated / Sockets) 1 / 2
+Types Present [0x400] DDR3 SDRAM
+ROM Size 0 MiB
+
+Mainboard ➤ ChannelA-DIMM0 M471B1G73EB0-YK0 |8192 MiB| Samsung
+
+Memory Socket
+DMI Handles (Array, Socket) 0x8, 0x9
+Locator Mainboard ➤ BANK 0 ➤ ChannelA-DIMM0
+Bank Locator BANK 0
+Form Factor Chip
+Type DDR3 / DDR3-1600 (PC3-12800)
+Fabricant [004e] Samsung
+Part Number M471B1G73EB0-YK0
+Taille 8192 MiB
+Rated Speed 1600 MT/s
+Configured Speed 1600 MT/s
+Data Width/Total Width 64 bits / 64 bits
+Rank 2
+Minimum Voltage (Inconnu)
+Maximum Voltage (Inconnu)
+Configured Voltage 1.35 V
+Serial Presence Detect (SPD) - DDR3 SDRAM
+Source 0-0050 (at24)
+SPD Revision 1.3
+Form Factor SODIMM (Small Outline DIMM)
+Type DDR3-1600 (PC3-12800)
+Module Vendor [004e] Samsung
+DRAM Vendor [004e] Samsung
+Part Number M471B1G73EB0-YK0
+Numéro de série 0x17bb043b
+Taille 8 GiB
+Manufacturing Date (Year / Week) 2016 / 51
+Ranks 2
+IO Pins per Chip 8
+Die count 0 (Unspecified)
+Thermal Sensor [00] Not present
+Supported Voltages 1.35V 1.5V
+Supported CAS Latencies 11 10 9 8 7 6 5
+Timings
+tCL 11
+tRCD 13,125ns
+tRP 13,125ns
+tRAS 3,125ns
+
+
Périphériques PCI
+Périphériques PCI
+
+
+0000:00:00.0 Lenovo Intel Xeon E3-1200 v5/E3-1500 v5/6th Gen Core Processor Host Bridge/DRAM Registers
+
+
+0000:00:02.0 Lenovo Intel Skylake GT2 [HD Graphics 520]
+
+
+0000:00:08.0 Lenovo Intel Xeon E3-1200 v5/v6 / E3-1500 v5 / 6th/7th/8th Gen Core Processor Gaussian Mixture Model
+
+
+0000:00:13.0 Lenovo Intel Sunrise Point-LP Integrated Sensor Hub
+
+
+0000:00:14.0 Lenovo Intel Sunrise Point-LP USB 3.0 xHCI Controller
+
+
+0000:00:14.2 Lenovo Intel Sunrise Point-LP Thermal subsystem
+
+
+0000:00:16.0 Lenovo Intel Sunrise Point-LP CSME HECI
+
+
+0000:00:17.0 Lenovo Intel Sunrise Point-LP SATA Controller [AHCI mode]
+
+
+0000:00:1c.0 Intel Sunrise Point-LP PCI Express Root Port
+
+
+0000:00:1c.5 Intel Sunrise Point-LP PCI Express Root Port
+
+
+0000:00:1e.0 Lenovo Intel Sunrise Point-LP Serial IO UART Controller
+
+
+0000:00:1f.0 Lenovo Intel Sunrise Point-LP LPC Controller
+
+
+0000:00:1f.2 Lenovo Intel Sunrise Point-LP PMC
+
+
+0000:00:1f.3 Lenovo Intel Sunrise Point-LP HD Audio
+
+
+0000:00:1f.4 Lenovo Intel Sunrise Point-LP SMBus
+
+
+0000:00:1f.6 Lenovo Intel Ethernet Connection I219-V
+
+
+0000:02:00.0 O2 Micro, Inc. SD/MMC Card Reader Controller
+
+
+0000:03:00.0 Intel Wireless 8260
+
Périphériques USB
+Périphériques USB
+001:001 Linux 2.0 root hub
+001:002 Wacom Co., Ltd Pen and multitouch sensor
+001:003 Chicony Integrated Camera
+001:004 Intel Bluetooth wireless interface
+001:008 Validity Sensors, Inc. VFS 5011 fingerprint sensor
+002:001 Linux 3.0 root hub
+
Firmware
+CT480BX500SSD1
+Version M6CR056
+VendorId OUI:00a075|ATA:0xC0A9
+Serial 2249E6911F28
+Résumé ATA drive
+Protocol org.t13.ata
+Plugin ata
+Icon drive-harddisk
+Guid f73b3707-6794-5a1d-b360-1a63510e9ba9
+Guid 9e8e685b-df78-5e87-9f97-90911e61f8f9
+Guid b82a6f66-f1d9-57bf-8a1b-e3bc901bc23d
+Flags [internal] Device cannot be removed easily
+[updatable] Device is updatable in this or any other mode
+[require-ac] Requires AC power
+[needs-reboot] Requires a reboot to apply firmware or to reload hardware
+Fabricant Crucial (Micron)
+DeviceId 51a3c776538508a7135b4b407cad2f5edd938db4
+Created 17/12/2025
+Intel Management Engine
+VersionLowest 176.0.1202
+Version 176.0.1202
+VendorId DMI:LENOVO
+Résumé UEFI System Resource Table device (updated via NVRAM)
+Protocol org.uefi.capsule
+Plugin uefi_capsule
+Guid 03d3297b-3851-4379-95ad-12e21a96c80a
+Flags [internal] Device cannot be removed easily
+[updatable] Device is updatable in this or any other mode
+[require-ac] Requires AC power
+[needs-reboot] Requires a reboot to apply firmware or to reload hardware
+Fabricant Lenovo Group
+DeviceId 7ff0a3ca84ab2a0fabc0f7e195df578fc8d4c86b
+Created 17/12/2025
+KEK CA
+Version 2011
+VendorId UEFI:Microsoft
+Protocol org.uefi.dbx2
+Plugin uefi_kek
+ParentDeviceId 2a4c23bfb79b5dabe474cb7b1b3e604645d6f9c6
+Icon application-certificate
+Guid 814e950f-1449-566a-a190-42c9d3a3a2df
+Guid 2724b069-1a00-502e-922f-f4f591a1ce28
+Flags [internal] Device cannot be removed easily
+[updatable] Device is updatable in this or any other mode
+Fabricant Microsoft
+DeviceId 55c1452f0bce48c789810edcb96f4cd7f9c6f078
+Created 17/12/2025
+CompositeId e680566c57c60f6136e48bb2f879a5df7ca629f1
+KEK CA
+Version 2012
+VendorId UEFI:Lenovo
+Protocol org.uefi.dbx2
+Plugin uefi_kek
+ParentDeviceId 2a4c23bfb79b5dabe474cb7b1b3e604645d6f9c6
+Icon application-certificate
+Guid 955078db-02bc-510f-875c-65c0167f06fa
+Guid 01cd940c-2a5e-54df-9278-e6d9e2fb6919
+Flags [internal] Device cannot be removed easily
+[updatable] Device is updatable in this or any other mode
+Fabricant Lenovo Group
+DeviceId 88a75309ae83ced99cb849e33b1b1f149d58bd71
+Created 17/12/2025
+CompositeId e680566c57c60f6136e48bb2f879a5df7ca629f1
+System Firmware
+VersionLowest 65595
+Version 65595
+VendorId DMI:LENOVO
+Résumé UEFI System Resource Table device (updated via NVRAM)
+Protocol org.uefi.capsule
+Plugin uefi_capsule
+Icon computer
+Guid b8d73eb5-feba-42e1-a106-04540b6ae7d6
+Flags [internal] Device cannot be removed easily
+[updatable] Device is updatable in this or any other mode
+[require-ac] Requires AC power
+[needs-reboot] Requires a reboot to apply firmware or to reload hardware
+Fabricant Lenovo Group
+DeviceId e680566c57c60f6136e48bb2f879a5df7ca629f1
+Created 17/12/2025
+Checksum 0beb9883daed33cf3be45011c65a078d8278347a
+ThinkPad Product CA
+Version 2012
+VendorId UEFI:Lenovo
+Protocol org.uefi.dbx2
+Plugin uefi_db
+ParentDeviceId 0352a8acc949c7df21fec16e566ba9a74e797a97
+Icon application-certificate
+Guid d9087fb0-a15e-564e-9307-c9fc954a69fd
+Guid f7bf52da-56f8-55b7-8074-e5c3ec7b30bc
+Flags [internal] Device cannot be removed easily
+[updatable] Device is updatable in this or any other mode
+Fabricant Lenovo Group
+DeviceId add3ac6dc32b7f7ebb74f8013f6e1ba696d21e70
+Created 17/12/2025
+CompositeId e680566c57c60f6136e48bb2f879a5df7ca629f1
+UEFI CA
+Version 2011
+VendorId UEFI:Microsoft
+Protocol org.uefi.dbx2
+Plugin uefi_db
+ParentDeviceId 0352a8acc949c7df21fec16e566ba9a74e797a97
+Icon application-certificate
+Guid 26f42cba-9bf6-5365-802b-e250eb757e96
+Guid b9e6422f-87b5-5459-baf2-206413298506
+Flags [internal] Device cannot be removed easily
+[updatable] Device is updatable in this or any other mode
+Fabricant Microsoft
+DeviceId 32b0ea7f50551f62e1b289f4735af7832042c594
+Created 17/12/2025
+CompositeId e680566c57c60f6136e48bb2f879a5df7ca629f1
+UEFI CA
+Version 2014
+VendorId UEFI:Lenovo
+Protocol org.uefi.dbx2
+Plugin uefi_db
+ParentDeviceId 0352a8acc949c7df21fec16e566ba9a74e797a97
+Icon application-certificate
+Guid 6e98b506-553c-57f1-9020-ed7a1db2c014
+Guid f2f2fb62-7f3c-5870-a860-b0490450fbdb
+Flags [internal] Device cannot be removed easily
+[updatable] Device is updatable in this or any other mode
+Fabricant Lenovo Group
+DeviceId 2c9551117aca12ec520ebc49805d21b669434961
+Created 17/12/2025
+CompositeId e680566c57c60f6136e48bb2f879a5df7ca629f1
+UEFI dbx
+VersionLowest 20230301
+Version 20230301
+VendorId UEFI:Microsoft
+Résumé UEFI revocation database
+Protocol org.uefi.dbx2
+Plugin uefi_dbx
+ParentDeviceId e680566c57c60f6136e48bb2f879a5df7ca629f1
+Icon application-certificate
+Guid 5971a208-da00-5fce-b5f5-1234342f9cf7
+Guid f8ba2887-9411-5c36-9cee-88995bb39731
+Flags [internal] Device cannot be removed easily
+[updatable] Device is updatable in this or any other mode
+[needs-reboot] Requires a reboot to apply firmware or to reload hardware
+DeviceId 362301da643102b9f38477387e2193e57abaa590
+Created 17/12/2025
+CompositeId e680566c57c60f6136e48bb2f879a5df7ca629f1
+Checksum 6a0e824654b7479152058cf738a378e629483874b6dbd67e0d8c3327b2fcac64
+Windows Production PCA
+Version 2011
+VendorId UEFI:Microsoft
+Protocol org.uefi.dbx2
+Plugin uefi_db
+ParentDeviceId 0352a8acc949c7df21fec16e566ba9a74e797a97
+Icon application-certificate
+Guid 675d2184-6c9a-59f1-a6f1-3c229b5dbb79
+Guid 1a84097f-714e-51b7-b293-9a803c98bb1d
+Flags [internal] Device cannot be removed easily
+[updatable] Device is updatable in this or any other mode
+Fabricant Microsoft
+DeviceId ea9d4960094c43d107b919fe44941f2c774f84df
+Created 17/12/2025
+CompositeId e680566c57c60f6136e48bb2f879a5df7ca629f1
+
Imprimantes
+Imprimantes
+Aucune imprimante trouvée
+
Batterie
+Power Status
+Power State AC
+AC Power Supply: ADP1
+Online Attached
+AC Power Type Mains
+Battery: BAT0
+State Charging
+Capacity 55 % / Normal
+Battery Usage -1 W
+Battery Health -23 %
+Design Full Energy 50,050 Wh
+Current Full Energy -11,400 Wh
+Design Full Capacity 4,390 Ah
+Current Full Capacity -1,000 Ah
+Voltage Design 11,400 V
+Battery Technology Li-ion
+Manufacturer SANYO
+Model Number 00HW020
+Serial Number 214
+
Capteurs
+Temperature
+temp1 49,00°C|acpitz
+temp2 33,00°C|acpitz
+temp1 46,00°C|pch_skylake
+GPU 1,00°C|thinkpad
+temp3 4,00°C|thinkpad
+temp4 45,00°C|thinkpad
+temp5 0,00°C|thinkpad
+temp6 0,00°C|thinkpad
+temp7 0,00°C|thinkpad
+temp8 0,00°C|thinkpad
+Package id 0 50,00°C|coretemp
+Core 0 50,00°C|coretemp
+Core 1 49,00°C|coretemp
+temp1 39,00°C|iwlwifi_1
+thermal_zone2 46,00°C|thermal
+thermal_zone0 49,00°C|thermal
+thermal_zone3 50,00°C|thermal
+thermal_zone1 33,00°C|thermal
+thermal_zone4 39,00°C|thermal
+Voltage
+in0 12,08V|BAT0
+Power
+power1 22,35 W|BAT0
+Fan Speed
+fan1 0,00 RPM|thinkpad
+fan2 0,00 RPM|thinkpad
+Drive Temperature
+CT480BX500SSD1 35,00°C|udisks2
+
Périphériques d'entrée
+Input Devices
+
+
+ Lid Switch Audio
+
+
+ Sleep Button Keyboard
+
+
+ Power Button Keyboard
+
+
+ AT Translated Set 2 keyboard Keyboard
+
+
+ ETPS/2 Elantech TrackPoint Mouse
+
+
+ ETPS/2 Elantech Touchpad Mouse
+
+
+ Wacom Pen and multitouch sensor Finger Mouse
+
+
+ Video Bus Keyboard
+
+
+ Wacom Pen and multitouch sensor Pen Mouse
+
+
+ PC Speaker Speaker
+
+
+ ThinkPad Extra Buttons Keyboard
+
+
+ HDA Intel PCH Mic Audio Intel
+
+
+ HDA Intel PCH Headphone Audio Intel
+
+
+ HDA Intel PCH HDMI/DP,pcm:3 Audio Intel
+
+
+ HDA Intel PCH HDMI/DP,pcm:7 Audio Intel
+
+
+ HDA Intel PCH HDMI/DP,pcm:8 Audio Intel
+
Stockage
+UDisks2
+
+
+sda Micron CT480BX500SSD1447,1 GiB
+
Ressources
+I/O Ports
+
+
+0000-0cf7 PCI Bus 0000:00
+
+
+ 0000-001f dma1
+
+
+ 0020-0021 pic1
+
+
+ 0040-0043 timer0
+
+
+ 0050-0053 timer1
+
+
+ 0060-0060 keyboard
+
+
+ 0062-0062 PNP0C09:00
+
+
+ 0062-0062 EC data
+
+
+ 0064-0064 keyboard
+
+
+ 0066-0066 PNP0C09:00
+
+
+ 0066-0066 EC cmd
+
+
+ 0070-0077 rtc0
+
+
+ 0080-008f dma page reg
+
+
+ 00a0-00a1 pic2
+
+
+ 00c0-00df dma2
+
+
+ 00f0-00ff fpu
+
+
+ 0400-041f Module Intel TCO WatchDog Timer Driver
+
+
+ 0400-041f Module Intel TCO WatchDog Timer Driver
+
+
+ 0680-069f pnp 00:02
+
+
+0cf8-0cff PCI conf1
+
+
+0d00-ffff PCI Bus 0000:00
+
+
+ 164e-164f pnp 00:02
+
+
+ 1800-1803 ACPI PM1a_EVT_BLK
+
+
+ 1804-1805 ACPI PM1a_CNT_BLK
+
+
+ 1808-180b ACPI PM_TMR
+
+
+ 1810-1815 ACPI CPU throttle
+
+
+ 1850-1850 ACPI PM2_CNT_BLK
+
+
+ 1854-1857 INT3F0D:00
+
+
+ 1854-1857 INT3F0D:00
+
+
+ 1880-189f ACPI GPE0_BLK
+
+
+ 2000-20fe pnp 00:01
+
+
+ 3000-3fff PCI Bus 0000:02
+
+
+ 4000-403f 0000:00:02.0
+
+
+ 4060-407f 0000:00:17.0
+
+
+ 4060-407f ahci
+
+
+ 4080-4087 0000:00:17.0
+
+
+ 4080-4087 ahci
+
+
+ 4088-408b 0000:00:17.0
+
+
+ 4088-408b ahci
+
+
+ efa0-efbf 0000:00:1f.4
+
+
+ ffff-ffff pnp 00:02
+
+
+ ffff-ffff pnp 00:02
+
+
+ ffff-ffff pnp 00:02
+Memory
+
+
+00000000-00000fff Reserved
+
+
+00058000-00058fff Reserved
+
+
+0008c000-000fffff Reserved
+
+
+ 000a0000-000bffff PCI Bus 0000:00
+
+
+ 000c0000-000cfbff Video ROM
+
+
+87c48000-87c49fff Reserved
+
+
+87f65000-87f65fff Reserved
+
+
+887ae000-887b7fff ACPI Tables
+
+
+8ca81000-8ca81fff ACPI Non-volatile Storage
+
+
+8ca82000-8caabfff Reserved
+
+
+963ac000-97f6afff Reserved
+
+
+97f6b000-97fbafff ACPI Non-volatile Storage
+
+
+97fbb000-97ffdfff ACPI Tables
+
+
+97fff000-980fffff Reserved
+
+
+ 97fff000-97ffffff MSFT0101:00
+
+
+ 97fff000-97ffffff MSFT0101:00
+
+
+98600000-9c7fffff Reserved
+
+
+ 9a800000-9c7fffff Graphics Stolen Memory
+
+
+9c800000-dfffffff PCI Bus 0000:00
+
+
+ 9c800000-9c81ffff pnp 00:07
+
+
+ a0000000-bfffffff 0000:00:02.0
+
+
+ c0000000-c0ffffff 0000:00:02.0
+
+
+ c1000000-c19fffff PCI Bus 0000:02
+
+
+ c1a00000-c1afffff PCI Bus 0000:03
+
+
+ c1a00000-c1a01fff 0000:03:00.0
+
+
+ c1a00000-c1a01fff Module Intel(R) Wireless WiFi driver for Linux
+
+
+ c1b00000-c25fffff PCI Bus 0000:02
+
+
+ c1b00000-c1b007ff 0000:02:00.0
+
+
+ c1b01000-c1b01fff 0000:02:00.0
+
+
+ c1b01000-c1b01fff mmc0
+
+
+ c2600000-c261ffff 0000:00:1f.6
+
+
+ c2600000-c261ffff Module Intel(R) PRO/1000 Network Driver
+
+
+ c2620000-c262ffff 0000:00:14.0
+
+
+ c2620000-c262ffff xhci-hcd
+
+
+ c2628070-c262846f intel_xhci_usb_sw
+
+
+ c2630000-c263ffff 0000:00:1f.3
+
+
+ c2640000-c2643fff 0000:00:1f.3
+
+
+ c2640000-c2643fff ICH HD audio
+
+
+ c2644000-c2647fff 0000:00:1f.2
+
+
+ c2648000-c2649fff 0000:00:17.0
+
+
+ c2648000-c2649fff ahci
+
+
+ c264a000-c264afff 0000:00:08.0
+
+
+ c264b000-c264bfff 0000:00:13.0
+
+
+ c264b000-c264bfff Module Intel(R) Integrated Sensor Hub PCI Device Driver
+
+
+ c264c000-c264cfff 0000:00:14.2
+
+
+ c264c000-c264cfff Intel PCH thermal driver
+
+
+ c264d000-c264dfff 0000:00:16.0
+
+
+ c264d000-c264dfff Module Intel(R) Management Engine Interface
+
+
+ c264e000-c264efff 0000:00:1e.0
+
+
+ c264e000-c264e1ff lpss_dev
+
+
+ c264e000-c264e1ff serial
+
+
+ c264e200-c264e2ff lpss_priv
+
+
+ c264e800-c264efff idma64.0
+
+
+ c264e800-c264efff idma64.0 idma64.0
+
+
+ c264f000-c264f7ff 0000:00:17.0
+
+
+ c264f000-c264f7ff ahci
+
+
+ c2650000-c26500ff 0000:00:1f.4
+
+
+ c2650000-c26500ff i801_smbus
+
+
+ c2651000-c26510ff 0000:00:17.0
+
+
+ c2651000-c26510ff ahci
+
+
+e0000000-efffffff PCI ECAM 0000 [bus 00-ff]
+
+
+ e00fa000-e00fafff Reserved
+
+
+ e00fd000-e00fdfff Reserved
+
+
+fd000000-fe7fffff PCI Bus 0000:00
+
+
+ fd000000-fdabffff pnp 00:00
+
+
+ fdac0000-fdacffff INT344B:00
+
+
+ fdac0000-fdacffff INT344B:00 INT344B:00
+
+
+ fdad0000-fdadffff pnp 00:00
+
+
+ fdae0000-fdaeffff INT344B:00
+
+
+ fdae0000-fdaeffff INT344B:00 INT344B:00
+
+
+ fdaf0000-fdafffff INT344B:00
+
+
+ fdaf0000-fdafffff INT344B:00 INT344B:00
+
+
+ fdb00000-fdffffff pnp 00:00
+
+
+ fdc6000c-fdc6000f Module Intel TCO WatchDog Timer Driver
+
+
+ fdc6000c-fdc6000f iTCO_wdt iTCO_wdt
+
+
+ fe000000-fe010fff Reserved
+
+
+ fe028000-fe028fff pnp 00:06
+
+
+ fe029000-fe029fff pnp 00:06
+
+
+ fe036000-fe03bfff pnp 00:00
+
+
+ fe03d000-fe3fffff pnp 00:00
+
+
+ fe410000-fe7fffff pnp 00:00
+
+
+fe800000-fe80ffff pnp 00:02
+
+
+fec00000-fec003ff IOAPIC 0
+
+
+fed00000-fed003ff pnp 00:07
+
+
+fed10000-fed17fff pnp 00:07
+
+
+fed18000-fed18fff pnp 00:07
+
+
+fed19000-fed19fff pnp 00:07
+
+
+fed20000-fed3ffff pnp 00:07
+
+
+fed40000-fed40fff MSFT0101:00
+
+
+fed45000-fed8ffff pnp 00:07
+
+
+fed90000-fed90fff dmar0
+
+
+fed91000-fed91fff dmar1
+
+
+fee00000-feefffff pnp 00:07
+
+
+ff000000-ffffffff INT0800:00
+
+
+ ff000000-ffffffff pnp 00:07
+
+
+262800000-263ffffff RAM buffer
+DMA
+
+
+ 4 cascade
+IRQ
+
+
+ 1 IR-IO-APIC 1-edge i8042
+
+
+ 8 IR-IO-APIC 8-edge rtc0
+
+
+ 9 IR-IO-APIC 9-fasteoi acpi
+
+
+ 12 IR-IO-APIC 12-edge i8042
+
+
+ 14 IR-IO-APIC 14-fasteoi INT344B:00
+
+
+ 16 IR-IO-APIC 16-fasteoi i801_smbus
+
+
+ 20 IR-IO-APIC 20-fasteoi idma64.0, intel_ish_ipc
+
+
+ 120 IR-PCI-MSI-0000:00:1c.0 0-edge PCIe PME, aerdrv, pciehp, PCIe bwctrl
+
+
+ 121 IR-PCI-MSI-0000:00:1c.5 0-edge PCIe PME, aerdrv, PCIe bwctrl
+
+
+ 122 IR-PCI-MSI-0000:00:17.0 0-edge ahci[0000:00:17.0]
+
+
+ 123 IR-PCI-MSI-0000:00:14.0 0-edge xhci_hcd
+
+
+ 128 IR-PCI-MSI-0000:00:1f.6 0-edge enp0s31f6
+
+
+ 129 IR-PCI-MSI-0000:02:00.0 0-edge mmc0
+
+
+ 130 IR-PCI-MSI-0000:00:02.0 0-edge i915
+
+
+ 131 IR-PCI-MSI-0000:00:16.0 0-edge mei_me
+
+
+ 132 als-dev0 als_consumer0
+
+
+ 140 accel_3d-dev4 accel_3d_consumer4
+
+
+ 142 IR-PCI-MSI-0000:03:00.0 0-edge iwlwifi
+
+
+ 143 IR-PCI-MSI-0000:00:1f.3 0-edge snd_hda_intel:card0
+
+
+ NMI Non-maskable interrupts
+
+
+ LOC Local timer interrupts
+
+
+ SPU Spurious interrupts
+
+
+ PMI Performance monitoring interrupts
+
+
+ IWI IRQ work interrupts
+
+
+ RTR APIC ICR read retries
+
+
+ RES Rescheduling interrupts
+
+
+ CAL Function call interrupts
+
+
+ TLB TLB shootdowns
+
+
+ TRM Thermal event interrupts
+
+
+ THR Threshold APIC interrupts
+
+
+ DFR Deferred Error APIC interrupts
+
+
+ MCE Machine check exceptions
+
+
+ MCP Machine check polls
+
+
+ ERR
+
+
+ MIS
+
+
+ PIN Posted-interrupt notification event
+
+
+ NPI Nested posted-interrupt event
+
+
+ PIW Posted-interrupt wakeup event
+
+
+ PMN Posted MSI notification event
+
Réseau Interfaces
+Interfaces Réseau
+lo 127.0.0.1|5,27MiB|5,27MiB
+enp0s31f6 |0,00MiB|0,00MiB
+wlp3s0 10.0.1.97|95,84MiB|3795,56MiB
+br-22ef1384d7b3 172.21.0.1|2,39MiB|0,15MiB
+br-5a2d5332a90b 172.18.0.1|2,31MiB|0,21MiB
+br-62e9638f6fe6 172.19.0.1|0,84MiB|0,03MiB
+docker0 172.17.0.1|0,00MiB|0,00MiB
+br-b8e0951a36d8 172.20.0.1|0,97MiB|2,10MiB
+veth1a9bd69 |0,87MiB|0,03MiB
+veth6797043 |2,42MiB|0,16MiB
+vethfbc2f8e |0,81MiB|0,00MiB
+vethebef7bc |0,26MiB|0,08MiB
+vetha97a377 |0,06MiB|0,01MiB
+
Connections IP
+Connections
+
+
+0.0.0.0:445 LISTEN 0.0.0.0:* tcp
+
+
+0.0.0.0:22 LISTEN 0.0.0.0:* tcp
+
+
+0.0.0.0:139 LISTEN 0.0.0.0:* tcp
+
+
+127.0.0.1:11434 LISTEN 0.0.0.0:* tcp
+
+
+0.0.0.0:5201 0.0.0.0:* udp
+
+
+0.0.0.0:5355 0.0.0.0:* udp
+
+
+0.0.0.0:5355 0.0.0.0:* udp
+
+
+0.0.0.0:5355 0.0.0.0:* udp
+
+
+0.0.0.0:5355 0.0.0.0:* udp
+
+
+0.0.0.0:5355 0.0.0.0:* udp
+
+
+127.0.0.1:64423 LISTEN 0.0.0.0:* tcp
+
+
+172.17.0.1:54138 0.0.0.0:* udp
+
+
+172.20.0.1:55191 0.0.0.0:* udp
+
+
+172.18.0.1:35978 0.0.0.0:* udp
+
+
+127.0.0.1:51095 LISTEN 0.0.0.0:* tcp
+
+
+127.0.0.1:34701 LISTEN 0.0.0.0:* tcp
+
+
+127.0.0.1:34503 0.0.0.0:* udp
+
+
+172.21.0.1:50789 0.0.0.0:* udp
+
+
+0.0.0.0:3552 LISTEN 0.0.0.0:* tcp
+
+
+0.0.0.0:8000 LISTEN 0.0.0.0:* tcp
+
+
+0.0.0.0:8007 LISTEN 0.0.0.0:* tcp
+
+
+0.0.0.0:8087 LISTEN 0.0.0.0:* tcp
+
+
+0.0.0.0:3702 0.0.0.0:* udp
+
+
+0.0.0.0:3702 0.0.0.0:* udp
+
+
+0.0.0.0:3702 0.0.0.0:* udp
+
+
+0.0.0.0:3702 0.0.0.0:* udp
+
+
+0.0.0.0:3702 0.0.0.0:* udp
+
+
+127.0.0.1:631 ESTABLISHED 127.0.0.1:59336 tcp
+
+
+10.0.1.97:47055 0.0.0.0:* udp
+
+
+172.19.0.1:45035 0.0.0.0:* udp
+
+
+10.0.1.97:33432 ESTABLISHED 151.101.193.91:443 tcp
+
+
+10.0.1.97:58804 ESTABLISHED 34.107.221.82:80 tcp
+
+
+10.0.1.97:58810 ESTABLISHED 34.107.221.82:80 tcp
+
+
+10.0.1.97:35414 ESTABLISHED 151.101.193.91:443 tcp
+
+
+10.0.1.97:36404 ESTABLISHED 160.79.104.10:443 tcp
+
+
+10.0.1.97:36418 ESTABLISHED 160.79.104.10:443 tcp
+
+
+10.0.1.97:36420 ESTABLISHED 160.79.104.10:443 tcp
+
+
+10.0.1.97:36436 ESTABLISHED 160.79.104.10:443 tcp
+
+
+10.0.1.97:36438 ESTABLISHED 160.79.104.10:443 tcp
+
+
+10.0.1.97:43648 ESTABLISHED 160.79.104.10:443 tcp
+
+
+10.0.1.97:43664 ESTABLISHED 160.79.104.10:443 tcp
+
+
+10.0.1.97:43666 ESTABLISHED 160.79.104.10:443 tcp
+
+
+10.0.1.97:47368 ESTABLISHED 34.107.243.93:443 tcp
+
+
+10.0.1.97:48826 ESTABLISHED 160.79.104.10:443 tcp
+
+
+10.0.1.97:48832 ESTABLISHED 160.79.104.10:443 tcp
+
+
+10.0.1.97:48834 ESTABLISHED 160.79.104.10:443 tcp
+
+
+10.0.1.97:54940 ESTABLISHED 10.0.0.106:8080 tcp
+
+
+10.0.1.97:58390 ESTABLISHED 160.79.104.10:443 tcp
+
+
+10.0.1.97:58392 ESTABLISHED 160.79.104.10:443 tcp
+
+
+10.0.1.97:58396 ESTABLISHED 160.79.104.10:443 tcp
+
+
+10.0.1.97:58408 ESTABLISHED 160.79.104.10:443 tcp
+
+
+10.0.1.97:58418 ESTABLISHED 160.79.104.10:443 tcp
+
+
+10.0.1.97:58426 ESTABLISHED 160.79.104.10:443 tcp
+
+
+10.0.1.97:58430 ESTABLISHED 160.79.104.10:443 tcp
+
+
+127.0.0.1:59336 ESTABLISHED 127.0.0.1:631 tcp
+
+
+10.0.1.97:46900 ESTABLISHED 10.0.0.105:8080 tcp
+
+
+10.0.1.97:36904 ESTABLISHED 13.107.213.42:443 tcp
+
+
+10.0.1.97:42550 ESTABLISHED 92.157.245.37:443 tcp
+
+
+127.0.0.1:631 ESTABLISHED 127.0.0.1:59336 tcp
+
+
+fe80::c05f:62ff:f:44025 :::* udp6
+
+
+:::445 LISTEN :::* tcp6
+
+
+fe80::f4db:8ff:fe:50593 :::* udp6
+
+
+:::22 LISTEN :::* tcp6
+
+
+:::41134 LISTEN :::* tcp6
+
+
+:::139 LISTEN :::* tcp6
+
+
+fe80::ce2:15ff:fe:60548 :::* udp6
+
+
+fe80::802b:caff:f:47883 :::* udp6
+
+
+:::5201 :::* udp6
+
+
+:::5355 :::* udp6
+
+
+:::5355 :::* udp6
+
+
+:::5355 :::* udp6
+
+
+:::5355 :::* udp6
+
+
+:::5355 :::* udp6
+
+
+fe80::a8fc:baff:f:52282 :::* udp6
+
+
+fe80::d097:11ff:f:53431 :::* udp6
+
+
+fe80::b888:3dff:f:53825 :::* udp6
+
+
+::1:631 LISTEN :::* tcp6
+
+
+fe80::f68c:50ff:f:51179 :::* udp6
+
+
+fe80::7420:a1ff:f:39641 :::* udp6
+
+
+fe80::4c2a:90ff:f:52158 :::* udp6
+
+
+::1:42587 :::* udp6
+
+
+:::3552 LISTEN :::* tcp6
+
+
+:::8000 LISTEN :::* tcp6
+
+
+:::8007 LISTEN :::* tcp6
+
+
+:::8087 LISTEN :::* tcp6
+
+
+:::3702 :::* udp6
+
+
+:::3702 :::* udp6
+
+
+:::3702 :::* udp6
+
+
+:::3702 :::* udp6
+
+
+:::3702 :::* udp6
+
+
+fe80::8c4b:66ff:f:52238 :::* udp6
+
+
+172.17.0.1:54138 0.0.0.0:* udp
+
+
+0.0.0.0:5201 0.0.0.0:* udp
+
+
+0.0.0.0:5353 0.0.0.0:* udp
+
+
+0.0.0.0:5355 0.0.0.0:* udp
+
+
+0.0.0.0:5355 0.0.0.0:* udp
+
+
+0.0.0.0:5355 0.0.0.0:* udp
+
+
+0.0.0.0:5355 0.0.0.0:* udp
+
+
+0.0.0.0:5355 0.0.0.0:* udp
+
+
+172.21.0.1:50789 0.0.0.0:* udp
+
+
+127.0.0.1:34503 0.0.0.0:* udp
+
+
+239.255.255.250:1900 0.0.0.0:* udp
+
+
+172.20.0.1:1900 0.0.0.0:* udp
+
+
+239.255.255.250:1900 0.0.0.0:* udp
+
+
+172.17.0.1:1900 0.0.0.0:* udp
+
+
+239.255.255.250:1900 0.0.0.0:* udp
+
+
+172.19.0.1:1900 0.0.0.0:* udp
+
+
+239.255.255.250:1900 0.0.0.0:* udp
+
+
+172.18.0.1:1900 0.0.0.0:* udp
+
+
+239.255.255.250:1900 0.0.0.0:* udp
+
+
+172.21.0.1:1900 0.0.0.0:* udp
+
+
+239.255.255.250:1900 0.0.0.0:* udp
+
+
+10.0.1.97:1900 0.0.0.0:* udp
+
+
+239.255.255.250:1900 0.0.0.0:* udp
+
+
+127.0.0.1:1900 0.0.0.0:* udp
+
+
+172.20.0.1:55191 0.0.0.0:* udp
+
+
+10.0.1.97:47055 0.0.0.0:* udp
+
+
+0.0.0.0:59442 0.0.0.0:* udp
+
+
+0.0.0.0:43975 0.0.0.0:* udp
+
+
+172.18.0.1:35978 0.0.0.0:* udp
+
+
+0.0.0.0:3702 0.0.0.0:* udp
+
+
+0.0.0.0:3702 0.0.0.0:* udp
+
+
+0.0.0.0:3702 0.0.0.0:* udp
+
+
+0.0.0.0:3702 0.0.0.0:* udp
+
+
+0.0.0.0:3702 0.0.0.0:* udp
+
+
+0.0.0.0:36536 0.0.0.0:* udp
+
+
+0.0.0.0:53098 0.0.0.0:* udp
+
+
+0.0.0.0:49129 0.0.0.0:* udp
+
+
+172.19.0.1:45035 0.0.0.0:* udp
+
+
+10.0.1.97:68 ESTABLISHED 10.0.0.1:67 udp
+
+
+172.21.255.255:137 0.0.0.0:* udp
+
+
+172.21.0.1:137 0.0.0.0:* udp
+
+
+172.20.255.255:137 0.0.0.0:* udp
+
+
+172.20.0.1:137 0.0.0.0:* udp
+
+
+172.19.255.255:137 0.0.0.0:* udp
+
+
+172.19.0.1:137 0.0.0.0:* udp
+
+
+172.18.255.255:137 0.0.0.0:* udp
+
+
+172.18.0.1:137 0.0.0.0:* udp
+
+
+172.17.255.255:137 0.0.0.0:* udp
+
+
+172.17.0.1:137 0.0.0.0:* udp
+
+
+10.0.3.255:137 0.0.0.0:* udp
+
+
+10.0.1.97:137 0.0.0.0:* udp
+
+
+0.0.0.0:137 0.0.0.0:* udp
+
+
+172.21.255.255:138 0.0.0.0:* udp
+
+
+172.21.0.1:138 0.0.0.0:* udp
+
+
+172.20.255.255:138 0.0.0.0:* udp
+
+
+172.20.0.1:138 0.0.0.0:* udp
+
+
+172.19.255.255:138 0.0.0.0:* udp
+
+
+172.19.0.1:138 0.0.0.0:* udp
+
+
+172.18.255.255:138 0.0.0.0:* udp
+
+
+172.18.0.1:138 0.0.0.0:* udp
+
+
+172.17.255.255:138 0.0.0.0:* udp
+
+
+172.17.0.1:138 0.0.0.0:* udp
+
+
+10.0.3.255:138 0.0.0.0:* udp
+
+
+10.0.1.97:138 0.0.0.0:* udp
+
+
+0.0.0.0:138 0.0.0.0:* udp
+
+
+0.0.0.0:41134 0.0.0.0:* udp
+
+
+:::5201 :::* udp6
+
+
+:::5353 :::* udp6
+
+
+:::5355 :::* udp6
+
+
+:::5355 :::* udp6
+
+
+:::5355 :::* udp6
+
+
+:::5355 :::* udp6
+
+
+:::5355 :::* udp6
+
+
+fe80::f4db:8ff:fe:50593 :::* udp6
+
+
+::1:42587 :::* udp6
+
+
+::1:1900 :::* udp6
+
+
+ff05::c:1900 :::* udp6
+
+
+fe80::f68c:50ff:fe:1900 :::* udp6
+
+
+ff02::c:1900 :::* udp6
+
+
+fe80::d097:11ff:fe:1900 :::* udp6
+
+
+ff02::c:1900 :::* udp6
+
+
+fe80::802b:caff:fe:1900 :::* udp6
+
+
+ff02::c:1900 :::* udp6
+
+
+fe80::8c4b:66ff:fe:1900 :::* udp6
+
+
+ff02::c:1900 :::* udp6
+
+
+fe80::4c2a:90ff:fe:1900 :::* udp6
+
+
+ff02::c:1900 :::* udp6
+
+
+fe80::a8fc:baff:fe:1900 :::* udp6
+
+
+ff02::c:1900 :::* udp6
+
+
+fe80::f4db:8ff:fe4:1900 :::* udp6
+
+
+ff02::c:1900 :::* udp6
+
+
+fe80::c05f:62ff:fe:1900 :::* udp6
+
+
+ff02::c:1900 :::* udp6
+
+
+fe80::ce2:15ff:fee:1900 :::* udp6
+
+
+ff02::c:1900 :::* udp6
+
+
+fe80::b888:3dff:fe:1900 :::* udp6
+
+
+ff02::c:1900 :::* udp6
+
+
+fe80::7420:a1ff:fe:1900 :::* udp6
+
+
+ff02::c:1900 :::* udp6
+
+
+fe80::f68c:50ff:f:51179 :::* udp6
+
+
+fe80::7420:a1ff:f:39641 :::* udp6
+
+
+fe80::802b:caff:f:47883 :::* udp6
+
+
+fe80::4c2a:90ff:f:52158 :::* udp6
+
+
+fe80::c05f:62ff:f:44025 :::* udp6
+
+
+fe80::8c4b:66ff:f:52238 :::* udp6
+
+
+fe80::a8fc:baff:f:52282 :::* udp6
+
+
+fe80::ce2:15ff:fe:60548 :::* udp6
+
+
+:::3702 :::* udp6
+
+
+:::3702 :::* udp6
+
+
+:::3702 :::* udp6
+
+
+:::3702 :::* udp6
+
+
+:::3702 :::* udp6
+
+
+:::53090 :::* udp6
+
+
+fe80::d097:11ff:f:53431 :::* udp6
+
+
+fe80::f68c:50ff:fe8:546 :::* udp6
+
+
+fe80::b888:3dff:f:53825 :::* udp6
+
Table de Routage
+IP routing table
+
+
+0.0.0.0 / 10.0.0.1 0.0.0.0 UG wlp3s0
+
+
+10.0.0.0 / 0.0.0.0 255.255.252.0 U wlp3s0
+
+
+172.17.0.0 / 0.0.0.0 255.255.0.0 U docker0
+
+
+172.18.0.0 / 0.0.0.0 255.255.0.0 U br-5a2d5332a90b
+
+
+172.19.0.0 / 0.0.0.0 255.255.0.0 U br-62e9638f6fe6
+
+
+172.20.0.0 / 0.0.0.0 255.255.0.0 U br-b8e0951a36d8
+
+
+172.21.0.0 / 0.0.0.0 255.255.0.0 U br-22ef1384d7b3
+
Table ARP
+Table ARP
+
+
+10.0.0.101 18:60:24:b0:80:ec wlp3s0
+
+
+10.0.0.5 bc:24:11:2c:d8:8f wlp3s0
+
+
+10.0.0.106 bc:24:11:f9:8f:44 wlp3s0
+
+
+10.0.0.203 bc:24:11:89:df:80 wlp3s0
+
+
+10.0.0.105 bc:24:11:af:57:52 wlp3s0
+
+
+10.0.0.15 bc:24:11:ed:2b:2b wlp3s0
+
+
+172.21.0.2 56:37:d0:c6:a8:e8 br-22ef1384d7b3
+
+
+10.0.0.214 bc:24:11:df:4b:f9 wlp3s0
+
+
+10.0.0.26 8e:b4:ab:2a:ba:7b wlp3s0
+
+
+172.19.0.2 aa:86:1b:e9:14:dd br-62e9638f6fe6
+
+
+172.20.0.4 ba:f6:b6:25:54:8a br-b8e0951a36d8
+
+
+10.0.1.105 02:7e:d5:ba:2f:1b wlp3s0
+
+
+172.20.0.3 1a:24:56:4c:a3:85 br-b8e0951a36d8
+
+
+10.0.0.201 bc:24:11:e0:a5:e0 wlp3s0
+
+
+10.0.0.8 26:f9:1c:cd:2e:68 wlp3s0
+
+
+10.0.0.24 60:57:18:99:ed:05 wlp3s0
+
+
+10.0.0.233 f6:70:00:08:db:33 wlp3s0
+
+
+10.0.1.192 ec:71:db:d9:4d:bf wlp3s0
+
+
+10.0.0.2 02:e2:88:d4:02:62 wlp3s0
+
+
+10.0.0.1 bc:24:11:74:64:7e wlp3s0
+
+
+172.20.0.2 5a:08:b3:86:82:49 br-b8e0951a36d8
+
Serveurs DNS
+Nom des Serveurs
+
+
+10.0.0.15 adguard.home
+
+
+fe80::5607:7dff:fe8c:b894%wlp3s0 adguard.home
+
Statistiques
+IP
+ Forwarding: 1
+ 306596 total packets received
+ 1 with invalid addresses
+ 4479 forwarded
+ 0 incoming packets discarded
+ 299801 incoming packets delivered
+ 249375 requests sent out
+ 7 outgoing packets dropped
+ 24 dropped because of missing route
+ 160 fragments dropped after timeout
+ 3768 reassemblies required
+ 1548 packets reassembled ok
+ 160 packet reassemblies failed
+ OutTransmits: 253854
+ICMP
+ 31 ICMP messages received
+ 4 input ICMP message failed
+ Histogramme d'entrée ICMP
+ destination unreachable: 9
+ echo requests: 16
+ echo replies: 6
+ 255 ICMP messages sent
+ 0 ICMP messages failed
+ OutRateLimitHost: 157
+ Histogramme de sortie ICMP
+ destination unreachable: 227
+ echo requests: 12
+ echo replies: 16
+ICMPMSG
+ InType0: 6
+ InType3: 9
+ InType8: 16
+ OutType0: 16
+ OutType3: 227
+ OutType8: 12
+TCP
+ 27861 active connection openings
+ 113 passive connection openings
+ 26769 failed connection attempts
+ 110 connection resets received
+ 29 connections established
+ 293004 segments received
+ 295702 segments sent out
+ 125 segments retransmitted
+ 0 bad segments received
+ 27505 resets sent
+UDP
+ 48366 packets received
+ 377 packets to unknown port received
+ 3 packet receive errors
+ 20676 packets sent
+ 3 receive buffer errors
+ 7 send buffer errors
+ IgnoredMulti: 4422
+UDPLITE
+TCPEXT
+ 43 packets pruned from receive queue because of socket buffer overrun
+ 2 packets pruned from receive queue
+ 503 TCP sockets finished time wait in fast timer
+ 58 packets rejected in established connections because of timestamp
+ BeyondWindow: 2
+ PAWSOldAck: 5
+ 1024 delayed acks sent
+ 2 delayed acks further delayed because of locked socket
+ Quick ack mode was activated 1744 times
+ 22203 packet headers predicted
+ 12397 acknowledgments not containing data payload received
+ 14769 predicted acknowledgments
+ TCPSackRecovery: 31
+ Detected reordering 186 times using SACK
+ Detected reordering 39 times using time stamp
+ 31 congestion windows partially recovered using Hoe heuristic
+ 1 congestion windows recovered without slow start after partial ack
+ TCPLostRetransmit: 15
+ 63 fast retransmits
+ TCPTimeouts: 32
+ TCPLossProbes: 43
+ 2051 packets collapsed in receive queue due to low socket buffer
+ TCPBacklogCoalesce: 746
+ TCPDSACKOldSent: 1747
+ TCPDSACKRecv: 67
+ 288 connections reset due to unexpected data
+ 25 connections reset due to early user close
+ 30 connections aborted due to timeout
+ TCPDSACKIgnoredNoUndo: 38
+ TCPSackShifted: 176
+ TCPSackMerged: 43
+ TCPSackShiftFallback: 171
+ TCPRcvCoalesce: 31829
+ TCPOFOQueue: 12948
+ TCPSpuriousRtxHostQueues: 3
+ TCPAutoCorking: 840
+ TCPFromZeroWindowAdv: 21
+ TCPToZeroWindowAdv: 19
+ TCPWantZeroWindowAdv: 1447
+ TCPSynRetrans: 3
+ TCPOrigDataSent: 54915
+ TCPACKSkippedPAWS: 53
+ TCPACKSkippedSeq: 3
+ TCPKeepAlive: 903
+ TCPDelivered: 55542
+ TCPAckCompressed: 6479
+ TCPRcvQDrop: 2
+ TcpTimeoutRehash: 29
+ TCPDSACKRecvSegs: 92
+IPEXT
+ InMcastPkts: 6665
+ OutMcastPkts: 5225
+ InBcastPkts: 6015
+ OutBcastPkts: 266
+ InOctets: 3815472913
+ OutOctets: 94054477
+ InMcastOctets: 3758210
+ OutMcastOctets: 2243693
+ InBcastOctets: 1360782
+ OutBcastOctets: 36412
+ InNoECTPkts: 2771710
+ InECT1Pkts: 23
+ InECT0Pkts: 38
+ InCEPkts: 1
+MPTCPEXT
+
Dossiers d'interopérabilités
+SAMBA
+Cannot parse smb.conf
+NFS
+No NFS exports
+
Benchmarks CPU Blowfish (Single-thread)
+CPU Blowfish (Single-thread)
+Intel Core i3 4160 10,92|4x 3600,00 MHz
+AMD Ryzen 5 PRO 3400GE 10,89|8x 3300.00 MHz
+Intel Core i7-3740QM 10,80|8x 3700.00 MHz
+AMD A9-9425 10,72|2x 3100.00 MHz
+AMD Ryzen 5 2400G 10,49|8x 3600.00 MHz
+AMD Ryzen 5 PRO 2600 10,48|12x 3400.00 MHz
+AMD FX-8370 10,42|1x 4334.71 MHz + 1x 4334.02 MHz + 1x 4314.73 MHz + 1x 4278.83 MHz + 1x 4274.69 MHz + 1x 4066.54 MHz + 1x 4023.79 MHz + 1x 3671.04 MHz
+AMD Opteron X3421 10,40|4x 2100.00 MHz
+Intel Pentium G2130 10,37|2x 3200.00 MHz
+Intel Core i5-3340M 10,33|4x 3400.00 MHz
+AMD Phenom II X6 1090T 10,32|6x 3200.00 MHz
+Intel Core i5-9300HF 10,30|8x 4100.00 MHz
+ This Machine Intel Core i5-6200U10,29|4x 2300,00 MHz
+Intel Core i7-2640M 10,23|4x 3500.00 MHz
+Intel Xeon E5-2690 0 (Dual) 10,20|32x 2900.00 MHz
+Intel Core i3-3240 10,17|4x 3400.00 MHz
+AMD Phenom II X4 970 10,14|4x 3500.00 MHz
+Intel Core i5-4460S 10,08|4x 3400.00 MHz
+Intel Pentium E5800 10,08|2x 3203.00 MHz
+Intel Core i7 M 640 10,07|4x 2800.00 MHz
+Intel Xeon E5-2699 v3 9,96|36x 3600.00 MHz
+Intel Pentium Silver N5000 9,89|4x 2700.00 MHz
+Intel Core i5-3230M 9,76|4x 3200.00 MHz
+Intel Pentium G3250 9,74|2x 3200.00 MHz
+Intel Core i3 530 9,72|4x 2933.00 MHz
+
CPU Blowfish (Multi-thread)
+CPU Blowfish (Multi-thread)
+
+
+AMD A8-5500 4x 3200,00 MHz 35,09
+
+
+AMD Phenom II X4 940 4x 3000.00 MHz 34,93
+
+
+AMD Ryzen 3 1200 4x 3100.00 MHz 34,28
+
+
+AMD FX-4300 4x 3800,00 MHz 33,96
+
+
+Intel Core i7-4610M 4x 3700.00 MHz 33,87
+
+
+Intel Core i7 Q 740 8x 1734.00 MHz 33,35
+
+
+Intel Pentium Silver N5000 4x 2700.00 MHz 33,18
+
+
+Amlogic A311D (VIM3) 4x 2208.00 MHz + 2x 1800.00 MHz 33,16
+
+
+Amlogic s922x (N2) 2x 1992.00 MHz + 4x 1908.00 MHz 32,64
+
+
+Intel Core i3-1005G1 4x 3400.00 MHz 31,74
+
+
+Intel Core i3-7100U 4x 2400.00 MHz 31,51
+
+
+Intel Core 2 Extreme Q9300 4x 2535.00 MHz 31,25
+
+
+ This Machine Intel Core i5-6200U4x 2300,00 MHz 31,13
+
+
+Intel Core i3 4160 4x 3600,00 MHz 31,12
+
+
+Intel Celeron N4120 4x 2600.00 MHz 30,52
+
+
+Intel Core i3 7020U 4x 2300.00 MHz 30,44
+
+
+Intel Core i5-4200H 4x 3400.00 MHz 29,13
+
+
+Intel Core i5-4278U 4x 3100.00 MHz 28,79
+
+
+Intel Core i5-6260U 2x 1800.00 MHz 27,83
+
+
+Intel Core i7-4600U 4x 3300.00 MHz 27,32
+
+
+Intel Core i7 Q 720 8x 1600,00 MHz 27,13
+
+
+Intel Core i7-2620M 4x 3400.00 MHz 27,11
+
+
+Intel Core 2 Quad Q6700 4x 2670,00 MHz 26,50
+
+
+AMD A6-7310 4x 2000.00 MHz 26,38
+
+
+AMD A8-3820 4x 2500.00 MHz 25,48
+
CPU Blowfish (Multi-core)
+CPU Blowfish (Multi-core)
+
+
+Intel Xeon E5462 4x 2800.00 MHz 26,56
+
+
+AMD A8-6410 4x 2000.00 MHz 25,87
+
+
+Intel Pentium G4400 2x 3300.00 MHz 25,85
+
+
+AMD A8-3820 4x 2500.00 MHz 25,32
+
+
+Intel Pentium N3530 4x 2582,00 MHz 25,05
+
+
+Intel Atom E3950 4x 2000,00 MHz 24,92
+
+
+AMD PRO A8-8600B R6 4x 1600.00 MHz 24,39
+
+
+ZHAOXIN KaiXian KX-6000G/4 4x 3300.00 MHz 23,38
+
+
+Intel Core i7-6600U 4x 3400.00 MHz 22,79
+
+
+Intel Celeron J1900 4x 1999.00 MHz 22,44
+
+
+Intel Core i7-3540M 4x 3700.00 MHz 21,58
+
+
+Intel Core i3-7130U 4x 2700.00 MHz 21,30
+
+
+ This Machine Intel Core i5-6200U4x 2300,00 MHz 20,66
+
+
+Intel Pentium G2130 2x 3200.00 MHz 20,50
+
+
+Intel Core i3 550 4x 3200.00 MHz 20,48
+
+
+Intel Core i7 620M 4x 2667,00 MHz 20,08
+
+
+Intel Core i3 7020U 4x 2300.00 MHz 19,50
+
+
+Intel Celeron N3150 4x 2080.00 MHz 19,46
+
+
+AMD A10-5745M 4x 2100.00 MHz 19,32
+
+
+Intel Core i3-3220 4x 3300.00 MHz 19,31
+
+
+Intel Core i5-4310M 4x 3400.00 MHz 19,30
+
+
+AMD Athlon II X4 605e 4x 2300.00 MHz 18,76
+
+
+Intel Core i5-4570T 4x 3600.00 MHz 18,68
+
+
+Intel Core i5-6260U 2x 1800.00 MHz 18,50
+
+
+Intel Pentium G3240 2x 3100.00 MHz 18,39
+
Test CPU Zlib
+CPU Zlib
+
+
+Intel Celeron N4100 4x 2400.00 MHz 21,26
+
+
+Intel Core i5-3317U 4x 2600.00 MHz 21,09
+
+
+Intel Pentium G645 2x 2900.00 MHz 20,26
+
+
+Intel Pentium G860 2x 3000,00 MHz 19,77
+
+
+Intel Celeron G3930 2x 2900.00 MHz 19,34
+
+
+Intel Pentium G3250 2x 3200.00 MHz 19,02
+
+
+Intel Core i3-5015U 4x 2000.00 MHz 18,84
+
+
+Intel Pentium 5405U 4x 2300.00 MHz 18,80
+
+
+AMD Athlon II X4 605e 4x 2300.00 MHz 18,76
+
+
+Intel Core i5-3337U 4x 1700.00 MHz 18,65
+
+
+Intel Pentium N3520 4x 2415,00 MHz 18,21
+
+
+Intel Core i3-5010U 4x 2000.00 MHz 17,87
+
+
+ This Machine Intel Core i5-6200U4x 2300,00 MHz 17,60
+
+
+AMD Athlon 5350 4x 2050.00 MHz 17,57
+
+
+Pentium E6700 2x 3667,00 MHz 17,45
+
+
+Intel Core 2 Duo T9900 2x 3068.00 MHz 17,26
+
+
+Intel Core i3-2330M 4x 2200.00 MHz 17,04
+
+
+Intel Celeron G4900 2x 3100.00 MHz 17,04
+
+
+Intel Core m7-6Y75 4x 3100.00 MHz 16,58
+
+
+Intel Atom E3950 4x 2000,00 MHz 16,51
+
+
+Intel Celeron N3160 4x 1601.00 MHz 16,51
+
+
+Intel Core i3-4010U 4x 1700.00 MHz 16,35
+
+
+Intel Celeron G1610T 2x 2300.00 MHz 16,01
+
+
+Intel Celeron N4120 4x 2600.00 MHz 15,97
+
+
+AMD A6-3420M 4x 1500.00 MHz 15,90
+
Test CPU CryptoHash
+CPU CryptoHash
+
+
+Intel Celeron G4900 2x 3100.00 MHz 13,60
+
+
+Intel Core i7-2620M 4x 3400.00 MHz 13,48
+
+
+Intel Core 2 Duo P7550 2x 2261.00 MHz 13,38
+
+
+Intel Core i3-7130U 4x 2700.00 MHz 12,65
+
+
+AMD Phenom II X4 925 4x 2800.00 MHz 12,50
+
+
+Intel Core i3-2350M 4x 2300.00 MHz 12,26
+
+
+Intel Core i5 M 460 4x 2534.00 MHz 12,14
+
+
+Intel Core i3-2125 4x 3300.00 MHz 11,90
+
+
+Intel Atom D2700 3x 2133,00 MHz + 1x 2130,00 MHz 11,60
+
+
+Intel Core i3-2328M 4x 2200.00 MHz 11,32
+
+
+Intel Core 2 Duo T7500 2x 2201.00 MHz 11,25
+
+
+Intel Core 2 Extreme Q9300 4x 2535.00 MHz 11,10
+
+
+ This Machine Intel Core i5-6200U4x 2300,00 MHz 10,80
+
+
+Intel Core 2 Duo E8500 2x 3164.00 MHz 10,72
+
+
+Intel L2400 2x 1667.00 MHz 10,10
+
+
+Intel Core Duo T2250 2x 1733,00 MHz 10,05
+
+
+Intel Celeron N4100 4x 2400.00 MHz 10,05
+
+
+Intel Core 2 Quad Q6700 4x 2670,00 MHz 9,37
+
+
+Intel Celeron N4020 2x 2800.00 MHz 9,26
+
+
+Intel T2050 2x 1600,00 MHz 8,90
+
+
+Intel N200 4x 3700.00 MHz 8,72
+
+
+Intel Core i7-2617M 4x 2600,00 MHz 7,80
+
+
+Intel Xeon E3-1505M v5 8x 3700,00 MHz 7,80
+
+
+AMD E2-9000e 2x 1500.00 MHz 7,76
+
+
+AMD A4-4020 2x 3200.00 MHz 7,60
+
Test CPU Fibonacci
+CPU Fibonacci
+
+
+AMD Phenom II X4 905e 4x 2500.00 MHz 814,25
+
+
+Intel Core i3-6100 4x 3700.00 MHz 808,72
+
+
+Intel Core i5-4210M 4x 3200.00 MHz 806,86
+
+
+AMD A6-3600 4x 2100.00 MHz 796,05
+
+
+Intel Pentium G3240T 2x 2700,00 MHz 770,05
+
+
+Intel Core i5-4210U 4x 2700.00 MHz 768,09
+
+
+Intel Pentium G4600 4x 3600,00 MHz 756,36
+
+
+Intel Core i5-4258U 4x 2900,00 MHz 748,71
+
+
+Intel Celeron N4100 4x 2400.00 MHz 744,71
+
+
+Intel Core i5-2415M 4x 2900.00 MHz 731,75
+
+
+AMD Phenom II X4 965 4x 3400.00 MHz 728,84
+
+
+AMD Ryzen 5 PRO 3400GE 8x 3300.00 MHz 714,58
+
+
+ This Machine Intel Core i5-6200U4x 2300,00 MHz 711,23
+
+
+Intel Core i5-4310U 4x 3000.00 MHz 709,95
+
+
+Intel Core i5-6300U 4x 3000.00 MHz 707,68
+
+
+Intel Core i3-7130U 4x 2700.00 MHz 704,98
+
+
+Intel Xeon E5462 4x 2800.00 MHz 690,39
+
+
+Intel Core 2 Quad Q8200 4x 2336.00 MHz 689,34
+
+
+Rockchip RK3399 2x 1800.00 MHz + 4x 1416.00 MHz 676,88
+
+
+Intel Core i5-2410M 4x 2900.00 MHz 673,91
+
+
+Intel Pentium G3250 2x 3200.00 MHz 672,17
+
+
+AMD Athlon II X4 640 4x 3000.00 MHz 659,01
+
+
+Intel Core i5-5350U 4x 2900.00 MHz 647,36
+
+
+AMD A10-7800 4x 3500.00 MHz 643,83
+
+
+Intel Pentium G3420 2x 3200.00 MHz 634,60
+
Test CPU N-Queens
+CPU N-Queens
+
+
+AMD Ryzen 5 3450U 8x 2100.00 MHz 472,41
+
+
+Intel Core i5-4300U 4x 2900.00 MHz 458,18
+
+
+Intel Core i7-2635QM 4x 2000.00 MHz 456,45
+
+
+Intel Celeron N3160 4x 1601.00 MHz 448,76
+
+
+Intel Pentium N4200 4x 2500.00 MHz 443,46
+
+
+Intel Pentium Gold G7400 4x 3700.00 MHz 439,28
+
+
+Intel Core i7-1250U 4x 4700.00 MHz + 8x 3500.00 MHz 435,32
+
+
+Intel Xeon E5345 (Dual) 8x 2333.00 MHz 432,36
+
+
+Intel Core 2 Quad Q8200 4x 2336.00 MHz 430,50
+
+
+Intel Core i3-5015U 4x 2000.00 MHz 421,24
+
+
+Intel Core i5-3427U 4x 2800.00 MHz 419,70
+
+
+Intel Core i7 880 8x 3068.00 MHz 412,32
+
+
+ This Machine Intel Core i5-6200U4x 2300,00 MHz 408,16
+
+
+Intel Core i5-3360M 4x 3500.00 MHz 405,86
+
+
+AMD A10-5750M 4x 2500,00 MHz 396,02
+
+
+Intel Core i3-4000M 4x 2400.00 MHz 386,64
+
+
+AMD A12-9720P 4x 2700,00 MHz 385,23
+
+
+Intel Core i3-4030U 4x 1900.00 MHz 384,14
+
+
+AMD Athlon II X2 265 2x 3300.00 MHz 375,44
+
+
+Intel Core i3-5020U 4x 2100.00 MHz 373,88
+
+
+Intel Core i3-5010U 4x 2000.00 MHz 368,41
+
+
+ZHAOXIN KaiXian KX-6640MA 4x 2600.00 MHz 351,32
+
+
+Intel Celeron G1610 2x 2600.00 MHz 347,20
+
+
+Intel Xeon E5606 (Dual) 8x 2128.00 MHz 340,12
+
+
+Intel Xeon E3-1505M v5 8x 3700,00 MHz 340,04
+
Test FPU FFT
+FPU FFT
+
+
+Intel Core i7-4510U 4x 3100,00 MHz 52,56
+
+
+Intel Core i5-3320M 4x 3300.00 MHz 51,89
+
+
+Intel Pentium G2030 2x 3000.00 MHz 51,72
+
+
+Intel Core 2 Quad Q6700 4x 2670,00 MHz 51,42
+
+
+Intel Pentium Silver N5000 4x 2700.00 MHz 51,29
+
+
+Intel Core i5-4200H 4x 3400.00 MHz 51,14
+
+
+AMD A12-9720P 4x 2700,00 MHz 50,64
+
+
+Intel Core i5 750 4x 2661.00 MHz 50,28
+
+
+Intel Pentium G2020 2x 2900.00 MHz 48,81
+
+
+Intel Core i7 860 8x 2800.00 MHz 48,07
+
+
+Intel Core i5-5300U 4x 2900.00 MHz 47,47
+
+
+Intel Core i3-7100U 4x 2400.00 MHz 47,23
+
+
+ This Machine Intel Core i5-6200U4x 2300,00 MHz 46,73
+
+
+Intel Core i3-6100U 4x 2300.00 MHz 46,31
+
+
+Intel Core i5-4300U 4x 2900.00 MHz 46,19
+
+
+AMD A6-3600 4x 2100.00 MHz 45,19
+
+
+AMD Phenom II X4 905e 4x 2500.00 MHz 44,59
+
+
+Intel Pentium G645 2x 2900.00 MHz 44,38
+
+
+Intel Core i5-4260U 4x 2700.00 MHz 43,46
+
+
+Intel Core i5-2415M 4x 2900.00 MHz 42,96
+
+
+Intel Pentium 4417U 4x 2300.00 MHz 41,22
+
+
+AMD Athlon 5350 4x 2050.00 MHz 41,22
+
+
+Intel Core i7-2620M 4x 3400.00 MHz 40,62
+
+
+Broadcom BCM2711 (RPi4) 4x 2000.00 MHz 40,51
+
+
+Intel Pentium 2030M 2x 2500,00 MHz 38,72
+
FPU Raytracing (Single-thread)
+FPU Raytracing (Single-thread)
+
+
+Intel Xeon E3-1231 v3 8x 3800.00 MHz 2019,27
+
+
+Intel Xeon E3-1225 v3 4x 3600.00 MHz 1987,50
+
+
+Intel Core i5-4570T 4x 3600.00 MHz 1937,48
+
+
+Intel Pentium Silver N6005 4x 3300,00 MHz 1922,30
+
+
+Intel Pentium G4560 4x 3500.00 MHz 1905,90
+
+
+Intel Core i7-5500U 4x 3000.00 MHz 1900,65
+
+
+Intel Core i5-1035G7 8x 3700.00 MHz 1890,00
+
+
+AMD Ryzen 5 PRO 3400GE 8x 3300.00 MHz 1872,95
+
+
+Intel Core i5-4440S 4x 3300.00 MHz 1854,20
+
+
+Intel Xeon E5-1650 v2 12x 3900.00 MHz 1847,60
+
+
+Intel Core i7-4702MQ 8x 2200.00 MHz 1838,98
+
+
+Intel Core i5-4210M 4x 3200.00 MHz 1828,28
+
+
+ This Machine Intel Core i5-6200U4x 2300,00 MHz 1818,90
+
+
+Intel Core i7-3770T 4x 3700.00 MHz 1804,30
+
+
+AMD BC-250 1x 3493.37 MHz + 1x 3493.10 MHz + 1x 3491.74 MHz + 1x 3491.55 MHz + 1x 3491.40 MHz + 1x 3488.76 MHz + 1x 3488.64 MHz + 1x 3485.41 MHz + 1x 3476.04 MHz + 1x 3456.44 MHz + 1x 2501.92 MHz + 1x 2010.37 MHz 1775,60
+
+
+Intel Core i7-4800MQ 8x 2700.00 MHz 1770,40
+
+
+Intel Core i3-7100U 4x 2400.00 MHz 1756,16
+
+
+Intel Core i7-5820K 12x 3600.00 MHz 1733,67
+
+
+Intel Xeon E5-2680 v3 24x 3300.00 MHz 1718,05
+
+
+Intel Xeon E5-2687W 0 (Dual) 32x 3800.00 MHz 1679,60
+
+
+Intel Core i5-3450 4x 3500.00 MHz 1651,50
+
+
+Intel Pentium Silver N6000 4x 3300.00 MHz 1642,45
+
+
+Intel Celeron N5105 4x 2900.00 MHz 1641,90
+
+
+Intel Core i7-2600K 8x 3701.00 MHz 1609,90
+
+
+Intel Core i7-4650U 4x 3300.00 MHz 1598,63
+
Internal Network Speed
+Internal Network Speed
+
+
+Intel Core i3-3220 4x 3300.00 MHz 27,71
+
+
+AMD Ryzen 3 3300U 4x 2100.00 MHz 27,49
+
+
+Intel Xeon E5-2643 0 (Dual) 16x 3500.00 MHz 27,43
+
+
+Intel Core i7-2670QM 8x 3100.00 MHz 27,42
+
+
+Intel Core i7-2760QM 8x 3500.00 MHz 27,14
+
+
+Intel Pentium 4417U 4x 2300.00 MHz 26,97
+
+
+Intel Core i7-5500U 4x 3000.00 MHz 26,87
+
+
+AMD Ryzen 5 4500U 6x 2375.00 MHz 26,75
+
+
+Intel Core i5-3320M 4x 3300.00 MHz 26,66
+
+
+Intel Celeron N5100 4x 2800.00 MHz 26,60
+
+
+AMD Ryzen 5 2600 2x 3900.00 MHz + 1x 3899.00 MHz + 2x 3898.00 MHz + 1x 3896.00 MHz + 1x 3894.00 MHz + 1x 3891.00 MHz + 1x 3884.00 MHz + 1x 3883.00 MHz + 1x 3860.00 MHz + 1x 3857.00 MHz 26,50
+
+
+Intel Xeon E5-2630 v4 20x 2200.00 MHz 26,22
+
+
+ This Machine Intel Core i5-6200U4x 2300,00 MHz 26,20
+
+
+Intel Core i7-3537U 4x 3100.00 MHz 25,86
+
+
+Intel Xeon E5-2696 v4 (Dual) 88x 2200.00 MHz 25,60
+
+
+Intel Core i5-2410M 4x 2900.00 MHz 25,52
+
+
+Intel Xeon E5-2690 0 (Dual) 32x 2900.00 MHz 25,45
+
+
+Intel Core i7-5700HQ 8x 3500.00 MHz 25,13
+
+
+Intel Core i3-7020U 4x 2300.00 MHz 25,08
+
+
+AMD Ryzen 3 3200GE 4x 3300.00 MHz 24,62
+
+
+Intel Core i5-3210M 4x 3100.00 MHz 24,56
+
+
+Intel Xeon W3690 12x 3459.00 MHz 23,81
+
+
+Intel Xeon X5670 (Dual) 24x 2927.00 MHz 23,74
+
+
+AMD Ryzen 7 2800H 8x 3350,00 MHz 23,68
+
+
+Intel Xeon E5-2620 0 12x 2500.00 MHz 23,55
+
SysBench CPU (Single-thread)
+SysBench CPU (Single-thread)
+
+
+Intel Core i5-3360M 4x 3500,00 MHz 891,00
+
+
+Intel Core i7-10875H 16x 2300.00 MHz 879,05
+
+
+Intel Core 2 Quad Q8300 4x 2500.00 MHz 872,20
+
+
+Intel Core i5-2310 4x 3200.00 MHz 870,36
+
+
+Intel Pentium G3220 2x 3000,00 MHz 863,00
+
+
+Intel Core i5-4430S 4x 3200.00 MHz 846,91
+
+
+Intel Core i3 540 4x 3059.00 MHz 845,62
+
+
+Intel Core m3-7Y30 4x 2600.00 MHz 839,00
+
+
+Intel Core i5-5250U 4x 2700.00 MHz 835,10
+
+
+Intel Core i5-3230M 4x 3200.00 MHz 832,82
+
+
+Intel Core i5-5200U 4x 2700.00 MHz 827,42
+
+
+Intel Xeon E5-2450 v2 (Dual) 32x 3300.00 MHz 810,02
+
+
+ This Machine Intel Core i5-6200U4x 2300,00 MHz 810,00
+
+
+Intel Xeon E5-2640 v4 (Dual) 40x 2400.00 MHz 807,90
+
+
+Pentium T4500 2x 2300,00 MHz 807,33
+
+
+Intel Core i7-3610QM 8x 2300.00 MHz 806,13
+
+
+Intel Core i5-3427U 4x 2800.00 MHz 800,07
+
+
+Intel Core i5-4210U 4x 2700.00 MHz 790,67
+
+
+Intel Core i7-8650U 8x 1900.00 MHz 788,20
+
+
+Intel Core 2 Duo T6600 2x 2200.00 MHz 781,61
+
+
+Intel Xeon E5-2640 0 (Dual) 24x 3000.00 MHz 780,90
+
+
+Intel Core i5-2450M 4x 2500.00 MHz 777,20
+
+
+Intel Pentium G850 2x 2900.00 MHz 766,60
+
+
+Intel Pentium 5405U 4x 2300.00 MHz 751,29
+
+
+Amlogic S905 (C2) 4x 1536.00 MHz 748,56
+
SysBench CPU (Multi-thread)
+SysBench CPU (Multi-thread)
+
+
+Intel Core i3-3240 4x 3400.00 MHz 2915,86
+
+
+Intel Core i5-4570S 4x 3600,00 MHz 2799,00
+
+
+Intel Core i7-5700HQ 8x 3500.00 MHz 2777,38
+
+
+Intel Core i7-4600U 4x 3300.00 MHz 2709,22
+
+
+Intel Core i5-6200U 4x 2800.00 MHz 2670,66
+
+
+Intel Core i7-3520M 4x 3600.00 MHz 2605,04
+
+
+Intel Core i5-3230M 4x 3200.00 MHz 2580,36
+
+
+Intel Core i3-3220 4x 3300.00 MHz 2575,59
+
+
+Intel Core i7 620M 4x 2667,00 MHz 2566,00
+
+
+AMD A9-9425 2x 3100.00 MHz 2554,00
+
+
+AMD Phenom II X4 905e 4x 2500.00 MHz 2523,00
+
+
+AMD A8-4555M 4x 1600,00 MHz 2520,00
+
+
+ This Machine Intel Core i5-6200U4x 2300,00 MHz 2487,00
+
+
+Intel Core i5-5200U 4x 2700.00 MHz 2444,62
+
+
+Intel Core i3 540 4x 3059.00 MHz 2432,61
+
+
+Intel Core i5-4310U 4x 3000.00 MHz 2414,94
+
+
+Intel Core i5-4210U 4x 2700.00 MHz 2334,03
+
+
+Intel Core i7-2620M 4x 3400.00 MHz 2319,23
+
+
+Intel Core i3-6100U 4x 2300.00 MHz 2293,89
+
+
+Intel Core i7-5500U 4x 3000.00 MHz 2243,87
+
+
+Intel Core i3-2100 4x 3100.00 MHz 2243,00
+
+
+Intel Core i5-2410M 4x 2900.00 MHz 2233,00
+
+
+Intel Pentium E5700 2x 3003.00 MHz 2096,00
+
+
+Intel Core i5 M 450 4x 2400.00 MHz 2085,09
+
+
+Intel Pentium N3540 4x 2665.00 MHz 2071,50
+
SysBench Memory (Single-thread)
+SysBench Memory (Single-thread)
+
+
+Intel Core i7-3632QM 8x 2200.00 MHz 4479,40
+
+
+Intel Core i7-3610QM 8x 2300.00 MHz 4335,34
+
+
+Intel Core i5-4258U 4x 2900,00 MHz 4295,00
+
+
+AMD Ryzen 3 2200G 4x 3500.00 MHz 4262,02
+
+
+Intel Xeon E5-2697 v4 (Dual) 36x 2300.00 MHz 4230,69
+
+
+Intel Core i5-4250U 4x 2600.00 MHz 4208,55
+
+
+Intel Xeon E5-2630 v2 (Dual) 24x 3100.00 MHz 4180,30
+
+
+Intel Core 2 Duo P9700 2x 2801.00 MHz 4168,18
+
+
+Intel Core i3-7100U 4x 2400.00 MHz 4161,46
+
+
+Intel Core 2 Quad Q9650 4x 2992.00 MHz 4158,00
+
+
+Intel Xeon X3450 1x 2666.55 MHz + 3x 2666.55 MHz 4146,95
+
+
+Intel Core i5-2415M 4x 2900.00 MHz 4117,43
+
+
+ This Machine Intel Core i5-6200U4x 2300,00 MHz 4089,00
+
+
+Intel Core i5 M 460 4x 2534,00 MHz 4082,03
+
+
+Intel Core 2 Duo E7500 2x 2933.00 MHz 4040,56
+
+
+Intel Pentium 5405U 4x 2300.00 MHz 3974,50
+
+
+Intel Core i3-7020U 4x 2300.00 MHz 3966,00
+
+
+Intel Xeon E5530 1x 2605,00 MHz + 1x 2311,00 MHz + 1x 2282,00 MHz + 1x 2253,00 MHz 3952,00
+
+
+Intel Core i5 M 450 4x 2400.00 MHz 3948,20
+
+
+Intel Core i3-3120M 4x 2500.00 MHz 3919,77
+
+
+Intel Core i5-3317U 4x 2600.00 MHz 3898,16
+
+
+AMD Phenom II X4 965 4x 3400.00 MHz 3891,33
+
+
+Intel Pentium E5400 1x 2699.92 MHz + 1x 2699.92 MHz 3702,28
+
+
+AMD A10-6700 4x 3700,00 MHz 3673,00
+
+
+Intel Celeron J4105 4x 2500.00 MHz 3668,27
+
SysBench Memory (Multi-thread)
+SysBench Memory (Multi-thread)
+
+
+Intel Core i7-8650U 8x 1900.00 MHz 9512,70
+
+
+Intel Core i5-3427U 4x 2800.00 MHz 9491,52
+
+
+Intel Core i5-2410M 4x 2900.00 MHz 9491,33
+
+
+Intel Core i5-1235U 4x 4400.00 MHz + 8x 3300.00 MHz 9434,32
+
+
+Intel Core i5 M 460 4x 2534,00 MHz 9421,17
+
+
+Intel Core i5-4430S 4x 3200.00 MHz 9198,15
+
+
+Intel Core i5-5350U 4x 2900,00 MHz 9178,29
+
+
+AMD Ryzen 5 PRO 3500U 8x 2100,00 MHz 9118,00
+
+
+Intel Core i5-3210M 4x 3100.00 MHz 9011,07
+
+
+Intel Xeon E-2224G 4x 3500.00 MHz 9007,00
+
+
+Intel Xeon X5675 (Dual) 24x 3060.00 MHz 9004,00
+
+
+Apple M1 4x 3204.00 MHz + 4x 2064.00 MHz 8981,00
+
+
+ This Machine Intel Core i5-6200U4x 2300,00 MHz 8771,00
+
+
+Intel Core i3-7100U 4x 2400.00 MHz 8724,44
+
+
+Intel Core i5-4200U 4x 2600.00 MHz 8690,84
+
+
+Intel Core i5-8257U 8x 3900.00 MHz 8671,37
+
+
+Intel Core i5-6500 4x 3600.00 MHz 8649,40
+
+
+Intel Core i5-2520M 4x 3200.00 MHz 8630,74
+
+
+AMD Ryzen 5 3500X 6x 3600.00 MHz 8596,78
+
+
+Intel Core i5-3317U 4x 2600.00 MHz 8492,96
+
+
+Intel Core i5-4440S 4x 3300.00 MHz 8451,11
+
+
+Intel Core Ultra 7 155H 4x 4800.00 MHz + 8x 4500.00 MHz + 8x 3800.00 MHz + 2x 2500.00 MHz 8417,57
+
+
+Intel Core i3-2370M 4x 2400.00 MHz 8414,04
+
+
+Broadcom BCM2711 (RPi4) 4x 2000.00 MHz 8335,45
+
+
+AMD Ryzen 7 PRO 1700X 16x 3400.00 MHz 8241,78
+
Test GPU Drawing
+GPU Drawing
+
+
+ATI RV350 253,41
+
+
+Intel Iris 550 253,27
+
+
+GeForce GT 630 241,04
+
+
+AMD Radeon Pro W5500 240,29
+
+
+ATI RV515 229,48
+
+
+ATI RS480 221,55
+
+
+AMD ATI RS690 218,14
+
+
+RENOIR 210,25
+
+
+NVidia GeForce GTX 860M 182,32
+
+
+NVidia GF108 179,81
+
+
+AMD ATI RS480 170,03
+
+
+AMD Wrestler 168,12
+
+
+NVidia Quadro P620 143,57
+
+
+Intel HD 405 129,30
+
+
+AMD Radeon Pro W6400 119,74
+
+
+Intel HD P630 119,73
+
+
+Intel Iris Pro P5200 116,76
+
+
+Intel Iris Plus 645 116,73
+
+
+NVidia GeForce GTX 670M 96,20
+
+
+Intel Atom D2xxx/N2xxx Integrated 91,89
+
+
+NVidia GeForce 940MX 89,76
+
+
+Virtual 80,42
+
+
+Intel 945GSE 59,24
+
+
+Apple M2 49,13
+
+
+ This Machine Mesa Intel HD Graphics 52027,90
+
GPU OpenGL Drawing
+GPU OpenGL Drawing
+GeForce 210 157,34
+NV86 153,85
+NVidia Quadro 2000 147,89
+GeForce 310 140,40
+NVidia GeForce 9800 GT 137,40
+NVidia Quadro P620 136,50
+NVidia GeForce GTS 450 Rev. 2 133,22
+AMD Radeon R2 131,46
+AMD RV620 128,65
+GeForce 8400 127,44
+NVidia GeForce 8400 117,85
+Quadro NVS 160M 112,22
+ This Machine Mesa Intel HD Graphics 520106,98
+NVidia GeForce GT 320M 106,14
+NVidia GeForce 9400M 105,39
+NVidia GeForce 8400M GT 104,04
+AMD RV610 100,40
+Virtual 98,72
+NVidia GeForce GT 620 98,47
+AMD RS780 94,20
+NVidia GeForce GT 430 92,73
+Intel Q45/Q43 76,66
+Software 72,95
+NVidia GeForce 9500 GT 72,14
+NVidia GeForce 8400 GS Rev. 2 72,07
+
Storage R/W Speed
+Storage R/W Speed
+Phison PSSBN128GA87BC0 437,61
+WD SanDisk WDC WDS500G2B0A-00SM50 430,88
+Samsung SAMSUNG MZ7LN256HAJQ-000H1 425,24
+Netac SSD 512GB 424,31
+Kingston SKC600256G 421,51
+Intel SSDSC2BW180A3L 419,99
+XrayDisk 1TB SSD 419,58
+SSV5 418,10
+256GB EVM SSD 414,72
+XrayDisk 256GB 409,56
+Apacer Apacer AS340 240GB 404,24
+SK hynix SK hynix SC311 SATA 256GB 389,41
+ This Machine Micron CT480BX500SSD1380,76
+Patriot P210 2048GB 370,06
+Flash Disk 369,92
+Samsung SSD 830 Series 367,70
+Samsung SSD 860 EVO M.2 500GB 366,43
+Micron CT480BX500SSD1/HG 366,09
+Samsung SSD 840 EVO 250GB 364,25
+CT500P3SSD8 363,61
+MSI S270 240GB 362,59
+Micron CT250BX100SSD1 361,14
+Seagate ST1000DM010-2EP102 359,70
+Micron C300-CTFDDAC064MAG 356,68
+SPCC M.2 SSD 355,42
+
Cache/Memory
+Cache/Memory
+AMD Ryzen 3 PRO 2200G 36322,35|4x 3500.00 MHz
+AMD Ryzen 7 3700U 36287,94|8x 2300.00 MHz
+Intel Core i5-6500T 36211,45|4x 2500.00 MHz
+AMD Ryzen 5 PRO 4650U 36134,69|12x 2100.00 MHz
+AMD Custom 0932 (Valve Galileo) 36069,62|8x 3501.00 MHz
+AMD Ryzen 5 1600 35421,62|12x 3200.00 MHz
+Intel Core i7-4710HQ 34946,38|8x 3500,00 MHz
+Intel Core i7-3770K 34676,88|8x 3501.00 MHz
+Intel Core i5-4200M 34520,50|4x 3100,00 MHz
+AMD Ryzen 7 1700 33742,77|16x 3000,00 MHz
+Intel N100 33702,13|4x 3400,00 MHz
+Intel Xeon E5-2680 v4 33648,66|28x 3300.00 MHz
+ This Machine Intel Core i5-6200U33372,84|4x 2300,00 MHz
+Intel Core i5-5300U 32305,06|4x 2900.00 MHz
+Intel Core i5-5200U 32272,28|4x 2700,00 MHz
+AMD Ryzen 5 PRO 2400G 32206,28|8x 3600,00 MHz
+Intel Core i5-5350U 32052,25|4x 2900,00 MHz
+Intel Xeon E5-2650 v4 31455,14|24x 2201,00 MHz
+Intel Core i7-4930K 30978,88|12x 3900,00 MHz
+Intel Core i5-6260U 30812,28|4x 2900.00 MHz
+AMD Ryzen 5 5500U 30727,44|12x 4056,00 MHz
+Intel Core i5-4200U 30581,50|4x 2600,00 MHz
+Intel Core i7-5820K 30544,93|12x 3600.00 MHz
+Intel Core i7-3770 30522,36|8x 3900,00 MHz
+AMD Athlon 3000G 30299,36|4x 3500,00 MHz
+
+
\ No newline at end of file
diff --git a/icons/favicon/icons8-devices-3d-fluency-16.png b/icons/favicon/icons8-devices-3d-fluency-16.png
new file mode 100755
index 0000000..b4ded81
Binary files /dev/null and b/icons/favicon/icons8-devices-3d-fluency-16.png differ
diff --git a/icons/favicon/icons8-devices-3d-fluency-32.png b/icons/favicon/icons8-devices-3d-fluency-32.png
new file mode 100755
index 0000000..cab0586
Binary files /dev/null and b/icons/favicon/icons8-devices-3d-fluency-32.png differ
diff --git a/icons/favicon/icons8-devices-3d-fluency-57.png b/icons/favicon/icons8-devices-3d-fluency-57.png
new file mode 100755
index 0000000..715a675
Binary files /dev/null and b/icons/favicon/icons8-devices-3d-fluency-57.png differ
diff --git a/icons/favicon/icons8-devices-3d-fluency-60.png b/icons/favicon/icons8-devices-3d-fluency-60.png
new file mode 100755
index 0000000..2f5a16b
Binary files /dev/null and b/icons/favicon/icons8-devices-3d-fluency-60.png differ
diff --git a/icons/favicon/icons8-devices-3d-fluency-70.png b/icons/favicon/icons8-devices-3d-fluency-70.png
new file mode 100755
index 0000000..de863a9
Binary files /dev/null and b/icons/favicon/icons8-devices-3d-fluency-70.png differ
diff --git a/icons/favicon/icons8-devices-3d-fluency-72.png b/icons/favicon/icons8-devices-3d-fluency-72.png
new file mode 100755
index 0000000..6403f95
Binary files /dev/null and b/icons/favicon/icons8-devices-3d-fluency-72.png differ
diff --git a/icons/favicon/icons8-devices-3d-fluency-76.png b/icons/favicon/icons8-devices-3d-fluency-76.png
new file mode 100755
index 0000000..e30def0
Binary files /dev/null and b/icons/favicon/icons8-devices-3d-fluency-76.png differ
diff --git a/icons/favicon/icons8-devices-3d-fluency-96.png b/icons/favicon/icons8-devices-3d-fluency-96.png
new file mode 100755
index 0000000..a51b0d9
Binary files /dev/null and b/icons/favicon/icons8-devices-3d-fluency-96.png differ
diff --git a/icons/icons8-benchmark-64.png b/icons/icons8-benchmark-64.png
new file mode 100755
index 0000000..ee24655
Binary files /dev/null and b/icons/icons8-benchmark-64.png differ
diff --git a/icons/icons8-bios-94.png b/icons/icons8-bios-94.png
new file mode 100755
index 0000000..e296061
Binary files /dev/null and b/icons/icons8-bios-94.png differ
diff --git a/icons/icons8-check-mark-48.png b/icons/icons8-check-mark-48.png
new file mode 100755
index 0000000..a198782
Binary files /dev/null and b/icons/icons8-check-mark-48.png differ
diff --git a/icons/icons8-close-48.png b/icons/icons8-close-48.png
new file mode 100755
index 0000000..1f4caa2
Binary files /dev/null and b/icons/icons8-close-48.png differ
diff --git a/icons/icons8-debian-48.png b/icons/icons8-debian-48.png
new file mode 100755
index 0000000..c59aa27
Binary files /dev/null and b/icons/icons8-debian-48.png differ
diff --git a/icons/icons8-delete-48.png b/icons/icons8-delete-48.png
new file mode 100755
index 0000000..a1d2731
Binary files /dev/null and b/icons/icons8-delete-48.png differ
diff --git a/icons/icons8-done-48.png b/icons/icons8-done-48.png
new file mode 100755
index 0000000..7ca088a
Binary files /dev/null and b/icons/icons8-done-48.png differ
diff --git a/icons/icons8-edit-pencil-48.png b/icons/icons8-edit-pencil-48.png
new file mode 100755
index 0000000..0952e0c
Binary files /dev/null and b/icons/icons8-edit-pencil-48.png differ
diff --git a/icons/icons8-ethernet-on-94.png b/icons/icons8-ethernet-on-94.png
new file mode 100755
index 0000000..97fe259
Binary files /dev/null and b/icons/icons8-ethernet-on-94.png differ
diff --git a/icons/icons8-gpu-64.png b/icons/icons8-gpu-64.png
new file mode 100755
index 0000000..81c990f
Binary files /dev/null and b/icons/icons8-gpu-64.png differ
diff --git a/icons/icons8-hardware-64.png b/icons/icons8-hardware-64.png
new file mode 100755
index 0000000..9bab239
Binary files /dev/null and b/icons/icons8-hardware-64.png differ
diff --git a/icons/icons8-hdd-94.png b/icons/icons8-hdd-94.png
new file mode 100755
index 0000000..2c41411
Binary files /dev/null and b/icons/icons8-hdd-94.png differ
diff --git a/icons/icons8-laptop-50.png b/icons/icons8-laptop-50.png
new file mode 100755
index 0000000..b2b02cf
Binary files /dev/null and b/icons/icons8-laptop-50.png differ
diff --git a/icons/icons8-memory-slot-94.png b/icons/icons8-memory-slot-94.png
new file mode 100755
index 0000000..81c6f5d
Binary files /dev/null and b/icons/icons8-memory-slot-94.png differ
diff --git a/icons/icons8-motherboard-94.png b/icons/icons8-motherboard-94.png
new file mode 100755
index 0000000..941bfb2
Binary files /dev/null and b/icons/icons8-motherboard-94.png differ
diff --git a/icons/icons8-network-cable-94.png b/icons/icons8-network-cable-94.png
new file mode 100755
index 0000000..50333cb
Binary files /dev/null and b/icons/icons8-network-cable-94.png differ
diff --git a/icons/icons8-operating-system-64.png b/icons/icons8-operating-system-64.png
new file mode 100755
index 0000000..3a21dcc
Binary files /dev/null and b/icons/icons8-operating-system-64.png differ
diff --git a/icons/icons8-pcie-48.png b/icons/icons8-pcie-48.png
new file mode 100755
index 0000000..1b2acf6
Binary files /dev/null and b/icons/icons8-pcie-48.png differ
diff --git a/icons/icons8-picture-48.png b/icons/icons8-picture-48.png
new file mode 100755
index 0000000..4aa0aad
Binary files /dev/null and b/icons/icons8-picture-48.png differ
diff --git a/icons/icons8-processor-64.png b/icons/icons8-processor-64.png
new file mode 100755
index 0000000..99ffae7
Binary files /dev/null and b/icons/icons8-processor-64.png differ
diff --git a/icons/icons8-processor-94.png b/icons/icons8-processor-94.png
new file mode 100755
index 0000000..8df55ea
Binary files /dev/null and b/icons/icons8-processor-94.png differ
diff --git a/icons/icons8-ram-64.png b/icons/icons8-ram-64.png
new file mode 100755
index 0000000..22f4b7b
Binary files /dev/null and b/icons/icons8-ram-64.png differ
diff --git a/icons/icons8-save-48.png b/icons/icons8-save-48.png
new file mode 100755
index 0000000..b551de1
Binary files /dev/null and b/icons/icons8-save-48.png differ
diff --git a/icons/icons8-server-94.png b/icons/icons8-server-94.png
new file mode 100755
index 0000000..8aff138
Binary files /dev/null and b/icons/icons8-server-94.png differ
diff --git a/icons/icons8-setting-48.png b/icons/icons8-setting-48.png
new file mode 100755
index 0000000..06fc5cb
Binary files /dev/null and b/icons/icons8-setting-48.png differ
diff --git a/icons/icons8-shared-folder-94.png b/icons/icons8-shared-folder-94.png
new file mode 100755
index 0000000..88f2ffa
Binary files /dev/null and b/icons/icons8-shared-folder-94.png differ
diff --git a/icons/icons8-ssd-94.png b/icons/icons8-ssd-94.png
new file mode 100755
index 0000000..344ead7
Binary files /dev/null and b/icons/icons8-ssd-94.png differ
diff --git a/icons/icons8-usb-memory-stick-94.png b/icons/icons8-usb-memory-stick-94.png
new file mode 100755
index 0000000..0c5c5b6
Binary files /dev/null and b/icons/icons8-usb-memory-stick-94.png differ
diff --git a/icons/icons8-windows-11-48.png b/icons/icons8-windows-11-48.png
new file mode 100755
index 0000000..2549981
Binary files /dev/null and b/icons/icons8-windows-11-48.png differ
diff --git a/icons/icons8-workstation-94.png b/icons/icons8-workstation-94.png
new file mode 100755
index 0000000..cc706f2
Binary files /dev/null and b/icons/icons8-workstation-94.png differ
diff --git a/result_bench.md b/result_bench.md
old mode 100644
new mode 100755
diff --git a/scripts/bench.sh b/scripts/bench.sh
index 68de7a0..f36c3b5 100755
--- a/scripts/bench.sh
+++ b/scripts/bench.sh
@@ -2,7 +2,7 @@
#
# Linux BenchTools - Client Benchmark Script
-# Version: 1.1.0
+# Version: 1.3.0
#
# Collecte les informations hardware, exécute les benchmarks
# puis envoie les résultats au serveur backend (payload JSON).
@@ -14,7 +14,7 @@ set -e
# Version / variables globales
#=========================================================
-BENCH_SCRIPT_VERSION="1.2.0"
+BENCH_SCRIPT_VERSION="1.3.1"
# Couleurs
GREEN='\033[0;32m'
@@ -26,16 +26,16 @@ NC='\033[0m' # No Color
TOTAL_STEPS=8
CURRENT_STEP=0
-# Paramètres réseau / API (fixés)
-SERVER_URL="10.0.1.97:8007" # ajouter le port ou le schéma dans send_benchmark_payload si besoin
-API_TOKEN="29855796dacf5cfe75ff9b02d6adf3dd0f9c52db5b53e7abfb4c0df7ece1be0a"
-IPERF_SERVER="10.0.1.97"
+# Paramètres réseau / API (peuvent être surchargés par variables d'environnement)
+SERVER_URL="${SERVER_URL:-10.0.1.97:8007}" # ajouter le port ou le schéma dans send_benchmark_payload si besoin
+API_TOKEN="${API_TOKEN:-29855796dacf5cfe75ff9b02d6adf3dd0f9c52db5b53e7abfb4c0df7ece1be0a}"
+IPERF_SERVER="${IPERF_SERVER:-10.0.1.97}"
# Mode DEBUG
# Mettre à 1 pour afficher le payload JSON complet avant envoi
# Mettre à 0 pour désactiver le debug
-DEBUG_PAYLOAD="${DEBUG_PAYLOAD:-1}" # Par défaut: 1 (activé)
- # Peut être désactivé via: DEBUG_PAYLOAD=0 bash bench.sh
+DEBUG_PAYLOAD="${DEBUG_PAYLOAD:-0}" # Par défaut: 0 (désactivé)
+ # Peut être activé via: DEBUG_PAYLOAD=1 bash bench.sh
# Forcer locale en anglais pour parsing
export LC_ALL=C
@@ -50,9 +50,18 @@ RAM_INFO="null"
GPU_INFO="null"
MOTHERBOARD_INFO="null"
STORAGE_INFO="[]"
+PARTITION_INFO="[]"
NETWORK_INFO="[]"
+PCI_INFO="[]"
+USB_INFO="[]"
+NETWORK_SHARES_INFO="[]"
BENCHMARK_RESULTS="null"
+SMARTCTL_CMD="sudo smartctl"
+if command -v timeout &>/dev/null; then
+ SMARTCTL_CMD="timeout 10s sudo smartctl"
+fi
+
#=========================================================
# Affichage / logs
#=========================================================
@@ -190,6 +199,58 @@ safe_bc() {
echo "$out"
}
+bytes_to_gb() {
+ local bytes="$1"
+ if [[ -z "$bytes" || "$bytes" == "0" ]]; then
+ echo ""
+ else
+ safe_bc "scale=2; $bytes / (1024*1024*1024)"
+ fi
+}
+
+# Detect the current graphical session type (wayland/x11/tty)
+detect_session_type() {
+ local session_type="${XDG_SESSION_TYPE:-}"
+
+ if [[ -z "$session_type" && -n "$USER" && $(command -v loginctl) ]]; then
+ local session_id
+ session_id=$(loginctl 2>/dev/null | awk -v user="$USER" '$3==user {print $1; exit}')
+ if [[ -n "$session_id" ]]; then
+ session_type=$(loginctl show-session "$session_id" -p Type 2>/dev/null | awk -F= '/Type=/ {print $2}')
+ fi
+ fi
+
+ if [[ -z "$session_type" && -n "$WAYLAND_DISPLAY" ]]; then
+ session_type="wayland"
+ elif [[ -z "$session_type" && -n "$DISPLAY" ]]; then
+ session_type="x11"
+ fi
+
+ echo "$session_type"
+}
+
+# Try to detect the primary screen resolution
+detect_screen_resolution() {
+ local resolution=""
+
+ if command -v xrandr &>/dev/null && [[ -n "$DISPLAY" ]]; then
+ resolution=$(xrandr --current 2>/dev/null | awk '/\*/ {print $1; exit}')
+ fi
+
+ if [[ -z "$resolution" && -n "$DISPLAY" ]] && command -v xdpyinfo &>/dev/null; then
+ resolution=$(xdpyinfo 2>/dev/null | awk '/dimensions:/ {print $2; exit}')
+ fi
+
+ if [[ -z "$resolution" ]] && command -v swaymsg &>/dev/null; then
+ resolution=$(swaymsg -t get_outputs 2>/dev/null | jq -r '
+ [.[] | select(.active == true) | "\(.current_mode.width)x\(.current_mode.height)"] | .[0]'
+ )
+ [[ "$resolution" == "null" ]] && resolution=""
+ fi
+
+ echo "$resolution"
+}
+
#=========================================================
# Étape 1 : Système
#=========================================================
@@ -198,12 +259,48 @@ collect_system_info() {
log_step "Collecte des informations système de base"
local hostname os_name os_version kernel arch
+ local session_type screen_resolution last_boot uptime_seconds
+ local battery_percentage="" battery_status="" battery_health=""
hostname=$(hostname)
os_name=$(grep '^ID=' /etc/os-release | cut -d= -f2 | tr -d '"')
os_version=$(grep '^VERSION=' /etc/os-release | cut -d= -f2 | tr -d '"')
kernel=$(uname -r)
arch=$(uname -m)
+ session_type=$(detect_session_type)
+ screen_resolution=$(detect_screen_resolution)
+
+ if command -v who &>/dev/null; then
+ last_boot=$(who -b 2>/dev/null | awk '{print $3 " " $4}')
+ fi
+
+ if [[ -r /proc/uptime ]]; then
+ uptime_seconds=$(awk '{print int($1)}' /proc/uptime)
+ fi
+
+ for bat in /sys/class/power_supply/BAT*; do
+ [[ -d "$bat" ]] || continue
+ if [[ -f "$bat/present" ]]; then
+ local present
+ present=$(cat "$bat/present")
+ [[ "$present" != "1" ]] && continue
+ fi
+
+ if [[ -z "$battery_percentage" && -f "$bat/capacity" ]]; then
+ battery_percentage=$(cat "$bat/capacity" | tr -dc '0-9.')
+ fi
+ if [[ -z "$battery_status" && -f "$bat/status" ]]; then
+ battery_status=$(cat "$bat/status")
+ fi
+ if [[ -z "$battery_health" ]]; then
+ if [[ -f "$bat/health" ]]; then
+ battery_health=$(cat "$bat/health")
+ elif [[ -f "$bat/uevent" ]]; then
+ battery_health=$(grep -m1 'POWER_SUPPLY_HEALTH' "$bat/uevent" 2>/dev/null | cut -d= -f2)
+ fi
+ fi
+ break
+ done
SYSTEM_INFO=$(jq -n \
--arg hostname "$hostname" \
@@ -211,19 +308,40 @@ collect_system_info() {
--arg os_version "$os_version" \
--arg kernel "$kernel" \
--arg arch "$arch" \
+ --arg session "$session_type" \
+ --arg resolution "$screen_resolution" \
+ --arg last_boot "$last_boot" \
+ --arg uptime "$uptime_seconds" \
+ --arg battery_pct "$battery_percentage" \
+ --arg battery_status "$battery_status" \
+ --arg battery_health "$battery_health" \
'{
hostname: $hostname,
os: {
name: $os_name,
version: $os_version,
kernel_version: $kernel,
- architecture: $arch
+ architecture: $arch,
+ session_type: (if $session != "" then $session else null end),
+ display_server: (if $session != "" then $session else null end),
+ screen_resolution: (if $resolution != "" then $resolution else null end),
+ last_boot_time: (if $last_boot != "" then $last_boot else null end),
+ uptime_seconds: (if $uptime != "" then ($uptime | tonumber?) else null end),
+ battery_percentage: (if $battery_pct != "" then ($battery_pct | tonumber?) else null end),
+ battery_status: (if $battery_status != "" then $battery_status else null end),
+ battery_health: (if $battery_health != "" then $battery_health else null end)
}
}')
log_info "Hostname: $hostname"
log_info "OS: $os_name $os_version"
log_info "Kernel: $kernel"
+ [[ -n "$session_type" ]] && log_info "Session: $session_type"
+ [[ -n "$screen_resolution" ]] && log_info "Résolution écran: $screen_resolution"
+ [[ -n "$last_boot" ]] && log_info "Dernier boot: $last_boot"
+ if [[ -n "$battery_percentage" ]]; then
+ log_info "Batterie: ${battery_percentage}% ${battery_status:+($battery_status)}"
+ fi
echo ""
}
@@ -442,13 +560,23 @@ collect_hardware_info() {
# Essayer d'obtenir plus d'infos avec nvidia-smi
if command -v nvidia-smi &>/dev/null; then
local nvidia_model nvidia_vram nvidia_driver
- nvidia_model=$(nvidia-smi --query-gpu=name --format=csv,noheader 2>/dev/null | head -1)
- nvidia_vram=$(nvidia-smi --query-gpu=memory.total --format=csv,noheader,nounits 2>/dev/null | head -1)
- nvidia_driver=$(nvidia-smi --query-gpu=driver_version --format=csv,noheader 2>/dev/null | head -1)
+ # Vérifier que nvidia-smi fonctionne avant d'extraire les infos
+ if nvidia-smi &>/dev/null; then
+ nvidia_model=$(nvidia-smi --query-gpu=name --format=csv,noheader 2>/dev/null | head -1 | tr -d '\n')
+ nvidia_vram=$(nvidia-smi --query-gpu=memory.total --format=csv,noheader,nounits 2>/dev/null | head -1 | tr -d '\n')
+ nvidia_driver=$(nvidia-smi --query-gpu=driver_version --format=csv,noheader 2>/dev/null | head -1 | tr -d '\n')
- [[ -n "$nvidia_model" ]] && gpu_model="$nvidia_model"
- [[ -n "$nvidia_vram" ]] && gpu_vram="$nvidia_vram"
- [[ -n "$nvidia_driver" ]] && gpu_driver="$nvidia_driver"
+ # Ne remplacer que si les valeurs sont non-vides et valides
+ if [[ -n "$nvidia_model" && ! "$nvidia_model" =~ (failed|error|Error) ]]; then
+ gpu_model="$nvidia_model"
+ fi
+ if [[ -n "$nvidia_vram" && "$nvidia_vram" =~ ^[0-9]+$ ]]; then
+ gpu_vram="$nvidia_vram"
+ fi
+ if [[ -n "$nvidia_driver" && ! "$nvidia_driver" =~ (failed|error|Error) ]]; then
+ gpu_driver="$nvidia_driver"
+ fi
+ fi
fi
elif echo "$gpu_line" | grep -qi 'amd'; then
gpu_vendor="AMD"
@@ -510,6 +638,79 @@ collect_hardware_info() {
log_info "Motherboard: $mb_manufacturer $mb_model"
log_info "BIOS: $bios_version ($bios_date)"
echo ""
+
+ collect_pci_devices
+ collect_usb_devices
+}
+
+collect_pci_devices() {
+ PCI_INFO="[]"
+
+ if ! command -v lspci &>/dev/null; then
+ log_warn "lspci non disponible - périphériques PCI ignorés"
+ return
+ fi
+
+ local count=0
+ while IFS= read -r line; do
+ [[ -z "$line" ]] && continue
+
+ local slot class vendor device
+ slot=$(echo "$line" | awk '{print $1}')
+ class=$(echo "$line" | cut -d'"' -f2)
+ vendor=$(echo "$line" | cut -d'"' -f4)
+ device=$(echo "$line" | cut -d'"' -f6)
+
+ [[ -z "$slot" ]] && continue
+
+ local entry
+ entry=$(jq -n \
+ --arg slot "$slot" \
+ --arg class "$class" \
+ --arg vendor "$vendor" \
+ --arg device "$device" \
+ '{slot: $slot, class: $class, vendor: $vendor, device: $device}')
+
+ PCI_INFO=$(echo "$PCI_INFO" | jq --argjson e "$entry" '. + [$e]')
+ count=$((count + 1))
+ done < <(lspci -mm 2>/dev/null || true)
+
+ log_info "Périphériques PCI détectés: $count"
+}
+
+collect_usb_devices() {
+ USB_INFO="[]"
+
+ if ! command -v lsusb &>/dev/null; then
+ log_warn "lsusb non disponible - périphériques USB ignorés"
+ return
+ fi
+
+ local count=0
+ while IFS= read -r line; do
+ [[ -z "$line" ]] && continue
+ if [[ "$line" =~ Bus[[:space:]]([0-9]+)[[:space:]]Device[[:space:]]([0-9]+):[[:space:]]ID[[:space:]]([0-9a-fA-F]+):([0-9a-fA-F]+)[[:space:]](.*)$ ]]; then
+ local bus="${BASH_REMATCH[1]}"
+ local dev="${BASH_REMATCH[2]}"
+ local vendor_id="${BASH_REMATCH[3]}"
+ local product_id="${BASH_REMATCH[4]}"
+ local name="${BASH_REMATCH[5]}"
+
+ local entry
+ entry=$(jq -n \
+ --arg bus "$bus" \
+ --arg device "$dev" \
+ --arg vendor_id "$vendor_id" \
+ --arg product_id "$product_id" \
+ --arg name "$name" \
+ '{bus: $bus, device: $device, vendor_id: $vendor_id, product_id: $product_id, name: $name}')
+
+ USB_INFO=$(echo "$USB_INFO" | jq --argjson e "$entry" '. + [$e]')
+ count=$((count + 1))
+ fi
+ done < <(lsusb 2>/dev/null || true)
+
+ log_info "Périphériques USB détectés: $count"
}
#=========================================================
@@ -537,15 +738,16 @@ collect_storage_info() {
local model="Unknown" serial="Unknown" temperature="null" smart_health="null"
if command -v smartctl &>/dev/null; then
- if sudo smartctl -i "/dev/$d" &>/dev/null; then
+ log_info "→ SMART (/dev/$d): collecte en cours (timeout 10s)"
+ if $SMARTCTL_CMD -i "/dev/$d" &>/dev/null; then
local s
- s=$(sudo smartctl -i "/dev/$d" 2>/dev/null)
+ s=$($SMARTCTL_CMD -i "/dev/$d" 2>/dev/null)
model=$(echo "$s" | awk -F: '/Device Model|Model Number/ {gsub(/^[ \t]+/,"",$2); print $2}' | head -1)
serial=$(echo "$s" | awk -F: '/Serial Number/ {gsub(/^[ \t]+/,"",$2); print $2}' | head -1)
# Essayer de récupérer la température et le statut SMART
local smart_all
- smart_all=$(sudo smartctl -A "/dev/$d" 2>/dev/null || true)
+ smart_all=$($SMARTCTL_CMD -A "/dev/$d" 2>/dev/null || true)
# Température (diverses variantes selon le type de disque)
# SATA/HDD: Temperature_Celsius, Airflow_Temperature_Cel, Current Drive Temperature
# RAW_VALUE est après le "-" (colonne 8 dans la plupart des cas, mais peut varier)
@@ -557,7 +759,7 @@ collect_storage_info() {
# Statut SMART health
local health
- health=$(sudo smartctl -H "/dev/$d" 2>/dev/null | awk '/SMART overall-health|SMART Health Status/ {print $NF}' | head -1)
+ health=$($SMARTCTL_CMD -H "/dev/$d" 2>/dev/null | awk '/SMART overall-health|SMART Health Status/ {print $NF}' | head -1)
[[ -n "$health" ]] && smart_health="$health" || smart_health="null"
fi
fi
@@ -595,9 +797,144 @@ collect_storage_info() {
done
STORAGE_INFO="$storage_array"
+
+ local partitions_json="[]"
+ if command -v lsblk &>/dev/null; then
+ log_info "→ Récupération des partitions via lsblk"
+ local lsblk_payload=""
+ local lsblk_fields="NAME,TYPE,MOUNTPOINT,SIZE,FSTYPE,FSUSED,FSAVAIL"
+ local lsblk_cmd="lsblk -b -J -o"
+
+ if command -v timeout &>/dev/null; then
+ lsblk_cmd="timeout 10s lsblk -b -J -o"
+ fi
+
+ if ! lsblk_payload=$(bash -c "$lsblk_cmd \"$lsblk_fields\" 2>/dev/null"); then
+ log_warn "lsblk avec colonnes étendues indisponible, tentative en mode simplifié"
+ lsblk_fields="NAME,TYPE,MOUNTPOINT,SIZE,FSTYPE"
+ lsblk_payload=$(bash -c "$lsblk_cmd \"$lsblk_fields\" 2>/dev/null" || true)
+ fi
+
+ if [[ -n "$lsblk_payload" ]]; then
+ if ! partitions_json=$(echo "$lsblk_payload" | jq '
+ def flatten(node):
+ [node]
+ + ( (node.children // []) | map(flatten(.)) | add );
+ (.blockdevices // [])
+ | map(flatten(.))
+ | add
+ | map(select(.type == "part"))
+ | map({
+ name: (if .name then "/dev/" + .name else null end),
+ mount_point: (if (.mountpoint // "") == "" then null else .mountpoint end),
+ fs_type: (.fstype // null),
+ total_gb: (if (.size? // null) then (((.size | tonumber) / (1024*1024*1024)) * 100 | round / 100) else null end),
+ used_gb: (if (.fsused? // null) then (((.fsused | tonumber) / (1024*1024*1024)) * 100 | round / 100) else null end),
+ free_gb: (if (.fsavail? // null) then (((.fsavail | tonumber) / (1024*1024*1024)) * 100 | round / 100) else null end)
+ })
+ ' 2>/dev/null); then
+ log_warn "Parsing JSON lsblk échoué"
+ partitions_json="[]"
+ fi
+ [[ -z "$partitions_json" ]] && partitions_json="[]"
+ else
+ log_warn "Impossible d'obtenir la sortie lsblk (timeout ?)"
+ fi
+ fi
+
+ PARTITION_INFO="$partitions_json"
+ if [[ "$partitions_json" != "[]" ]]; then
+ local partition_count
+ partition_count=$(echo "$partitions_json" | jq 'length')
+ log_info "Partitions détectées: $partition_count"
+ fi
+
+ collect_network_shares
echo ""
}
+collect_network_shares() {
+ NETWORK_SHARES_INFO="[]"
+
+ if [[ ! -r /proc/mounts ]]; then
+ log_warn "Impossible de lire /proc/mounts - partages réseau ignorés"
+ return
+ fi
+
+ local count=0
+ while read -r raw_src raw_target raw_type raw_opts _; do
+ local fstype
+ fstype=$(printf '%b' "$raw_type" | tr '[:upper:]' '[:lower:]')
+ case "$fstype" in
+ nfs|nfs4|cifs|smbfs|fuse.cifs|fuse.smbfs|afp|afpfs|fuse.afpfs|sshfs|fuse.sshfs|ftpfs|curlftpfs|fuse.ftpfs|davfs|davfs2|fuse.webdavfs)
+ ;;
+ *)
+ continue
+ ;;
+ esac
+
+ local protocol="$fstype"
+ case "$fstype" in
+ cifs|smbfs|fuse.cifs|fuse.smbfs) protocol="smb" ;;
+ nfs|nfs4) protocol="nfs" ;;
+ afp|afpfs|fuse.afpfs) protocol="afp" ;;
+ sshfs|fuse.sshfs) protocol="sshfs" ;;
+ ftpfs|curlftpfs|fuse.ftpfs) protocol="ftp" ;;
+ davfs|davfs2|fuse.webdavfs) protocol="webdav" ;;
+ esac
+
+ local source target options
+ source=$(printf '%b' "$raw_src")
+ target=$(printf '%b' "$raw_target")
+ options=$(printf '%b' "$raw_opts")
+
+ local total_gb="" used_gb="" free_gb=""
+ if [[ -d "$target" ]]; then
+ local df_line
+ df_line=$(df -B1 "$target" 2>/dev/null | tail -1)
+ if [[ -n "$df_line" ]]; then
+ local total_bytes used_bytes free_bytes
+ total_bytes=$(echo "$df_line" | awk '{print $2}')
+ used_bytes=$(echo "$df_line" | awk '{print $3}')
+ free_bytes=$(echo "$df_line" | awk '{print $4}')
+ [[ -n "$total_bytes" ]] && total_gb=$(bytes_to_gb "$total_bytes")
+ [[ -n "$used_bytes" ]] && used_gb=$(bytes_to_gb "$used_bytes")
+ [[ -n "$free_bytes" ]] && free_gb=$(bytes_to_gb "$free_bytes")
+ fi
+ fi
+
+ local entry
+ entry=$(jq -n \
+ --arg protocol "$protocol" \
+ --arg source "$source" \
+ --arg mount "$target" \
+ --arg fs "$fstype" \
+ --arg opts "$options" \
+ --arg total "${total_gb:-}" \
+ --arg used "${used_gb:-}" \
+ --arg free "${free_gb:-}" \
+ '{
+ protocol: ( $protocol | select(. != "") ),
+ source: $source,
+ mount_point: $mount,
+ fs_type: $fs,
+ options: ( $opts | select(. != "") ),
+ total_gb: ( if $total == "" then null else ($total | tonumber?) end ),
+ used_gb: ( if $used == "" then null else ($used | tonumber?) end ),
+ free_gb: ( if $free == "" then null else ($free | tonumber?) end )
+ }')
+
+ NETWORK_SHARES_INFO=$(echo "$NETWORK_SHARES_INFO" | jq --argjson e "$entry" '. + [$e]')
+ count=$((count + 1))
+ done < /proc/mounts
+
+ if (( count > 0 )); then
+ log_info "Partages réseau détectés: $count"
+ else
+ log_info "Aucun partage réseau monté"
+ fi
+}
+
#=========================================================
# Étape 6 : Réseau
#=========================================================
@@ -625,7 +962,7 @@ collect_network_info() {
local ip_addr
ip_addr=$(ip -4 addr show "$iface" | awk '/inet /{print $2}' | cut -d/ -f1 | head -1)
- local speed="" wol_supported=""
+ local speed="" wol_supported="" driver="" ssid=""
if [[ "$type" = "ethernet" && -x /usr/sbin/ethtool ]]; then
local e
e=$(sudo ethtool "$iface" 2>/dev/null || true)
@@ -643,6 +980,39 @@ collect_network_info() {
fi
fi
+ if [[ "$type" = "wifi" ]] && command -v iw &>/dev/null; then
+ local iw_info
+ iw_info=$(iw dev "$iface" link 2>/dev/null || true)
+ if echo "$iw_info" | grep -q "SSID"; then
+ ssid=$(echo "$iw_info" | awk -F': ' '/SSID/ {print $2; exit}' | sed 's/[[:space:]]*$//')
+ fi
+
+ local bitrate_line
+ bitrate_line=$(echo "$iw_info" | awk -F': ' '/tx bitrate/ {print $2; exit}' | xargs)
+ if [[ -n "$bitrate_line" ]]; then
+ local br_value=$(echo "$bitrate_line" | awk '{print $1}')
+ local br_unit=$(echo "$bitrate_line" | awk '{print $2}')
+ if [[ "$br_value" =~ ^[0-9]+(\.[0-9]+)?$ ]]; then
+ if [[ "$br_unit" =~ ^MBit/s$ ]]; then
+ speed=$(printf "%.0f" "$br_value")
+ elif [[ "$br_unit" =~ ^GBit/s$ ]]; then
+ speed=$(awk "BEGIN {printf \"%.0f\", $br_value * 1000}")
+ fi
+ fi
+ fi
+ fi
+
+ if command -v ethtool &>/dev/null; then
+ local ethtool_info
+ ethtool_info=$(sudo ethtool -i "$iface" 2>/dev/null || true)
+ if [[ -n "$ethtool_info" ]]; then
+ driver=$(echo "$ethtool_info" | awk -F: '/driver:/ {gsub(/^[ \t]+/,"",$2); print $2; exit}')
+ fi
+ fi
+ if [[ -z "$driver" && -L "/sys/class/net/$iface/device/driver" ]]; then
+ driver=$(basename "$(readlink -f "/sys/class/net/$iface/device/driver")" 2>/dev/null)
+ fi
+
# Convertir wol_supported en booléen ou null pour jq
local wol_json="null"
if [[ "$wol_supported" == "true" ]]; then
@@ -658,6 +1028,8 @@ collect_network_info() {
--arg mac "$mac" \
--arg ip "${ip_addr:-}" \
--arg speed "$speed" \
+ --arg driver "$driver" \
+ --arg ssid "$ssid" \
--argjson wol "$wol_json" \
'{
name: $name,
@@ -665,6 +1037,8 @@ collect_network_info() {
mac: $mac,
ip_address: ( $ip | select(. != "") ),
speed_mbps: ( ( $speed | tonumber? ) // null ),
+ driver: ( $driver | select(. != "") ),
+ ssid: ( $ssid | select(. != "") ),
wake_on_lan: $wol
}' 2>/dev/null || echo '{}')
@@ -675,7 +1049,14 @@ collect_network_info() {
log_warn "Interface $iface: JSON invalide, ignorée"
fi
- log_info "Interface: $iface ($type) - IP: ${ip_addr:-N/A}"
+ local log_line="Interface: $iface ($type) - IP: ${ip_addr:-N/A}"
+ if [[ -n "$speed" ]]; then
+ log_line+=" - Speed: ${speed}Mbps"
+ fi
+ if [[ "$type" = "wifi" && -n "$ssid" ]]; then
+ log_line+=" - SSID: $ssid"
+ fi
+ log_info "$log_line"
done
NETWORK_INFO="$network_array"
@@ -693,27 +1074,54 @@ run_benchmarks() {
local cpu_bench="null"
if command -v sysbench &>/dev/null; then
log_info "Benchmark CPU en cours..."
- local cpu_res
- cpu_res=$(sysbench cpu --cpu-max-prime=20000 --threads="$(nproc)" run 2>&1 || true)
- local eps time_s
- eps=$(echo "$cpu_res" | awk '/events per second/ {print $4}')
- time_s=$(echo "$cpu_res" | awk '/total time:/ {gsub(/s/,"",$3); print $3}')
- [[ -z "$eps" ]] && eps=0
+
+ # Test single-core (1 thread)
+ log_info "→ Test single-core (1 thread)..."
+ local cpu_single
+ cpu_single=$(sysbench cpu --cpu-max-prime=20000 --threads=1 run 2>&1 || true)
+ local eps_single
+ eps_single=$(echo "$cpu_single" | awk '/events per second/ {print $4}')
+ [[ -z "$eps_single" ]] && eps_single=0
+ local cpu_score_single
+ cpu_score_single=$(safe_bc "scale=2; $eps_single / 100")
+ log_info " Single-core: ${eps_single} events/sec (score: ${cpu_score_single})"
+
+ # Test multi-core (tous les threads)
+ log_info "→ Test multi-core ($(nproc) threads)..."
+ local cpu_multi
+ cpu_multi=$(sysbench cpu --cpu-max-prime=20000 --threads="$(nproc)" run 2>&1 || true)
+ local eps_multi time_s
+ eps_multi=$(echo "$cpu_multi" | awk '/events per second/ {print $4}')
+ time_s=$(echo "$cpu_multi" | awk '/total time:/ {gsub(/s/,"",$3); print $3}')
+ [[ -z "$eps_multi" ]] && eps_multi=0
[[ -z "$time_s" ]] && time_s=0
+ local cpu_score_multi
+ cpu_score_multi=$(safe_bc "scale=2; $eps_multi / 100")
+ log_info " Multi-core: ${eps_multi} events/sec (score: ${cpu_score_multi})"
+
+ # Score global = moyenne des deux
local cpu_score
- cpu_score=$(safe_bc "scale=2; $eps / 100")
+ cpu_score=$(safe_bc "scale=2; ($cpu_score_single + $cpu_score_multi) / 2")
cpu_bench=$(jq -n \
- --arg eps "$eps" \
+ --arg eps "$eps_multi" \
+ --arg eps_single "$eps_single" \
+ --arg eps_multi "$eps_multi" \
--arg time "$time_s" \
--arg score "$cpu_score" \
+ --arg score_single "$cpu_score_single" \
+ --arg score_multi "$cpu_score_multi" \
'{
events_per_sec: ($eps | tonumber? // 0),
+ events_per_sec_single: ($eps_single | tonumber? // 0),
+ events_per_sec_multi: ($eps_multi | tonumber? // 0),
duration_s: ($time | tonumber? // 0),
- score: ($score | tonumber? // 0)
+ score: ($score | tonumber? // 0),
+ score_single: ($score_single | tonumber? // 0),
+ score_multi: ($score_multi | tonumber? // 0)
}')
- log_info "CPU: ${eps} events/sec (score: ${cpu_score})"
+ log_info "CPU Global: score ${cpu_score} (single: ${cpu_score_single}, multi: ${cpu_score_multi})"
else
log_warn "sysbench non disponible - CPU bench ignoré"
fi
@@ -935,13 +1343,46 @@ run_benchmarks() {
send_benchmark_payload() {
log_step "Construction du payload JSON et envoi au serveur"
- # Si SERVER_URL n’a pas de schéma, on préfixe par http://
+ # Si SERVER_URL n'a pas de schéma, on préfixe par http://
local base_url="$SERVER_URL"
if [[ "$base_url" != http*://* ]]; then
base_url="http://$base_url"
fi
local api_url="${base_url%/}/api/benchmark"
+ # Validation des variables JSON avant envoi (éviter les variables vides qui causent des erreurs jq)
+ # Si DEBUG_PAYLOAD=1, on affiche les variables pour diagnostic
+ if [[ "${DEBUG_PAYLOAD:-0}" == "1" ]]; then
+ echo -e "${BLUE}════════════════════════════════════════════════════════${NC}"
+ echo -e "${YELLOW}DEBUG: Validation des variables JSON${NC}"
+ echo -e "${BLUE}════════════════════════════════════════════════════════${NC}"
+ for var in SYSTEM_INFO CPU_INFO RAM_INFO GPU_INFO MOTHERBOARD_INFO STORAGE_INFO PARTITION_INFO NETWORK_INFO PCI_INFO USB_INFO NETWORK_SHARES_INFO BENCHMARK_RESULTS; do
+ local val="${!var}"
+ if [[ -z "$val" ]]; then
+ echo -e "${RED}✗ $var est VIDE${NC}"
+ elif echo "$val" | jq empty 2>/dev/null; then
+ echo -e "${GREEN}✓ $var est JSON valide${NC} (${#val} chars)"
+ else
+ echo -e "${RED}✗ $var est JSON INVALIDE${NC}: ${val:0:100}..."
+ fi
+ done
+ echo -e "${BLUE}════════════════════════════════════════════════════════${NC}"
+ echo ""
+ fi
+
+ [[ -z "$SYSTEM_INFO" ]] && SYSTEM_INFO="null"
+ [[ -z "$CPU_INFO" ]] && CPU_INFO="null"
+ [[ -z "$RAM_INFO" ]] && RAM_INFO="null"
+ [[ -z "$GPU_INFO" ]] && GPU_INFO="null"
+ [[ -z "$MOTHERBOARD_INFO" ]] && MOTHERBOARD_INFO="null"
+ [[ -z "$STORAGE_INFO" ]] && STORAGE_INFO="[]"
+ [[ -z "$PARTITION_INFO" ]] && PARTITION_INFO="[]"
+ [[ -z "$NETWORK_INFO" ]] && NETWORK_INFO="[]"
+ [[ -z "$PCI_INFO" ]] && PCI_INFO="[]"
+ [[ -z "$USB_INFO" ]] && USB_INFO="[]"
+ [[ -z "$NETWORK_SHARES_INFO" ]] && NETWORK_SHARES_INFO="[]"
+ [[ -z "$BENCHMARK_RESULTS" ]] && BENCHMARK_RESULTS="null"
+
local payload
payload=$(
jq -n \
@@ -952,7 +1393,11 @@ send_benchmark_payload() {
--argjson gpu "$GPU_INFO" \
--argjson mb "$MOTHERBOARD_INFO" \
--argjson storage "$STORAGE_INFO" \
+ --argjson partitions "$PARTITION_INFO" \
--argjson network "$NETWORK_INFO" \
+ --argjson pci "$PCI_INFO" \
+ --argjson usb "$USB_INFO" \
+ --argjson shares "$NETWORK_SHARES_INFO" \
--argjson bench "$BENCHMARK_RESULTS" \
'
{
@@ -1025,9 +1470,13 @@ send_benchmark_payload() {
temperature_c
}
],
- partitions: []
+ partitions: $partitions
},
+ pci_devices: $pci,
+ usb_devices: $usb,
+ network_shares: $shares,
+
network: {
interfaces: [
$network[]
@@ -1037,7 +1486,8 @@ send_benchmark_payload() {
mac,
ip: .ip_address,
speed_mbps,
- driver: null,
+ driver: (.driver // null),
+ ssid: (.ssid // null),
wake_on_lan
}
]
@@ -1053,7 +1503,11 @@ send_benchmark_payload() {
os: (
$system.os
- + { virtualization_type: "none" }
+ + {
+ hostname: $system.hostname,
+ virtualization_type: "none",
+ desktop_environment: (.session_type // .display_server // null)
+ }
),
sensors: {
@@ -1110,7 +1564,13 @@ send_benchmark_payload() {
echo -e "${GREEN}✓${NC} Payload sauvegardé dans: ${debug_file}"
echo ""
- read -p "Appuyez sur Entrée pour continuer l'envoi ou Ctrl+C pour annuler..."
+ # Demander confirmation seulement si on a un terminal interactif
+ if [[ -t 0 ]]; then
+ read -p "Appuyez sur Entrée pour continuer l'envoi ou Ctrl+C pour annuler..."
+ else
+ log_warn "Mode non-interactif détecté - envoi automatique dans 2 secondes..."
+ sleep 2
+ fi
fi
log_info "Envoi du payload vers: $api_url"
@@ -1144,7 +1604,47 @@ send_benchmark_payload() {
# MAIN
#=========================================================
+parse_args() {
+ while [[ $# -gt 0 ]]; do
+ case "$1" in
+ --server)
+ SERVER_URL="$2"
+ shift 2
+ ;;
+ --token)
+ API_TOKEN="$2"
+ shift 2
+ ;;
+ --iperf-server)
+ IPERF_SERVER="$2"
+ shift 2
+ ;;
+ --debug)
+ DEBUG_PAYLOAD=1
+ shift
+ ;;
+ --help|-h)
+ echo "Usage: $0 [OPTIONS]"
+ echo ""
+ echo "Options:"
+ echo " --server URL Backend server URL (default: $SERVER_URL)"
+ echo " --token TOKEN API authentication token"
+ echo " --iperf-server IP iperf3 server IP (default: $IPERF_SERVER)"
+ echo " --debug Enable debug mode (shows full JSON payload)"
+ echo " --help, -h Show this help message"
+ echo ""
+ exit 0
+ ;;
+ *)
+ log_warn "Option inconnue: $1 (ignorée)"
+ shift
+ ;;
+ esac
+ done
+}
+
main() {
+ parse_args "$@"
check_sudo
check_dependencies
diff --git a/scripts/resultat_bench_aorus.md b/scripts/resultat_bench_aorus.md
old mode 100644
new mode 100755
diff --git a/simple_bench.md b/simple_bench.md
old mode 100644
new mode 100755