All checks were successful
Build APK / build-apk (push) Successful in 1m49s
IP-Test: USB-RJ45-Adapter in Netzwerkdose stecken und sofort IP-Adresse, DHCP-Server, Gateway und Link-Geschwindigkeit (10/100/1000 Mbit) ablesen. Auto-Refresh alle 2 s, Speichern mit optionalem Raum/Dose-Name ins Protokoll. WLAN-Empfangstracker: Netz auswählen und beim Durchgehen live RSSI verfolgen. Hybrid-Modus: 500 ms Polling bei verbundenem Netz (kein Scan-Throttling), ~30 s Scan-Sweep bei Fremd-BSSID. Sessions mit Samples, Min/Max/Avg und Sparkline-Verlauf werden im Protokoll gespeichert. Ersetzt DHCP-Info-Tool und WLAN-Scan-Tool (eigene Routen /iptest/ + /wifi/). Kotlin-Plugin: linkInfo(), startWifiScan(), startWifiTrack/stop/status(). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
120 lines
4.1 KiB
TypeScript
120 lines
4.1 KiB
TypeScript
/**
|
|
* Lokaler Offline-Speicher für Diagnose-Protokolle.
|
|
*
|
|
* Ein Protokoll ist ein in sich geschlossenes JSON-Objekt (inkl. Geräte und
|
|
* Messungen) und wird als JSON-Blob abgelegt:
|
|
* - Android: SQLite (@capacitor-community/sqlite)
|
|
* - Browser-Dev: localStorage
|
|
*
|
|
* So bleiben die Daten auf der Baustelle auch ohne Verbindung erhalten und
|
|
* werden später vom Sync-Dienst zum Dolibarr-Server geschoben.
|
|
*/
|
|
|
|
import { Capacitor } from '@capacitor/core';
|
|
import { CapacitorSQLite, SQLiteConnection, type SQLiteDBConnection } from '@capacitor-community/sqlite';
|
|
import type { Protocol } from './types';
|
|
|
|
const DB_NAME = 'netdiag';
|
|
const LS_PREFIX = 'netdiag.protocol.';
|
|
|
|
/**
|
|
* Fehlende optionale Felder eines geladenen Protokolls defensiv ergänzen.
|
|
* Ältere Protokolle (vor den Geräte-Features) haben weder `savedScans` noch
|
|
* `monitorSessions` — ohne diese Normalisierung liefen Komponenten auf
|
|
* `undefined.map`. Mutiert das Objekt und gibt es zurück.
|
|
*/
|
|
function normalizeProtocol(p: Protocol): Protocol {
|
|
p.devices ??= [];
|
|
p.measurements ??= [];
|
|
p.savedScans ??= [];
|
|
p.monitorSessions ??= [];
|
|
p.wifiTrackSessions ??= [];
|
|
return p;
|
|
}
|
|
|
|
let useSqlite = false;
|
|
let db: SQLiteDBConnection | null = null;
|
|
|
|
/** Speicher initialisieren (Tabelle anlegen) */
|
|
export async function initDb(): Promise<void> {
|
|
useSqlite = Capacitor.isNativePlatform();
|
|
if (!useSqlite) return;
|
|
|
|
const sqlite = new SQLiteConnection(CapacitorSQLite);
|
|
// Nach einem WebView-Reload lebt die native SQLite-Verbindung weiter —
|
|
// createConnection wuerfe dann "Connection netdiag already exists" und der
|
|
// App-Start haengt ewig auf "NetDiag startet…". Vorhandene wiederverwenden.
|
|
const exists = (await sqlite.isConnection(DB_NAME, false)).result;
|
|
db = exists
|
|
? await sqlite.retrieveConnection(DB_NAME, false)
|
|
: await sqlite.createConnection(DB_NAME, false, 'no-encryption', 1, false);
|
|
|
|
if (!(await db.isDBOpen()).result) {
|
|
await db.open();
|
|
}
|
|
await db.execute(`
|
|
CREATE TABLE IF NOT EXISTS protocols (
|
|
uuid TEXT PRIMARY KEY,
|
|
json TEXT NOT NULL,
|
|
dirty INTEGER NOT NULL DEFAULT 1,
|
|
updated_at INTEGER NOT NULL
|
|
);
|
|
`);
|
|
}
|
|
|
|
/** Protokoll speichern (anlegen oder ersetzen) */
|
|
export async function saveProtocol(p: Protocol): Promise<void> {
|
|
p.updatedAt = Date.now();
|
|
const json = JSON.stringify(p);
|
|
if (useSqlite && db) {
|
|
await db.run(
|
|
'INSERT OR REPLACE INTO protocols (uuid, json, dirty, updated_at) VALUES (?, ?, ?, ?)',
|
|
[p.clientUuid, json, p.dirty ? 1 : 0, p.updatedAt],
|
|
);
|
|
} else {
|
|
localStorage.setItem(LS_PREFIX + p.clientUuid, json);
|
|
}
|
|
}
|
|
|
|
/** Einzelnes Protokoll laden */
|
|
export async function getProtocol(uuid: string): Promise<Protocol | null> {
|
|
if (useSqlite && db) {
|
|
const res = await db.query('SELECT json FROM protocols WHERE uuid = ?', [uuid]);
|
|
const row = res.values?.[0];
|
|
return row ? normalizeProtocol(JSON.parse(row.json) as Protocol) : null;
|
|
}
|
|
const raw = localStorage.getItem(LS_PREFIX + uuid);
|
|
return raw ? normalizeProtocol(JSON.parse(raw) as Protocol) : null;
|
|
}
|
|
|
|
/** Alle Protokolle laden (neueste zuerst) */
|
|
export async function getAllProtocols(): Promise<Protocol[]> {
|
|
let list: Protocol[] = [];
|
|
if (useSqlite && db) {
|
|
const res = await db.query('SELECT json FROM protocols ORDER BY updated_at DESC');
|
|
list = (res.values ?? []).map((r) => normalizeProtocol(JSON.parse(r.json) as Protocol));
|
|
} else {
|
|
for (let i = 0; i < localStorage.length; i++) {
|
|
const key = localStorage.key(i);
|
|
if (key?.startsWith(LS_PREFIX)) {
|
|
list.push(normalizeProtocol(JSON.parse(localStorage.getItem(key)!) as Protocol));
|
|
}
|
|
}
|
|
list.sort((a, b) => b.updatedAt - a.updatedAt);
|
|
}
|
|
return list;
|
|
}
|
|
|
|
/** Alle noch nicht synchronisierten Protokolle */
|
|
export async function getDirtyProtocols(): Promise<Protocol[]> {
|
|
return (await getAllProtocols()).filter((p) => p.dirty);
|
|
}
|
|
|
|
/** Protokoll löschen */
|
|
export async function deleteProtocol(uuid: string): Promise<void> {
|
|
if (useSqlite && db) {
|
|
await db.run('DELETE FROM protocols WHERE uuid = ?', [uuid]);
|
|
} else {
|
|
localStorage.removeItem(LS_PREFIX + uuid);
|
|
}
|
|
}
|