/** * Document API — listing, fetching, uploading, content streaming. * * Consumers: stores/documents.js, DocumentCard.vue, DocumentView.vue, * DocumentPreviewModal.vue, FileManagerView.vue, CloudFolderView.vue */ import { request, fetchWithRetry, jsonRequest } from './utils.js' export function listDocuments({ topic, page = 1, perPage = 20, folderId = null, q = null, sort = null, order = null } = {}) { const params = new URLSearchParams({ page, per_page: perPage }) if (topic) params.set('topic', topic) if (folderId != null) params.set('folder_id', folderId) if (q) params.set('q', q) if (sort) params.set('sort', sort) if (order) params.set('order', order) return request(`/api/documents?${params}`) } export function getDocument(id) { return request(`/api/documents/${id}`) } export function deleteDocument(id, removeOnly = false) { const url = removeOnly ? `/api/documents/${id}?remove_only=true` : `/api/documents/${id}` return request(url, { method: 'DELETE' }) } export function deleteDocumentRemoveOnly(id) { return deleteDocument(id, true) } export function classifyDocument(id, topics = null) { return jsonRequest(`/api/documents/${id}/classify`, 'POST', topics ? { topics } : {}) } export function getUploadUrl(filename, contentType) { return jsonRequest('/api/documents/upload-url', 'POST', { filename, content_type: contentType, }) } export function confirmUpload(documentId) { return request(`/api/documents/${documentId}/confirm`, { method: 'POST' }) } export function uploadToCloud(file, provider, folderPath) { const form = new FormData() form.append('file', file) form.append('target_backend', provider) if (folderPath) form.append('cloud_folder_path', folderPath) return request('/api/documents/upload', { method: 'POST', body: form }) } export function getDocumentContentUrl(docId) { return `/api/documents/${docId}/content` } /** * Fetch document content bytes with authentication, returning the raw Response. * * Refactored to use fetchWithRetry() which consolidates the auth-injection + * 401-retry boilerplate that was previously copy-pasted here (CODE-08). * * Unlike request(), this does NOT call res.json() — returns raw Response so * callers can call .blob() to build an object URL for iframe preview or * window.open() without an unauthenticated src= attribute. * * Security: closes the unauthenticated content-access gap where an iframe src= * or window.open() with a raw /content URL would bypass the Bearer auth check. * See plan 05-09 trust boundary: frontend→/api/documents/{id}/content. * * @param {string} docId * @param {RequestInit} [options] * @returns {Promise} — raw Response; throws on non-ok status */ export async function fetchDocumentContent(docId, options = {}) { const res = await fetchWithRetry(`/api/documents/${docId}/content`, options) if (!res.ok) throw new Error(`Failed to fetch document content: ${res.status}`) return res }