use std::{
collections::HashMap,
process::exit,
sync::{Arc, Mutex},
};
use clap::Parser;
use log::{debug, error, info, warn};
use notify::{Event, RecommendedWatcher, RecursiveMode, Watcher};
use subgraph::{
cli_args,
configuration::{environment::Environment, subgraph::SubGraphConfig},
run, utils,
};
#[tokio::main]
async fn main() -> Result<(), std::io::Error> {
let args = cli_args::CliArgs::parse();
let environment = Environment::new();
match args.handle_flags() {
Ok(_) => {}
Err(e) => {
error!("Error handling flags: {}", e);
exit(1);
}
};
if args.config.is_none() {
println!("No config provided, exiting...");
return Ok(());
}
let mut subgraph_config = SubGraphConfig::new(&args).unwrap();
subgraph_config = Environment::replace_env_vars_in_config(subgraph_config, environment.clone());
utils::logger::Logger::init(&args, &subgraph_config);
let license_key = subgraph_config.service.license_key.clone();
match license_key {
Some(key) => {
let license = utils::verify_license::verify_license(key).await;
match license {
Ok(license) => {
debug!("License: {:?}", license);
info!("✅ License Validated");
}
Err(e) => {
error!("License Error: {}", e.message);
exit(1);
}
};
}
None => {
info!("No License Provided, but you are welcome to use the software for free for a limited amount of time.");
let _ = tokio::spawn(async move {
tokio::time::sleep(tokio::time::Duration::from_secs(60 * 20)).await;
warn!("Demo License Expired, exiting... Please purchase a license to continue using the software.");
exit(1);
});
}
};
if args.clone().watch {
start_server_with_watcher(args.clone(), subgraph_config, environment).await?;
Ok(())
} else {
start_server(args.clone(), subgraph_config).await?;
Ok(())
}
}
async fn start_server(
args: cli_args::CliArgs,
subgraph_config: SubGraphConfig,
) -> Result<(), std::io::Error> {
let (server, _schema, _shutdown) = run(args.clone(), subgraph_config).await?;
server.await;
Ok(())
}
async fn start_server_with_watcher(
args: cli_args::CliArgs,
subgraph_config: SubGraphConfig,
environment: HashMap<String, String>,
) -> Result<(), std::io::Error> {
let config = Arc::new(Mutex::new(subgraph_config.clone()));
let cloned_config = Arc::clone(&config);
let cloned_environment = environment.clone();
let cloned_args = args.clone();
let (tx, rx) = std::sync::mpsc::channel::<bool>();
tx.send(false).unwrap();
let mut last_received = std::time::Instant::now();
let mut watcher: RecommendedWatcher = match Watcher::new(
move |event: Result<Event, notify::Error>| {
let event = event.unwrap();
let cloned_environment = cloned_environment.clone();
let is_timeout = last_received.elapsed().as_secs() > 1;
if event.kind.is_modify() && is_timeout {
let subgraph_config = SubGraphConfig::new(&cloned_args);
match subgraph_config {
Ok(config) => {
let subgraph_config =
Environment::replace_env_vars_in_config(config, cloned_environment);
*cloned_config.lock().unwrap() = subgraph_config;
last_received = std::time::Instant::now();
tx.send(true).unwrap();
debug!("Testing")
}
Err(error) => {
error!(
"Something went wrong, waiting for changes. Error Message: {:?}",
error.message
);
}
};
}
},
notify::Config::default(),
) {
Ok(watcher) => {
info!("👀 Watching for changes...");
watcher
}
Err(error) => {
error!(
"Failed to create watcher. Error Message: {}",
error.to_string()
);
exit(1)
}
};
match watcher.watch(
args.clone().config.unwrap().as_path().parent().unwrap(), RecursiveMode::Recursive,
) {
Ok(_) => (),
Err(e) => error!("Watcher failed to start. Error Message: {e}"),
};
let cloned_config = Arc::clone(&config);
let mut shutdown_handle: Option<tokio::sync::oneshot::Sender<()>> = None;
loop {
match rx.recv() {
Ok(is_restart) => {
if is_restart && shutdown_handle.is_some() {
shutdown_handle.unwrap().send(()).unwrap();
}
let server_instance =
run(args.clone(), cloned_config.lock().unwrap().clone()).await;
match server_instance {
Ok((server, _schema, shutdown)) => {
shutdown_handle = Some(shutdown);
tokio::spawn(async move {
server.await;
});
}
Err(_) => {
shutdown_handle = None;
error!("Something went wrong, waiting for changes...")
}
}
}
Err(e) => {
error!("Watcher failed. Error Message: {}", e.to_string());
}
}
}
}