adding bootstrap and core info in wallet at creation time

pull/19/head
Niko PLP 1 year ago
parent b71d5a18ba
commit be04dd0c97
  1. 1
      Cargo.lock
  2. 1
      ng-app/src/api.ts
  3. 907
      ng-app/src/routes/WalletCreate.svelte
  4. 19
      ng-sdk-js/src/lib.rs
  5. 35
      ng-wallet/src/lib.rs
  6. 24
      ng-wallet/src/types.rs
  7. 1
      ngaccount/Cargo.toml
  8. 60
      ngaccount/src/main.rs
  9. 1
      ngaccount/web/src/routes/Create.svelte
  10. 10
      ngcli/src/main.rs
  11. 12
      p2p-net/src/actors/add_invitation.rs
  12. 66
      p2p-net/src/types.rs
  13. 10
      p2p-net/src/utils.rs

1
Cargo.lock generated

@ -2838,6 +2838,7 @@ dependencies = [
"anyhow", "anyhow",
"base64-url", "base64-url",
"bytes", "bytes",
"duration-str",
"env_logger", "env_logger",
"log", "log",
"ng-wallet", "ng-wallet",

@ -115,6 +115,7 @@ export const NG_EU_BSP_REGISTER = "https://account.nextgraph.eu/#/create";
export const NG_EU_BSP_REGISTERED = "https://nextgraph.eu/#/user/registered"; export const NG_EU_BSP_REGISTERED = "https://nextgraph.eu/#/user/registered";
export const APP_ACCOUNT_REGISTERED_SUFFIX = "/#/user/registered"; export const APP_ACCOUNT_REGISTERED_SUFFIX = "/#/user/registered";
export const APP_WALLET_CREATE_SUFFIX = "/#/wallet/create";
export const NG_NET_BSP = "https://nextgraph.net"; export const NG_NET_BSP = "https://nextgraph.net";
export const NG_NET_BSP_REGISTER = "https://account.nextgraph.net/#/create"; export const NG_NET_BSP_REGISTER = "https://account.nextgraph.net/#/create";

File diff suppressed because it is too large Load Diff

@ -76,6 +76,17 @@ pub async fn get_local_url(location: String) -> JsValue {
} }
} }
#[cfg(target_arch = "wasm32")]
#[wasm_bindgen]
pub async fn get_ngone_url_of_invitation(invitation_string: String) -> JsValue {
let res = decode_invitation_string(invitation_string);
if res.is_some() {
serde_wasm_bindgen::to_value(&res.unwrap().get_urls()[0]).unwrap()
} else {
JsValue::FALSE
}
}
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
#[wasm_bindgen] #[wasm_bindgen]
pub async fn get_local_bootstrap_with_public(location: String, invite: JsValue) -> JsValue { pub async fn get_local_bootstrap_with_public(location: String, invite: JsValue) -> JsValue {
@ -308,11 +319,16 @@ pub async fn wallet_create_wallet(js_params: JsValue) -> Result<JsValue, JsValue
let mut params = serde_wasm_bindgen::from_value::<CreateWalletV0>(js_params) let mut params = serde_wasm_bindgen::from_value::<CreateWalletV0>(js_params)
.map_err(|_| "Deserialization error of args")?; .map_err(|_| "Deserialization error of args")?;
params.result_with_wallet_file = true; params.result_with_wallet_file = true;
let local_save = params.local_save;
let res = create_wallet_v0(params).await; let res = create_wallet_v0(params).await;
match res { match res {
Ok(r) => { Ok(r) => {
if local_save {
let session = save_wallet_locally(&r)?; let session = save_wallet_locally(&r)?;
Ok(serde_wasm_bindgen::to_value(&(r, session)).unwrap()) Ok(serde_wasm_bindgen::to_value(&(r, session)).unwrap())
} else {
Ok(serde_wasm_bindgen::to_value(&(r, false)).unwrap())
}
} }
Err(e) => Err(e.to_string().into()), Err(e) => Err(e.to_string().into()),
} }
@ -329,6 +345,9 @@ pub fn test_create_wallet() -> JsValue {
9, 9,
false, false,
false, false,
BootstrapContentV0::new(),
None,
None,
); );
serde_wasm_bindgen::to_value(&r).unwrap() serde_wasm_bindgen::to_value(&r).unwrap()
} }

