use axum::{ middleware, routing::{get, get_service, post}, Extension, Router, }; use dotenvy::var; use secret_gift_exchange::{giftexchange, giftexchange_save, giftexchanges}; use sqlx::{migrate::Migrator, sqlite::SqlitePoolOptions, Row, SqlitePool}; use sqlx::{postgres::PgPoolOptions, PgPool}; use std::net::SocketAddr; use tower_http::services::ServeDir; mod calendar; mod email; mod error_handling; mod google_oauth; mod middlewares; mod routes; mod secret_gift_exchange; mod user; mod wishlist; use calendar::{calendar, get_events, create_event, new_event}; use error_handling::AppError; use google_oauth::{google_auth_return, login, logout}; use middlewares::inject_user_data; use routes::{about, contact, dashboard, index, profile, user_profile, user_profile_account, useradmin}; use user::{add_user_role, delete_user_role, AccountData}; use wishlist::{ user_wishlist, user_wishlist_add, user_wishlist_add_item, user_wishlist_bought_item, user_wishlist_delete_item, user_wishlist_edit_item, user_wishlist_received_item, user_wishlist_returned_item, user_wishlist_save_item, wishlists, }; use crate::calendar::new_request; //use email::send_emails; #[derive(Clone)] pub struct AppState { pub db_pool: PgPool, } #[tokio::main] async fn main() { // initialize tracing tracing_subscriber::fmt::init(); // Get the server settings from the env file let database_url = var("DATABASE_URL").expect("DATABASE_URL not set"); let db_pool = PgPoolOptions::new().connect(&database_url).await.unwrap(); let app_state = AppState { db_pool: db_pool }; static MIGRATOR: Migrator = sqlx::migrate!(); MIGRATOR .run(&app_state.db_pool) .await .expect("Failed to run migrations"); // Copy from old sqlite database if it exists if let Ok(source_db_url) = var("SOURCE_DB_URL") { let sdb_pool = SqlitePoolOptions::new() .max_connections(5) .connect(&source_db_url) .await .unwrap(); copy_database(&sdb_pool, &app_state.db_pool).await; } else { println!("SOURCE_DB_URL not set"); } let user_data: Option = None; // build our application with some routes let app = Router::new() .route("/dashboard", get(dashboard)) // User .route("/profile", get(profile)) .route("/useradmin", get(useradmin)) .route("/user/{user_id}", get(user_profile)) .route("/user/{user_id}/{account_id}", get(user_profile_account)) .route("/roles/{user_id}/{role_id}/add", get(add_user_role)) .route( "/roles/{user_id}/{user_role_id}/delete", get(delete_user_role), ) // Calendar .route("/calendar", get(calendar)) .route("/calendar/getevents", get(get_events)) .route("/calendar/createevent", post(create_event)) .route("/calendar/newevent", get(new_event)) .route("/calendar/newrequest", post(new_request)) // Wishlist .route("/wishlists", get(wishlists)) .route("/userwishlist/{user_id}", get(user_wishlist)) .route( "/userwishlist/add/{user_id}", get(user_wishlist_add).post(user_wishlist_add_item), ) .route( "/userwishlist/edit/{item_id}", get(user_wishlist_edit_item).post(user_wishlist_save_item), ) .route( "/userwishlist/bought/{user_id}", get(user_wishlist_bought_item), ) .route( "/userwishlist/received/{user_id}", get(user_wishlist_received_item), ) .route( "/userwishlist/delete/{item_id}", get(user_wishlist_delete_item), ) .route( "/userwishlist/returned/{item_id}", get(user_wishlist_returned_item), ) // Secret Gift Exchange - Not ready for public use yet .route("/giftexchanges", get(giftexchanges)) .route( "/giftexchange/{giftexchange_id}", get(giftexchange).post(giftexchange_save), ) .nest_service( "/assets", ServeDir::new("templates/assets") .fallback(get_service(ServeDir::new("templates/assets"))), ) .route("/", get(index)) .route("/about", get(about)) .route("/contactus", get(contact)) .route("/login", get(login)) .route("/logout", get(logout)) .route("/google_auth_return", get(google_auth_return)) .route_layer(middleware::from_fn_with_state( app_state.db_pool.clone(), inject_user_data, )) .with_state(app_state.db_pool.clone()) .layer(Extension(user_data)); // Send email indicating server has started //let recipients = get_useremails_by_role("admin".to_string(), &app_state.db_pool).await; //send_emails("Server started".to_string(), recipients, "Server has been started".to_string()); // run it let addr = SocketAddr::from(([0, 0, 0, 0], 40192)); tracing::debug!("listening on {}", addr); axum_server::bind(addr) .serve(app.into_make_service()) .await .unwrap(); } async fn copy_database(sdb_pool: &SqlitePool, db_pool: &PgPool) { // Copy users let users = sqlx::query( r#"select datetime(u.created_at, 'unixepoch'), coalesce(cb.email, 'admin@jean-marie.ca') as created_by_email, datetime(u.updated_at, 'unixepoch'), coalesce(ub.email, 'admin@jean-marie.ca') as updated_by_email, u.email, u.name, u.family_name, u.given_name from users u left join users cb on cb.id = u.created_by left join users ub on ub.id = u.updated_by;"#, ) .fetch_all(sdb_pool) .await .expect("Failed to copy users from SQLite to Postgres"); println!("\nCopying {} users", users.len()); for user in users { if let ( Ok(created_at), Ok(created_by), Ok(updated_at), Ok(updated_by), Ok(email), Ok(name), Ok(family_name), Ok(given_name), ) = ( user.try_get::(0), user.try_get::(1), user.try_get::(2), user.try_get::(3), user.try_get::(4), user.try_get::(5), user.try_get::(6), user.try_get::(7), ) { let result = sqlx::query( r#"insert into users (created_at, created_by, updated_at, updated_by, email, name, family_name, given_name) values ($1, (select id from users where email =$2), $3, (select id from users where email =$4), $5, $6, $7, $8)"# ) .bind(created_at) .bind(created_by) .bind(updated_at) .bind(updated_by) .bind(email) .bind(name) .bind(family_name) .bind(given_name) .execute(db_pool) .await; if let Err(e) = result { println!("Error: {}", e); } } } // Copy user roles let user_roles = sqlx::query( r#"select datetime(ur.created_at, 'unixepoch'), coalesce(cb.email, 'admin@jean-marie.ca') as created_by_email, datetime(ur.updated_at, 'unixepoch'), coalesce(ub.email, 'admin@jean-marie.ca') as updated_by_email, u.email as user_email, r.name as role_name from user_roles ur left join users cb on cb.id = ur.created_by left join users ub on ub.id = ur.updated_by join users u on u.id = ur.user_id join roles r on r.id = ur.role_id;"#, ) .fetch_all(sdb_pool) .await .expect("Failed to copy user roles from SQLite to Postgres"); println!("\nCopying {} user roles", user_roles.len()); for user_role in user_roles { if let ( Ok(created_at), Ok(created_by), Ok(updated_at), Ok(updated_by), Ok(user_email), Ok(role_name), ) = ( user_role.try_get::(0), user_role.try_get::(1), user_role.try_get::(2), user_role.try_get::(3), user_role.try_get::(4), user_role.try_get::(5), ) { let result = sqlx::query( r#"insert into user_roles (created_at, created_by, updated_at, updated_by, user_id, role_id) values ($1, (select id from users where email=$2), $3, (select id from users where email=$4), (select id from users where email=$5), (select id from roles where name=$6))"# ) .bind(created_at) .bind(created_by) .bind(updated_at) .bind(updated_by) .bind(user_email) .bind(role_name) .execute(db_pool) .await; if let Err(e) = result { println!("Error: {}", e); } } } // Copy wishlistitems let wishlistitems = sqlx::query( r#"select datetime(wi.created_at, 'unixepoch'), coalesce(cb.email, 'admin@jean-marie.ca') as created_by_email, datetime(wi.updated_at, 'unixepoch'), coalesce(ub.email, 'admin@jean-marie.ca') as updated_by_email, u.email as user_email, wi.item, wi.item_url, pb.email, datetime(wi.received_at, 'unixepoch') from wishlist_items wi left join users cb on cb.id = wi.created_by left join users ub on ub.id = wi.updated_by left join users pb on pb.id = wi.purchased_by join users u on u.id = wi.user_id;"#, ) .fetch_all(sdb_pool) .await .expect("Failed to copy wishlistitems from SQLite to Postgres"); println!("\nCopying {} wishlistitems", wishlistitems.len()); for wishlistitem in wishlistitems { let result = sqlx::query( r#"insert into wishlist_items (created_at, created_by, updated_at, updated_by, user_id, item, item_url, purchased_by, received_at) values ($1, (select id from users where email=$2), $3, (select id from users where email=$4), (select id from users where email=$5), $6, $7, (select id from users where email=$8), $9)"# ) .bind(wishlistitem.try_get::(0).unwrap()) .bind(wishlistitem.try_get::(1).unwrap()) .bind(wishlistitem.try_get::(2).unwrap()) .bind(wishlistitem.try_get::(3).unwrap()) .bind(wishlistitem.try_get::(4).unwrap()) .bind(wishlistitem.try_get::(5).unwrap()) .bind(wishlistitem.try_get::(6).unwrap()) .bind(wishlistitem.try_get::(7).unwrap()) .bind(wishlistitem.try_get::(8).unwrap_or_default()) .execute(db_pool) .await; if let Err(e) = result { println!("Error: {}", e); } } // Run migration scripts again // Copy accounts(users) to profiles(people) let result =sqlx::query(r#"insert into people (created_by, updated_by, email, name, family_name, given_name) select created_by, updated_by, email, name, family_name, given_name from users on conflict do nothing;"#) .execute(db_pool) .await; if let Err(e) = result { println!("Error: {}", e); } // Link accounts to profiles let result = sqlx::query(r#"update users u set person_id = p.id from people p where p.email = u.email;"#) .execute(db_pool) .await; if let Err(e) = result { println!("Error: {}", e); } // Move wishlist items from accounts to profiles let result = sqlx::query(r#"update wishlist_items wi set user_id = p.person_id from users p where p.id = wi.user_id;"#) .execute(db_pool) .await; if let Err(e) = result { println!("Error: {}", e); } // Copy normal role from accounts to profiles let result = sqlx::query(r#"insert into user_roles (created_at, created_by, updated_at, updated_by, user_id, role_id) select ur.created_at, ur.created_by, ur.updated_at, ur.updated_by, u.person_id, ur.role_id from user_roles ur join roles r on r.id = ur.role_id join users u on u.id = ur.user_id where r.name = 'normal' on conflict do nothing;"#) .execute(db_pool) .await; if let Err(e) = result { println!("Error: {}", e); } }