#[cfg(not(target_arch = "wasm32"))]
mod cli;
#[cfg(not(target_arch = "wasm32"))]
use beam::actor::Actor;
#[cfg(not(target_arch = "wasm32"))]
use beam::adapters::{
MemoryStorage, Multicast, OutgoingWebsocketManager, RedbStorage, WsServer, WsServerConfig,
};
#[cfg(not(target_arch = "wasm32"))]
use beam::{Config, Node};
#[cfg(not(target_arch = "wasm32"))]
use clap::Parser;
#[cfg(not(target_arch = "wasm32"))]
use cli::{Cli, Command};
#[cfg(not(target_arch = "wasm32"))]
async fn wait_for_signal() {
#[cfg(unix)]
{
let mut sigterm = tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate())
.expect("failed to install SIGTERM handler");
tokio::select! {
_ = tokio::signal::ctrl_c() => {
eprintln!("\nSIGINT received — initiating graceful shutdown...");
}
_ = sigterm.recv() => {
eprintln!("SIGTERM received — initiating graceful shutdown...");
}
}
}
#[cfg(not(unix))]
{
tokio::signal::ctrl_c()
.await
.expect("failed to listen for Ctrl-C");
eprintln!("\nCtrl-C received — initiating graceful shutdown...");
}
}
#[tokio::main]
#[cfg(not(target_arch = "wasm32"))]
async fn main() {
let cli = Cli::parse();
match cli.command {
#[cfg(any(feature = "persy", feature = "fjall"))]
Command::Migrate(args) => {
#[cfg(not(target_arch = "wasm32"))]
use beam::migration::{MigrateOpts, migrate};
#[cfg(not(target_arch = "wasm32"))]
use std::path::PathBuf;
let from = beam::migration::MigrateError::parse_backend(&args.from)
.unwrap_or_else(|e| panic!("Invalid --from value: {}", e));
let to = beam::migration::MigrateError::parse_backend(&args.to)
.unwrap_or_else(|e| panic!("Invalid --to value: {}", e));
let opts = MigrateOpts {
from,
to,
source_path: PathBuf::from(&args.source),
target_path: PathBuf::from(&args.target),
batch_size: args.batch_size,
force: args.force,
dry_run: args.dry_run,
};
eprintln!(
"Migrating {} -> {} (batch_size={}, dry_run={})",
from.as_str(),
to.as_str(),
opts.batch_size,
opts.dry_run
);
match migrate(&opts) {
Ok(report) => {
println!(
"Migration complete: {} records migrated",
report.records_migrated
);
}
Err(e) => {
eprintln!("Migration failed: {:?}", e);
std::process::exit(1);
}
}
}
#[cfg(not(any(feature = "persy", feature = "fjall")))]
Command::Migrate(_) => {
eprintln!(
"Migration requires the 'persy' or 'fjall' feature. Rebuild with: cargo run --features fjall"
);
std::process::exit(1);
}
Command::Start(args) => {
let mut outgoing_websocket_peers = Vec::new();
if let Some(peers) = &args.peers {
outgoing_websocket_peers = peers.split(',').map(|s| s.to_string()).collect();
}
env_logger::init();
let mut network_adapters: Vec<Box<dyn Actor>> = Vec::new();
let mut storage_adapters: Vec<Box<dyn Actor>> = Vec::new();
let websocket_server = args.ws_server == "true";
let config = Config {
allow_public_space: args.allow_public_space != "false",
..Config::default()
};
if args.multicast == "true" {
network_adapters.push(Box::new(Multicast::new(config.clone())));
}
if websocket_server {
network_adapters.push(Box::new(WsServer::new_with_config(
config.clone(),
WsServerConfig {
port: args.port,
cert_path: args.cert_path.clone(),
key_path: args.key_path.clone(),
},
)));
}
if args.redb_storage != "false" {
storage_adapters.push(Box::new(RedbStorage::new_with_config(
config.clone(),
&args.redb_path,
None,
)));
}
if args.memory_storage == "true" {
storage_adapters.push(Box::new(MemoryStorage::new()));
}
if !outgoing_websocket_peers.is_empty() {
network_adapters.push(Box::new(OutgoingWebsocketManager::new(
config.clone(),
outgoing_websocket_peers,
)));
}
let node = Node::new_with_config(config, storage_adapters, network_adapters);
println!("BEAM node starting...");
let shutdown_timeout = web_time::Duration::from_secs(args.shutdown_timeout);
let mut node_clone = node.clone();
wait_for_signal().await;
tokio::select! {
result = node_clone.shutdown(shutdown_timeout) => {
match result {
Ok(()) => {
eprintln!("Graceful shutdown complete.");
}
Err(e) => {
eprintln!("Graceful shutdown timed out ({}), force-stopped.", e);
}
}
}
_ = wait_for_signal() => {
eprintln!("Second signal received — forcing exit.");
std::process::exit(1);
}
}
}
}
}
#[cfg(target_arch = "wasm32")]
fn main() {}