@ -341,10 +341,8 @@ pub fn gen_shuffle_for_pin() -> Vec<u8> {
/// creates a Wallet from a pin, a security text and image (with option to send the bootstrap and wallet to nextgraph.one) /// creates a Wallet from a pin, a security text and image (with option to send the bootstrap and wallet to nextgraph.one)
/// and returns the Wallet, the pazzle and the mnemonic /// and returns the Wallet, the pazzle and the mnemonic
pub async fn create_wallet_v0( pub async fn create_wallet_v0(
params: CreateWalletV0, mut params: CreateWalletV0,
) -> Result<CreateWalletResultV0, NgWalletError> { ) -> Result<CreateWalletResultV0, NgWalletError> {
// TODO : use some automatically zeroed variable for the 2 first arguments, and for the returned values
let creating_pazzle = Instant::now(); let creating_pazzle = Instant::now();
// pazzle_length can only be 9, 12, or 15 // pazzle_length can only be 9, 12, or 15
@ -481,7 +479,33 @@ pub async fn create_wallet_v0(
//Creating a new peerId for this Client and User //Creating a new peerId for this Client and User
let peer = generate_keypair(); let peer = generate_keypair();
let wallet_log = WalletLog::new_v0(create_op); let mut wallet_log = WalletLog::new_v0(create_op);
// adding some more operations in the log
// pub core_bootstrap: BootstrapContentV0,
// #[zeroize(skip)]
// pub core_registration: Option<[u8; 32]>,
// #[zeroize(skip)]
// pub additional_bootstrap: Option<BootstrapContentV0>,
wallet_log.add(WalletOperation::AddSiteCoreV0((
user,
params
.core_bootstrap
.get_first_peer_id()
.ok_or(NgWalletError::InvalidBootstrap)?,
params.core_registration,
)));
if let Some(additional) = &params.additional_bootstrap {
params.core_bootstrap.merge(additional);
}
for server in &params.core_bootstrap.servers {
wallet_log.add(WalletOperation::AddBrokerServerV0(server.clone()));
wallet_log.add(WalletOperation::AddSiteBootstrapV0((user, server.peer_id)));
}
let mut master_key = [0u8; 32]; let mut master_key = [0u8; 32];
getrandom::getrandom(&mut master_key).map_err(|e| NgWalletError::InternalError)?; getrandom::getrandom(&mut master_key).map_err(|e| NgWalletError::InternalError)?;
@ -632,6 +656,9 @@ mod test {
9, 9,
false, false,
false, false,
BootstrapContentV0::new(),
None,
None,
)) ))
.await .await
.expect("create_wallet_v0"); .expect("create_wallet_v0");

@ -226,6 +226,11 @@ impl WalletLog {
pub fn new_v0(create_op: WalletOpCreateV0) -> Self { pub fn new_v0(create_op: WalletOpCreateV0) -> Self {
WalletLog::V0(WalletLogV0::new(create_op)) WalletLog::V0(WalletLogV0::new(create_op))
} }
pub fn add(&mut self, op: WalletOperation) {
match self {
Self::V0(v0) => v0.add(op),
}
}
} }
impl WalletLogV0 { impl WalletLogV0 {
@ -289,10 +294,10 @@ impl WalletLogV0 {
} }
} }
WalletOperation::RemoveOverlayCoreOverrideV0(_) => {} WalletOperation::RemoveOverlayCoreOverrideV0(_) => {}
WalletOperation::AddSiteCoreV0((site, core)) => { WalletOperation::AddSiteCoreV0((site, core, registration)) => {
if self.is_first_and_not_deleted_afterwards(op, "RemoveSiteCoreV0") { if self.is_first_and_not_deleted_afterwards(op, "RemoveSiteCoreV0") {
let _ = wallet.sites.get_mut(&site).and_then(|site| { let _ = wallet.sites.get_mut(&site).and_then(|site| {
site.cores.push(*core); site.cores.push((*core, *registration));
None::<SiteV0> None::<SiteV0>
}); });
} }
@ -460,7 +465,7 @@ pub enum WalletOperation {
SetClientV0(ClientV0), SetClientV0(ClientV0),
AddOverlayCoreOverrideV0((OverlayId, Vec<PubKey>)), AddOverlayCoreOverrideV0((OverlayId, Vec<PubKey>)),
RemoveOverlayCoreOverrideV0(OverlayId), RemoveOverlayCoreOverrideV0(OverlayId),
AddSiteCoreV0((PubKey, PubKey)), AddSiteCoreV0((PubKey, PubKey, Option<[u8; 32]>)),
RemoveSiteCoreV0((PubKey, PubKey)), RemoveSiteCoreV0((PubKey, PubKey)),
AddSiteBootstrapV0((PubKey, PubKey)), AddSiteBootstrapV0((PubKey, PubKey)),
RemoveSiteBootstrapV0((PubKey, PubKey)), RemoveSiteBootstrapV0((PubKey, PubKey)),
@ -763,6 +768,12 @@ pub struct CreateWalletV0 {
pub result_with_wallet_file: bool, pub result_with_wallet_file: bool,
#[zeroize(skip)] #[zeroize(skip)]
pub local_save: bool, pub local_save: bool,
#[zeroize(skip)]
pub core_bootstrap: BootstrapContentV0,
#[zeroize(skip)]
pub core_registration: Option<[u8; 32]>,
#[zeroize(skip)]
pub additional_bootstrap: Option<BootstrapContentV0>,
} }
impl CreateWalletV0 { impl CreateWalletV0 {
@ -773,6 +784,9 @@ impl CreateWalletV0 {
pazzle_length: u8, pazzle_length: u8,
send_bootstrap: bool, send_bootstrap: bool,
send_wallet: bool, send_wallet: bool,
core_bootstrap: BootstrapContentV0,
core_registration: Option<[u8; 32]>,
additional_bootstrap: Option<BootstrapContentV0>,
) -> Self { ) -> Self {
CreateWalletV0 { CreateWalletV0 {
result_with_wallet_file: false, result_with_wallet_file: false,
@ -783,6 +797,9 @@ impl CreateWalletV0 {
pazzle_length, pazzle_length,
send_bootstrap, send_bootstrap,
send_wallet, send_wallet,
core_bootstrap,
core_registration,
additional_bootstrap,
} }
} }
} }
@ -822,6 +839,7 @@ pub enum NgWalletError {
DecryptionError, DecryptionError,
InvalidSignature, InvalidSignature,
NoCreateWalletPresent, NoCreateWalletPresent,
InvalidBootstrap,
} }
impl fmt::Display for NgWalletError { impl fmt::Display for NgWalletError {

@ -27,3 +27,4 @@ base64-url = "2.0.0"
serde_json = "1.0.96" serde_json = "1.0.96"
bytes = "1.0" bytes = "1.0"
anyhow = "1.0.71" anyhow = "1.0.71"
duration-str = "0.5.1"

@ -11,8 +11,9 @@ extern crate anyhow;
mod types; mod types;
use duration_str::parse;
use p2p_client_ws::remote_ws::ConnectionWebSocket; use p2p_client_ws::remote_ws::ConnectionWebSocket;
use p2p_net::actors::add_user::*; use p2p_net::actors::add_invitation::*;
use p2p_net::broker::BROKER; use p2p_net::broker::BROKER;
use p2p_repo::store::StorageError; use p2p_repo::store::StorageError;
use warp::reply::Response; use warp::reply::Response;
@ -30,12 +31,12 @@ use std::{env, fs};
use crate::types::*; use crate::types::*;
use ng_wallet::types::*; use ng_wallet::types::*;
use p2p_net::types::{ use p2p_net::types::{
BindAddress, CreateAccountBSP, Invitation, InvitationV0, APP_ACCOUNT_REGISTERED_SUFFIX, AdminResponseContentV0, BindAddress, CreateAccountBSP, Invitation, InvitationCode,
APP_NG_ONE_URL, NG_ONE_URL, InvitationV0, APP_ACCOUNT_REGISTERED_SUFFIX, APP_NG_ONE_URL, NG_ONE_URL,
}; };
use p2p_repo::log::*; use p2p_repo::log::*;
use p2p_repo::types::*; use p2p_repo::types::*;
use p2p_repo::utils::{generate_keypair, sign, verify}; use p2p_repo::utils::{generate_keypair, sign, timestamp_after, verify};
#[derive(RustEmbed)] #[derive(RustEmbed)]
#[folder = "web/dist"] #[folder = "web/dist"]
@ -60,6 +61,11 @@ impl Server {
// if needed, proceed with payment and verify it here. once validated, add the user // if needed, proceed with payment and verify it here. once validated, add the user
let duration = parse("5m").unwrap();
let expiry = timestamp_after(duration);
let symkey = SymKey::random();
let invite_code = InvitationCode::Unique(symkey.clone());
let local_peer_pubk = self.local_peer_key.to_pub(); let local_peer_pubk = self.local_peer_key.to_pub();
let res = BROKER let res = BROKER
.write() .write()
@ -75,43 +81,45 @@ impl Server {
port: self.port, port: self.port,
ip: (&self.ip).into(), ip: (&self.ip).into(),
}, },
AddUser::V0(AddUserV0 { AddInvitation::V0(AddInvitationV0 {
user: cabsp.user(), invite_code,
is_admin: false, expiry,
memo: None,
tos_url: false,
}), }),
) )
.await; .await;
let mut redirect_url = cabsp let redirect_url = cabsp
.redirect_url() .redirect_url()
.clone() .clone()
.unwrap_or(format!("{}{}", self.domain, APP_ACCOUNT_REGISTERED_SUFFIX)); .unwrap_or(format!("{}{}", self.domain, APP_ACCOUNT_REGISTERED_SUFFIX));
match &res { match res {
Err(e) => { Err(e) => {
log_err!("error while registering: {e} {:?}", cabsp); log_err!("error while registering: {e} {:?}", cabsp);
Err(Some(format!("{}?e={}", redirect_url, e))) Err(Some(format!("{}?re={}", redirect_url, e)))
}
Ok(_) => {
log_info!("User added successfully {}", cabsp.user());
let mut return_invitation = if cabsp.invitation().is_some() {
InvitationV0 {
bootstrap: cabsp.invitation().as_ref().unwrap().bootstrap.clone(),
name: cabsp.invitation().as_ref().unwrap().name.clone(),
code: None,
url: None,
} }
} else { Ok(AdminResponseContentV0::Invitation(Invitation::V0(mut invitation))) => {
InvitationV0::empty(Some(self.domain.clone())) log_info!("invitation created successfully {:?}", invitation);
}; invitation.name = Some(self.domain.clone());
return_invitation.append_bootstraps(cabsp.additional_bootstrap());
Ok(format!( Ok(format!(
"{}?i={}&u={}", "{}?i={}&rs={}",
redirect_url, redirect_url,
Invitation::V0(return_invitation), Invitation::V0(invitation),
cabsp.user() self.domain
)) ))
} }
_ => {
log_err!(
"error while registering: invalid response from add_invitation {:?}",
cabsp
);
Err(Some(format!(
"{}?re={}",
redirect_url, "add_invitation_failed"
)))
}
} }
} }

