Compare commits
No commits in common. "701192b53c975777d3399ca2675420afa0c9648d" and "7236017e87ec47facf0bcb0d74571fe2fbefe34f" have entirely different histories.
701192b53c
...
7236017e87
9 changed files with 7 additions and 688 deletions
|
|
@ -155,62 +155,13 @@ export function searchCustomers(q: string): Promise<{ customers: Customer[] }> {
|
||||||
return request<{ customers: Customer[] }>(`customers.php?q=${encodeURIComponent(q)}`);
|
return request<{ customers: Customer[] }>(`customers.php?q=${encodeURIComponent(q)}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Kurzform eines Protokolls, wie sie in der Kundenübersicht steht */
|
|
||||||
export interface ProtocolListItem {
|
|
||||||
id: number;
|
|
||||||
ref?: string;
|
|
||||||
label?: string;
|
|
||||||
clientUuid?: string;
|
|
||||||
socId?: number | null;
|
|
||||||
orderId?: number | null;
|
|
||||||
/** Unix-Sekunden (nicht Millisekunden — so liefert es die API) */
|
|
||||||
dateDiag?: number;
|
|
||||||
location?: string;
|
|
||||||
subnet?: string;
|
|
||||||
status?: number;
|
|
||||||
deviceCount?: number;
|
|
||||||
/** heißt serverseitig `measureCount` — nicht `measurementCount` (netdiag_api.lib.php) */
|
|
||||||
measureCount?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Einzelnen Kunden mit Aufträgen und Protokollen laden */
|
/** Einzelnen Kunden mit Aufträgen und Protokollen laden */
|
||||||
export function getCustomer(
|
export function getCustomer(
|
||||||
id: number,
|
id: number,
|
||||||
): Promise<{ customer: Customer; orders: Order[]; protocols: ProtocolListItem[] }> {
|
): Promise<{ customer: Customer; orders: Order[]; protocols: unknown[] }> {
|
||||||
return request(`customers.php?id=${id}`);
|
return request(`customers.php?id=${id}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Ein Gerät, wie es der Server im Protokoll liefert */
|
|
||||||
export interface ServerDevice {
|
|
||||||
id: number;
|
|
||||||
ip: string;
|
|
||||||
mac?: string | null;
|
|
||||||
hostname?: string | null;
|
|
||||||
netbiosName?: string | null;
|
|
||||||
mdnsName?: string | null;
|
|
||||||
mdnsServices?: string[];
|
|
||||||
customName?: string | null;
|
|
||||||
vendor?: string | null;
|
|
||||||
deviceType?: string | null;
|
|
||||||
openPorts?: number[];
|
|
||||||
foundVia?: string | null;
|
|
||||||
lastSeen?: number | null;
|
|
||||||
note?: string | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ein Protokoll vom Server laden — samt Geräten und Messungen.
|
|
||||||
*
|
|
||||||
* Gebraucht vom Geräte-Vergleich, der die Liste des letzten Besuchs braucht.
|
|
||||||
* Bewusst NICHT in den lokalen Bestand übernommen: das frühere Protokoll wird
|
|
||||||
* nur gelesen, nie überschrieben.
|
|
||||||
*/
|
|
||||||
export function getProtocol(
|
|
||||||
id: number,
|
|
||||||
): Promise<{ protocol: Record<string, unknown>; devices: ServerDevice[]; measurements: unknown[] }> {
|
|
||||||
return request(`protocols.php?id=${id}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SyncResult {
|
export interface SyncResult {
|
||||||
ok: boolean;
|
ok: boolean;
|
||||||
protocolId: number;
|
protocolId: number;
|
||||||
|
|
|
||||||
|
|
@ -65,13 +65,6 @@ const FELDER: Record<string, Feld> = {
|
||||||
// DNS-Prüfung (je ein Paket an je einen Server)
|
// DNS-Prüfung (je ein Paket an je einen Server)
|
||||||
gefragteServer: { label: 'Befragte DNS-Server' },
|
gefragteServer: { label: 'Befragte DNS-Server' },
|
||||||
antworten: { label: 'Antworten der Server' },
|
antworten: { label: 'Antworten der Server' },
|
||||||
// Geräte-Vergleich zweier Protokolle
|
|
||||||
verglichenMit: { label: 'Verglichen mit' },
|
|
||||||
geraeteVorher: { label: 'Geräte beim letzten Besuch' },
|
|
||||||
geraeteJetzt: { label: 'Geräte jetzt' },
|
|
||||||
mitNeuerAdresse: { label: 'Mit neuer IP-Adresse' },
|
|
||||||
andereOffenePorts: { label: 'Gleiche Adresse, andere offene Ports' },
|
|
||||||
unveraendert: { label: 'Unverändert' },
|
|
||||||
// Netz / DHCP / Dose
|
// Netz / DHCP / Dose
|
||||||
server: { label: 'DHCP-Server' },
|
server: { label: 'DHCP-Server' },
|
||||||
dhcpServer: { label: 'DHCP-Server' },
|
dhcpServer: { label: 'DHCP-Server' },
|
||||||
|
|
|
||||||
|
|
@ -1,74 +0,0 @@
|
||||||
/**
|
|
||||||
* Prüfung der Server-Adresse — ohne Laufzeit-Import, damit
|
|
||||||
* `tools/pruefe-serveradresse.mjs` sie nachrechnen kann.
|
|
||||||
*
|
|
||||||
* Anlass (16./17.08.2026, im Emulator gefunden): Wird hier eine `http://`-Adresse
|
|
||||||
* eingetragen, funktioniert die App **stumm nicht**. Sie meldet nur „Keine
|
|
||||||
* Verbindung zum Server"; der wahre Grund steht ausschließlich im `logcat`, an
|
|
||||||
* den beim Kunden niemand kommt. Es sind sogar zwei Sperren übereinander:
|
|
||||||
*
|
|
||||||
* 1. **Mixed Content** — die App läuft auf der Origin `https://localhost`; der
|
|
||||||
* WebView blockiert daraus jede `http://`-Anfrage.
|
|
||||||
* 2. **Androids Cleartext-Richtlinie** — ab API 28 ist unverschlüsseltes HTTP
|
|
||||||
* ohne `android:usesCleartextTraffic` grundsätzlich verboten.
|
|
||||||
*
|
|
||||||
* Deshalb wird die Adresse hier abgewiesen, statt sie anzunehmen und den
|
|
||||||
* Techniker vor Ort raten zu lassen.
|
|
||||||
*
|
|
||||||
* WICHTIG — warum nicht einfach „http verbieten": Es hängt an der Origin der
|
|
||||||
* App, nicht an einer festen Regel. Ein Debug-Build mit
|
|
||||||
* `server.androidScheme: 'http'` (der Weg, auf dem die lokale Testinstanz
|
|
||||||
* überhaupt prüfbar ist, siehe KB #1089) läuft selbst auf `http://localhost`
|
|
||||||
* und darf HTTP-Server ansprechen. Die Prüfung fragt deshalb die eigene
|
|
||||||
* Herkunft ab — sonst wäre ausgerechnet der Testbau unbenutzbar.
|
|
||||||
*/
|
|
||||||
|
|
||||||
export interface AdressPruefung {
|
|
||||||
ok: boolean;
|
|
||||||
/** Meldung für den Benutzer; leer wenn alles in Ordnung ist */
|
|
||||||
meldung: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Server-Adresse prüfen.
|
|
||||||
*
|
|
||||||
* @param eingabe Rohe Eingabe aus dem Feld
|
|
||||||
* @param appProtokoll Protokoll der App-Origin — `window.location.protocol`,
|
|
||||||
* also 'https:' oder 'http:'
|
|
||||||
*/
|
|
||||||
export function pruefeServerAdresse(eingabe: string, appProtokoll: string): AdressPruefung {
|
|
||||||
const url = (eingabe ?? '').trim();
|
|
||||||
if (!url) {
|
|
||||||
return { ok: false, meldung: 'Server-Adresse fehlt' };
|
|
||||||
}
|
|
||||||
if (!/^https?:\/\//i.test(url)) {
|
|
||||||
return { ok: false, meldung: 'Adresse muss mit http:// oder https:// beginnen' };
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tippfehler wie "https://" ohne Host früh abfangen — sonst schlägt erst der
|
|
||||||
// erste Aufruf fehl, und zwar mit derselben nichtssagenden Meldung.
|
|
||||||
let host = '';
|
|
||||||
try {
|
|
||||||
host = new URL(url).hostname;
|
|
||||||
} catch {
|
|
||||||
return { ok: false, meldung: 'Adresse ist keine gültige URL' };
|
|
||||||
}
|
|
||||||
if (!host) {
|
|
||||||
return { ok: false, meldung: 'In der Adresse fehlt der Servername' };
|
|
||||||
}
|
|
||||||
|
|
||||||
const appLaeuftUeberHttps = appProtokoll.toLowerCase().startsWith('https');
|
|
||||||
const zielIstHttp = /^http:\/\//i.test(url);
|
|
||||||
|
|
||||||
if (zielIstHttp && appLaeuftUeberHttps) {
|
|
||||||
return {
|
|
||||||
ok: false,
|
|
||||||
meldung:
|
|
||||||
'Diese App kann nur HTTPS-Server erreichen — Android blockiert unverschlüsselte ' +
|
|
||||||
'Verbindungen aus der App heraus. Mit einer http://-Adresse käme nur „Keine ' +
|
|
||||||
'Verbindung zum Server", ohne erkennbaren Grund.',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return { ok: true, meldung: '' };
|
|
||||||
}
|
|
||||||
|
|
@ -10,7 +10,6 @@
|
||||||
import type { Tool, ToolCategory } from './types';
|
import type { Tool, ToolCategory } from './types';
|
||||||
|
|
||||||
import { dnsCheckTool } from './netzwerk/dnscheck';
|
import { dnsCheckTool } from './netzwerk/dnscheck';
|
||||||
import { geraeteDiffTool } from './netzwerk/geraetediff';
|
|
||||||
import { ipConflictTool } from './netzwerk/ipconflict';
|
import { ipConflictTool } from './netzwerk/ipconflict';
|
||||||
import { ipScanTool } from './netzwerk/ipscan';
|
import { ipScanTool } from './netzwerk/ipscan';
|
||||||
import { pingTool } from './netzwerk/ping';
|
import { pingTool } from './netzwerk/ping';
|
||||||
|
|
@ -39,7 +38,6 @@ export const TOOLS: Tool[] = [
|
||||||
snmpTool,
|
snmpTool,
|
||||||
tracerouteTool,
|
tracerouteTool,
|
||||||
dnsCheckTool,
|
dnsCheckTool,
|
||||||
geraeteDiffTool,
|
|
||||||
stressTestTool,
|
stressTestTool,
|
||||||
// Internet — die Kette steht bewusst vorn: sie ist die Erstdiagnose,
|
// Internet — die Kette steht bewusst vorn: sie ist die Erstdiagnose,
|
||||||
// wenn der Kunde „Internet geht nicht" sagt.
|
// wenn der Kunde „Internet geht nicht" sagt.
|
||||||
|
|
|
||||||
|
|
@ -1,266 +0,0 @@
|
||||||
/**
|
|
||||||
* Geräte-Vergleich zweier Protokolle — ohne Laufzeit-Import, damit
|
|
||||||
* `tools/pruefe-geraetediff.mjs` die Zuordnung nachrechnen kann.
|
|
||||||
*
|
|
||||||
* WARUM NICHT ÜBER DIE MAC-ADRESSE
|
|
||||||
*
|
|
||||||
* Der naheliegende Weg wäre, Geräte über ihre MAC wiederzuerkennen. Er
|
|
||||||
* funktioniert hier nachweislich nicht: Im ausgelieferten Protokoll ND2026-0015
|
|
||||||
* sind bei **allen 68 Geräten** MAC, Hostname und Hersteller leer, weil
|
|
||||||
* `/proc/net/arp` ab Android 10 gesperrt ist. Ein MAC-Diff hätte dort bei jedem
|
|
||||||
* Besuch „alles neu, alles verschwunden" gemeldet — eine Aussage, die schlimmer
|
|
||||||
* ist als gar keine, weil sie im Kundendokument nach einem umgebauten Netz
|
|
||||||
* aussieht.
|
|
||||||
*
|
|
||||||
* ACHTUNG BEIM PRÜFEN: Der Browser-Mock gibt jedem Gerät eine MAC und meldet
|
|
||||||
* `arpAvailable: true`. Ein MAC-Diff sieht dort perfekt aus und beweist nichts.
|
|
||||||
* `tools/pruefe-geraetediff.mjs` arbeitet deshalb ausdrücklich mit Geräten OHNE
|
|
||||||
* MAC — so, wie sie beim Kunden ankommen.
|
|
||||||
*
|
|
||||||
* DIE KASKADE
|
|
||||||
*
|
|
||||||
* Wiedererkannt wird über den ersten belastbaren Anhaltspunkt, den ein Gerät
|
|
||||||
* hergibt. Die Reihenfolge ist nach Verlässlichkeit sortiert, nicht nach
|
|
||||||
* Bequemlichkeit:
|
|
||||||
*
|
|
||||||
* 1. **MAC** — wenn ausnahmsweise vorhanden (eigener Adapter, ältere Android-
|
|
||||||
* Version), ist sie unschlagbar. Nur eben fast nie da.
|
|
||||||
* 2. **mDNS-Name** — „Brother HL-L2350DW", „Wohnzimmer-TV". Vom Gerät selbst
|
|
||||||
* vergeben und über Neustarts hinweg stabil.
|
|
||||||
* 3. **NetBIOS-Name** — dasselbe für Windows-Geräte.
|
|
||||||
* 4. **Hostname** aus dem DNS.
|
|
||||||
* 5. **IP + Portmuster** — der Rückfallwert. Eine IP allein taugt nicht: im
|
|
||||||
* DHCP-Bereich wandert sie. Zusammen mit den offenen Ports wird daraus ein
|
|
||||||
* brauchbarer Fingerabdruck (eine Kamera mit 80/554 an .51 ist mit hoher
|
|
||||||
* Wahrscheinlichkeit dieselbe wie beim letzten Mal).
|
|
||||||
*
|
|
||||||
* Der benutzervergebene Name (`customName`) fließt bewusst NICHT ein: er wird
|
|
||||||
* am Gerät im Protokoll vergeben und wäre beim nächsten Besuch nur dann da,
|
|
||||||
* wenn dasselbe Protokoll fortgeschrieben wird.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** Minimalform eines Geräts, wie sie App und API gleichermaßen liefern */
|
|
||||||
export interface DiffGeraet {
|
|
||||||
ip: string;
|
|
||||||
mac?: string | null;
|
|
||||||
hostname?: string | null;
|
|
||||||
netbiosName?: string | null;
|
|
||||||
mdnsName?: string | null;
|
|
||||||
openPorts?: number[] | null;
|
|
||||||
deviceType?: string | null;
|
|
||||||
vendor?: string | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Wie ein Gerät wiedererkannt wurde — steht so im Kundendokument */
|
|
||||||
export type Erkennung = 'mac' | 'mdns' | 'netbios' | 'hostname' | 'ip+ports';
|
|
||||||
|
|
||||||
export interface DiffEintrag {
|
|
||||||
ip: string;
|
|
||||||
/** Anzeigename, so gut wie das Gerät ihn hergibt */
|
|
||||||
name: string;
|
|
||||||
erkennung: Erkennung;
|
|
||||||
/** bei Wiedersehen: die frühere IP, falls sie sich geändert hat */
|
|
||||||
frühereIp?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface DiffErgebnis {
|
|
||||||
neu: DiffEintrag[];
|
|
||||||
verschwunden: DiffEintrag[];
|
|
||||||
geblieben: DiffEintrag[];
|
|
||||||
/** geblieben, aber unter anderer IP */
|
|
||||||
ipGewechselt: DiffEintrag[];
|
|
||||||
/**
|
|
||||||
* Gleiche Adresse, aber anderes Portmuster — dasselbe Gerät bietet andere
|
|
||||||
* Dienste an, oder es sitzt ein anderes Gerät auf der Adresse. Ohne diese
|
|
||||||
* Kategorie stünde es gleichzeitig unter „neu" und „verschwunden".
|
|
||||||
*/
|
|
||||||
portsGeaendert: DiffEintrag[];
|
|
||||||
/**
|
|
||||||
* Anteil der Geräte, die nur über „IP + Portmuster" zugeordnet werden
|
|
||||||
* konnten (0–100). Ab hier wird die Aussage unsicher, und das gehört ins
|
|
||||||
* Dokument statt in eine Fußnote.
|
|
||||||
*/
|
|
||||||
schwacheZuordnungProzent: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Namen normalisieren: Groß/Klein und .local-Endung sind kein Unterschied */
|
|
||||||
function normName(v: string | null | undefined): string {
|
|
||||||
return (v ?? '')
|
|
||||||
.trim()
|
|
||||||
.toLowerCase()
|
|
||||||
.replace(/\.local\.?$/, '');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Schlüssel eines Geräts nach der Kaskade — plus die Angabe, worauf er beruht.
|
|
||||||
*/
|
|
||||||
export function geraeteSchluessel(g: DiffGeraet): { key: string; erkennung: Erkennung } {
|
|
||||||
const mac = normName(g.mac).replace(/[^0-9a-f]/g, '');
|
|
||||||
if (mac.length === 12) {
|
|
||||||
return { key: 'mac:' + mac, erkennung: 'mac' };
|
|
||||||
}
|
|
||||||
const mdns = normName(g.mdnsName);
|
|
||||||
if (mdns) {
|
|
||||||
return { key: 'mdns:' + mdns, erkennung: 'mdns' };
|
|
||||||
}
|
|
||||||
const nb = normName(g.netbiosName);
|
|
||||||
if (nb) {
|
|
||||||
return { key: 'nb:' + nb, erkennung: 'netbios' };
|
|
||||||
}
|
|
||||||
const hn = normName(g.hostname);
|
|
||||||
if (hn) {
|
|
||||||
return { key: 'host:' + hn, erkennung: 'hostname' };
|
|
||||||
}
|
|
||||||
// Rückfall: IP + sortiertes Portmuster. Ports sortiert, weil die
|
|
||||||
// Scan-Reihenfolge nicht garantiert ist.
|
|
||||||
const ports = [...(g.openPorts ?? [])].sort((a, b) => a - b).join(',');
|
|
||||||
return { key: `ipp:${g.ip}|${ports}`, erkennung: 'ip+ports' };
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Anzeigename für das Protokoll — der erste, den das Gerät hergibt */
|
|
||||||
export function anzeigeName(g: DiffGeraet): string {
|
|
||||||
for (const kandidat of [g.mdnsName, g.netbiosName, g.hostname]) {
|
|
||||||
const v = (kandidat ?? '').trim();
|
|
||||||
if (v) return v;
|
|
||||||
}
|
|
||||||
return g.ip;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Zwei Gerätelisten vergleichen.
|
|
||||||
*
|
|
||||||
* @param vorher Geräte des früheren Protokolls
|
|
||||||
* @param jetzt Geräte des aktuellen Protokolls
|
|
||||||
*/
|
|
||||||
export function vergleicheGeraete(vorher: DiffGeraet[], jetzt: DiffGeraet[]): DiffErgebnis {
|
|
||||||
const vorherMap = new Map<string, DiffGeraet>();
|
|
||||||
for (const g of vorher) {
|
|
||||||
vorherMap.set(geraeteSchluessel(g).key, g);
|
|
||||||
}
|
|
||||||
const jetztMap = new Map<string, DiffGeraet>();
|
|
||||||
for (const g of jetzt) {
|
|
||||||
jetztMap.set(geraeteSchluessel(g).key, g);
|
|
||||||
}
|
|
||||||
|
|
||||||
const neu: DiffEintrag[] = [];
|
|
||||||
const geblieben: DiffEintrag[] = [];
|
|
||||||
const ipGewechselt: DiffEintrag[] = [];
|
|
||||||
let schwach = 0;
|
|
||||||
|
|
||||||
for (const g of jetzt) {
|
|
||||||
const { key, erkennung } = geraeteSchluessel(g);
|
|
||||||
if (erkennung === 'ip+ports') schwach++;
|
|
||||||
const alt = vorherMap.get(key);
|
|
||||||
const eintrag: DiffEintrag = { ip: g.ip, name: anzeigeName(g), erkennung };
|
|
||||||
if (!alt) {
|
|
||||||
neu.push(eintrag);
|
|
||||||
} else if (alt.ip !== g.ip) {
|
|
||||||
ipGewechselt.push({ ...eintrag, frühereIp: alt.ip });
|
|
||||||
} else {
|
|
||||||
geblieben.push(eintrag);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const verschwunden: DiffEintrag[] = [];
|
|
||||||
for (const g of vorher) {
|
|
||||||
const { key, erkennung } = geraeteSchluessel(g);
|
|
||||||
if (!jetztMap.has(key)) {
|
|
||||||
verschwunden.push({ ip: g.ip, name: anzeigeName(g), erkennung });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Dieselbe Adresse in „neu" UND „verschwunden" — das passiert bei einem
|
|
||||||
* namenlosen Gerät, dessen Portmuster sich geändert hat: der Schlüssel
|
|
||||||
* `ip+ports` ist dann ein anderer, obwohl die Adresse dieselbe blieb.
|
|
||||||
*
|
|
||||||
* Formal ist das richtig, im Kundendokument aber Unsinn: „10.0.2.2 ist neu"
|
|
||||||
* direkt über „10.0.2.2 ist nicht mehr erreichbar" liest sich wie ein
|
|
||||||
* kaputtes Werkzeug. Genau so stand es im ersten erzeugten PDF (ND2026-0016).
|
|
||||||
* Solche Fälle bekommen deshalb eine eigene, ehrliche Kategorie — und sie ist
|
|
||||||
* sogar der interessantere Befund: an dieser Adresse hat sich etwas an den
|
|
||||||
* angebotenen Diensten geändert.
|
|
||||||
*/
|
|
||||||
const portsGeaendert: DiffEintrag[] = [];
|
|
||||||
const verschwundenIps = new Map(verschwunden.map((e) => [e.ip, e]));
|
|
||||||
for (let i = neu.length - 1; i >= 0; i--) {
|
|
||||||
const alt = verschwundenIps.get(neu[i].ip);
|
|
||||||
if (!alt || alt.erkennung !== 'ip+ports' || neu[i].erkennung !== 'ip+ports') continue;
|
|
||||||
portsGeaendert.push(neu[i]);
|
|
||||||
neu.splice(i, 1);
|
|
||||||
const j = verschwunden.findIndex((e) => e.ip === alt.ip);
|
|
||||||
if (j >= 0) verschwunden.splice(j, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
neu,
|
|
||||||
verschwunden,
|
|
||||||
geblieben,
|
|
||||||
ipGewechselt,
|
|
||||||
portsGeaendert,
|
|
||||||
schwacheZuordnungProzent: jetzt.length ? Math.round((schwach / jetzt.length) * 100) : 0,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ergebnis bewerten.
|
|
||||||
*
|
|
||||||
* Bewusst KEINE Ampel nach „viele Änderungen = schlecht": ein neues Gerät im
|
|
||||||
* Netz ist erst einmal nur eine Tatsache, und der Techniker hat es womöglich
|
|
||||||
* selbst angeschlossen. Rot gibt es hier nicht — Gelb nur, wenn die Aussage
|
|
||||||
* selbst wackelt oder etwas fehlt, das vorher da war.
|
|
||||||
*/
|
|
||||||
export function bewerteDiff(d: DiffErgebnis, vorherAnzahl: number): {
|
|
||||||
status: 0 | 1 | 2 | 3;
|
|
||||||
label: string;
|
|
||||||
hinweis?: string;
|
|
||||||
} {
|
|
||||||
if (vorherAnzahl === 0) {
|
|
||||||
return {
|
|
||||||
status: 3,
|
|
||||||
label: 'Kein früheres Protokoll zum Vergleichen',
|
|
||||||
hinweis:
|
|
||||||
'Für diesen Kunden liegt kein abgeschlossenes früheres Protokoll vor. Der Vergleich ' +
|
|
||||||
'ist damit nicht möglich — es wurde nichts über Veränderungen im Netz festgestellt.',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const teile: string[] = [];
|
|
||||||
if (d.neu.length) teile.push(`${d.neu.length} neu`);
|
|
||||||
if (d.verschwunden.length) teile.push(`${d.verschwunden.length} nicht mehr erreichbar`);
|
|
||||||
if (d.ipGewechselt.length) teile.push(`${d.ipGewechselt.length} mit neuer IP`);
|
|
||||||
if (d.portsGeaendert.length) teile.push(`${d.portsGeaendert.length} mit anderen offenen Ports`);
|
|
||||||
const label = teile.length
|
|
||||||
? `Veränderungen: ${teile.join(', ')} (${d.geblieben.length} unverändert)`
|
|
||||||
: `Keine Veränderung — alle ${d.geblieben.length} Geräte wie beim letzten Mal`;
|
|
||||||
|
|
||||||
const hinweise: string[] = [];
|
|
||||||
if (d.schwacheZuordnungProzent >= 50) {
|
|
||||||
hinweise.push(
|
|
||||||
`Bei ${d.schwacheZuordnungProzent} % der Geräte war keine Kennung verfügbar (kein ` +
|
|
||||||
'mDNS-, NetBIOS- oder Hostname); sie wurden über IP und Portmuster zugeordnet. ' +
|
|
||||||
'Wechselt ein Gerät im DHCP-Bereich die Adresse, kann es dabei fälschlich als neu ' +
|
|
||||||
'gelten. Die Einzelzeilen nennen jeweils die verwendete Kennung.',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (d.verschwunden.length) {
|
|
||||||
hinweise.push(
|
|
||||||
'Nicht erreichbar heißt nicht zwingend „abgebaut" — ein ausgeschaltetes Gerät, ein ' +
|
|
||||||
'schlafender Rechner oder eine Kamera im Standby fehlen im Scan genauso.',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (d.portsGeaendert.length) {
|
|
||||||
hinweise.push(
|
|
||||||
'Bei gleicher Adresse mit anderen offenen Ports kann ein Dienst hinzugekommen oder ' +
|
|
||||||
'weggefallen sein — oder es sitzt inzwischen ein anderes Gerät auf der Adresse. ' +
|
|
||||||
'Ohne Gerätenamen lässt sich das nicht unterscheiden.',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
// Gelb nur, wenn etwas fehlt oder die Zuordnung wackelt.
|
|
||||||
status: d.verschwunden.length || d.schwacheZuordnungProzent >= 50 ? 1 : 0,
|
|
||||||
label,
|
|
||||||
hinweis: hinweise.length ? hinweise.join(' ') : undefined,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -1,130 +0,0 @@
|
||||||
/**
|
|
||||||
* Tool: Geräte-Vergleich — was hat sich seit dem letzten Besuch geändert?
|
|
||||||
*
|
|
||||||
* Holt das jüngste frühere Protokoll desselben Kunden vom Server und
|
|
||||||
* vergleicht dessen Geräteliste mit der aktuellen. Beim Wiederholungsbesuch
|
|
||||||
* ist das die Frage, die der Kunde als Erstes stellt („da ist doch nichts
|
|
||||||
* dazugekommen, oder?") — und die einzige, die ein einzelnes Protokoll nicht
|
|
||||||
* beantworten kann.
|
|
||||||
*
|
|
||||||
* Die Wiedererkennung läuft NICHT über MAC-Adressen; warum, steht ausführlich
|
|
||||||
* im Kopf von `geraete-diff.ts`. Kurz: `/proc/net/arp` ist ab Android 10
|
|
||||||
* gesperrt, im ausgelieferten Protokoll ND2026-0015 hatten alle 68 Geräte
|
|
||||||
* keine MAC. Ein MAC-Diff meldete dort bei jedem Besuch „alles neu".
|
|
||||||
*
|
|
||||||
* Braucht Serververbindung. Ohne die gibt es kein Vergleichsprotokoll — dann
|
|
||||||
* meldet das Werkzeug „nicht messbar" statt zu raten.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { getCustomer, getProtocol } from '../../api';
|
|
||||||
import type { Tool } from '../types';
|
|
||||||
import { bewerteDiff, vergleicheGeraete, type DiffGeraet } from './geraete-diff';
|
|
||||||
|
|
||||||
export { bewerteDiff, vergleicheGeraete, geraeteSchluessel } from './geraete-diff';
|
|
||||||
export type { DiffGeraet, DiffErgebnis, DiffEintrag, Erkennung } from './geraete-diff';
|
|
||||||
|
|
||||||
/** Erkennungsart in Klartext fürs Kundendokument */
|
|
||||||
const ERKENNUNG_TEXT: Record<string, string> = {
|
|
||||||
mac: 'MAC-Adresse',
|
|
||||||
mdns: 'Netzwerkname (mDNS)',
|
|
||||||
netbios: 'Windows-Name',
|
|
||||||
hostname: 'Hostname',
|
|
||||||
'ip+ports': 'IP und Portmuster',
|
|
||||||
};
|
|
||||||
|
|
||||||
export const geraeteDiffTool: Tool = {
|
|
||||||
id: 'geraetediff',
|
|
||||||
category: 'netzwerk',
|
|
||||||
name: 'Geräte-Vergleich',
|
|
||||||
icon: 'git-compare',
|
|
||||||
description: 'Vergleicht die Geräteliste mit dem letzten Protokoll dieses Kunden.',
|
|
||||||
scope: 'protocol',
|
|
||||||
quickRun: true,
|
|
||||||
params: [],
|
|
||||||
async run(ctx) {
|
|
||||||
const socId = ctx.protocol.socId;
|
|
||||||
if (!socId) {
|
|
||||||
return {
|
|
||||||
label: 'Kein Kunde am Protokoll — kein Vergleich möglich',
|
|
||||||
measureStatus: 3,
|
|
||||||
result: {
|
|
||||||
hinweis:
|
|
||||||
'Das Protokoll ist keinem Kunden zugeordnet. Der Vergleich braucht die ' +
|
|
||||||
'früheren Protokolle desselben Kunden.',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Frühere Protokolle des Kunden holen. Das aktuelle selbst fliegt raus —
|
|
||||||
// sonst verglichen wir es mit sich selbst und meldeten stets „keine
|
|
||||||
// Veränderung".
|
|
||||||
const { protocols } = await getCustomer(socId);
|
|
||||||
const frühere = (protocols ?? [])
|
|
||||||
.filter((p) => p.clientUuid !== ctx.protocol.clientUuid && p.id !== ctx.protocol.serverId)
|
|
||||||
// Nur Protokolle mit Geräten taugen als Vergleich; ein leerer Entwurf
|
|
||||||
// vom selben Tag würde sonst „alle Geräte neu" ergeben.
|
|
||||||
.filter((p) => (p.deviceCount ?? 0) > 0)
|
|
||||||
.sort((a, b) => (b.dateDiag ?? 0) - (a.dateDiag ?? 0));
|
|
||||||
|
|
||||||
const vorgänger = frühere[0];
|
|
||||||
if (!vorgänger) {
|
|
||||||
const urteil = bewerteDiff(vergleicheGeraete([], []), 0);
|
|
||||||
return {
|
|
||||||
label: urteil.label,
|
|
||||||
measureStatus: urteil.status,
|
|
||||||
result: { hinweis: urteil.hinweis },
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const alt = await getProtocol(vorgänger.id);
|
|
||||||
const vorher: DiffGeraet[] = (alt.devices ?? []).map((d) => ({
|
|
||||||
ip: d.ip,
|
|
||||||
mac: d.mac,
|
|
||||||
hostname: d.hostname,
|
|
||||||
netbiosName: d.netbiosName,
|
|
||||||
mdnsName: d.mdnsName,
|
|
||||||
openPorts: d.openPorts,
|
|
||||||
deviceType: d.deviceType,
|
|
||||||
}));
|
|
||||||
const jetzt: DiffGeraet[] = ctx.protocol.devices.map((d) => ({
|
|
||||||
ip: d.ip,
|
|
||||||
mac: d.mac,
|
|
||||||
hostname: d.hostname,
|
|
||||||
netbiosName: d.netbiosName,
|
|
||||||
mdnsName: d.mdnsName,
|
|
||||||
openPorts: d.openPorts,
|
|
||||||
deviceType: d.deviceType,
|
|
||||||
}));
|
|
||||||
|
|
||||||
const diff = vergleicheGeraete(vorher, jetzt);
|
|
||||||
const urteil = bewerteDiff(diff, vorher.length);
|
|
||||||
|
|
||||||
const zeile = (e: { name: string; ip: string; erkennung: string; frühereIp?: string }) =>
|
|
||||||
`${e.name === e.ip ? e.ip : `${e.name} (${e.ip})`}` +
|
|
||||||
(e.frühereIp ? ` — vorher ${e.frühereIp}` : '') +
|
|
||||||
` · erkannt über ${ERKENNUNG_TEXT[e.erkennung] ?? e.erkennung}`;
|
|
||||||
|
|
||||||
const vergleichsDatum = vorgänger.dateDiag
|
|
||||||
? new Date(vorgänger.dateDiag * 1000).toLocaleDateString('de-DE')
|
|
||||||
: '';
|
|
||||||
|
|
||||||
return {
|
|
||||||
label: urteil.label,
|
|
||||||
measureStatus: urteil.status,
|
|
||||||
result: {
|
|
||||||
verglichenMit: `${vorgänger.ref ?? 'Protokoll'}${vergleichsDatum ? ' vom ' + vergleichsDatum : ''}`,
|
|
||||||
geraeteVorher: vorher.length,
|
|
||||||
geraeteJetzt: jetzt.length,
|
|
||||||
neu: diff.neu.map(zeile),
|
|
||||||
nichtMehrErreichbar: diff.verschwunden.map(zeile),
|
|
||||||
mitNeuerAdresse: diff.ipGewechselt.map(zeile),
|
|
||||||
andereOffenePorts: diff.portsGeaendert.map(zeile),
|
|
||||||
unveraendert: diff.geblieben.length,
|
|
||||||
hinweis: urteil.hinweis,
|
|
||||||
verfahren:
|
|
||||||
'Wiedererkennung über Netzwerkname, Windows-Name, Hostname oder IP und Portmuster — ' +
|
|
||||||
'nicht über die MAC-Adresse, die Android seit Version 10 nicht mehr herausgibt.',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
@ -6,7 +6,6 @@
|
||||||
import { Capacitor } from '@capacitor/core';
|
import { Capacitor } from '@capacitor/core';
|
||||||
import { sync } from '$lib/sync.svelte';
|
import { sync } from '$lib/sync.svelte';
|
||||||
import { toast } from '$lib/toast.svelte';
|
import { toast } from '$lib/toast.svelte';
|
||||||
import { pruefeServerAdresse } from '$lib/serveradresse';
|
|
||||||
import { APP_VERSION, checkForUpdate, installUpdate, type UpdateInfo } from '$lib/updater';
|
import { APP_VERSION, checkForUpdate, installUpdate, type UpdateInfo } from '$lib/updater';
|
||||||
import { ladeDemoSchalter, setzeDemoSchalter } from '$lib/wifi/demo';
|
import { ladeDemoSchalter, setzeDemoSchalter } from '$lib/wifi/demo';
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
|
|
@ -94,12 +93,12 @@
|
||||||
*/
|
*/
|
||||||
async function saveServer() {
|
async function saveServer() {
|
||||||
const url = serverInput.trim();
|
const url = serverInput.trim();
|
||||||
// Prüfung ausgelagert und einzeln nachrechenbar (tools/pruefe-serveradresse.mjs).
|
if (!url) {
|
||||||
// Sie weist unter anderem http://-Adressen ab, solange die App selbst über
|
toast.show('Server-Adresse fehlt', 'error');
|
||||||
// https läuft — die würden sonst stumm scheitern, siehe serveradresse.ts.
|
return;
|
||||||
const pruefung = pruefeServerAdresse(url, window.location.protocol);
|
}
|
||||||
if (!pruefung.ok) {
|
if (!/^https?:\/\//i.test(url)) {
|
||||||
toast.show(pruefung.meldung, 'error', 8000);
|
toast.show('Adresse muss mit http:// oder https:// beginnen', 'error', 5000);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await setServerUrl(url);
|
await setServerUrl(url);
|
||||||
|
|
|
||||||
|
|
@ -1,113 +0,0 @@
|
||||||
/**
|
|
||||||
* Prueft den Geraete-Vergleich zweier Protokolle.
|
|
||||||
*
|
|
||||||
* ALLE Faelle arbeiten mit Geraeten OHNE MAC - genau so kommen sie beim Kunden
|
|
||||||
* an (/proc/net/arp ist ab Android 10 gesperrt; im ausgelieferten Protokoll
|
|
||||||
* ND2026-0015 waren bei allen 68 Geraeten MAC, Hostname und Hersteller leer).
|
|
||||||
* Der Browser-Mock dagegen gibt jedem Geraet eine MAC - dort saehe auch ein
|
|
||||||
* naiver MAC-Diff perfekt aus und wuerde nichts beweisen.
|
|
||||||
*
|
|
||||||
* Start: node tools/pruefe-geraetediff.mjs
|
|
||||||
*/
|
|
||||||
const { vergleicheGeraete, bewerteDiff, geraeteSchluessel } =
|
|
||||||
await import('../src/lib/tools/netzwerk/geraete-diff.ts');
|
|
||||||
|
|
||||||
// Geraete wie sie real ankommen: keine MAC, oft kein Name.
|
|
||||||
const kamera = { ip: '192.168.1.51', mdnsName: 'IPC-Hof', openPorts: [80, 554] };
|
|
||||||
const drucker = { ip: '192.168.1.20', mdnsName: 'Brother HL-L2350DW', openPorts: [80, 9100, 515] };
|
|
||||||
const pc = { ip: '192.168.1.30', netbiosName: 'BUERO-PC', openPorts: [139, 445] };
|
|
||||||
const nas = { ip: '192.168.1.10', hostname: 'nas.fritz.box', openPorts: [80, 445, 5000] };
|
|
||||||
const namenlos= { ip: '192.168.1.77', openPorts: [502] }; // Wechselrichter, Modbus
|
|
||||||
const router = { ip: '192.168.1.1', hostname: 'fritz.box', openPorts: [80, 443, 53] };
|
|
||||||
|
|
||||||
let fehler = 0;
|
|
||||||
function pruefe(name, ist, soll) {
|
|
||||||
const ok = JSON.stringify(ist) === JSON.stringify(soll);
|
|
||||||
if (!ok) fehler++;
|
|
||||||
console.log(`${ok ? 'OK ' : 'FEHL'} ${name}`);
|
|
||||||
if (!ok) console.log(` ist=${JSON.stringify(ist)} soll=${JSON.stringify(soll)}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1. Identisch -> keine Veraenderung
|
|
||||||
let d = vergleicheGeraete([router, nas, kamera], [router, nas, kamera]);
|
|
||||||
pruefe('Unveraendertes Netz -> 0 neu, 0 weg, 3 geblieben',
|
|
||||||
[d.neu.length, d.verschwunden.length, d.geblieben.length], [0, 0, 3]);
|
|
||||||
|
|
||||||
// 2. DER KERNFALL: dieselben Geraete OHNE MAC, aber mit neuen DHCP-Adressen.
|
|
||||||
// Ein MAC-Diff haette hier "alles neu, alles verschwunden" gemeldet.
|
|
||||||
const kameraNeueIp = { ...kamera, ip: '192.168.1.99' };
|
|
||||||
const druckerNeueIp = { ...drucker, ip: '192.168.1.98' };
|
|
||||||
d = vergleicheGeraete([kamera, drucker], [kameraNeueIp, druckerNeueIp]);
|
|
||||||
pruefe('Beide Geraete mit neuer IP -> 0 neu, 0 weg, 2 mit IP-Wechsel',
|
|
||||||
[d.neu.length, d.verschwunden.length, d.ipGewechselt.length], [0, 0, 2]);
|
|
||||||
pruefe('IP-Wechsel nennt die fruehere Adresse',
|
|
||||||
d.ipGewechselt.map((e) => e.frühereIp).sort(), ['192.168.1.20', '192.168.1.51']);
|
|
||||||
|
|
||||||
// 3. Echtes neues Geraet
|
|
||||||
d = vergleicheGeraete([router, nas], [router, nas, kamera]);
|
|
||||||
pruefe('Ein Geraet dazugekommen', [d.neu.length, d.neu[0]?.name], [1, 'IPC-Hof']);
|
|
||||||
|
|
||||||
// 4. Geraet verschwunden
|
|
||||||
d = vergleicheGeraete([router, nas, kamera], [router, nas]);
|
|
||||||
pruefe('Ein Geraet verschwunden', [d.verschwunden.length, d.verschwunden[0]?.name], [1, 'IPC-Hof']);
|
|
||||||
|
|
||||||
// 5. Namenloses Geraet: gleiche IP + gleiche Ports -> wiedererkannt
|
|
||||||
d = vergleicheGeraete([namenlos], [{ ...namenlos }]);
|
|
||||||
pruefe('Namenlos, IP+Ports gleich -> wiedererkannt', [d.geblieben.length, d.neu.length], [1, 0]);
|
|
||||||
|
|
||||||
// 6. Namenloses Geraet mit neuer IP -> NICHT zuordenbar, ehrlich als neu+weg
|
|
||||||
d = vergleicheGeraete([namenlos], [{ ...namenlos, ip: '192.168.1.78' }]);
|
|
||||||
pruefe('Namenlos mit neuer IP -> gilt als neu UND verschwunden (Grenze des Verfahrens)',
|
|
||||||
[d.neu.length, d.verschwunden.length], [1, 1]);
|
|
||||||
|
|
||||||
// 6b. DER FALL AUS DEM ERSTEN PDF: gleiche IP, anderes Portmuster.
|
|
||||||
// Darf NICHT gleichzeitig als neu und verschwunden erscheinen.
|
|
||||||
d = vergleicheGeraete(
|
|
||||||
[{ ip: '10.0.2.2', openPorts: [80] }],
|
|
||||||
[{ ip: '10.0.2.2', openPorts: [80, 443] }],
|
|
||||||
);
|
|
||||||
pruefe('Gleiche IP, andere Ports -> eigene Kategorie statt "neu UND weg"',
|
|
||||||
[d.neu.length, d.verschwunden.length, d.portsGeaendert.length], [0, 0, 1]);
|
|
||||||
|
|
||||||
// 7. Erkennungsart wird korrekt benannt
|
|
||||||
pruefe('Kaskade: mDNS schlaegt Hostname',
|
|
||||||
geraeteSchluessel({ ip: '1.2.3.4', mdnsName: 'A', hostname: 'B' }).erkennung, 'mdns');
|
|
||||||
pruefe('Kaskade: NetBIOS vor Hostname',
|
|
||||||
geraeteSchluessel({ ip: '1.2.3.4', netbiosName: 'A', hostname: 'B' }).erkennung, 'netbios');
|
|
||||||
pruefe('Kaskade: ohne alles -> ip+ports',
|
|
||||||
geraeteSchluessel({ ip: '1.2.3.4', openPorts: [80] }).erkennung, 'ip+ports');
|
|
||||||
pruefe('Kaskade: MAC schlaegt alles (falls ausnahmsweise da)',
|
|
||||||
geraeteSchluessel({ ip: '1.2.3.4', mac: 'AA:BB:CC:11:22:33', mdnsName: 'A' }).erkennung, 'mac');
|
|
||||||
|
|
||||||
// 8. Schreibweise: Gross/Klein und .local duerfen kein Unterschied sein
|
|
||||||
d = vergleicheGeraete(
|
|
||||||
[{ ip: '10.0.0.5', mdnsName: 'Drucker.local' }],
|
|
||||||
[{ ip: '10.0.0.5', mdnsName: 'drucker' }],
|
|
||||||
);
|
|
||||||
pruefe('mDNS "Drucker.local" == "drucker"', [d.geblieben.length, d.neu.length], [1, 0]);
|
|
||||||
|
|
||||||
// 9. Portmuster-Reihenfolge darf egal sein
|
|
||||||
d = vergleicheGeraete(
|
|
||||||
[{ ip: '10.0.0.9', openPorts: [443, 80] }],
|
|
||||||
[{ ip: '10.0.0.9', openPorts: [80, 443] }],
|
|
||||||
);
|
|
||||||
pruefe('Portreihenfolge egal', [d.geblieben.length, d.neu.length], [1, 0]);
|
|
||||||
|
|
||||||
// 10. Bewertung
|
|
||||||
let b = bewerteDiff(vergleicheGeraete([router, nas], [router, nas]), 2);
|
|
||||||
pruefe('Keine Veraenderung -> GRUEN', b.status, 0);
|
|
||||||
b = bewerteDiff(vergleicheGeraete([router, nas, kamera], [router, nas]), 3);
|
|
||||||
pruefe('Geraet fehlt -> GELB mit Einschraenkung', [b.status, /Standby/.test(b.hinweis || '')], [1, true]);
|
|
||||||
b = bewerteDiff(vergleicheGeraete([], []), 0);
|
|
||||||
pruefe('Kein Vorgaengerprotokoll -> NICHT MESSBAR', b.status, 3);
|
|
||||||
b = bewerteDiff(vergleicheGeraete([namenlos], [namenlos]), 1);
|
|
||||||
pruefe('Nur schwache Zuordnung -> GELB mit Warnung', [b.status, /IP und Portmuster/.test(b.hinweis || '')], [1, true]);
|
|
||||||
b = bewerteDiff(vergleicheGeraete([router, nas], [router, nas, kamera]), 2);
|
|
||||||
pruefe('Nur neue Geraete -> GRUEN (neu ist kein Fehler)', b.status, 0);
|
|
||||||
b = bewerteDiff(vergleicheGeraete(
|
|
||||||
[{ ip: '10.0.2.2', openPorts: [80] }],
|
|
||||||
[{ ip: '10.0.2.2', openPorts: [80, 443] }]), 1);
|
|
||||||
pruefe('Portwechsel wird im Label genannt', /andere.*Ports/.test(b.label), true);
|
|
||||||
|
|
||||||
console.log(fehler === 0 ? 'ALLE FAELLE KORREKT' : `${fehler} FEHLER`);
|
|
||||||
process.exit(fehler === 0 ? 0 : 1);
|
|
||||||
|
|
@ -1,39 +0,0 @@
|
||||||
/**
|
|
||||||
* Prueft die Validierung der Server-Adresse.
|
|
||||||
*
|
|
||||||
* Kern: eine http://-Adresse wird abgewiesen, solange die App selbst ueber
|
|
||||||
* https laeuft - sonst scheitert sie beim Kunden stumm mit "Keine Verbindung
|
|
||||||
* zum Server". Ein Debug-Build auf http://localhost darf HTTP dagegen sehr
|
|
||||||
* wohl, sonst waere ausgerechnet der Testbau unbenutzbar.
|
|
||||||
*
|
|
||||||
* Start: node tools/pruefe-serveradresse.mjs
|
|
||||||
*/
|
|
||||||
const { pruefeServerAdresse } = await import('../src/lib/serveradresse.ts');
|
|
||||||
|
|
||||||
const HTTPS = 'https:';
|
|
||||||
const HTTP = 'http:';
|
|
||||||
|
|
||||||
const faelle = [
|
|
||||||
{ e: 'https://awl.data-it-solution.de', p: HTTPS, ok: true, name: 'Prod-Adresse ueber https' },
|
|
||||||
{ e: 'https://10.0.2.2:8443', p: HTTPS, ok: true, name: 'IP mit Port ueber https' },
|
|
||||||
{ e: 'http://10.0.2.2:8080', p: HTTPS, ok: false, name: 'HTTP bei https-App -> ABGEWIESEN (der eigentliche Fall)' },
|
|
||||||
{ e: 'http://10.0.2.2:8080', p: HTTP, ok: true, name: 'HTTP bei http-App (Debug-Build) -> ERLAUBT' },
|
|
||||||
{ e: 'HTTP://10.0.2.2:8080', p: HTTPS, ok: false, name: 'Grossschreibung wird auch erkannt' },
|
|
||||||
{ e: ' https://awl.example ', p: HTTPS, ok: true, name: 'Leerzeichen aussen stoeren nicht' },
|
|
||||||
{ e: '', p: HTTPS, ok: false, name: 'Leer' },
|
|
||||||
{ e: ' ', p: HTTPS, ok: false, name: 'Nur Leerzeichen' },
|
|
||||||
{ e: 'awl.data-it-solution.de', p: HTTPS, ok: false, name: 'Ohne Schema' },
|
|
||||||
{ e: 'ftp://server', p: HTTPS, ok: false, name: 'Falsches Schema' },
|
|
||||||
{ e: 'https://', p: HTTPS, ok: false, name: 'Schema ohne Host' },
|
|
||||||
];
|
|
||||||
|
|
||||||
let fehler = 0;
|
|
||||||
for (const f of faelle) {
|
|
||||||
const r = pruefeServerAdresse(f.e, f.p);
|
|
||||||
const okk = r.ok === f.ok;
|
|
||||||
if (!okk) fehler++;
|
|
||||||
console.log(`${okk ? 'OK ' : 'FEHL'} ${f.name}`);
|
|
||||||
if (!r.ok) console.log(` -> ${r.meldung.slice(0, 90)}`);
|
|
||||||
}
|
|
||||||
console.log(fehler === 0 ? 'ALLE FAELLE KORREKT' : `${fehler} FEHLER`);
|
|
||||||
process.exit(fehler === 0 ? 0 : 1);
|
|
||||||
Loading…
Reference in a new issue