You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
125 lines
4.2 KiB
125 lines
4.2 KiB
'use client';
|
|
|
|
import { useState, useEffect } from 'react';
|
|
import { useNotification } from '@/app/context/NotificationContext';
|
|
|
|
export default function TFTPPage() {
|
|
const [files, setFiles] = useState([]);
|
|
const [selectedFile, setSelectedFile] = useState(null);
|
|
const { showNotification } = useNotification();
|
|
|
|
const fetchFiles = async () => {
|
|
try {
|
|
const response = await fetch('/api/tftp/files');
|
|
const data = await response.json();
|
|
if (response.ok) {
|
|
setFiles(data.files);
|
|
showNotification('File list refreshed successfully.', 'success');
|
|
} else {
|
|
showNotification(data.error, 'error');
|
|
}
|
|
} catch (error) {
|
|
showNotification('Failed to fetch files.', 'error');
|
|
}
|
|
};
|
|
|
|
const handleDownload = (filename) => {
|
|
window.location.href = `/api/tftp/files/${filename}`;
|
|
};
|
|
|
|
const handleDelete = async (filename) => {
|
|
if (confirm(`Are you sure you want to delete ${filename}?`)) {
|
|
try {
|
|
const response = await fetch(`/api/tftp/files/${filename}`, {
|
|
method: 'DELETE',
|
|
});
|
|
const data = await response.json();
|
|
if (response.ok) {
|
|
showNotification('File deleted successfully.', 'success');
|
|
fetchFiles();
|
|
} else {
|
|
showNotification(data.error, 'error');
|
|
}
|
|
} catch (error) {
|
|
showNotification('Failed to delete file.', 'error');
|
|
}
|
|
}
|
|
};
|
|
|
|
const handleUpload = async (event) => {
|
|
event.preventDefault();
|
|
if (!selectedFile) return;
|
|
|
|
const formData = new FormData();
|
|
formData.append('file', selectedFile);
|
|
|
|
try {
|
|
const response = await fetch('/api/tftp/files/upload', {
|
|
method: 'POST',
|
|
body: formData,
|
|
});
|
|
const data = await response.json();
|
|
if (response.ok) {
|
|
showNotification('File uploaded successfully.', 'success');
|
|
fetchFiles();
|
|
} else {
|
|
showNotification(data.error, 'error');
|
|
}
|
|
} catch (error) {
|
|
showNotification('Failed to upload file.', 'error');
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
fetchFiles();
|
|
}, []);
|
|
|
|
return (
|
|
<div className="container mt-5">
|
|
<h1 className="mb-4">TFTP Server Files</h1>
|
|
|
|
<div className="mb-3">
|
|
<label htmlFor="fileUpload" className="form-label">Upload New File</label>
|
|
<input
|
|
type="file"
|
|
className="form-control"
|
|
id="fileUpload"
|
|
onChange={(e) => setSelectedFile(e.target.files[0])}
|
|
/>
|
|
<button className="btn btn-primary mt-2" onClick={handleUpload}>
|
|
Upload File
|
|
</button>
|
|
<button className="btn btn-secondary mt-2 ml-2" onClick={fetchFiles}>
|
|
Refresh
|
|
</button>
|
|
</div>
|
|
|
|
<table className="table table-striped">
|
|
<thead>
|
|
<tr>
|
|
<th scope="col">Filename</th>
|
|
<th scope="col">Size (bytes)</th>
|
|
<th scope="col">Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{files.map((file) => (
|
|
<tr key={file.name}>
|
|
<td>{file.name}</td>
|
|
<td>{file.size}</td>
|
|
<td>
|
|
<button className="btn btn-success btn-sm mr-2" onClick={() => handleDownload(file.name)}>
|
|
Download
|
|
</button>
|
|
<button className="btn btn-danger btn-sm" onClick={() => handleDelete(file.name)}>
|
|
Delete
|
|
</button>
|
|
</td>
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
);
|
|
}
|