@ -45,6 +45,7 @@
error = "We are redirecting you..."; error = "We are redirecting you...";
go_back = false; go_back = false;
} else { } else {
//console.log(result);
success(result); success(result);
} }
} catch (e) { } catch (e) {

@ -95,7 +95,7 @@ async fn main() -> Result<(), ProtocolError> {
.arg(arg!( .arg(arg!(
-v --verbose ... "Increase the logging output. once : info, twice : debug, 3 times : trace" -v --verbose ... "Increase the logging output. once : info, twice : debug, 3 times : trace"
)) ))
.arg(arg!(-b --base [PATH] "Base path for client home folder containing all persistent files, config, and key") .arg(arg!(-b --base <PATH> "Base path for client home folder containing all persistent files, config, and key")
.required(false) .required(false)
.value_parser(value_parser!(PathBuf)) .value_parser(value_parser!(PathBuf))
.default_value(".ng")) .default_value(".ng"))
@ -137,12 +137,12 @@ async fn main() -> Result<(), ProtocolError> {
.subcommand( .subcommand(
Command::new("add-user") Command::new("add-user")
.about("add a user to the server, so it can connect to it") .about("add a user to the server, so it can connect to it")
.arg(arg!([USER_ID] "userId of the user to add. should be a base64-url encoded serde serialization of its pubkey [u8; 32]").required(true)) .arg(arg!(<USER_ID> "userId of the user to add. should be a base64-url encoded serde serialization of its pubkey [u8; 32]").required(true))
.arg(arg!(-a --admin "make this user admin as well").required(false))) .arg(arg!(-a --admin "make this user admin as well").required(false)))
.subcommand( .subcommand(
Command::new("del-user") Command::new("del-user")
.about("removes a user from the broker") .about("removes a user from the broker")
.arg(arg!([USER_ID] "userId of the user to remove. should be a base64-url encoded serde serialization of its pubkey [u8; 32]").required(true))) .arg(arg!(<USER_ID> "userId of the user to remove. should be a base64-url encoded serde serialization of its pubkey [u8; 32]").required(true)))
.subcommand( .subcommand(
Command::new("list-users") Command::new("list-users")
.about("list all users registered in the broker") .about("list all users registered in the broker")
@ -156,7 +156,8 @@ async fn main() -> Result<(), ProtocolError> {
.arg(arg!(-u --unique "this invitation can be used only once. this is the default").required(false).conflicts_with("admin")) .arg(arg!(-u --unique "this invitation can be used only once. this is the default").required(false).conflicts_with("admin"))
.arg(arg!(-f --forever "this invitation does not expire. it can be used forever (or until deleted by an admin). default if no EXPIRES provided").required(false)) .arg(arg!(-f --forever "this invitation does not expire. it can be used forever (or until deleted by an admin). default if no EXPIRES provided").required(false))
.arg(arg!(-n --name <NAME> "optional name of this broker that will be displayed to the user when registering: You have been invited to register an account at [NAME]").required(false)) .arg(arg!(-n --name <NAME> "optional name of this broker that will be displayed to the user when registering: You have been invited to register an account at [NAME]").required(false))
.arg(arg!(-m --memo <MEMO> "optional memo about this invitation that will be kept in the server. it will help you to remember who you invited and to manage the invitation").required(false))) .arg(arg!(-m --memo <MEMO> "optional memo about this invitation that will be kept in the server. it will help you to remember who you invited and to manage the invitation").required(false))
.arg(arg!(--notos "the TOS have already been accepted by the user. No need to redirect to a page for TOS acceptance.").required(false)))
.subcommand( .subcommand(
Command::new("list-invitations") Command::new("list-invitations")
.about("list all invitations") .about("list all invitations")
@ -521,6 +522,7 @@ async fn main() -> Result<(), ProtocolError> {
invite_code, invite_code,
expiry, expiry,
memo: sub2_matches.get_one::<String>("memo").map(|s| s.clone()), memo: sub2_matches.get_one::<String>("memo").map(|s| s.clone()),
tos_url: !sub2_matches.get_flag("notos"),
}), }),
) )
.await; .await;

@ -27,6 +27,7 @@ pub struct AddInvitationV0 {
pub invite_code: InvitationCode, pub invite_code: InvitationCode,
pub expiry: u32, pub expiry: u32,
pub memo: Option<String>, pub memo: Option<String>,
pub tos_url: bool,
} }
/// Add invitation /// Add invitation
@ -51,6 +52,11 @@ impl AddInvitation {
AddInvitation::V0(o) => &o.memo, AddInvitation::V0(o) => &o.memo,
} }
} }
pub fn tos_url(&self) -> bool {
match self {
AddInvitation::V0(o) => o.tos_url,
}
}
pub fn get_actor(&self) -> Box<dyn EActor> { pub fn get_actor(&self) -> Box<dyn EActor> {
Actor::<AddInvitation, AdminResponse>::new_responder() Actor::<AddInvitation, AdminResponse>::new_responder()
} }
@ -103,7 +109,11 @@ impl EActor for Actor<'_, AddInvitation, AdminResponse> {
broker.get_bootstrap()?.clone(), broker.get_bootstrap()?.clone(),
Some(req.code().get_symkey()), Some(req.code().get_symkey()),
None, None,
broker.get_registration_url().map(|s| s.clone()), if req.tos_url() {
broker.get_registration_url().map(|s| s.clone())
} else {
None
},
)); ));
let response: AdminResponseV0 = invitation.into(); let response: AdminResponseV0 = invitation.into();
fsm.lock().await.send(response.into()).await?; fsm.lock().await.send(response.into()).await?;

