From 119e6cefe94d29d8371905bbee47aa3f2212ae11 Mon Sep 17 00:00:00 2001 From: lgcosta Date: Thu, 5 Nov 2026 10:32:40 -0300 Subject: [PATCH] =?UTF-8?q?feat(files):=20gerenciador=20de=20arquivos/anex?= =?UTF-8?q?os=20com=20=C3=A1rvore=20de=20pastas,=20upload,=20preview=20e?= =?UTF-8?q?=20picker?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - store/files.go: pastas (parent, anti-ciclo), FileMeta (nota opcional = avulso), blobs com extensão neutralizada p/ tipos perigosos, sanitização de nome - api/files.go: upload multipart (64MiB), tree, static com ETag/Range + CSP sandbox + attachment p/ html/svg/js (stored-XSS mitigado), move/rename/delete, CRUD de pastas - web: overlay Ctrl+J / 📎 (modo seletor insere sintaxe md/html/txt e associa), filtros Todos/Anexos/Avulsos/Nota atual, busca, lightbox img/video/audio/pdf/texto, colar-e-arrastar no editor e no gerenciador, ações por card (preview/associar/ mover/renomear/excluir), mobile-first (ações sempre visíveis) - fix: api() já serializa o body — chamadas do gerenciador passavam JSON pré-string (dupla codificação); auth por cookie (sem Bearer manual no fetch) - sw v10; testes store+api (traversal, ciclo, XSS headers, range); README --- README.md | 49 ++++ internal/api/files.go | 128 ++++++++ internal/api/files_test.go | 232 +++++++++++++++ internal/api/server.go | 10 + internal/store/files.go | 577 +++++++++++++++++++++++++++++++++++++ internal/store/store.go | 11 +- web/css/app.css | 119 ++++++++ web/index.html | 32 ++ web/js/app.js | 449 +++++++++++++++++++++++++++++ web/sw.js | 2 +- 10 files changed, 1605 insertions(+), 4 deletions(-) create mode 100644 internal/api/files.go create mode 100644 internal/api/files_test.go create mode 100644 internal/store/files.go diff --git a/README.md b/README.md index d4afcfe..7c29f3d 100644 --- a/README.md +++ b/README.md @@ -301,6 +301,55 @@ No actix-web, o `fs::Files` já define MIME por extensão, mas registre antes: "application/manifest+json")` e um `wrap_fn` que sobrepõe `Cache-Control` quando o caminho for `/sw.js`. (No `tower-http` `SetResponseHeaderLayer` com `Match` por path é o caminho limpo.) +## Gerenciador de arquivos (anexos) + +Árvore de pastas estilo sistema de arquivos + arquivos com **associação opcional +a uma nota** (sem nota = avulso). Abra com **Ctrl+J**, menu **Arquivo → +Gerenciador de arquivos…** ou o botão **📎 Anexo** (modo seletor: clicar num +arquivo insere no editor e vincula à nota). + +- **Persistência (disc)**: `data/folders.json` (árvore `parent`), `data/files.json` + (metadados: nome, pasta, nota, tamanho, MIME, SHA-256) e blobs em + `data/files/.` — a extensão física de tipos perigosos (html/js/svg) é + neutralizada (`.bin`/`.img`); o MIME real só vive no metadado. +- **API**: + - `POST /api/files/upload` — multipart (`file`, opcionais `folder_id`, `note_id`) → 201 com `url` + - `GET /api/files/tree` — hierarquia JSON (pastas aninhadas + arquivos + raiz) + - `GET /api/files/static/{id}` — serve com MIME real, **ETag (SHA-256) e Range** + (vídeos fazem seek), `CSP: sandbox` + `nosniff`; tipos não-inline-seguros + (html/svg/js…) vão como **`Content-Disposition: attachment`** — anexar um + `.html` malicioso **não executa same-origin** (stored-XSS mitigado) + - `PUT /api/files/{id}/move` — `{folder, note, name}` (mover/associar/desvincular/renomear) + - `DELETE /api/files/{id}` · `POST/PUT/DELETE /api/folders[/{id}]` (com anti-ciclo) + - Limite: 64 MiB/arquivo; nomes sanitizados (`filepath.Base` + whitelist); ids + gerados — path do disco nunca aparece na URL. +- **Editor**: colar (Ctrl+V) imagem/arquivo do clipboard faz upload automático e + insere `![…](url)` / `![]()` / link conforme o formato; arrastar-e-soltar + funciona no editor e no gerenciador; no WYSIWYG a inserção é visual (``/ + `