import React, { useState, useEffect } from 'react'; import { Button } from '@/components/ui/index'; import { RefreshCw } from 'lucide-react'; import { RemotesList } from '@/components/Proxmox'; import { AddRemoteForm } from '@/components/Proxmox'; import { EditRemoteForm } from '@/components/Proxmox'; import { RemoveRemoteDialog } from '@/components/Proxmox'; import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/index'; import { listProxmoxClusters, addProxmoxCluster, removeProxmoxCluster } from '@/lib/proxmoxClient'; import { ClusterType } from '@/lib/domain'; import { toast } from 'sonner'; interface RemoteInfo { id: string; name: string; url: string; username: string; type: 'pve' | 'pbs'; status: 'connected' | 'disconnected' | 'error'; } export function ProxmoxRemotesPage() { const [remotes, setRemotes] = useState([]); const [showAddDialog, setShowAddDialog] = useState(false); const [editingRemote, setEditingRemote] = useState(null); const [removingRemote, setRemovingRemote] = useState(null); const loadRemotes = async () => { try { const clusters = await listProxmoxClusters(); // TODO: Implement actual status checking via backend connection test const remotesList: RemoteInfo[] = clusters.map((c) => ({ id: c.id, name: c.name, url: c.url, username: c.username, type: c.clusterType === 've' ? 'pve' : 'pbs', status: 'connected' as const, // Placeholder - actual status requires connection test })); setRemotes(remotesList); } catch (err) { console.error('Failed to load remotes:', err); } }; useEffect(() => { void loadRemotes(); }, []); const generateId = (): string => { return Date.now().toString(36) + Math.random().toString(36).substr(2); }; /** * Helper function to parse a Proxmox URL and extract hostname and port. * Handles URLs with or without explicit port numbers. * * @param url - The full URL (e.g., "https://172.0.0.18:8006" or "https://pve.example.com") * @param type - The cluster type ('pve' or 'pbs') to determine default port * @returns Object with hostname (stripped of protocol and port) and port number */ const parseRemoteUrl = (url: string, type: 'pve' | 'pbs'): { hostname: string; port: number } => { let hostname = url.replace(/^https?:\/\//, ''); let port = type === 'pve' ? 8006 : 8007; const portMatch = hostname.match(/:(\d+)$/); if (portMatch) { port = parseInt(portMatch[1], 10); hostname = hostname.replace(/:\d+$/, ''); } return { hostname, port }; }; // eslint-disable-next-line @typescript-eslint/no-explicit-any const handleAddRemote = async (config: any) => { try { const clusterType = config.type === 'pve' ? 've' : 'pbs'; const { hostname, port } = parseRemoteUrl(config.url, config.type); const id = config.id || generateId(); await addProxmoxCluster( id, config.name, clusterType as ClusterType, { url: hostname, port }, config.username, config.password || '' ); await loadRemotes(); setShowAddDialog(false); } catch (err) { console.error('Failed to add remote:', err); toast.error('Failed to add remote: ' + String(err)); throw err; } }; // eslint-disable-next-line @typescript-eslint/no-explicit-any const handleEditRemote = async (config: any) => { try { const clusterType = config.type === 'pve' ? 've' : 'pbs'; const { hostname, port } = parseRemoteUrl(config.url, config.type); // Edit operation requires remove-then-add since backend doesn't support update. // If add fails after remove, the remote will be lost - this is a known limitation // until backend supports atomic update operations. await removeProxmoxCluster(config.id); await addProxmoxCluster( config.id, config.name, clusterType as ClusterType, { url: hostname, port }, config.username, config.password || '' ); await loadRemotes(); setEditingRemote(null); } catch (err) { console.error('Failed to edit remote:', err); toast.error('Failed to edit remote: ' + String(err)); throw err; } }; const handleRemoveRemote = async () => { if (removingRemote) { try { await removeProxmoxCluster(removingRemote.id); await loadRemotes(); setRemovingRemote(null); } catch (err) { console.error('Failed to remove remote:', err); toast.error('Failed to remove remote: ' + String(err)); } } }; return (

Remotes

Manage Proxmox VE and Backup Server connections

{ void loadRemotes(); }} onEdit={(remote) => { setEditingRemote(remote as RemoteInfo | null); }} onDelete={(remote) => { setRemovingRemote(remote as RemoteInfo | null); }} /> {showAddDialog && ( Add New Remote setShowAddDialog(false)} /> )} {editingRemote !== null && ( setEditingRemote(null)}> Edit Remote setEditingRemote(null)} /> )} {removingRemote !== null && ( setRemovingRemote(null)}> Remove Remote setRemovingRemote(null)} /> )}
); }