170 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			170 lines
		
	
	
		
			4.9 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::MultipartForm;
 | 
						|
use actix_multipart::form::tempfile::TempFile;
 | 
						|
use actix_web::{HttpRequest, HttpResponse, web};
 | 
						|
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.as_bytes() {
 | 
						|
        log::error!("Uploaded ISO file is too large!");
 | 
						|
        return Ok(HttpResponse::BadRequest().json("File is too large!"));
 | 
						|
    }
 | 
						|
 | 
						|
    if let Some(m) = &file.content_type
 | 
						|
        && !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()
 | 
						|
        && len > constants::ISO_MAX_SIZE.as_bytes() as u64
 | 
						|
    {
 | 
						|
        return Ok(HttpResponse::BadRequest().json("File is too large!"));
 | 
						|
    }
 | 
						|
 | 
						|
    if let Some(ct) = response.headers().get("content-type")
 | 
						|
        && !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 IsoFilePath {
 | 
						|
    filename: String,
 | 
						|
}
 | 
						|
 | 
						|
/// Download ISO file
 | 
						|
pub async fn download_file(p: web::Path<IsoFilePath>, 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<IsoFilePath>) -> 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())
 | 
						|
}
 |