docbox_core/files/
delete_file.rs1use crate::{
2 events::{TenantEventMessage, TenantEventPublisher},
3 files::generated::{GeneratedFileDeleteResult, delete_generated_files},
4};
5use docbox_database::{
6 DbErr, DbPool,
7 models::{
8 document_box::{DocumentBoxScopeRaw, WithScope},
9 file::File,
10 generated_file::GeneratedFile,
11 },
12};
13use docbox_search::{SearchError, TenantSearchIndex};
14use docbox_storage::{StorageLayerError, TenantStorageLayer};
15use futures::{StreamExt, stream::FuturesUnordered};
16use thiserror::Error;
17
18#[derive(Debug, Error)]
19pub enum DeleteFileError {
20 #[error("failed to delete tenant search index: {0}")]
22 DeleteIndex(SearchError),
23
24 #[error(transparent)]
26 Database(#[from] DbErr),
27
28 #[error("failed to remove file from storage: {0}")]
30 DeleteFileStorage(StorageLayerError),
31
32 #[error("failed to remove generated file from storage: {0}")]
34 DeleteGeneratedFileStorage(StorageLayerError),
35}
36
37pub async fn delete_file(
50 db: &DbPool,
51 storage: &TenantStorageLayer,
52 search: &TenantSearchIndex,
53 events: &TenantEventPublisher,
54 file: File,
55 scope: DocumentBoxScopeRaw,
56) -> Result<(), DeleteFileError> {
57 let generated = GeneratedFile::find_all(db, file.id)
58 .await
59 .inspect_err(|error| tracing::error!(?error, "failed to query generated files"))?;
60
61 match delete_generated_files(storage, &generated).await {
62 GeneratedFileDeleteResult::Ok => {}
63 GeneratedFileDeleteResult::Err(deleted, err) => {
64 let mut delete_files_future = generated
66 .into_iter()
67 .filter(|file| deleted.contains(&file.id))
68 .map(|file| file.delete(db))
69 .collect::<FuturesUnordered<_>>();
70
71 while let Some(result) = delete_files_future.next().await {
73 if let Err(error) = result {
74 tracing::error!(?error, "failed to delete generated file from db");
75 }
76 }
77
78 return Err(DeleteFileError::DeleteGeneratedFileStorage(err));
79 }
80 }
81
82 let mut delete_files_future = generated
83 .into_iter()
84 .map(|file| file.delete(db))
85 .collect::<FuturesUnordered<_>>();
86
87 while let Some(result) = delete_files_future.next().await {
89 if let Err(error) = result {
90 tracing::error!(?error, "failed to delete generated file");
91 return Err(DeleteFileError::Database(error));
92 }
93 }
94
95 storage
97 .delete_file(&file.file_key)
98 .await
99 .map_err(DeleteFileError::DeleteFileStorage)?;
100
101 search
103 .delete_data(file.id)
104 .await
105 .map_err(DeleteFileError::DeleteIndex)?;
106
107 let result = file
109 .delete(db)
110 .await
111 .inspect_err(|error| tracing::error!(?error, "failed to delete file from database"))?;
112
113 if result.rows_affected() < 1 {
115 return Ok(());
116 }
117
118 events.publish_event(TenantEventMessage::FileDeleted(WithScope::new(file, scope)));
120
121 Ok(())
122}