fix: resolve upload delay and refine trash functionality
All checks were successful
Automated Container Build / build-and-push (push) Successful in 1m22s

This commit is contained in:
Elijah 2026-05-23 14:11:23 -07:00
parent 45989ef97d
commit db946461d7
3 changed files with 49 additions and 21 deletions

View file

@ -534,6 +534,19 @@ func (h *FSHandler) Delete(c *fiber.Ctx) error {
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "file not found"})
}
// Check if permanent
isPermanent := c.Query("permanent") == "true"
if isPermanent {
if err := os.RemoveAll(resolvedPath); err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "failed to permanently delete"})
}
cleanPath := filepath.ToSlash(relativePath)
h.DB.Exec(`DELETE FROM files WHERE path = ? OR path LIKE ?`, cleanPath, cleanPath+"/%")
h.DB.AddAuditLog("permanently_deleted", cleanPath, c.IP())
return c.JSON(fiber.Map{"message": "permanently deleted"})
}
// Soft-delete: move to .trash directory
trashPath := filepath.Join(h.Config.TrashDir, relativePath)
if err := os.MkdirAll(filepath.Dir(trashPath), 0755); err != nil {

View file

@ -631,20 +631,22 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) {
async function executeDeleteSelected() {
const count = selectedFiles.size;
const isPermanent = activeSection === 'trash';
for (const path of selectedFiles) {
await api.deleteFile(path);
await api.deleteFile(path, isPermanent);
}
setSelectedFiles(new Set());
loadFiles(undefined, true);
setDeleteConfirm(null);
showToast(`Deleted ${count} item${count > 1 ? 's' : ''}`);
showToast(isPermanent ? `Permanently deleted ${count} item${count > 1 ? 's' : ''}` : `Deleted ${count} item${count > 1 ? 's' : ''}`);
}
async function executeDeleteSingle(file: FileItem) {
await api.deleteFile(file.path);
const isPermanent = activeSection === 'trash';
await api.deleteFile(file.path, isPermanent);
loadFiles(undefined, true);
setDeleteConfirm(null);
showToast(`Deleted ${file.name}`);
showToast(isPermanent ? `Permanently deleted ${file.name}` : `Deleted ${file.name}`);
}
async function handlePin(file: FileItem) {
@ -880,16 +882,16 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) {
}
await Promise.all(workers);
loadFiles(undefined, true);
setTimeout(() => {
setUploads(prev => prev.filter(u => u.id !== batchId));
loadFiles(undefined, true);
}, 3000);
}
async function handleDrop(e: React.DragEvent) {
e.preventDefault();
setDragOver(false);
if (draggedFile) return;
if (draggedFile || activeSection === 'trash') return;
if (e.dataTransfer.items) {
const filesToUpload: { file: File; path: string }[] = [];
@ -968,7 +970,7 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) {
async function handleFileUpload(e: React.ChangeEvent<HTMLInputElement>) {
const uploadFiles = e.target.files;
if (!uploadFiles) return;
if (!uploadFiles || activeSection === 'trash') return;
const filesArray = Array.from(uploadFiles).map(f => ({ file: f, path: '' }));
const fileNames = filesArray.map(f => f.file.name);
@ -1777,6 +1779,7 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) {
>
<Download className="w-4 h-4" />
</button>
{activeSection !== 'trash' && (
<button
className="p-2 hover:bg-white/5 rounded-lg transition-colors flex items-center gap-2 text-sm"
style={{ color: 'var(--color-text-primary)' }}
@ -1789,6 +1792,7 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) {
>
<Move className="w-4 h-4" />
</button>
)}
<button
className="p-2 hover:bg-red-500/10 rounded-lg transition-colors flex items-center gap-2 text-sm text-red-500"
title="Delete Selected"
@ -1839,6 +1843,16 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) {
label="Restore"
onClick={() => { handleRestore(contextMenu.file); setContextMenu(null); }}
/>
<div className="h-px mx-2 my-1" style={{ backgroundColor: 'var(--color-border-subtle)' }} />
<ContextMenuItem
icon={<Trash2 className="w-4 h-4" />}
label="Delete Permanently"
danger
onClick={() => {
setDeleteConfirm({ type: 'single', file: contextMenu.file });
setContextMenu(null);
}}
/>
</>
) : (
<>

View file

@ -201,8 +201,9 @@ class ApiClient {
return res.json();
}
async deleteFile(path: string) {
const res = await this.request(`/api/files/?path=${encodeURIComponent(path)}`, {
async deleteFile(path: string, permanent: boolean = false) {
const query = permanent ? '&permanent=true' : '';
const res = await this.request(`/api/files/?path=${encodeURIComponent(path)}${query}`, {
method: 'DELETE',
});
return res.json();