use std::io::Write; use actix::Addr; use actix_multipart::{Field, Multipart}; use actix_web::web::Data; use actix_web::{post, web, Error, HttpResponse}; use bitque_data::msg::WsMsgUser; use bitque_data::{User, UserId}; use database_actor::authorize_user::AuthorizeUser; use database_actor::user_projects::CurrentUserProject; use database_actor::users::UpdateAvatarUrl; use database_actor::DbExecutor; use futures::executor::block_on; use futures::{StreamExt, TryStreamExt}; use tracing::{error, warn}; use websocket_actor::server::InnerMsg::BroadcastToChannel; use websocket_actor::server::WsServer; use crate::ServiceError; #[cfg(feature = "cloud-storage")] #[post("/")] pub async fn upload( mut payload: Multipart, db: Data>, ws: Data>, fs: Data>, cloud_storage: Data>, ) -> Result { let mut user_id: Option = None; let mut avatar_url: Option = None; while let Ok(Some(field)) = payload.try_next().await { let disposition = field.content_disposition(); if !disposition.is_form_data() { return Ok(HttpResponse::BadRequest().finish()); } match disposition.get_name() { Some("token") => { user_id = Some(handle_token(field, db.clone()).await?); } Some("avatar") => { let Some(id) = user_id else { warn!("user id not found. Not authorized"); return Ok(ServiceError::Unauthorized.into()); }; avatar_url = Some( crate::handlers::upload_avatar_image::handle_image( id, field, fs.clone(), cloud_storage.clone(), ) .await?, ); } _ => continue, }; } tracing::info!("user_id {user_id:?}"); tracing::info!("token {avatar_url:?}"); let user_id = match user_id { Some(id) => id, _ => return Ok(HttpResponse::Unauthorized().finish()), }; let project_id = match block_on(db.send(CurrentUserProject { user_id })) { Ok(Ok(user_project)) => user_project.project_id, _ => return Ok(HttpResponse::UnprocessableEntity().finish()), }; match (user_id, avatar_url) { (user_id, Some(avatar_url)) => { let user = update_user_avatar(user_id, avatar_url.clone(), db).await?; let Ok(_) = ws.send(BroadcastToChannel( project_id, WsMsgUser::AvatarUrlChanged(user.id, avatar_url).into(), )) .await else { return Ok(HttpResponse::UnprocessableEntity().finish()); }; Ok(HttpResponse::NoContent().finish()) } _ => Ok(HttpResponse::UnprocessableEntity().finish()), } } #[cfg(not(feature = "cloud-storage"))] #[post("/")] pub async fn upload( mut payload: Multipart, db: Data>, ws: Data>, fs: Data>, ) -> Result { let mut user_id: Option = None; let mut avatar_url: Option = None; while let Ok(Some(field)) = payload.try_next().await { let disposition = field.content_disposition(); if !disposition.is_form_data() { return Ok(HttpResponse::BadRequest().finish()); } match disposition.get_name() { Some("token") => { user_id = Some(handle_token(field, db.clone()).await?); } Some("avatar") => { let Some(id) = user_id else { return Ok(HttpResponse::Unauthorized().finish()); }; avatar_url = Some( crate::handlers::upload_avatar_image::handle_image(id, field, fs.clone()) .await?, ); } _ => continue, }; } let user_id = match user_id { Some(id) => id, _ => return Ok(HttpResponse::Unauthorized().finish()), }; let project_id = match block_on(db.send(CurrentUserProject { user_id })) { Ok(Ok(user_project)) => user_project.project_id, _ => return Ok(HttpResponse::UnprocessableEntity().finish()), }; match (user_id, avatar_url) { (user_id, Some(avatar_url)) => { let user = update_user_avatar(user_id, avatar_url.clone(), db).await?; if ws .send(BroadcastToChannel( project_id, WsMsg::User(WsMsgUser::AvatarUrlChanged(user.id, avatar_url)), )) .await .is_err() { return Ok(HttpResponse::UnprocessableEntity().finish()); }; Ok(HttpResponse::NoContent().finish()) } _ => Ok(HttpResponse::UnprocessableEntity().finish()), } } async fn update_user_avatar( user_id: UserId, new_url: String, db: Data>, ) -> Result { match db .send(UpdateAvatarUrl { user_id, avatar_url: Some(new_url), }) .await { Ok(Ok(user)) => Ok(user), Ok(Err(e)) => { error!("{:?}", e); Err(ServiceError::Unauthorized.into()) } Err(e) => { error!("{:?}", e); Err(ServiceError::Unauthorized.into()) } } } async fn handle_token(mut field: Field, db: Data>) -> Result { let mut f: Vec = vec![]; while let Some(chunk) = field.next().await { let data = chunk.unwrap(); f = web::block(move || { if let Err(e) = f.write_all(&data) { error!("{e}"); } f }) .await?; } let access_token = String::from_utf8(f) .unwrap_or_default() .parse::() .map_err(|_| ServiceError::Unauthorized)?; match db.send(AuthorizeUser { access_token }).await { Ok(Ok(user)) => Ok(user.id), Ok(Err(e)) => { error!("{:?}", e); Err(ServiceError::Unauthorized.into()) } Err(e) => { error!("{:?}", e); Err(ServiceError::Unauthorized.into()) } } }