Server-Adresse: das Schema richtet sich nach dem Ziel
Beim Neuverbinden stand "localhost:5173" im Feld, die App meldete "nicht erreichbar". Ursache war basisUrlNormalisieren(): ohne Schema setzte es blind https:// davor - womit die eigene Vorlage der App unbrauchbar war, denn im Feld steht als Beispiel 192.168.1.20:8123, und im Heimnetz gibt es kein Zertifikat. Jetzt nach dem Ziel: localhost, 127.x, 10.x, 192.168.x, 172.16-31.x, ::1 und .local/.home.arpa/.localhost bekommen http, alles andere weiter https (datametric360.de laeuft ueber Cloudflare). Steht ein Schema da, wird nichts geraten. Vier Regressionstests, darunter der gemeldete Fall, die Vorlage aus dem Feld und 172.32.0.1 als Gegenprobe zum privaten Bereich. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -13,7 +13,8 @@ Debug-Knopf und der HA-Zugang bei den Zugaengen; der Cache-Brecher der Bilder ha
|
||||
jetzt am Foto statt am App-Start; dazu ein Audit mit zwei Befunden in der
|
||||
Wisch-Zeile und der Auswahlliste der App; der Leerzustand der Karte ist deckend,
|
||||
dazu die Rueckfrage vor loeschenden Aktionen als eigenes Blatt statt als
|
||||
Browserdialog, `2026.9.4.17`,
|
||||
Browserdialog; das geratene Schema der Server-Adresse richtet sich jetzt nach dem
|
||||
Ziel, `2026.9.4.18`,
|
||||
Abschnitte CC bis CK. Davor: Geraetezeit statt Ankunftszeit - die Wurzel hinter der 3-km-Fahrt:
|
||||
das Fahrtfenster stand in Geraetezeit, der Verlauf war nach Ankunftszeit sortiert. Dazu die neun
|
||||
Paritaetsbefunde und die verunreinigte Batteriehistorie, `2026.9.4.1`, Abschnitt CB. Davor: Der Regler stellt den Schlaf-Timeout des Dongles, flespi lesend und
|
||||
@@ -10363,3 +10364,36 @@ nicht** - das haette den eingefuegten Token des Eigentuemers geloescht;
|
||||
Abbrechen schliesst das Blatt, die Sitzung bleibt.
|
||||
|
||||
Kein `window.confirm` mehr im Quelltext ausser dem dokumentierten Rueckfall.
|
||||
|
||||
### Nachtrag (2026.9.4.18): das geratene Schema war falsch herum
|
||||
|
||||
Beim Neuverbinden stand „localhost:5173" im Adressfeld, und die App meldete
|
||||
„Der Server ist unter dieser Adresse nicht erreichbar". Ursache war nicht die
|
||||
Eingabe, sondern `basisUrlNormalisieren()`: fehlte das Schema, setzte es
|
||||
**blind `https://`** davor.
|
||||
|
||||
Damit war die **eigene Vorlage der App** unbrauchbar - im Eingabefeld steht
|
||||
als Beispiel `192.168.1.20:8123`, und genau so eingetippt entstand
|
||||
`https://192.168.1.20:8123`. Im Heimnetz gibt es kein Zertifikat, Home
|
||||
Assistant spricht dort `http`.
|
||||
|
||||
Geraten wird jetzt nach dem Ziel: `localhost`, `127.x`, `10.x`,
|
||||
`192.168.x`, `172.16-31.x`, `::1`, `.local`/`.home.arpa`/`.localhost` bekommen
|
||||
`http`, alles andere weiterhin `https` - datametric360.de laeuft ueber
|
||||
Cloudflare, dort waere `http` ein Rueckschritt. Steht ein Schema da, wird
|
||||
nichts geraten.
|
||||
|
||||
Vier Regressionstests (`src/api/umgebung.test.ts`), darunter der gemeldete
|
||||
Fall und die Vorlage aus dem Eingabefeld; `172.32.0.1` als Gegenprobe, weil
|
||||
der private Bereich bei `172.31` endet.
|
||||
|
||||
Ohne Entsprechung im Panel, notwendigerweise: es kennt keine Server-Adresse.
|
||||
|
||||
**Nebenbefund, nicht reproduzierbar:** unmittelbar nach dem Verbinden zeigte
|
||||
die App einmalig „401 Unauthorized" auf einer Datenabfrage, obwohl die
|
||||
Pruefung beim Einrichten (`GET /api/`) durchgegangen war. Direkt danach
|
||||
gemessen: derselbe gespeicherte Token liefert an beiden Endpunkten **200**,
|
||||
und ein Neuladen zeigt die App vollstaendig. Der Verdacht ist ein Rest der
|
||||
alten Sitzung im Speicher der Seite (getrennt und ohne Neuladen neu
|
||||
verbunden); wiederholt sich das, gehoert die Reihenfolge in `fertig()` ->
|
||||
`AngemeldeteApp` -> `DatenAnbieter` genauer angesehen.
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Das Schema der Server-Adresse.
|
||||
*
|
||||
* Anlass: beim Neuverbinden stand „localhost:5173" im Feld — die App machte
|
||||
* daraus `https://localhost:5173` und meldete „Der Server ist unter dieser
|
||||
* Adresse nicht erreichbar". Das Beispiel im Feld selbst
|
||||
* (`192.168.1.20:8123`) hatte dasselbe Problem: im Heimnetz gibt es kein
|
||||
* Zertifikat, Home Assistant spricht dort `http`.
|
||||
*/
|
||||
|
||||
import { describe, expect, it } from "vitest"
|
||||
|
||||
import { basisUrlNormalisieren } from "./umgebung"
|
||||
|
||||
describe("basisUrlNormalisieren", () => {
|
||||
it("nimmt http fuer das eigene Netz", () => {
|
||||
// Genau die Vorlage aus dem Eingabefeld, und der gemeldete Fall.
|
||||
expect(basisUrlNormalisieren("192.168.1.20:8123")).toBe("http://192.168.1.20:8123")
|
||||
expect(basisUrlNormalisieren("localhost:5173")).toBe("http://localhost:5173")
|
||||
expect(basisUrlNormalisieren("127.0.0.1:8123")).toBe("http://127.0.0.1:8123")
|
||||
expect(basisUrlNormalisieren("10.0.0.5:8123")).toBe("http://10.0.0.5:8123")
|
||||
expect(basisUrlNormalisieren("172.16.3.4:8123")).toBe("http://172.16.3.4:8123")
|
||||
expect(basisUrlNormalisieren("homeassistant.local:8123")).toBe(
|
||||
"http://homeassistant.local:8123",
|
||||
)
|
||||
})
|
||||
|
||||
it("bleibt bei https fuer alles von aussen Erreichbare", () => {
|
||||
// Der echte Betriebsfall: die Domain laeuft ueber Cloudflare.
|
||||
expect(basisUrlNormalisieren("datametric360.de")).toBe("https://datametric360.de")
|
||||
expect(basisUrlNormalisieren("ha.example.com:8123")).toBe("https://ha.example.com:8123")
|
||||
// 172.32 liegt bereits AUSSERHALB des privaten Bereichs (nur 16-31).
|
||||
expect(basisUrlNormalisieren("172.32.0.1")).toBe("https://172.32.0.1")
|
||||
})
|
||||
|
||||
it("raet gar nicht, wenn ein Schema dasteht", () => {
|
||||
expect(basisUrlNormalisieren("https://192.168.1.20:8123")).toBe("https://192.168.1.20:8123")
|
||||
expect(basisUrlNormalisieren("http://datametric360.de")).toBe("http://datametric360.de")
|
||||
})
|
||||
|
||||
it("schneidet abschliessende Schraegstriche ab", () => {
|
||||
// Sonst entstehen Adressen wie "https://host//api/states".
|
||||
expect(basisUrlNormalisieren(" https://datametric360.de/// ")).toBe(
|
||||
"https://datametric360.de",
|
||||
)
|
||||
expect(basisUrlNormalisieren("192.168.1.20:8123/")).toBe("http://192.168.1.20:8123")
|
||||
})
|
||||
})
|
||||
@@ -129,9 +129,42 @@ export async function zugangVerwerfen(): Promise<void> {
|
||||
|
||||
/** Schneidet abschließende Schrägstriche ab und ergänzt fehlendes Schema.
|
||||
Ohne das entstehen sonst Adressen wie "https://host//api/states". */
|
||||
/**
|
||||
* Adressen, die per Definition im eigenen Netz liegen - dort gibt es kein
|
||||
* Zertifikat, also spricht der Server http.
|
||||
*
|
||||
* Home Assistant im Heimnetz ist genau dieser Fall: "192.168.1.20:8123" steht
|
||||
* sogar als Beispiel im Eingabefeld. Bis zum 04.09.2026 setzte die App davor
|
||||
* blind https:// - die eigene Vorlage, so eingetippt, ergab damit eine
|
||||
* Adresse, unter der nichts antwortet ("Der Server ist unter dieser Adresse
|
||||
* nicht erreichbar", gemeldet beim Neuverbinden).
|
||||
*
|
||||
* Umgekehrt bleibt https richtig fuer alles, was von aussen erreichbar ist -
|
||||
* datametric360.de laeuft ueber Cloudflare, dort waere http ein Rueckschritt.
|
||||
* Deshalb nicht generell umgestellt, sondern nach dem Ziel unterschieden.
|
||||
*/
|
||||
function imEigenenNetz(host: string): boolean {
|
||||
const h = host.toLowerCase().replace(/:[0-9]+$/, "").replace(/^\[|\]$/g, "");
|
||||
if (h === "localhost" || h === "::1") return true;
|
||||
if (h.endsWith(".local") || h.endsWith(".home.arpa") || h.endsWith(".localhost")) return true;
|
||||
const v4 = /^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$/.exec(h);
|
||||
if (!v4) return false;
|
||||
const a = Number(v4[1]);
|
||||
const b = Number(v4[2]);
|
||||
return a === 127 || a === 10 || (a === 192 && b === 168) || (a === 172 && b >= 16 && b <= 31);
|
||||
}
|
||||
|
||||
/** Schneidet abschliessende Schraegstriche ab und ergaenzt fehlendes Schema.
|
||||
Ohne das entstehen sonst Adressen wie "https://host//api/states".
|
||||
|
||||
Geraten wird nur, wenn kein Schema dasteht - wer http:// oder https://
|
||||
tippt, bekommt genau das. */
|
||||
export function basisUrlNormalisieren(eingabe: string): string {
|
||||
let url = eingabe.trim();
|
||||
if (!/^https?:\/\//i.test(url)) url = `https://${url}`;
|
||||
if (!/^https?:\/\//i.test(url)) {
|
||||
const ziel = url.split("/")[0] ?? "";
|
||||
url = `${imEigenenNetz(ziel) ? "http" : "https"}://${url}`;
|
||||
}
|
||||
return url.replace(/\/+$/, "");
|
||||
}
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version":"2026.9.4.17","sha256":"72442f3c75c5a9a467d552e55ce3892686d7bdecb7ff147920bf3bbcb78cb1e3","bytes":322203,"gebaut":"2026-09-04T12:06:58Z"}
|
||||
{"version":"2026.9.4.18","sha256":"d8b50e70f23ee796ac1618a94ee49893c202bed6139863e840fa76456b2694ca","bytes":322373,"gebaut":"2026-09-04T12:14:55Z"}
|
||||
Binary file not shown.
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"domain": "audi_dashboard",
|
||||
"name": "Audi Dashboard",
|
||||
"version": "2026.9.4.17",
|
||||
"version": "2026.9.4.18",
|
||||
"documentation": "https://gitea.nothaft.cloud/paul/audi-app/src/branch/main/README.md",
|
||||
"issue_tracker": "https://gitea.nothaft.cloud/paul/audi-app/issues",
|
||||
"codeowners": [
|
||||
|
||||
Reference in New Issue
Block a user