// Storage-Adapter: Auswahl zwischen LocalStorage und Supabase (Cloud). // // Auswahl-Logik: // localStorage["rapport_backend"] === "cloud" → SupabaseAdapter // alles andere (default) → LocalStorageAdapter // // Umschalten: `localStorage.setItem("rapport_backend", "cloud")` (später UI-Toggle). // Cloud braucht zusätzlich VITE_SUPABASE_URL und VITE_SUPABASE_ANON_KEY in .env.local. // Fallback: wenn Cloud gewählt aber env fehlt, kommt LocalStorage zurück (mit Warning). // // Bewusst NICHT im Adapter: // - UI-State (Dark Mode, Zoom, …) — per-Device, bleibt direkt in localStorage // - Session/Auth — sessionStorage / Supabase-Auth-eigenes Storage // - Migrations — siehe migrations.js, läuft nach dem load auf den Rohdaten import { STORAGE_KEY } from "../constants.js"; import { SupabaseAdapter } from "./supabase-adapter.js"; export class LocalStorageAdapter { async hasExistingData() { return !!localStorage.getItem(STORAGE_KEY); } async load() { try { const stored = localStorage.getItem(STORAGE_KEY); if (!stored) return null; return JSON.parse(stored); } catch { return null; } } async save(data) { try { localStorage.setItem(STORAGE_KEY, JSON.stringify(data)); } catch (e) { console.error("LocalStorage save failed:", e); throw e; } } async clear() { localStorage.removeItem(STORAGE_KEY); } } function createAdapter() { const isTauri = typeof window !== "undefined" && !!window.__TAURI_INTERNALS__; // Build-time-URL nur für Web-Deploy gültig. Tauri-Builds ignorieren den // eingebrannten Wert — Desktop-User geben die Server-URL aktiv ein. // Der Anon-Key bleibt aus dem Build, weil er pro Cloud-Instanz konstant ist // (kein User-Geheimnis, sondern Public-Konfig). const envUrl = isTauri ? null : import.meta.env.VITE_SUPABASE_URL; const envKey = import.meta.env.VITE_SUPABASE_ANON_KEY; if (typeof localStorage !== "undefined") { // ── Auto-Recovery für 0.8.0-Upgrade-Bug ────────────────────────────── // 0.8.0 hat Cloud-Modus ungewollt gesetzt bei Lokal-Usern (siehe 0.8.1 // CHANGELOG). 0.8.1 verhindert das nur prospektiv, behebt aber den // bestehenden Cloud-State nicht. Hier räumen wir nachträglich auf: // Cloud-Modus gesetzt + lokale Daten vorhanden + keine Cloud-Session // → Cloud-State löschen, User landet im BackendChoice oder Lokal-Modus. const RECOVERY_MARKER = "rapport_080_recovery"; const backend = localStorage.getItem("rapport_backend"); const hasLocalData = !!localStorage.getItem(STORAGE_KEY); const hasCloudSession = !!localStorage.getItem("rapport_supabase_session"); if (backend === "cloud" && hasLocalData && !hasCloudSession && !localStorage.getItem(RECOVERY_MARKER)) { console.warn("Auto-Recovery: Cloud-Modus gesetzt, aber Lokal-Daten vorhanden und kein Cloud-Login — räume Cloud-State auf."); localStorage.removeItem("rapport_backend"); localStorage.removeItem("rapport_backend_chosen"); localStorage.removeItem("rapport_cloud_url"); localStorage.setItem(RECOVERY_MARKER, "1"); } // ── Auto-Cloud-Default für Web-Deploy ──────────────────────────────── // Nur wenn keine lokalen Daten existieren und der Browser auf einer // konfigurierten Web-Instanz landet (envUrl aus build). if (import.meta.env.PROD && envUrl && !localStorage.getItem("rapport_backend_chosen") && !hasLocalData) { localStorage.setItem("rapport_backend_chosen", "1"); localStorage.setItem("rapport_backend", "cloud"); if (!localStorage.getItem("rapport_cloud_url")) { localStorage.setItem("rapport_cloud_url", envUrl.replace(/\/+$/, "")); } } } const backend = (typeof localStorage !== "undefined" && localStorage.getItem("rapport_backend")) || "local"; if (backend === "cloud") { // URL kommt bevorzugt aus localStorage (vom User eingegeben). Tauri hat // gar keine env-URL; Web-Build hat sie ggf. als Fallback. const url = (typeof localStorage !== "undefined" && localStorage.getItem("rapport_cloud_url")) || envUrl; if (!url || !envKey) { console.warn("rapport_backend=cloud, aber URL oder ANON_KEY fehlen — Fallback auf LocalStorage."); return new LocalStorageAdapter(); } console.info("Storage-Adapter: SupabaseAdapter aktiv (URL:", url + ")"); return new SupabaseAdapter(url, envKey); } return new LocalStorageAdapter(); } // Singleton — wird beim Modul-Load gewählt. // Switch zur Laufzeit erfordert (vorerst) einen App-Reload. export const storage = createAdapter(); export const isCloudBackend = storage instanceof SupabaseAdapter; // Für Dev-Tests im Browser: window.__rapport.storage und ein Helper, um die // Cloud-Verbindung ohne UI zu testen. if (typeof window !== "undefined") { window.__rapport = window.__rapport || {}; window.__rapport.storage = storage; window.__rapport.useCloud = () => { localStorage.setItem("rapport_backend", "cloud"); location.reload(); }; window.__rapport.useLocal = () => { localStorage.setItem("rapport_backend", "local"); location.reload(); }; }