VirtWeb/virtweb_backend/src/controllers/iso_controller.rs

170 lines
5.0 KiB
Rust

use crate::app_config::AppConfig;
use crate::constants;
use crate::controllers::HttpResult;
use crate::utils::files_utils;
use actix_files::NamedFile;
use actix_multipart::form::tempfile::TempFile;
use actix_multipart::form::MultipartForm;
use actix_web::{web, HttpRequest, HttpResponse};
use futures_util::StreamExt;
use std::fs::File;
use std::io::Write;
use std::os::unix::fs::MetadataExt;
#[derive(Debug, MultipartForm)]
pub struct UploadIsoForm {
#[multipart(rename = "file")]
files: Vec<TempFile>,
}
/// Upload iso file
pub async fn upload_file(MultipartForm(mut form): MultipartForm<UploadIsoForm>) -> HttpResult {
if form.files.is_empty() {
log::error!("Missing uploaded ISO file!");
return Ok(HttpResponse::BadRequest().json("Missing file!"));
}
let file = form.files.remove(0);
if file.size > constants::ISO_MAX_SIZE {
log::error!("Uploaded ISO file is too large!");
return Ok(HttpResponse::BadRequest().json("File is too large!"));
}
if let Some(m) = &file.content_type {
if !constants::ALLOWED_ISO_MIME_TYPES.contains(&m.to_string().as_str()) {
log::error!("Uploaded ISO file has an invalid mimetype!");
return Ok(HttpResponse::BadRequest().json("Invalid mimetype!"));
}
}
let file_name = match &file.file_name {
None => {
log::error!("Uploaded ISO file does not have a name!");
return Ok(HttpResponse::BadRequest().json("Missing file name!"));
}
Some(f) => f,
};
if !files_utils::check_file_name(file_name) {
log::error!("Bad file name for uploaded iso!");
return Ok(HttpResponse::BadRequest().json("Bad file name!"));
}
let dest_file = AppConfig::get().iso_storage_path().join(file_name);
log::info!("Will save ISO file {:?}", dest_file);
if dest_file.exists() {
log::error!("Conflict with uploaded iso file name!");
return Ok(HttpResponse::Conflict().json("The file already exists!"));
}
file.file.persist(&dest_file)?;
// Set file permissions
files_utils::set_file_permission(dest_file, 0o644)?;
Ok(HttpResponse::Accepted().finish())
}
#[derive(serde::Deserialize)]
pub struct DownloadFromURLReq {
url: String,
filename: String,
}
/// Upload ISO file from URL
pub async fn upload_from_url(req: web::Json<DownloadFromURLReq>) -> HttpResult {
if !files_utils::check_file_name(&req.filename) || !req.filename.ends_with(".iso") {
return Ok(HttpResponse::BadRequest().json("Invalid file name!"));
}
let dest_file = AppConfig::get().iso_storage_path().join(&req.filename);
if dest_file.exists() {
return Ok(HttpResponse::Conflict().json("A similar file already exists!"));
}
let response = reqwest::get(&req.url).await?;
if let Some(len) = response.content_length() {
if len > constants::ISO_MAX_SIZE as u64 {
return Ok(HttpResponse::BadRequest().json("File is too large!"));
}
}
if let Some(ct) = response.headers().get("content-type") {
if !constants::ALLOWED_ISO_MIME_TYPES.contains(&ct.to_str()?) {
return Ok(HttpResponse::BadRequest().json("Invalid file mimetype!"));
}
}
let mut stream = response.bytes_stream();
let mut file = File::create(dest_file)?;
while let Some(item) = stream.next().await {
let bytes = item?;
file.write_all(&bytes)?;
}
Ok(HttpResponse::Accepted().finish())
}
#[derive(serde::Serialize)]
struct IsoFile {
filename: String,
size: u64,
}
/// Get ISO files list
pub async fn get_list() -> HttpResult {
let mut list = vec![];
for entry in AppConfig::get().iso_storage_path().read_dir()? {
let entry = entry?;
list.push(IsoFile {
filename: entry.file_name().to_string_lossy().to_string(),
size: entry.path().metadata()?.size(),
})
}
Ok(HttpResponse::Ok().json(list))
}
#[derive(serde::Deserialize)]
pub struct DownloadFilePath {
filename: String,
}
/// Download ISO file
pub async fn download_file(p: web::Path<DownloadFilePath>, req: HttpRequest) -> HttpResult {
if !files_utils::check_file_name(&p.filename) {
return Ok(HttpResponse::BadRequest().json("Invalid file name!"));
}
let file_path = AppConfig::get().iso_storage_path().join(&p.filename);
if !file_path.exists() {
return Ok(HttpResponse::NotFound().json("File does not exists!"));
}
Ok(NamedFile::open(file_path)?.into_response(&req))
}
/// Delete ISO file
pub async fn delete_file(p: web::Path<DownloadFilePath>) -> HttpResult {
if !files_utils::check_file_name(&p.filename) {
return Ok(HttpResponse::BadRequest().json("Invalid file name!"));
}
let file_path = AppConfig::get().iso_storage_path().join(&p.filename);
if !file_path.exists() {
return Ok(HttpResponse::NotFound().json("File does not exists!"));
}
std::fs::remove_file(file_path)?;
Ok(HttpResponse::Accepted().finish())
}