import { useEffect, useState } from 'react'; interface Mount { id: string; name: string; type: 'nfs' | 'smb' | 'rclone'; enabled: boolean; desiredMounted: boolean; mountPath: string; mounted: boolean; error?: string; lastChecked: number; } interface RcloneRemote { name: string; type: string; } const cardStyle: React.CSSProperties = { background: '#16213e', borderRadius: '8px', padding: '1.5rem', marginBottom: '1rem', }; const buttonStyle: React.CSSProperties = { padding: '0.5rem 1rem', background: '#0f3460', color: '#fff', border: 'none', borderRadius: '4px', cursor: 'pointer', marginRight: '0.5rem', }; const dangerButtonStyle: React.CSSProperties = { ...buttonStyle, background: '#7f1d1d', }; export default function Mounts() { const [mounts, setMounts] = useState([]); const [remotes, setRemotes] = useState([]); const [loading, setLoading] = useState(true); const [showForm, setShowForm] = useState(false); const [formData, setFormData] = useState({ name: '', type: 'nfs' as 'nfs' | 'smb' | 'rclone', nfsServer: '', nfsPath: '', smbServer: '', smbShare: '', smbUsername: '', smbPassword: '', rcloneRemote: '', rclonePath: '', }); const fetchMounts = async () => { try { const res = await fetch('/api/mounts'); if (res.ok) { const data = await res.json(); setMounts(data.mounts || []); } } catch (err) { console.error('Failed to fetch mounts:', err); } finally { setLoading(false); } }; const fetchRemotes = async () => { try { const res = await fetch('/api/mounts/rclone/remotes'); if (res.ok) { const data = await res.json(); setRemotes(data.remotes || []); } } catch (err) { console.error('Failed to fetch remotes:', err); } }; useEffect(() => { fetchMounts(); fetchRemotes(); const interval = setInterval(fetchMounts, 5000); return () => clearInterval(interval); }, []); const handleMount = async (id: string) => { await fetch(`/api/mounts/${id}/mount`, { method: 'POST' }); fetchMounts(); }; const handleUnmount = async (id: string) => { await fetch(`/api/mounts/${id}/unmount`, { method: 'POST' }); fetchMounts(); }; const handleDelete = async (id: string) => { if (!confirm('Are you sure you want to delete this mount?')) return; await fetch(`/api/mounts/${id}`, { method: 'DELETE' }); fetchMounts(); }; const handleCreate = async (e: React.FormEvent) => { e.preventDefault(); const body: Record = { name: formData.name, type: formData.type, }; if (formData.type === 'nfs') { body.nfsServer = formData.nfsServer; body.nfsPath = formData.nfsPath; } else if (formData.type === 'smb') { body.smbServer = formData.smbServer; body.smbShare = formData.smbShare; if (formData.smbUsername) body.smbUsername = formData.smbUsername; if (formData.smbPassword) body.smbPassword = formData.smbPassword; } else if (formData.type === 'rclone') { body.rcloneRemote = formData.rcloneRemote; body.rclonePath = formData.rclonePath; } await fetch('/api/mounts', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body), }); setShowForm(false); setFormData({ name: '', type: 'nfs', nfsServer: '', nfsPath: '', smbServer: '', smbShare: '', smbUsername: '', smbPassword: '', rcloneRemote: '', rclonePath: '', }); fetchMounts(); }; const inputStyle: React.CSSProperties = { width: '100%', padding: '0.5rem', marginBottom: '0.5rem', background: '#1a1a2e', border: '1px solid #0f3460', borderRadius: '4px', color: '#fff', }; if (loading) return
Loading...
; return (

Mount Management

rclone UI
{showForm && (

Add New Mount

setFormData({ ...formData, name: e.target.value })} required /> {formData.type === 'nfs' && ( <> setFormData({ ...formData, nfsServer: e.target.value })} required /> setFormData({ ...formData, nfsPath: e.target.value })} required /> )} {formData.type === 'smb' && ( <> setFormData({ ...formData, smbServer: e.target.value })} required /> setFormData({ ...formData, smbShare: e.target.value })} required /> setFormData({ ...formData, smbUsername: e.target.value })} /> setFormData({ ...formData, smbPassword: e.target.value })} /> )} {formData.type === 'rclone' && ( <> setFormData({ ...formData, rclonePath: e.target.value })} /> )}
)} {mounts.length === 0 ? (

No mounts configured. Click "Add Mount" to get started.

) : ( mounts.map((mount) => (

{mount.name}

Type: {mount.type} | Path: {mount.mountPath}

Status:{' '} {mount.mounted ? 'Mounted' : 'Not mounted'}

{mount.error && (

Error: {mount.error}

)}
{mount.mounted ? ( ) : ( )}
)) )}
); }