Add SSH-managed node registry with connection testing and reauth.

Register hosts under Containers/VMs/Docker with encrypted key storage, and require re-authentication for sensitive account changes.
This commit is contained in:
2026-07-18 16:39:10 +02:00
parent b93b7519ec
commit f4dc8f63d7
31 changed files with 4801 additions and 223 deletions
+96
View File
@@ -0,0 +1,96 @@
import { useState, type FormEvent } from 'react'
import type { ReauthCredentials } from './api/client'
type ReauthModalProps = {
title: string
hint?: string
requireTotp: boolean
confirmLabel?: string
onCancel: () => void
onConfirm: (credentials: ReauthCredentials) => Promise<void>
}
export function ReauthModal({
title,
hint,
requireTotp,
confirmLabel = 'Confirm',
onCancel,
onConfirm,
}: ReauthModalProps) {
const [password, setPassword] = useState('')
const [totpCode, setTotpCode] = useState('')
const [error, setError] = useState<string | null>(null)
const [isSubmitting, setIsSubmitting] = useState(false)
async function handleSubmit(event: FormEvent) {
event.preventDefault()
setError(null)
setIsSubmitting(true)
try {
await onConfirm({
current_password: password,
totp_code: totpCode,
})
} catch (err) {
setError(err instanceof Error ? err.message : 'Re-authentication failed')
setIsSubmitting(false)
}
}
return (
<div className="reauth-backdrop" role="presentation">
<div
className="reauth-modal"
role="dialog"
aria-modal="true"
aria-labelledby="reauth-title"
>
<h2 id="reauth-title">{title}</h2>
{hint ? <p className="config-hint">{hint}</p> : null}
<form className="reauth-form" onSubmit={(event) => void handleSubmit(event)}>
<label htmlFor="reauth-password">Password</label>
<input
id="reauth-password"
type="password"
autoComplete="current-password"
value={password}
onChange={(event) => setPassword(event.target.value)}
required
/>
{requireTotp ? (
<>
<label htmlFor="reauth-totp">Authenticator code</label>
<input
id="reauth-totp"
type="text"
inputMode="numeric"
autoComplete="one-time-code"
value={totpCode}
onChange={(event) => setTotpCode(event.target.value)}
required
/>
</>
) : null}
{error ? (
<p className="groups-error" role="alert">
{error}
</p>
) : null}
<div className="reauth-actions">
<button type="button" className="config-action-revert" onClick={onCancel}>
Cancel
</button>
<button
type="submit"
className="config-action-save"
disabled={isSubmitting || !password}
>
{isSubmitting ? 'Checking…' : confirmLabel}
</button>
</div>
</form>
</div>
</div>
)
}