Display the list of ISO files

This commit is contained in:
Pierre HUBERT 2023-09-06 14:21:26 +02:00
parent e7d5747b99
commit 08b59b6f67
6 changed files with 152 additions and 7 deletions

View File

@ -8,6 +8,7 @@ use actix_web::{web, HttpResponse};
use futures_util::StreamExt; use futures_util::StreamExt;
use std::fs::File; use std::fs::File;
use std::io::Write; use std::io::Write;
use std::os::unix::fs::MetadataExt;
#[derive(Debug, MultipartForm)] #[derive(Debug, MultipartForm)]
pub struct UploadIsoForm { pub struct UploadIsoForm {
@ -105,3 +106,23 @@ pub async fn upload_from_url(req: web::Json<DownloadFromURLReq>) -> HttpResult {
Ok(HttpResponse::Accepted().finish()) 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))
}

View File

@ -108,6 +108,7 @@ async fn main() -> std::io::Result<()> {
"/api/iso/upload_from_url", "/api/iso/upload_from_url",
web::post().to(iso_controller::upload_from_url), web::post().to(iso_controller::upload_from_url),
) )
.route("/api/iso/list", web::get().to(iso_controller::get_list))
}) })
.bind(&AppConfig::get().listen_address)? .bind(&AppConfig::get().listen_address)?
.run() .run()

View File

