1use chrono::{DateTime, Utc};
8use serde::{Deserialize, Serialize};
9use sqlx::{prelude::FromRow, types::Json};
10use uuid::Uuid;
11
12use super::{document_box::DocumentBoxScopeRaw, file::FileId, folder::FolderId, user::UserId};
13use crate::{DbErr, DbExecutor, DbResult};
14
15pub type PresignedUploadTaskId = Uuid;
16
17#[derive(Debug, FromRow, Serialize)]
19pub struct PresignedUploadTask {
20 pub id: PresignedUploadTaskId,
22 #[sqlx(json)]
24 pub status: PresignedTaskStatus,
25
26 pub name: String,
28 pub mime: String,
30 pub size: i32,
32
33 pub document_box: DocumentBoxScopeRaw,
35 pub folder_id: FolderId,
37 pub file_key: String,
39
40 pub created_at: DateTime<Utc>,
42 pub expires_at: DateTime<Utc>,
45 pub created_by: Option<UserId>,
47
48 pub parent_id: Option<FileId>,
50
51 pub processing_config: Option<Json<serde_json::Value>>,
54}
55
56#[derive(Debug, Clone, Deserialize, Serialize)]
57#[serde(tag = "status")]
58pub enum PresignedTaskStatus {
59 Pending,
60 Completed { file_id: FileId },
61 Failed { error: String },
62}
63
64pub struct CreatePresignedUploadTask {
66 pub name: String,
67 pub mime: String,
68 pub document_box: DocumentBoxScopeRaw,
69 pub folder_id: FolderId,
70 pub size: i32,
71 pub file_key: String,
72 pub created_by: Option<UserId>,
73 pub expires_at: DateTime<Utc>,
74 pub parent_id: Option<FileId>,
75 pub processing_config: Option<serde_json::Value>,
76}
77
78impl PresignedUploadTask {
79 pub async fn create(
81 db: impl DbExecutor<'_>,
82 create: CreatePresignedUploadTask,
83 ) -> DbResult<PresignedUploadTask> {
84 let id = Uuid::new_v4();
85 let created_at = Utc::now();
86
87 let task = PresignedUploadTask {
88 id,
89 status: PresignedTaskStatus::Pending,
90 name: create.name,
92 mime: create.mime,
93 size: create.size,
94 document_box: create.document_box,
96 folder_id: create.folder_id,
97 file_key: create.file_key,
98 created_at,
100 expires_at: create.expires_at,
101 created_by: create.created_by,
102
103 parent_id: create.parent_id,
104 processing_config: create.processing_config.map(Json),
105 };
106
107 let status_json =
108 serde_json::to_value(&task.status).map_err(|err| DbErr::Encode(Box::new(err)))?;
109 let processing_config_json = task.processing_config.clone();
110
111 sqlx::query(
112 r#"
113 INSERT INTO "docbox_presigned_upload_tasks" (
114 "id",
115 "status",
116 "name",
117 "mime",
118 "size",
119 "document_box",
120 "folder_id",
121 "file_key",
122 "created_at",
123 "expires_at",
124 "created_by",
125 "parent_id",
126 "processing_config"
127 ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)
128 "#,
129 )
130 .bind(task.id)
131 .bind(status_json)
132 .bind(task.name.as_str())
133 .bind(task.mime.clone())
134 .bind(task.size)
135 .bind(task.document_box.as_str())
136 .bind(task.folder_id)
137 .bind(task.file_key.as_str())
138 .bind(task.created_at)
139 .bind(task.expires_at)
140 .bind(task.created_by.clone())
141 .bind(task.parent_id)
142 .bind(processing_config_json)
143 .execute(db)
144 .await?;
145
146 Ok(task)
147 }
148
149 pub async fn set_status(
150 &mut self,
151 db: impl DbExecutor<'_>,
152 status: PresignedTaskStatus,
153 ) -> DbResult<()> {
154 let status_json =
155 serde_json::to_value(&status).map_err(|err| DbErr::Encode(Box::new(err)))?;
156
157 sqlx::query(r#"UPDATE "docbox_presigned_upload_tasks" SET "status" = $1 WHERE "id" = $2"#)
158 .bind(status_json)
159 .bind(self.id)
160 .execute(db)
161 .await?;
162
163 self.status = status;
164 Ok(())
165 }
166
167 pub async fn find(
169 db: impl DbExecutor<'_>,
170 scope: &DocumentBoxScopeRaw,
171 task_id: PresignedUploadTaskId,
172 ) -> DbResult<Option<PresignedUploadTask>> {
173 sqlx::query_as(
174 r#"SELECT * FROM "docbox_presigned_upload_tasks"
175 WHERE "id" = $1 AND "document_box" = $2"#,
176 )
177 .bind(task_id)
178 .bind(scope)
179 .fetch_optional(db)
180 .await
181 }
182
183 pub async fn find_expired(
185 db: impl DbExecutor<'_>,
186 current_date: DateTime<Utc>,
187 ) -> DbResult<Vec<PresignedUploadTask>> {
188 sqlx::query_as(r#"SELECT * FROM "docbox_presigned_upload_tasks" WHERE "expires_at" < $1"#)
189 .bind(current_date)
190 .fetch_all(db)
191 .await
192 }
193
194 pub async fn find_by_file_key(
196 db: impl DbExecutor<'_>,
197 file_key: &str,
198 ) -> DbResult<Option<PresignedUploadTask>> {
199 sqlx::query_as(r#"SELECT * FROM "docbox_presigned_upload_tasks" WHERE "file_key" = $1"#)
200 .bind(file_key)
201 .fetch_optional(db)
202 .await
203 }
204
205 pub async fn delete(db: impl DbExecutor<'_>, task_id: PresignedUploadTaskId) -> DbResult<()> {
207 sqlx::query(r#"DELETE FROM "docbox_presigned_upload_tasks" WHERE "id" = $1"#)
208 .bind(task_id)
209 .execute(db)
210 .await?;
211
212 Ok(())
213 }
214}