Files
quoter/src/handlers/upload.rs
Untone ea92a376ed
Some checks failed
CI / test (push) Failing after 4m0s
CI / lint (push) Failing after 4s
CI / deploy (push) Has been skipped
docs
2025-08-02 00:18:09 +03:00

130 lines
5.2 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
use actix_multipart::Multipart;
use actix_web::{web, HttpRequest, HttpResponse, Result};
use log::{error, warn};
use crate::app_state::AppState;
use crate::auth::{get_id_by_token, user_added_file};
use crate::handlers::MAX_USER_QUOTA_BYTES;
use crate::lookup::store_file_info;
use crate::s3_utils::{self, generate_key_with_extension, upload_to_s3};
use futures::TryStreamExt;
// use crate::thumbnail::convert_heic_to_jpeg;
/// Обработчик для аплоада файлов.
pub async fn upload_handler(
req: HttpRequest,
mut payload: Multipart,
state: web::Data<AppState>,
) -> Result<HttpResponse, actix_web::Error> {
// Получаем токен из заголовка авторизации
let token = req
.headers()
.get("Authorization")
.and_then(|header_value| header_value.to_str().ok());
if token.is_none() {
return Err(actix_web::error::ErrorUnauthorized("Unauthorized")); // Если токен отсутствует, возвращаем ошибку
}
let user_id = get_id_by_token(token.unwrap()).await?;
// Получаем текущую квоту пользователя
let current_quota: u64 = state.get_or_create_quota(&user_id).await.unwrap_or(0);
let mut body = "ok".to_string();
while let Ok(Some(field)) = payload.try_next().await {
let mut field = field;
let mut file_bytes = Vec::new();
let mut file_size: u64 = 0;
// Читаем данные файла
while let Ok(Some(chunk)) = field.try_next().await {
file_size += chunk.len() as u64;
file_bytes.extend_from_slice(&chunk);
}
// Определяем MIME-тип из содержимого файла
let detected_mime_type = match s3_utils::detect_mime_type(&file_bytes) {
Some(mime) => mime,
None => {
warn!("Неподдерживаемый формат файла");
return Err(actix_web::error::ErrorUnsupportedMediaType(
"Неподдерживаемый формат файла",
));
}
};
// Для HEIC файлов просто сохраняем как есть
let (file_bytes, content_type) = if detected_mime_type == "image/heic" {
warn!("HEIC support is temporarily disabled, saving original file");
(file_bytes, detected_mime_type)
} else {
(file_bytes, detected_mime_type)
};
// Получаем расширение из MIME-типа
let extension = match s3_utils::get_extension_from_mime(&content_type) {
Some(ext) => ext,
None => {
warn!("Неподдерживаемый тип содержимого: {}", content_type);
return Err(actix_web::error::ErrorUnsupportedMediaType(
"Неподдерживаемый тип содержимого",
));
}
};
// Проверяем, что добавление файла не превышает лимит квоты
if current_quota + file_size > MAX_USER_QUOTA_BYTES {
warn!(
"Quota would exceed limit: current={}, adding={}, limit={}",
current_quota, file_size, MAX_USER_QUOTA_BYTES
);
return Err(actix_web::error::ErrorUnauthorized("Quota exceeded"));
}
// Генерируем имя файла с правильным расширением
let filename = format!("{}.{}", uuid::Uuid::new_v4(), extension);
// Загружаем файл в S3 storj
match upload_to_s3(
&state.storj_client,
&state.bucket,
&filename,
file_bytes,
&content_type,
)
.await
{
Ok(_) => {
warn!(
"file {} uploaded to storj, incrementing quota by {} bytes",
filename, file_size
);
if let Err(e) = state.increment_uploaded_bytes(&user_id, file_size).await {
error!("Failed to increment quota: {}", e);
return Err(e);
}
// Сохраняем информацию о файле в Redis
let mut redis = state.redis.clone();
store_file_info(&mut redis, &filename, &content_type).await?;
user_added_file(&mut redis, &user_id, &filename).await?;
// Сохраняем маппинг пути
let generated_key =
generate_key_with_extension(filename.clone(), content_type.clone());
state.set_path(&filename, &generated_key).await;
if let Ok(new_quota) = state.get_or_create_quota(&user_id).await {
warn!("New quota for user {}: {} bytes", user_id, new_quota);
}
body = filename;
}
Err(e) => {
warn!("Failed to upload to storj: {}", e);
return Err(actix_web::error::ErrorInternalServerError(e));
}
}
}
Ok(HttpResponse::Ok().body(body))
}