diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..f1c96fe --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,16 @@ +{ + "cSpell.words": [ + "actix", + "bindgen", + "creds", + "dotenv", + "Gloo", + "minio", + "noopener", + "noreferrer", + "onclick", + "ondragover", + "ondrop", + "onresize" + ] +} diff --git a/backend/src/api/photos_api.rs b/backend/src/api/photos_api.rs new file mode 100644 index 0000000..7d46df7 --- /dev/null +++ b/backend/src/api/photos_api.rs @@ -0,0 +1,57 @@ +use actix_session::Session; +use actix_web::{ + post, + web::{Data, Json}, + HttpResponse, +}; +use s3::bucket::Bucket; +use serde::{Deserialize, Serialize}; + +use crate::repository::mongodb_repo::MongoRepo; + +#[derive(Debug, Deserialize, Serialize)] +pub struct PhotosWrapper { + photos: Vec, +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct PhotosUrlsWrapper { + photos: Vec<(String, String)>, +} + +// Return an opaque 500 while preserving the error's root cause for logging. +fn e500(e: T) -> actix_web::Error +where + T: std::fmt::Debug + std::fmt::Display + 'static, +{ + actix_web::error::ErrorInternalServerError(e) +} + +#[post("/api/photos/upload")] +pub async fn get_presigned_post_urls( + db: Data, + bucket: Data, + request: Json, + session: Session, +) -> Result { + let Some(user_id) = session.get::("user_id").map_err(e500)? else { + return Ok(HttpResponse::Unauthorized().body("Not authorized")); + }; + + let Ok(_) = db.get_user(&user_id).await else { + return Ok(HttpResponse::Unauthorized().body("Not authorized")); + }; + + let photos: Vec<(String, String)> = request + .photos + .iter() + .map(|x| { + ( + x.clone(), + bucket.presign_put(format!("/{x}"), 86400, None).unwrap(), + ) + }) + .collect(); + + Ok(HttpResponse::Ok().json(PhotosUrlsWrapper { photos })) +}