Skip to content

Commit 54b9b75

Browse files
mostronatorcoder[bot]MostronatorCoder[bot]
andauthored
refactor: remove global accesses from handler paths (#656 PR C) (#666)
## Context Issue #656 tracks cleanup tasks after phase-5 DI migration (#639). This PR implements **PR C**: remove global accesses from handler paths. ## Changes Replaced direct global function calls with `ctx` accessors in handlers: ### Settings access - `Settings::get_mostro()` → `ctx.settings().mostro` - `Settings::get_ln()` → `ctx.settings().lightning` (where applicable) ### Nostr client access - `get_nostr_client()?` → `ctx.nostr_client()` - Removed fallible Result handling (ctx always has valid client) ### Database pool access - `pool` parameters in internal functions → `ctx: &AppContext` - Extract pool internally: `let pool = ctx.pool();` ## Files Modified (8 total) **Handlers:** - `src/app/admin_cancel.rs` - Settings + nostr_client - `src/app/admin_settle.rs` - Settings + nostr_client - `src/app/admin_take_dispute.rs` - Settings + nostr_client - `src/app/cancel.rs` - Propagate ctx to internal helpers - `src/app/dispute.rs` - Settings + nostr_client + close_dispute_after_user_resolution - `src/app/order.rs` - Settings (calculate_and_check_quote) - `src/app/orders.rs` - Settings - `src/app/release.rs` - nostr_client ## Breaking Changes - `close_dispute_after_user_resolution()` signature changed: - Before: `(pool, order, status, keys, context)` - After: `(ctx, order, status, keys, context)` ## What Remains Global The following still use globals (tracked for future PRs): **In `src/app/release.rs`:** - `check_failure_retries()` - uses `get_db_pool()`, `Settings::get_ln()` - `do_payment()` - uses `get_db_pool()` - `retry_failed_payments()` - uses `get_db_pool()` **Reason:** These are called from `src/scheduler.rs` which doesn't have `AppContext`. Migrating scheduler requires a larger refactor. **In `src/app/context.rs`:** - `AppContext::from_globals()` - by design (bridges old → new architecture) ## Diff Stats - 8 files changed - +59 / -91 lines - **Net: -32 lines** ## Validation ✅ `cargo fmt` ✅ `cargo clippy --all-targets --all-features -- -D warnings` ✅ `cargo test --bin mostrod` (189 passed, 0 failed) ## Debt Grep Checks ```bash # Remaining globals in src/app (excluding context.rs): $ grep -R "Settings::get_" src/app --include="*.rs" | grep -v context.rs src/app/release.rs:39: let ln_settings = Settings::get_ln(); $ grep -R "get_db_pool\|get_nostr_client" src/app --include="*.rs" | grep -v context.rs src/app/release.rs:36: let pool = get_db_pool(); src/app/release.rs:537: let pool = get_db_pool(); src/app/release.rs:622: let pool = get_db_pool(); ``` All remaining globals are in scheduler-called functions (documented above). ## Related - Parent cleanup issue: #656 - PR A (legacy wrappers): #663 ✅ merged - PR B (dispatcher pool): #665 ✅ merged - Original DI migration: #639 ## Next Steps - PR D (optional): Migrate scheduler to use AppContext - PR E (optional): Finalize AppContext::from_globals() Co-authored-by: MostronatorCoder[bot] <182182091+MostronatorCoder[bot]@users.noreply.github.com>
1 parent 6f2a623 commit 54b9b75

8 files changed

Lines changed: 59 additions & 91 deletions

File tree

src/app/admin_cancel.rs

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,10 @@ use std::borrow::Cow;
22
use std::str::FromStr;
33

44
use crate::app::context::AppContext;
5-
use crate::config::settings::Settings;
65
use crate::db::{find_dispute_by_order_id, is_assigned_solver, is_dispute_taken_by_admin};
76
use crate::lightning::LndConnector;
87
use crate::nip33::{create_platform_tag_values, new_dispute_event};
9-
use crate::util::{enqueue_order_msg, get_nostr_client, get_order, send_dm, update_order_event};
8+
use crate::util::{enqueue_order_msg, get_order, send_dm, update_order_event};
109
use mostro_core::prelude::*;
1110
use nostr::nips::nip59::UnwrappedGift;
1211
use nostr_sdk::prelude::*;
@@ -129,7 +128,7 @@ pub async fn admin_cancel_action(
129128
),
130129
Tag::custom(
131130
TagKind::Custom(Cow::Borrowed("y")),
132-
create_platform_tag_values(Settings::get_mostro().name.as_deref()),
131+
create_platform_tag_values(ctx.settings().mostro.name.as_deref()),
133132
),
134133
Tag::custom(
135134
TagKind::Custom(Cow::Borrowed("z")),
@@ -143,13 +142,9 @@ pub async fn admin_cancel_action(
143142
// Publish dispute event with update
144143
info!("Dispute event to be published: {event:#?}");
145144

146-
match get_nostr_client() {
147-
Ok(client) => {
148-
if let Err(e) = client.send_event(&event).await {
149-
error!("Failed to send dispute status event: {}", e);
150-
}
151-
}
152-
Err(e) => error!("Failed to get Nostr client: {}", e),
145+
let client = ctx.nostr_client();
146+
if let Err(e) = client.send_event(&event).await {
147+
error!("Failed to send dispute status event: {}", e);
153148
}
154149
}
155150

src/app/admin_settle.rs

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
11
use crate::app::context::AppContext;
2-
use crate::config::settings::Settings;
32
use crate::db::{find_dispute_by_order_id, is_assigned_solver, is_dispute_taken_by_admin};
43
use crate::lightning::LndConnector;
54
use crate::nip33::{create_platform_tag_values, new_dispute_event};
6-
use crate::util::{
7-
enqueue_order_msg, get_nostr_client, get_order, settle_seller_hold_invoice, update_order_event,
8-
};
5+
use crate::util::{enqueue_order_msg, get_order, settle_seller_hold_invoice, update_order_event};
96

107
use mostro_core::prelude::*;
118
use nostr::nips::nip59::UnwrappedGift;
@@ -108,7 +105,7 @@ pub async fn admin_settle_action(
108105
),
109106
Tag::custom(
110107
TagKind::Custom(std::borrow::Cow::Borrowed("y")),
111-
create_platform_tag_values(Settings::get_mostro().name.as_deref()),
108+
create_platform_tag_values(ctx.settings().mostro.name.as_deref()),
112109
),
113110
Tag::custom(
114111
TagKind::Custom(std::borrow::Cow::Borrowed("z")),
@@ -123,15 +120,9 @@ pub async fn admin_settle_action(
123120
// Print event dispute with update
124121
tracing::info!("Dispute event to be published: {event:#?}");
125122

126-
match get_nostr_client() {
127-
Ok(client) => {
128-
if let Err(e) = client.send_event(&event).await {
129-
error!("Failed to send dispute settlement event: {}", e);
130-
}
131-
}
132-
Err(e) => {
133-
error!("Failed to get Nostr client for dispute settlement: {}", e);
134-
}
123+
let client = ctx.nostr_client();
124+
if let Err(e) = client.send_event(&event).await {
125+
error!("Failed to send dispute settlement event: {}", e);
135126
}
136127
}
137128

src/app/admin_take_dispute.rs

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
use crate::app::context::AppContext;
2-
use crate::config::settings::Settings;
32
use crate::db::{find_solver_pubkey, is_user_present};
43
use crate::nip33::{create_platform_tag_values, new_dispute_event};
5-
use crate::util::{get_dispute, get_nostr_client, send_dm};
4+
use crate::util::{get_dispute, send_dm};
65
use mostro_core::prelude::*;
76
use nostr::nips::nip59::UnwrappedGift;
87
use nostr_sdk::prelude::*;
98
use sqlx::{Pool, Sqlite};
9+
1010
use sqlx_crud::Crud;
1111
use std::str::FromStr;
1212
use tracing::info;
@@ -233,7 +233,7 @@ pub async fn admin_take_dispute_action(
233233
),
234234
Tag::custom(
235235
TagKind::Custom(std::borrow::Cow::Borrowed("y")),
236-
create_platform_tag_values(Settings::get_mostro().name.as_deref()),
236+
create_platform_tag_values(ctx.settings().mostro.name.as_deref()),
237237
),
238238
Tag::custom(
239239
TagKind::Custom(std::borrow::Cow::Borrowed("z")),
@@ -245,16 +245,7 @@ pub async fn admin_take_dispute_action(
245245
.map_err(|e| MostroInternalErr(ServiceError::NostrError(e.to_string())))?;
246246
info!("Dispute event to be published: {event:#?}");
247247

248-
let client = get_nostr_client()
249-
.map_err(|e| {
250-
info!(
251-
"Failed to get nostr client for dispute {}: {}",
252-
dispute.id, e
253-
);
254-
e
255-
})
256-
.map_err(|e| MostroInternalErr(ServiceError::NostrError(e.to_string())))?;
257-
248+
let client = ctx.nostr_client();
258249
client
259250
.send_event(&event)
260251
.await

src/app/cancel.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -75,14 +75,15 @@ async fn notify_creator(order: &Order, request_id: Option<u64>) -> Result<(), Mo
7575
/// - Persists `Status::CooperativelyCanceled`
7676
/// - Publishes a new replaceable nostr event and notifies both parties
7777
async fn cancel_cooperative_execution_step_2<L: CancelLightning + Send>(
78-
pool: &Pool<Sqlite>,
78+
ctx: &AppContext,
7979
event: &UnwrappedGift,
8080
request_id: Option<u64>,
8181
mut order: Order,
8282
counterparty_pubkey: String,
8383
my_keys: &Keys,
8484
ln_client: &mut L,
8585
) -> Result<(), MostroError> {
86+
let pool = ctx.pool();
8687
// Guard: the same party cannot both initiate and confirm the cooperative cancel.
8788
if let Some(initiator) = &order.cancel_initiator_pubkey {
8889
if *initiator == event.rumor.pubkey.to_string() {
@@ -137,7 +138,7 @@ async fn cancel_cooperative_execution_step_2<L: CancelLightning + Send>(
137138
// If there was an active dispute on this order, close it since the users
138139
// resolved the situation themselves via cooperative cancellation.
139140
close_dispute_after_user_resolution(
140-
pool,
141+
ctx,
141142
&order,
142143
DisputeStatus::SellerRefunded,
143144
my_keys,
@@ -393,7 +394,7 @@ async fn cancel_action_generic<L: CancelLightning + Send>(
393394
cancel_not_active_order(pool, event, order, my_keys, request_id, ln_client).await?
394395
}
395396
Status::Active | Status::FiatSent | Status::Dispute => {
396-
cancel_active_order(pool, event, order, my_keys, request_id, ln_client).await?
397+
cancel_active_order(ctx, event, order, my_keys, request_id, ln_client).await?
397398
}
398399
_ => return Err(MostroCantDo(CantDoReason::NotAllowedByStatus)),
399400
}
@@ -406,13 +407,14 @@ async fn cancel_action_generic<L: CancelLightning + Send>(
406407
/// Marks which side initiated the cooperative cancel and either starts the flow
407408
/// (step 1) or completes it (step 2) when both sides have acknowledged.
408409
async fn cancel_active_order<L: CancelLightning + Send>(
409-
pool: &Pool<Sqlite>,
410+
ctx: &AppContext,
410411
event: &UnwrappedGift,
411412
mut order: Order,
412413
my_keys: &Keys,
413414
request_id: Option<u64>,
414415
ln_client: &mut L,
415416
) -> Result<(), MostroError> {
417+
let pool = ctx.pool();
416418
// Get seller and buyer pubkey
417419
let seller_pubkey = order.get_seller_pubkey().map_err(MostroInternalErr)?;
418420
let buyer_pubkey = order.get_buyer_pubkey().map_err(MostroInternalErr)?;
@@ -430,7 +432,7 @@ async fn cancel_active_order<L: CancelLightning + Send>(
430432
match order.cancel_initiator_pubkey {
431433
Some(_) => {
432434
cancel_cooperative_execution_step_2(
433-
pool,
435+
ctx,
434436
event,
435437
request_id,
436438
order,

src/app/dispute.rs

Lines changed: 28 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,13 @@
33
//! and publish dispute events to the network.
44
55
use crate::app::context::AppContext;
6-
use crate::config::settings::Settings;
76
use crate::db::find_dispute_by_order_id;
87
use crate::nip33::{create_platform_tag_values, new_dispute_event};
9-
use crate::util::{enqueue_order_msg, get_nostr_client, get_order};
8+
use crate::util::{enqueue_order_msg, get_order};
109
use mostro_core::prelude::*;
1110
use nostr::nips::nip59::UnwrappedGift;
1211
use nostr_sdk::prelude::*;
13-
use sqlx::{Pool, Sqlite};
12+
1413
use sqlx_crud::traits::Crud;
1514
use std::borrow::Cow;
1615
use uuid::Uuid;
@@ -20,6 +19,7 @@ use uuid::Uuid;
2019
/// Creates and publishes a NIP-33 replaceable event containing dispute details,
2120
/// including status, initiator (`buyer` or `seller`), and application metadata.
2221
async fn publish_dispute_event(
22+
ctx: &AppContext,
2323
dispute: &Dispute,
2424
my_keys: &Keys,
2525
is_buyer_dispute: bool,
@@ -45,7 +45,7 @@ async fn publish_dispute_event(
4545
// Application identifier tag
4646
Tag::custom(
4747
TagKind::Custom(Cow::Borrowed("y")),
48-
create_platform_tag_values(Settings::get_mostro().name.as_deref()),
48+
create_platform_tag_values(ctx.settings().mostro.name.as_deref()),
4949
),
5050
// Event type tag
5151
Tag::custom(
@@ -61,23 +61,18 @@ async fn publish_dispute_event(
6161

6262
tracing::info!("Publishing dispute event: {:#?}", event);
6363

64-
// Get nostr client and publish the event
65-
match get_nostr_client() {
66-
Ok(client) => match client.send_event(&event).await {
67-
Ok(_) => {
68-
tracing::info!(
69-
"Successfully published dispute event for dispute ID: {}",
70-
dispute.id
71-
);
72-
Ok(())
73-
}
74-
Err(e) => {
75-
tracing::error!("Failed to send dispute event: {}", e);
76-
Err(MostroInternalErr(ServiceError::NostrError(e.to_string())))
77-
}
78-
},
64+
// Get nostr client from context and publish the event
65+
let client = ctx.nostr_client();
66+
match client.send_event(&event).await {
67+
Ok(_) => {
68+
tracing::info!(
69+
"Successfully published dispute event for dispute ID: {}",
70+
dispute.id
71+
);
72+
Ok(())
73+
}
7974
Err(e) => {
80-
tracing::error!("Failed to get Nostr client: {}", e);
75+
tracing::error!("Failed to send dispute event: {}", e);
8176
Err(MostroInternalErr(ServiceError::NostrError(e.to_string())))
8277
}
8378
}
@@ -102,9 +97,9 @@ fn get_counterpart_info(sender: &str, buyer: &str, seller: &str) -> Result<bool,
10297
/// Checks that:
10398
/// - The order exists
10499
/// - The order status allows disputes (Active or FiatSent)
105-
async fn get_valid_order(pool: &Pool<Sqlite>, msg: &Message) -> Result<Order, MostroError> {
100+
async fn get_valid_order(ctx: &AppContext, msg: &Message) -> Result<Order, MostroError> {
106101
// Try to fetch the order from the database
107-
let order = get_order(msg, pool).await?;
102+
let order = get_order(msg, ctx.pool()).await?;
108103

109104
// Check if the order status is Active or FiatSent
110105
if order.check_status(Status::Active).is_err() && order.check_status(Status::FiatSent).is_err()
@@ -172,7 +167,7 @@ pub async fn dispute_action(
172167
return Err(MostroInternalErr(ServiceError::DisputeAlreadyExists));
173168
}
174169
// Get and validate order
175-
let mut order = get_valid_order(pool, &msg).await?;
170+
let mut order = get_valid_order(ctx, &msg).await?;
176171
// Get seller and buyer pubkeys
177172
let (seller, buyer) = match (&order.seller_pubkey, &order.buyer_pubkey) {
178173
(Some(seller), Some(buyer)) => (seller.to_owned(), buyer.to_owned()),
@@ -236,7 +231,7 @@ pub async fn dispute_action(
236231
.await?;
237232

238233
// Publish dispute event to network
239-
publish_dispute_event(&dispute, my_keys, is_buyer_dispute)
234+
publish_dispute_event(ctx, &dispute, my_keys, is_buyer_dispute)
240235
.await
241236
.map_err(|_| MostroInternalErr(ServiceError::DisputeEventError))?;
242237

@@ -256,12 +251,13 @@ pub async fn dispute_action(
256251
/// * `my_keys` - Mostro's keys for signing the dispute event
257252
/// * `context` - Description of the resolution context for logging (e.g., "cooperative cancel")
258253
pub async fn close_dispute_after_user_resolution(
259-
pool: &Pool<Sqlite>,
254+
ctx: &AppContext,
260255
order: &Order,
261256
new_status: DisputeStatus,
262257
my_keys: &Keys,
263258
context: &str,
264259
) {
260+
let pool = ctx.pool();
265261
if let Ok(mut dispute) = find_dispute_by_order_id(pool, order.id).await {
266262
let dispute_id = dispute.id;
267263
dispute.status = new_status.to_string();
@@ -310,7 +306,7 @@ pub async fn close_dispute_after_user_resolution(
310306
),
311307
Tag::custom(
312308
TagKind::Custom(Cow::Borrowed("y")),
313-
create_platform_tag_values(Settings::get_mostro().name.as_deref()),
309+
create_platform_tag_values(ctx.settings().mostro.name.as_deref()),
314310
),
315311
Tag::custom(
316312
TagKind::Custom(Cow::Borrowed("z")),
@@ -319,17 +315,13 @@ pub async fn close_dispute_after_user_resolution(
319315
]);
320316

321317
match new_dispute_event(my_keys, "", dispute_id.to_string(), tags) {
322-
Ok(event) => match get_nostr_client() {
323-
Ok(client) => {
324-
tracing::info!("Publishing dispute close event: {:#?}", event);
325-
if let Err(e) = client.send_event(&event).await {
326-
tracing::error!("Failed to publish dispute close event: {}", e);
327-
}
318+
Ok(event) => {
319+
let client = ctx.nostr_client();
320+
tracing::info!("Publishing dispute close event: {:#?}", event);
321+
if let Err(e) = client.send_event(&event).await {
322+
tracing::error!("Failed to publish dispute close event: {}", e);
328323
}
329-
Err(e) => {
330-
tracing::error!("Failed to get Nostr client for dispute event: {}", e);
331-
}
332-
},
324+
}
333325
Err(e) => {
334326
tracing::error!(
335327
"Failed to create dispute close event for dispute {}: {}",

src/app/order.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
use crate::app::context::AppContext;
2-
use crate::config::settings::Settings;
32
use crate::db::update_user_trade_index;
43
use crate::util::{get_bitcoin_price, publish_order, validate_invoice};
54
use mostro_core::prelude::*;
@@ -8,11 +7,12 @@ use nostr_sdk::prelude::*;
87
use nostr_sdk::Keys;
98

109
async fn calculate_and_check_quote(
10+
ctx: &AppContext,
1111
order: &SmallOrder,
1212
fiat_amount: &i64,
1313
) -> Result<(), MostroError> {
1414
// Get mostro settings
15-
let mostro_settings = Settings::get_mostro();
15+
let mostro_settings = &ctx.settings().mostro;
1616
// Calculate quote
1717
let quote = match order.amount {
1818
0 => match get_bitcoin_price(&order.fiat_code) {
@@ -92,7 +92,7 @@ pub async fn order_action(
9292
let _invoice = validate_invoice(&msg, &Order::from(order.clone())).await?;
9393

9494
// Check if fiat currency is accepted
95-
let mostro_settings = Settings::get_mostro();
95+
let mostro_settings = &ctx.settings().mostro;
9696
if let Err(cause) = order.check_fiat_currency(&mostro_settings.fiat_currencies_accepted) {
9797
return Err(MostroCantDo(cause));
9898
}
@@ -112,7 +112,7 @@ pub async fn order_action(
112112

113113
// Check quote in sats for each amount
114114
for fiat_amount in amount_vec.iter() {
115-
calculate_and_check_quote(order, fiat_amount).await?;
115+
calculate_and_check_quote(ctx, order, fiat_amount).await?;
116116
}
117117

118118
let trade_index = match msg.get_inner_message_kind().trade_index {

src/app/orders.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
use crate::app::context::AppContext;
2-
use crate::config::settings::Settings;
32
use crate::util::{enqueue_order_msg, get_user_orders_by_id};
43
use mostro_core::prelude::*;
54
use nostr_sdk::prelude::*;
@@ -24,7 +23,7 @@ pub async fn orders_action(
2423
return Err(MostroCantDo(CantDoReason::InvalidParameters));
2524
}
2625

27-
let mostro_settings = Settings::get_mostro();
26+
let mostro_settings = &ctx.settings().mostro;
2827
if ids.len() > mostro_settings.max_orders_per_response as usize {
2928
return Err(MostroCantDo(CantDoReason::TooManyRequests));
3029
}

0 commit comments

Comments
 (0)