Skip to content
use std::sync::Arc;

use actix_files::Files;
use actix_web::{App, HttpServer, web};
use env_logger::Env;
use log::info;

use corium::{
    code::{Code, CodeVersion, Lang},
    compiler::Compiler,
    config,
    runner::Runner,
    store::DB,
};

mod routes;

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    let server_config = config::from_env();

    env_logger::Builder::from_env(Env::default().default_filter_or(server_config.log_level())).init();

    let db_path = server_config.db_path().to_string();
    let db = Arc::new(DB::new(&db_path).expect("Failed to open database"));

    initialize_default_endpoint(&db).await;

    info!("{}", server_config);

    let db_data = web::Data::new(db);
    let compiler_data = web::Data::new(Arc::new(Compiler::new()));
    let runner_data = web::Data::new(Arc::new(Runner::new()));
    let config_data = web::Data::new(Arc::new(server_config.clone()));

    let bind_address = server_config.address();

    HttpServer::new(move || {
        App::new()
            .app_data(db_data.clone())
            .app_data(compiler_data.clone())
            .app_data(runner_data.clone())
            .app_data(config_data.clone())
            .service(Files::new("/assets", "/home/silen/code/silen/corium/assets"))
            .service(routes::health)
            .service(routes::up)
            .configure(routes::configure_routes)
    })
    .bind(bind_address)?
    .run()
    .await
}

async fn initialize_default_endpoint(db: &Arc<DB>) {
    if db.load_code("/api/hello").await.is_none() {
        let compiler = Compiler::new();

        let default_code = r#"fun main() {
    val name = getQueryParam("name") ?: "World"
    val response = """{"message": "Hello, $name!", "timestamp": "${getTimestamp()}"}"""
    respond(200, response)
}

fun getQueryParam(name: String): String? {
    return null
}

fun getTimestamp(): String {
    return "2024-01-01T00:00:00Z"
}

fun respond(status: Int, body: String) {
    println(body)
}"#;

        match compiler.compile(default_code, Lang::Kotlin).await {
            Ok(wasm_bytes) => {
                let code = Code::new("/api/hello", default_code, Lang::Kotlin, wasm_bytes.clone());
                match db.save_code(&code).await {
                    Ok(_) => {
                        let code_version = CodeVersion::new(code.id(), 1, default_code, Lang::Kotlin, wasm_bytes);
                        let _ = db.save_code_version(&code_version).await;
                        info!("Created default endpoint at /api/hello");
                    }
                    Err(e) => info!("Could not save default endpoint: {}", e),
                }
            }
            Err(e) => {
                info!("Could not compile default endpoint: {}", e);
                let code = Code::new("/api/hello", default_code, Lang::Kotlin, vec![]);
                match db.save_code(&code).await {
                    Ok(_) => {
                        let code_version = CodeVersion::new(code.id(), 1, default_code, Lang::Kotlin, vec![]);
                        let _ = db.save_code_version(&code_version).await;
                    }
                    Err(e) => info!("Could not save default endpoint: {}", e),
                }
            }
        }
    }
}