use dotenvy::dotenv;
use fancy_log::{LogLevel, log};
use std::sync::Arc;
use tokio::signal;
use crate::bootstrap::{console, logging, monitor};
use crate::common::{
config::env_loader,
sys::{lifecycle, watcher},
};
use crate::ingress::{hotswap, listener, state};
use crate::layers::l4p::{hotswap as resolver_hotswap, model as resolver_model};
use crate::layers::l7::{hotswap as app_hotswap, model as app_model};
use crate::plugins::core::loader as plugin_loader;
use crate::resources::{certs, service_discovery as nodes};
pub async fn start() {
setup_crypto();
dotenv().ok();
logging::setup();
logging::print_motd();
lifecycle::ensure_config_files_exist().await;
if let Some(initial_nodes) = nodes::hotswap::scan_nodes_config().await {
nodes::model::NODES_STATE.store(Arc::new(initial_nodes));
}
certs::loader::initialize().await;
let initial_ports: Vec<crate::ingress::state::PortStatus> = hotswap::scan_ports_config(&[]).await;
state::CONFIG_STATE.store(Arc::new(initial_ports.clone()));
let initial_resolvers =
resolver_hotswap::scan_resolver_config(&resolver_model::RESOLVER_REGISTRY.load()).await;
resolver_model::RESOLVER_REGISTRY.store(Arc::new(initial_resolvers));
log(
LogLevel::Info,
&format!(
"✓ Loaded {} resolver protocols.",
resolver_model::RESOLVER_REGISTRY.load().len()
),
);
let initial_apps =
app_hotswap::scan_application_config(&app_model::APPLICATION_REGISTRY.load()).await;
app_model::APPLICATION_REGISTRY.store(Arc::new(initial_apps));
log(
LogLevel::Info,
&format!(
"✓ Loaded {} application protocols.",
app_model::APPLICATION_REGISTRY.load().len()
),
);
lifecycle::start_background_tasks().await;
plugin_loader::initialize().await;
monitor::start_l7_memory_monitor().await;
start_initial_listeners(&initial_ports).await;
let receivers = watcher::start_config_watchers_only();
spawn_hotswap_tasks(receivers).await;
let console_handles = console::start().await;
wait_for_shutdown_signal().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(ports: &[state::PortStatus]) {
log(
LogLevel::Info,
"⚙ Initializing listeners from existing config...",
);
let ip_version =
if env_loader::get_env("LISTEN_IPV6", "false".to_owned()).to_lowercase() == "true" {
"IPv4 + IPv6"
} else {
"IPv4"
};
for status in ports {
if status.tcp_config.is_some() {
log(
LogLevel::Info,
&format!("↑ {} PORT {} TCP UP", ip_version, status.port),
);
listener::start_listener(status.port, state::Protocol::Tcp);
}
if status.udp_config.is_some() {
log(
LogLevel::Info,
&format!("↑ {} PORT {} UDP UP", ip_version, status.port),
);
listener::start_listener(status.port, state::Protocol::Udp);
}
}
}
async fn spawn_hotswap_tasks(receivers: watcher::ConfigChangeReceivers) {
tokio::spawn(hotswap::listen_for_updates(receivers.ports));
tokio::spawn(nodes::hotswap::listen_for_updates(receivers.nodes));
tokio::spawn(resolver_hotswap::listen_for_updates(receivers.resolvers));
tokio::spawn(certs::loader::listen_for_updates(receivers.certs));
tokio::spawn(app_hotswap::listen_for_updates(receivers.applications));
}
async fn wait_for_shutdown_signal() {
let ctrl_c = async {
signal::ctrl_c()
.await
.expect("failed to install Ctrl+C handler");
};
#[cfg(unix)]
let terminate = async {
signal::unix::signal(signal::unix::SignalKind::terminate())
.expect("failed to install signal handler")
.recv()
.await;
};
#[cfg(not(unix))]
let terminate = std::future::pending::<()>();
tokio::select! { _ = ctrl_c => {}, _ = terminate => {}, }
}