Can generate release builds

This commit is contained in:
Pierre HUBERT 2023-12-12 15:45:56 +01:00
parent f651c756b6
commit e8422f4b3c
11 changed files with 143 additions and 11 deletions

9
Makefile Normal file
View File

@ -0,0 +1,9 @@
all: frontend backend
frontend:
cd virtweb_frontend && npm run build && cd ..
rm -rf virtweb_backend/static
mv virtweb_frontend/build virtweb_backend/static
backend: frontend
cd virtweb_backend && cargo clippy -- -D warnings && cargo build --release

View File

@ -1,3 +1,4 @@
target/ target/
.idea .idea
storage storage
static

View File

@ -2022,6 +2022,42 @@ dependencies = [
"winreg", "winreg",
] ]
[[package]]
name = "rust-embed"
version = "8.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1e7d90385b59f0a6bf3d3b757f3ca4ece2048265d70db20a2016043d4509a40"
dependencies = [
"actix-web",
"mime_guess",
"rust-embed-impl",
"rust-embed-utils",
"walkdir",
]
[[package]]
name = "rust-embed-impl"
version = "8.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c3d8c6fd84090ae348e63a84336b112b5c3918b3bf0493a581f7bd8ee623c29"
dependencies = [
"proc-macro2",
"quote",
"rust-embed-utils",
"syn 2.0.29",
"walkdir",
]
[[package]]
name = "rust-embed-utils"
version = "8.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "873feff8cb7bf86fdf0a71bb21c95159f4e4a37dd7a4bd1855a940909b583ada"
dependencies = [
"sha2",
"walkdir",
]
[[package]] [[package]]
name = "rustc-demangle" name = "rustc-demangle"
version = "0.1.23" version = "0.1.23"
@ -2056,6 +2092,15 @@ version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
[[package]]
name = "same-file"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
dependencies = [
"winapi-util",
]
[[package]] [[package]]
name = "schannel" name = "schannel"
version = "0.1.22" version = "0.1.22"
@ -2629,9 +2674,11 @@ dependencies = [
"lazy_static", "lazy_static",
"light-openid", "light-openid",
"log", "log",
"mime_guess",
"num", "num",
"rand", "rand",
"reqwest", "reqwest",
"rust-embed",
"serde", "serde",
"serde-xml-rs", "serde-xml-rs",
"serde_json", "serde_json",
@ -2644,6 +2691,16 @@ dependencies = [
"virt", "virt",
] ]
[[package]]
name = "walkdir"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee"
dependencies = [
"same-file",
"winapi-util",
]
[[package]] [[package]]
name = "want" name = "want"
version = "0.3.1" version = "0.3.1"

View File

@ -41,3 +41,5 @@ tokio = "1.32.0"
futures = "0.3.28" futures = "0.3.28"
ipnetwork = "0.20.0" ipnetwork = "0.20.0"
num = "0.4.1" num = "0.4.1"
rust-embed = { version = "8.0.0", features = ["actix"] }
mime_guess = "2.0.4"

View File

