|
1 | | -use std::path::PathBuf; |
| 1 | +use std::path::{Path, PathBuf}; |
2 | 2 |
|
3 | 3 | use actix_multipart::form::MultipartForm; |
4 | 4 | use actix_web::web; |
| 5 | +use bytes::Bytes; |
5 | 6 | use chrono::Local; |
6 | 7 | use color_eyre::{Report, Result}; |
7 | | -use color_eyre::eyre::Context; |
| 8 | +use color_eyre::eyre::{Context, OptionExt}; |
8 | 9 | use diesel::{ExpressionMethods, insert_into, update}; |
9 | 10 | use diesel::prelude::*; |
10 | 11 | use diesel_async::{AsyncConnection, RunQueryDsl}; |
11 | 12 | use diesel_async::scoped_futures::ScopedFutureExt; |
12 | 13 | use itertools::Itertools; |
13 | | -use log::debug; |
| 14 | +use log::{debug, error, info}; |
| 15 | +use mime_guess::from_ext; |
| 16 | +use reqwest::Client; |
14 | 17 | use serde::{Deserialize, Serialize}; |
15 | 18 | use uuid::Uuid; |
16 | 19 |
|
17 | 20 | use crate::config::DbPool; |
18 | 21 | use crate::endpoints::delete::DeleteDocumentRequest; |
19 | 22 | use crate::endpoints::filter::DocumentFilterRequest; |
20 | | -use crate::endpoints::save::SaveDocumentRequest; |
| 23 | +use crate::endpoints::save::{SaveDocumentByUrlRequest, SaveDocumentRequest}; |
21 | 24 | use crate::EnvironmentState; |
22 | 25 | use crate::models::{Content, DeleteContent, DeleteDocument, Document, NewContent, NewDocument}; |
23 | | -use crate::operations::fs::{generate_path_by_uuid, generate_url_by_uuid, get_extension_and_file_name, move_file, read_content_file_to_base64}; |
| 26 | +use crate::operations::fs::{delete_file, generate_path_by_uuid, generate_url_by_uuid, get_extension_and_file_name, get_file_name_in_url, move_file, read_content_bytes_to_base64, read_content_file_to_base64, save_file}; |
24 | 27 | use crate::schema::{content, document}; |
25 | 28 |
|
26 | 29 | mod fs; |
@@ -86,6 +89,101 @@ pub async fn save_document( |
86 | 89 | .await |
87 | 90 | } |
88 | 91 |
|
| 92 | +pub async fn save_document_by_url(params: SaveDocumentByUrlRequest, |
| 93 | + env_state: web::Data<EnvironmentState>, |
| 94 | + conn: &DbPool) -> Result<Uuid> { |
| 95 | + let conn = &mut conn.get().await?; |
| 96 | + let uuid_document = Uuid::new_v4(); |
| 97 | + debug!("Generate UUID: {uuid_document}, to save document"); |
| 98 | + |
| 99 | + |
| 100 | + let r = conn.transaction::<Uuid, Report, _>(|conn| { |
| 101 | + async move { |
| 102 | + let url_file = params.url_file.clone(); |
| 103 | + let file_name_in_url = get_file_name_in_url(&url_file) |
| 104 | + .ok_or_eyre(format!("Error not match filename in url: {:?} ", url_file))?; |
| 105 | + let (filename, extension) = get_extension_and_file_name(file_name_in_url); |
| 106 | + |
| 107 | + let mime_type = extension |
| 108 | + .map(|x| from_ext(x).first_or_octet_stream().to_string()); |
| 109 | + let info_download = download_file(¶ms.url_file).await?; |
| 110 | + let new_document = NewDocument { |
| 111 | + id_document: &uuid_document, |
| 112 | + name: filename, |
| 113 | + extension: extension.map(|x| x.to_string()), |
| 114 | + content_type: mime_type, |
| 115 | + application: ¶ms.application, |
| 116 | + create_username: ¶ms.username, |
| 117 | + }; |
| 118 | + insert_into(document::table) |
| 119 | + .values(new_document) |
| 120 | + .execute(conn) |
| 121 | + .await |
| 122 | + .with_context(|| "Error create document")?; |
| 123 | + |
| 124 | + if params.is_private_document { |
| 125 | + debug!("Document is private saving in database"); |
| 126 | + let content_file = read_content_bytes_to_base64(&info_download.content).await?; |
| 127 | + let content = NewContent { |
| 128 | + id_document: &uuid_document, |
| 129 | + data: &content_file, |
| 130 | + create_username: ¶ms.username, |
| 131 | + }; |
| 132 | + insert_into(content::table) |
| 133 | + .values(content) |
| 134 | + .execute(conn) |
| 135 | + .await |
| 136 | + .with_context(|| "Error save row in table content")?; |
| 137 | + } else { |
| 138 | + let new_path = PathBuf::from(generate_path_by_uuid( |
| 139 | + env_state.disk_storage_directory_path.clone(), |
| 140 | + extension.unwrap_or(""), |
| 141 | + uuid_document, |
| 142 | + )?); |
| 143 | + debug!("Document is public saving in {:?}", new_path); |
| 144 | + save_file(new_path, &info_download.content).await?; |
| 145 | + } |
| 146 | + debug!("Finish procces save document"); |
| 147 | + Ok(uuid_document) |
| 148 | + } |
| 149 | + .scope_boxed() |
| 150 | + }) |
| 151 | + .await; |
| 152 | + |
| 153 | + |
| 154 | + match r { |
| 155 | + Ok(e) => Ok(e), |
| 156 | + Err(e) => { |
| 157 | + //delete_file(&clone_new_path).await?; |
| 158 | + |
| 159 | + Err(e) |
| 160 | + } |
| 161 | + } |
| 162 | +} |
| 163 | + |
| 164 | + |
| 165 | +struct DownloadFileInfo { |
| 166 | + pub content: Bytes, |
| 167 | +} |
| 168 | +async fn download_file(url: &str) -> Result<DownloadFileInfo> { |
| 169 | + let client = Client::new(); |
| 170 | + let response = client.get(url).send().await?; |
| 171 | + |
| 172 | + if response.status().is_success() { |
| 173 | + let content = response.bytes().await?; |
| 174 | + info!("File downloaded"); |
| 175 | + Ok( |
| 176 | + DownloadFileInfo { |
| 177 | + content, |
| 178 | + } |
| 179 | + ) |
| 180 | + } else { |
| 181 | + error!("Error download file: {}", response.status()); |
| 182 | + Err(Report::msg("Error download file")) |
| 183 | + } |
| 184 | +} |
| 185 | + |
| 186 | + |
89 | 187 | pub async fn delete_document_and_content( |
90 | 188 | document_delete: web::Query<DeleteDocumentRequest>, |
91 | 189 | conn: web::Data<DbPool>, |
|
0 commit comments