Compare commits
	
		
			1 Commits
		
	
	
		
			master
			...
			e33b79f7d5
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| e33b79f7d5 | 
| @@ -5,7 +5,7 @@ name: default | ||||
|  | ||||
| steps: | ||||
| - name: frontend_build | ||||
|   image: node:24 | ||||
|   image: node:23 | ||||
|   volumes: | ||||
|     - name: frontend_app | ||||
|       path: /tmp/frontend_build | ||||
|   | ||||
							
								
								
									
										11
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								README.md
									
									
									
									
									
								
							| @@ -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. | ||||
|   | ||||
							
								
								
									
										1068
									
								
								remote_backend/Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1068
									
								
								remote_backend/Cargo.lock
									
									
									
										generated
									
									
									
								
							
										
											
												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.10.1", 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.1" | ||||
| mime_guess = "2.0.5" | ||||
| rust-embed = { version = "8.7.2" } | ||||
| 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; | ||||
|  | ||||
|   | ||||
							
								
								
									
										18
									
								
								remote_frontend/.eslintrc.cjs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								remote_frontend/.eslintrc.cjs
									
									
									
									
									
										Normal file
									
								
							| @@ -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 }, | ||||
|       ], | ||||
|     }, | ||||
|   }, | ||||
| ) | ||||
							
								
								
									
										3996
									
								
								remote_frontend/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										3996
									
								
								remote_frontend/package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -10,25 +10,22 @@ | ||||
|     "preview": "vite preview" | ||||
|   }, | ||||
|   "dependencies": { | ||||
|     "@fluentui/react-components": "^9.72.3", | ||||
|     "@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.38.0", | ||||
|     "@types/react": "^18.3.26", | ||||
|     "@types/react-dom": "^18.3.7", | ||||
|     "@typescript-eslint/eslint-plugin": "^8.46.2", | ||||
|     "@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} • RAM :{" "} | ||||
|                   {filesize(item.memory)} •{" "} | ||||
|                   {filesize(item.memory * 1000 * 1000)} •{" "} | ||||
|                   {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} • RAM : {filesize(p.vm.memory)}{" "} | ||||
|         {p.vm.architecture} • RAM : {filesize(p.vm.memory * 1000 * 1000)}{" "} | ||||
|         • {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 | ||||
|     } | ||||
|   ] | ||||
| } | ||||
		Reference in New Issue
	
	Block a user