"use client"; import { useState, useEffect } from "react"; import { Button } from "./ui/button"; import { Lock, CheckCircle, AlertCircle } from "lucide-react"; import { useRegisterModal } from "./modal/ModalStackProvider"; import { api } from "~/trpc/react"; import type { Storage } from "~/server/services/storageService"; interface PBSCredentialsModalProps { isOpen: boolean; onClose: () => void; serverId: number; serverName: string; storage: Storage; } export function PBSCredentialsModal({ isOpen, onClose, serverId, serverName: _serverName, storage, }: PBSCredentialsModalProps) { const [pbsIp, setPbsIp] = useState(""); const [pbsDatastore, setPbsDatastore] = useState(""); const [pbsPassword, setPbsPassword] = useState(""); const [pbsFingerprint, setPbsFingerprint] = useState(""); const [isLoading, setIsLoading] = useState(false); // Extract PBS info from storage object const pbsIpFromStorage = (storage as { server?: string }).server ?? null; const pbsDatastoreFromStorage = (storage as { datastore?: string }).datastore ?? null; // Fetch existing credentials const { data: credentialData, refetch } = api.pbsCredentials.getCredentialsForStorage.useQuery( { serverId, storageName: storage.name }, { enabled: isOpen }, ); // Initialize form with storage config values or existing credentials useEffect(() => { if (isOpen) { if (credentialData?.success && credentialData.credential) { // Load existing credentials setPbsIp(String(credentialData.credential.pbs_ip)); setPbsDatastore(String(credentialData.credential.pbs_datastore)); setPbsPassword(""); // Don't show password setPbsFingerprint( String(credentialData.credential.pbs_fingerprint ?? ""), ); } else { // Initialize with storage config values setPbsIp(pbsIpFromStorage ?? ""); setPbsDatastore(pbsDatastoreFromStorage ?? ""); setPbsPassword(""); setPbsFingerprint(""); } } }, [isOpen, credentialData, pbsIpFromStorage, pbsDatastoreFromStorage]); const saveCredentials = api.pbsCredentials.saveCredentials.useMutation({ onSuccess: () => { void refetch(); onClose(); }, onError: (error) => { console.error("Failed to save PBS credentials:", error); alert(`Failed to save credentials: ${error.message}`); }, }); const deleteCredentials = api.pbsCredentials.deleteCredentials.useMutation({ onSuccess: () => { void refetch(); onClose(); }, onError: (error) => { console.error("Failed to delete PBS credentials:", error); alert(`Failed to delete credentials: ${error.message}`); }, }); useRegisterModal(isOpen, { id: "pbs-credentials-modal", allowEscape: true, onClose, }); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!pbsIp || !pbsDatastore || !pbsFingerprint) { alert("Please fill in all required fields (IP, Datastore, Fingerprint)"); return; } // Password is optional when updating existing credentials setIsLoading(true); try { await saveCredentials.mutateAsync({ serverId, storageName: storage.name, pbs_ip: pbsIp, pbs_datastore: pbsDatastore, pbs_password: pbsPassword || undefined, // Undefined means keep existing password pbs_fingerprint: pbsFingerprint, }); } finally { setIsLoading(false); } }; const handleDelete = async () => { if ( !confirm( "Are you sure you want to delete the PBS credentials for this storage?", ) ) { return; } setIsLoading(true); try { await deleteCredentials.mutateAsync({ serverId, storageName: storage.name, }); } finally { setIsLoading(false); } }; if (!isOpen) return null; const hasCredentials = credentialData?.success && credentialData.credential; return (
{/* Header */}

PBS Credentials - {storage.name}

{/* Content */}
{/* Storage Name (read-only) */}
{/* PBS IP */}
setPbsIp(e.target.value)} required disabled={isLoading} className="bg-card text-foreground placeholder-muted-foreground focus:ring-ring focus:border-ring border-border w-full rounded-md border px-3 py-2 shadow-sm focus:ring-2 focus:outline-none" placeholder="e.g., 10.10.10.226" />

IP address of the Proxmox Backup Server

{/* PBS Datastore */}
setPbsDatastore(e.target.value)} required disabled={isLoading} className="bg-card text-foreground placeholder-muted-foreground focus:ring-ring focus:border-ring border-border w-full rounded-md border px-3 py-2 shadow-sm focus:ring-2 focus:outline-none" placeholder="e.g., NAS03-ISCSI-BACKUP" />

Name of the datastore on the PBS server

{/* PBS Password */}
setPbsPassword(e.target.value)} required={!hasCredentials} disabled={isLoading} className="bg-card text-foreground placeholder-muted-foreground focus:ring-ring focus:border-ring border-border w-full rounded-md border px-3 py-2 shadow-sm focus:ring-2 focus:outline-none" placeholder={ hasCredentials ? "Enter new password (leave empty to keep existing)" : "Enter PBS password" } />

Password for root@pam user on PBS server

{/* PBS Fingerprint */}
setPbsFingerprint(e.target.value)} disabled={isLoading} className="bg-card text-foreground placeholder-muted-foreground focus:ring-ring focus:border-ring border-border w-full rounded-md border px-3 py-2 shadow-sm focus:ring-2 focus:outline-none" placeholder="e.g., 7b:e5:87:38:5e:16:05:d1:12:22:7f:73:d2:e2:d0:cf:8c:cb:28:e2:74:0c:78:91:1a:71:74:2e:79:20:5a:02" />

Leave empty if PBS uses a trusted CA (e.g. Let's Encrypt). For self-signed certificates, enter the server fingerprint from the PBS dashboard ("Show Fingerprint").

{/* Status indicator */} {hasCredentials && (
Credentials are configured for this storage
)} {/* Action Buttons */}
{hasCredentials && ( )}
); }