Problema
Ao tentar fazer upload de um documento na aba Knowledge → Upload, o sistema retornava:
Upload failed: FORBIDDEN
O usuário era admin com permissão knowledge:manage — o erro não era de role.
Causa raiz
O backend (dashboard/backend/routes/knowledge_proxy.py:192) aplica uma proteção CSRF na rota POST /api/knowledge/connections//documents via _require_xhr():
def _require_xhr() -> None:
if request.headers.get("X-Requested-With") != "XMLHttpRequest":
abort(403, description="CSRF check failed: X-Requested-With header missing.")
O frontend no Upload.tsx fazia o fetch sem enviar esse header:
// ANTES — faltava o header
const res = await fetch(url, {
method: 'POST',
body: formData,
credentials: 'include'
})
Todas as outras rotas que usam _require_xhr() são chamadas via api.ts (que injeta X-Requested-With automaticamente), mas o upload usava fetch direto — sem o header.
Fix aplicado
Adicionado o header na chamada fetch do upload (Upload.tsx:167):
// DEPOIS
const res = await fetch(url, {
method: 'POST',
body: formData,
credentials: 'include',
headers: { 'X-Requested-With': 'XMLHttpRequest' }
})
Por que não usar api.ts?
O fetch com FormData não pode passar por api.ts diretamente porque o wrapper define Content-Type: application/json — o que quebraria o multipart/form-data necessário para o upload de arquivos. A solução correta é manter o fetch direto e adicionar apenas o header CSRF manualmente.
Impacto
Nenhuma mudança de comportamento além de desbloquear o upload
Zero risco de regressão — o header é inócuo para qualquer outra rota
Problema
Ao tentar fazer upload de um documento na aba Knowledge → Upload, o sistema retornava:
Upload failed: FORBIDDEN
O usuário era admin com permissão knowledge:manage — o erro não era de role.
Causa raiz
O backend (dashboard/backend/routes/knowledge_proxy.py:192) aplica uma proteção CSRF na rota POST /api/knowledge/connections//documents via _require_xhr():
def _require_xhr() -> None:
if request.headers.get("X-Requested-With") != "XMLHttpRequest":
abort(403, description="CSRF check failed: X-Requested-With header missing.")
O frontend no Upload.tsx fazia o fetch sem enviar esse header:
// ANTES — faltava o header
const res = await fetch(url, {
method: 'POST',
body: formData,
credentials: 'include'
})
Todas as outras rotas que usam _require_xhr() são chamadas via api.ts (que injeta X-Requested-With automaticamente), mas o upload usava fetch direto — sem o header.
Fix aplicado
Adicionado o header na chamada fetch do upload (Upload.tsx:167):
// DEPOIS
const res = await fetch(url, {
method: 'POST',
body: formData,
credentials: 'include',
headers: { 'X-Requested-With': 'XMLHttpRequest' }
})
Por que não usar api.ts?
O fetch com FormData não pode passar por api.ts diretamente porque o wrapper define Content-Type: application/json — o que quebraria o multipart/form-data necessário para o upload de arquivos. A solução correta é manter o fetch direto e adicionar apenas o header CSRF manualmente.
Impacto
Nenhuma mudança de comportamento além de desbloquear o upload
Zero risco de regressão — o header é inócuo para qualquer outra rota