Fix issues with develop and serve (#24602)

* fix issues with develop and serve

* fix get image data, use hassUrl

* save picture-upload

* Update bundje.cjs

* Prettier

* Fix profile picture in dev serve mode

* person badge too

---------

Co-authored-by: Wendelin <w@pe8.at>
pull/24615/head
Bram Kragten 2025-03-12 16:59:40 +01:00 committed by GitHub
parent 1e000d2740
commit dda7de3301
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 54 additions and 43 deletions

View File

@ -55,7 +55,7 @@ module.exports.definedVars = ({ isProdBuild, latestBuild, defineOverlay }) => ({
__STATIC_PATH__: "/static/", __STATIC_PATH__: "/static/",
__HASS_URL__: `\`${ __HASS_URL__: `\`${
"HASS_URL" in process.env "HASS_URL" in process.env
? process.env["HASS_URL"] ? process.env.HASS_URL
: "${location.protocol}//${location.host}" : "${location.protocol}//${location.host}"
}\``, }\``,
"process.env.NODE_ENV": JSON.stringify( "process.env.NODE_ENV": JSON.stringify(

View File

@ -56,6 +56,7 @@ const getCommonTemplateVars = () => {
); );
return { return {
modernRegex: compileRegex(browserRegexes.concat(haMacOSRegex)).toString(), modernRegex: compileRegex(browserRegexes.concat(haMacOSRegex)).toString(),
hassUrl: process.env.HASS_URL || "",
}; };
}; };

View File

@ -123,7 +123,7 @@ class HaHLSPlayer extends LitElement {
try { try {
const { url } = await fetchStreamUrl(this.hass!, this.entityid); const { url } = await fetchStreamUrl(this.hass!, this.entityid);
this._url = url; this._url = this.hass.hassUrl(url);
this._cleanUp(); this._cleanUp();
this._resetError(); this._resetError();
this._startHls(); this._startHls();
@ -181,15 +181,7 @@ class HaHLSPlayer extends LitElement {
let playlist_url: string; let playlist_url: string;
if (match !== null && matchTwice === null) { if (match !== null && matchTwice === null) {
// Only send the regular playlist url if we match exactly once // Only send the regular playlist url if we match exactly once
// In case we arrive here with a relative URL, we need to provide a valid playlist_url = new URL(match[3], this._url).href;
// base/absolute URL to avoid the URL() constructor throwing an error.
let base_url: string;
try {
base_url = new URL(this._url).href;
} catch (_error) {
base_url = new URL(this._url, window.location.href).href;
}
playlist_url = new URL(match[3], base_url).href;
} else { } else {
playlist_url = this._url; playlist_url = this._url;
} }
@ -219,7 +211,7 @@ class HaHLSPlayer extends LitElement {
await this.hass!.auth.external!.fireMessage({ await this.hass!.auth.external!.fireMessage({
type: "exoplayer/play_hls", type: "exoplayer/play_hls",
payload: { payload: {
url: new URL(url, window.location.href).toString(), url,
muted: this.muted, muted: this.muted,
}, },
}); });

View File

@ -198,7 +198,7 @@ export class HaPictureUpload extends LitElement {
const url = generateImageThumbnailUrl(mediaId, undefined, true); const url = generateImageThumbnailUrl(mediaId, undefined, true);
let data; let data;
try { try {
data = await getImageData(url); data = await getImageData(this.hass, url);
} catch (err: any) { } catch (err: any) {
showAlertDialog(this, { showAlertDialog(this, {
text: err.toString(), text: err.toString(),

View File

@ -4,9 +4,12 @@ import { classMap } from "lit/directives/class-map";
import { styleMap } from "lit/directives/style-map"; import { styleMap } from "lit/directives/style-map";
import type { BasePerson } from "../../data/person"; import type { BasePerson } from "../../data/person";
import { computeUserInitials } from "../../data/user"; import { computeUserInitials } from "../../data/user";
import type { HomeAssistant } from "../../types";
@customElement("ha-person-badge") @customElement("ha-person-badge")
class PersonBadge extends LitElement { class PersonBadge extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public person?: BasePerson; @property({ attribute: false }) public person?: BasePerson;
protected render() { protected render() {
@ -18,7 +21,9 @@ class PersonBadge extends LitElement {
if (picture) { if (picture) {
return html`<div return html`<div
style=${styleMap({ backgroundImage: `url(${picture})` })} style=${styleMap({
backgroundImage: `url(${this.hass.hassUrl(picture)})`,
})}
class="picture" class="picture"
></div>`; ></div>`;
} }

View File

@ -50,7 +50,9 @@ class UserBadge extends LitElement {
if (picture) { if (picture) {
return html`<div return html`<div
style=${styleMap({ backgroundImage: `url(${picture})` })} style=${styleMap({
backgroundImage: `url(${this.hass.hassUrl(picture)})`,
})}
class="picture" class="picture"
></div>`; ></div>`;
} }

View File

@ -22,7 +22,7 @@ export interface BackupOnboardingConfig extends BackupOnboardingInfo {
export const fetchBackupOnboardingInfo = async () => export const fetchBackupOnboardingInfo = async () =>
handleFetchPromise<BackupOnboardingConfig>( handleFetchPromise<BackupOnboardingConfig>(
fetch("/api/onboarding/backup/info") fetch(`${__HASS_URL__}/api/onboarding/backup/info`)
); );
export interface RestoreOnboardingBackupParams { export interface RestoreOnboardingBackupParams {
@ -38,7 +38,7 @@ export const restoreOnboardingBackup = async (
params: RestoreOnboardingBackupParams params: RestoreOnboardingBackupParams
) => ) =>
handleFetchPromise( handleFetchPromise(
fetch("/api/onboarding/backup/restore", { fetch(`${__HASS_URL__}/api/onboarding/backup/restore`, {
method: "POST", method: "POST",
body: JSON.stringify(params), body: JSON.stringify(params),
}) })
@ -58,7 +58,7 @@ export const uploadOnboardingBackup = async (
}); });
return handleFetchPromise( return handleFetchPromise(
fetch(`/api/onboarding/backup/upload?${params.toString()}`, { fetch(`${__HASS_URL__}/api/onboarding/backup/upload?${params.toString()}`, {
method: "POST", method: "POST",
body: fd, body: fd,
}) })

View File

@ -214,7 +214,7 @@ export const uploadBackup = async (
); );
} else { } else {
// When called from onboarding we don't have hass // When called from onboarding we don't have hass
resp = await fetch("/api/hassio/backups/new/upload", { resp = await fetch(`${__HASS_URL__}/api/hassio/backups/new/upload`, {
method: "POST", method: "POST",
body: fd, body: fd,
}); });

View File

@ -81,8 +81,8 @@ export const deleteImage = (hass: HomeAssistant, id: string) =>
image_id: id, image_id: id,
}); });
export const getImageData = async (url: string) => { export const getImageData = async (hass: HomeAssistant, url: string) => {
const response = await fetch(url); const response = await fetch(hass.hassUrl(url));
if (!response.ok) { if (!response.ok) {
throw new Error( throw new Error(

View File

@ -37,7 +37,7 @@ export interface OnboardingStep {
} }
export const fetchOnboardingOverview = () => export const fetchOnboardingOverview = () =>
fetch("/api/onboarding", { credentials: "same-origin" }); fetch(`${__HASS_URL__}/api/onboarding`, { credentials: "same-origin" });
export const onboardUserStep = (params: { export const onboardUserStep = (params: {
client_id: string; client_id: string;
@ -47,7 +47,7 @@ export const onboardUserStep = (params: {
language: string; language: string;
}) => }) =>
handleFetchPromise<OnboardingUserStepResponse>( handleFetchPromise<OnboardingUserStepResponse>(
fetch("/api/onboarding/users", { fetch(`${__HASS_URL__}/api/onboarding/users`, {
method: "POST", method: "POST",
credentials: "same-origin", credentials: "same-origin",
body: JSON.stringify(params), body: JSON.stringify(params),
@ -74,9 +74,12 @@ export const onboardIntegrationStep = (
); );
export const fetchInstallationType = async (): Promise<InstallationType> => { export const fetchInstallationType = async (): Promise<InstallationType> => {
const response = await fetch("/api/onboarding/installation_type", { const response = await fetch(
method: "GET", `${__HASS_URL__}/api/onboarding/installation_type`,
}); {
method: "GET",
}
);
if (response.status === 401) { if (response.status === 401) {
throw Error("unauthorized"); throw Error("unauthorized");

View File

@ -1,5 +1,5 @@
<meta charset="utf-8" /> <meta charset="utf-8" />
<link rel="manifest" href="/manifest.json" crossorigin="use-credentials" /> <link rel="manifest" href="<%= hassUrl %>/manifest.json" crossorigin="use-credentials" />
<link rel="icon" href="/static/icons/favicon.ico" /> <link rel="icon" href="/static/icons/favicon.ico" />
<% for (const entry of latestEntryJS) { %> <% for (const entry of latestEntryJS) { %>
<link rel="modulepreload" href="<%= entry %>" crossorigin="use-credentials" /> <link rel="modulepreload" href="<%= entry %>" crossorigin="use-credentials" />

View File

@ -55,7 +55,7 @@
<%= renderTemplate("_script_loader.html.template") %> <%= renderTemplate("_script_loader.html.template") %>
<script crossorigin="use-credentials"> <script crossorigin="use-credentials">
if (window.latestJS) { if (window.latestJS) {
window.providersPromise = fetch("/auth/providers", { window.providersPromise = fetch("<%= hassUrl %>/auth/providers", {
credentials: "same-origin", credentials: "same-origin",
}); });
} }

View File

@ -89,11 +89,13 @@
window.latestJS = true; window.latestJS = true;
} }
</script> </script>
<script> <% if (obj.hassUrl === "") { %>
{%- for extra_module in extra_modules -%} <script>
import("{{ extra_module }}"); {%- for extra_module in extra_modules -%}
{%- endfor -%} import("{{ extra_module }}");
</script> {%- endfor -%}
</script>
<% } %>
<script> <script>
if (!window.latestJS) { if (!window.latestJS) {
window.customPanelJS = "<%= es5CustomPanelJS %>"; window.customPanelJS = "<%= es5CustomPanelJS %>";
@ -102,12 +104,15 @@
<% } %> <% } %>
} }
</script> </script>
<script>
if (!window.latestJS) { <% if (obj.hassUrl === "") { %>
{%- for extra_script in extra_js_es5 -%} <script>
_ls("{{ extra_script }}"); if (!window.latestJS) {
{%- endfor -%} {%- for extra_script in extra_js_es5 -%}
} _ls("{{ extra_script }}");
</script> {%- endfor -%}
}
</script>
<% } %>
</body> </body>
</html> </html>

View File

@ -51,7 +51,7 @@
<%= renderTemplate("_script_loader.html.template") %> <%= renderTemplate("_script_loader.html.template") %>
<script crossorigin="use-credentials"> <script crossorigin="use-credentials">
if (window.latestJS) { if (window.latestJS) {
window.stepsPromise = fetch("/api/onboarding", { window.stepsPromise = fetch("<%= hassUrl %>/api/onboarding", {
credentials: "same-origin", credentials: "same-origin",
}); });
} }

View File

@ -128,12 +128,15 @@ describe("image_upload", () => {
}); });
describe("getImageData", () => { describe("getImageData", () => {
const hass = {
hassUrl: vi.fn((url) => url),
} as unknown as HomeAssistant;
it("should fetch image data", async () => { it("should fetch image data", async () => {
global.fetch = vi.fn().mockResolvedValue({ global.fetch = vi.fn().mockResolvedValue({
ok: true, ok: true,
blob: vi.fn().mockResolvedValue(new Blob()), blob: vi.fn().mockResolvedValue(new Blob()),
}); });
const data = await getImageData("http://example.com/image.png"); const data = await getImageData(hass, "http://example.com/image.png");
expect(global.fetch).toHaveBeenCalledWith("http://example.com/image.png"); expect(global.fetch).toHaveBeenCalledWith("http://example.com/image.png");
expect(data).toBeInstanceOf(Blob); expect(data).toBeInstanceOf(Blob);
}); });
@ -143,7 +146,7 @@ describe("image_upload", () => {
.fn() .fn()
.mockResolvedValue({ ok: false, statusText: "Not Found" }); .mockResolvedValue({ ok: false, statusText: "Not Found" });
await expect( await expect(
getImageData("http://example.com/image.png") getImageData(hass, "http://example.com/image.png")
).rejects.toThrow("Failed to fetch image: Not Found"); ).rejects.toThrow("Failed to fetch image: Not Found");
}); });
}); });