Admin: onglet Infos systeme (reseau, RAM, disque, temp, camera)
- Endpoint /api/systeme/info : IPs par interface, hostname, uptime, disque, RAM, temperature CPU, version Python, statut camera - Onglet Infos dans le backoffice avec affichage visuel (barres, couleurs) - Bouton Actualiser pour rafraichir les donnees en temps reel Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
103
backend/main.py
103
backend/main.py
@@ -152,6 +152,109 @@ async def api_preview():
|
||||
return {"image": f"data:image/jpeg;base64,{b64}"}
|
||||
|
||||
|
||||
@app.get("/api/systeme/info")
|
||||
async def api_systeme_info():
|
||||
import socket
|
||||
import subprocess
|
||||
import shutil
|
||||
|
||||
# Interfaces reseau avec leurs IPs
|
||||
interfaces = []
|
||||
try:
|
||||
import netifaces
|
||||
for iface in netifaces.interfaces():
|
||||
addrs = netifaces.ifaddresses(iface)
|
||||
ipv4 = addrs.get(netifaces.AF_INET, [])
|
||||
ipv6 = addrs.get(netifaces.AF_INET6, [])
|
||||
ips = [a['addr'] for a in ipv4 if a.get('addr') != '127.0.0.1']
|
||||
ips += [a['addr'].split('%')[0] for a in ipv6 if not a.get('addr', '').startswith('::1')]
|
||||
if ips:
|
||||
interfaces.append({"nom": iface, "ips": ips})
|
||||
except ImportError:
|
||||
# Fallback sans netifaces
|
||||
try:
|
||||
out = subprocess.check_output(["ip", "-o", "addr", "show"], text=True)
|
||||
for ligne in out.splitlines():
|
||||
parts = ligne.split()
|
||||
if len(parts) >= 4:
|
||||
iface = parts[1]
|
||||
ip = parts[3].split('/')[0]
|
||||
if ip not in ('127.0.0.1', '::1') and not ip.startswith('fe80'):
|
||||
existing = next((x for x in interfaces if x['nom'] == iface), None)
|
||||
if existing:
|
||||
existing['ips'].append(ip)
|
||||
else:
|
||||
interfaces.append({"nom": iface, "ips": [ip]})
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Hostname
|
||||
hostname = socket.gethostname()
|
||||
|
||||
# Uptime
|
||||
uptime_str = ""
|
||||
try:
|
||||
with open('/proc/uptime') as f:
|
||||
secs = float(f.read().split()[0])
|
||||
h, rem = divmod(int(secs), 3600)
|
||||
m = rem // 60
|
||||
uptime_str = f"{h}h {m}min"
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Espace disque (partition racine)
|
||||
disque = {}
|
||||
try:
|
||||
total, used, free = shutil.disk_usage("/")
|
||||
disque = {
|
||||
"total": round(total / 1e9, 1),
|
||||
"utilise": round(used / 1e9, 1),
|
||||
"libre": round(free / 1e9, 1),
|
||||
}
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Memoire RAM
|
||||
ram = {}
|
||||
try:
|
||||
with open('/proc/meminfo') as f:
|
||||
lignes = {l.split(':')[0]: l.split(':')[1].strip() for l in f.readlines()}
|
||||
total_kb = int(lignes.get('MemTotal', '0 kB').split()[0])
|
||||
libre_kb = int(lignes.get('MemAvailable', '0 kB').split()[0])
|
||||
utilise_kb = total_kb - libre_kb
|
||||
ram = {
|
||||
"total": round(total_kb / 1e6, 1),
|
||||
"utilise": round(utilise_kb / 1e6, 1),
|
||||
"libre": round(libre_kb / 1e6, 1),
|
||||
}
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Temperature CPU (RPi / Linux)
|
||||
temp = None
|
||||
try:
|
||||
with open('/sys/class/thermal/thermal_zone0/temp') as f:
|
||||
temp = round(int(f.read().strip()) / 1000, 1)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Version Python et app
|
||||
import sys
|
||||
python_version = f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}"
|
||||
|
||||
return {
|
||||
"hostname": hostname,
|
||||
"interfaces": interfaces,
|
||||
"uptime": uptime_str,
|
||||
"disque": disque,
|
||||
"ram": ram,
|
||||
"temperature_cpu": temp,
|
||||
"python": python_version,
|
||||
"camera_mode": camera.mode,
|
||||
"camera_connectee": camera.connectee,
|
||||
}
|
||||
|
||||
|
||||
@app.get("/api/camera/statut")
|
||||
async def api_camera_statut():
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user