use crate::controllers::HttpResult; use crate::extractors::auth_extractor::AuthExtractor; use crate::extractors::file_extractor::FileExtractor; use crate::extractors::file_id_extractor::FileIdExtractor; use crate::models::files::File; use crate::services::files_service; use crate::utils::time_utils; use actix_web::http::header; use actix_web::{HttpRequest, HttpResponse}; use std::ops::Add; use std::time::Duration; /// Upload a new file pub async fn upload(auth: AuthExtractor, file: FileExtractor) -> HttpResult { let file = files_service::create_file_with_mimetype( auth.user_id(), &file.name(), &file.mime, &file.buff, ) .await?; Ok(HttpResponse::Ok().json(file)) } /// Download an uploaded file pub async fn download(req: HttpRequest, file_extractor: FileIdExtractor) -> HttpResult { serve_file(req, file_extractor.as_ref()).await } /// Serve a file, returning 304 status code if the requested file already exists pub async fn serve_file(req: HttpRequest, file: &File) -> HttpResult { // Check if the browser already knows the etag if let Some(c) = req.headers().get(header::IF_NONE_MATCH) { if c.to_str().unwrap_or("") == file.sha512.as_str() { return Ok(HttpResponse::NotModified().finish()); } } // Check if the browser already knows the file by date if let Some(c) = req.headers().get(header::IF_MODIFIED_SINCE) { let date_str = c.to_str().unwrap_or(""); if let Ok(date) = httpdate::parse_http_date(date_str) { if date.add(Duration::from_secs(1)) >= time_utils::unix_to_system_time(file.time_create as u64) { return Ok(HttpResponse::NotModified().finish()); } } } Ok(HttpResponse::Ok() .content_type(file.mime_type.as_str()) .insert_header(("etag", file.sha512.as_str())) .insert_header(( "last-modified", time_utils::unix_to_http_date(file.time_create as u64), )) .body(files_service::get_file_content(file).await?)) } /// Delete an uploaded file pub async fn delete(file_extractor: FileIdExtractor) -> HttpResult { match files_service::delete_file_if_unused(file_extractor.as_ref().id()).await { Ok(true) => Ok(HttpResponse::Accepted().finish()), Ok(false) => Ok(HttpResponse::Conflict().finish()), Err(e) => { log::error!("Failed to delete file: {e}"); Ok(HttpResponse::InternalServerError().finish()) } } }