1
0
mirror of https://github.com/home-assistant/frontend.git synced 2026-04-02 00:27:49 +01:00

Validate external and internal URL on network tab (#30267)

This commit is contained in:
Maarten Lakerveld
2026-03-23 10:01:05 +01:00
committed by GitHub
parent 9979bb13ea
commit 3bbc3403d6
2 changed files with 57 additions and 54 deletions

View File

@@ -1,7 +1,7 @@
import { mdiContentCopy, mdiEye, mdiEyeOff } from "@mdi/js";
import type { PropertyValues } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { customElement, property, query, state } from "lit/decorators";
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
import { isIPAddress } from "../../../common/string/is_ip_address";
import { copyToClipboard } from "../../../common/util/copy-clipboard";
@@ -11,8 +11,8 @@ import "../../../components/ha-card";
import "../../../components/ha-md-list-item";
import "../../../components/ha-switch";
import type { HaSwitch } from "../../../components/ha-switch";
import "../../../components/ha-textfield";
import type { HaTextField } from "../../../components/ha-textfield";
import "../../../components/input/ha-input";
import type { HaInput } from "../../../components/input/ha-input";
import type { CloudStatus } from "../../../data/cloud";
import { fetchCloudStatus } from "../../../data/cloud";
import { saveCoreConfig } from "../../../data/core";
@@ -48,6 +48,12 @@ class ConfigUrlForm extends SubscribeMixin(LitElement) {
@state() private _cloudChecked = false;
@query('[name="external_url"]')
private _externalUrlField?: HaInput;
@query('[name="internal_url"]')
private _internalUrlField?: HaInput;
protected hassSubscribe() {
return [
this.hass.connection.subscribeEvents(() => {
@@ -143,9 +149,13 @@ class ConfigUrlForm extends SubscribeMixin(LitElement) {
: nothing}
<div class="url-container">
<div class="textfield-container">
<ha-textfield
<ha-input
name="external_url"
type="url"
auto-validate
.validationMessage=${this.hass.localize(
"ui.panel.config.url.invalid_url"
)}
placeholder="https://example.duckdns.org:8123"
.value=${this._unmaskedExternalUrl ||
(this._showCustomExternalUrl && canEdit)
@@ -153,23 +163,20 @@ class ConfigUrlForm extends SubscribeMixin(LitElement) {
: obfuscateUrl(externalUrl)}
@change=${this._handleChange}
.disabled=${disabled || !this._showCustomExternalUrl}
.suffix=${
// reserve some space for the icon.
html`<div style="width: 24px"></div>`
}
></ha-textfield>
${!this._showCustomExternalUrl || !canEdit
? html`
<ha-icon-button
class="toggle-unmasked-url"
.label=${this.hass.localize(
`ui.panel.config.common.${this._unmaskedExternalUrl ? "hide" : "show"}_url`
)}
@click=${this._toggleUnmaskedExternalUrl}
.path=${this._unmaskedExternalUrl ? mdiEyeOff : mdiEye}
></ha-icon-button>
`
: nothing}
>
${!this._showCustomExternalUrl || !canEdit
? html`
<ha-icon-button
slot="end"
.label=${this.hass.localize(
`ui.panel.config.common.${this._unmaskedExternalUrl ? "hide" : "show"}_url`
)}
@click=${this._toggleUnmaskedExternalUrl}
.path=${this._unmaskedExternalUrl ? mdiEyeOff : mdiEye}
></ha-icon-button>
`
: nothing}
</ha-input>
</div>
<ha-button
size="small"
@@ -258,9 +265,13 @@ class ConfigUrlForm extends SubscribeMixin(LitElement) {
<div class="url-container">
<div class="textfield-container">
<ha-textfield
<ha-input
name="internal_url"
type="url"
auto-validate
.validationMessage=${this.hass.localize(
"ui.panel.config.url.invalid_url"
)}
placeholder=${this.hass.localize(
"ui.panel.config.url.internal_url_placeholder"
)}
@@ -270,23 +281,20 @@ class ConfigUrlForm extends SubscribeMixin(LitElement) {
: obfuscateUrl(internalUrl)}
@change=${this._handleChange}
.disabled=${disabled || !this._showCustomInternalUrl}
.suffix=${
// reserve some space for the icon.
html`<div style="width: 24px"></div>`
}
></ha-textfield>
${!this._showCustomInternalUrl || !canEdit
? html`
<ha-icon-button
class="toggle-unmasked-url"
.label=${this.hass.localize(
`ui.panel.config.common.${this._unmaskedInternalUrl ? "hide" : "show"}_url`
)}
@click=${this._toggleUnmaskedInternalUrl}
.path=${this._unmaskedInternalUrl ? mdiEyeOff : mdiEye}
></ha-icon-button>
`
: nothing}
>
${!this._showCustomInternalUrl || !canEdit
? html`
<ha-icon-button
slot="end"
.label=${this.hass.localize(
`ui.panel.config.common.${this._unmaskedInternalUrl ? "hide" : "show"}_url`
)}
@click=${this._toggleUnmaskedInternalUrl}
.path=${this._unmaskedInternalUrl ? mdiEyeOff : mdiEye}
></ha-icon-button>
`
: nothing}
</ha-input>
</div>
<ha-button
size="small"
@@ -376,11 +384,17 @@ class ConfigUrlForm extends SubscribeMixin(LitElement) {
}
private _handleChange(ev: ValueChangedEvent<string>) {
const target = ev.currentTarget as HaTextField;
const target = ev.currentTarget as HaInput;
this[`_${target.name}`] = target.value || "";
}
private async _save() {
if (
this._externalUrlField?.reportValidity() === false ||
this._internalUrlField?.reportValidity() === false
) {
return;
}
this._working = true;
this._error = undefined;
try {
@@ -458,23 +472,11 @@ class ConfigUrlForm extends SubscribeMixin(LitElement) {
margin-top: 8px;
}
.textfield-container {
position: relative;
flex: 1;
}
.textfield-container ha-textfield {
.textfield-container ha-input {
display: block;
}
.toggle-unmasked-url {
position: absolute;
top: 8px;
right: 8px;
inset-inline-start: initial;
inset-inline-end: 8px;
--ha-icon-button-size: 40px;
--mdc-icon-size: 20px;
color: var(--secondary-text-color);
direction: var(--direction);
}
ha-md-list-item {
--md-list-item-top-space: 0;

View File

@@ -4181,7 +4181,8 @@
"internal_url_https_error_title": "Invalid local network URL",
"internal_url_https_error_description": "You have configured an HTTPS certificate in Home Assistant. This means that your internal URL needs to be set to a domain covered by the certificate.",
"internal_url_automatic_description": "Use the configured network settings",
"internal_url_placeholder": "http://<some IP address>:8123"
"internal_url_placeholder": "http://<some IP address>:8123",
"invalid_url": "Invalid URL"
},
"hardware": {
"caption": "Hardware",