import React, { useEffect, useState } from "react"; import ReactDOM from "react-dom/client"; import { createClient, type Session } from "@supabase/supabase-js"; import type { VM } from "./api"; import { fetchMe } from "./api"; import "./styles.css"; import { AuthScreen } from "./components/AuthScreen"; import { VMList } from "./components/VMList"; import { VMCreate } from "./components/VMCreate"; import { SSHKeys } from "./components/SSHKeys"; import { AuditLog } from "./components/AuditLog"; import { ConsoleView } from "./components/ConsoleView"; type ViewKey = "overview" | "vms" | "ssh" | "audit" | "console"; const supabaseUrl = import.meta.env.VITE_SUPABASE_URL ?? ""; const supabaseAnonKey = import.meta.env.VITE_SUPABASE_ANON_KEY ?? ""; const supabase = supabaseUrl && supabaseAnonKey ? createClient(supabaseUrl, supabaseAnonKey) : undefined; const navItems: Array<{ key: ViewKey; label: string }> = [ { key: "overview", label: "Uebersicht" }, { key: "vms", label: "VMs" }, { key: "ssh", label: "SSH-Keys" }, { key: "audit", label: "Audit" }, { key: "console", label: "Konsole" }, ]; function App() { const [session, setSession] = useState(null); const [loadingSession, setLoadingSession] = useState(true); const [activeView, setActiveView] = useState("overview"); const [backendPrincipal, setBackendPrincipal] = useState<{ Subject: string; Email: string; Role: string; } | null>(null); const [backendStatus, setBackendStatus] = useState< "idle" | "loading" | "ok" | "error" >("idle"); const [backendError, setBackendError] = useState(""); useEffect(() => { if (!supabase) { setLoadingSession(false); return; } supabase.auth.getSession().then(({ data }) => { setSession(data.session); setLoadingSession(false); }); const { data } = supabase.auth.onAuthStateChange((_event, nextSession) => { setSession(nextSession); setBackendPrincipal(null); setBackendStatus("idle"); setBackendError(""); }); return () => data.subscription.unsubscribe(); }, []); useEffect(() => { if (session) { refreshBackendPrincipal(); } }, [session]); async function refreshBackendPrincipal() { if (!session) return; setBackendStatus("loading"); setBackendError(""); try { const principal = await fetchMe(session.access_token); setBackendPrincipal(principal); setBackendStatus("ok"); } catch (error) { setBackendStatus("error"); setBackendError( error instanceof Error ? error.message : "Unbekannter Fehler" ); } } const connected = Boolean(session && backendPrincipal); if (loadingSession) { return (
); } if (!supabase) { return (

Konfiguration

Frontend Env fehlt

`VITE_SUPABASE_URL` und `VITE_SUPABASE_ANON_KEY` mussen gesetzt sein.

); } if (!session) { return ; } return (

ProxUI

Proxmox Multi-Tenant Console

{session.user.email} {connected ? "Backend verbunden" : "Session aktiv"}

Backend

Status

{backendError ? (

{backendError}

) : null}
); } function ViewPanel({ activeView, connected, token, }: { activeView: ViewKey; connected: boolean; token: string; }) { const projectID = "00000000-0000-0000-0000-000000000000"; return (
{activeView === "overview" && } {activeView === "vms" && } {activeView === "ssh" && } {activeView === "audit" && } {activeView === "console" && }
); } function Overview({ connected }: { connected: boolean }) { return ( <>

Status

Arbeitsbereich

{connected ? "Verbunden" : "Warte"}
Backend {connected ? "Online" : "Offline"}
RBAC Aktiv
Worker Aktiv
Audit Aktiv
); } function VMView({ token, projectID }: { token: string; projectID: string }) { const [refreshKey, setRefreshKey] = useState(0); return ( <>

VMs

Virtuelle Maschinen

setRefreshKey((k) => k + 1)} /> ); } function Metric({ label, value }: { label: string; value: string }) { return (
{label} {value}
); } function statusLabel(status: "idle" | "loading" | "ok" | "error") { if (status === "loading") return "Pruefung laeuft"; if (status === "ok") return "Verbunden"; if (status === "error") return "Fehler"; return "Nicht geprueft"; } ReactDOM.createRoot(document.getElementById("root")!).render( );