use dotenvy::dotenv;
use fancy_log::{LogLevel, log};
use live::signal::Config as WatcherConfig;
use sigterm;
use crate::bootstrap::{console, logging, monitor};
use crate::common::sys::lifecycle;
use crate::config::{self, ConfigManager};
use crate::ingress::{hotswap, listener, state};
use crate::plugins::core::loader as plugin_loader;
use crate::resources::certs;
pub async fn start() {
setup_crypto();
dotenv().ok();
logging::setup();
logging::print_motd();
lifecycle::ensure_config_files_exist().await;
let config_dir_path = crate::common::config::file_loader::get_config_dir();
let config_dir_str = config_dir_path
.to_str()
.expect("Config dir path is not valid UTF-8");
let mut config = match ConfigManager::init(config_dir_str).await {
Ok(c) => c,
Err(e) => {
log(
LogLevel::Error,
&format!("Failed to initialize config: {e}"),
);
return;
}
};
match config.listeners.tcp.load().await {
Ok(result) => {
for (key, error) in &result.failed {
if error.to_lowercase().contains("validation") {
log(
LogLevel::Error,
&format!("✗ Validation failed for TCP listener [{key}]: {error}"),
);
} else {
log(
LogLevel::Error,
&format!("✗ Failed to parse config file for TCP listener [{key}]: {error}"),
);
}
}
}
Err(e) => log(
LogLevel::Error,
&format!("Failed to load TCP listeners: {e}"),
),
}
match config.listeners.udp.load().await {
Ok(result) => {
for (key, error) in &result.failed {
if error.to_lowercase().contains("validation") {
log(
LogLevel::Error,
&format!("✗ Validation failed for UDP listener [{key}]: {error}"),
);
} else {
log(
LogLevel::Error,
&format!("✗ Failed to parse config file for UDP listener [{key}]: {error}"),
);
}
}
}
Err(e) => log(
LogLevel::Error,
&format!("Failed to load UDP listeners: {e}"),
),
}
match config.resolvers.load().await {
Ok(result) => {
for (key, error) in &result.failed {
if error.to_lowercase().contains("validation") {
log(
LogLevel::Error,
&format!("✗ Validation failed for resolver [{key}]: {error}"),
);
} else {
log(
LogLevel::Error,
&format!("✗ Failed to parse config file for resolver [{key}]: {error}"),
);
}
}
}
Err(e) => log(LogLevel::Error, &format!("Failed to load resolvers: {e}")),
}
match config.applications.load().await {
Ok(result) => {
for (key, error) in &result.failed {
if error.to_lowercase().contains("validation") {
log(
LogLevel::Error,
&format!("✗ Validation failed for application [{key}]: {error}"),
);
} else {
log(
LogLevel::Error,
&format!("✗ Failed to parse config file for application [{key}]: {error}"),
);
}
}
}
Err(e) => log(
LogLevel::Error,
&format!("Failed to load applications: {e}"),
),
}
match config.nodes.load().await {
Ok(_) => log(LogLevel::Debug, "⚙ Loaded nodes configuration."),
Err(live::controller::LiveError::Load(live::loader::FmtError::NotFound)) => {
log(
LogLevel::Debug,
"⚙ Nodes configuration file not found. Using default.",
);
}
Err(e) => log(LogLevel::Error, &format!("Failed to load nodes: {e}")),
}
if let Some(lc) = &config.lazycert {
let _ = lc.load().await;
}
let watch_config = WatcherConfig::default();
let _ = config.listeners.start_watching(watch_config.clone()).await;
let _ = config.resolvers.start_watching(watch_config.clone()).await;
let _ = config
.applications
.start_watching(watch_config.clone())
.await;
let _ = config.nodes.start_watching(watch_config.clone()).await;
if let Some(lc) = &mut config.lazycert {
let _ = lc.start_watching(watch_config.clone()).await;
}
if config::CONFIG.set(config).is_err() {
panic!("Config already initialized");
}
let config = config::get();
certs::loader::initialize().await;
crate::lazycert::initialize().await;
lifecycle::start_background_tasks().await;
plugin_loader::initialize().await;
monitor::start_l7_memory_monitor().await;
start_initial_listeners(config).await;
tokio::spawn(hotswap::start_listener_event_loop(config));
start_certs_watcher(config_dir_path.join("certs")).await;
let console_handles = console::start().await;
sigterm::wait().await;
log(LogLevel::Info, "➜ Signal received, shutdown now...");
if let Some(handles) = console_handles {
console::stop(handles).await;
}
log(LogLevel::Info, "✓ Server has been shut down gracefully.");
}
fn setup_crypto() {
#[cfg(feature = "aws-lc-rs")]
{
use rustls::crypto::aws_lc_rs;
let _ = aws_lc_rs::default_provider().install_default();
}
#[cfg(feature = "ring")]
{
use rustls::crypto::ring;
let _ = ring::default_provider().install_default();
}
}
async fn start_initial_listeners(config: &ConfigManager) {
log(
LogLevel::Info,
"⚙ Initializing listeners from existing config...",
);
let tcp_map = config.listeners.tcp.snapshot().await;
for (port_str, _) in tcp_map {
if let Ok(port) = port_str.parse::<u16>() {
log(LogLevel::Info, &format!("↑ PORT {port} TCP UP"));
listener::start_listener(port, state::Protocol::Tcp);
}
}
let udp_map = config.listeners.udp.snapshot().await;
for (port_str, _) in udp_map {
if let Ok(port) = port_str.parse::<u16>() {
log(LogLevel::Info, &format!("↑ PORT {port} UDP UP"));
listener::start_listener(port, state::Protocol::Udp);
}
}
}
async fn start_certs_watcher(cert_dir: std::path::PathBuf) {
use live::signal::{Config as WatcherConfig, Target, Watcher};
let target = Target::Filtered {
path: cert_dir,
include: vec!["*.pem".to_owned(), "*.crt".to_owned(), "*.key".to_owned()],
exclude: vec!["*.bak".to_owned()],
};
match Watcher::new(target, WatcherConfig::default()) {
Ok(watcher) => {
tokio::spawn(async move {
let _watcher = watcher; let mut rx = _watcher.subscribe();
while rx.recv().await.is_ok() {
crate::resources::certs::loader::scan_and_load_certs().await;
}
});
}
Err(e) => {
log(
LogLevel::Error,
&format!("✗ Failed to start certs watcher: {e}"),
);
}
}
}