server: main.rs — Actix-web entry point with CORS, compression, static serving, SPA fallback; placeholder static/index.html

This commit is contained in:
Butterfly Dev 2026-04-07 03:12:35 +00:00
parent a0b834ed15
commit 0c35db1746
2 changed files with 111 additions and 2 deletions

View File

@ -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
}

21
server/static/index.html Normal file
View File

@ -0,0 +1,21 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Butterfly Desktop</title>
<style>
body { margin: 0; font-family: system-ui, sans-serif; background: #0078d4; color: #fff; display: flex; align-items: center; justify-content: center; height: 100vh; }
.loader { text-align: center; }
.spinner { border: 4px solid rgba(255,255,255,.3); border-top: 4px solid #fff; border-radius: 50%; width: 48px; height: 48px; animation: spin 1s linear infinite; margin: 0 auto 16px; }
@keyframes spin { to { transform: rotate(360deg); } }
</style>
</head>
<body>
<div class="loader">
<div class="spinner"></div>
<p>Butterfly Desktop Environment — loading…</p>
</div>
<!-- Angular app will replace this once built -->
</body>
</html>