use actix_multipart::Multipart; use actix_web::{web, HttpResponse, post, delete, get, ResponseError}; use log::error; use serenity::futures::StreamExt; use siren::ServiceError; use crate::{auth::{JwtAuth, InsertUser, QueryUser}, storage::{upload_file, get_file, delete_file}}; #[post("/picture")] async fn set_picture(mut payload: Multipart, auth: JwtAuth) -> HttpResponse { while let Some(item) = payload.next().await { let mut bytes = web::BytesMut::new(); let mut field = match item { Ok(field) => field, Err(err) => return ResponseError::error_response(&err) }; let content_type = field.content_disposition(); // Get file name and construct the file path let file_name = match content_type.get_filename() { Some(name) => { // Verify extension is supported match name.split(".").last() { Some(ext) => { match ext { "png" | "jpg" | "jpeg" => name, _ => return ResponseError::error_response(&ServiceError { status: 400, message: "File extension is not supported".to_string() }) } }, None => return ResponseError::error_response(&ServiceError { status: 400, message: "Unknown file extension".to_string() }) } }, None => return ResponseError::error_response(&ServiceError { status: 400, message: "File name is not provided".to_string() }) }; let path = format!("users/{}/{}", auth.user.email, file_name); // Build the file and store it in minio while let Some(chunk) = field.next().await { let data = match chunk { Ok(data) => data, Err(err) => { error!("Failed to get chunk: {}", err); return ResponseError::error_response(&err); } }; bytes.extend_from_slice(&data); } match upload_file(&path, &bytes).await { Ok(_) => { match InsertUser::update_profile(&auth.user.email, Some(&path)) { Ok(_) => {} Err(err) => { error!("Failed to update user profile: {}", err); return ResponseError::error_response(&err); } }; }, Err(err) => { error!("Failed to upload file: {}", err); return ResponseError::error_response(&err); } } }; return HttpResponse::Ok().finish(); } #[get("/picture")] async fn get_picture(auth: JwtAuth) -> HttpResponse { let user = match QueryUser::get_by_email(&auth.user.email) { Ok(user) => user, Err(err) => { error!("Failed to get user: {}", err); return ResponseError::error_response(&err); } }; if let Some(path) = user.profile_picture { match get_file(&path).await { Ok(bytes) => return HttpResponse::Ok().body(bytes), Err(err) => { error!("Failed to get file: {}", err); return ResponseError::error_response(&err); } } } else { return HttpResponse::NotFound().finish(); } } #[delete("/picture")] async fn delete_picture(auth: JwtAuth) -> HttpResponse { match QueryUser::get_by_email(&auth.user.email) { Ok(user) => { match user.profile_picture { Some(path) => { match delete_file(&path).await { Ok(_) => { match InsertUser::update_profile(&auth.user.email, None) { Ok(_) => {} Err(err) => { error!("Failed to update user profile: {}", err); return ResponseError::error_response(&err); } }; } Err(err) => { error!("Failed to delete file: {}", err); return ResponseError::error_response(&err); } }; }, None => {} } }, Err(err) => { error!("Failed to get user: {}", err); return ResponseError::error_response(&err); } }; return HttpResponse::Ok().finish(); } pub fn init_routes(config: &mut web::ServiceConfig) { config.service(web::scope("users") .service(set_picture) .service(get_picture) .service(delete_picture) ); }