fix: knowledge base name collision, orphan dirs, and document download

- Name uniqueness check scoped to user domain (system KBs no longer block user KBs)
- Delete KB now cleans up its directory with shutil.rmtree
- User KB uploads saved to knowledge_bases/{kb_name}/ subdirectory
- New /documents/{id}/download endpoint with auth-aware FileResponse
- Frontend uses fetch+token instead of direct window.open for downloads

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-06-02 11:30:23 +08:00
parent 5f768e5c83
commit 3250e87b30
2 changed files with 95 additions and 15 deletions
+27 -5
View File
@@ -155,11 +155,33 @@ export default function KnowledgeBaseDetailPage() {
}
};
const handleViewDocument = (doc: Document) => {
// Use the file_path from the document to construct the download URL
// Since backend serves static files from /uploads, we can use the file_path directly
const fileUrl = `/api/uploads/${doc.filename}`;
window.open(fileUrl, '_blank');
const handleViewDocument = async (doc: Document) => {
try {
const token = localStorage.getItem("auth_token");
const res = await fetch(`/api/knowledge-bases/documents/${doc.id}/download`, {
headers: { Authorization: `Bearer ${token}` },
});
if (!res.ok) throw new Error("下载失败");
const blob = await res.blob();
// 从 Content-Disposition 提取文件名,或用 doc 信息拼接
const disposition = res.headers.get("Content-Disposition");
let filename = `${doc.title}${doc.file_type}`;
if (disposition) {
const match = disposition.match(/filename\*?=(?:UTF-8'')?(.+)/i);
if (match) filename = decodeURIComponent(match[1].replace(/["']/g, ""));
}
// 触发浏览器下载
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = filename;
a.click();
URL.revokeObjectURL(url);
} catch {
window.open(`/api/knowledge-bases/documents/${doc.id}/download`, '_blank');
}
};
const filteredDocuments = knowledgeBase?.documents.filter(doc =>