custom_components/bahmcloud_store/store.py aktualisiert

This commit is contained in:
2026-01-14 18:35:34 +00:00
parent baac800e17
commit 107ceede57

View File

@@ -23,7 +23,7 @@ DOMAIN = "bahmcloud_store"
class StoreError(Exception): class StoreError(Exception):
pass """Store error."""
@dataclass @dataclass
@@ -114,6 +114,7 @@ class BahmcloudStore:
async def refresh(self) -> None: async def refresh(self) -> None:
session = async_get_clientsession(self.hass) session = async_get_clientsession(self.hass)
try: try:
async with session.get(self.config.store_url, timeout=20) as resp: async with session.get(self.config.store_url, timeout=20) as resp:
if resp.status != 200: if resp.status != 200:
@@ -146,7 +147,7 @@ class BahmcloudStore:
pkg.zip_url = self._zip_url(pkg.repo, pkg.branch) pkg.zip_url = self._zip_url(pkg.repo, pkg.branch)
parsed[pkg.id] = pkg parsed[pkg.id] = pkg
# update latest versions (sequential, safe) # compute latest versions
for pkg in parsed.values(): for pkg in parsed.values():
latest, rel_url = await self._fetch_latest_version(pkg) latest, rel_url = await self._fetch_latest_version(pkg)
pkg.latest_version = latest or "unknown" pkg.latest_version = latest or "unknown"
@@ -176,6 +177,7 @@ class BahmcloudStore:
raise StoreError("zip_url not set") raise StoreError("zip_url not set")
session = async_get_clientsession(self.hass) session = async_get_clientsession(self.hass)
with tempfile.TemporaryDirectory() as td: with tempfile.TemporaryDirectory() as td:
zip_path = Path(td) / "repo.zip" zip_path = Path(td) / "repo.zip"
extract_dir = Path(td) / "extract" extract_dir = Path(td) / "extract"
@@ -224,27 +226,46 @@ class BahmcloudStore:
return None return None
async def register_http_views(self) -> None: async def register_http_views(self) -> None:
"""Register HTTP views for static panel assets and JSON API."""
self.hass.http.register_view(_StaticView()) self.hass.http.register_view(_StaticView())
self.hass.http.register_view(_APIListView(self)) self.hass.http.register_view(_APIListView(self))
class _StaticView(HomeAssistantView): class _StaticView(HomeAssistantView):
"""
Serves panel assets from: custom_components/bahmcloud_store/panel/
URLs:
/api/bahmcloud_store_static/index.html
/api/bahmcloud_store_static/panel.js
/api/bahmcloud_store_static/app.js
/api/bahmcloud_store_static/styles.css
"""
requires_auth = True requires_auth = True
name = "bahmcloud_store_static" name = "bahmcloud_store_static"
url = "/api/bahmcloud_store_static/{path:.*}" url = "/api/bahmcloud_store_static/{path:.*}"
async def get(self, request, path): async def get(self, request, path):
base = Path(__file__).resolve().parent / "panel" base = Path(__file__).resolve().parent / "panel"
f = (base / path).resolve() if not path:
if not str(f).startswith(str(base)) or not f.exists(): path = "index.html"
return web.Response(status=404)
if f.suffix == ".js": f = (base / path).resolve()
# Prevent path traversal
if not str(f).startswith(str(base)) or not f.exists() or not f.is_file():
return web.Response(status=404, text="Not found")
suffix = f.suffix.lower()
if suffix == ".js":
return web.Response(body=f.read_bytes(), content_type="application/javascript") return web.Response(body=f.read_bytes(), content_type="application/javascript")
if f.suffix == ".css": if suffix == ".css":
return web.Response(body=f.read_bytes(), content_type="text/css") return web.Response(body=f.read_bytes(), content_type="text/css")
if suffix in (".html", ".htm"):
return web.Response(body=f.read_bytes(), content_type="text/html") return web.Response(body=f.read_bytes(), content_type="text/html")
return web.Response(body=f.read_bytes(), content_type="application/octet-stream")
class _APIListView(HomeAssistantView): class _APIListView(HomeAssistantView):
requires_auth = True requires_auth = True