Compare commits
4 Commits
c8ad541a9d
...
feb6cb4732
Author | SHA1 | Date | |
---|---|---|---|
feb6cb4732 | |||
35e7f4b59c | |||
d08516a72d | |||
479cc1fa0f |
@ -11,7 +11,7 @@ steps:
|
|||||||
path: /tmp/web_build
|
path: /tmp/web_build
|
||||||
commands:
|
commands:
|
||||||
- cd virtweb_frontend
|
- cd virtweb_frontend
|
||||||
- npm install --legacy-peer-deps # TODO : remove when mui-file-input is updated
|
- npm install
|
||||||
- npm run lint
|
- npm run lint
|
||||||
- npm run build
|
- npm run build
|
||||||
- mv dist /tmp/web_build
|
- mv dist /tmp/web_build
|
||||||
|
571
virtweb_backend/Cargo.lock
generated
571
virtweb_backend/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -7,12 +7,12 @@ edition = "2024"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
log = "0.4.27"
|
log = "0.4.27"
|
||||||
env_logger = "0.11.7"
|
env_logger = "0.11.8"
|
||||||
clap = { version = "4.5.34", features = ["derive", "env"] }
|
clap = { version = "4.5.38", features = ["derive", "env"] }
|
||||||
light-openid = { version = "1.0.4", features = ["crypto-wrapper"] }
|
light-openid = { version = "1.0.4", features = ["crypto-wrapper"] }
|
||||||
lazy_static = "1.5.0"
|
lazy_static = "1.5.0"
|
||||||
actix = "0.13.5"
|
actix = "0.13.5"
|
||||||
actix-web = "4.10.2"
|
actix-web = "4.11.0"
|
||||||
actix-remote-ip = "0.1.0"
|
actix-remote-ip = "0.1.0"
|
||||||
actix-session = { version = "0.10.1", features = ["cookie-session"] }
|
actix-session = { version = "0.10.1", features = ["cookie-session"] }
|
||||||
actix-identity = "0.8.0"
|
actix-identity = "0.8.0"
|
||||||
@ -22,27 +22,26 @@ actix-ws = "0.3.0"
|
|||||||
actix-http = "3.10.0"
|
actix-http = "3.10.0"
|
||||||
serde = { version = "1.0.219", features = ["derive"] }
|
serde = { version = "1.0.219", features = ["derive"] }
|
||||||
serde_json = "1.0.140"
|
serde_json = "1.0.140"
|
||||||
quick-xml = { version = "0.37.3", features = ["serialize", "overlapped-lists"] }
|
quick-xml = { version = "0.37.5", features = ["serialize", "overlapped-lists"] }
|
||||||
futures-util = "0.3.31"
|
futures-util = "0.3.31"
|
||||||
anyhow = "1.0.97"
|
anyhow = "1.0.98"
|
||||||
actix-multipart = "0.7.2"
|
actix-multipart = "0.7.2"
|
||||||
tempfile = "3.19.1"
|
tempfile = "3.20.0"
|
||||||
reqwest = { version = "0.12.15", features = ["stream"] }
|
reqwest = { version = "0.12.15", features = ["stream"] }
|
||||||
url = "2.5.4"
|
url = "2.5.4"
|
||||||
virt = "0.4.2"
|
virt = "0.4.2"
|
||||||
sysinfo = { version = "0.34.2", features = ["serde"] }
|
sysinfo = { version = "0.35.1", features = ["serde"] }
|
||||||
uuid = { version = "1.16.0", features = ["v4", "serde"] }
|
uuid = { version = "1.16.0", features = ["v4", "serde"] }
|
||||||
lazy-regex = "3.4.1"
|
lazy-regex = "3.4.1"
|
||||||
thiserror = "2.0.12"
|
thiserror = "2.0.12"
|
||||||
image = "0.25.6"
|
image = "0.25.6"
|
||||||
rand = "0.9.0"
|
rand = "0.9.1"
|
||||||
bytes = "1.10.1"
|
tokio = { version = "1.45.0", features = ["rt", "time", "macros"] }
|
||||||
tokio = { version = "1.44.1", features = ["rt", "time", "macros"] }
|
|
||||||
futures = "0.3.31"
|
futures = "0.3.31"
|
||||||
ipnetwork = { version = "0.21.1", features = ["serde"] }
|
ipnetwork = { version = "0.21.1", features = ["serde"] }
|
||||||
num = "0.4.3"
|
num = "0.4.3"
|
||||||
rust-embed = { version = "8.6.0" }
|
rust-embed = { version = "8.7.2" }
|
||||||
mime_guess = "2.0.5"
|
mime_guess = "2.0.5"
|
||||||
dotenvy = "0.15.7"
|
dotenvy = "0.15.7"
|
||||||
nix = { version = "0.29.0", features = ["net"] }
|
nix = { version = "0.30.1", features = ["net"] }
|
||||||
basic-jwt = "0.3.0"
|
basic-jwt = "0.3.0"
|
||||||
|
@ -4,7 +4,6 @@ use actix_web::body::BoxBody;
|
|||||||
use actix_web::{HttpResponse, web};
|
use actix_web::{HttpResponse, web};
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
use std::io::ErrorKind;
|
|
||||||
|
|
||||||
pub mod api_tokens_controller;
|
pub mod api_tokens_controller;
|
||||||
pub mod auth_controller;
|
pub mod auth_controller;
|
||||||
@ -62,7 +61,7 @@ impl From<serde_json::Error> for HttpErr {
|
|||||||
|
|
||||||
impl From<Box<dyn Error>> for HttpErr {
|
impl From<Box<dyn Error>> for HttpErr {
|
||||||
fn from(value: Box<dyn Error>) -> Self {
|
fn from(value: Box<dyn Error>) -> Self {
|
||||||
HttpErr::Err(std::io::Error::new(ErrorKind::Other, value.to_string()).into())
|
HttpErr::Err(std::io::Error::other(value.to_string()).into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,7 +97,7 @@ impl From<reqwest::header::ToStrError> for HttpErr {
|
|||||||
|
|
||||||
impl From<actix_web::Error> for HttpErr {
|
impl From<actix_web::Error> for HttpErr {
|
||||||
fn from(value: actix_web::Error) -> Self {
|
fn from(value: actix_web::Error) -> Self {
|
||||||
HttpErr::Err(std::io::Error::new(ErrorKind::Other, value.to_string()).into())
|
HttpErr::Err(std::io::Error::other(value.to_string()).into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
1655
virtweb_frontend/package-lock.json
generated
1655
virtweb_frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -15,39 +15,38 @@
|
|||||||
"@fontsource/roboto": "^5.2.5",
|
"@fontsource/roboto": "^5.2.5",
|
||||||
"@mdi/js": "^7.4.47",
|
"@mdi/js": "^7.4.47",
|
||||||
"@mdi/react": "^1.6.1",
|
"@mdi/react": "^1.6.1",
|
||||||
"@mui/icons-material": "^7.0.0",
|
"@mui/icons-material": "^7.1.0",
|
||||||
"@mui/material": "^7.0.0",
|
"@mui/material": "^7.1.0",
|
||||||
"@mui/x-charts": "^7.28.0",
|
"@mui/x-charts": "^8.3.1",
|
||||||
"@mui/x-data-grid": "^7.28.1",
|
"@mui/x-data-grid": "^8.3.1",
|
||||||
"date-and-time": "^3.6.0",
|
"date-and-time": "^3.6.0",
|
||||||
"filesize": "^10.1.6",
|
"filesize": "^10.1.6",
|
||||||
"humanize-duration": "^3.29.0",
|
"humanize-duration": "^3.32.2",
|
||||||
"mui-file-input": "^7.0.0",
|
"react": "^19.1.0",
|
||||||
"react": "^19.0.0",
|
"react-dom": "^19.1.0",
|
||||||
"react-dom": "^19.0.0",
|
"react-router-dom": "^7.6.0",
|
||||||
"react-router-dom": "^7.4.0",
|
|
||||||
"react-syntax-highlighter": "^15.6.1",
|
"react-syntax-highlighter": "^15.6.1",
|
||||||
"react-vnc": "^3.0.7",
|
"react-vnc": "^3.1.0",
|
||||||
"uuid": "^11.1.0",
|
"uuid": "^11.1.0",
|
||||||
"xml-formatter": "^3.6.0"
|
"xml-formatter": "^3.6.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.21.0",
|
"@eslint/js": "^9.27.0",
|
||||||
"@types/humanize-duration": "^3.27.1",
|
"@types/humanize-duration": "^3.27.1",
|
||||||
"@types/jest": "^29.5.14",
|
"@types/jest": "^29.5.14",
|
||||||
"@types/react": "^19.0.12",
|
"@types/react": "^19.1.4",
|
||||||
"@types/react-dom": "^19.0.4",
|
"@types/react-dom": "^19.1.5",
|
||||||
"@types/react-syntax-highlighter": "^15.5.13",
|
"@types/react-syntax-highlighter": "^15.5.13",
|
||||||
"@types/uuid": "^10.0.0",
|
"@types/uuid": "^10.0.0",
|
||||||
"@vitejs/plugin-react": "^4.3.4",
|
"@vitejs/plugin-react": "^4.4.1",
|
||||||
"eslint": "^9.21.0",
|
"eslint": "^9.27.0",
|
||||||
"eslint-plugin-react-dom": "^1.38.3",
|
"eslint-plugin-react-dom": "^1.49.0",
|
||||||
"eslint-plugin-react-hooks": "^5.1.0",
|
"eslint-plugin-react-hooks": "^5.1.0",
|
||||||
"eslint-plugin-react-refresh": "^0.4.19",
|
"eslint-plugin-react-refresh": "^0.4.20",
|
||||||
"eslint-plugin-react-x": "^1.38.3",
|
"eslint-plugin-react-x": "^1.49.0",
|
||||||
"globals": "^15.15.0",
|
"globals": "^16.1.0",
|
||||||
"typescript": "^5.8.2",
|
"typescript": "^5.8.3",
|
||||||
"typescript-eslint": "^8.24.1",
|
"typescript-eslint": "^8.32.1",
|
||||||
"vite": "^6.2.3"
|
"vite": "^6.3.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import DeleteIcon from "@mui/icons-material/Delete";
|
import DeleteIcon from "@mui/icons-material/Delete";
|
||||||
|
import DownloadIcon from "@mui/icons-material/Download";
|
||||||
import {
|
import {
|
||||||
Alert,
|
Alert,
|
||||||
Button,
|
Button,
|
||||||
@ -9,21 +10,20 @@ import {
|
|||||||
Tooltip,
|
Tooltip,
|
||||||
Typography,
|
Typography,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import DownloadIcon from "@mui/icons-material/Download";
|
|
||||||
import { DataGrid, GridColDef } from "@mui/x-data-grid";
|
import { DataGrid, GridColDef } from "@mui/x-data-grid";
|
||||||
import { filesize } from "filesize";
|
import { filesize } from "filesize";
|
||||||
import { MuiFileInput } from "mui-file-input";
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { IsoFile, 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 { useConfirm } from "../hooks/providers/ConfirmDialogProvider";
|
||||||
import { useLoadingMessage } from "../hooks/providers/LoadingMessageProvider";
|
import { useLoadingMessage } from "../hooks/providers/LoadingMessageProvider";
|
||||||
import { useSnackbar } from "../hooks/providers/SnackbarProvider";
|
import { useSnackbar } from "../hooks/providers/SnackbarProvider";
|
||||||
|
import { downloadBlob } from "../utils/FilesUtils";
|
||||||
import { AsyncWidget } from "../widgets/AsyncWidget";
|
import { AsyncWidget } from "../widgets/AsyncWidget";
|
||||||
|
import { FileInput } from "../widgets/forms/FileInput";
|
||||||
import { VirtWebPaper } from "../widgets/VirtWebPaper";
|
import { VirtWebPaper } from "../widgets/VirtWebPaper";
|
||||||
import { VirtWebRouteContainer } from "../widgets/VirtWebRouteContainer";
|
import { VirtWebRouteContainer } from "../widgets/VirtWebRouteContainer";
|
||||||
import { useConfirm } from "../hooks/providers/ConfirmDialogProvider";
|
|
||||||
import { downloadBlob } from "../utils/FilesUtils";
|
|
||||||
|
|
||||||
export function IsoFilesRoute(): React.ReactElement {
|
export function IsoFilesRoute(): React.ReactElement {
|
||||||
const [list, setList] = React.useState<IsoFile[] | undefined>();
|
const [list, setList] = React.useState<IsoFile[] | undefined>();
|
||||||
@ -116,7 +116,7 @@ function UploadIsoFileCard(p: {
|
|||||||
return (
|
return (
|
||||||
<VirtWebPaper label="File upload">
|
<VirtWebPaper label="File upload">
|
||||||
<div style={{ display: "flex", alignItems: "center" }}>
|
<div style={{ display: "flex", alignItems: "center" }}>
|
||||||
<MuiFileInput
|
<FileInput
|
||||||
value={value}
|
value={value}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
style={{ flex: 1 }}
|
style={{ flex: 1 }}
|
||||||
|
77
virtweb_frontend/src/widgets/forms/FileInput.tsx
Normal file
77
virtweb_frontend/src/widgets/forms/FileInput.tsx
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
import AttachFileIcon from "@mui/icons-material/AttachFile";
|
||||||
|
import ClearIcon from "@mui/icons-material/Clear";
|
||||||
|
import {
|
||||||
|
IconButton,
|
||||||
|
InputAdornment,
|
||||||
|
OutlinedInputProps,
|
||||||
|
TextField,
|
||||||
|
TextFieldSlotsAndSlotProps,
|
||||||
|
Tooltip,
|
||||||
|
} from "@mui/material";
|
||||||
|
import { filesize } from "filesize";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
export function FileInput(
|
||||||
|
p: {
|
||||||
|
label?: string;
|
||||||
|
value: File | null;
|
||||||
|
onChange: (file: File | null) => void;
|
||||||
|
style?: React.CSSProperties;
|
||||||
|
} & TextFieldSlotsAndSlotProps<OutlinedInputProps>
|
||||||
|
): React.ReactElement {
|
||||||
|
const fileInput = React.useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TextField
|
||||||
|
onClick={() => fileInput.current?.click()}
|
||||||
|
{...p}
|
||||||
|
value={""}
|
||||||
|
onChange={console.log}
|
||||||
|
type="file"
|
||||||
|
slotProps={{
|
||||||
|
input: {
|
||||||
|
startAdornment: (
|
||||||
|
<>
|
||||||
|
<InputAdornment position="start">
|
||||||
|
<AttachFileIcon />
|
||||||
|
|
||||||
|
{p.value ? p.value.name : "Insert a file"}
|
||||||
|
</InputAdornment>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
endAdornment: p.value ? (
|
||||||
|
<InputAdornment position="end">
|
||||||
|
{filesize(p.value.size)}
|
||||||
|
<Tooltip
|
||||||
|
title="Remove attached file"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<IconButton
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
p.onChange(null);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ClearIcon />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
</InputAdornment>
|
||||||
|
) : undefined,
|
||||||
|
|
||||||
|
onChange: (e) => {
|
||||||
|
const files = (e.target as any).files;
|
||||||
|
p.onChange(files.length > 0 ? files[0] : null);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
htmlInput: {
|
||||||
|
ref: fileInput,
|
||||||
|
style: { opacity: 0 },
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
placeholder={"Insert a file"}
|
||||||
|
variant="outlined"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
Reference in New Issue
Block a user