diff --git a/Cargo.lock b/Cargo.lock index c6f32d7..1e2699d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1940,6 +1940,7 @@ dependencies = [ "nostr", "serde", "serde_json", + "url", "wasm-bindgen", "wasm-bindgen-futures", "wasm-logger", diff --git a/mint-manager/Cargo.toml b/mint-manager/Cargo.toml index add9982..eb29131 100644 --- a/mint-manager/Cargo.toml +++ b/mint-manager/Cargo.toml @@ -24,3 +24,4 @@ nostr = { workspace = true } gloo-storage = "0.2.2" anyhow = "1.0.71" jwt-compact = { workspace = true } +url = "2.4.0" diff --git a/mint-manager/src/app.rs b/mint-manager/src/app.rs index 7d2f7f4..e3d53be 100644 --- a/mint-manager/src/app.rs +++ b/mint-manager/src/app.rs @@ -1,18 +1,34 @@ +use std::str::FromStr; + use anyhow::Result; use cashu_crab::Amount; use gloo::storage::LocalStorage; use gloo_net::http::Request; use gloo_storage::Storage; +use log::debug; use node_manager_types::responses::BalanceResponse; +use url::Url; use yew::platform::spawn_local; use yew::prelude::*; +use crate::components::manager_url::SetManagerUrl; + use crate::components::{ balance::Balance, cashu::Cashu, channels::Channels, ln::Ln, login::Login, on_chain::OnChain, }; -async fn get_balances(jwt: &str, fetech_callback: Callback) -> Result<()> { - let fetched_balance: BalanceResponse = Request::get("http://127.0.0.1:8086/balance") +pub const NODE_URL_KEY: &str = "node_url"; + +pub const JWT_KEY: &str = "auth_token"; + +async fn get_balances( + url: &Url, + jwt: &str, + fetech_callback: Callback, +) -> Result<()> { + let url = url.join("/balance")?; + + let fetched_balance: BalanceResponse = Request::get(url.as_str()) .header("Authorization", &format!("Bearer {}", jwt)) .send() .await? @@ -23,8 +39,9 @@ async fn get_balances(jwt: &str, fetech_callback: Callback) -> Ok(()) } -async fn get_cashu(jwt: &str, fetech_callback: Callback) -> Result<()> { - let fetched_balance: Amount = Request::get("http://127.0.0.1:8086/outstanding") +async fn get_cashu(url: &Url, jwt: &str, fetech_callback: Callback) -> Result<()> { + let url = url.join("/outstanding")?; + let fetched_balance: Amount = Request::get(url.as_str()) .header("Authorization", &format!("Bearer {}", jwt)) .send() .await? @@ -35,8 +52,9 @@ async fn get_cashu(jwt: &str, fetech_callback: Callback) -> Result<()> { Ok(()) } -async fn check_login(jwt: &str, callback: Callback) -> Result<()> { - let response = Request::post("http://127.0.0.1:8086/auth") +async fn check_login(url: &Url, jwt: &str, callback: Callback) -> Result<()> { + let url = url.join("/auth")?; + let response = Request::post(url.as_str()) .header("Authorization", &format!("Bearer {}", jwt)) .send() .await?; @@ -50,16 +68,27 @@ async fn check_login(jwt: &str, callback: Callback) -> Result<()> { Ok(()) } +#[derive(Debug, Default)] +pub enum View { + #[default] + Dashboard, + SetUrl, + Login, +} + pub enum Msg { LoggedIn(String), FetechedBalances(BalanceResponse), FetechedCashu(Amount), CheckedAuth(bool), + UrlSet(Url), } -#[derive(Debug, Clone, Default)] +#[derive(Debug, Default)] pub struct App { + view: View, jwt: Option, + node_url: Option, on_chain_confimed: Amount, on_chain_pending: Amount, ln: Amount, @@ -71,26 +100,50 @@ impl Component for App { type Properties = (); fn create(ctx: &Context) -> Self { - let jwt = if let Ok(jwt) = LocalStorage::get::("auth_token") { - let balance_callback = ctx.link().callback(Msg::FetechedBalances); - let cashu_callback = ctx.link().callback(Msg::FetechedCashu); - let jwt_clone = jwt.clone(); - let check_auth = ctx.link().callback(Msg::CheckedAuth); - spawn_local(async move { - if check_login(&jwt_clone, check_auth).await.is_ok() { - get_balances(&jwt_clone, balance_callback).await.ok(); - get_cashu(&jwt_clone, cashu_callback).await.ok(); - } - }); + let node_url: Option = LocalStorage::get::(NODE_URL_KEY) + .ok() + .map(|u| Url::from_str(&u).ok()) + .flatten(); + let jwt = LocalStorage::get::(JWT_KEY); + + debug!("node: {:?}", node_url); + + debug!("Jwt: {:?}", jwt); + + match (node_url, jwt) { + (Some(url), Ok(jwt)) => { + let balance_callback = ctx.link().callback(Msg::FetechedBalances); + let cashu_callback = ctx.link().callback(Msg::FetechedCashu); + let jwt_clone = jwt.clone(); + let check_auth = ctx.link().callback(Msg::CheckedAuth); + let node_url = url.clone(); - Some(jwt) - } else { - None - }; + spawn_local(async move { + if check_login(&node_url, &jwt_clone, check_auth).await.is_ok() { + get_balances(&node_url, &jwt_clone, balance_callback) + .await + .ok(); + get_cashu(&node_url, &jwt_clone, cashu_callback).await.ok(); + } + }); - Self { - jwt, - ..Default::default() + Self { + jwt: Some(jwt), + node_url: Some(url), + ..Default::default() + } + } + // Mint Url is not set + (None, _) => Self { + view: View::SetUrl, + ..Default::default() + }, + // Mint url is set but user not logged in + (Some(url), Err(_)) => Self { + node_url: Some(url), + view: View::Login, + ..Default::default() + }, } } @@ -99,9 +152,10 @@ impl Component for App { Msg::LoggedIn(jwt) => { let jwt_clone = jwt.clone(); let balance_callback = ctx.link().callback(Msg::FetechedBalances); + let url = self.node_url.clone().unwrap(); spawn_local(async move { - get_balances(&jwt_clone, balance_callback).await.ok(); + get_balances(&url, &jwt_clone, balance_callback).await.ok(); }); self.jwt = Some(jwt); @@ -123,45 +177,78 @@ impl Component for App { } Msg::CheckedAuth(status) => { if !status { - LocalStorage::delete("auth_token"); + LocalStorage::delete(JWT_KEY); true } else { false } } + Msg::UrlSet(url) => { + self.node_url = Some(url); + self.view = View::Login; + true + } } } fn view(&self, ctx: &Context) -> Html { - let logged_in_callback = ctx.link().callback(Msg::LoggedIn); - + log::debug!("{:?}", self.view); html! { -
- if self.jwt.is_some() { -
-
- -
-
- -
-
-
-
- -
-
- -
-
- -
-
} - else { - - } -
+
+ + { + + match &self.view { + View::Dashboard => { + html!{ + <> +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+ + + } + + } + View::Login => { + let logged_in_callback = ctx.link().callback(Msg::LoggedIn); + html! { + <> + + + + } + } + View::SetUrl => { + + let url_set_cb = ctx.link().callback(Msg::UrlSet); + html!{ + + <> + + + } + } + } + } - } +
+ } } } diff --git a/mint-manager/src/components/channel.rs b/mint-manager/src/components/channel.rs index b107acb..3175ad7 100644 --- a/mint-manager/src/components/channel.rs +++ b/mint-manager/src/components/channel.rs @@ -5,25 +5,26 @@ use gloo_net::http::Request; use node_manager_types::requests; use node_manager_types::ChannelStatus; use serde_json::Value; +use url::Url; use yew::platform::spawn_local; use yew::prelude::*; async fn post_close_channel( + url: &Url, jwt: &str, close_channel_request: requests::CloseChannel, channel_close_cb: Callback, ) -> Result<()> { - let _fetched_channels: Value = Request::post("http://127.0.0.1:8086/close") + let url = url.join("close")?; + + let _: Value = Request::post(url.as_str()) .header("Authorization", &format!("Bearer {}", jwt)) .json(&close_channel_request) .unwrap() .send() - .await - .unwrap() + .await? .json() - .await - .unwrap(); - log::debug!("{:?}", _fetched_channels); + .await?; channel_close_cb.emit("OK".to_string()); Ok(()) @@ -31,6 +32,7 @@ async fn post_close_channel( #[derive(PartialEq, Properties, Clone)] pub struct Props { + pub url: Url, pub jwt: String, // pub onedit: Callback<(usize, String)>, pub channel_id: String, @@ -59,6 +61,7 @@ impl Component for Channel { fn update(&mut self, ctx: &Context, msg: Self::Message) -> bool { match msg { Msg::Delete => { + let url = ctx.props().url.clone(); let jwt = ctx.props().jwt.clone(); let channel_close = requests::CloseChannel { channel_id: ctx.props().channel_id.clone(), @@ -68,7 +71,7 @@ impl Component for Channel { let callback = ctx.link().callback(|_| Msg::ChannelClosed); spawn_local(async move { - let _ = post_close_channel(&jwt, channel_close, callback).await; + let _ = post_close_channel(&url, &jwt, channel_close, callback).await; }); true @@ -79,6 +82,7 @@ impl Component for Channel { fn view(&self, ctx: &Context) -> Html { let Props { + url: _, jwt: _, channel_id: _, peer_id, diff --git a/mint-manager/src/components/channels.rs b/mint-manager/src/components/channels.rs index 2f302b6..5ecf66e 100644 --- a/mint-manager/src/components/channels.rs +++ b/mint-manager/src/components/channels.rs @@ -1,6 +1,7 @@ use anyhow::Result; use gloo_net::http::Request; use node_manager_types::responses::{self, ChannelInfo}; +use url::Url; use yew::platform::spawn_local; use yew::prelude::*; @@ -9,8 +10,9 @@ use crate::components::connect_peer::ConnectPeer; use super::channel::Channel; use super::open_channel::OpenChannel; -#[derive(Properties, PartialEq, Default, Clone)] +#[derive(Properties, PartialEq, Clone)] pub struct Props { + pub url: Url, pub jwt: String, } @@ -46,9 +48,10 @@ impl Component for Channels { let peers_callback = ctx.link().callback(Msg::FetechedPeers); let jwt = ctx.props().jwt.clone(); + let url = ctx.props().url.clone(); spawn_local(async move { - get_channels(&jwt, channels_callback).await.ok(); - get_peers(&jwt, peers_callback).await.ok(); + get_channels(&jwt, &url, channels_callback).await.ok(); + get_peers(&jwt, &url, peers_callback).await.ok(); }); Self::default() @@ -98,7 +101,7 @@ impl Component for Channels { let remote_balance = channel.value - channel.balance; html!{ - + }}).collect::() } + + + } + } +} diff --git a/mint-manager/src/components/mod.rs b/mint-manager/src/components/mod.rs index 30f37e8..cc96582 100644 --- a/mint-manager/src/components/mod.rs +++ b/mint-manager/src/components/mod.rs @@ -5,5 +5,6 @@ pub mod channels; pub mod connect_peer; pub mod ln; pub mod login; +pub mod manager_url; pub mod on_chain; pub mod open_channel; diff --git a/mint-manager/src/components/on_chain.rs b/mint-manager/src/components/on_chain.rs index 287b8e1..e64f306 100644 --- a/mint-manager/src/components/on_chain.rs +++ b/mint-manager/src/components/on_chain.rs @@ -2,12 +2,14 @@ use anyhow::Result; use cashu_crab::Amount; use gloo_net::http::Request; use node_manager_types::{requests::PayOnChainRequest, responses::FundingAddressResponse}; +use url::Url; use web_sys::HtmlInputElement; use yew::platform::spawn_local; use yew::prelude::*; -async fn get_new_addr(jwt: &str, new_addr_callback: Callback) -> Result<()> { - let fetched_channels: FundingAddressResponse = Request::get("http://127.0.0.1:8086/fund") +async fn get_new_addr(jwt: &str, url: &Url, new_addr_callback: Callback) -> Result<()> { + let url = url.join("fund")?; + let fetched_channels: FundingAddressResponse = Request::get(url.as_str()) .header("Authorization", &format!("Bearer {}", jwt)) .send() .await? @@ -21,10 +23,12 @@ async fn get_new_addr(jwt: &str, new_addr_callback: Callback) -> Result< async fn pay_on_chain( jwt: &str, + url: &Url, pay_request: PayOnChainRequest, pay_on_chain_callback: Callback, ) -> Result<()> { - let response: String = Request::post(&format!("http://127.0.0.1:8086/pay-on-chain")) + let url = url.join("pay-on-chain")?; + let response: String = Request::post(url.as_str()) .header("Authorization", &format!("Bearer {}", jwt)) .json(&pay_request)? .send() @@ -37,9 +41,10 @@ async fn pay_on_chain( Ok(()) } -#[derive(Properties, PartialEq, Default, Clone)] +#[derive(Properties, PartialEq, Clone)] pub struct Props { pub jwt: String, + pub url: Url, } pub enum Msg { @@ -80,8 +85,10 @@ impl Component for OnChain { Msg::FetechNewAddr => { let callback = ctx.link().callback(Msg::NewAddr); let jwt = ctx.props().jwt.clone(); + + let url = ctx.props().url.clone(); spawn_local(async move { - get_new_addr(&jwt, callback).await.ok(); + get_new_addr(&jwt, &url, callback).await.ok(); }); false } @@ -100,6 +107,7 @@ impl Component for OnChain { Msg::Pay => { let callback = ctx.link().callback(Msg::Paid); let jwt = ctx.props().jwt.clone(); + let url = ctx.props().url.clone(); let mut amount_value = None; let mut address = None; @@ -124,7 +132,7 @@ impl Component for OnChain { }; spawn_local(async move { - pay_on_chain(&jwt, pay_request, callback).await.ok(); + pay_on_chain(&jwt, &url, pay_request, callback).await.ok(); }); } diff --git a/mint-manager/src/components/open_channel.rs b/mint-manager/src/components/open_channel.rs index e78e128..1c26f50 100644 --- a/mint-manager/src/components/open_channel.rs +++ b/mint-manager/src/components/open_channel.rs @@ -7,16 +7,19 @@ use gloo_net::http::Request; use log::warn; use node_manager_types::requests::OpenChannelRequest; use node_manager_types::responses::{self, ChannelInfo}; +use url::Url; use web_sys::{HtmlInputElement, HtmlSelectElement}; use yew::platform::spawn_local; use yew::prelude::*; async fn post_open_channel( jwt: &str, + url: &Url, open_channel_request: OpenChannelRequest, open_channel_callback: Callback, ) -> Result<()> { - let _fetched_channels: ChannelInfo = Request::post("http://127.0.0.1:8086/open-channel") + let url = url.join("open-channel")?; + let _fetched_channels: ChannelInfo = Request::post(url.as_str()) .header("Authorization", &format!("Bearer {}", jwt)) .json(&open_channel_request)? .send() @@ -29,8 +32,13 @@ async fn post_open_channel( Ok(()) } -async fn get_peers(jwt: &str, peers_callback: Callback>) -> Result<()> { - let peers: Vec = Request::get("http://127.0.0.1:8086/peers") +async fn get_peers( + jwt: &str, + url: &Url, + peers_callback: Callback>, +) -> Result<()> { + let url = url.join("peers")?; + let peers: Vec = Request::get(url.as_str()) .header("Authorization", &format!("Bearer {}", jwt)) .send() .await? @@ -45,6 +53,7 @@ async fn get_peers(jwt: &str, peers_callback: Callback> #[derive(PartialEq, Properties)] pub struct Props { pub jwt: String, + pub url: Url, pub peers: Vec, pub back_callback: Callback, } @@ -72,8 +81,9 @@ impl Component for OpenChannel { fn create(ctx: &Context) -> Self { let callback = ctx.link().callback(Msg::FetechedPeers); let jwt = ctx.props().jwt.clone(); + let url = ctx.props().url.clone(); spawn_local(async move { - get_peers(&jwt, callback).await.unwrap(); + get_peers(&jwt, &url, callback).await.unwrap(); }); Self::default() } @@ -139,9 +149,12 @@ impl Component for OpenChannel { let callback = ctx.link().callback(Msg::ChannelOpened); let jwt = ctx.props().jwt.clone(); + let url = ctx.props().url.clone(); spawn_local(async move { - post_open_channel(&jwt, open_channel, callback).await.ok(); + post_open_channel(&jwt, &url, open_channel, callback) + .await + .ok(); }); } else { warn!("Sommethitng is missing");