@ -15,6 +15,7 @@
"@mdi/react": "^1.6.1", "@mdi/react": "^1.6.1",
"@mui/icons-material": "^5.14.7", "@mui/icons-material": "^5.14.7",
"@mui/material": "^5.14.7", "@mui/material": "^5.14.7",
"@mui/x-data-grid": "^6.12.1",
"@testing-library/jest-dom": "^5.17.0", "@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0", "@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0", "@testing-library/user-event": "^13.5.0",
@ -3589,6 +3590,31 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
"integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w=="
}, },
"node_modules/@mui/x-data-grid": {
"version": "6.12.1",
"resolved": "https://registry.npmjs.org/@mui/x-data-grid/-/x-data-grid-6.12.1.tgz",
"integrity": "sha512-lSY1dFBv6yh2ffkkWeMM9c0ajpwIWn/da/ec0kY8OfLa79Hboh53mN09nopylZO8MHJGfDlPvduwuWcgWs+zFw==",
"dependencies": {
"@babel/runtime": "^7.22.11",
"@mui/utils": "^5.14.7",
"clsx": "^2.0.0",
"prop-types": "^15.8.1",
"reselect": "^4.1.8"
},
"engines": {
"node": ">=14.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/mui"
},
"peerDependencies": {
"@mui/material": "^5.4.1",
"@mui/system": "^5.4.1",
"react": "^17.0.0 || ^18.0.0",
"react-dom": "^17.0.0 || ^18.0.0"
}
},
"node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": {
"version": "5.1.1-v1", "version": "5.1.1-v1",
"resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz",
@ -15303,6 +15329,11 @@
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
"integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ=="
}, },
"node_modules/reselect": {
"version": "4.1.8",
"resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz",
"integrity": "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ=="
},
"node_modules/resolve": { "node_modules/resolve": {
"version": "1.22.4", "version": "1.22.4",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.4.tgz", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.4.tgz",
@ -20424,6 +20455,18 @@
} }
} }
}, },
"@mui/x-data-grid": {
"version": "6.12.1",
"resolved": "https://registry.npmjs.org/@mui/x-data-grid/-/x-data-grid-6.12.1.tgz",
"integrity": "sha512-lSY1dFBv6yh2ffkkWeMM9c0ajpwIWn/da/ec0kY8OfLa79Hboh53mN09nopylZO8MHJGfDlPvduwuWcgWs+zFw==",
"requires": {
"@babel/runtime": "^7.22.11",
"@mui/utils": "^5.14.7",
"clsx": "^2.0.0",
"prop-types": "^15.8.1",
"reselect": "^4.1.8"
}
},
"@nicolo-ribaudo/eslint-scope-5-internals": { "@nicolo-ribaudo/eslint-scope-5-internals": {
"version": "5.1.1-v1", "version": "5.1.1-v1",
"resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz",
@ -28769,6 +28812,11 @@
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
"integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ=="
}, },
"reselect": {
"version": "4.1.8",
"resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz",
"integrity": "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ=="
},
"resolve": { "resolve": {
"version": "1.22.4", "version": "1.22.4",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.4.tgz", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.4.tgz",

View File

@ -10,6 +10,7 @@
"@mdi/react": "^1.6.1", "@mdi/react": "^1.6.1",
"@mui/icons-material": "^5.14.7", "@mui/icons-material": "^5.14.7",
"@mui/material": "^5.14.7", "@mui/material": "^5.14.7",
"@mui/x-data-grid": "^6.12.1",
"@testing-library/jest-dom": "^5.17.0", "@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0", "@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0", "@testing-library/user-event": "^13.5.0",

View File

@ -1,5 +1,10 @@
import { APIClient } from "./ApiClient"; import { APIClient } from "./ApiClient";
export interface IsoFile {
filename: string;
size: number;
}
export class IsoFilesApi { export class IsoFilesApi {
/** /**
* Upload a new ISO file to the server * Upload a new ISO file to the server
@ -29,4 +34,16 @@ export class IsoFilesApi {
jsonData: { url: url, filename: filename }, jsonData: { url: url, filename: filename },
}); });
} }
/**
* Get iso files list
*/
static async GetList(): Promise<IsoFile[]> {
return (
await APIClient.exec({
method: "GET",
uri: "/iso/list",
})
).data;
}
} }

View File

@ -2,22 +2,44 @@ import { Button, LinearProgress, TextField, Typography } from "@mui/material";
import { filesize } from "filesize"; import { filesize } from "filesize";
import { MuiFileInput } from "mui-file-input"; import { MuiFileInput } from "mui-file-input";
import React from "react"; import React from "react";
import { IsoFilesApi } from "../api/IsoFilesApi"; import { IsoFile, IsoFilesApi } from "../api/IsoFilesApi";
import { ServerApi } from "../api/ServerApi"; import { ServerApi } from "../api/ServerApi";
import { useAlert } from "../hooks/providers/AlertDialogProvider"; import { useAlert } from "../hooks/providers/AlertDialogProvider";
import { useSnackbar } from "../hooks/providers/SnackbarProvider"; import { useSnackbar } from "../hooks/providers/SnackbarProvider";
import { VirtWebPaper } from "../widgets/VirtWebPaper"; import { VirtWebPaper } from "../widgets/VirtWebPaper";
import { VirtWebRouteContainer } from "../widgets/VirtWebRouteContainer"; import { VirtWebRouteContainer } from "../widgets/VirtWebRouteContainer";
import { useLoadingMessage } from "../hooks/providers/LoadingMessageProvider"; import { useLoadingMessage } from "../hooks/providers/LoadingMessageProvider";
import { AsyncWidget } from "../widgets/AsyncWidget";
import { DataGrid, GridColDef, GridRowsProp } from "@mui/x-data-grid";
export function IsoFilesRoute(): React.ReactElement { export function IsoFilesRoute(): React.ReactElement {
const [list, setList] = React.useState<IsoFile[] | undefined>();
const loadKey = React.useRef(1);
const load = async () => {
setList(await IsoFilesApi.GetList());
};
const reload = () => {
loadKey.current += 1;
setList(undefined);
};
return ( return (
<AsyncWidget
loadKey={loadKey.current}
errMsg="Failed to load ISO files list!"
load={load}
ready={list !== undefined}
build={() => (
<VirtWebRouteContainer label="ISO files management"> <VirtWebRouteContainer label="ISO files management">
<UploadIsoFileCard onFileUploaded={() => alert("file uploaded!")} /> <UploadIsoFileCard onFileUploaded={reload} />
<UploadIsoFileFromUrlCard <UploadIsoFileFromUrlCard onFileUploaded={reload} />
onFileUploaded={() => alert("file uploaded!")} <IsoFilesList list={list!} onReload={reload} />
/>
</VirtWebRouteContainer> </VirtWebRouteContainer>
)}
/>
); );
} }
@ -145,3 +167,38 @@ function UploadIsoFileFromUrlCard(p: {
</VirtWebPaper> </VirtWebPaper>
); );
} }
function IsoFilesList(p: {
list: IsoFile[];
onReload: () => void;
}): React.ReactElement {
if (p.list.length === 0)
return (
<Typography variant="body1" style={{ textAlign: "center" }}>
No ISO file uploaded for now.
</Typography>
);
const columns: GridColDef[] = [
{ field: "filename", headerName: "File name", flex: 3 },
{
field: "size",
headerName: "File size",
flex: 1,
renderCell(params) {
return filesize(params.row.size);
},
},
];
return (
<VirtWebPaper label="Files list">
<DataGrid
getRowId={(c) => c.filename}
rows={p.list}
columns={columns}
autoHeight={true}
/>
</VirtWebPaper>
);
}