65 lines
2.0 KiB
Rust
65 lines
2.0 KiB
Rust
use axum::{extract::Multipart, http::StatusCode, Json};
|
|
use serde::Serialize;
|
|
use uuid::Uuid;
|
|
|
|
use crate::middleware::auth::AuthUser;
|
|
|
|
/// Response returned after a chat image upload succeeds.
|
|
#[derive(Serialize)]
|
|
pub struct UploadResponse {
|
|
pub url: String,
|
|
}
|
|
|
|
/// Accept a multipart chat image upload and store it under `uploads/chat-images`.
|
|
pub async fn upload_chat_image(
|
|
_auth: AuthUser,
|
|
mut multipart: Multipart,
|
|
) -> Result<Json<UploadResponse>, (StatusCode, String)> {
|
|
while let Some(field) = multipart
|
|
.next_field()
|
|
.await
|
|
.map_err(|e| (StatusCode::BAD_REQUEST, e.to_string()))?
|
|
{
|
|
let content_type = field.content_type().unwrap_or("").to_string();
|
|
let ext = match content_type.as_str() {
|
|
"image/png" => "png",
|
|
"image/jpeg" => "jpg",
|
|
"image/gif" => "gif",
|
|
"image/webp" => "webp",
|
|
_ => {
|
|
return Err((
|
|
StatusCode::BAD_REQUEST,
|
|
"Only PNG, JPG, GIF, WebP images are allowed".into(),
|
|
))
|
|
}
|
|
};
|
|
|
|
let data = field
|
|
.bytes()
|
|
.await
|
|
.map_err(|e| (StatusCode::BAD_REQUEST, e.to_string()))?;
|
|
|
|
if data.len() > 5 * 1024 * 1024 {
|
|
return Err((StatusCode::BAD_REQUEST, "Image must be under 5MB".into()));
|
|
}
|
|
|
|
let dir = "uploads/chat-images";
|
|
tokio::fs::create_dir_all(dir)
|
|
.await
|
|
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
|
|
|
|
let filename = format!("{}.{}", Uuid::new_v4(), ext);
|
|
let path = format!("{}/{}", dir, filename);
|
|
|
|
tokio::fs::write(&path, &data)
|
|
.await
|
|
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
|
|
|
|
return Ok(Json(UploadResponse {
|
|
url: format!("/uploads/chat-images/{}", filename),
|
|
}));
|
|
}
|
|
|
|
Err((StatusCode::BAD_REQUEST, "No image provided".into()))
|
|
}
|