365 lines
13 KiB
Rust
365 lines
13 KiB
Rust
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<AccountData> = 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::<chrono::NaiveDateTime, _>(0),
|
|
user.try_get::<String, _>(1),
|
|
user.try_get::<chrono::NaiveDateTime, _>(2),
|
|
user.try_get::<String, _>(3),
|
|
user.try_get::<String, _>(4),
|
|
user.try_get::<String, _>(5),
|
|
user.try_get::<String, _>(6),
|
|
user.try_get::<String, _>(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::<chrono::NaiveDateTime, _>(0),
|
|
user_role.try_get::<String, _>(1),
|
|
user_role.try_get::<chrono::NaiveDateTime, _>(2),
|
|
user_role.try_get::<String, _>(3),
|
|
user_role.try_get::<String, _>(4),
|
|
user_role.try_get::<String, _>(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::<chrono::NaiveDateTime,_>(0).unwrap())
|
|
.bind(wishlistitem.try_get::<String,_>(1).unwrap())
|
|
.bind(wishlistitem.try_get::<chrono::NaiveDateTime,_>(2).unwrap())
|
|
.bind(wishlistitem.try_get::<String,_>(3).unwrap())
|
|
.bind(wishlistitem.try_get::<String,_>(4).unwrap())
|
|
.bind(wishlistitem.try_get::<String,_>(5).unwrap())
|
|
.bind(wishlistitem.try_get::<String,_>(6).unwrap())
|
|
.bind(wishlistitem.try_get::<String,_>(7).unwrap())
|
|
.bind(wishlistitem.try_get::<chrono::NaiveDateTime,_>(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);
|
|
}
|
|
}
|