docbox_core/files/
generated.rs1use crate::files::create_generated_file_key;
4use chrono::Utc;
5use docbox_database::models::{
6 file::FileId,
7 generated_file::{CreateGeneratedFile, GeneratedFile, GeneratedFileId},
8};
9use docbox_processing::QueuedUpload;
10use docbox_storage::{StorageLayerError, TenantStorageLayer};
11use futures::{
12 StreamExt,
13 stream::{FuturesOrdered, FuturesUnordered},
14};
15use tracing::{Instrument, debug, error};
16use uuid::Uuid;
17
18pub enum GeneratedFileDeleteResult {
19 Ok,
21 Err(Vec<GeneratedFileId>, StorageLayerError),
24}
25
26pub async fn delete_generated_files(
27 storage: &TenantStorageLayer,
28 files: &[GeneratedFile],
29) -> GeneratedFileDeleteResult {
30 let files_count = files.len();
31
32 let mut futures = files
33 .iter()
34 .map(|file| {
35 async {
36 let id = file.id;
37 let file_id = file.file_id;
38 let file_key = file.file_key.to_string();
39
40 debug!(%id, %file_id, %file_key, "deleting file from storage");
41
42 if let Err(error) = storage.delete_file(&file_key).await {
44 error!(%id, %file_id, %file_key, ?error, "failed to delete generated file");
45 return Err(error);
46 }
47
48 debug!("deleted file from storage");
49 Ok(id)
50 }
51 })
52 .collect::<FuturesUnordered<_>>();
53
54 let mut deleted: Vec<GeneratedFileId> = Vec::with_capacity(files_count);
55
56 while let Some(result) = futures.next().await {
57 match result {
58 Ok(id) => deleted.push(id),
59 Err(err) => return GeneratedFileDeleteResult::Err(deleted, err),
60 }
61 }
62
63 GeneratedFileDeleteResult::Ok
64}
65
66pub struct PreparedGeneratedFile {
67 create: CreateGeneratedFile,
68 upload: QueuedUpload,
69}
70
71pub fn make_create_generated_files(
72 base_file_key: &str,
73 file_id: &FileId,
74 file_hash: &str,
75 queued_uploads: Vec<QueuedUpload>,
76) -> Vec<PreparedGeneratedFile> {
77 queued_uploads
78 .into_iter()
79 .map(|upload| {
80 let id = Uuid::new_v4();
81 let created_at = Utc::now();
82 let file_key = create_generated_file_key(base_file_key, &upload.mime);
83
84 let create = CreateGeneratedFile {
85 id,
86 file_id: *file_id,
87 hash: file_hash.to_string(),
88 mime: upload.mime.to_string(),
89 ty: upload.ty,
90 file_key,
91 created_at,
92 };
93
94 PreparedGeneratedFile { create, upload }
95 })
96 .collect()
97}
98
99pub async fn upload_generated_files(
102 storage: &TenantStorageLayer,
103 prepared: Vec<PreparedGeneratedFile>,
104) -> Vec<Result<CreateGeneratedFile, StorageLayerError>> {
105 prepared
106 .into_iter()
107 .map(|PreparedGeneratedFile { create, upload }| {
108 let span = tracing::info_span!("upload_generated_files", ?create);
109 async move {
110 storage
112 .upload_file(&create.file_key, create.mime.clone(), upload.bytes)
113 .await
114 .inspect_err(|error| {
115 tracing::error!(?error, "failed to store generated file");
116 })?;
117
118 Ok(create)
119 }
120 .instrument(span)
121 })
122 .collect::<FuturesOrdered<_>>()
123 .collect()
124 .await
125}