73 lines
2.5 KiB
Rust

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())
}
}
}