Can delete uploaded disk images

This commit is contained in:
2025-05-29 08:45:38 +02:00
parent 615dc1ed83
commit 927a51cda7
6 changed files with 89 additions and 11 deletions

View File

@ -245,7 +245,7 @@ impl AppConfig {
storage_path.canonicalize().unwrap() storage_path.canonicalize().unwrap()
} }
/// Get iso storage directory /// Get iso files storage directory
pub fn iso_storage_path(&self) -> PathBuf { pub fn iso_storage_path(&self) -> PathBuf {
self.storage_path().join("iso") self.storage_path().join("iso")
} }
@ -265,15 +265,17 @@ impl AppConfig {
self.vnc_sockets_path().join(format!("vnc-{}", name)) self.vnc_sockets_path().join(format!("vnc-{}", name))
} }
/// Get VM vnc sockets directory /// Get VM root disks storage directory
pub fn disks_storage_path(&self) -> PathBuf { pub fn root_vm_disks_storage_path(&self) -> PathBuf {
self.storage_path().join("disks") self.storage_path().join("disks")
} }
/// Get specific VM disk storage directory
pub fn vm_storage_path(&self, id: XMLUuid) -> PathBuf { pub fn vm_storage_path(&self, id: XMLUuid) -> PathBuf {
self.disks_storage_path().join(id.as_string()) self.root_vm_disks_storage_path().join(id.as_string())
} }
/// Get the path were VM definitions are backed up
pub fn definitions_path(&self) -> PathBuf { pub fn definitions_path(&self) -> PathBuf {
self.storage_path().join("definitions") self.storage_path().join("definitions")
} }

View File

@ -5,7 +5,7 @@ use crate::utils::file_disks_utils::DiskFileInfo;
use crate::utils::files_utils; use crate::utils::files_utils;
use actix_multipart::form::MultipartForm; use actix_multipart::form::MultipartForm;
use actix_multipart::form::tempfile::TempFile; use actix_multipart::form::tempfile::TempFile;
use actix_web::HttpResponse; use actix_web::{HttpResponse, web};
#[derive(Debug, MultipartForm)] #[derive(Debug, MultipartForm)]
pub struct UploadDiskImageForm { pub struct UploadDiskImageForm {
@ -67,3 +67,27 @@ pub async fn get_list() -> HttpResult {
Ok(HttpResponse::Ok().json(list)) Ok(HttpResponse::Ok().json(list))
} }
#[derive(serde::Deserialize)]
pub struct DiskFilePath {
filename: String,
}
/// Delete a disk image
pub async fn delete(p: web::Path<DiskFilePath>) -> HttpResult {
if !files_utils::check_file_name(&p.filename) {
return Ok(HttpResponse::BadRequest().json("Invalid file name!"));
}
let file_path = AppConfig::get()
.disk_images_storage_path()
.join(&p.filename);
if !file_path.exists() {
return Ok(HttpResponse::NotFound().json("Disk image does not exists!"));
}
std::fs::remove_file(file_path)?;
Ok(HttpResponse::Accepted().finish())
}

View File

@ -132,12 +132,12 @@ pub async fn get_list() -> HttpResult {
} }
#[derive(serde::Deserialize)] #[derive(serde::Deserialize)]
pub struct DownloadFilePath { pub struct IsoFilePath {
filename: String, filename: String,
} }
/// Download ISO file /// Download ISO file
pub async fn download_file(p: web::Path<DownloadFilePath>, req: HttpRequest) -> HttpResult { pub async fn download_file(p: web::Path<IsoFilePath>, req: HttpRequest) -> HttpResult {
if !files_utils::check_file_name(&p.filename) { if !files_utils::check_file_name(&p.filename) {
return Ok(HttpResponse::BadRequest().json("Invalid file name!")); return Ok(HttpResponse::BadRequest().json("Invalid file name!"));
} }
@ -152,7 +152,7 @@ pub async fn download_file(p: web::Path<DownloadFilePath>, req: HttpRequest) ->
} }
/// Delete ISO file /// Delete ISO file
pub async fn delete_file(p: web::Path<DownloadFilePath>) -> HttpResult { pub async fn delete_file(p: web::Path<IsoFilePath>) -> HttpResult {
if !files_utils::check_file_name(&p.filename) { if !files_utils::check_file_name(&p.filename) {
return Ok(HttpResponse::BadRequest().json("Invalid file name!")); return Ok(HttpResponse::BadRequest().json("Invalid file name!"));
} }

View File

@ -60,7 +60,8 @@ async fn main() -> std::io::Result<()> {
files_utils::create_directory_if_missing(AppConfig::get().disk_images_storage_path()).unwrap(); files_utils::create_directory_if_missing(AppConfig::get().disk_images_storage_path()).unwrap();
files_utils::create_directory_if_missing(AppConfig::get().vnc_sockets_path()).unwrap(); files_utils::create_directory_if_missing(AppConfig::get().vnc_sockets_path()).unwrap();
files_utils::set_file_permission(AppConfig::get().vnc_sockets_path(), 0o777).unwrap(); files_utils::set_file_permission(AppConfig::get().vnc_sockets_path(), 0o777).unwrap();
files_utils::create_directory_if_missing(AppConfig::get().disks_storage_path()).unwrap(); files_utils::create_directory_if_missing(AppConfig::get().root_vm_disks_storage_path())
.unwrap();
files_utils::create_directory_if_missing(AppConfig::get().nat_path()).unwrap(); files_utils::create_directory_if_missing(AppConfig::get().nat_path()).unwrap();
files_utils::create_directory_if_missing(AppConfig::get().definitions_path()).unwrap(); files_utils::create_directory_if_missing(AppConfig::get().definitions_path()).unwrap();
files_utils::create_directory_if_missing(AppConfig::get().api_tokens_path()).unwrap(); files_utils::create_directory_if_missing(AppConfig::get().api_tokens_path()).unwrap();
@ -344,6 +345,10 @@ async fn main() -> std::io::Result<()> {
"/api/disk_images/list", "/api/disk_images/list",
web::get().to(disk_images_controller::get_list), web::get().to(disk_images_controller::get_list),
) )
.route(
"/api/disk_images/{filename}",
web::delete().to(disk_images_controller::delete),
)
// API tokens controller // API tokens controller
.route( .route(
"/api/token/create", "/api/token/create",

View File

@ -42,4 +42,14 @@ export class DiskImageApi {
}) })
).data; ).data;
} }
/**
* Delete disk image file
*/
static async Delete(file: DiskImage): Promise<void> {
await APIClient.exec({
method: "DELETE",
uri: `/disk_images/${file.file_name}`,
});
}
} }

