use axum::{ middleware, routing::{get, post, IntoMakeService}, }; use axum_login::AuthManagerLayerBuilder; use sqlx::SqlitePool; #[macro_use] extern crate justerror; /// Some public interfaces for interacting with the database outside of the web /// app pub use db::get_db_pool; pub mod conf; pub mod imdb_utils; pub mod import_utils; pub mod misc_util; pub use conf::*; pub use signup::Invitation; pub use stars::*; pub use users::User; pub use watches::{ShowKind, Watch, WatchQuest}; pub type WWRouter = axum::Router; // everything else is private to the crate mod auth; mod db; mod generic_handlers; mod login; mod search; mod signup; mod stars; mod templates; mod users; mod watches; // things we want in the crate namespace use auth::AuthSession; use optional_optional_user::OptionalOptionalUser; use templates::*; use watches::templates::*; /// Returns the router to be used as a service or test object, you do you. pub async fn app(db_pool: sqlx::SqlitePool) -> IntoMakeService { // don't bother bringing handlers into the whole crate namespace use auth::*; use generic_handlers::{handle_slash, handle_slash_redir}; use login::{get_login, get_logout, post_login, post_logout}; use search::get_search_watch; use signup::handlers::{get_create_user, get_signup_success, post_create_user}; use tower_http::services::ServeDir; use watches::handlers::{ get_add_new_watch, get_watch, get_watch_status, get_watches, post_add_new_watch, post_add_watch_quest, }; let auth_layer = { let session_layer = session_layer(db_pool.clone()).await; let store = AuthStore::new(db_pool.clone()); AuthManagerLayerBuilder::new(store, session_layer).build() }; let assets_dir = std::env::current_dir().unwrap().join("assets"); let assets_svc = ServeDir::new(assets_dir.as_path()); axum::Router::new() .route("/", get(handle_slash).post(handle_slash)) .nest_service("/assets", assets_svc) .route("/signup", post(post_create_user)) .route("/signup/:invitation", get(get_create_user)) .route("/signup_success/:user", get(get_signup_success)) .route("/login", get(get_login).post(post_login)) .route("/logout", get(get_logout).post(post_logout)) .route("/watches", get(get_watches)) .route("/watch", get(get_watch)) .route("/watch/:watch", get(get_watch)) .route("/watch/status/:watch", get(get_watch_status)) .route("/search", get(get_search_watch)) .route("/add", get(get_add_new_watch).post(post_add_new_watch)) .route( "/add/watch", get(get_search_watch).post(post_add_watch_quest), ) .fallback(handle_slash_redir) .layer(middleware::from_fn_with_state( db_pool.clone(), users::handle_update_last_seen, )) .layer(auth_layer) .with_state(db_pool) .into_make_service() } #[cfg(test)] pub mod test_utils; //-************************************************************************ // tests for the proc macro for optional user //-************************************************************************ #[cfg(test)] mod test { use super::{signup::templates::SignupSuccessPage, MainPage, OptionalOptionalUser, User}; #[test] fn main_page_has_optional_user() { assert!(MainPage::default().has_optional_user()); assert!(MainPage::default().has_user()); } #[test] fn signup_success_has_no_optional_user() { assert!(!SignupSuccessPage::default().has_optional_user()); } #[test] fn user_is_not_optional() { #[derive(Default, OptionalOptionalUser)] struct TestThing { user: User, } assert!(!TestThing::default().has_optional_user()); assert!(TestThing::default().has_mandatory_user()); assert!(TestThing::default().has_user()); } #[test] fn user_is_not_user() { #[derive(Default, OptionalOptionalUser)] struct TestThing { user: Option, } assert!(!TestThing::default().has_optional_user()); assert!(!TestThing::default().has_user()); } }