@ -12,10 +12,17 @@ pub struct AppConfig {
#[clap(short, long, env, default_value = "0.0.0.0:8000")] #[clap(short, long, env, default_value = "0.0.0.0:8000")]
pub listen_address: String, pub listen_address: String,
/// Website origin /// Website main origin
#[clap(short, long, env, default_value = "http://localhost:3000")] #[clap(short, long, env, default_value = "http://localhost:3000")]
pub website_origin: String, pub website_origin: String,
/// Additional allowed website origin
///
/// Warning! These origins won't be usable for OpenID authentication,
/// only for local auth
#[clap(long, env)]
pub additional_origins: Vec<String>,
/// Proxy IP, might end with a star "*" /// Proxy IP, might end with a star "*"
#[clap(short, long, env)] #[clap(short, long, env)]
pub proxy_ip: Option<String>, pub proxy_ip: Option<String>,
@ -74,7 +81,7 @@ pub struct AppConfig {
oidc_redirect_url: String, oidc_redirect_url: String,
/// Storage directory /// Storage directory
#[arg(long, env, default_value = "storage")] #[arg(short, long, env, default_value = "storage")]
pub storage: String, pub storage: String,
/// Directory where temporary files are stored /// Directory where temporary files are stored

View File

@ -9,6 +9,7 @@ pub mod auth_controller;
pub mod iso_controller; pub mod iso_controller;
pub mod network_controller; pub mod network_controller;
pub mod server_controller; pub mod server_controller;
pub mod static_controller;
pub mod vm_controller; pub mod vm_controller;
/// Custom error to ease controller writing /// Custom error to ease controller writing

View File

@ -8,10 +8,6 @@ use crate::libvirt_rest_structures::HypervisorInfo;
use actix_web::{HttpResponse, Responder}; use actix_web::{HttpResponse, Responder};
use sysinfo::{NetworksExt, System, SystemExt}; use sysinfo::{NetworksExt, System, SystemExt};
pub async fn root_index() -> impl Responder {
HttpResponse::Ok().body("Hello world!")
}
#[derive(serde::Serialize)] #[derive(serde::Serialize)]
struct StaticConfig { struct StaticConfig {
auth_disabled: bool, auth_disabled: bool,

View File

@ -0,0 +1,45 @@
#[cfg(debug_assertions)]
pub use serve_static_debug::{root_index, serve_static_content};
#[cfg(not(debug_assertions))]
pub use serve_static_release::{root_index, serve_static_content};
#[cfg(debug_assertions)]
mod serve_static_debug {
use actix_web::{HttpResponse, Responder};
pub async fn root_index() -> impl Responder {
HttpResponse::Ok().body("Hello world! Debug=on for VirtWeb!")
}
pub async fn serve_static_content() -> impl Responder {
HttpResponse::NotFound().body("Hello world! Static assets are not served in debug mode")
}
}
#[cfg(not(debug_assertions))]
mod serve_static_release {
use actix_web::{web, HttpResponse, Responder};
use rust_embed::RustEmbed;
#[derive(RustEmbed)]
#[folder = "static/"]
struct Asset;
fn handle_embedded_file(path: &str, can_fallback: bool) -> HttpResponse {
match (Asset::get(path), can_fallback) {
(Some(content), _) => HttpResponse::Ok()
.content_type(mime_guess::from_path(path).first_or_octet_stream().as_ref())
.body(content.data.into_owned()),
(None, false) => HttpResponse::NotFound().body("404 Not Found"),
(None, true) => handle_embedded_file("index.html", false),
}
}
pub async fn root_index() -> impl Responder {
handle_embedded_file("index.html", false)
}
pub async fn serve_static_content(path: web::Path<String>) -> impl Responder {
handle_embedded_file(path.as_ref(), !path.as_ref().starts_with("static/"))
}
}

View File

@ -22,7 +22,8 @@ use virtweb_backend::constants::{
MAX_INACTIVITY_DURATION, MAX_SESSION_DURATION, SESSION_COOKIE_NAME, MAX_INACTIVITY_DURATION, MAX_SESSION_DURATION, SESSION_COOKIE_NAME,
}; };
use virtweb_backend::controllers::{ use virtweb_backend::controllers::{
auth_controller, iso_controller, network_controller, server_controller, vm_controller, auth_controller, iso_controller, network_controller, server_controller, static_controller,
vm_controller,
}; };
use virtweb_backend::libvirt_client::LibVirtClient; use virtweb_backend::libvirt_client::LibVirtClient;
use virtweb_backend::middlewares::auth_middleware::AuthChecker; use virtweb_backend::middlewares::auth_middleware::AuthChecker;
@ -69,7 +70,7 @@ async fn main() -> std::io::Result<()> {
.login_deadline(Some(Duration::from_secs(MAX_SESSION_DURATION))) .login_deadline(Some(Duration::from_secs(MAX_SESSION_DURATION)))
.build(); .build();
let cors = Cors::default() let mut cors = Cors::default()
.allowed_origin(&AppConfig::get().website_origin) .allowed_origin(&AppConfig::get().website_origin)
.allowed_methods(vec!["GET", "POST", "DELETE", "PUT"]) .allowed_methods(vec!["GET", "POST", "DELETE", "PUT"])
.allowed_headers(vec![header::AUTHORIZATION, header::ACCEPT]) .allowed_headers(vec![header::AUTHORIZATION, header::ACCEPT])
@ -77,6 +78,10 @@ async fn main() -> std::io::Result<()> {
.supports_credentials() .supports_credentials()
.max_age(3600); .max_age(3600);
for additional_origin in &AppConfig::get().additional_origins {
cors = cors.allowed_origin(additional_origin);
}
App::new() App::new()
.wrap(Logger::default()) .wrap(Logger::default())
.wrap(AuthChecker) .wrap(AuthChecker)
@ -93,7 +98,6 @@ async fn main() -> std::io::Result<()> {
.app_data(MultipartFormConfig::default().total_limit(constants::ISO_MAX_SIZE)) .app_data(MultipartFormConfig::default().total_limit(constants::ISO_MAX_SIZE))
.app_data(TempFileConfig::default().directory(&AppConfig::get().temp_dir)) .app_data(TempFileConfig::default().directory(&AppConfig::get().temp_dir))
// Server controller // Server controller
.route("/", web::get().to(server_controller::root_index))
.route( .route(
"/api/server/static_config", "/api/server/static_config",
web::get().to(server_controller::static_config), web::get().to(server_controller::static_config),
@ -231,6 +235,12 @@ async fn main() -> std::io::Result<()> {
"/api/network/{uid}/stop", "/api/network/{uid}/stop",
web::get().to(network_controller::stop), web::get().to(network_controller::stop),
) )
// Static assets
.route("/", web::get().to(static_controller::root_index))
.route(
"/{tail:.*}",
web::get().to(static_controller::serve_static_content),
)
}) })
.bind(&AppConfig::get().listen_address)? .bind(&AppConfig::get().listen_address)?
.run() .run()

View File

@ -69,7 +69,10 @@ where
let auth_disabled = AppConfig::get().unsecure_disable_auth; let auth_disabled = AppConfig::get().unsecure_disable_auth;
// Check authentication, if required // Check authentication, if required
if !auth_disabled && !constants::ROUTES_WITHOUT_AUTH.contains(&req.path()) { if !auth_disabled
&& !constants::ROUTES_WITHOUT_AUTH.contains(&req.path())
&& req.path().starts_with("/api/")
{
let auth = match AuthExtractor::from_request(req.request(), &mut Payload::None) let auth = match AuthExtractor::from_request(req.request(), &mut Payload::None)
.into_inner() .into_inner()
{ {

View File

@ -0,0 +1 @@
REACT_APP_BACKEND=/api