From 0c35db1746783f27e5afb03567f0795c00ac8402 Mon Sep 17 00:00:00 2001 From: Butterfly Dev Date: Tue, 7 Apr 2026 03:12:35 +0000 Subject: [PATCH] =?UTF-8?q?server:=20main.rs=20=E2=80=94=20Actix-web=20ent?= =?UTF-8?q?ry=20point=20with=20CORS,=20compression,=20static=20serving,=20?= =?UTF-8?q?SPA=20fallback;=20placeholder=20static/index.html?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/src/main.rs | 92 +++++++++++++++++++++++++++++++++++++++- server/static/index.html | 21 +++++++++ 2 files changed, 111 insertions(+), 2 deletions(-) create mode 100644 server/static/index.html diff --git a/server/src/main.rs b/server/src/main.rs index e7a11a9..1af67a0 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -1,3 +1,91 @@ -fn main() { - println!("Hello, world!"); +mod api; +mod config; +mod models; +mod state; +mod stream; +mod ws; + +use std::sync::Arc; + +use actix_cors::Cors; +use actix_web::{web, App, HttpServer, middleware}; +use log::info; + +use config::Config; +use state::AppState; + +#[actix_web::main] +async fn main() -> std::io::Result<()> { + // Initialize logger from RUST_LOG env var (default: info). + env_logger::init_from_env(env_logger::Env::new().default_filter_or("info")); + + let config = Config::from_env(); + let bind_addr = config.bind_addr(); + + info!("🦋 Butterfly Server v{}", env!("CARGO_PKG_VERSION")); + info!(" config: {:?}", &config); + + let app_state = AppState::new( + config.max_sessions, + config.idle_timeout_secs, + config.frame_buffer_size, + ); + + // Build CORS middleware. + let cors = if config.cors_origins.iter().any(|o| o == "*") { + Cors::permissive() + } else { + let mut cors = Cors::default(); + for origin in &config.cors_origins { + cors = cors.allowed_origin(origin); + } + cors + }; + + // Path to the Angular frontend build output (relative to server/). + // Falls back to "static" if the Angular dist dir doesn't exist yet. + let frontend_dir = std::path::Path::new("../desktop/dist/browser"); + let static_dir = if frontend_dir.exists() { + frontend_dir.to_path_buf() + } else { + std::path::PathBuf::from("static") + }; + + info!(" serving static files from: {:?}", static_dir); + + HttpServer::new(move || { + let json_cfg = web::JsonConfig::default() + .limit(1024 * 1024) // 1 MB max JSON payload + .error_handler(|err, _req| { + let resp = actix_web::HttpResponse::BadRequest().json( + models::ApiResponse::<()>::err(format!("invalid JSON: {}", err)), + ); + actix_web::error::InternalError::from_response(err, resp).into() + }); + + App::new() + .app_data(web::Data::new(app_state.clone())) + .app_data(json_cfg) + .wrap(cors.clone()) + .wrap(middleware::Logger::default()) + .wrap(middleware::Compress::default()) + .configure(api::configure) + .configure(ws::configure) + // Serve Angular static assets; falls back to index.html for SPA routing. + .service(actix_files::Files::new("/assets", &static_dir.join("assets")).show_files_listing()) + .route("/", web::get().to(|| async { + actix_web::HttpResponse::Ok() + .content_type("text/html; charset=utf-8") + .body(include_str!("../../static/index.html")) + })) + .default_service(web::route().to(|| async { + actix_web::HttpResponse::Ok() + .content_type("text/html; charset=utf-8") + .body(include_str!("../../static/index.html")) + })) + }) + .bind(&bind_addr)? + .workers(4) + .run() + .await } diff --git a/server/static/index.html b/server/static/index.html new file mode 100644 index 0000000..7392dd1 --- /dev/null +++ b/server/static/index.html @@ -0,0 +1,21 @@ + + + + + + Butterfly Desktop + + + +
+
+

Butterfly Desktop Environment — loading…

+
+ + +