use actix::Actor; use actix_cors::Cors; use actix_identity::config::LogoutBehaviour; use actix_identity::IdentityMiddleware; use actix_multipart::form::tempfile::TempFileConfig; use actix_multipart::form::MultipartFormConfig; use actix_remote_ip::RemoteIPConfig; use actix_session::storage::CookieSessionStore; use actix_session::SessionMiddleware; use actix_web::cookie::{Key, SameSite}; use actix_web::http::header; use actix_web::middleware::Logger; use actix_web::web::Data; use actix_web::{web, App, HttpServer}; use light_openid::basic_state_manager::BasicStateManager; use std::time::Duration; use virtweb_backend::actors::libvirt_actor::LibVirtActor; use virtweb_backend::actors::vnc_tokens_actor::VNCTokensManager; use virtweb_backend::app_config::AppConfig; use virtweb_backend::constants; use virtweb_backend::constants::{ MAX_INACTIVITY_DURATION, MAX_SESSION_DURATION, SESSION_COOKIE_NAME, }; use virtweb_backend::controllers::{ api_tokens_controller, auth_controller, groups_controller, iso_controller, network_controller, nwfilter_controller, server_controller, static_controller, vm_controller, }; use virtweb_backend::libvirt_client::LibVirtClient; use virtweb_backend::middlewares::auth_middleware::AuthChecker; use virtweb_backend::nat::nat_conf_mode; use virtweb_backend::utils::files_utils; #[actix_web::main] async fn main() -> std::io::Result<()> { env_logger::init_from_env(env_logger::Env::new().default_filter_or("info")); // Run in NAT configuration mode, if requested if std::env::var(constants::NAT_MODE_ENV_VAR_NAME).is_ok() { nat_conf_mode::sub_main().await.unwrap(); return Ok(()); } // Load additional config from file, if requested AppConfig::parse_env_file().unwrap(); log::debug!("Create required directory, if missing"); files_utils::create_directory_if_missing(AppConfig::get().iso_storage_path()).unwrap(); files_utils::create_directory_if_missing(AppConfig::get().vnc_sockets_path()).unwrap(); files_utils::set_file_permission(AppConfig::get().vnc_sockets_path(), 0o777).unwrap(); files_utils::create_directory_if_missing(AppConfig::get().disks_storage_path()).unwrap(); files_utils::create_directory_if_missing(AppConfig::get().nat_path()).unwrap(); files_utils::create_directory_if_missing(AppConfig::get().definitions_path()).unwrap(); files_utils::create_directory_if_missing(AppConfig::get().api_tokens_path()).unwrap(); let conn = Data::new(LibVirtClient( LibVirtActor::connect() .await .expect("Failed to connect to hypervisor!") .start(), )); let vnc_tokens = Data::new(VNCTokensManager::start()); log::info!("Start to listen on {}", AppConfig::get().listen_address); let state_manager = Data::new(BasicStateManager::new()); HttpServer::new(move || { let session_mw = SessionMiddleware::builder( CookieSessionStore::default(), Key::from(AppConfig::get().secret().as_bytes()), ) .cookie_name(SESSION_COOKIE_NAME.to_string()) .cookie_secure(AppConfig::get().cookie_secure) .cookie_same_site(SameSite::Strict) .cookie_domain(AppConfig::get().cookie_domain()) .cookie_http_only(true) .build(); let identity_middleware = IdentityMiddleware::builder() .logout_behaviour(LogoutBehaviour::PurgeSession) .visit_deadline(Some(Duration::from_secs(MAX_INACTIVITY_DURATION))) .login_deadline(Some(Duration::from_secs(MAX_SESSION_DURATION))) .build(); let mut cors = Cors::default() .allowed_origin(&AppConfig::get().website_origin) .allowed_methods(vec!["GET", "POST", "DELETE", "PUT", "PATCH"]) .allowed_headers(vec![header::AUTHORIZATION, header::ACCEPT]) .allowed_header(header::CONTENT_TYPE) .supports_credentials() .max_age(3600); for additional_origin in &AppConfig::get().additional_origins { cors = cors.allowed_origin(additional_origin); } App::new() .wrap(Logger::default()) .wrap(AuthChecker) .wrap(identity_middleware) .wrap(session_mw) .wrap(cors) .app_data(state_manager.clone()) .app_data(vnc_tokens.clone()) .app_data(Data::new(RemoteIPConfig { proxy: AppConfig::get().proxy_ip.clone(), })) .app_data(conn.clone()) // Uploaded files .app_data(MultipartFormConfig::default().total_limit(constants::ISO_MAX_SIZE)) .app_data(TempFileConfig::default().directory(&AppConfig::get().temp_dir)) // Server controller .route( "/api/server/static_config", web::get().to(server_controller::static_config), ) .route( "/api/server/info", web::get().to(server_controller::server_info), ) .route( "/api/server/network_hook_status", web::get().to(server_controller::network_hook_status), ) .route( "/api/server/number_vcpus", web::get().to(server_controller::number_vcpus), ) .route( "/api/server/networks", web::get().to(server_controller::networks_list), ) // Auth controller .route( "/api/auth/local", web::post().to(auth_controller::local_auth), ) .route( "/api/auth/start_oidc", web::get().to(auth_controller::start_oidc), ) .route( "/api/auth/finish_oidc", web::post().to(auth_controller::finish_oidc), ) .route( "/api/auth/user", web::get().to(auth_controller::current_user), ) .route( "/api/auth/sign_out", web::get().to(auth_controller::sign_out), ) // ISO controller .route( "/api/iso/upload", web::post().to(iso_controller::upload_file), ) .route( "/api/iso/upload_from_url", web::post().to(iso_controller::upload_from_url), ) .route("/api/iso/list", web::get().to(iso_controller::get_list)) .route( "/api/iso/{filename}", web::get().to(iso_controller::download_file), ) .route( "/api/iso/{filename}", web::delete().to(iso_controller::delete_file), ) // Virtual machines controller .route("/api/vm/create", web::post().to(vm_controller::create)) .route("/api/vm/list", web::get().to(vm_controller::list_all)) .route("/api/vm/{uid}", web::get().to(vm_controller::get_single)) .route( "/api/vm/{uid}/src", web::get().to(vm_controller::get_single_src_def), ) .route( "/api/vm/{uid}/autostart", web::get().to(vm_controller::get_autostart), ) .route( "/api/vm/{uid}/autostart", web::put().to(vm_controller::set_autostart), ) .route("/api/vm/{uid}", web::put().to(vm_controller::update)) .route("/api/vm/{uid}", web::delete().to(vm_controller::delete)) .route("/api/vm/{uid}/start", web::get().to(vm_controller::start)) .route( "/api/vm/{uid}/shutdown", web::get().to(vm_controller::shutdown), ) .route("/api/vm/{uid}/kill", web::get().to(vm_controller::kill)) .route("/api/vm/{uid}/reset", web::get().to(vm_controller::reset)) .route( "/api/vm/{uid}/suspend", web::get().to(vm_controller::suspend), ) .route("/api/vm/{uid}/resume", web::get().to(vm_controller::resume)) .route("/api/vm/{uid}/state", web::get().to(vm_controller::state)) .route( "/api/vm/{uid}/screenshot", web::get().to(vm_controller::screenshot), ) .route( "/api/vm/{uid}/vnc_token", web::get().to(vm_controller::vnc_token), ) .route("/api/vnc", web::get().to(vm_controller::vnc)) // Groups controller .route("/api/group/list", web::get().to(groups_controller::list)) // Network controller .route( "/api/network/create", web::post().to(network_controller::create), ) .route("/api/network/list", web::get().to(network_controller::list)) .route( "/api/network/{uid}", web::get().to(network_controller::get_single), ) .route( "/api/network/{uid}/src", web::get().to(network_controller::single_src), ) .route( "/api/network/{uid}", web::put().to(network_controller::update), ) .route( "/api/network/{uid}", web::delete().to(network_controller::delete), ) .route( "/api/network/{uid}/autostart", web::get().to(network_controller::get_autostart), ) .route( "/api/network/{uid}/autostart", web::put().to(network_controller::set_autostart), ) .route( "/api/network/{uid}/status", web::get().to(network_controller::status), ) .route( "/api/network/{uid}/start", web::get().to(network_controller::start), ) .route( "/api/network/{uid}/stop", web::get().to(network_controller::stop), ) // Network filters controller .route( "/api/nwfilter/create", web::post().to(nwfilter_controller::create), ) .route( "/api/nwfilter/list", web::get().to(nwfilter_controller::list), ) .route( "/api/nwfilter/{uid}", web::get().to(nwfilter_controller::get_single), ) .route( "/api/nwfilter/{uid}/src", web::get().to(nwfilter_controller::single_src), ) .route( "/api/nwfilter/{uid}", web::put().to(nwfilter_controller::update), ) .route( "/api/nwfilter/{uid}", web::delete().to(nwfilter_controller::delete), ) // API tokens controller .route( "/api/token/create", web::post().to(api_tokens_controller::create), ) .route( "/api/token/list", web::get().to(api_tokens_controller::list), ) .route( "/api/token/{uid}", web::get().to(api_tokens_controller::get_single), ) .route( "/api/token/{uid}", web::patch().to(api_tokens_controller::update), ) .route( "/api/token/{uid}", web::delete().to(api_tokens_controller::delete), ) // 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)? .run() .await }