1 Commits

Author SHA1 Message Date
be4e6f3c4b Update dependency @eslint/js to ^9.29.0
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2025-06-23 00:24:21 +00:00
17 changed files with 1007 additions and 1842 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -6,9 +6,9 @@ edition = "2024"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
log = "0.4.28"
log = "0.4.27"
env_logger = "0.11.8"
clap = { version = "4.5.53", features = ["derive", "env"] }
clap = { version = "4.5.40", features = ["derive", "env"] }
light-openid = { version = "1.0.4", features = ["crypto-wrapper"] }
lazy_static = "1.5.0"
actix = "0.13.5"
@@ -17,27 +17,27 @@ actix-remote-ip = "0.1.0"
actix-session = { version = "0.10.1", features = ["cookie-session"] }
actix-identity = "0.8.0"
actix-cors = "0.7.1"
actix-files = "0.6.8"
actix-files = "0.6.6"
actix-ws = "0.3.0"
actix-http = "3.11.2"
actix-http = "3.11.0"
serde = { version = "1.0.219", features = ["derive"] }
serde_json = "1.0.145"
serde_json = "1.0.140"
serde_yml = "0.0.12"
quick-xml = { version = "0.38.3", features = ["serialize", "overlapped-lists"] }
quick-xml = { version = "0.37.5", features = ["serialize", "overlapped-lists"] }
futures-util = "0.3.31"
anyhow = "1.0.100"
anyhow = "1.0.98"
actix-multipart = "0.7.2"
tempfile = "3.20.0"
reqwest = { version = "0.12.24", features = ["stream"] }
url = "2.5.7"
virt = "0.4.3"
sysinfo = { version = "0.36.1", features = ["serde"] }
uuid = { version = "1.17.0", features = ["v4", "serde"] }
lazy-regex = "3.4.2"
thiserror = "2.0.17"
image = "0.25.9"
rand = "0.9.2"
tokio = { version = "1.47.1", features = ["rt", "time", "macros"] }
reqwest = { version = "0.12.20", features = ["stream"] }
url = "2.5.4"
virt = "0.4.2"
sysinfo = { version = "0.35.2", features = ["serde"] }
uuid = { version = "1.16.0", features = ["v4", "serde"] }
lazy-regex = "3.4.1"
thiserror = "2.0.12"
image = "0.25.6"
rand = "0.9.1"
tokio = { version = "1.45.0", features = ["rt", "time", "macros"] }
futures = "0.3.31"
ipnetwork = { version = "0.21.1", features = ["serde"] }
num = "0.4.3"
@@ -45,5 +45,5 @@ rust-embed = { version = "8.7.2", features = ["mime-guess"] }
dotenvy = "0.15.7"
nix = { version = "0.30.1", features = ["net"] }
basic-jwt = "0.3.0"
zip = "4.3.0"
chrono = "0.4.42"
zip = "4.1.0"
chrono = "0.4.41"

View File

