From 98a6b4d3f46a10cdf7aab81041f01b26f373438e Mon Sep 17 00:00:00 2001 From: arkanoider <113362043+arkanoider@users.noreply.github.com> Date: Fri, 3 Jan 2025 14:33:56 +0100 Subject: [PATCH] Mostro database automatic creation (#411) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * New way to create automatically db of mostro - config template copied automatically * Update src/db.rs rabbit suggestion Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Update src/db.rs Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Fix Keys::parse() call * feat: create mostro.db automatically at first mostro run * chore: remove patch file * fix: added check to migrations folder existence * Fix: remove a lot o bloat code - discovered cargo can do migration with migrate! macro * Update src/db.rs LGTM Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Rabbit improvements * Fix: possible typo in db query * Create directory and files without asking --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: Francisco Calderón --- epa.patch | 57 ---------------------------------- src/cli/settings.rs | 49 +++++++++--------------------- src/db.rs | 74 +++++++++++++++++++++++++++++++++++++++------ src/util.rs | 10 ++++-- 4 files changed, 86 insertions(+), 104 deletions(-) delete mode 100644 epa.patch diff --git a/epa.patch b/epa.patch deleted file mode 100644 index 3acc6f94..00000000 --- a/epa.patch +++ /dev/null @@ -1,57 +0,0 @@ -diff --git a/src/app/release.rs b/src/app/release.rs -index 2513540..ffdefed 100644 ---- a/src/app/release.rs -+++ b/src/app/release.rs -@@ -328,6 +328,7 @@ pub async fn get_child_order( - } - Ordering::Less => { - notify_invalid_amount(order, request_id).await; -+ return Ok((false, order.clone())); - } - } - } -@@ -337,6 +338,8 @@ pub async fn get_child_order( - - fn create_base_order(order: &Order) -> Order { - let mut new_order = order.clone(); -+ new_order.id = uuid::Uuid::new_v4(); -+ new_order.status = Status::Pending.to_string(); - new_order.amount = 0; - new_order.hash = None; - new_order.preimage = None; -@@ -363,8 +366,6 @@ async fn update_order_for_equal(new_max: i64, new_order: &mut Order, my_keys: &K - new_order.fiat_amount = new_max; - new_order.max_amount = None; - new_order.min_amount = None; -- new_order.status = Status::Pending.to_string(); -- new_order.id = uuid::Uuid::new_v4(); - - let tags = crate::nip33::order_to_tags(new_order, None); - let event = crate::nip33::new_event(my_keys, "", new_order.id.to_string(), tags)?; -@@ -372,7 +373,7 @@ async fn update_order_for_equal(new_max: i64, new_order: &mut Order, my_keys: &K - new_order.clone().create(&pool).await?; - NOSTR_CLIENT - .get() -- .unwrap() -+ .ok_or_else(|| anyhow::Error::msg("NOSTR_CLIENT not initialized"))? - .send_event(event) - .await - .map_err(|err| anyhow::Error::msg(err.to_string()))?; -@@ -388,8 +389,6 @@ async fn update_order_for_greater( - let pool = db::connect().await?; - new_order.max_amount = Some(new_max); - new_order.fiat_amount = 0; -- new_order.id = uuid::Uuid::new_v4(); -- new_order.status = Status::Pending.to_string(); - - let tags = crate::nip33::order_to_tags(new_order, None); - let event = crate::nip33::new_event(my_keys, "", new_order.id.to_string(), tags)?; -@@ -397,7 +396,7 @@ async fn update_order_for_greater( - new_order.clone().create(&pool).await?; - NOSTR_CLIENT - .get() -- .unwrap() -+ .ok_or_else(|| anyhow::Error::msg("NOSTR_CLIENT not initialized"))? - .send_event(event) - .await - .map_err(|err| anyhow::Error::msg(err.to_string()))?; diff --git a/src/cli/settings.rs b/src/cli/settings.rs index 99c32f91..1c4dc8ce 100644 --- a/src/cli/settings.rs +++ b/src/cli/settings.rs @@ -3,14 +3,12 @@ use anyhow::{Error, Result}; use config::{Config, ConfigError, Environment, File}; use serde::Deserialize; use std::ffi::OsString; -use std::fs; -use std::io::{stdin, stdout, BufRead, Write}; +use std::io::Write; #[cfg(unix)] use std::os::unix::ffi::OsStrExt; #[cfg(windows)] use std::os::windows::ffi::OsStrExt; use std::path::{Path, PathBuf}; -use std::process; #[cfg(windows)] fn has_trailing_slash(p: &Path) -> bool { @@ -198,39 +196,20 @@ pub fn init_default_dir(config_path: Option) -> Result { // If settings dir is not existing if !folder_default { - println!( - "Creating .mostro default directory {}", + if std::fs::create_dir(settings_dir_default.clone()).is_ok() { + tracing::info!("Created mostro default directory!"); + let mut config_file = + std::fs::File::create_new(settings_dir_default.join("settings.toml"))?; + let buf = include_bytes!("../../settings.tpl.toml"); + config_file.write_all(buf)?; + config_file.flush()?; + } + tracing::info!( + "Created settings file based on template and copied to {} directory", settings_dir_default.display() ); - print!("Are you sure? (Y/n) > "); - - // Ask user confirm for default folder - let mut user_input = String::new(); - let _input = stdin(); - - stdout().flush()?; - - let mut answer = stdin().lock(); - answer.read_line(&mut user_input)?; - - match user_input.to_lowercase().as_str().trim_end() { - "y" | "" => { - fs::create_dir(settings_dir_default.clone())?; - println!("You have created mostro default directory!"); - println!("Please, copy settings.tpl.toml and mostro.db too files in {} folder then edit settings file fields with right values (see README.md)", settings_dir_default.display()); - process::exit(0); - } - "n" => { - println!("Try again with another folder..."); - process::exit(0); - } - &_ => { - println!("Can't get what you're saying!"); - process::exit(0); - } - }; - } else { - // Set path - Ok(settings_dir_default) + return Ok(settings_dir_default); } + // Set path + Ok(settings_dir_default) } diff --git a/src/db.rs b/src/db.rs index ad7ca86d..d0a15378 100644 --- a/src/db.rs +++ b/src/db.rs @@ -1,29 +1,85 @@ use crate::app::rate_user::{MAX_RATING, MIN_RATING}; +use anyhow::Result; use mostro_core::dispute::Dispute; use mostro_core::order::Order; use mostro_core::order::Status; use mostro_core::user::User; use nostr_sdk::prelude::*; -use sqlx::migrate::MigrateDatabase; use sqlx::pool::Pool; use sqlx::sqlite::SqliteRow; use sqlx::Row; use sqlx::Sqlite; use sqlx::SqlitePool; +use std::path::Path; use uuid::Uuid; use crate::cli::settings::Settings; -pub async fn connect() -> Result, sqlx::Error> { +pub async fn connect() -> Result> { + // Get mostro settings let db_settings = Settings::get_db(); let mut db_url = db_settings.url; db_url.push_str("mostro.db"); - if !Sqlite::database_exists(&db_url).await.unwrap_or(false) { - panic!("Not database found, please create a new one first!"); - } - let pool = SqlitePool::connect(&db_url).await?; - - Ok(pool) + // Remove sqlite:// from db_url + let tmp = db_url.replace("sqlite://", ""); + let db_path = Path::new(&tmp); + let conn = if !db_path.exists() { + let _file = std::fs::File::create_new(db_path).map_err(|e| { + anyhow::anyhow!( + "Failed to create database file at {}: {}", + db_path.display(), + e + ) + })?; + match SqlitePool::connect(&db_url).await { + Ok(pool) => { + tracing::info!( + "Successfully created Mostro database file at {}", + db_path.display(), + ); + match sqlx::migrate!().run(&pool).await { + Ok(_) => (), + Err(e) => { + // Clean up the created file on migration failure + if let Err(cleanup_err) = std::fs::remove_file(db_path) { + tracing::error!( + error = %cleanup_err, + path = %db_path.display(), + "Failed to create database connection" + ); + } + return Err(anyhow::anyhow!( + "Failed to create database connection at {}: {}", + db_path.display(), + e + )); + } + } + pool + } + Err(e) => { + tracing::error!( + error = %e, + path = %db_path.display(), + "Failed to create database connection" + ); + return Err(anyhow::anyhow!( + "Failed to create database connection at {}: {}", + db_path.display(), + e + )); + } + } + } else { + SqlitePool::connect(&db_url).await.map_err(|e| { + anyhow::anyhow!( + "Failed to connect to existing database at {}: {}", + db_path.display(), + e + ) + })? + }; + Ok(conn) } pub async fn edit_buyer_pubkey_order( @@ -273,7 +329,7 @@ pub async fn find_held_invoices(pool: &SqlitePool) -> anyhow::Result> r#" SELECT * FROM orders - WHERE invoice_held_at !=0 true AND status == 'active' + WHERE invoice_held_at !=0 AND status == 'active' "#, ) .fetch_all(pool) diff --git a/src/util.rs b/src/util.rs index 35cab119..36bbc2e0 100644 --- a/src/util.rs +++ b/src/util.rs @@ -331,9 +331,13 @@ pub async fn send_dm( pub fn get_keys() -> Result { let nostr_settings = Settings::get_nostr(); // nostr private key - let my_keys = Keys::parse(&nostr_settings.nsec_privkey)?; - - Ok(my_keys) + match Keys::parse(&nostr_settings.nsec_privkey) { + Ok(my_keys) => Ok(my_keys), + Err(e) => { + tracing::error!("Failed to parse nostr private key: {}", e); + std::process::exit(1); + } + } } #[allow(clippy::too_many_arguments)]