import { useState, useEffect } from 'react'; interface MountConfig { id: string; name: string; type: 'nfs' | 'smb' | 'rclone'; enabled: boolean; mountPath: string; // Type-specific nfsServer?: string; nfsPath?: string; smbServer?: string; smbShare?: string; smbUsername?: string; smbPassword?: string; smbDomain?: string; rcloneRemote?: string; rclonePath?: string; } interface MountStatus extends MountConfig { mounted: boolean; error?: string; lastChecked: number; } interface RcloneRemote { name: string; type: string; } function Mounts() { const [mounts, setMounts] = useState([]); const [remotes, setRemotes] = useState([]); const [loading, setLoading] = useState(true); const [showAddModal, setShowAddModal] = useState(false); const [newMount, setNewMount] = useState>({ type: 'rclone', enabled: true, }); useEffect(() => { fetchData(); }, []); const fetchData = async () => { try { setLoading(true); const [mountsRes, remotesRes] = await Promise.all([ fetch('/api/mounts'), fetch('/api/mounts/rclone/remotes') ]); if (mountsRes.ok) { const data = await mountsRes.json(); setMounts(data.mounts || []); } if (remotesRes.ok) { const data = await remotesRes.json(); setRemotes(data.remotes || []); } } catch (err) { console.error('Failed to fetch mounts:', err); } finally { setLoading(false); } }; const handleMount = async (id: string) => { try { await fetch(`/api/mounts/${id}/mount`, { method: 'POST' }); fetchData(); } catch (err) { console.error('Failed to mount:', err); } }; const handleUnmount = async (id: string) => { try { await fetch(`/api/mounts/${id}/unmount`, { method: 'POST' }); fetchData(); } catch (err) { console.error('Failed to unmount:', err); } }; const handleDelete = async (id: string) => { if (!confirm('Are you sure you want to remove this mount?')) return; try { await fetch(`/api/mounts/${id}`, { method: 'DELETE' }); fetchData(); } catch (err) { console.error('Failed to delete mount:', err); } }; const handleAddMount = async () => { try { const res = await fetch('/api/mounts', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(newMount), }); if (res.ok) { setShowAddModal(false); setNewMount({ type: 'rclone', enabled: true }); fetchData(); } } catch (err) { console.error('Failed to add mount:', err); } }; const getMountIcon = (type: string) => { switch (type) { case 'nfs': return ( ); case 'smb': return ( ); case 'rclone': return ( ); default: return null; } }; return (

Remote Mounts

rclone Web UI
{remotes.length > 0 && (

Available rclone Remotes

{remotes.map((remote) => (
{remote.name} {remote.type}
))}
)} {loading ? (
Loading mounts...
) : mounts.length === 0 ? (

No mounts configured

Add a mount to connect remote storage like NFS, SMB, or cloud storage via rclone

) : (
{mounts.map((mount) => (
{getMountIcon(mount.type)}

{mount.name}

{mount.type.toUpperCase()}
{mount.mounted ? 'Mounted' : 'Unmounted'}
Mount Path {mount.mountPath}
{mount.type === 'nfs' && (
Server {mount.nfsServer}:{mount.nfsPath}
)} {mount.type === 'smb' && (
Share //{mount.smbServer}/{mount.smbShare}
)} {mount.type === 'rclone' && (
Remote {mount.rcloneRemote}{mount.rclonePath || ''}
)}
{mount.error && (
{mount.error}
)}
{mount.mounted ? ( ) : ( )}
))}
)} {showAddModal && (
setShowAddModal(false)}>
e.stopPropagation()}>

Add Mount

setNewMount({ ...newMount, name: e.target.value })} placeholder="My Remote Storage" />

Mount path will be: /files/{newMount.name ? newMount.name.toLowerCase().replace(/[^a-z0-9-_]/g, '-').slice(0, 64) : '{name}'}

{newMount.type === 'rclone' && ( <>
setNewMount({ ...newMount, rclonePath: e.target.value })} placeholder="/path/to/folder" />
)} {newMount.type === 'nfs' && ( <>
setNewMount({ ...newMount, nfsServer: e.target.value })} placeholder="192.168.1.100" />
setNewMount({ ...newMount, nfsPath: e.target.value })} placeholder="/export/media" />
)} {newMount.type === 'smb' && ( <>
setNewMount({ ...newMount, smbServer: e.target.value })} placeholder="192.168.1.100" />
setNewMount({ ...newMount, smbShare: e.target.value })} placeholder="media" />
setNewMount({ ...newMount, smbUsername: e.target.value })} placeholder="user" />
setNewMount({ ...newMount, smbPassword: e.target.value })} placeholder="********" />
setNewMount({ ...newMount, smbDomain: e.target.value })} placeholder="WORKGROUP" />
)}
)}
); } export default Mounts;