use std::env; mod database; mod routes; mod utils; use axum::{ routing::{get, post}, Router, }; use routes::problem::{get_problem, get_problems, create_problem}; use routes::user::{create_user, me}; use routes::auth::{login, logout}; use tower_http::services::ServeDir; use crate::{database::Database, routes::submission::{create_submission, get_display_submission, get_display_submissions}, utils::register_admin}; #[derive(Clone)] struct AppState { secret: String, register_code: String, database: Database, } #[tokio::main] async fn main() { let database = Database::new_in_memory().unwrap(); // initialize tables database.initialize().unwrap(); // initialize problems if none exist yet if let Ok(problems) = database.fetch_problems() { if problems.len() == 0 { database.initialize_problems().unwrap(); } } let Ok(secret) = env::var("JWT_SECRET") else { eprintln!("missing environment variable JWT_SECRET"); return; }; let Ok(register_code) = env::var("REGISTER_CODE") else { eprintln!("missing environment variable REGISTER_CODE"); return; }; let Ok(listen_address) = env::var("LISTEN_ADDRESS") else { eprintln!("missing environment variable LISTEN_ADDRESS"); return; }; let Ok(admin_email) = env::var("ADMIN_EMAIL") else { eprintln!("missing environment variable ADMIN_EMAIL"); return; }; let Ok(admin_username) = env::var("ADMIN_USERNAME") else { eprintln!("missing environment variable ADMIN_USERNAME"); return; }; let Ok(admin_password) = env::var("ADMIN_PASSWORD") else { eprintln!("missing environment variable ADMIN_PASSWORD"); return; }; if let Ok(None) = database.fetch_user_by_email(&admin_email) { // register admin if admin user does not exist yet let Ok(()) = register_admin(&database, &admin_email, &admin_username, &admin_password) else { eprintln!("failed to register admin user"); return; }; } let state = AppState { secret: secret, register_code: register_code, database: database, }; let static_files = ServeDir::new("./static"); let app = Router::new() .route("/login", post(login)) .route("/logout", post(logout)) .route("/problem/{problem_id}", get(get_problem)) .route("/problem/{problem_id}/submission", get(get_display_submissions)) .route("/problem", get(get_problems)) .route("/problem", post(create_problem)) .route("/submission", post(create_submission)) .route("/submission/{submission_id}", get(get_display_submission)) .route("/user", post(create_user)) .route("/me", get(me)) .nest_service("/static", static_files) .with_state(state); let listener = tokio::net::TcpListener::bind(listen_address).await.unwrap(); axum::serve(listener, app).await.unwrap(); }