refactor: integra reconcile no boot, dedup categoria/pasta, CI, Go 1.24
ci / check (push) Successful in 4m12s

- Boot chama ReconcileBlobs com log de órfãos (era código morto)
- treeNode/validTreeName/siblingDup/moveCreatesCycle compartilhados
  entre Create/Update Category/Folder (mesma regra, um só lugar)
- mapStoreErrorFor com mensagens 404 por recurso (nota/arquivo/pasta/categoria)
- revisionHash helper + teste GET /revisions/{hash} (200 + 404)
- ReconcileBlobs com lock curto (coleta sob RLock, I/O fora do lock)
- AddFile com LimitReader no stream (cap antes de gravar, não depois)
- DeleteFile/DeleteFolder transacionais com rollback em memória
- attachmentKind único no frontend (syntax/html/preview)
- CI: gofmt, vet, test -race, node --check; go.mod 1.24 = Dockerfile
- README: notas operacionais (memória, proxy, boot, SSE, 413/400)
This commit is contained in:
lgcosta committed 2026-11-05 19:57:38 -03:00
1 parent 0e59ef23f4
commit e0bef77e31
16 files changed
+518 -126

No files matched your search

+20 -25
View File
@@ -2371,27 +2371,24 @@
? 'Anexo(s) inserido(s) no editor' : `Inseridos ${done.length}/${files.length}`);
}
function attachmentKind(f) {
const mime = f.mime || '';
if (mime.startsWith('image/') && mime !== 'image/svg+xml') return 'img';
if (mime.startsWith('video/')) return 'vid';
if (mime.startsWith('audio/')) return 'aud';
return 'doc';
}
function attachmentSyntax(f, fmt) {
const isImg = f.mime.startsWith('image/') && f.mime !== 'image/svg+xml';
const isVid = f.mime.startsWith('video/');
const isAud = f.mime.startsWith('audio/');
if (fmt === 'html') {
if (isImg) return `<img src="${f.url}" alt="${esc(f.name)}">`;
if (isVid) return `<video controls src="${f.url}"></video>`;
if (isAud) return `<audio controls src="${f.url}"></audio>`;
return `<a href="${f.url}" target="_blank" rel="noopener">${esc(f.name)}</a>`;
}
if (isImg) return `![${f.name}](${f.url})`;
return `[${(isVid ? '▶ ' : isAud ? ' ' : '') + f.name}](${f.url})`;
const kind = attachmentKind(f);
if (fmt === 'html') return attachmentHtml(f);
if (kind === 'img') return `![${f.name}](${f.url})`;
return `[${(kind === 'vid' ? '▶ ' : kind === 'aud' ? ' ' : '') + f.name}](${f.url})`;
}
function attachmentHtml(f) {
const isImg = f.mime.startsWith('image/') && f.mime !== 'image/svg+xml';
const isVid = f.mime.startsWith('video/');
const isAud = f.mime.startsWith('audio/');
if (isImg) return `<img src="${f.url}" alt="${esc(f.name)}">`;
if (isVid) return `<video controls src="${f.url}"></video>`;
if (isAud) return `<audio controls src="${f.url}"></audio>`;
const kind = attachmentKind(f);
if (kind === 'img') return `<img src="${f.url}" alt="${esc(f.name)}">`;
if (kind === 'vid') return `<video controls src="${f.url}"></video>`;
if (kind === 'aud') return `<audio controls src="${f.url}"></audio>`;
return `<a href="${f.url}" target="_blank" rel="noopener">${esc(f.name)}</a>`;
}
@@ -2436,14 +2433,12 @@
F.lbDl.href = f.url;
F.lbDl.setAttribute('download', f.name);
F.lbBody.textContent = '';
const isImg = f.mime.startsWith('image/') && f.mime !== 'image/svg+xml';
const isVid = f.mime.startsWith('video/');
const isAud = f.mime.startsWith('audio/');
if (isImg || isVid || isAud || f.mime.includes('pdf')) {
const kind = attachmentKind(f);
if (kind !== 'doc' || (f.mime || '').includes('pdf')) {
let el;
if (isImg) { el = document.createElement('img'); el.src = f.url; el.alt = f.name; }
else if (isVid) { el = document.createElement('video'); el.src = f.url; el.controls = true; }
else if (isAud) { el = document.createElement('audio'); el.src = f.url; el.controls = true; }
if (kind === 'img') { el = document.createElement('img'); el.src = f.url; el.alt = f.name; }
else if (kind === 'vid') { el = document.createElement('video'); el.src = f.url; el.controls = true; }
else if (kind === 'aud') { el = document.createElement('audio'); el.src = f.url; el.controls = true; }
else { el = document.createElement('iframe'); el.src = f.url; el.title = f.name; }
F.lbBody.appendChild(el);
} else {