feat: add manual disk discovery and weekly capacity cache
This commit is contained in:
@@ -7,14 +7,15 @@ from concurrent.futures import ThreadPoolExecutor,as_completed
|
||||
from http import HTTPStatus
|
||||
from http.server import BaseHTTPRequestHandler,ThreadingHTTPServer
|
||||
from pathlib import Path
|
||||
STORAGE_SCAN_LOCK=threading.Lock()
|
||||
from urllib.parse import parse_qs,urlencode,urlparse
|
||||
from urllib.request import Request,urlopen
|
||||
|
||||
APP_VERSION="3.7.2"
|
||||
APP_VERSION="3.11.2"
|
||||
CFG=Path("/etc/pigway-pi-control.conf")
|
||||
STATUS=Path("/run/pigway-pi-control/status.json")
|
||||
TOKEN=Path("/etc/pigway-pi-control-api.token")
|
||||
ALLOWED_SECTIONS={"device","services","processes","alerts","timing","dark_mode","api"}
|
||||
ALLOWED_SECTIONS={"device","services","processes","alerts","timing","dark_mode","api","storage_names","storage_visible"}
|
||||
NAME_RE=re.compile(r"^[A-Za-z0-9_.-]{1,64}$")
|
||||
UNIT_RE=re.compile(r"^[A-Za-z0-9_.@:-]{1,128}\.service$")
|
||||
RGB_KEY_RE=re.compile(r"^(cpu|power|memory|storage|network|service|normal)_[rgb]$")
|
||||
@@ -74,9 +75,16 @@ def api_setting(key,default,cast=str):
|
||||
|
||||
def config_json():
|
||||
cfg=load_cfg()
|
||||
return {section:{k:v for k,v in cfg.items(section) if not (section=="timing" and k in {"oled_refresh_interval","page_interval"})} for section in cfg.sections() if section in ALLOWED_SECTIONS}
|
||||
return {section:{k:v for k,v in cfg.items(section,raw=True) if not (section=="timing" and k in {"oled_refresh_interval","page_interval"})} for section in cfg.sections() if section in ALLOWED_SECTIONS}
|
||||
|
||||
def validate_value(section,key,value,existing):
|
||||
if section in {"storage_names","storage_visible"}:
|
||||
if not re.fullmatch(r"[a-f0-9]{32}",key):raise ValueError("invalid storage identifier")
|
||||
if section=="storage_visible":
|
||||
if value.lower() not in {"true","false"}:raise ValueError("visibility must be true or false")
|
||||
elif len(value)>64 or any(ord(c)<32 for c in value):
|
||||
raise ValueError("storage name must be at most 64 characters without control characters")
|
||||
return
|
||||
if section=="api" and key=="enabled":
|
||||
if value.lower() not in {"true","false"}:raise ValueError("enabled must be true or false")
|
||||
return
|
||||
@@ -388,6 +396,16 @@ class Handler(BaseHTTPRequestHandler):
|
||||
|
||||
def do_PUT(self):
|
||||
path=urlparse(self.path).path
|
||||
if path=="/api/v1/storage/scan":
|
||||
if not self.authorized():self.send_json(401,{"error":"bearer token required"});return
|
||||
try:
|
||||
from storage_cache import StorageInventory
|
||||
with STORAGE_SCAN_LOCK:
|
||||
result=StorageInventory(cache_path=CFG.with_suffix(".storage.json")).scan()
|
||||
self.send_json(200,result)
|
||||
except Exception:
|
||||
self.send_json(503,{"error":"storage scan failed; previous inventory retained"})
|
||||
return
|
||||
if path not in {"/api/v1/plugins","/api/v1/plugin/config","/api/v1/config","/api/v1/services","/api/v1/services/control","/api/v1/processes"}: self.send_json(404,{"error":"not found"}); return
|
||||
if not self.authorized(): self.send_json(401,{"error":"bearer token required"}); return
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user