Complete conversion to postgres

This commit is contained in:
Chris Jean-Marie 2024-12-13 16:14:32 +00:00
parent 4ff14e6fa1
commit b39dde34e4
4 changed files with 245 additions and 41 deletions

View File

@ -23,7 +23,7 @@ oauth2 = "4.4"
http = "1.1"
tower-http = { version = "0.6.1", features = ["full"] }
chrono = { version = "0.4.38", features = ["serde"] }
sqlx = { version = "0.8", features = ["postgres", "runtime-tokio", "macros", "chrono", "uuid"] }
sqlx = { version = "0.8", features = ["postgres", "sqlite","runtime-tokio", "macros", "chrono", "uuid"] }
uuid = { version = "1.10", features = ["v4"] }
dotenvy = "0.15"
constant_time_eq = "0.3"

View File

@ -1,13 +1,13 @@
-- Drop Postgres tables
drop table oauth2_state_storage;
drop table user_sessions;
drop table users;
drop table roles;
drop table user_roles;
drop table role_permissions;
drop table wishlist_items;
drop table gift_exchange;
drop table gift_exchange_participants;
drop table calendar;
drop table calendar_event_types;
drop table calendar_events;
drop table if exists oauth2_state_storage;
drop table if exists user_sessions;
drop table if exists users;
drop table if exists roles;
drop table if exists user_roles;
drop table if exists role_permissions;
drop table if exists wishlist_items;
drop table if exists gift_exchange;
drop table if exists gift_exchange_participants;
drop table if exists calendar;
drop table if exists calendar_event_types;
drop table if exists calendar_events;

View File

@ -47,6 +47,8 @@ create table IF NOT EXISTS user_roles (
role_id uuid NOT NULL
);
create unique index if not exists unique_user_role on user_roles(user_id, role_id);
create table IF NOT EXISTS role_permissions (
id uuid PRIMARY KEY default gen_random_uuid(),
created_at timestamp NOT NULL default now(),
@ -64,8 +66,8 @@ create table if not exists wishlist_items (
updated_at timestamp null default now(),
updated_by uuid null,
user_id uuid null,
item varchar(255) null,
item_url varchar(255) null,
item varchar(512) null,
item_url varchar(1024) null,
purchased_by uuid null,
received_at timestamp null
);

View File

@ -3,13 +3,12 @@ use axum::{
routing::{get, get_service},
Extension, Router,
};
use dotenvy::var;
use secret_gift_exchange::{giftexchange, giftexchange_save, giftexchanges};
use sqlx::migrate::Migrator;
use sqlx::{PgPool, postgres::PgPoolOptions};
use sqlx::{migrate::Migrator, sqlite::SqlitePoolOptions, sqlite::SqliteRow, Row, SqlitePool};
use sqlx::{postgres::PgPoolOptions, PgPool};
use std::net::SocketAddr;
use tower_http::services::ServeDir;
use dotenvy::var;
mod calendar;
mod email;
@ -17,17 +16,21 @@ mod error_handling;
mod google_oauth;
mod middlewares;
mod routes;
mod secret_gift_exchange;
mod user;
mod wishlist;
mod secret_gift_exchange;
use calendar::{calendar, get_events};
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, useradmin};
use user::{add_user_role, delete_user_role, UserData};
use calendar::{calendar, get_events};
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 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 email::send_emails;
#[derive(Clone)]
@ -44,9 +47,7 @@ async fn main() {
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,
};
let app_state = AppState { db_pool: db_pool };
static MIGRATOR: Migrator = sqlx::migrate!();
@ -55,39 +56,74 @@ async fn main() {
.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<UserData> = 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("/users/:user_id", get(user_profile))
.route("/roles/:user_id/:role_id/add", get(add_user_role))
.route("/roles/:user_id/:user_role_id/delete", get(delete_user_role))
.route(
"/roles/:user_id/:user_role_id/delete",
get(delete_user_role),
)
// Calendar
.route("/calendar", get(calendar))
.route("/getevents/:calendar", get(get_events))
// Wishlist
.route("/wishlists", get(wishlists))
.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))
.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(
"/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))
@ -113,3 +149,169 @@ async fn main() {
.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);
}
}
}