mod config; mod db; mod handlers; mod views; use std::fs; use std::sync::Arc; use axum::Router; use axum::routing::{get, post}; use rusqlite::Connection; use tokio::sync::Mutex; use crate::handlers::AppState; const DB_DIR: &str = "data"; const DB_PATH: &str = "data/app.db"; const LISTEN_HOST: &str = "127.0.0.1"; #[tokio::main] async fn main() -> Result<(), Box> { fs::create_dir_all(DB_DIR)?; let conn = Connection::open(DB_PATH)?; db::init_db(&conn)?; db::seed_db_if_empty(&conn)?; let app_config = config::load_config(); let state = AppState { db: Arc::new(Mutex::new(conn)), config: app_config, }; // Route wiring stays here so `main` is the single startup overview. let app = Router::new() .route("/", get(handlers::show_calendar)) .route("/login", get(handlers::show_login)) .route("/login", post(handlers::login)) .route("/signup", get(handlers::show_signup)) .route("/signup", post(handlers::signup)) .route("/logout", post(handlers::logout)) .route("/u/{username}", get(handlers::show_public_profile)) .route("/u/{username}/day/{date}", get(handlers::show_public_day)) .route("/reports", get(handlers::show_reports)) .route("/planning", get(handlers::show_planning)) .route("/planning", post(handlers::update_planning)) .route("/planning/password", get(handlers::show_change_password)) .route("/planning/password", post(handlers::change_password)) .route( "/calendar/{year}/{month}", get(handlers::show_calendar_for_month), ) .route("/day/{date}", get(handlers::show_day_entries)) .route("/day/{date}/weight", post(handlers::update_day_weight)) .route("/day/{date}/add", post(handlers::create_entry)) .route("/day/{date}/entry/{id}/update", post(handlers::edit_entry)) .route( "/day/{date}/entry/{id}/delete", post(handlers::remove_entry), ) .with_state(state); let listen_addr = format!("{}:{}", LISTEN_HOST, app_config.bind_port); let listener = tokio::net::TcpListener::bind(&listen_addr).await?; println!("Listening on http://{}", listen_addr); axum::serve(listener, app).await?; Ok(()) }