View File

@ -14,6 +14,8 @@ import React from "react";
import { DiskImage, DiskImageApi } from "../api/DiskImageApi"; import { DiskImage, DiskImageApi } from "../api/DiskImageApi";
import { ServerApi } from "../api/ServerApi"; import { ServerApi } from "../api/ServerApi";
import { useAlert } from "../hooks/providers/AlertDialogProvider"; import { useAlert } from "../hooks/providers/AlertDialogProvider";
import { useConfirm } from "../hooks/providers/ConfirmDialogProvider";
import { useLoadingMessage } from "../hooks/providers/LoadingMessageProvider";
import { useSnackbar } from "../hooks/providers/SnackbarProvider"; import { useSnackbar } from "../hooks/providers/SnackbarProvider";
import { AsyncWidget } from "../widgets/AsyncWidget"; import { AsyncWidget } from "../widgets/AsyncWidget";
import { DateWidget } from "../widgets/DateWidget"; import { DateWidget } from "../widgets/DateWidget";
@ -152,6 +154,41 @@ function DiskImageList(p: {
list: DiskImage[]; list: DiskImage[];
onReload: () => void; onReload: () => void;
}): React.ReactElement { }): React.ReactElement {
const alert = useAlert();
const snackbar = useSnackbar();
const confirm = useConfirm();
const loadingMessage = useLoadingMessage();
// Delete disk image
const deleteDiskImage = async (entry: DiskImage) => {
if (
!(await confirm(
`Do you really want to delete this disk image (${entry.file_name}) ?`
))
)
return;
loadingMessage.show("Deleting disk image file...");
try {
await DiskImageApi.Delete(entry);
snackbar("The disk image has been successfully deleted!");
p.onReload();
} catch (e) {
console.error(e);
alert(`Failed to delete disk image!\n${e}`);
}
loadingMessage.hide();
};
if (p.list.length === 0)
return (
<Typography variant="body1" style={{ textAlign: "center" }}>
No disk image uploaded for now.
</Typography>
);
const columns: GridColDef<(typeof p.list)[number]>[] = [ const columns: GridColDef<(typeof p.list)[number]>[] = [
{ field: "file_name", headerName: "File name", flex: 3 }, { field: "file_name", headerName: "File name", flex: 3 },
{ {
@ -187,8 +224,8 @@ function DiskImageList(p: {
<DownloadIcon /> <DownloadIcon />
</IconButton> </IconButton>
</Tooltip> </Tooltip>
<Tooltip title="Delete file"> <Tooltip title="Delete disk image">
<IconButton onClick={() => deleteIso(params.row)}> <IconButton onClick={() => deleteDiskImage(params.row)}>
<DeleteIcon /> <DeleteIcon />
</IconButton> </IconButton>
</Tooltip> </Tooltip>