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, } /// Upload iso file pub async fn upload_file(MultipartForm(mut form): MultipartForm) -> 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) -> 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, 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) -> 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()) }