From d27782ea9c31e46905f1a149f9f7b7137c4daf45 Mon Sep 17 00:00:00 2001 From: bahmcloud Date: Thu, 15 Jan 2026 09:19:58 +0000 Subject: [PATCH] custom_components/bahmcloud_store/views.py aktualisiert --- custom_components/bahmcloud_store/views.py | 55 ++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/custom_components/bahmcloud_store/views.py b/custom_components/bahmcloud_store/views.py index b5e974f..378182c 100644 --- a/custom_components/bahmcloud_store/views.py +++ b/custom_components/bahmcloud_store/views.py @@ -11,6 +11,10 @@ if TYPE_CHECKING: class StaticAssetsView(HomeAssistantView): + """ + Static panel assets MUST be public (no auth), because Home Assistant loads + custom panel JS modules without auth headers. + """ requires_auth = False name = "bahmcloud_store_static" url = "/api/bahmcloud_store_static/{path:.*}" @@ -22,6 +26,7 @@ class StaticAssetsView(HomeAssistantView): 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") @@ -37,6 +42,11 @@ class StaticAssetsView(HomeAssistantView): class BCSApiView(HomeAssistantView): + """ + BCS API (auth required) + - GET /api/bcs + - POST /api/bcs (op=add_custom_repo) + """ requires_auth = True name = "bcs_api" url = "/api/bcs" @@ -45,7 +55,9 @@ class BCSApiView(HomeAssistantView): self.core = core async def get(self, request): + # Refresh on-demand so the UI "Refresh" button updates immediately. await self.core.refresh() + return self.json( { "repos": self.core.list_repos_public(), @@ -71,3 +83,46 @@ class BCSApiView(HomeAssistantView): repo = await self.core.add_custom_repo(url=url, name=name) return self.json({"ok": True, "repo": {"id": repo.id, "url": repo.url, "name": repo.name}}) + + +class BCSReadmeView(HomeAssistantView): + """ + - GET /api/bcs/readme?repo_id= + Returns README markdown from repository root (best-effort). + """ + requires_auth = True + name = "bcs_readme" + url = "/api/bcs/readme" + + def __init__(self, core: "BCSCore") -> None: + self.core = core + + async def get(self, request): + repo_id = str(request.query.get("repo_id") or "").strip() + if not repo_id: + return self.json({"error": "repo_id missing"}, status_code=400) + + repo = self.core.get_repo(repo_id) + if not repo: + return self.json({"error": "repo not found"}, status_code=404) + + md = await self.core.fetch_readme_markdown(repo_id) + if md is None: + return self.json( + { + "ok": False, + "repo_id": repo_id, + "url": repo.url, + "readme": None, + "message": "README not found in repository root.", + } + ) + + return self.json( + { + "ok": True, + "repo_id": repo_id, + "url": repo.url, + "readme": md, + } + )