Add React frontend, ZFS pool mapping, and multi-stage Docker build

- Vite + React frontend with dark-themed dashboard, slot grid per
  enclosure, and SMART detail overlay
- ZFS pool membership via zpool status -P
- Multi-stage Dockerfile (Node build + Python runtime)
- Updated docker-compose with network_mode host and healthcheck
This commit is contained in:
2026-03-07 03:04:23 +00:00
parent e2bd413041
commit 7beead8cae
13 changed files with 554 additions and 6 deletions

42
services/zfs.py Normal file
View File

@@ -0,0 +1,42 @@
import asyncio
import os
import logging
logger = logging.getLogger(__name__)
async def get_zfs_pool_map() -> dict[str, str]:
"""Return a dict mapping device names to ZFS pool names.
e.g. {"sda": "tank", "sdb": "tank", "sdc": "fast"}
"""
pool_map = {}
try:
proc = await asyncio.create_subprocess_exec(
"zpool", "status", "-P",
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE,
)
stdout, _ = await proc.communicate()
if proc.returncode != 0:
return pool_map
current_pool = None
for line in stdout.decode().splitlines():
stripped = line.strip()
if stripped.startswith("pool:"):
current_pool = stripped.split(":", 1)[1].strip()
elif current_pool and "/dev/" in stripped:
parts = stripped.split()
dev_path = parts[0]
try:
real = os.path.realpath(dev_path)
dev_name = os.path.basename(real)
# Strip partition numbers (sda1 -> sda)
dev_name = dev_name.rstrip("0123456789")
pool_map[dev_name] = current_pool
except Exception:
pass
except FileNotFoundError:
logger.debug("zpool not available")
return pool_map