From 839197d4b8c9a102192853e296d8624da8c83157 Mon Sep 17 00:00:00 2001 From: Jannis Braun <151788261+TheZwiss@users.noreply.github.com> Date: Wed, 1 Apr 2026 18:23:27 +0200 Subject: [PATCH] fix(web): add re-authenticate flow, safe URL parsing, and minor UI fixes --- .../components/modals/ConnectedInstances.tsx | 93 +++++++++++++++++-- 1 file changed, 86 insertions(+), 7 deletions(-) diff --git a/packages/web/src/components/modals/ConnectedInstances.tsx b/packages/web/src/components/modals/ConnectedInstances.tsx index 85f68d7d..5cdc98ab 100644 --- a/packages/web/src/components/modals/ConnectedInstances.tsx +++ b/packages/web/src/components/modals/ConnectedInstances.tsx @@ -2,11 +2,16 @@ import React, { useState } from 'react'; import ReactDOM from 'react-dom'; import type { InstanceInfoResponse, FederationRegistryEntry } from '@backspace/shared'; import { useInstanceStore, DifferentPasswordError } from '../../stores/instanceStore'; -import type { ConnectedInstance } from '../../stores/instanceStore'; import { useAuthStore } from '../../stores/authStore'; import { isElectron } from '../../platform/platform'; import { ConfirmDialog } from '../ui/ConfirmDialog'; +// ─── URL helpers ───────────────────────────────────────────────────────────── + +function safeHost(origin: string): string { + try { return new URL(origin).host; } catch { return origin; } +} + // ─── Status indicator ──────────────────────────────────────────────────────── function StatusDot({ status }: { status: string }) { @@ -546,11 +551,16 @@ function RegistryRow({ const disconnectInstance = useInstanceStore((s) => s.disconnectInstance); const reconnectInstance = useInstanceStore((s) => s.reconnectInstance); const forceRemoveEntry = useInstanceStore((s) => s.forceRemoveEntry); + const reauthenticateInstance = useInstanceStore((s) => s.reauthenticateInstance); const [showForceRemoveConfirm, setShowForceRemoveConfirm] = useState(false); const [showDeleteIdentity, setShowDeleteIdentity] = useState(false); + const [showReauth, setShowReauth] = useState(false); + const [reauthPassword, setReauthPassword] = useState(''); + const [reauthLoading, setReauthLoading] = useState(false); + const [reauthError, setReauthError] = useState(''); - const name = entry.label || new URL(entry.origin).host; + const name = entry.label || safeHost(entry.origin); const isDisconnected = entry.status === 'disconnected'; const isConnected = entry.status === 'connected'; const hasIssue = entry.status === 'unreachable' || entry.status === 'auth_expired'; @@ -592,6 +602,21 @@ function RegistryRow({ setShowForceRemoveConfirm(false); }; + const handleReauth = async () => { + if (!reauthPassword) return; + setReauthError(''); + setReauthLoading(true); + try { + await reauthenticateInstance(entry.origin, reauthPassword); + setShowReauth(false); + setReauthPassword(''); + } catch (err) { + setReauthError((err as Error).message); + } finally { + setReauthLoading(false); + } + }; + return ( <>