Finished image upload

This commit is contained in:
Franklin 2023-04-23 11:42:51 -04:00
parent 1cfe059b5c
commit ca01d8d8ab
5 changed files with 291 additions and 44 deletions

238
Cargo.lock generated
View File

@ -83,6 +83,44 @@ dependencies = [
"syn 1.0.109", "syn 1.0.109",
] ]
[[package]]
name = "actix-multipart"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dee489e3c01eae4d1c35b03c4493f71cb40d93f66b14558feb1b1a807671cc4e"
dependencies = [
"actix-multipart-derive",
"actix-utils",
"actix-web",
"bytes",
"derive_more",
"futures-core",
"futures-util",
"httparse",
"local-waker",
"log",
"memchr",
"mime",
"serde",
"serde_json",
"serde_plain",
"tempfile",
"tokio",
]
[[package]]
name = "actix-multipart-derive"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ec592f234db8a253cf80531246a4407c8a70530423eea80688a6c5a44a110e7"
dependencies = [
"darling",
"parse-size",
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]] [[package]]
name = "actix-router" name = "actix-router"
version = "0.5.1" version = "0.5.1"
@ -963,6 +1001,41 @@ dependencies = [
"syn 2.0.15", "syn 2.0.15",
] ]
[[package]]
name = "darling"
version = "0.14.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850"
dependencies = [
"darling_core",
"darling_macro",
]
[[package]]
name = "darling_core"
version = "0.14.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0"
dependencies = [
"fnv",
"ident_case",
"proc-macro2",
"quote",
"strsim",
"syn 1.0.109",
]
[[package]]
name = "darling_macro"
version = "0.14.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e"
dependencies = [
"darling_core",
"quote",
"syn 1.0.109",
]
[[package]] [[package]]
name = "derive_more" name = "derive_more"
version = "0.99.17" version = "0.99.17"
@ -1057,6 +1130,27 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "errno"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a"
dependencies = [
"errno-dragonfly",
"libc",
"windows-sys 0.48.0",
]
[[package]]
name = "errno-dragonfly"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
dependencies = [
"cc",
"libc",
]
[[package]] [[package]]
name = "event-listener" name = "event-listener"
version = "2.5.3" version = "2.5.3"
@ -1106,6 +1200,21 @@ dependencies = [
"regex", "regex",
] ]
[[package]]
name = "futures"
version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40"
dependencies = [
"futures-channel",
"futures-core",
"futures-executor",
"futures-io",
"futures-sink",
"futures-task",
"futures-util",
]
[[package]] [[package]]
name = "futures-channel" name = "futures-channel"
version = "0.3.28" version = "0.3.28"
@ -1122,6 +1231,17 @@ version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c"
[[package]]
name = "futures-executor"
version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0"
dependencies = [
"futures-core",
"futures-task",
"futures-util",
]
[[package]] [[package]]
name = "futures-intrusive" name = "futures-intrusive"
version = "0.4.2" version = "0.4.2"
@ -1139,6 +1259,17 @@ version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964"
[[package]]
name = "futures-macro"
version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.15",
]
[[package]] [[package]]
name = "futures-sink" name = "futures-sink"
version = "0.3.28" version = "0.3.28"
@ -1157,8 +1288,10 @@ version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533"
dependencies = [ dependencies = [
"futures-channel",
"futures-core", "futures-core",
"futures-io", "futures-io",
"futures-macro",
"futures-sink", "futures-sink",
"futures-task", "futures-task",
"memchr", "memchr",
@ -1243,6 +1376,12 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "hermit-abi"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
[[package]] [[package]]
name = "hex" name = "hex"
version = "0.4.3" version = "0.4.3"
@ -1364,6 +1503,12 @@ dependencies = [
"cxx-build", "cxx-build",
] ]
[[package]]
name = "ident_case"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]] [[package]]
name = "idna" name = "idna"
version = "0.3.0" version = "0.3.0"
@ -1393,6 +1538,17 @@ dependencies = [
"cfg-if", "cfg-if",
] ]
[[package]]
name = "io-lifetimes"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220"
dependencies = [
"hermit-abi 0.3.1",
"libc",
"windows-sys 0.48.0",
]
[[package]] [[package]]
name = "ipnet" name = "ipnet"
version = "2.7.2" version = "2.7.2"
@ -1419,6 +1575,7 @@ name = "jl-backend"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"actix-cors", "actix-cors",
"actix-multipart",
"actix-web", "actix-web",
"actix-web-utils", "actix-web-utils",
"aws-config", "aws-config",
@ -1429,6 +1586,7 @@ dependencies = [
"dotenvy", "dotenvy",
"dotenvy_macro", "dotenvy_macro",
"err", "err",
"futures",
"jl-types", "jl-types",
"rand", "rand",
"reqwest", "reqwest",
@ -1499,6 +1657,12 @@ dependencies = [
"cc", "cc",
] ]
[[package]]
name = "linux-raw-sys"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36eb31c1778188ae1e64398743890d0877fef36d11521ac60406b42016e8c2cf"
[[package]] [[package]]
name = "local-channel" name = "local-channel"
version = "0.1.3" version = "0.1.3"
@ -1620,7 +1784,7 @@ version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
dependencies = [ dependencies = [
"hermit-abi", "hermit-abi 0.2.6",
"libc", "libc",
] ]
@ -1672,7 +1836,7 @@ dependencies = [
"cfg-if", "cfg-if",
"instant", "instant",
"libc", "libc",
"redox_syscall", "redox_syscall 0.2.16",
"smallvec", "smallvec",
"winapi", "winapi",
] ]
@ -1685,11 +1849,17 @@ checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"libc", "libc",
"redox_syscall", "redox_syscall 0.2.16",
"smallvec", "smallvec",
"windows-sys 0.45.0", "windows-sys 0.45.0",
] ]
[[package]]
name = "parse-size"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "944553dd59c802559559161f9816429058b869003836120e262e8caec061b7ae"
[[package]] [[package]]
name = "parse-zoneinfo" name = "parse-zoneinfo"
version = "0.3.0" version = "0.3.0"
@ -1850,6 +2020,15 @@ dependencies = [
"bitflags", "bitflags",
] ]
[[package]]
name = "redox_syscall"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
dependencies = [
"bitflags",
]
[[package]] [[package]]
name = "redox_users" name = "redox_users"
version = "0.4.3" version = "0.4.3"
@ -1857,7 +2036,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
dependencies = [ dependencies = [
"getrandom", "getrandom",
"redox_syscall", "redox_syscall 0.2.16",
"thiserror", "thiserror",
] ]
@ -1941,6 +2120,20 @@ dependencies = [
"semver", "semver",
] ]
[[package]]
name = "rustix"
version = "0.37.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b864d3c18a5785a05953adeed93e2dca37ed30f18e69bba9f30079d51f363f"
dependencies = [
"bitflags",
"errno",
"io-lifetimes",
"libc",
"linux-raw-sys",
"windows-sys 0.48.0",
]
[[package]] [[package]]
name = "rustls" name = "rustls"
version = "0.20.8" version = "0.20.8"
@ -2071,6 +2264,15 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "serde_plain"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6018081315db179d0ce57b1fe4b62a12a0028c9cf9bbef868c9cf477b3c34ae"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "serde_urlencoded" name = "serde_urlencoded"
version = "0.7.1" version = "0.7.1"
@ -2270,6 +2472,12 @@ dependencies = [
"unicode-normalization", "unicode-normalization",
] ]
[[package]]
name = "strsim"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]] [[package]]
name = "subtle" name = "subtle"
version = "2.4.1" version = "2.4.1"
@ -2298,6 +2506,19 @@ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]]
name = "tempfile"
version = "3.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998"
dependencies = [
"cfg-if",
"fastrand",
"redox_syscall 0.3.5",
"rustix",
"windows-sys 0.45.0",
]
[[package]] [[package]]
name = "termcolor" name = "termcolor"
version = "1.2.0" version = "1.2.0"
@ -2807,6 +3028,15 @@ dependencies = [
"windows-targets 0.42.2", "windows-targets 0.42.2",
] ]
[[package]]
name = "windows-sys"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
"windows-targets 0.48.0",
]
[[package]] [[package]]
name = "windows-targets" name = "windows-targets"
version = "0.42.2" version = "0.42.2"

