update ui

This commit is contained in:
buchtioof 2026-02-10 15:52:33 +01:00
parent 9471892284
commit ddd114a8fe
3 changed files with 84 additions and 181 deletions

View File

@ -1,21 +1,18 @@
from typing import Optional from typing import Optional
from datetime import datetime from datetime import datetime
from sqlmodel import Field, Session, SQLModel, create_engine, select # <--- Ajout de select from sqlmodel import Field, Session, SQLModel, create_engine, select
# --- CONFIGURATION BASE DE DONNÉES ---
DB_FILE = "grabberman.db" DB_FILE = "grabberman.db"
sqlite_url = f"sqlite:///{DB_FILE}" sqlite_url = f"sqlite:///{DB_FILE}"
engine = create_engine(sqlite_url, echo=False) engine = create_engine(sqlite_url, echo=False)
# --- MODÈLE DE DONNÉES (TABLE SQL) ---
class SystemLog(SQLModel, table=True): class SystemLog(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True) id: Optional[int] = Field(default=None, primary_key=True)
date_scan: datetime = Field(default_factory=datetime.now) date_scan: datetime = Field(default_factory=datetime.now)
mac_address: str = Field(index=True)
mac_address: str = Field(index=True) # On s'en sert pour la recherche
hostname: str hostname: str
# Champs Hardware # Hardware
motherboard: Optional[str] = None motherboard: Optional[str] = None
cpu_model: Optional[str] = None cpu_model: Optional[str] = None
cpu_id: Optional[str] = None cpu_id: Optional[str] = None
@ -25,9 +22,13 @@ class SystemLog(SQLModel, table=True):
cpu_frequency_cur: Optional[str] = None cpu_frequency_cur: Optional[str] = None
cpu_frequency_max: Optional[str] = None cpu_frequency_max: Optional[str] = None
gpu_model: Optional[str] = None gpu_model: Optional[str] = None
ram_slots: Optional[str] = None
# Champs Software # NOUVEAUX CHAMPS
ram_slots: Optional[str] = None
ram_total: Optional[str] = None
total_storage: Optional[str] = None
# Software
os: Optional[str] = None os: Optional[str] = None
arch: Optional[str] = None arch: Optional[str] = None
desktop_env: Optional[str] = None desktop_env: Optional[str] = None
@ -37,7 +38,7 @@ class SystemLog(SQLModel, table=True):
def create_db_and_tables(): def create_db_and_tables():
SQLModel.metadata.create_all(engine) SQLModel.metadata.create_all(engine)
# --- GESTION DE LA FLOTTE EN MÉMOIRE --- # Flotte temporaire pour compatibilité, mais on utilise surtout la BDD
flotte = {} flotte = {}
class Grabber: class Grabber:
@ -47,7 +48,6 @@ class Grabber:
self.data_cache = {} self.data_cache = {}
def update(self, json_data): def update(self, json_data):
"""Met à jour les données et prépare l'objet SQLModel."""
hw = json_data.get("HARDWARE", {}) hw = json_data.get("HARDWARE", {})
sw = json_data.get("SOFTWARE", {}) sw = json_data.get("SOFTWARE", {})
@ -67,6 +67,8 @@ class Grabber:
"cpu_frequency_max": hw.get("cpu_frequency_max", "N/A"), "cpu_frequency_max": hw.get("cpu_frequency_max", "N/A"),
"gpu_model": hw.get("gpu_model", "N/A"), "gpu_model": hw.get("gpu_model", "N/A"),
"ram_slots": hw.get("ram_slots", "N/A"), "ram_slots": hw.get("ram_slots", "N/A"),
"ram_total": hw.get("ram_total", "N/A"), # Ajouté
"total_storage": hw.get("total_storage", "N/A"), # Ajouté
"os": sw.get("os", "N/A"), "os": sw.get("os", "N/A"),
"arch": sw.get("arch", "N/A"), "arch": sw.get("arch", "N/A"),
"desktop_env": sw.get("desktop_env", "N/A"), "desktop_env": sw.get("desktop_env", "N/A"),
@ -75,37 +77,22 @@ class Grabber:
} }
def save(self): def save(self):
"""Enregistre ou met à jour les données dans la BDD."""
try: try:
with Session(engine) as session: with Session(engine) as session:
# 1. On cherche si une machine avec cette MAC existe déjà
statement = select(SystemLog).where(SystemLog.mac_address == self.mac_address) statement = select(SystemLog).where(SystemLog.mac_address == self.mac_address)
existing_pc = session.exec(statement).first() existing_pc = session.exec(statement).first()
if existing_pc: if existing_pc:
# --- MISE À JOUR (UPDATE) --- # Update
print(f"Mise à jour de la BDD pour : {self.hostname} ({self.mac_address})")
# On met à jour chaque champ existant
for key, value in self.data_cache.items(): for key, value in self.data_cache.items():
if hasattr(existing_pc, key): if hasattr(existing_pc, key):
setattr(existing_pc, key, value) setattr(existing_pc, key, value)
# On force la mise à jour de la date de scan
existing_pc.date_scan = datetime.now() existing_pc.date_scan = datetime.now()
session.add(existing_pc) session.add(existing_pc)
else: else:
# --- CRÉATION (INSERT) --- # Insert
print(f"Création d'une nouvelle entrée BDD : {self.hostname}")
log_entry = SystemLog(**self.data_cache) log_entry = SystemLog(**self.data_cache)
session.add(log_entry) session.add(log_entry)
session.commit() session.commit()
except Exception as e: except Exception as e:
print(f"Erreur critique BDD : {e}") print(f"Erreur BDD : {e}")
def __getattr__(self, name):
return self.data_cache.get(name, "N/A")

