custom_components/bahmcloud_store/views.py aktualisiert
This commit is contained in:
@@ -6,11 +6,10 @@ from pathlib import Path
|
|||||||
from typing import Any, TYPE_CHECKING
|
from typing import Any, TYPE_CHECKING
|
||||||
|
|
||||||
from aiohttp import web
|
from aiohttp import web
|
||||||
|
|
||||||
from homeassistant.components.http import HomeAssistantView
|
from homeassistant.components.http import HomeAssistantView
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from .core import BCSCore # type hints only, avoids circular import
|
from .core import BCSCore # typing only
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -18,55 +17,50 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
class StaticAssetsView(HomeAssistantView):
|
class StaticAssetsView(HomeAssistantView):
|
||||||
url = "/api/bahmcloud_store_static/{path:.*}"
|
url = "/api/bahmcloud_store_static/{path:.*}"
|
||||||
name = "api:bahmcloud_store_static"
|
name = "api:bahmcloud_store_static"
|
||||||
|
requires_auth = True # keep as before (you said auth works)
|
||||||
# IMPORTANT:
|
|
||||||
# Custom panel JS is safe to serve without auth. Some setups (reverse proxies / cookie quirks)
|
|
||||||
# may cause 401 for module loads. Disabling auth here avoids "Unable to load custom panel".
|
|
||||||
requires_auth = False
|
|
||||||
|
|
||||||
async def get(self, request: web.Request, path: str) -> web.Response:
|
async def get(self, request: web.Request, path: str) -> web.Response:
|
||||||
base = Path(__file__).resolve().parent / "panel"
|
base = Path(__file__).resolve().parent / "panel"
|
||||||
base_resolved = base.resolve()
|
base_resolved = base.resolve()
|
||||||
|
|
||||||
req_path = (path or "").lstrip("/")
|
req_path = (path or "").lstrip("/")
|
||||||
|
|
||||||
# Default file
|
|
||||||
if req_path == "":
|
if req_path == "":
|
||||||
req_path = "index.html"
|
req_path = "index.html"
|
||||||
|
|
||||||
target = (base / req_path).resolve()
|
target = (base / req_path).resolve()
|
||||||
|
|
||||||
# Security: prevent path traversal
|
# Prevent path traversal
|
||||||
if not str(target).startswith(str(base_resolved)):
|
if not str(target).startswith(str(base_resolved)):
|
||||||
return web.Response(status=404)
|
return web.Response(status=404)
|
||||||
|
|
||||||
# If folder -> index.html
|
|
||||||
if target.is_dir():
|
if target.is_dir():
|
||||||
target = (target / "index.html").resolve()
|
target = (target / "index.html").resolve()
|
||||||
|
|
||||||
if not target.exists():
|
if not target.exists():
|
||||||
_LOGGER.warning("BCS static asset not found: %s", target)
|
|
||||||
return web.Response(status=404)
|
return web.Response(status=404)
|
||||||
|
|
||||||
# Content types
|
# Content types (NO charset here!)
|
||||||
ct = "text/plain; charset=utf-8"
|
content_type = "text/plain"
|
||||||
|
charset = None
|
||||||
|
|
||||||
if target.suffix == ".js":
|
if target.suffix == ".js":
|
||||||
ct = "application/javascript; charset=utf-8"
|
content_type = "application/javascript"
|
||||||
|
charset = "utf-8"
|
||||||
elif target.suffix == ".html":
|
elif target.suffix == ".html":
|
||||||
ct = "text/html; charset=utf-8"
|
content_type = "text/html"
|
||||||
|
charset = "utf-8"
|
||||||
elif target.suffix == ".css":
|
elif target.suffix == ".css":
|
||||||
ct = "text/css; charset=utf-8"
|
content_type = "text/css"
|
||||||
|
charset = "utf-8"
|
||||||
elif target.suffix == ".svg":
|
elif target.suffix == ".svg":
|
||||||
ct = "image/svg+xml"
|
content_type = "image/svg+xml"
|
||||||
elif target.suffix == ".png":
|
elif target.suffix == ".png":
|
||||||
ct = "image/png"
|
content_type = "image/png"
|
||||||
|
|
||||||
resp = web.Response(body=target.read_bytes(), content_type=ct)
|
resp = web.Response(body=target.read_bytes(), content_type=content_type, charset=charset)
|
||||||
|
# During development: avoid stale caching issues
|
||||||
# Avoid stale caching issues during development
|
|
||||||
resp.headers["Cache-Control"] = "no-store, no-cache, must-revalidate, max-age=0"
|
resp.headers["Cache-Control"] = "no-store, no-cache, must-revalidate, max-age=0"
|
||||||
resp.headers["Pragma"] = "no-cache"
|
resp.headers["Pragma"] = "no-cache"
|
||||||
|
|
||||||
return resp
|
return resp
|
||||||
|
|
||||||
|
|
||||||
@@ -139,7 +133,6 @@ class BCSReadmeView(HomeAssistantView):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
from homeassistant.util.markdown import async_render_markdown # type: ignore
|
from homeassistant.util.markdown import async_render_markdown # type: ignore
|
||||||
|
|
||||||
html = await async_render_markdown(self.core.hass, md)
|
html = await async_render_markdown(self.core.hass, md)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
_LOGGER.debug("Markdown render failed: %s", e)
|
_LOGGER.debug("Markdown render failed: %s", e)
|
||||||
@@ -148,7 +141,6 @@ class BCSReadmeView(HomeAssistantView):
|
|||||||
if html:
|
if html:
|
||||||
try:
|
try:
|
||||||
from homeassistant.util.sanitize_html import async_sanitize_html # type: ignore
|
from homeassistant.util.sanitize_html import async_sanitize_html # type: ignore
|
||||||
|
|
||||||
html = await async_sanitize_html(self.core.hass, html)
|
html = await async_sanitize_html(self.core.hass, html)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
_LOGGER.debug("HTML sanitize not available/failed: %s", e)
|
_LOGGER.debug("HTML sanitize not available/failed: %s", e)
|
||||||
|
|||||||
Reference in New Issue
Block a user