docbox_core/files/
generated.rs1use crate::{files::create_generated_file_key, storage::TenantStorageLayer};
4use anyhow::Context;
5use bytes::Bytes;
6use futures::{
7 StreamExt,
8 stream::{FuturesOrdered, FuturesUnordered},
9};
10use mime::Mime;
11use tracing::{debug, error};
12
13use docbox_database::models::{
14 file::FileId,
15 generated_file::{CreateGeneratedFile, GeneratedFile, GeneratedFileId, GeneratedFileType},
16};
17
18#[derive(Debug)]
19pub struct QueuedUpload {
20 pub mime: Mime,
21 pub ty: GeneratedFileType,
22 pub bytes: Bytes,
23}
24
25impl QueuedUpload {
26 pub fn new(mime: Mime, ty: GeneratedFileType, bytes: Bytes) -> Self {
27 Self { mime, ty, bytes }
28 }
29}
30
31pub enum GeneratedFileDeleteResult {
32 Ok,
34 Err(Vec<GeneratedFileId>, anyhow::Error),
37}
38
39pub async fn delete_generated_files(
40 storage: &TenantStorageLayer,
41 files: &[GeneratedFile],
42) -> GeneratedFileDeleteResult {
43 let files_count = files.len();
44
45 let mut futures = files
46 .iter()
47 .map(|file| {
48 async {
49 let id = file.id;
50 let file_id = file.file_id;
51 let file_key = file.file_key.to_string();
52
53 debug!(%id, %file_id, %file_key, "uploading file to s3",);
54
55 if let Err(cause) = storage.delete_file(&file_key).await {
57 error!(%id, %file_id, %file_key, ?cause, "failed to delete generated file");
58 }
59
60 debug!("deleted file from s3");
61
62 anyhow::Ok(id)
63 }
64 })
65 .collect::<FuturesUnordered<_>>();
66
67 let mut deleted: Vec<GeneratedFileId> = Vec::with_capacity(files_count);
68
69 while let Some(result) = futures.next().await {
70 match result {
71 Ok(id) => deleted.push(id),
72 Err(err) => return GeneratedFileDeleteResult::Err(deleted, err),
73 }
74 }
75
76 GeneratedFileDeleteResult::Ok
77}
78
79pub async fn upload_generated_files(
82 storage: &TenantStorageLayer,
83 base_file_key: &str,
84 file_id: &FileId,
85 file_hash: &str,
86 queued_uploads: Vec<QueuedUpload>,
87) -> Vec<anyhow::Result<CreateGeneratedFile>> {
88 queued_uploads
89 .into_iter()
90 .map(|queued_upload| {
91 let file_id = *file_id;
93 let file_hash = file_hash.to_string();
94
95 async move {
96 let file_mime = queued_upload.mime.to_string();
97 let file_key = create_generated_file_key(base_file_key, &queued_upload.mime);
98
99 debug!(%file_id, %file_hash, %file_key, %file_mime, "uploading file to s3");
100
101 storage
103 .upload_file(&file_key, file_mime, queued_upload.bytes)
104 .await
105 .context("failed to upload generated file")?;
106
107 anyhow::Ok(CreateGeneratedFile {
108 file_id,
109 hash: file_hash,
110 mime: queued_upload.mime.to_string(),
111 ty: queued_upload.ty,
112 file_key,
113 })
114 }
115 })
116 .collect::<FuturesOrdered<_>>()
117 .collect()
118 .await
119}