Display the list of ISO files
This commit is contained in:
parent
e7d5747b99
commit
08b59b6f67
@ -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))
|
||||||
|
}
|
||||||
|
@ -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()
|
||||||
|
48
virtweb_frontend/package-lock.json
generated
48
virtweb_frontend/package-lock.json
generated
@ -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",
|
||||||
|
@ -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",
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 (
|
||||||
<VirtWebRouteContainer label="ISO files management">
|
<AsyncWidget
|
||||||
<UploadIsoFileCard onFileUploaded={() => alert("file uploaded!")} />
|
loadKey={loadKey.current}
|
||||||
<UploadIsoFileFromUrlCard
|
errMsg="Failed to load ISO files list!"
|
||||||
onFileUploaded={() => alert("file uploaded!")}
|
load={load}
|
||||||
/>
|
ready={list !== undefined}
|
||||||
</VirtWebRouteContainer>
|
build={() => (
|
||||||
|
<VirtWebRouteContainer label="ISO files management">
|
||||||
|
<UploadIsoFileCard onFileUploaded={reload} />
|
||||||
|
<UploadIsoFileFromUrlCard onFileUploaded={reload} />
|
||||||
|
<IsoFilesList list={list!} onReload={reload} />
|
||||||
|
</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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user