diff --git a/backend/src/openapi.rs b/backend/src/openapi.rs index 4ec3234..b0f1ac2 100644 --- a/backend/src/openapi.rs +++ b/backend/src/openapi.rs @@ -18,7 +18,6 @@ use uuid::Uuid; doc::get_document, doc::update_document, doc::delete_document, - doc::download_document, doc::download_with_token, doc::move_document, doc::assign_tags, @@ -64,7 +63,6 @@ use uuid::Uuid; schemas::DocumentAssetObject, schemas::DocumentCorrespondent, schemas::DocumentTag, - schemas::DocumentDownloadResponse, schemas::UpdateDocumentRequest, schemas::BulkMoveDocumentsRequest, schemas::BulkMoveDocumentsResponse, @@ -270,15 +268,6 @@ mod doc { )] pub(super) fn restore_document() {} - #[utoipa::path( - get, - path = "/api/documents/{id}/download", - params(("id" = Uuid, Path, description = "Document ID")), - responses((status = 200, description = "Download metadata", body = DocumentDownloadResponse)), - tag = "Documents" - )] - pub(super) fn download_document() {} - #[utoipa::path( get, path = "/download/{token}", @@ -728,16 +717,6 @@ pub mod schemas { pub document: DocumentResponse, } - #[derive(Serialize, Deserialize, ToSchema)] - pub struct DocumentDownloadResponse { - pub url: String, - pub expires_in: u64, - pub filename: String, - #[schema(nullable)] - pub content_type: Option, - pub size_bytes: i64, - } - #[derive(Serialize, Deserialize, ToSchema)] pub struct DocumentMetadataUpdate { pub value: Value, diff --git a/backend/src/routes/documents.rs b/backend/src/routes/documents.rs index 46057f7..741713c 100644 --- a/backend/src/routes/documents.rs +++ b/backend/src/routes/documents.rs @@ -215,15 +215,6 @@ pub struct DocumentDetailResponse { pub document: DocumentResponse, } -#[derive(Serialize, ToSchema)] -pub struct DocumentDownloadResponse { - pub url: String, - pub expires_in: u64, - pub filename: String, - pub content_type: Option, - pub size_bytes: i64, -} - #[derive(Serialize, ToSchema)] pub struct BulkReanalyzeResponse { pub queued: usize, @@ -1211,46 +1202,6 @@ pub async fn get_document_version( })) } -pub async fn download_document( - State(state): State, - Path(document_id): Path, - TenantScopedConn { - mut conn, - tenant_id, - .. - }: TenantScopedConn, -) -> AppResult> { - let doc: Document = documents::table - .find(document_id) - .filter(documents::tenant_id.eq(tenant_id)) - .first(&mut conn)?; - if doc.deleted_at.is_some() { - return Err(AppError::not_found()); - } - - let version: DocumentVersion = document_versions::table - .find(doc.current_version_id) - .first(&mut conn)?; - - let storage = state.storage_for_tenant(tenant_id)?; - - let presigned_url = storage - .presign_get_object( - &version.s3_key, - Duration::from_secs(PRESIGNED_URL_EXPIRY_SECONDS), - ) - .await - .map_err(|err| AppError::internal(format!("failed to generate download URL: {err}")))?; - - Ok(Json(DocumentDownloadResponse { - url: presigned_url, - expires_in: PRESIGNED_URL_EXPIRY_SECONDS, - filename: doc.original_name.clone(), - content_type: doc.content_type.clone(), - size_bytes: version.size_bytes, - })) -} - pub async fn download_with_token( State(state): State, Path(token): Path, diff --git a/backend/src/routes/mod.rs b/backend/src/routes/mod.rs index 52351f3..52fea31 100644 --- a/backend/src/routes/mod.rs +++ b/backend/src/routes/mod.rs @@ -79,7 +79,6 @@ pub fn create_router(state: AppState) -> Router<()> { .delete(documents::delete_document) .patch(documents::update_document), ) - .route("/:id/download", get(documents::download_document)) .route( "/:id/assets", get(documents::list_document_assets).post(documents::request_document_assets), diff --git a/backend/tests/documents_flow.rs b/backend/tests/documents_flow.rs index 7856043..845dfe1 100644 --- a/backend/tests/documents_flow.rs +++ b/backend/tests/documents_flow.rs @@ -60,11 +60,6 @@ struct DocumentListItem { current_version: Option, } -#[derive(Deserialize)] -struct DocumentDownload { - filename: String, -} - #[derive(Deserialize)] struct BulkReanalyze { queued: usize, @@ -238,24 +233,6 @@ async fn upload_and_list_document() -> Result<()> { .download_path .starts_with("/download/")); - let download = app - .get( - &format!("/api/documents/{}/download", detail.document.id), - Some(&token), - ) - .await?; - { - let status = download.status(); - assert!( - status == StatusCode::OK - || status == StatusCode::CREATED - || status == StatusCode::NO_CONTENT - ); - } - let body = body_to_vec(download.into_body()).await?; - let download_info: DocumentDownload = serde_json::from_slice(&body)?; - assert_eq!(download_info.filename, "doc.txt"); - let redirect = app.get(¤t_version.download_path, None).await?; assert_eq!(redirect.status(), StatusCode::TEMPORARY_REDIRECT); let location = redirect diff --git a/frontend/src/index.jsx b/frontend/src/index.jsx index b5440a2..518fed7 100644 --- a/frontend/src/index.jsx +++ b/frontend/src/index.jsx @@ -3107,13 +3107,18 @@ const AppLayout = () => { const request = (async () => { try { - const { data } = await api.get(`/documents/${documentId}/download`); - const ttl = data.expires_in ? Math.max(data.expires_in - 60, 30) * 1000 : 5 * 60 * 1000; + const docResponse = await api.get(`/documents/${documentId}`); + const downloadPath = docResponse.data?.document?.current_version?.download_path; + if (!downloadPath || !resolveApiPath) { + throw new Error('Document missing download path'); + } + + const href = resolveApiPath(downloadPath); const entry = { - url: data.url, - contentType: data.content_type || null, - filename: data.filename, - expiresAt: Date.now() + ttl, + url: href, + contentType: docResponse.data?.document?.current_version?.version?.content_type || null, + filename: docResponse.data?.document?.filename || 'document', + expiresAt: Date.now() + 5 * 60 * 1000, }; setPreviewEntries((prev) => { const next = new Map(prev); @@ -3132,7 +3137,7 @@ const AppLayout = () => { previewInflightRef.current.set(documentId, request); return request; }, - [previewEntries, notifyApiError], + [previewEntries, notifyApiError, resolveApiPath], ); const extractFilesFromDataTransfer = useCallback(async (dataTransfer) => {