diff --git a/public/js/api.js b/public/js/api.js
index 813618d..32cabec 100644
--- a/public/js/api.js
+++ b/public/js/api.js
@@ -144,6 +144,7 @@ const API = {
files: {
list(path) { return API.request('GET', `/files${path ? '?path=' + encodeURIComponent(path) : ''}`); },
+ delete(path) { return API.request('DELETE', `/files?path=${encodeURIComponent(path)}`); },
},
reports: {
diff --git a/public/js/app.js b/public/js/app.js
index f15da09..153e444 100644
--- a/public/js/app.js
+++ b/public/js/app.js
@@ -774,6 +774,7 @@ const App = {
case 'navigate-files': FilesUI.navigate(path || ''); break;
case 'download-file': FilesUI.downloadFile(path); break;
case 'download-folder': FilesUI.downloadFolder(path); break;
+ case 'delete-entry': FilesUI.deleteEntry(path, el.dataset.entryType); break;
}
});
diff --git a/public/js/components/files.js b/public/js/components/files.js
index 268ac14..79183d4 100644
--- a/public/js/components/files.js
+++ b/public/js/components/files.js
@@ -40,7 +40,7 @@ const FilesUI = {
${breadcrumb}
${entries.length} ${entries.length === 1 ? 'item' : 'itens'}
-
+
@@ -87,9 +87,11 @@ const FilesUI = {
? ` ${Utils.escapeHtml(entry.name)}`
: ` ${Utils.escapeHtml(entry.name)}`;
- const actions = entry.type === 'directory'
- ? ``
+ const downloadBtn = entry.type === 'directory'
+ ? ``
: ``;
+ const deleteBtn = ``;
+ const actions = `${downloadBtn}${deleteBtn}`;
return `
@@ -147,6 +149,24 @@ const FilesUI = {
a.download = '';
a.click();
},
+
+ async deleteEntry(path, entryType) {
+ const label = entryType === 'directory' ? 'pasta' : 'arquivo';
+ const name = path.split('/').pop();
+ const confirmed = await Modal.confirm(
+ `Excluir ${label}`,
+ `Tem certeza que deseja excluir "${name}"? Esta ação não pode ser desfeita.`
+ );
+ if (!confirmed) return;
+
+ try {
+ await API.files.delete(path);
+ Toast.success(`${label.charAt(0).toUpperCase() + label.slice(1)} excluído`);
+ await FilesUI.navigate(FilesUI.currentPath);
+ } catch (err) {
+ Toast.error(`Erro ao excluir: ${err.message}`);
+ }
+ },
};
window.FilesUI = FilesUI;
diff --git a/src/routes/api.js b/src/routes/api.js
index 6a65769..ba4c4e1 100644
--- a/src/routes/api.js
+++ b/src/routes/api.js
@@ -11,7 +11,7 @@ import * as pipeline from '../agents/pipeline.js';
import { getBinPath, updateMaxConcurrent, cancelAllExecutions, getActiveExecutions } from '../agents/executor.js';
import { invalidateAgentMapCache } from '../agents/pipeline.js';
import { cached } from '../cache/index.js';
-import { readdirSync, readFileSync, unlinkSync, existsSync, mkdirSync, statSync, createReadStream } from 'fs';
+import { readdirSync, readFileSync, unlinkSync, existsSync, mkdirSync, statSync, createReadStream, rmSync } from 'fs';
import { join, dirname, resolve as pathResolve, extname, basename, relative } from 'path';
import { createGzip } from 'zlib';
import { Readable } from 'stream';
@@ -1140,4 +1140,24 @@ router.get('/files/download-folder', (req, res) => {
}
});
+router.delete('/files', (req, res) => {
+ try {
+ const targetPath = resolveProjectPath(req.query.path || '');
+ if (!targetPath) return res.status(400).json({ error: 'Caminho inválido' });
+ if (targetPath === PROJECTS_DIR) return res.status(400).json({ error: 'Não é permitido excluir o diretório raiz' });
+ if (!existsSync(targetPath)) return res.status(404).json({ error: 'Arquivo ou pasta não encontrado' });
+
+ const stat = statSync(targetPath);
+ if (stat.isDirectory()) {
+ rmSync(targetPath, { recursive: true, force: true });
+ } else {
+ unlinkSync(targetPath);
+ }
+
+ res.json({ success: true });
+ } catch (err) {
+ res.status(500).json({ error: err.message });
+ }
+});
+
export default router;