@ -145,7 +145,7 @@ pub struct SiteV0 {
// Identity::OrgPrivate or Identity::IndividualPrivate // Identity::OrgPrivate or Identity::IndividualPrivate
pub private: SiteStore, pub private: SiteStore,
pub cores: Vec<PubKey>, pub cores: Vec<(PubKey, Option<[u8; 32]>)>,
pub bootstraps: Vec<PubKey>, pub bootstraps: Vec<PubKey>,
} }
@ -529,6 +529,25 @@ pub struct BootstrapContentV0 {
pub servers: Vec<BrokerServerV0>, pub servers: Vec<BrokerServerV0>,
} }
impl BootstrapContentV0 {
pub fn new() -> Self {
BootstrapContentV0 { servers: vec![] }
}
pub fn merge(&mut self, with: &BootstrapContentV0) {
'outer: for server2 in &with.servers {
for server1 in &self.servers {
if *server1 == *server2 {
continue 'outer;
}
}
self.servers.push(server2.clone());
}
}
pub fn get_first_peer_id(&self) -> Option<PubKey> {
self.servers.first().map(|s| s.peer_id)
}
}
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub enum BootstrapContent { pub enum BootstrapContent {
V0(BootstrapContentV0), V0(BootstrapContentV0),
@ -679,6 +698,14 @@ impl Invitation {
} }
} }
pub fn set_url(&mut self, url: Option<&String>) {
if url.is_some() {
match self {
Invitation::V0(v0) => v0.url = Some(url.unwrap().clone()),
}
}
}
/// first URL in the list is the ngone one /// first URL in the list is the ngone one
pub fn get_urls(&self) -> Vec<String> { pub fn get_urls(&self) -> Vec<String> {
match self { match self {
@ -773,37 +800,36 @@ impl CreateAccountBSP {
} }
Some(base64_url::encode(&payload_ser.unwrap())) Some(base64_url::encode(&payload_ser.unwrap()))
} }
pub fn user(&self) -> PubKey { // pub fn user(&self) -> PubKey {
match self { // match self {
Self::V0(v0) => v0.user, // Self::V0(v0) => v0.user,
} // }
} // }
pub fn redirect_url(&self) -> &Option<String> { pub fn redirect_url(&self) -> &Option<String> {
match self { match self {
Self::V0(v0) => &v0.redirect_url, Self::V0(v0) => &v0.redirect_url,
} }
} }
pub fn invitation(&self) -> &Option<InvitationV0> { // pub fn invitation(&self) -> &Option<InvitationV0> {
match self { // match self {
Self::V0(v0) => &v0.invitation, // Self::V0(v0) => &v0.invitation,
} // }
} // }
pub fn additional_bootstrap(&mut self) -> &mut Option<BootstrapContentV0> { // pub fn additional_bootstrap(&mut self) -> &mut Option<BootstrapContentV0> {
match self { // match self {
Self::V0(v0) => &mut v0.additional_bootstrap, // Self::V0(v0) => &mut v0.additional_bootstrap,
} // }
} // }
} }
/// Create an account at a Broker Service Provider (BSP). Version 0 /// Create an account at a Broker Service Provider (BSP). Version 0
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct CreateAccountBSPV0 { pub struct CreateAccountBSPV0 {
pub invitation: Option<InvitationV0>, //pub invitation: Option<InvitationV0>,
pub additional_bootstrap: Option<BootstrapContentV0>,
//pub additional_bootstrap: Option<BootstrapContentV0>,
/// the user asking to create an account /// the user asking to create an account
pub user: PubKey, //pub user: PubKey,
/// signature over serialized invitation code, with user key /// signature over serialized invitation code, with user key
// pub sig: Sig, // pub sig: Sig,

@ -9,6 +9,7 @@
* according to those terms. * according to those terms.
*/ */
use crate::broker::BROKER;
use crate::types::*; use crate::types::*;
use crate::NG_BOOTSTRAP_LOCAL_PATH; use crate::NG_BOOTSTRAP_LOCAL_PATH;
use async_std::task; use async_std::task;
@ -150,10 +151,13 @@ pub async fn retrieve_local_bootstrap(
let resp = reqwest::get(format!("{}{}", APP_PREFIX, NG_BOOTSTRAP_LOCAL_PATH)).await; let resp = reqwest::get(format!("{}{}", APP_PREFIX, NG_BOOTSTRAP_LOCAL_PATH)).await;
if resp.is_ok() { if resp.is_ok() {
let resp = resp.unwrap().json::<BootstrapContent>().await; let resp = resp.unwrap().json::<BootstrapContent>().await;
resp.ok().map(|v| v.into()) if resp.is_ok() {
} else { let mut inv: Invitation = resp.unwrap().into();
None inv.set_url(BROKER.read().await.get_registration_url());
return Some(inv);
} }
}
None
}; };
let res = if invite1.is_none() { let res = if invite1.is_none() {

Loading…
Cancel
Save