1 Commits

Author SHA1 Message Date
841f410abb Update dependency @typescript-eslint/parser to v8.19.1 2025-01-07 00:29:09 +00:00
23 changed files with 2316 additions and 2868 deletions

@@ -5,7 +5,7 @@ name: default
steps:
- name: frontend_build
image: node:24
image: node:23
volumes:
- name: frontend_app
path: /tmp/frontend_build

@@ -1,11 +1,4 @@
# VirtWeb Remote
Web UI that allows to start and stop VMs managed by VirtWEB without having to expose the VirtWEB directly on the Internet.
WIP project
VirtWebRemote rely on OpenID to authenticate users.
VirtWebRemote authenticates against VirtWEB API using an API token. Both the token ID and private key are required to be able to authenticate against the VirtWEB API.
## Docker image options
```bash
docker run --rm -it pierre42100/virtweb_remote --help
```
This project aims to use the VirtWeb API to start and stop VM without directly exposing the VirtWEB API to the Internet.

File diff suppressed because it is too large Load Diff

@@ -1,28 +1,28 @@
[package]
name = "remote_backend"
version = "0.1.0"
edition = "2024"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
log = "0.4.28"
env_logger = "0.11.8"
clap = { version = "4.5.51", features = ["derive", "env"] }
serde = { version = "1.0.228", features = ["derive"] }
light-openid = { version = "1.0.4", features = ["crypto-wrapper"] }
basic-jwt = "0.3.0"
actix-web = "4.11.0"
log = "0.4.21"
env_logger = "0.11.3"
clap = { version = "4.5.21", features = ["derive", "env"] }
serde = { version = "1.0.215", features = ["derive"] }
light-openid = { version = "1.0.2", features = ["crypto-wrapper"] }
basic-jwt = "0.2.0"
actix-web = "4.5.1"
actix-remote-ip = "0.1.0"
actix-session = { version = "0.11.0", features = ["cookie-session"] }
actix-identity = "0.9.0"
actix-cors = "0.7.1"
lazy_static = "1.5.0"
anyhow = "1.0.100"
reqwest = { version = "0.12.24", features = ["json"] }
thiserror = "2.0.17"
uuid = { version = "1.18.1", features = ["v4", "serde"] }
futures-util = "0.3.31"
lazy-regex = "3.4.2"
mime_guess = "2.0.5"
rust-embed = { version = "8.7.2" }
actix-session = { version = "0.10.1", features = ["cookie-session"] }
actix-identity = "0.8.0"
actix-cors = "0.7.0"
lazy_static = "1.4.0"
anyhow = "1.0.93"
reqwest = { version = "0.12.9", features = ["json"] }
thiserror = "2.0.3"
uuid = { version = "1.8.0", features = ["v4", "serde"] }
futures-util = "0.3.30"
lazy-regex = "3.1.0"
mime_guess = "2.0.4"
rust-embed = { version = "8.3.0" }

@@ -1,6 +1,6 @@
use actix_remote_ip::RemoteIP;
use actix_web::web::Data;
use actix_web::{HttpResponse, Responder, web};
use actix_web::{web, HttpResponse, Responder};
use light_openid::basic_state_manager::BasicStateManager;
use crate::app_config::AppConfig;