View File

@ -4,20 +4,8 @@ export LC_ALL=C
export LANG=C export LANG=C
# ============================================================================== # ==============================================================================
# Script : grabber.sh # Script : grabber.sh
# Date : 2025-12-11 # Version: 0.4 (Full Display)
# Version: 0.2
#
# Description :
# Grabber is a bash program that fetch some informations
# of the computer like memory, storage or cpu for exemple.
#
# Usage :
# ./grabber.sh
#
# Dependancies :
# - dmidecode
# - inxi
# ============================================================================== # ==============================================================================
##### Start process ##### ##### Start process #####
@ -29,29 +17,18 @@ echo " \____| |_| \_\ /_/ \_\ |____/ |____/ |_____| |_| \_\ "
echo " _)(|_ // \ \_ \ \ \ \ _|| \ \_ _|| \ \_ << >> / / \ \ " echo " _)(|_ // \ \_ \ \ \ \ _|| \ \_ _|| \ \_ << >> / / \ \ "
echo " (__)__) (__) (__)(__) (__)(__) (__)(__) (__)(__) (__) (__) (__) " echo " (__)__) (__) (__)(__) (__)(__) (__)(__) (__)(__) (__) (__) (__) "
echo "" echo ""
echo "Welcome to grabber!"
#----- Verify sudo command ----- #----- Verify dependecies -----
if [[ $EUID -ne 0 ]]; then REQUIRED_CMDS=(inxi lscpu lsblk nproc numfmt python3 jq sqlite3)
echo "Please run as root to be able to use superuser commands as dmidecode -> sudo ./grabber.sh"
exit 1
fi
echo ""
#----- Verify dependecies available -----
REQUIRED_CMDS_SIMPLE=(inxi dmidecode lscpu lsblk nproc numfmt)
REQUIRED_CMDS_FULL=(inxi dmidecode lscpu lsblk nproc numfmt python3 jq sqlite3)
requirements() { requirements() {
echo -n "Checking dependencies... " echo -n "Checking dependencies... "
MISSING=() MISSING=()
for cmd in "${REQUIRED_CMDS[@]}"; do
for cmd in "${REQUIRED_CMDS_FULL[@]}"; do
command -v "$cmd" >/dev/null 2>&1 || MISSING+=("$cmd") command -v "$cmd" >/dev/null 2>&1 || MISSING+=("$cmd")
done done
if (( ${#MISSING[@]} > 0 )); then if (( ${#MISSING[@]} > 0 )); then
echo "Missing dependencies:" echo "Missing dependencies: ${MISSING[*]}"
printf ' - %s\n' "${MISSING[@]}"
echo "Install with: sudo apt install ${MISSING[*]}" echo "Install with: sudo apt install ${MISSING[*]}"
exit 1 exit 1
else else
@ -59,109 +36,58 @@ requirements() {
fi fi
} }
#----- Ask what user wants to do ----- echo "1: Launch grabber | 2: Uninstall | c: Cancel"
echo "What you want grabber to do for you?" read -p ":- " choice
echo "1: Launch grabber" if [ "$choice" = "1" ]; then requirements;
echo "2: Uninstall grabber" elif [ "$choice" = "2" ]; then rm -rf gbvenv; exit;
elif [ "$choice" = "c" ]; then exit;
read -p " 1 / 2 / Cancel(c):- " choice else echo "Invalid choice"; exit; fi
if [ "$choice" = "1" ]; then
echo "Big work for today"
requirements
elif [ "$choice" = "2" ];then
echo "Uninstalling"
exit
elif [ "$choice" = "c" ];then
echo "Grabber canceled"
exit
else
echo "No choices detected!"
fi
##### MAIN VARIABLES ##### ##### MAIN VARIABLES #####
DATE=$(date +'%Y-%m-%d_%H:%M:%S') DATE=$(date +'%Y-%m-%d_%H:%M:%S')
# Check who is behind sudo command then fetch his $HOME
REAL_USER="${SUDO_USER:-$USER}"
REAL_HOME="$(getent passwd "$REAL_USER" | cut -d: -f6)"
# Declare where to store grabber results
NAME_DIR="logs_$DATE"
WORKING_DIR="$REAL_HOME/grabber/$NAME_DIR"
mkdir $WORKING_DIR -p
# Declare the files to be written
SUM_FILE=$WORKING_DIR/summary.txt
SUCCESS_LOG=$WORKING_DIR/grabber-success.log
ERROR_LOG=$WORKING_DIR/grabber-error.log
############ HARDWARE FETCHER ################# ############ HARDWARE FETCHER #################
#------------ CPU ----------------
CPU_MODEL=$(lscpu -eMODELNAME | tail -n1 | cut -d' ' -f1,2,3,4) # --- CPU ---
CPU_ID=$(dmidecode -t processor | grep ID | cut -d: -f2 | sed 's/^ *//' | xargs) CPU_MODEL=$(lscpu | grep "Model name:" | cut -d: -f2 | sed 's/^ *//')
CPU_FREQUENCY_MIN=$(lscpu | grep MHz | cut -d: -f2 | sed -n '3p' | tr -s " " | sed 's/\ //' | cut -d, -f1) _VENDOR=$(lscpu | grep "Vendor ID:" | cut -d: -f2 | xargs)
CPU_FREQUENCY_CUR=$(dmidecode | grep "MHz" | cut -d: -f2 | sed -n '3p' | sed 's/\ //') _FAMILY=$(lscpu | grep "CPU family:" | cut -d: -f2 | xargs)
CPU_FREQUENCY_MAX=$(dmidecode | grep "MHz" | cut -d: -f2 | sed -n '2p' | sed 's/\ //') _MODEL=$(lscpu | grep "Model:" | cut -d: -f2 | xargs)
CPU_CORES=$(inxi | grep core | cut -d' ' -f2 | sed 's/-core//') CPU_ID="${_VENDOR} Fam ${_FAMILY} Mod ${_MODEL}"
CPU_FREQUENCY_MIN=$(lscpu | grep "CPU min MHz" | cut -d: -f2 | xargs | cut -d. -f1)
CPU_FREQUENCY_MAX=$(lscpu | grep "CPU max MHz" | cut -d: -f2 | xargs | cut -d. -f1)
CPU_FREQUENCY_CUR=$(grep "cpu MHz" /proc/cpuinfo | head -n1 | cut -d: -f2 | cut -d. -f1 | xargs)
CPU_CORES=$(lscpu | grep "^CPU(s):" | cut -d: -f2 | xargs)
CPU_THREADS=$(nproc) CPU_THREADS=$(nproc)
#---------------------------------
#------------ RAM ---------------- # --- RAM ---
RAM_SIZE=$(lsmem | grep "Total online memory" | cut -d: -f2 | sed 's/\ *//') # On récupère la RAM totale via free -h
RAM_NUMBER=$(dmidecode --type memory | grep 'Rank' | wc -l) RAM_TOTAL=$(free -h | awk '/^Mem:/ {print $2}')
RAM_SLOTS=$(dmidecode --type memory | grep "Number Of Devices" | cut -d: -f2 | sed 's/\ //') # On récupère les slots via inxi
#--------------------------------- RAM_SLOTS=$(inxi -m -c 0 | grep "slots:" | head -n1 | sed -E 's/.*slots: ([0-9]+).*/\1/')
if [ -z "$RAM_SLOTS" ]; then RAM_SLOTS="N/A"; fi
#------------ COMPONENTS --------- # --- MOTHERBOARD / GPU ---
MB_SERIAL=$(dmidecode | grep -A 4 "Base Board" | tail -n1 | cut -d: -f2 | sed 's/\ //') MB_SERIAL=$(inxi -M -c 0 | grep "Mobo:" | sed -E 's/.*Mobo: (.*) model: (.*) serial: .*/\1 \2/' | xargs)
[ -z "$MB_SERIAL" ] && MB_SERIAL=$(inxi -M -c 0 | grep "Mobo:" | cut -d: -f2 | cut -d',' -f1 | xargs)
#------------ STORAGE ------------ GPU_MODEL=$(inxi -G -c 0 | grep "Device-1:" | cut -d: -f2 | xargs)
disks_partitions(){
declare -a DEVICES
mapfile -t DEVICES < <(lsblk -dn -o NAME |grep -v loop)
declare -A PARTITIONS_BY_DISK
for disk in ${DEVICES[@]}; do
disk_path="/dev/$disk"
disk_parts=$(lsblk -nr -o PKNAME,PATH $disk_path |grep -vE "^\ " |cut -d\ -f 2)
PARTITIONS_BY_DISK[$disk]="${disk_parts[@]}"
done
echo "DISKS=${DEVICES[@]}" >> $SUM_FILE
echo "Partitions in each disks: " >> $SUM_FILE
for disk in ${!PARTITIONS_BY_DISK[@]}; do
echo "PARTS_$disk=$(printf '%s ' ${PARTITIONS_BY_DISK[$disk]})" >> $SUM_FILE
done
}
# --- STORAGE ---
# Calcul du stockage total
SIZES=$(lsblk -dnb | grep -v loop | grep -v boot | tr -s " " | cut -d \ -f4) SIZES=$(lsblk -dnb | grep -v loop | grep -v boot | tr -s " " | cut -d \ -f4)
TOTAL_STORAGE=0 TOTAL_STORAGE=0
for SIZE in ${SIZES[@]}; do TOTAL_STORAGE=$((TOTAL_STORAGE + SIZE)); done
for SIZE in ${SIZES[@]}; do
TOTAL_STORAGE+=$SIZE
done
TOTAL_STORAGE=$(numfmt --to iec $TOTAL_STORAGE) TOTAL_STORAGE=$(numfmt --to iec $TOTAL_STORAGE)
#---------------------------------
################################################ # --- SOFTWARE ---
OS=$(lsb_release -d 2>/dev/null | cut -f2 || grep PRETTY_NAME /etc/os-release | cut -d= -f2 | tr -d '"')
######## SOFTWARE PART ######################### ARCH=$(uname -m)
OS=$(lsb_release -a | grep Description | cut -f2)
ARCH=$(uname -a | cut -d' ' -f10)
KERNEL=$(uname -r) KERNEL=$(uname -r)
HOSTNAME=$(hostname) HOSTNAME=$(hostname)
MAC_ADDRESS=$(cat /sys/class/net/$(ls /sys/class/net | grep -vE '^(lo|docker|veth|br)' | head -n 1)/address) DEFAULT_IFACE=$(ls /sys/class/net | grep -vE '^(lo|docker|veth|br)' | head -n 1)
MAC_ADDRESS=$(cat "/sys/class/net/$DEFAULT_IFACE/address" 2>/dev/null || echo "Unknown-MAC")
############################################### ##### JSON PART #####
##### JSON PART ###############################
json_file() { json_file() {
json_data=$(jq -n \ json_data=$(jq -n \
--arg motherboard "$MB_SERIAL" \ --arg motherboard "$MB_SERIAL" \
@ -174,12 +100,14 @@ json_file() {
--arg cpu_frequency_max "$CPU_FREQUENCY_MAX" \ --arg cpu_frequency_max "$CPU_FREQUENCY_MAX" \
--arg gpu_model "$GPU_MODEL" \ --arg gpu_model "$GPU_MODEL" \
--arg ram_slots "$RAM_SLOTS" \ --arg ram_slots "$RAM_SLOTS" \
--arg ram_total "$RAM_TOTAL" \
--arg total_storage "$TOTAL_STORAGE" \
--arg hostname "$HOSTNAME" \ --arg hostname "$HOSTNAME" \
--arg mac_address "$MAC_ADDRESS" \ --arg mac_address "$MAC_ADDRESS" \
--arg os "$OS" \ --arg os "$OS" \
--arg arch "$ARCH" \ --arg arch "$ARCH" \
--arg desktop_env "$XDG_CURRENT_DESKTOP" \ --arg desktop_env "${XDG_CURRENT_DESKTOP:-N/A}" \
--arg window_manager "$XDG_SESSION_TYPE" \ --arg window_manager "${XDG_SESSION_TYPE:-N/A}" \
--arg kernel "$KERNEL" \ --arg kernel "$KERNEL" \
'{ '{
HARDWARE: { HARDWARE: {
@ -192,7 +120,9 @@ json_file() {
cpu_frequency_cur: $cpu_frequency_cur, cpu_frequency_cur: $cpu_frequency_cur,
cpu_frequency_max: $cpu_frequency_max, cpu_frequency_max: $cpu_frequency_max,
gpu_model: $gpu_model, gpu_model: $gpu_model,
ram_slots: $ram_slots ram_slots: $ram_slots,
ram_total: $ram_total,
total_storage: $total_storage
}, },
SOFTWARE: { SOFTWARE: {
hostname: $hostname, hostname: $hostname,
@ -206,46 +136,27 @@ json_file() {
}' }'
) )
# Envoi au serveur
curl -X POST http://localhost:8000/endpoint \ curl -X POST http://localhost:8000/endpoint \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
-d "$json_data" -d "$json_data" \
--connect-timeout 5 || echo "Erreur: Serveur injoignable."
} }
python_venv() { python_venv() {
if [ ! -d "./gbvenv" ]; then [ ! -d "./gbvenv" ] && python3 -m venv gbvenv
echo "Virtual environement doesn't exist, creating one..."
python3 -m venv gbvenv
fi
source gbvenv/bin/activate source gbvenv/bin/activate
pip install --upgrade pip pip install -q --upgrade pip
pip install -r requirements.txt pip install -q -r requirements.txt
uvicorn app:app --reload --host 0.0.0.0 --port 8000 uvicorn app:app --reload --host 0.0.0.0 --port 8000
} }
if [ "$choice" = "2" ]; then if [ "$choice" = "1" ]; then
echo "Grabber has complete his mission! Find every logs saved in your home repository inside the /grabber folder." echo "Starting Python Server..."
echo "See you space cowboy..."
else
echo "It's grabbin time!"
echo "Opening a python virtual environement and starting server..."
# 1. On lance la fonction python_venv en arrière-plan avec '&'
python_venv & python_venv &
# On récupère l'ID du processus du serveur pour pouvoir l'attendre plus tard
SERVER_PID=$! SERVER_PID=$!
echo "Waiting for server to initialize (10s)..."
# 2. On attend quelques secondes que uvicorn soit bien démarré
sleep 5 sleep 5
echo "Fetching data..."
echo "Pushing fetch data into json file..."
# 3. Maintenant que le serveur tourne, on envoie le JSON
json_file json_file
echo "✅ Dashboard: http://localhost:8000"
echo "Grabber has complete his mission! Find every logs saved in your home repository inside the /grabber folder."
echo "Server is running on http://localhost:8000. Press Ctrl+C to stop."
# 4. On empêche le script de se fermer (ce qui tuerait le serveur si configuré ainsi)
wait $SERVER_PID wait $SERVER_PID
fi fi

View File

@ -207,9 +207,18 @@
<span class="label">Coeurs / Threads</span> <span class="label">Coeurs / Threads</span>
<span class="value">{{ ordi.cpu_cores }}C / {{ ordi.cpu_threads }}T</span> <span class="value">{{ ordi.cpu_cores }}C / {{ ordi.cpu_threads }}T</span>
</div> </div>
<div class="data-row"> <div class="data-row">
<span class="label">Fréquence Max</span> <span class="label">Mémoire RAM</span>
<span class="value">{{ ordi.cpu_frequency_max }} MHz</span> <span class="value badge badge-ram">{{ ordi.ram_total }}</span>
</div>
<div class="data-row">
<span class="label">Slots RAM</span>
<span class="value">{{ ordi.ram_slots }}</span>
</div>
<div class="data-row">
<span class="label">Stockage Total</span>
<span class="value" style="color: #fbbf24; font-weight:bold;">{{ ordi.total_storage }}</span>
</div> </div>
<div class="data-row"> <div class="data-row">
<span class="label">Carte Mère</span> <span class="label">Carte Mère</span>
@ -219,10 +228,6 @@
<span class="label">GPU</span> <span class="label">GPU</span>
<span class="value">{{ ordi.gpu_model }}</span> <span class="value">{{ ordi.gpu_model }}</span>
</div> </div>
<div class="data-row">
<span class="label">Slots RAM</span>
<span class="value badge badge-ram">{{ ordi.ram_slots }}</span>
</div>
</div> </div>
<div class="card"> <div class="card">