custom_components/bahmcloud_store/panel/panel.js aktualisiert

This commit is contained in:
2026-01-15 12:45:18 +00:00
parent 597d1556ff
commit 106872063a

View File

@@ -21,9 +21,9 @@ class BahmcloudStorePanel extends HTMLElement {
this._detailRepo = null; this._detailRepo = null;
this._readmeLoading = false; this._readmeLoading = false;
this._readmeText = null; this._readmeText = null; // markdown string
this._readmeHtml = null; this._readmeHtml = null; // sanitized html string
this._readmeError = null; this._readmeError = null; // string
} }
set hass(hass) { set hass(hass) {
@@ -46,7 +46,7 @@ class BahmcloudStorePanel extends HTMLElement {
const data = await this._hass.callApi("get", "bcs"); const data = await this._hass.callApi("get", "bcs");
this._data = data; this._data = data;
} catch (e) { } catch (e) {
this._error = e?.message ? String(e.message) : String(e); this._error = this._toErrString(e);
} finally { } finally {
this._loading = false; this._loading = false;
this._update(); this._update();
@@ -106,7 +106,7 @@ class BahmcloudStorePanel extends HTMLElement {
this._view = "manage"; this._view = "manage";
this._update(); this._update();
} catch (e) { } catch (e) {
this._error = e?.message ? String(e.message) : String(e); this._error = this._toErrString(e);
this._update(); this._update();
} }
} }
@@ -120,7 +120,7 @@ class BahmcloudStorePanel extends HTMLElement {
this._view = "manage"; this._view = "manage";
this._update(); this._update();
} catch (e) { } catch (e) {
this._error = e?.message ? String(e.message) : String(e); this._error = this._toErrString(e);
this._update(); this._update();
} }
} }
@@ -142,9 +142,13 @@ class BahmcloudStorePanel extends HTMLElement {
this._loadReadme(repoId); this._loadReadme(repoId);
} }
// --- README fetching (hardened) ---
async _loadReadme(repoId) { async _loadReadme(repoId) {
if (!this._hass) return; if (!this._hass) return;
this._readmeLoading = true; this._readmeLoading = true;
this._readmeText = null;
this._readmeHtml = null;
this._readmeError = null; this._readmeError = null;
this._update(); this._update();
@@ -154,18 +158,37 @@ class BahmcloudStorePanel extends HTMLElement {
`bcs/readme?repo_id=${encodeURIComponent(repoId)}` `bcs/readme?repo_id=${encodeURIComponent(repoId)}`
); );
if (resp?.ok && typeof resp.readme === "string" && resp.readme.trim()) { // Normalize fields strictly (avoid [object Object])
this._readmeText = resp.readme; const ok = resp && resp.ok === true;
this._readmeHtml = typeof resp.html === "string" && resp.html.trim() ? resp.html : null;
const readme = (resp && typeof resp.readme === "string") ? resp.readme : null;
const html = (resp && typeof resp.html === "string") ? resp.html : null;
if (ok && readme && readme.trim()) {
this._readmeText = readme;
this._readmeHtml = html && html.trim() ? html : null;
this._readmeError = null;
} else { } else {
// If backend provided a message, convert it safely
const msg =
(resp && typeof resp.message === "string" && resp.message.trim())
? resp.message.trim()
: "README not found.";
// Extra hint if backend returned unexpected types
if (ok && resp && resp.readme && typeof resp.readme !== "string") {
this._readmeError = "README has an unsupported format (expected text).";
} else {
this._readmeError = msg;
}
this._readmeText = null; this._readmeText = null;
this._readmeHtml = null; this._readmeHtml = null;
this._readmeError = this._safeText(resp?.message) || "README not found.";
} }
} catch (e) { } catch (e) {
this._readmeText = null; this._readmeText = null;
this._readmeHtml = null; this._readmeHtml = null;
this._readmeError = e?.message ? String(e.message) : String(e); this._readmeError = this._toErrString(e) || "README not found.";
} finally { } finally {
this._readmeLoading = false; this._readmeLoading = false;
this._update(); this._update();
@@ -544,10 +567,10 @@ class BahmcloudStorePanel extends HTMLElement {
const creator = this._safeText(r?.owner) ? `Creator: ${this._safeText(r?.owner)}` : "Creator: -"; const creator = this._safeText(r?.owner) ? `Creator: ${this._safeText(r?.owner)}` : "Creator: -";
const latest = this._safeText(r?.latest_version) ? `Latest: ${this._safeText(r?.latest_version)}` : "Latest: unknown"; const latest = this._safeText(r?.latest_version) ? `Latest: ${this._safeText(r?.latest_version)}` : "Latest: unknown";
const cat = this._safeText(r?.category) ? `Category: ${this._safeText(r?.category)}` : null; const prov = this._safeText(r?.provider) ? `Provider: ${this._safeText(r?.provider)}` : null;
const metaSrc = this._safeText(r?.meta_source) ? `Meta: ${this._safeText(r?.meta_source)}` : null; const metaSrc = this._safeText(r?.meta_source) ? `Meta: ${this._safeText(r?.meta_source)}` : null;
const lineBits = [creator, latest, cat, metaSrc].filter(Boolean); const lineBits = [creator, latest, prov, metaSrc].filter(Boolean);
return ` return `
<div class="card clickable" data-repo="${this._esc(id)}"> <div class="card clickable" data-repo="${this._esc(id)}">
@@ -652,7 +675,6 @@ class BahmcloudStorePanel extends HTMLElement {
for (const r of repos) { for (const r of repos) {
s.total += 1; s.total += 1;
if (r?.source === "custom") s.custom += 1; if (r?.source === "custom") s.custom += 1;
const p = this._safeLower(r?.provider) || "other"; const p = this._safeLower(r?.provider) || "other";
@@ -683,9 +705,6 @@ class BahmcloudStorePanel extends HTMLElement {
this._safeText(r?.owner) ? `Creator: ${this._safeText(r?.owner)}` : "Creator: -", this._safeText(r?.owner) ? `Creator: ${this._safeText(r?.owner)}` : "Creator: -",
latest, latest,
this._safeText(r?.provider) ? `Provider: ${this._safeText(r?.provider)}` : null, this._safeText(r?.provider) ? `Provider: ${this._safeText(r?.provider)}` : null,
this._safeText(r?.category) ? `Category: ${this._safeText(r?.category)}` : null,
this._safeText(r?.meta_author) ? `Author: ${this._safeText(r?.meta_author)}` : null,
this._safeText(r?.meta_maintainer) ? `Maintainer: ${this._safeText(r?.meta_maintainer)}` : null,
this._safeText(r?.meta_source) ? `Meta: ${this._safeText(r?.meta_source)}` : null, this._safeText(r?.meta_source) ? `Meta: ${this._safeText(r?.meta_source)}` : null,
].filter(Boolean); ].filter(Boolean);
@@ -758,8 +777,8 @@ class BahmcloudStorePanel extends HTMLElement {
if (!mount) return; if (!mount) return;
if (this._readmeText) { if (this._readmeText) {
// Client renderer may be unavailable; prefer server-provided HTML // We render server-side; only accept a real string html
if (this._readmeHtml) { if (typeof this._readmeHtml === "string" && this._readmeHtml.trim()) {
mount.innerHTML = this._readmeHtml; mount.innerHTML = this._readmeHtml;
this._postprocessRenderedMarkdown(mount); this._postprocessRenderedMarkdown(mount);
return; return;
@@ -914,13 +933,13 @@ class BahmcloudStorePanel extends HTMLElement {
return Array.from(set).sort(); return Array.from(set).sort();
} }
// --- HARDENING HELPERS (fixes [object Object]) --- // --- helpers (prevent [object Object]) ---
_safeText(v) { _safeText(v) {
if (v === null || v === undefined) return ""; if (v === null || v === undefined) return "";
const t = typeof v; const t = typeof v;
if (t === "string") return v; if (t === "string") return v;
if (t === "number" || t === "boolean") return String(v); if (t === "number" || t === "boolean") return String(v);
return ""; // objects/arrays/functions => empty (prevents [object Object]) return "";
} }
_safeLower(v) { _safeLower(v) {
@@ -929,8 +948,18 @@ class BahmcloudStorePanel extends HTMLElement {
} }
_safeId(v) { _safeId(v) {
const s = this._safeText(v); return this._safeText(v) || "";
return s || ""; }
_toErrString(e) {
if (!e) return "Unknown error";
if (typeof e === "string") return e;
if (typeof e?.message === "string") return e.message;
try {
return JSON.stringify(e);
} catch (_) {
return "Unknown error";
}
} }
_esc(s) { _esc(s) {