use small_bin::*;
use anyhow::Result;
use config::AppConfig;
use database::Database;
use std::{env, fs, sync::Arc, time::Duration};
use tokio::time;
#[tokio::main]
async fn main() -> Result<()> {
initialize();
let config = Arc::new(AppConfig::new()?);
let version = env!("CARGO_PKG_VERSION");
info!("Launching Small v{version}");
if let Err(e) = notification::notification(
&format!("Launching SmallApplication v{version}"),
"start",
&config.notifications,
&config.sounds,
) {
warn!("Failed to send startup notification: {e:?}");
}
let db_path = config.database_path();
let database = Arc::new(Database::new(&db_path)?);
info!("Initializing database backend: {db_path:?}");
let (sftp_manager, _rx) = sftp::SftpManager::new(config.clone(), database.clone());
let sftp_manager = Arc::new(sftp_manager);
let sftp_handle = {
tokio::spawn(async move {
sftp_manager.start().await;
})
};
let web_api = Arc::new(webapi::WebApi::new(config.clone(), database.clone()));
let web_handle = {
tokio::spawn(async move {
if let Err(e) = web_api.start().await {
error!("Web API error: {e:?}");
}
})
};
if config.open_history_on_start {
info!(
"Automatically opening http dashboard: http://localhost:{} in default browser.",
config.webapi_port
);
if let Err(e) = std::process::Command::new("open")
.arg(format!("http://localhost:{}", config.webapi_port))
.spawn()
{
warn!("Failed to open browser: {e:?}");
}
}
let file_watcher = Arc::new(watcher::FileWatcher::new(config.clone(), database.clone()));
let watcher_handle = {
tokio::spawn(async move {
if let Err(e) = file_watcher.start().await {
error!("File watcher error: {e:?}");
}
})
};
let dump_interval = config.db_autodump_interval;
let periodic_dump_handle = {
let dumps_dir = config.db_dumps_dir();
fs::create_dir_all(&dumps_dir).ok();
tokio::spawn(async move {
info!(
"Initializing periodic dumper (triggered every {} hours)",
dump_interval / 3600000
);
let mut interval = time::interval(Duration::from_millis(dump_interval));
loop {
interval.tick().await;
let timestamp = chrono::Local::now().format("%Y-%m-%d-%H-%M-%S").to_string();
let dump_path = dumps_dir.join(format!("database.{timestamp}.db"));
if let Err(e) = database.dump_to_file(&dump_path) {
error!("Failed to dump database: {e:?}");
}
}
})
};
info!("Starting an eternal watch…");
tokio::select! {
_ = sftp_handle => error!("SFTP manager stopped"),
_ = web_handle => error!("Web API stopped"),
_ = watcher_handle => error!("File watcher stopped"),
_ = periodic_dump_handle => error!("Periodic dump stopped"),
}
Ok(())
}