From fc3f17925022ee9c166641359f35e9f04a363290 Mon Sep 17 00:00:00 2001
From: buchtioof <48603083+buchtioof@users.noreply.github.com>
Date: Tue, 10 Feb 2026 10:55:36 +0100
Subject: [PATCH] update grabber (sql+req)
---
app.py | 84 +++++++++++++++---------
logo.png => assets/logo.png | Bin
grabber.py | 125 ++++++++++++++++++++++++++----------
grabber.sh | 61 ++----------------
grabberman.db | Bin 12288 -> 0 bytes
readme.md | 2 +-
requirements.txt | 3 +-
templates/item.html | 104 ++++++++++++++++++++----------
8 files changed, 225 insertions(+), 154 deletions(-)
rename logo.png => assets/logo.png (100%)
delete mode 100644 grabberman.db
diff --git a/app.py b/app.py
index 3d55b50..a88ef79 100644
--- a/app.py
+++ b/app.py
@@ -3,50 +3,76 @@ from fastapi.responses import HTMLResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
import json
-from grabber import Grabber
+from contextlib import asynccontextmanager
-app = FastAPI()
+from grabber import Grabber, flotte, create_db_and_tables
+
+@asynccontextmanager
+async def lifespan(app: FastAPI):
+ create_db_and_tables()
+ yield
+
+app = FastAPI(lifespan=lifespan)
app.mount("/static", StaticFiles(directory="static"), name="static")
templates = Jinja2Templates(directory="templates")
-ordi1 = Grabber()
@app.post("/endpoint")
async def receive_info(request: Request):
- # Lire le body brut
body = await request.body()
- print(body)
-
- # Parser le JSON
try:
data = json.loads(body)
except json.JSONDecodeError:
raise HTTPException(status_code=400, detail="Invalid JSON")
- hw = data["HARDWARE"]
- sw = data["SOFTWARE"]
+ sw = data.get("SOFTWARE", {})
+ # On récupère l'adresse MAC, c'est notre nouvel identifiant unique
+ mac = sw.get("mac_address")
+ hostname = sw.get("hostname", "Inconnu")
- ordi1.motherboard = hw["motherboard"]
- ordi1.cpu_model = hw["cpu_model"]
- ordi1.cpu_id = hw["cpu_id"]
- ordi1.cpu_cores = hw["cpu_cores"]
- ordi1.cpu_threads = hw["cpu_threads"]
- ordi1.cpu_frequency_min = hw["cpu_frequency_min"]
- ordi1.cpu_frequency_cur = hw["cpu_frequency_cur"]
- ordi1.cpu_frequency_max = hw["cpu_frequency_max"]
+ if not mac:
+ raise HTTPException(status_code=400, detail="Adresse MAC manquante dans le JSON")
- ordi1.hostname = sw["hostname"]
- ordi1.os = sw["os"]
- ordi1.arch = sw["arch"]
- ordi1.desktop_env = sw["desktop_env"]
- ordi1.window_manager = sw["window_manager"]
- ordi1.kernel = sw["kernel"]
+ # Si la machine n'est pas encore connue par son adresse MAC
+ if mac not in flotte:
+ print(f"Nouvelle machine détectée : {hostname} ({mac})")
+ flotte[mac] = Grabber(mac, hostname)
+
+ ordi_actuel = flotte[mac]
+ ordi_actuel.update(data)
+ ordi_actuel.save()
- print(f"Hostname is {ordi1.hostname}")
- print(f"Motherboard serial is {ordi1.motherboard}")
+ return {"status": "ok", "mac": mac}
- return {"status": "ok"}
+@app.get("/")
+async def list_ordis(request: Request):
+ """Affiche la liste des ordis, identifiés par MAC mais affichés par Hostname."""
+ # On crée des liens qui pointent vers /ordi/ADRESSE_MAC
+ # Mais pour l'humain, on affiche "Hostname (Mac)"
+ list_items = []
+ for mac, grabber_obj in flotte.items():
+ nom_affiche = f"{grabber_obj.hostname} ({mac})"
+ list_items.append(f'
{nom_affiche}')
+
+ liens_html = "".join(list_items)
+ return HTMLResponse(f"""
+
+
+ Dashboard Grabber
+
+
+
+ Tableau de bord Grabber
+ Machines connectées
+ {liens_html or "En attente de données..."}
+
+
+ """)
-@app.get("/ordi1")
-async def show_info(request: Request):
- return templates.TemplateResponse("item.html", {"request": request, "ordi": ordi1})
\ No newline at end of file
+# L'URL attend maintenant une adresse MAC (ex: /ordi/00:11:22:33:44:55)
+@app.get("/ordi/{mac_address}")
+async def show_info(request: Request, mac_address: str):
+ if mac_address in flotte:
+ return templates.TemplateResponse("item.html", {"request": request, "ordi": flotte[mac_address]})
+ else:
+ return HTMLResponse("Machine introuvable
", status_code=404)
\ No newline at end of file
diff --git a/logo.png b/assets/logo.png
similarity index 100%
rename from logo.png
rename to assets/logo.png
diff --git a/grabber.py b/grabber.py
index 960618d..a3fbd14 100644
--- a/grabber.py
+++ b/grabber.py
@@ -1,36 +1,95 @@
-#!/usr/bin/python3
-import configparser
-import requests
+from typing import Optional
+from datetime import datetime
+from sqlmodel import Field, Session, SQLModel, create_engine
-class Grabber():
- motherboard = " "
- cpu_model = " "
- cpu_id = " "
- cpu_cores = " "
- cpu_threads = " "
- cpu_frequency_min = " "
- cpu_frequency_cur = " "
- cpu_frequency_max = " "
- ram_size = " "
- ram_slots = " "
- ram_number = " "
+# --- CONFIGURATION BASE DE DONNÉES ---
+DB_FILE = "grabberman.db"
+sqlite_url = f"sqlite:///{DB_FILE}"
+engine = create_engine(sqlite_url, echo=False)
- hostname = " "
- os = " "
- arch = " "
- desktop_env = " "
- window_manager = " "
- kernel = " "
+# --- MODÈLE DE DONNÉES (TABLE SQL) ---
+class SystemLog(SQLModel, table=True):
+ id: Optional[int] = Field(default=None, primary_key=True)
+ date_scan: datetime = Field(default_factory=datetime.now)
+
+ # NOUVEAU : mac_address devient un champ indexé important
+ mac_address: str = Field(index=True)
+ hostname: str
- def fetch_summary(self):
- return
- def shutdown():
- return
- def status(self):
- return
- def link_to_user(self,user):
- return
- def remove_user_access(self):
- return
- def show_users(self):
- return
+ # Champs Hardware
+ motherboard: Optional[str] = None
+ cpu_model: Optional[str] = None
+ cpu_id: Optional[str] = None
+ cpu_cores: Optional[str] = None
+ cpu_threads: Optional[str] = None
+ cpu_frequency_min: Optional[str] = None
+ cpu_frequency_cur: Optional[str] = None
+ cpu_frequency_max: Optional[str] = None
+ gpu_model: Optional[str] = None
+ ram_slots: Optional[str] = None
+
+ # Champs Software
+ os: Optional[str] = None
+ arch: Optional[str] = None
+ desktop_env: Optional[str] = None
+ window_manager: Optional[str] = None
+ kernel: Optional[str] = None
+
+def create_db_and_tables():
+ SQLModel.metadata.create_all(engine)
+
+# --- GESTION DE LA FLOTTE EN MÉMOIRE ---
+# Le dictionnaire stockera maintenant : {"AA:BB:CC:DD:EE:FF": GrabberObject}
+flotte = {}
+
+class Grabber:
+ def __init__(self, mac_address, hostname="Inconnu"):
+ self.mac_address = mac_address
+ self.hostname = hostname
+ self.data_cache = {}
+
+ def update(self, json_data):
+ """Met à jour les données et prépare l'objet SQLModel."""
+ hw = json_data.get("HARDWARE", {})
+ sw = json_data.get("SOFTWARE", {})
+
+ # Mise à jour du hostname s'il a changé, mais on garde la MAC comme ancre
+ if "hostname" in sw:
+ self.hostname = sw["hostname"]
+
+ # On prépare les données pour la DB
+ self.data_cache = {
+ "mac_address": self.mac_address, # On n'oublie pas la MAC
+ "hostname": self.hostname,
+ "motherboard": hw.get("motherboard", "N/A"),
+ "cpu_model": hw.get("cpu_model", "N/A"),
+ "cpu_id": hw.get("cpu_id", "N/A"),
+ "cpu_cores": hw.get("cpu_cores", "N/A"),
+ "cpu_threads": hw.get("cpu_threads", "N/A"),
+ "cpu_frequency_min": hw.get("cpu_frequency_min", "N/A"),
+ "cpu_frequency_cur": hw.get("cpu_frequency_cur", "N/A"),
+ "cpu_frequency_max": hw.get("cpu_frequency_max", "N/A"),
+ "gpu_model": hw.get("gpu_model", "N/A"),
+ "ram_slots": hw.get("ram_slots", "N/A"),
+ "os": sw.get("os", "N/A"),
+ "arch": sw.get("arch", "N/A"),
+ "desktop_env": sw.get("desktop_env", "N/A"),
+ "window_manager": sw.get("window_manager", "N/A"),
+ "kernel": sw.get("kernel", "N/A")
+ }
+
+ def save(self):
+ """Enregistre les données via SQLModel."""
+ try:
+ log_entry = SystemLog(**self.data_cache)
+ with Session(engine) as session:
+ session.add(log_entry)
+ session.commit()
+ session.refresh(log_entry)
+ print(f"Sauvegarde réussie pour {self.hostname} ({self.mac_address})")
+ except Exception as e:
+ print(f"Erreur SQLModel : {e}")
+
+ # Permet d'accéder aux propriétés comme ordi.cpu_model dans le template
+ def __getattr__(self, name):
+ return self.data_cache.get(name, "N/A")
\ No newline at end of file
diff --git a/grabber.sh b/grabber.sh
index 8827bed..99fb184 100755
--- a/grabber.sh
+++ b/grabber.sh
@@ -40,7 +40,7 @@ echo ""
#----- Verify dependecies available -----
REQUIRED_CMDS_SIMPLE=(inxi dmidecode lscpu lsblk nproc numfmt)
-REQUIRED_CMDS_FULL=(inxi dmidecode lscpu lsblk nproc numfmt python3 jq)
+REQUIRED_CMDS_FULL=(inxi dmidecode lscpu lsblk nproc numfmt python3 jq sqlite3)
requirements_simple() {
echo -n "Checking dependencies... "
@@ -184,48 +184,6 @@ done
TOTAL_STORAGE=$(numfmt --to iec $TOTAL_STORAGE)
#---------------------------------
-# Compile Hardware informations
-hardware() {
- echo "[HARDWARE]" >> $SUM_FILE
- echo "MB_SERIAL = $MB_SERIAL" >> $SUM_FILE
- echo "" >> $SUM_FILE
-
- echo "--- CPU DATA ---" >> $SUM_FILE
- echo "CPU_MODEL = $CPU_MODEL" >> $SUM_FILE
- echo "CPU_ID = $CPU_ID" >> $SUM_FILE
- echo "CPU_CORES=$CPU_CORES" >> $SUM_FILE
- echo "CPU_THREADS=$CPU_THREADS" >> $SUM_FILE
- echo "CPU_FREQUENCY_MIN=$CPU_FREQUENCY_MIN" >> $SUM_FILE
- echo "CPU_FREQUENCY_CUR=$CPU_FREQUENCY_CUR" >> $SUM_FILE
- echo "CPU_FREQUENCY_MAX=$CPU_FREQUENCY_MAX" >> $SUM_FILE
- echo "" >> $SUM_FILE
-
- echo "--- GPU DATA ---" >> $SUM_FILE
- echo "GPU_MODEL=$GPU_MODEL" >> $SUM_FILE
- echo "" >> $SUM_FILE
-
- echo "--- RAM DATA ---" >> $SUM_FILE
- echo "RAM_SIZE = $RAM_SIZE" >> $SUM_FILE
- echo "RAM_SLOTS=$RAM_SLOTS" >> $SUM_FILE
- echo "RAM_NUMBER=$RAM_NUMBER" >> $SUM_FILE
-
- for i in $(seq 1 $RAM_SLOTS_NUMBER); do
- R_SIZE=$(sudo dmidecode --type=memory | grep "Size:" | grep -v "Volatile" | grep -v "Cache" | grep -v "Logical" | cut -d: -f2 | sed -n "${i}p" | sed 's/\ //')
- R_SLOT=$i
- R_FREQ=$(sudo dmidecode --type=memory | grep Speed | grep -v "Memory" | cut -d: -f2 | sed -n "${i}p" | sed 's/\ //')
-
- echo "RAM_${i}_SIZE=$R_SIZE" >> $SUM_FILE
- echo "RAM_${i}_SLOT=$R_SLOT" >> $SUM_FILE
- echo "RAM_${i}_FREQ=$R_FREQ" >> $SUM_FILE
- done
- echo "" >> $SUM_FILE
-
- echo "--- STORAGE DATA ---" >> $SUM_FILE
- disks_partitions
- echo "STORAGE = $TOTAL_STORAGE" >> $SUM_FILE
- echo "" >> $SUM_FILE
-}
-
################################################
######## SOFTWARE PART #########################
@@ -233,17 +191,7 @@ OS=$(lsb_release -a | grep Description | cut -f2)
ARCH=$(uname -a | cut -d' ' -f10)
KERNEL=$(uname -r)
HOSTNAME=$(hostname)
-
-# Compile Software informations
-software() {
- echo "[SOFTWARE]"
- echo "OS = $OS"
- echo "HOSTNAME = $HOSTNAME"
- echo "ARCHITECTURE = $ARCH"
- echo "KERNEL = $KERNEL"
- echo "DESKTOP_ENV = $XDG_CURRENT_DESKTOP"
- echo "WINDOW_MANAGER = $XDG_SESSION_TYPE"
-} >> $SUM_FILE
+MAC_ADDRESS=$(cat /sys/class/net/$(ls /sys/class/net | grep -vE '^(lo|docker|veth|br)' | head -n 1)/address)
###############################################
@@ -261,6 +209,7 @@ json_file() {
--arg gpu_model "$GPU_MODEL" \
--arg ram_slots "$RAM_SLOTS" \
--arg hostname "$HOSTNAME" \
+ --arg mac_address "$MAC_ADDRESS" \
--arg os "$OS" \
--arg arch "$ARCH" \
--arg desktop_env "$XDG_CURRENT_DESKTOP" \
@@ -281,6 +230,7 @@ json_file() {
},
SOFTWARE: {
hostname: $hostname,
+ mac_address:$mac_address,
os: $os,
arch: $arch,
desktop_env: $desktop_env,
@@ -311,10 +261,7 @@ python_venv() {
echo "It's grabbin time!"
hello
echo "Fetching hardware data..."
-hardware
echo "Fetching software data..."
-software
-echo "Writing everything in summary.txt"
if [ "$choice" = "1" ]; then
echo "Grabber has complete his mission! Find every logs saved in your home repository inside the /grabber folder."
echo "See you space cowboy..."
diff --git a/grabberman.db b/grabberman.db
deleted file mode 100644
index 9fc86f9d166c9d8b7245f3125a6f1bddbeb465bb..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 12288
zcmeI#K}*9h6bJC66FtfFH1PDcQ_G47;@vod3%2QusnC>&QBvP9w?doEwZAjB)({^8^2Vo{tVf>4;fOKL>r%@DAoX>x*1YK|LntlWX
zAOHafKmY;|fB*y_009U<00KKKfc}4ne=e>D0SG_<0uX=z1Rwwb2tWV=5cn4O1c|Cl
A?f?J)
diff --git a/readme.md b/readme.md
index fa895fd..9b60c63 100644
--- a/readme.md
+++ b/readme.md
@@ -1,4 +1,4 @@
-
+
# Grabber - Fetch all your PC
diff --git a/requirements.txt b/requirements.txt
index 3ae2b65..d1ed23f 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -39,4 +39,5 @@ starlette==0.50.0
typing-inspection==0.4.2
typing_extensions==4.15.0
urllib3==2.6.3
-uvicorn==0.40.0
\ No newline at end of file
+uvicorn==0.40.0
+sqlmodel==0.0.32
\ No newline at end of file
diff --git a/templates/item.html b/templates/item.html
index c56ca9f..b3fa927 100644
--- a/templates/item.html
+++ b/templates/item.html
@@ -5,45 +5,83 @@
Informations Système
- Informations de l'Ordinateur
+ Parc Informatique ({{ ordinateurs|length }} machines)
-
-
[HARDWARE]
-
- | Propriété | Valeur |
- | Hostname | {{ ordi.hostname }} |
- | CPU | {{ ordi.cpu_model }} |
- | ID CPU | {{ ordi.cpu_id }} |
- | Nombre de Cœurs CPU | {{ ordi.cpu_cores }} |
- | Nombre de Threads CPU | {{ ordi.cpu_threads }} |
- | Fréquence Min CPU | {{ ordi.cpu_frequency_min }} |
- | Fréquence Courante CPU | {{ ordi.cpu_frequency_cur }} |
- | Fréquence Max CPU | {{ ordi.cpu_frequency_max }} |
- | Nombre de slots de RAM | {{ ordi.ram_slots }} |
-
-
+ {% for ordi in ordinateurs %}
+
+
+
-
-
[SOFTWARE]
-
- | Propriété | Valeur |
- | Hostname | {{ ordi.hostname }} |
- | OS | {{ ordi.os }} |
- | Architecture | {{ ordi.arch }} |
- | Desktop | {{ ordi.desktop_env }} |
- | Window Manager | {{ ordi.window_manager }} |
- | Kernel | {{ ordi.kernel }} |
-
+
+
[HARDWARE]
+
+ | Propriété | Valeur |
+ | Série Carte Mère | {{ ordi.mb_serial }} |
+ | Série Châssis | {{ ordi.chassis_serial }} |
+ | CPU | {{ ordi.cpu }} |
+ | CPU ID | {{ ordi.cpu_id }} |
+ | Nombre de Cœurs CPU | {{ ordi.cpu_cores_number }} |
+ | Nombre de Threads CPU | {{ ordi.cpu_threads_number }} |
+ | Fréquence Min CPU | {{ ordi.cpu_frequency_min }} |
+ | Fréquence Courante CPU | {{ ordi.cpu_frequency_cur }} |
+ | Fréquence Max CPU | {{ ordi.cpu_frequency_max }} |
+ | Modèle GPU | {{ ordi.gpu_model }} |
+ | Stockage Total | {{ ordi.stockage_total }} |
+ | Nombre de slots de RAM | {{ ordi.ram_slots_number }} |
+ | Génération RAM | {{ ordi.ram_gen }} |
+
+
+
+
+
[SOFTWARE]
+
+ | Propriété | Valeur |
+ | Hostname | {{ ordi.hostname }} |
+ | OS | {{ ordi.os }} |
+ | Architecture | {{ ordi.arch }} |
+ | Desktop | {{ ordi.desktop }} |
+ | Window Manager | {{ ordi.wm }} |
+ | Kernel | {{ ordi.kernel }} |
+
+
+
+ {% endfor %}
+ {% if not ordinateurs %}
+
Aucun ordinateur enregistré dans la base de données.
+ {% endif %}
+
-