@@ -27,7 +27,10 @@ impl LibVirtActor {
/// Connect to hypervisor
pub async fn connect() -> anyhow::Result<Self> {
let hypervisor_uri = AppConfig::get().hypervisor_uri.as_deref().unwrap_or("");
log::info!("Will connect to hypvervisor at address '{hypervisor_uri}'",);
log::info!(
"Will connect to hypvervisor at address '{}'",
hypervisor_uri
);
let conn = Connect::open(Some(hypervisor_uri))?;
Ok(Self { m: conn })
@@ -99,7 +102,7 @@ impl Handler<GetDomainXMLReq> for LibVirtActor {
log::debug!("Get domain XML:\n{}", msg.0.as_string());
let domain = Domain::lookup_by_uuid_string(&self.m, &msg.0.as_string())?;
let xml = domain.get_xml_desc(VIR_DOMAIN_XML_SECURE)?;
log::debug!("XML = {xml}");
log::debug!("XML = {}", xml);
DomainXML::parse_xml(&xml)
}
}
@@ -128,7 +131,7 @@ impl Handler<DefineDomainReq> for LibVirtActor {
fn handle(&mut self, mut msg: DefineDomainReq, _ctx: &mut Self::Context) -> Self::Result {
let xml = msg.1.as_xml()?;
log::debug!("Define domain:\n{xml}");
log::debug!("Define domain:\n{}", xml);
let domain = Domain::define_xml(&self.m, &xml)?;
let uuid = XMLUuid::parse_from_str(&domain.get_uuid_string()?)?;
@@ -443,7 +446,7 @@ impl Handler<GetNetworkXMLReq> for LibVirtActor {
log::debug!("Get network XML:\n{}", msg.0.as_string());
let network = Network::lookup_by_uuid_string(&self.m, &msg.0.as_string())?;
let xml = network.get_xml_desc(0)?;
log::debug!("XML = {xml}");
log::debug!("XML = {}", xml);
NetworkXML::parse_xml(&xml)
}
}
@@ -599,7 +602,7 @@ impl Handler<GetNWFilterXMLReq> for LibVirtActor {
log::debug!("Get network filter XML:\n{}", msg.0.as_string());
let filter = NWFilter::lookup_by_uuid_string(&self.m, &msg.0.as_string())?;
let xml = filter.get_xml_desc(0)?;
log::debug!("XML = {xml}");
log::debug!("XML = {}", xml);
NetworkFilterXML::parse_xml(xml)
}
}
@@ -614,7 +617,7 @@ impl Handler<DefineNWFilterReq> for LibVirtActor {
fn handle(&mut self, mut msg: DefineNWFilterReq, _ctx: &mut Self::Context) -> Self::Result {
let xml = msg.1.into_xml()?;
log::debug!("Define network filter:\n{xml}");
log::debug!("Define network filter:\n{}", xml);
let filter = NWFilter::define_xml(&self.m, &xml)?;
let uuid = XMLUuid::parse_from_str(&filter.get_uuid_string()?)?;

View File

@@ -104,11 +104,11 @@ impl Token {
/// Check whether a token is expired or not
pub fn is_expired(&self) -> bool {
if let Some(max_inactivity) = self.max_inactivity
&& max_inactivity + self.last_used < time()
{
if let Some(max_inactivity) = self.max_inactivity {
if max_inactivity + self.last_used < time() {
return true;
}
}
false
}
@@ -188,11 +188,11 @@ impl NewToken {
return Some(err);
}
if let Some(t) = self.max_inactivity
&& t < 3600
{
if let Some(t) = self.max_inactivity {
if t < 3600 {
return Some("API tokens shall be valid for at least 1 hour!");
}
}
None
}

View File

@@ -280,7 +280,7 @@ impl AppConfig {
/// Get VM vnc sockets path for domain
pub fn vnc_socket_for_domain(&self, name: &str) -> PathBuf {
self.vnc_sockets_path().join(format!("vnc-{name}"))
self.vnc_sockets_path().join(format!("vnc-{}", name))
}
/// Get VM root disks storage directory

View File

@@ -31,13 +31,14 @@ pub async fn upload(MultipartForm(mut form): MultipartForm<UploadDiskImageForm>)
}
// Check file mime type
if let Some(mime_type) = file.content_type
&& !constants::ALLOWED_DISK_IMAGES_MIME_TYPES.contains(&mime_type.as_ref())
{
if let Some(mime_type) = file.content_type {
if !constants::ALLOWED_DISK_IMAGES_MIME_TYPES.contains(&mime_type.as_ref()) {
return Ok(HttpResponse::BadRequest().json(format!(
"Unsupported file type for disk upload: {mime_type}"
"Unsupported file type for disk upload: {}",
mime_type
)));
}
}
// Extract and check file name
let Some(file_name) = file.file_name else {

View File

@@ -31,12 +31,12 @@ pub async fn upload_file(MultipartForm(mut form): MultipartForm<UploadIsoForm>)
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())
{
if let Some(m) = &file.content_type {
if !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 => {
@@ -52,7 +52,7 @@ pub async fn upload_file(MultipartForm(mut form): MultipartForm<UploadIsoForm>)
}
let dest_file = AppConfig::get().iso_storage_path().join(file_name);
log::info!("Will save ISO file {dest_file:?}");
log::info!("Will save ISO file {:?}", dest_file);
if dest_file.exists() {
log::error!("Conflict with uploaded iso file name!");
@@ -87,17 +87,17 @@ pub async fn upload_from_url(req: web::Json<DownloadFromURLReq>) -> HttpResult {
let response = reqwest::get(&req.url).await?;
if let Some(len) = response.content_length()
&& len > constants::ISO_MAX_SIZE.as_bytes() as u64
{
if let Some(len) = response.content_length() {
if 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()?)
{
if let Some(ct) = response.headers().get("content-type") {
if !constants::ALLOWED_ISO_MIME_TYPES.contains(&ct.to_str()?) {
return Ok(HttpResponse::BadRequest().json("Invalid file mimetype!"));
}
}
let mut stream = response.bytes_stream();

View File

@@ -43,7 +43,7 @@ impl actix_web::error::ResponseError for HttpErr {
}
}
fn error_response(&self) -> HttpResponse<BoxBody> {
log::error!("Error while processing request! {self}");
log::error!("Error while processing request! {}", self);
HttpResponse::InternalServerError().body("Failed to execute request!")
}

View File

@@ -128,22 +128,22 @@ impl FromRequest for ApiAuthExtractor {
));
}
if let Some(ip) = token.ip_restriction
&& !ip.contains(remote_ip.0)
{
if let Some(ip) = token.ip_restriction {
if !ip.contains(remote_ip.0) {
log::error!(
"Attempt to use a token for an unauthorized IP! {remote_ip:?} token_id={}",
token.id.0
);
return Err(ErrorUnauthorized("Token cannot be used from this IP!"));
}
}
if token.should_update_last_activity()
&& let Err(e) = api_tokens::refresh_last_used(token.id).await
{
if token.should_update_last_activity() {
if let Err(e) = api_tokens::refresh_last_used(token.id).await {
log::error!("Could not update token last activity! {e}");
return Err(ErrorBadRequest("Couldn't refresh token last activity!"));
}
}
Ok(ApiAuthExtractor { token, claims })
})

View File

@@ -96,29 +96,29 @@ impl NetworkInfo {
return Err(StructureExtraction("network name is invalid!").into());
}
if let Some(n) = &self.title
&& n.contains('\n')
{
if let Some(n) = &self.title {
if n.contains('\n') {
return Err(StructureExtraction("Network title contain newline char!").into());
}
}
if let Some(dev) = &self.device
&& !regex!("^[a-zA-Z0-9]+$").is_match(dev)
{
if let Some(dev) = &self.device {
if !regex!("^[a-zA-Z0-9]+$").is_match(dev) {
return Err(StructureExtraction("Network device name is invalid!").into());
}
if let Some(bridge) = &self.bridge_name
&& !regex!("^[a-zA-Z0-9]+$").is_match(bridge)
{
return Err(StructureExtraction("Network bridge name is invalid!").into());
}
if let Some(domain) = &self.domain
&& !regex!("^[a-zA-Z0-9.]+$").is_match(domain)
{
if let Some(bridge) = &self.bridge_name {
if !regex!("^[a-zA-Z0-9]+$").is_match(bridge) {
return Err(StructureExtraction("Network bridge name is invalid!").into());
}
}
if let Some(domain) = &self.domain {
if !regex!("^[a-zA-Z0-9.]+$").is_match(domain) {
return Err(StructureExtraction("Domain name is invalid!").into());
}
}
let mut ips = Vec::with_capacity(2);
@@ -303,17 +303,17 @@ impl NetworkInfo {
/// Check if at least one NAT definition was specified on this interface
pub fn has_nat_def(&self) -> bool {
if let Some(ipv4) = &self.ip_v4
&& ipv4.nat.is_some()
{
if let Some(ipv4) = &self.ip_v4 {
if ipv4.nat.is_some() {
return true;
}
}
if let Some(ipv6) = &self.ip_v6
&& ipv6.nat.is_some()
{
if let Some(ipv6) = &self.ip_v6 {
if ipv6.nat.is_some() {
return true;
}
}
false
}

View File

@@ -43,12 +43,14 @@ impl From<&String> for NetworkFilterMacAddressOrVar {
fn extract_mac_address_or_var(
n: &Option<NetworkFilterMacAddressOrVar>,
) -> anyhow::Result<Option<String>> {
if let Some(mac) = n
&& !mac.is_valid()
{
return Err(
NetworkFilterExtraction(format!("Invalid mac address or variable! {}", mac.0)).into(),
);
if let Some(mac) = n {
if !mac.is_valid() {
return Err(NetworkFilterExtraction(format!(
"Invalid mac address or variable! {}",
mac.0
))
.into());
}
}
Ok(n.as_ref().map(|n| n.0.to_string()))
@@ -81,34 +83,34 @@ impl<const V: usize> From<&String> for NetworkFilterIPOrVar<V> {
fn extract_ip_or_var<const V: usize>(
n: &Option<NetworkFilterIPOrVar<V>>,
) -> anyhow::Result<Option<String>> {
if let Some(ip) = n
&& !ip.is_valid()
{
if let Some(ip) = n {
if !ip.is_valid() {
return Err(NetworkFilterExtraction(format!(
"Invalid IPv{V} address or variable! {}",
ip.0
))
.into());
}
}
Ok(n.as_ref().map(|n| n.0.to_string()))
}
fn extract_ip_mask<const V: usize>(n: Option<u8>) -> anyhow::Result<Option<u8>> {
if let Some(mask) = n
&& !net_utils::is_mask_valid(V, mask)
{
if let Some(mask) = n {
if !net_utils::is_mask_valid(V, mask) {
return Err(NetworkFilterExtraction(format!("Invalid IPv{V} mask! {mask}")).into());
}
}
Ok(n)
}
fn extract_nw_filter_comment(n: &Option<String>) -> anyhow::Result<Option<String>> {
if let Some(comment) = n
&& (comment.len() > 256 || comment.contains('\"') || comment.contains('\n'))
{
return Err(NetworkFilterExtraction(format!("Invalid comment! {comment}")).into());
if let Some(comment) = n {
if comment.len() > 256 || comment.contains('\"') || comment.contains('\n') {
return Err(NetworkFilterExtraction(format!("Invalid comment! {}", comment)).into());
}
}
Ok(n.clone())
@@ -867,10 +869,12 @@ impl NetworkFilter {
);
}
if let Some(priority) = self.priority
&& !(-1000..=1000).contains(&priority)
{
return Err(NetworkFilterExtraction("Network priority is invalid!".to_string()).into());
if let Some(priority) = self.priority {
if !(-1000..=1000).contains(&priority) {
return Err(
NetworkFilterExtraction("Network priority is invalid!".to_string()).into(),
);
}
}
for fref in &self.join_filters {

View File

@@ -118,23 +118,23 @@ impl VMInfo {
XMLUuid::new_random()
};
if let Some(n) = &self.genid
&& !n.is_valid()
{
if let Some(n) = &self.genid {
if !n.is_valid() {
return Err(StructureExtraction("VM genid is invalid!").into());
}
if let Some(n) = &self.title
&& n.contains('\n')
{
return Err(StructureExtraction("VM title contain newline char!").into());
}
if let Some(group) = &self.group
&& !regex!("^[a-zA-Z0-9]+$").is_match(&group.0)
{
if let Some(n) = &self.title {
if n.contains('\n') {
return Err(StructureExtraction("VM title contain newline char!").into());
}
}
if let Some(group) = &self.group {
if !regex!("^[a-zA-Z0-9]+$").is_match(&group.0) {
return Err(StructureExtraction("VM group name is invalid!").into());
}
}
if self.memory < constants::MIN_VM_MEMORY || self.memory > constants::MAX_VM_MEMORY {
return Err(StructureExtraction("VM memory is invalid!").into());

View File

@@ -69,7 +69,8 @@ where
if !AppConfig::get().is_allowed_ip(remote_ip.0) {
log::error!(
"An attempt to access VirtWeb from an unauthorized network has been intercepted! {remote_ip:?}"
"An attempt to access VirtWeb from an unauthorized network has been intercepted! {:?}",
remote_ip
);
return Ok(req
.into_response(

View File

@@ -60,11 +60,11 @@ pub struct Nat<IPv> {
impl<IPv> Nat<IPv> {
pub fn check(&self) -> anyhow::Result<()> {
if let NatSourceIP::Interface { name } = &self.host_ip
&& !net_utils::is_net_interface_name_valid(name)
{
if let NatSourceIP::Interface { name } = &self.host_ip {
if !net_utils::is_net_interface_name_valid(name) {
return Err(NatDefError::InvalidNatDef("Invalid nat interface name!").into());
}
}
if let NatHostPort::Range { start, end } = &self.host_port {
if *start == 0 {
@@ -84,11 +84,11 @@ impl<IPv> Nat<IPv> {
return Err(NatDefError::InvalidNatDef("Invalid guest port!").into());
}
if let Some(comment) = &self.comment
&& comment.len() > constants::NET_NAT_COMMENT_MAX_SIZE
{
if let Some(comment) = &self.comment {
if comment.len() > constants::NET_NAT_COMMENT_MAX_SIZE {
return Err(NatDefError::InvalidNatDef("Comment is too large!").into());
}
}
Ok(())
}

View File

@@ -81,10 +81,10 @@ impl CloudInitConfig {
// Process metadata
let mut metadatas = vec![];
if let Some(inst_id) = &self.instance_id {
metadatas.push(format!("instance-id: {inst_id}"));
metadatas.push(format!("instance-id: {}", inst_id));
}
if let Some(local_hostname) = &self.local_hostname {
metadatas.push(format!("local-hostname: {local_hostname}"));
metadatas.push(format!("local-hostname: {}", local_hostname));
}
if let Some(dsmode) = &self.dsmode {
metadatas.push(format!(

File diff suppressed because it is too large Load Diff

View File

@@ -11,46 +11,46 @@
},
"dependencies": {
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.1",
"@fontsource/roboto": "^5.2.8",
"@emotion/styled": "^11.14.0",
"@fontsource/roboto": "^5.2.6",
"@mdi/js": "^7.4.47",
"@mdi/react": "^1.6.1",
"@monaco-editor/react": "^4.7.0",
"@mui/icons-material": "^7.3.5",
"@mui/material": "^7.3.5",
"@mui/icons-material": "^7.1.2",
"@mui/material": "^7.1.2",
"@mui/x-charts": "^8.3.1",
"@mui/x-data-grid": "^8.17.0",
"@mui/x-data-grid": "^8.5.3",
"date-and-time": "^3.6.0",
"filesize": "^10.1.6",
"humanize-duration": "^3.33.1",
"humanize-duration": "^3.32.2",
"monaco-editor": "^0.52.2",
"monaco-yaml": "^5.4.0",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"react-router-dom": "^7.9.6",
"react-syntax-highlighter": "^15.6.6",
"react": "^19.1.0",
"react-dom": "^19.1.0",
"react-router-dom": "^7.6.2",
"react-syntax-highlighter": "^15.6.1",
"react-vnc": "^3.1.0",
"uuid": "^11.1.0",
"xml-formatter": "^3.6.6",
"yaml": "^2.8.1"
"yaml": "^2.8.0"
},
"devDependencies": {
"@eslint/js": "^9.39.1",
"@eslint/js": "^9.29.0",
"@types/humanize-duration": "^3.27.4",
"@types/jest": "^30.0.0",
"@types/react": "^19.2.6",
"@types/react-dom": "^19.2.3",
"@types/jest": "^29.5.14",
"@types/react": "^19.1.8",
"@types/react-dom": "^19.1.6",
"@types/react-syntax-highlighter": "^15.5.13",
"@types/uuid": "^10.0.0",
"@vitejs/plugin-react": "^4.7.0",
"eslint": "^9.39.1",
"eslint-plugin-react-dom": "^1.53.1",
"@vitejs/plugin-react": "^4.5.2",
"eslint": "^9.27.0",
"eslint-plugin-react-dom": "^1.49.0",
"eslint-plugin-react-hooks": "^5.2.0",
"eslint-plugin-react-refresh": "^0.4.24",
"eslint-plugin-react-x": "^1.53.1",
"globals": "^16.3.0",
"typescript": "^5.9.3",
"typescript-eslint": "^8.43.0",
"vite": "^6.3.6"
"eslint-plugin-react-refresh": "^0.4.20",
"eslint-plugin-react-x": "^1.49.0",
"globals": "^16.1.0",
"typescript": "^5.8.3",
"typescript-eslint": "^8.32.1",
"vite": "^6.3.5"
}
}