AkurAI Build
Menu

Public repository / 0150c3705544adc2275b2871151545a61bdb741c

AkurAI-DNS / src/main.rs

Latest change 1f985ee57c4d7762e35f4e0bd2fbbfe716bf42ee - feat(domains): audited domain lifecycle - certificate-ready state, transitions, events, customer-safe status (AKURAI-DNS-4) by agent

mod dns;
mod health;
mod models;
mod routes;
mod schema;
mod server;
mod zone;

use std::net::SocketAddr;
use std::path::Path;
use std::sync::Arc;

use axum::{Router, routing};
use tokio::sync::RwLock;
use tracing::info;
use tracing_subscriber::EnvFilter;

#[tokio::main]
async fn main() {
    // Init tracing
    tracing_subscriber::fmt()
        .with_env_filter(
            EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info")),
        )
        .init();

    // Config from env
    let zone_dir = std::env::var("DNS_ZONE_DIR").unwrap_or_else(|_| "/etc/akurai-dns/zones".into());
    let listen = std::env::var("DNS_LISTEN").unwrap_or_else(|_| "0.0.0.0".into());
    let port: u16 = std::env::var("DNS_PORT")
        .ok()
        .and_then(|p| p.parse().ok())
        .unwrap_or(53);
    let api_port: u16 = std::env::var("API_PORT")
        .ok()
        .and_then(|p| p.parse().ok())
        .unwrap_or(8080);
    let db_path =
        std::env::var("DNS_DB_PATH").unwrap_or_else(|_| "/var/lib/akurai-dns/domains.db".into());

    info!(zone_dir = %zone_dir, listen = %listen, port = port, api_port = api_port, db_path = %db_path, "AkurAI DNS starting");

    // Init database
    let db = routes::init_db(&db_path).expect("failed to initialize database");

    // Load zones
    let zones = match zone::ZoneSet::load_dir(Path::new(&zone_dir)) {
        Ok(z) => z,
        Err(e) => {
            eprintln!("FATAL: Failed to load zones from {zone_dir}: {e}");
            std::process::exit(1);
        }
    };

    let zones = Arc::new(RwLock::new(zones));

    health::start();

    info!("Zones loaded, starting servers");

    // Build HTTP API router
    let state = routes::AppState { db };
    let api = Router::new()
        .route("/api/domains", routing::post(routes::create_domain))
        .route("/api/domains", routing::get(routes::list_domains))
        .route("/api/domains/{id}", routing::delete(routes::delete_domain))
        .route(
            "/api/domains/{id}/status",
            routing::get(routes::get_domain_status),
        )
        .route(
            "/api/domains/{id}/verify",
            routing::post(routes::verify_domain),
        )
        .route(
            "/api/domains/{id}/cert-ready",
            routing::post(routes::mark_cert_ready),
        )
        .route(
            "/api/domains/{id}/renew",
            routing::post(routes::renew_domain),
        )
        .route("/api/domains/{id}/fail", routing::post(routes::fail_domain))
        .route(
            "/api/domains/{id}/retry",
            routing::post(routes::retry_domain),
        )
        .with_state(state);

    let api_addr: SocketAddr = format!("{listen}:{api_port}")
        .parse()
        .expect("invalid API address");
    let api_server = tokio::spawn(async move {
        info!(addr = %api_addr, "HTTP API listening");
        let listener = tokio::net::TcpListener::bind(api_addr).await.unwrap();
        axum::serve(listener, api).await.unwrap();
    });

    // Run DNS server (blocks until exit)
    server::run(listen, port, zones, zone_dir).await;

    api_server.abort();
}