akas 2.3.0

AKAS: API Key Authorization Server
mod cli;
mod handlers;
mod models;
mod state;
mod utils;

use crate::{
    cli::{load_client, Cli, Commands},
    models::AppConfig,
    state::{init_state, AppState},
};
use actix_web::{web, App, HttpServer};
use clap::Parser;
use tracing::info;
use tracing_subscriber::{filter::LevelFilter, fmt, prelude::*};

/// The main entry point of the application.
///
/// Initializes the application state by reading a configuration file, creates an HTTP server,
/// and starts listening on the specified port.
///
/// # Errors
///
/// Returns a `std::io::Error` if there's an error initializing the application state,
/// binding the server, or running the server.
///
#[actix_web::main]
async fn main() -> std::io::Result<()> {
    let args = Cli::parse();

    match args.command {
        Commands::Serve {
            admin_key,
            no_admin_key,
            port,
            log_level,
            original_length,
            metadata_length,
            length,
            prefix,
        } => {
            let app_state: AppState = match init_state(AppConfig {
                admin_key: admin_key.to_string(),
                no_admin_key,
                port,
                log_level,
                original_length,
                metadata_length,
                length,
                prefix: prefix.to_string(),
            }) {
                Ok(a) => a,
                Err(e) => {
                    eprintln!("Error: {}", e);
                    std::process::exit(1);
                }
            };

            let tracing_level = app_state.log_level;

            tracing_subscriber::registry()
                .with(
                    fmt::layer()
                        .json() // Enable JSON output
                        .with_ansi(false) // Disable ANSI escape codes for cleaner JSON
                        .with_target(true) // Include the target field (e.g., "akas")
                        .with_level(true) // Include the log level
                        .with_thread_ids(false) // Disable thread IDs
                        .with_current_span(false) // Disable current span information
                        .with_span_events(fmt::format::FmtSpan::NONE) // Log span start/end events
                        .flatten_event(false) // Flatten event fields directly into the top-level JSON object
                        .with_thread_names(false),
                )
                .with(LevelFilter::from_level(tracing_level)) // Apply the configured log level
                .init();

            info!("Log level: {}", tracing_level);
            info!(
                "Max x-original headers length: {}",
                original_length.to_string()
            );
            info!(
                "Max metadata header length: {}",
                metadata_length.to_string()
            );

            let data = web::Data::new(app_state);

            let server = HttpServer::new(move || {
                App::new()
                    .app_data(data.clone())
                    .service(handlers::auth)
                    .service(handlers::auth_unauthorized)
                    .service(handlers::health)
                    .service(handlers::load)
                    .service(handlers::status)
            });

            if no_admin_key {
                info!("No admin key provided.");
            } else {
                info!("Admin key provided.");
            }

            if length > 0 {
                info!("Key length: {}", length);
            }
            if !prefix.is_empty() {
                info!("Key prefix: {}", prefix);
            }
            info!("Starting server on http://localhost:{} ...", port);

            server.bind(("127.0.0.1", port))?.run().await
        }
        Commands::Load { host, file, format } => match load_client(&host, &file, &format) {
            Ok(m) => {
                println!("{}", m);
                Ok(())
            }
            Err(e) => {
                eprintln!("Error: {}", e);
                std::process::exit(1);
            }
        },
    }
}