View File

@ -9,6 +9,7 @@ edition = "2021"
dotenvy = "0.15.6" dotenvy = "0.15.6"
dotenvy_macro = "0.15.1" dotenvy_macro = "0.15.1"
tokio = {version = "1.20.1", features = ["full"]} tokio = {version = "1.20.1", features = ["full"]}
futures = "0.3.28"
reqwest = { version = "0.11.11", features = ["rustls-tls", "json", "blocking"], default-features = false } reqwest = { version = "0.11.11", features = ["rustls-tls", "json", "blocking"], default-features = false }
chrono = "0.4.23" chrono = "0.4.23"
chrono-tz = "0.8" chrono-tz = "0.8"
@ -18,8 +19,10 @@ sqlx = { version = "0.6.0", features = [ "runtime-tokio-rustls", "postgres", "ch
dotenv = { version = "0.15.0" } dotenv = { version = "0.15.0" }
actix-web = {version = "4.1.0"} actix-web = {version = "4.1.0"}
actix-cors = "0.6.2" actix-cors = "0.6.2"
actix-multipart = "0.6.0"
uuid = { version = "1.3.0", features = ["v4", "fast-rng", "macro-diagnostics"] } uuid = { version = "1.3.0", features = ["v4", "fast-rng", "macro-diagnostics"] }
actix-web-utils = { git = "https://git.franklinblanco.dev/franklinblanco/actix-web-utils.git" } actix-web-utils = { git = "https://git.franklinblanco.dev/franklinblanco/actix-web-utils.git" }
err = { git = "https://git.franklinblanco.dev/franklinblanco/err.git" } err = { git = "https://git.franklinblanco.dev/franklinblanco/err.git" }

View File

@ -1,5 +1,6 @@
use std::sync::Arc; use std::sync::Arc;
use actix_multipart::Multipart;
use actix_web::{ use actix_web::{
delete, get, post, put, delete, get, post, put,
web::{self, Path}, web::{self, Path},
@ -10,17 +11,20 @@ use jl_types::{
agent::Agent, contact::Contact, count::Count, location::Location, project::Project, agent::Agent, contact::Contact, count::Count, location::Location, project::Project,
unit::Unit, unit::Unit,
}, },
dto::payloads::{ dto::{
agent::{NewAgentPayload, UpdateAgentPayload}, payloads::{
location::NewLocationPayload, agent::{NewAgentPayload, UpdateAgentPayload},
project::{NewProjectPayload, UpdateProjectPayload}, location::NewLocationPayload,
unit::{NewUnitPayload, UpdateUnitPayload}, project::{NewProjectPayload, UpdateProjectPayload},
}, unit::{NewUnitPayload, UpdateUnitPayload},
},
item::Item,
}
}; };
use sqlx::PgPool; use sqlx::PgPool;
use uuid::Uuid; use uuid::Uuid;
use crate::{services, utils::s3::Item}; use crate::{services};
#[post("/agent")] #[post("/agent")]
pub async fn create_new_agent_profile( pub async fn create_new_agent_profile(
@ -125,7 +129,7 @@ pub async fn get_all_contacts(db_conn: web::Data<Arc<PgPool>>) -> TypedHttpRespo
services::admin::get_all_contacts(&db_conn).await services::admin::get_all_contacts(&db_conn).await
} }
#[post("images/{item}/{id}/{file_name}")] #[post("images/{item}/{id}")]
pub async fn upload_image(aws_client: web::Data<Arc<aws_sdk_s3::Client>>, path_vars: Path<(Item, Uuid, String)>) -> TypedHttpResponse<String> { pub async fn upload_image(aws_client: web::Data<Arc<aws_sdk_s3::Client>>, path_vars: Path<(Item, Uuid)>, multipart: Multipart) -> TypedHttpResponse<String> {
services::admin::upload_image(&aws_client, path_vars.0.clone(), path_vars.1, path_vars.2.clone()).await services::admin::upload_image(&aws_client, path_vars.0.clone(), path_vars.1, multipart).await
} }

View File

@ -1,3 +1,4 @@
use actix_multipart::Multipart;
use actix_web_utils::extensions::typed_response::TypedHttpResponse; use actix_web_utils::extensions::typed_response::TypedHttpResponse;
use err::MessageResource; use err::MessageResource;
use jl_types::{ use jl_types::{
@ -5,18 +6,23 @@ use jl_types::{
agent::Agent, contact::Contact, count::Count, location::Location, project::Project, agent::Agent, contact::Contact, count::Count, location::Location, project::Project,
unit::Unit, unit::Unit,
}, },
dto::payloads::{ dto::{
agent::{NewAgentPayload, UpdateAgentPayload}, payloads::{
location::NewLocationPayload, agent::{NewAgentPayload, UpdateAgentPayload},
project::{NewProjectPayload, UpdateProjectPayload}, location::NewLocationPayload,
unit::{NewUnitPayload, UpdateUnitPayload}, project::{NewProjectPayload, UpdateProjectPayload},
}, unit::{NewUnitPayload, UpdateUnitPayload},
},
item::Item,
}
}; };
use futures::StreamExt as _;
use sqlx::PgPool; use sqlx::PgPool;
use uuid::Uuid; use uuid::Uuid;
use crate::{dao, handle_db_read_op, handle_db_write_op, handle_tx, success, unwrap_or_not_found, utils::s3::{Item, self}}; use crate::{dao, handle_db_read_op, handle_db_write_op, handle_tx, success, unwrap_or_not_found, utils::s3::{self}};
// //
// Insert Methods // Insert Methods
@ -197,8 +203,30 @@ pub async fn get_contact_count(conn: &PgPool) -> TypedHttpResponse<Count> {
success!(count) success!(count)
} }
pub async fn upload_image(client: &aws_sdk_s3::Client, item: Item, id: Uuid, file_name: String) -> TypedHttpResponse<String> { pub async fn upload_image(client: &aws_sdk_s3::Client, item: Item, id: Uuid, mut multipart: Multipart) -> TypedHttpResponse<String> {
match s3::upload_image(client, id, item, file_name).await { let mut bytes: Vec<u8> = Vec::new();
match multipart.next().await {
Some(item) => {
let mut field = match item {
Ok(field) => field,
Err(error) => return TypedHttpResponse::return_standard_error(400, MessageResource::new_from_string(error.to_string())),
};
// Field in turn is stream of *Bytes* object
while let Some(chunk) = field.next().await {
bytes.append(&mut chunk.unwrap().to_vec());
}
},
None => {
return TypedHttpResponse::return_empty_response(400);
}
}
if bytes.is_empty() {
return TypedHttpResponse::return_empty_response(400);
}
match s3::upload_image(client, id, item, Uuid::new_v4().to_string(), bytes).await {
Ok(url) => success!(url), Ok(url) => success!(url),
Err(error) => return TypedHttpResponse::return_standard_error(400, MessageResource::new_from_string(error.to_string())), Err(error) => return TypedHttpResponse::return_standard_error(400, MessageResource::new_from_string(error.to_string())),
}; };

View File

@ -1,31 +1,12 @@
use std::fmt::Display; use jl_types::dto::item::Item;
use aws_sdk_s3::{ use aws_sdk_s3::{
error::SdkError, error::SdkError,
operation::put_object::{PutObjectError}, operation::put_object::{PutObjectError},
primitives::ByteStream, primitives::ByteStream,
Client, Client,
}; };
use serde::{Deserialize, Serialize};
use uuid::Uuid; use uuid::Uuid;
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, PartialOrd, Ord, Default)]
pub enum Item {
#[default]
Project,
Unit,
Agent,
}
impl Display for Item {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Item::Project => write!(f, "Project"),
Item::Unit => write!(f, "Unit"),
Item::Agent => write!(f, "Agent"),
}
}
}
pub async fn init_aws_client() -> Client { pub async fn init_aws_client() -> Client {
dotenv::dotenv().expect("Failed loading env"); dotenv::dotenv().expect("Failed loading env");
let config = aws_config::load_from_env().await; let config = aws_config::load_from_env().await;
@ -37,13 +18,14 @@ pub async fn upload_image(
id: Uuid, id: Uuid,
item: Item, item: Item,
file_name: String, file_name: String,
multipart: Vec<u8>,
) -> Result<String, SdkError<PutObjectError>> { ) -> Result<String, SdkError<PutObjectError>> {
let path = format!("jl-images/{item}/{id}/{file_name}"); let path = format!("jl-images/{item}/{id}/{file_name}");
match client match client
.put_object() .put_object()
.bucket("jorge-ledesma-bucket") .bucket("jorge-ledesma-bucket")
.key(path.clone()) .key(path.clone())
.body(ByteStream::from_static("Hey there".as_bytes())) .body(ByteStream::from(multipart))
.acl(aws_sdk_s3::types::ObjectCannedAcl::PublicRead) .acl(aws_sdk_s3::types::ObjectCannedAcl::PublicRead)
.send() .send()
.await { .await {