From f88825ebe0d5dbeefee1e5b83e8200babd3a9af3 Mon Sep 17 00:00:00 2001 From: karim Date: Sun, 31 May 2026 15:03:53 +0200 Subject: [PATCH] feat(server-mode): gehostete Web-GUI fest an einen Server binden MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VITE_SERVER_MODE=1 (vom Dockerfile.app gesetzt) → die App ist die Web-GUI GENAU DIESES Servers: keine Lokal/Server-Wahl (BackendChoice), kein Server-Adress-Wechsel, kein Verbinden auf andere Instanzen. Nur Login auf diesem Server. Tauri (lokale DMG) setzt die Flag NIE → behält volle Wahl (Lokal / beliebige Server-IP). - adapter.js: isServerMode-Export; im Server-Modus fest SupabaseAdapter mit Build-URL/Key, localStorage-Werte erzwungen (kein User-Override) - App.jsx: BackendChoice im Server-Modus überspringen - Login.jsx: Verbindungs-Switch + Server-Adressfeld im Server-Modus ausblenden Beide Builds verifiziert: Server-Build brennt URL ein, Normal-Build nicht. Co-Authored-By: Claude Opus 4.8 --- src/App.jsx | 6 ++++-- src/storage/adapter.js | 27 ++++++++++++++++++++++++++- src/views/Login.jsx | 13 ++++++++++--- 3 files changed, 40 insertions(+), 6 deletions(-) diff --git a/src/App.jsx b/src/App.jsx index 940c819..3ffb752 100755 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,7 +1,7 @@ import React, { useState, useEffect, useCallback, useRef, Suspense, lazy } from "react"; import { NAV_ITEMS, defaultData } from "./constants.js"; import { verifyPassword, withHashedPassword, stripCredentials } from "./utils.js"; -import { storage, isCloudBackend } from "./storage/adapter.js"; +import { storage, isCloudBackend, isServerMode } from "./storage/adapter.js"; import { applyMigrations } from "./storage/migrations.js"; import Login from "./views/Login.jsx"; import Setup from "./views/Setup.jsx"; @@ -464,8 +464,10 @@ export default function App() { // lokalen Daten gibt. Sobald er gewählt hat, übernimmt der jeweilige Wizard. // UpdateNotifier wird in allen Pre-Login-Screens mitgerendert, damit ein // hängender Setup-Wizard sich via Auto-Update selbst befreien kann. + // Server-Modus (gehostete Web-GUI): nie die Lokal/Server-Wahl zeigen — die + // App ist fest an diesen Server gebunden. Nur die lokale DMG zeigt die Wahl. const hasChosenBackend = localStorage.getItem("rapport_backend_chosen") === "1"; - if (!hasChosenBackend && isNewInstall && !data.settings.setupCompleted && !currentUser) { + if (!isServerMode && !hasChosenBackend && isNewInstall && !data.settings.setupCompleted && !currentUser) { return <>; } diff --git a/src/storage/adapter.js b/src/storage/adapter.js index 52e93a7..1ccbdb9 100644 --- a/src/storage/adapter.js +++ b/src/storage/adapter.js @@ -45,8 +45,17 @@ export class LocalStorageAdapter { } } +// SERVER-MODUS: dieser Build ist die gehostete Web-GUI eines bestimmten +// Servers (gesetzt via VITE_SERVER_MODE=1 im Dockerfile.app). Dann ist die App +// FEST an diesen einen Server gebunden — keine Lokal/Server-Wahl, kein Wechsel +// der Server-Adresse, kein Verbinden auf andere Instanzen. Nur der Login auf +// genau diesem Server. Die lokale DMG (Tauri) setzt diese Flag NIE und behält +// die volle Wahl (Lokal / beliebige Server-IP). +const _isTauri = typeof window !== "undefined" && !!window.__TAURI_INTERNALS__; +export const isServerMode = import.meta.env.VITE_SERVER_MODE === "1" && !_isTauri; + function createAdapter() { - const isTauri = typeof window !== "undefined" && !!window.__TAURI_INTERNALS__; + const isTauri = _isTauri; // 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 @@ -54,6 +63,22 @@ function createAdapter() { const envUrl = isTauri ? null : import.meta.env.VITE_SUPABASE_URL; const envKey = import.meta.env.VITE_SUPABASE_ANON_KEY; + // ── Server-Modus: fest auf diesen Server, ohne Wahlmöglichkeit ─────────── + if (isServerMode) { + if (!envUrl || !envKey) { + console.error("VITE_SERVER_MODE=1, aber VITE_SUPABASE_URL/ANON_KEY fehlen — Fehlkonfiguration des Web-Builds."); + return new LocalStorageAdapter(); + } + // localStorage konsistent halten (Login liest cloud_url für listStudios), + // aber der User kann nichts davon ändern — die Werte kommen aus dem Build. + if (typeof localStorage !== "undefined") { + localStorage.setItem("rapport_backend", "cloud"); + localStorage.setItem("rapport_backend_chosen", "1"); + localStorage.setItem("rapport_cloud_url", envUrl.replace(/\/+$/, "")); + } + return new SupabaseAdapter(envUrl, envKey); + } + 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 diff --git a/src/views/Login.jsx b/src/views/Login.jsx index fec49fb..3ae131b 100755 --- a/src/views/Login.jsx +++ b/src/views/Login.jsx @@ -1,5 +1,5 @@ import React, { useEffect, useRef, useState } from "react"; -import { storage, isCloudBackend } from "../storage/adapter.js"; +import { storage, isCloudBackend, isServerMode } from "../storage/adapter.js"; const isValidEmail = (s) => /.+@.+\..+/.test(s); @@ -121,8 +121,10 @@ export default function Login({ verifyLogin, settings, version, cloudUnreachable const userPlaceholder = isCloud ? "name@studio.ch" : "admin"; const userInputType = isCloud ? "email" : "text"; - const showUrlField = isCloud && editUrl; - const showUrlBadge = isCloud && !editUrl && cloudUrl; + // Im Server-Modus (gehostete Web-GUI) gibt es keine Server-Adress-Eingabe — + // die App ist fest an diesen Server gebunden. + const showUrlField = isCloud && editUrl && !isServerMode; + const showUrlBadge = isCloud && !editUrl && cloudUrl && !isServerMode; // Hostname zur Anzeige (ohne Protokoll, ohne Port falls Standard) let urlDisplay = cloudUrl; @@ -381,6 +383,10 @@ export default function Login({ verifyLogin, settings, version, cloudUnreachable )} {/* Verbindung-Switch + Server-Anzeige (dezent darunter) */} + {/* Verbindungs-Switch (Lokal/Server + Server-Adresse) nur in der lokalen + DMG. Die gehostete Web-GUI (Server-Modus) ist fest an diesen Server + gebunden — keine Wahl, kein Wechsel. */} + {!isServerMode && (
)}
+ )}
{version ? `V${version}` : ""}