download pictures in the frontend and then create data-urls from blobs
This commit is contained in:
@@ -22,8 +22,9 @@ serde_json = "1.0"
|
||||
common = { path = "../common" }
|
||||
uuid = { version = "1", features = ["v4", "serde"] }
|
||||
rust-s3 = "0.32.3"
|
||||
futures = "0.3"
|
||||
|
||||
[dependencies.mongodb]
|
||||
version = "2.2.0"
|
||||
version = "2.4.0"
|
||||
default-features = false
|
||||
features = ["async-std-runtime"]
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
pub mod photos_api;
|
||||
pub mod photo_api;
|
||||
pub mod user_api;
|
||||
|
||||
149
backend/src/api/photo_api.rs
Normal file
149
backend/src/api/photo_api.rs
Normal file
@@ -0,0 +1,149 @@
|
||||
use actix_session::Session;
|
||||
use actix_web::Result;
|
||||
use actix_web::{
|
||||
get, post,
|
||||
web::{Data, Json},
|
||||
HttpResponse, Responder,
|
||||
};
|
||||
use actix_web_lab::__reexports::tokio;
|
||||
use common::OutputPicture;
|
||||
use mongodb::bson::oid::ObjectId;
|
||||
use s3::bucket::Bucket;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{models::photo_model::Photo, repository::mongodb_repo::MongoRepo};
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct PhotosWrapper {
|
||||
photos: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct DetailPhoto {
|
||||
key: String,
|
||||
width: u32,
|
||||
height: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct DetailPhotoWrapper {
|
||||
photos: Vec<DetailPhoto>,
|
||||
}
|
||||
|
||||
#[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<T>(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<MongoRepo>,
|
||||
bucket: Data<Bucket>,
|
||||
request: Json<PhotosWrapper>,
|
||||
session: Session,
|
||||
) -> Result<HttpResponse, actix_web::Error> {
|
||||
let Some(user_id) = session.get::<String>("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!("/{}/{}", user_id, x), 86400, None)
|
||||
.unwrap(),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(HttpResponse::Ok().json(PhotosUrlsWrapper { photos }))
|
||||
}
|
||||
|
||||
#[post("/api/photos/upload/done")]
|
||||
pub async fn upload_done(
|
||||
db: Data<MongoRepo>,
|
||||
request: Json<DetailPhotoWrapper>,
|
||||
session: Session,
|
||||
) -> Result<HttpResponse, actix_web::Error> {
|
||||
let Some(user_id) = session.get::<String>("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"));
|
||||
};
|
||||
|
||||
for p in &request.photos {
|
||||
db.create_photo(Photo {
|
||||
id: None,
|
||||
user_id: ObjectId::parse_str(&user_id).unwrap(),
|
||||
key: p.key.clone(),
|
||||
width: p.width,
|
||||
height: p.height,
|
||||
})
|
||||
.await
|
||||
.map_err(e500)?;
|
||||
}
|
||||
|
||||
Ok(HttpResponse::Ok().json(&request))
|
||||
}
|
||||
|
||||
#[get("/api/photos/get")]
|
||||
async fn get_user_photos(
|
||||
db: Data<MongoRepo>,
|
||||
bucket: Data<Bucket>,
|
||||
session: Session,
|
||||
) -> Result<impl Responder> {
|
||||
let Some(user_id) = session.get::<String>("user_id").map_err(e500)? else {
|
||||
return Ok(HttpResponse::Unauthorized().body("Not authorized"));
|
||||
};
|
||||
|
||||
let Ok(user) = db.get_user(&user_id).await else {
|
||||
return Ok(HttpResponse::Unauthorized().body("Not authorized"));
|
||||
};
|
||||
let photos: Vec<OutputPicture> = db
|
||||
.get_photos(&user_id)
|
||||
.await
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|p| {
|
||||
// let mut w: u32 = x.width.try_into().unwrap();
|
||||
// let mut h: u32 = x.height.try_into().unwrap();
|
||||
// if let Some(o) = &x.orientation {
|
||||
// if o == "Rotate 270 CW" {
|
||||
// swap(&mut w, &mut h);
|
||||
// }
|
||||
// }
|
||||
let url = bucket
|
||||
.presign_get(format!("/{}/{}", user_id, p.key.clone()), 86400, None)
|
||||
.unwrap();
|
||||
|
||||
OutputPicture {
|
||||
thumbnail: Some(url),
|
||||
// thumbnail: Some(String::from("https://upload.wikimedia.org/wikipedia/commons/thumb/b/b4/Mila_Kunis_2018.jpg/220px-Mila_Kunis_2018.jpg")),
|
||||
// thumbnail: x.thumbnail.clone(),
|
||||
// width: w,
|
||||
// height: h,
|
||||
width: p.width,
|
||||
height: p.height,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(HttpResponse::Ok().json(photos))
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
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<String>,
|
||||
}
|
||||
|
||||
#[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<T>(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<MongoRepo>,
|
||||
bucket: Data<Bucket>,
|
||||
request: Json<PhotosWrapper>,
|
||||
session: Session,
|
||||
) -> Result<HttpResponse, actix_web::Error> {
|
||||
let Some(user_id) = session.get::<String>("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 }))
|
||||
}
|
||||
@@ -2,7 +2,8 @@ mod api;
|
||||
mod models;
|
||||
mod repository;
|
||||
|
||||
use api::photos_api::get_presigned_post_urls;
|
||||
use api::photo_api::get_presigned_post_urls;
|
||||
use api::photo_api::get_user_photos;
|
||||
use api::user_api::{create_user, delete_user, get_user, is_logged_in, login, update_user};
|
||||
use common::OutputPicture;
|
||||
use repository::mongodb_repo::MongoRepo;
|
||||
@@ -31,30 +32,7 @@ use actix_web::{
|
||||
use actix_web::{Responder, Result};
|
||||
use actix_web_lab::web::spa;
|
||||
|
||||
#[get("/api/pictures/")]
|
||||
async fn get_pictures() -> Result<impl Responder> {
|
||||
let pics: Vec<OutputPicture> = (1..15)
|
||||
.map(|_| {
|
||||
// let mut w: u32 = x.width.try_into().unwrap();
|
||||
// let mut h: u32 = x.height.try_into().unwrap();
|
||||
// if let Some(o) = &x.orientation {
|
||||
// if o == "Rotate 270 CW" {
|
||||
// swap(&mut w, &mut h);
|
||||
// }
|
||||
// }
|
||||
OutputPicture {
|
||||
thumbnail: Some(String::from("https://upload.wikimedia.org/wikipedia/commons/thumb/b/b4/Mila_Kunis_2018.jpg/220px-Mila_Kunis_2018.jpg")),
|
||||
// thumbnail: x.thumbnail.clone(),
|
||||
// width: w,
|
||||
// height: h,
|
||||
width: 100,
|
||||
height: 100,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(web::Json(pics))
|
||||
}
|
||||
use crate::api::photo_api::upload_done;
|
||||
|
||||
#[actix_web::main]
|
||||
async fn main() -> std::io::Result<()> {
|
||||
@@ -119,7 +97,8 @@ async fn main() -> std::io::Result<()> {
|
||||
.app_data(bucket.clone())
|
||||
.wrap(middleware::Logger::default())
|
||||
.service(get_presigned_post_urls)
|
||||
.service(get_pictures)
|
||||
.service(upload_done)
|
||||
.service(get_user_photos)
|
||||
.service(is_logged_in)
|
||||
.service(login)
|
||||
.service(create_user)
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
pub mod photo_model;
|
||||
pub mod user_model;
|
||||
|
||||
12
backend/src/models/photo_model.rs
Normal file
12
backend/src/models/photo_model.rs
Normal file
@@ -0,0 +1,12 @@
|
||||
use mongodb::bson::oid::ObjectId;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct Photo {
|
||||
#[serde(rename = "_id", skip_serializing_if = "Option::is_none")]
|
||||
pub id: Option<ObjectId>,
|
||||
pub user_id: ObjectId,
|
||||
pub key: String,
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
}
|
||||
@@ -2,15 +2,19 @@ use std::env;
|
||||
extern crate dotenv;
|
||||
use dotenv::dotenv;
|
||||
|
||||
use crate::models::photo_model::Photo;
|
||||
use crate::models::user_model::User;
|
||||
use futures::stream::TryStreamExt;
|
||||
use mongodb::{
|
||||
bson::{doc, extjson::de::Error, oid::ObjectId},
|
||||
bson::{doc, oid::ObjectId},
|
||||
error::Error,
|
||||
results::{DeleteResult, InsertOneResult, UpdateResult},
|
||||
Client, Collection,
|
||||
};
|
||||
|
||||
pub struct MongoRepo {
|
||||
col: Collection<User>,
|
||||
user_col: Collection<User>,
|
||||
photo_col: Collection<Photo>,
|
||||
}
|
||||
|
||||
impl MongoRepo {
|
||||
@@ -22,8 +26,12 @@ impl MongoRepo {
|
||||
};
|
||||
let client = Client::with_uri_str(uri).await.unwrap();
|
||||
let db = client.database("photos");
|
||||
let col: Collection<User> = db.collection("User");
|
||||
MongoRepo { col }
|
||||
let user_col: Collection<User> = db.collection("User");
|
||||
let photo_col: Collection<Photo> = db.collection("Photo");
|
||||
MongoRepo {
|
||||
user_col,
|
||||
photo_col,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn create_user(&self, new_user: User) -> Result<InsertOneResult, Error> {
|
||||
@@ -34,7 +42,7 @@ impl MongoRepo {
|
||||
password: new_user.password,
|
||||
};
|
||||
let user = self
|
||||
.col
|
||||
.user_col
|
||||
.insert_one(new_doc, None)
|
||||
.await
|
||||
.ok()
|
||||
@@ -46,7 +54,7 @@ impl MongoRepo {
|
||||
let obj_id = ObjectId::parse_str(id).unwrap();
|
||||
let filter = doc! {"_id": obj_id};
|
||||
let user_detail = self
|
||||
.col
|
||||
.user_col
|
||||
.find_one(filter, None)
|
||||
.await
|
||||
.ok()
|
||||
@@ -57,7 +65,7 @@ impl MongoRepo {
|
||||
pub async fn get_user_from_email(&self, email: &String) -> Result<User, Error> {
|
||||
let filter = doc! {"email": email};
|
||||
let user_detail = self
|
||||
.col
|
||||
.user_col
|
||||
.find_one(filter, None)
|
||||
.await
|
||||
.ok()
|
||||
@@ -78,7 +86,7 @@ impl MongoRepo {
|
||||
},
|
||||
};
|
||||
let updated_doc = self
|
||||
.col
|
||||
.user_col
|
||||
.update_one(filter, new_doc, None)
|
||||
.await
|
||||
.ok()
|
||||
@@ -90,11 +98,44 @@ impl MongoRepo {
|
||||
let obj_id = ObjectId::parse_str(id).unwrap();
|
||||
let filter = doc! {"_id": obj_id};
|
||||
let user_detail = self
|
||||
.col
|
||||
.user_col
|
||||
.delete_one(filter, None)
|
||||
.await
|
||||
.ok()
|
||||
.expect("Error deleting user");
|
||||
Ok(user_detail)
|
||||
}
|
||||
|
||||
pub async fn create_photo(&self, new_photo: Photo) -> Result<InsertOneResult, Error> {
|
||||
let new_doc = Photo {
|
||||
id: None,
|
||||
user_id: new_photo.user_id,
|
||||
key: new_photo.key,
|
||||
width: new_photo.width,
|
||||
height: new_photo.height,
|
||||
};
|
||||
let photo = self
|
||||
.photo_col
|
||||
.insert_one(new_doc, None)
|
||||
.await
|
||||
.ok()
|
||||
.expect("Error creating user");
|
||||
Ok(photo)
|
||||
}
|
||||
|
||||
pub async fn get_photos(&self, id: &String) -> Result<Vec<Photo>, Error> {
|
||||
let obj_id = ObjectId::parse_str(id).unwrap();
|
||||
let filter = doc! {"user_id": obj_id};
|
||||
let mut cursor = self
|
||||
.photo_col
|
||||
.find(filter, None)
|
||||
.await
|
||||
.ok()
|
||||
.expect("Error getting photos cursor");
|
||||
let mut photos = Vec::new();
|
||||
while let Some(photo) = cursor.try_next().await? {
|
||||
photos.push(photo);
|
||||
}
|
||||
Ok(photos)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user