@@ -1,7 +1,7 @@
use crate::controllers::HttpResult;
use crate::virtweb_client;
use crate::virtweb_client::{GroupID, VMUuid};
use actix_web::{HttpResponse, web};
use actix_web::{web, HttpResponse};
#[derive(serde::Deserialize)]
pub struct GroupIDInPath {

@@ -1,8 +1,9 @@
use actix_web::HttpResponse;
use actix_web::body::BoxBody;
use actix_web::http::StatusCode;
use actix_web::HttpResponse;
use std::error::Error;
use std::fmt::{Display, Formatter};
use std::io::ErrorKind;
pub mod auth_controller;
pub mod group_controller;
@@ -37,7 +38,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!")
}
@@ -51,7 +52,7 @@ impl From<anyhow::Error> for HttpErr {
impl From<Box<dyn Error>> for HttpErr {
fn from(value: Box<dyn Error>) -> Self {
HttpErr::Err(std::io::Error::other(value.to_string()).into())
HttpErr::Err(std::io::Error::new(ErrorKind::Other, value.to_string()).into())
}
}
@@ -81,7 +82,7 @@ impl From<reqwest::header::ToStrError> for HttpErr {
impl From<actix_web::Error> for HttpErr {
fn from(value: actix_web::Error) -> Self {
HttpErr::Err(std::io::Error::other(value.to_string()).into())
HttpErr::Err(std::io::Error::new(ErrorKind::Other, value.to_string()).into())
}
}

@@ -18,7 +18,7 @@ mod serve_static_debug {
#[cfg(not(debug_assertions))]
mod serve_static_release {
use actix_web::{HttpResponse, Responder, web};
use actix_web::{web, HttpResponse, Responder};
use rust_embed::RustEmbed;
#[derive(RustEmbed)]

@@ -3,7 +3,7 @@
use crate::controllers::HttpResult;
use crate::virtweb_client;
use crate::virtweb_client::VMUuid;
use actix_web::{HttpResponse, web};
use actix_web::{web, HttpResponse};
#[derive(serde::Deserialize)]
pub struct ReqPath {

@@ -1,7 +1,7 @@
use actix_identity::Identity;
use actix_web::dev::Payload;
use actix_web::{Error, FromRequest, HttpMessage, HttpRequest};
use futures_util::future::{Ready, ready};
use futures_util::future::{ready, Ready};
use std::fmt::Display;
pub struct AuthExtractor {

@@ -1,13 +1,13 @@
use actix_cors::Cors;
use actix_identity::config::LogoutBehaviour;
use actix_identity::IdentityMiddleware;
use actix_identity::config::LogoutBehavior;
use actix_remote_ip::RemoteIPConfig;
use actix_session::SessionMiddleware;
use actix_session::storage::CookieSessionStore;
use actix_session::SessionMiddleware;
use actix_web::cookie::{Key, SameSite};
use actix_web::middleware::Logger;
use actix_web::web::Data;
use actix_web::{App, HttpServer, web};
use actix_web::{web, App, HttpServer};
use light_openid::basic_state_manager::BasicStateManager;
use remote_backend::app_config::AppConfig;
use remote_backend::constants;
@@ -37,7 +37,7 @@ async fn main() -> std::io::Result<()> {
.build();
let identity_middleware = IdentityMiddleware::builder()
.logout_behavior(LogoutBehavior::PurgeSession)
.logout_behaviour(LogoutBehaviour::PurgeSession)
.visit_deadline(Some(Duration::from_secs(
constants::MAX_INACTIVITY_DURATION,
)))

@@ -1,4 +1,4 @@
use std::future::{Ready, ready};
use std::future::{ready, Ready};
use std::rc::Rc;
use crate::app_config::AppConfig;
@@ -7,8 +7,8 @@ use crate::extractors::auth_extractor::AuthExtractor;
use actix_web::body::EitherBody;
use actix_web::dev::Payload;
use actix_web::{
dev::{forward_ready, Service, ServiceRequest, ServiceResponse, Transform},
Error, FromRequest, HttpResponse,
dev::{Service, ServiceRequest, ServiceResponse, Transform, forward_ready},
};
use futures_util::future::LocalBoxFuture;

@@ -0,0 +1,18 @@
module.exports = {
root: true,
env: { browser: true, es2020: true },
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react-hooks/recommended',
],
ignorePatterns: ['dist', '.eslintrc.cjs'],
parser: '@typescript-eslint/parser',
plugins: ['react-refresh'],
rules: {
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
}

@@ -1,28 +0,0 @@
import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import tseslint from 'typescript-eslint'
export default tseslint.config(
{ ignores: ['dist'] },
{
extends: [js.configs.recommended, ...tseslint.configs.recommended],
files: ['**/*.{ts,tsx}'],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
},
plugins: {
'react-hooks': reactHooks,
'react-refresh': reactRefresh,
},
rules: {
...reactHooks.configs.recommended.rules,
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
},
)

File diff suppressed because it is too large Load Diff

@@ -10,25 +10,22 @@
"preview": "vite preview"
},
"dependencies": {
"@fluentui/react-components": "^9.72.4",
"@fluentui/react-icons": "^2.0.313",
"filesize": "^11.0.13",
"react": "^18.3.1",
"react-dom": "^18.3.1"
"@fluentui/react-components": "^9.56.3",
"@fluentui/react-icons": "^2.0.266",
"filesize": "^10.1.6",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@eslint/js": "^9.39.1",
"@types/react": "^18.3.26",
"@types/react-dom": "^18.3.7",
"@typescript-eslint/eslint-plugin": "^8.46.3",
"@typescript-eslint/parser": "^8.46.2",
"@vitejs/plugin-react": "^5.1.0",
"eslint": "^9.38.0",
"eslint-plugin-react-hooks": "^5.2.0",
"eslint-plugin-react-refresh": "^0.4.24",
"globals": "^16.4.0",
"typescript": "^5.9.3",
"typescript-eslint": "^8.43.0",
"vite": "^7.1.12"
"@types/react": "^18.3.12",
"@types/react-dom": "^18.3.1",
"@typescript-eslint/eslint-plugin": "^8.16.0",
"@typescript-eslint/parser": "^8.16.0",
"@vitejs/plugin-react": "^4.3.4",
"eslint": "^9.0.0",
"eslint-plugin-react-hooks": "^5.0.0",
"eslint-plugin-react-refresh": "^0.4.14",
"typescript": "^5.7.2",
"vite": "^6.0.1"
}
}

@@ -123,7 +123,7 @@ function GroupInfo(p: { group: VMGroup }): React.ReactElement {
</TableCell>
<TableCell>
{item.architecture} &bull; RAM :{" "}
{filesize(item.memory)} &bull;{" "}
{filesize(item.memory * 1000 * 1000)} &bull;{" "}
{item.number_vcpu} vCPU
</TableCell>
<TableCell>{state?.[item.uuid] ?? ""}</TableCell>

@@ -107,7 +107,7 @@ function VMWidget(p: { vm: VMInfoAndCaps }): React.ReactElement {
}
/>
<p className={styles.caption1} style={{ margin: "0px auto" }}>
{p.vm.architecture} &bull; RAM : {filesize(p.vm.memory)}{" "}
{p.vm.architecture} &bull; RAM : {filesize(p.vm.memory * 1000 * 1000)}{" "}
&bull; {p.vm.number_vcpu} vCPU
</p>

@@ -1,26 +0,0 @@
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
"jsx": "react-jsx",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["src"]
}

@@ -1,7 +1,25 @@
{
"files": [],
"references": [
{ "path": "./tsconfig.app.json" },
{ "path": "./tsconfig.node.json" }
]
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": ["src"],
"references": [{ "path": "./tsconfig.node.json" }]
}

@@ -1,24 +1,11 @@
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"target": "ES2022",
"lib": ["ES2023"],
"module": "ESNext",
"composite": true,
"skipLibCheck": true,
/* Bundler mode */
"module": "ESNext",
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
"allowSyntheticDefaultImports": true,
"strict": true
},
"include": ["vite.config.ts"]
}

@@ -1,7 +1,7 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
// https://vite.dev/config/
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
})

@@ -1,3 +1,9 @@
{
"extends": ["local>renovate/presets"]
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"packageRules": [
{
"matchUpdateTypes": ["major", "minor", "patch"],
"automerge": true
}
]
}