add ngcli get :j:k and fix bug in stores in tauri

master
Niko PLP 3 months ago
parent 48c63ead2f
commit 856d713a73
  1. 12
      nextgraph/src/local_broker.rs
  2. 2
      ng-app/src/lib/Document.svelte
  3. 2
      ng-app/src/lib/Login.svelte
  4. 4
      ng-app/src/lib/panes/History.svelte
  5. 15
      ng-app/src/lib/popups/Signature.svelte
  6. 10
      ng-app/src/locales/en.json
  7. 5
      ng-app/src/routes/WalletLogin.svelte
  8. 29
      ng-net/src/actors/client/pin_repo.rs
  9. 2
      ng-net/src/app_protocol.rs
  10. 23
      ng-repo/src/branch.rs
  11. 2
      ng-repo/src/file.rs
  12. 26
      ng-repo/src/types.rs
  13. 102
      ng-verifier/src/verifier.rs
  14. 6
      ng-wallet/src/lib.rs
  15. 2
      ngaccount/web/tailwind.config.cjs
  16. 180
      ngcli/src/main.rs

@ -1313,9 +1313,13 @@ impl LocalBroker {
// key
// );
let site = opened_wallet.wallet.site(&user_id)?;
let core = site.cores[0]; //TODO: cycle the other cores if failure to connect (failover)
let brokers = opened_wallet.wallet.broker(core.0)?;
let locator = if let Ok(site) = opened_wallet.wallet.site(&user_id) {
let core = site.cores[0]; //TODO: cycle the other cores if failure to connect (failover)
let brokers = opened_wallet.wallet.broker(core.0)?;
BrokerInfoV0::vec_into_locator(brokers)
} else {
Locator::empty()
};
key_material.zeroize();
let mut verifier = Verifier::new(
@ -1330,7 +1334,7 @@ impl LocalBroker {
private_store_id: credentials.2,
protected_store_id: credentials.3,
public_store_id: credentials.4,
locator: BrokerInfoV0::vec_into_locator(brokers),
locator,
},
block_storage,
)?;

@ -152,7 +152,7 @@
</div>
</div>
{:then app}
<div class:max-w-screen-lg={center} class="flex flex-col" style="overflow-wrap: anywhere;" class:w-[1024px]={center} >
<div class:max-w-screen-lg={center} class="flex flex-col break-all" style="overflow-wrap: anywhere;" class:w-[1024px]={center} >
<svelte:component this={app} commits={$commits}/>
</div>
{/await}

@ -258,7 +258,7 @@
}
} catch (e) {
console.error(e);
if (e.message.includes("constructor") || (typeof e === "string" && e.includes("constructor") )) e = "BrowserTooOld";
if (e.message && e.message.includes("constructor") || (typeof e === "string" && e.includes("constructor") )) e = "BrowserTooOld";
error = e;
step = "end";
dispatch("error", { error: e });

@ -107,7 +107,7 @@
}
});
get(branch).history.start();
},1);
},100);
});
onDestroy( ()=>{
@ -140,7 +140,7 @@
</script>
<div style="width:120px; min-width:120px;font-family: monospace; font: Courier; font-size:16px;">
{#each history as commit}
<div class="w-full commit relative text-gray-500" style="height:60px;" role="button" title={commit[0]} tabindex=0 on:click={()=>openCommit(commit[0])} on:keypress={()=>openCommit(commit[0])}>

@ -91,9 +91,8 @@
</script>
<div class="flex flex-col">
<span class="font-bold text-xl">Signature</span>
Current heads :
<span class="font-bold text-xl">{$t("doc.signature.title")}</span>
{$t("current_heads")} :
{#each heads as head}
{#if head[1]}
<div style="font-family: monospace; font: Courier; font-size:16px;" class="flex text-green-600 clickable my-2"
@ -119,7 +118,7 @@
<ShieldCheck tabindex="-1" class="mr-2 focus:outline-none" />
{$t("doc.sign_snapshot")}
</Button>
<span class="mb-2">or click on one of the signed heads to get its link.</span>
<span class="mb-2">{$t("doc.signature.or_click_on_head")}</span>
{:else if can_sign}
<button
@ -136,15 +135,15 @@
bind:checked={ snapshot }
><span class="text-gray-700 text-base">{$t("doc.take_snapshot")}</span>
</Toggle>
{#if has_signatures}<span>or click on one of the signed heads to get its link</span>{/if}
{#if has_signatures}<span>{$t("doc.signature.or_click_on_head")}</span>{/if}
{:else}
<div class="flex mt-3"><Camera tabindex="-1" class="w-6 h-6 mr-3 text-green-600"/><span class="text-green-600">A signed snapshot is currently at the head.</span></div>
<span>Here is its link that you can share.<br/>For now this link is only usable with the CLI, by running the following command :<br/><br/></span>
<div class="flex mt-3"><Camera tabindex="-1" class="w-6 h-6 mr-3 text-green-600"/><span class="text-green-600">{$t("doc.signature.signed_snap_at_head")}</span></div>
<span>{$t("doc.signature.here_is_the_link")}<br/>{$t("doc.signature.cli_warning")} :<br/><br/></span>
<span style="font-family: monospace; font: Courier; font-size:16px;" class="break-all">ngcli get {signed_commit_link(heads[0])}</span>
{/if}
{/if}
{#if (force_snapshot || can_sign) && cur_link }
<span class="mt-3">For now the link is only usable with the CLI, by running the following command :<br/><br/></span>
<span class="mt-3">{$t("doc.signature.cli_warning")} :<br/><br/></span>
<span style="font-family: monospace; font: Courier; font-size:16px;" class="break-all">ngcli get {cur_link}</span>
{/if}
</div>

@ -74,6 +74,14 @@
"chat": "Chat"
}
},
"signature": {
"title": "Signature",
"current_heads": "Current heads",
"or_click_on_head": "or click on one of the signed heads to get its link.",
"here_is_the_link": "Here is its link that you can share.",
"cli_warning": "For now the link is only usable with the CLI, by running the following command",
"signed_snap_at_head": "A signed snapshot is currently at the head."
},
"file": {
"download": "Download",
"upload_progress": "Uploading...",
@ -475,7 +483,7 @@
"download_wallet_done": "Your wallet file has been downloaded into your \"Downloads\" folder, with the name<br /><span class=\"text-black\"> {download_name}</span ><br /> <span class=\"font-bold\" >Please move it to a safe and durable place.</span ><br />",
"download_pdf_description": "Please download your Recovery PDF, print it, and delete it.",
"download_pdf": "Download my PDF",
"download_pdf_done": "Your Recovery PDF file has been downloaded into your \"Downloads\" folder, with the name<br /><span class=\"text-black\"> {pdf_name}</span ><br /> <span class=\"font-bold\" >Please print it and then delete it.</span ><br />",
"download_pdf_done": "Your Recovery PDF file has been downloaded into your \"Downloads\" folder,<br /> with the name <span class=\"text-black\"> {pdf_name}</span ><br /><br /> <span class=\"font-bold\" >Please print it and then delete it.</span ><br /><br />",
"your_pazzle": "Here below is your Pazzle. <br /> The <span class=\"font-bold\">order</span> of each image is <span class=\"font-bold\">important</span>!",
"your_mnemonic": "And here is your mnemonic (your alternative passphrase):",
"unlock_tips_1": "You can use both the pazzle or the mnemonic to unlock your wallet. The pazzle is easier to remember. The mnemonic is useful in some special cases. We recommend that you use the pazzle. <span class=\"font-bold text-xl\" >Copy both on a piece of paper.</span > You should try to memorize the pazzle. Once you did, you won't need the paper anymore.",

@ -73,6 +73,8 @@
});
active_wallet_unsub = active_wallet.subscribe(async (value) => {
if (value && value.wallet) {
step = "loggedin";
await tick();
if (!$active_session) {
try {
let session = await ng.session_start(
@ -85,6 +87,7 @@
loggedin();
}
} catch (e) {
step = "open";
error = e;
importing = false;
wallet = undefined;
@ -105,7 +108,7 @@
});
function loggedin() {
async function loggedin() {
step = "loggedin";
if ($redirect_after_login) {
let redir=$redirect_after_login;

@ -27,6 +27,35 @@ impl PinRepo {
pub fn get_actor(&self, id: i64) -> Box<dyn EActor> {
Actor::<PinRepo, RepoOpened>::new_responder(id)
}
pub fn for_branch(repo: &Repo, branch: &BranchId, broker_id: &DirectPeerId) -> PinRepo {
let overlay = OverlayAccess::new_write_access_from_store(&repo.store);
let mut rw_topics = Vec::with_capacity(1);
let mut ro_topics = vec![];
let branch = repo.branches.get(branch).unwrap();
if let Some(privkey) = &branch.topic_priv_key {
rw_topics.push(PublisherAdvert::new(
branch.topic.unwrap(),
privkey.clone(),
*broker_id,
));
} else {
ro_topics.push(branch.topic.unwrap());
}
PinRepo::V0(PinRepoV0 {
hash: repo.id.into(),
overlay,
// TODO: overlay_root_topic
overlay_root_topic: None,
expose_outer: false,
peers: vec![],
max_peer_count: 0,
//allowed_peers: vec![],
ro_topics,
rw_topics,
})
}
pub fn from_repo(repo: &Repo, broker_id: &DirectPeerId) -> PinRepo {
let overlay = OverlayAccess::new_write_access_from_store(&repo.store);
let mut rw_topics = Vec::with_capacity(repo.branches.len());

@ -41,7 +41,7 @@ lazy_static! {
Regex::new(r"^did:ng:o:([A-Za-z0-9-_]*):v:([A-Za-z0-9-_]*):a:([A-Za-z0-9-_%]*)$").unwrap(); //TODO: allow international chars. disallow digit as first char
#[doc(hidden)]
static ref RE_OBJECTS: Regex =
Regex::new(r"^did:ng(?::o:([A-Za-z0-9-_]{44}))?:v:([A-Za-z0-9-_]{44})((?::c:[A-Za-z0-9-_]{44}:k:[A-Za-z0-9-_]{44})+)(?::s:([A-Za-z0-9-_]{44}):k:([A-Za-z0-9-_]{44}))?:l:([A-Za-z0-9-_]*)$").unwrap();
Regex::new(r"^did:ng(?::o:([A-Za-z0-9-_]{44}))?:v:([A-Za-z0-9-_]{44})((?::[cj]:[A-Za-z0-9-_]{44}:k:[A-Za-z0-9-_]{44})+)(?::s:([A-Za-z0-9-_]{44}):k:([A-Za-z0-9-_]{44}))?:l:([A-Za-z0-9-_]*)$").unwrap();
#[doc(hidden)]
static ref RE_OBJECT_READ_CAPS: Regex =
Regex::new(r":[cj]:([A-Za-z0-9-_]{44}):k:([A-Za-z0-9-_]{44})").unwrap();

@ -196,14 +196,21 @@ impl Branch {
// check if this commit object is present in theirs or has already been visited in the current walk
// load deps, stop at the root(including it in visited) or if this is a commit object from known_heads
let found_in_filter = if let Some(filter) = theirs_filter {
let hash = id.get_hash();
filter.contains_hash(hash)
} else {
false
};
let mut found_in_theirs = theirs.contains(&id);
if !found_in_theirs {
found_in_theirs = if let Some(filter) = theirs_filter {
let hash = id.get_hash();
filter.contains_hash(hash)
} else {
false
};
}
if !found_in_filter && !theirs.contains(&id) {
if found_in_theirs {
if theirs_found.is_some() {
theirs_found.as_mut().unwrap().insert(id);
}
} else {
if let Some(past) = visited.get_mut(&id) {
// we update the future
if let Some(f) = future {
@ -238,8 +245,6 @@ impl Branch {
// }
// }
}
} else if theirs_found.is_some() {
theirs_found.as_mut().unwrap().insert(id);
}
}
Err(ObjectParseError::MissingBlocks(blocks)) => {

@ -1498,7 +1498,7 @@ mod test {
)
.expect("open");
// this only works because we chose a big block size (1MB) so the small JPG file files in one block.
// this only works because we chose a big block size (1MB) so the small JPG file fits in one block.
// if not, we would have to call read repeatedly and append the results into a buffer, in order to get the full file
let res = file.read(0, len).expect("read all");

@ -83,6 +83,32 @@ impl Digest {
}
hasher.finish()
}
pub fn print_all(all: &[Digest]) -> String {
all.iter()
.map(|d| d.to_string())
.collect::<Vec<String>>()
.join(" ")
}
pub fn print_iter(all: impl Iterator<Item = Digest>) -> String {
all.map(|d| d.to_string())
.collect::<Vec<String>>()
.join(" ")
}
pub fn print_iter_ref<'a>(all: impl Iterator<Item = &'a Digest>) -> String {
all.map(|d| d.to_string())
.collect::<Vec<String>>()
.join(" ")
}
pub fn print_all_ref(all: &[&Digest]) -> String {
all.into_iter()
.map(|d| d.to_string())
.collect::<Vec<String>>()
.join(" ")
}
}
impl fmt::Display for Digest {

@ -154,6 +154,11 @@ impl Verifier {
self.config.public_store_id.as_ref().unwrap()
}
pub fn update_locator(&mut self, locator: Locator) {
self.outer = NuriV0::locator(&locator);
self.config.locator = locator;
}
pub async fn close(&self) {
log_debug!("VERIFIER CLOSED {}", self.user_id());
BROKER
@ -301,7 +306,7 @@ impl Verifier {
branch_id: BranchId,
store_repo: StoreRepo,
) -> Result<(Receiver<AppResponse>, CancelFn), VerifierError> {
//log_info!("#### create_branch_subscription {}", branch);
//log_info!("#### create_branch_subscription {}", branch_id);
let (tx, rx) = mpsc::unbounded::<AppResponse>();
//log_info!("SUBSCRIBE");
if let Some(returned) = self.branch_subscriptions.insert(branch_id, tx.clone()) {
@ -1181,9 +1186,15 @@ impl Verifier {
let user = self.user_id().clone();
let broker = BROKER.read().await;
log_debug!("looping on branches {:?}", branches);
// log_debug!(
// "looping on branches {:?}",
// branches
// .iter()
// .map(|(_, b, _)| b.to_string())
// .collect::<Vec<String>>()
// );
for (repo, branch, publisher) in branches {
log_debug!("open_branch_ repo {} branch {}", repo, branch);
//log_debug!("open_branch_ repo {} branch {}", repo, branch);
let _e = self
.open_branch_(
&repo,
@ -1195,12 +1206,12 @@ impl Verifier {
false,
)
.await;
log_debug!(
"END OF open_branch_ repo {} branch {} with {:?}",
repo,
branch,
_e
);
// log_debug!(
// "END OF open_branch_ repo {} branch {} with {:?}",
// repo,
// branch,
// _e
// );
// discarding error.
}
Ok(())
@ -1296,7 +1307,11 @@ impl Verifier {
}
}
};
//log_info!("need_open {} need_sub {}", need_open, need_sub);
// log_info!(
// "OPEN BRANCH {branch} need_open {} need_sub {}",
// need_open,
// need_sub
// );
let remote = remote_broker.into();
@ -1316,7 +1331,9 @@ impl Verifier {
let (pin_req, topic_id) = {
let repo = self.repos.get(repo_id).ok_or(NgError::RepoNotFound)?;
let topic_id = repo.branch(branch).unwrap().topic.unwrap();
//TODO: only pin the requested branch.
// TODO only pinning the requested branch.
// let pin_req =
// PinRepo::for_branch(repo, branch, remote_broker.broker_peer_id());
let pin_req = PinRepo::from_repo(repo, remote_broker.broker_peer_id());
(pin_req, topic_id)
};
@ -1330,19 +1347,25 @@ impl Verifier {
//TODO: check that in the returned opened_repo, the branch we are interested in has effectively been subscribed as publisher by the broker.
for topic in opened {
if topic.topic_id() == &topic_id {
self.do_sync_req_if_needed(
broker,
user,
&remote,
branch,
repo_id,
topic.known_heads(),
topic.commits_nbr(),
)
.await?;
break;
}
let (_, branch_id) = self
.topics
.get(&(overlay, *topic.topic_id()))
.ok_or(NgError::TopicNotFound)?
.to_owned();
//if topic.topic_id() == &topic_id {
self.do_sync_req_if_needed(
broker,
user,
&remote,
&branch_id,
repo_id,
topic.known_heads(),
topic.commits_nbr(),
)
.await?;
//break;
//}
}
}
Ok(_) => return Err(NgError::InvalidResponse),
@ -1408,7 +1431,6 @@ impl Verifier {
Ok(SoS::Single(sub)) => {
let repo = self.repos.get_mut(&repo_id).ok_or(NgError::RepoNotFound)?;
Self::branch_was_opened(&self.topics, repo, &sub)?;
self.do_sync_req_if_needed(
broker,
user,
@ -1706,7 +1728,12 @@ impl Verifier {
remote_commits_nbr: u64,
) -> Result<(), NgError> {
let (store, msg, branch_secret) = {
//log_info!("do_sync_req_if_needed for branch {}", branch_id);
// log_info!(
// "do_sync_req_if_needed for branch {} {} {}",
// branch_id,
// remote_commits_nbr,
// Digest::print_all(remote_heads)
// );
if remote_commits_nbr == 0 || remote_heads.is_empty() {
log_debug!("branch is new on the broker. doing nothing");
return Ok(());
@ -1726,7 +1753,11 @@ impl Verifier {
&& theirs.difference(&ours_set).count() == 0
{
// no need to sync
log_info!("branch {} is up to date", branch_id);
log_debug!(
"branch {} is up to date at heads {}",
branch_id,
Digest::print_iter(ours)
);
return Ok(());
}
@ -1736,6 +1767,11 @@ impl Verifier {
let mut recursor: Vec<(ObjectId, Option<ObjectId>)> =
ours_set.iter().map(|h| (h.clone(), None)).collect();
// log_debug!(
// "SEARCHING FOR THEIR HEADS from OURS {}",
// Digest::print_iter(ours)
// );
let _ = Branch::load_causal_past(
&mut recursor,
&repo.store,
@ -1745,6 +1781,12 @@ impl Verifier {
&mut Some(&mut theirs_found),
&None,
);
// log_debug!(
// "FOUND THEIR HEADS {}",
// Digest::print_iter_ref(theirs_found.iter())
// );
// for our in ours_set.iter() {
// //log_info!("OUR HEADS {}", our);
// if let Ok(cobj) = Object::load(*our, None, &repo.store) {
@ -1756,6 +1798,7 @@ impl Verifier {
theirs.difference(&theirs_found).cloned().collect();
let known_commits = if theirs_not_found.is_empty() {
//log_debug!("local heads are newer than remote");
return Ok(());
} else {
if visited.is_empty() {
@ -1892,10 +1935,13 @@ impl Verifier {
)
.await?;
let repo = self.get_repo_mut(&repo_id, store.get_store_repo())?;
// for (b, _) in repo.branches.iter() {
// let _ = repo.opened_branches.insert(b.clone(), true);
// }
// adding the Store branch to the opened_branches
// TODO: only do it if the Store is 3P.
if let Some(store_branch_id) = store_branch {
let repo = self.get_repo_mut(&repo_id, store.get_store_repo())?;
let _ = repo.opened_branches.insert(store_branch_id, true);
}

@ -25,6 +25,7 @@ use aes_gcm_siv::{
use argon2::{Algorithm, Argon2, AssociatedData, ParamsBuilder, Version};
use chacha20poly1305::XChaCha20Poly1305;
use image::{imageops::FilterType, io::Reader as ImageReader, ImageOutputFormat};
use ng_net::types::Locator;
use rand::distributions::{Distribution, Uniform};
use rand::prelude::*;
use safe_transmute::transmute_to_bytes;
@ -650,8 +651,10 @@ pub async fn create_wallet_second_step_v0(
if let Some(additional) = &params.additional_bootstrap {
params.core_bootstrap.merge(additional);
}
let mut locator = Locator::empty();
for server in &params.core_bootstrap.servers {
locator.add(server.clone());
wallet_log.add(WalletOperation::AddBrokerServerV0(server.clone()));
wallet_log.add(WalletOperation::AddSiteBootstrapV0((user, server.peer_id)));
site.bootstraps.push(server.peer_id);
@ -666,6 +669,7 @@ pub async fn create_wallet_second_step_v0(
}
list.unwrap().push(broker);
}
verifier.update_locator(locator);
let mut master_key = [0u8; 32];
getrandom::getrandom(&mut master_key).map_err(|_e| NgWalletError::InternalError)?;

@ -17,7 +17,7 @@ const config = {
plugins: [
require('flowbite/plugin')
],
darkMode: 'class',
darkMode: 'selector',
};
module.exports = config;

@ -14,16 +14,19 @@ use std::collections::HashSet;
use std::error::Error;
use std::fs::{read_to_string, write};
use std::net::IpAddr;
use std::path::Path;
use std::path::PathBuf;
use std::str::FromStr;
use std::sync::Arc;
use clap::{arg, command, value_parser, Command};
use clap::{arg, command, value_parser, ArgMatches, Command};
use duration_str::parse;
use serde::{Deserialize, Serialize};
use serde_json::{from_str, to_string_pretty};
use zeroize::Zeroize;
use ng_repo::errors::*;
use ng_repo::file::{FileError, RandomAccessFile, ReadFile};
use ng_repo::log::*;
use ng_repo::object::Object;
use ng_repo::store::Store;
@ -76,6 +79,7 @@ pub enum NgcliError {
OtherConfigError(String),
OtherConfigErrorStr(&'static str),
CannotSaveConfig(String),
FileError(FileError),
}
impl Error for NgcliError {}
@ -97,6 +101,12 @@ impl From<NgError> for NgcliError {
}
}
impl From<FileError> for NgcliError {
fn from(err: FileError) -> NgcliError {
Self::FileError(err)
}
}
impl From<ProtocolError> for NgcliError {
fn from(err: ProtocolError) -> NgcliError {
Self::ProtocolError(err)
@ -154,6 +164,73 @@ async fn main() -> std::io::Result<()> {
}
Ok(())
}
fn get_random_access_file(
file_meta: RandomAccessFileMeta,
reference: ObjectRef,
mut filename: Option<String>,
sub_matches: &ArgMatches,
store: &Arc<Store>,
) -> Result<(), NgcliError> {
println!("File content_type {}", file_meta.content_type());
println!("File size {}", file_meta.total_size());
let file = RandomAccessFile::open(reference.id, reference.key, Arc::clone(&store))?;
let filename_opt = sub_matches.get_one::<String>("output");
let save = sub_matches.get_flag("save") || filename_opt.is_some();
if save {
if filename.is_none() && filename_opt.is_some() {
filename = Some(filename_opt.unwrap().clone());
}
if let Some(filename) = filename {
let total = file_meta.total_size() as usize;
let mut file_content = Vec::with_capacity(total);
let mut pos = 0;
loop {
let mut res = file.read(pos, 1024 * 1024 * 1024)?;
pos += res.len();
file_content.append(&mut res);
if pos >= total {
break;
}
}
let mut i: usize = 0;
let mut dest_filename;
loop {
dest_filename = if i == 0 {
filename.clone()
} else {
filename
.rsplit_once(".")
.map(|(l, r)| format!("{l} ({}).{r}", i.to_string()))
.or_else(|| Some(format!("{filename} ({})", i.to_string())))
.unwrap()
};
let path = Path::new(&dest_filename);
if path.exists() {
i = i + 1;
} else {
write(path, &file_content)?;
break;
}
}
println!(
"The file has been saved in the current directory with the filename: {}",
dest_filename
);
} else {
println!("The file doesn't have a name and you didn't set the argument -o or --output , so we cannot save it.");
}
} else {
println!(
"You didn't set the argument -s or --save or -o or --output so we are not downloading the file locally"
);
}
Ok(())
}
async fn main_inner() -> Result<(), NgcliError> {
let matches = command!()
.arg(arg!(
@ -237,6 +314,8 @@ async fn main_inner() -> Result<(), NgcliError> {
Command::new("get")
.about("fetches one or several commits, or a binary object, with an optional signature, from a broker, using the Ext Protocol, and connecting on the Outer Overlay. The request is anonymous and doesn't need any authentication")
.arg(arg!([NURI] "NextGraph URI of the commit(s) or object, containing the ReadCap in the form :c:k :j:k and optionally :s:k and the usual :o:v:l").required(true))
.arg(arg!(-s --save "Saves the binary file(s) of the commits that have the type AddFile").required(false))
.arg(arg!(-o --output <FILENAME> "Gives a filename for the binary file(s) to be saved locally. only used if no filename is present in metadata").required(false))
)
.get_matches();
@ -377,13 +456,41 @@ async fn main_inner() -> Result<(), NgcliError> {
let mut signature = None;
let arc_store = Arc::new(store);
for obj_ref in nuri.objects.into_iter() {
match Object::load(obj_ref.id, Some(obj_ref.key.clone()), &arc_store) {
Err(e) => println!("Error: {:?}", e),
Ok(o) => match o.content_v0() {
Err(e) => println!("Error: {:?}", e),
Ok(ObjectContentV0::Commit(c)) => {
next_round.push((c.body_ref().clone(), Some(obj_ref.id), c.files()));
}
Ok(ObjectContentV0::RandomAccessFileMeta(file_meta)) => {
if let Err(e) = get_random_access_file(
file_meta,
obj_ref,
None,
sub_matches,
&arc_store,
) {
println!("An error occurred: {:?}", e);
}
}
_ => println!("unsupported format"),
},
}
}
let store = Arc::try_unwrap(arc_store)
.map_err(|_| NgcliError::NgError(NgError::InternalError))?;
if let Some(sign_ref) = nuri.signature {
if let Ok(o) = Object::load(sign_ref.id, Some(sign_ref.key), &store) {
match o.content_v0() {
Ok(ObjectContentV0::Signature(Signature::V0(v0))) => {
let in_sig = HashSet::from_iter(v0.content.commits().iter().cloned());
if in_nuri.is_subset(&in_sig) {
next_round.push((v0.certificate_ref.clone(), None));
next_round.push((v0.certificate_ref.clone(), None, vec![]));
signature = Some(v0);
} else {
println!("Signature is invalid");
@ -394,20 +501,9 @@ async fn main_inner() -> Result<(), NgcliError> {
}
}
}
nuri.objects.into_iter().for_each(|obj_ref| {
match Object::load(obj_ref.id, Some(obj_ref.key), &store) {
Err(e) => println!("Error: {:?}", e),
Ok(o) => match o.content_v0() {
Err(e) => println!("Error: {:?}", e),
Ok(ObjectContentV0::Commit(c)) => {
next_round.push((c.body_ref().clone(), Some(obj_ref.id)));
}
_ => println!("unsupported format"),
},
}
});
if next_round.is_empty() {
return Ok(());
}
let blocks: Vec<Block> = Broker::ext(
Box::new(ConnectionWebSocket {}),
peer_privk.clone(),
@ -418,7 +514,7 @@ async fn main_inner() -> Result<(), NgcliError> {
overlay: overlay_id,
ids: next_round
.iter()
.map(|(o, _)| o.id)
.map(|(o, _, _)| o.id)
.collect::<Vec<ObjectId>>(),
include_files: true,
},
@ -428,7 +524,7 @@ async fn main_inner() -> Result<(), NgcliError> {
let mut third_round = Vec::with_capacity(2);
let mut certificate = None;
for (body_ref, commit_id) in next_round.into_iter() {
for (body_ref, commit_id, mut files) in next_round.into_iter() {
match Object::load(body_ref.id, Some(body_ref.key), &store) {
Err(e) => println!("Error: {:?}", e),
Ok(o) => match o.content_v0() {
@ -437,16 +533,34 @@ async fn main_inner() -> Result<(), NgcliError> {
CommitBodyV0::Snapshot(Snapshot::V0(snap)),
))) => {
println!("Snapshot: {}", commit_id.unwrap());
third_round.push(snap.content);
third_round.push((snap.content, None));
}
Ok(ObjectContentV0::CommitBody(CommitBody::V0(CommitBodyV0::AddFile(
AddFile::V0(add_file),
)))) => {
println!(
"File added: {}",
add_file.name.as_deref().unwrap_or("(no name)")
);
if files.len() != 1 {
println!("Error: invalid AddFile commit");
}
third_round.push((files.pop().unwrap(), add_file.name));
}
Ok(ObjectContentV0::CommitBody(CommitBody::V0(
CommitBodyV0::AsyncTransaction(_t),
CommitBodyV0::AsyncTransaction(t),
)))
| Ok(ObjectContentV0::CommitBody(CommitBody::V0(
CommitBodyV0::SyncTransaction(t),
))) => {
println!("Transaction: {}", commit_id.unwrap());
if t.body_type() == 0 || t.body_type() == 2 {
//TODO display ADDs and REMOVEs
}
}
Ok(ObjectContentV0::Certificate(Certificate::V0(certif))) => {
if commit_id.is_none() && signature.is_some() {
third_round.push(certif.content.previous.clone());
third_round.push((certif.content.previous.clone(), None));
certificate = Some(certif);
}
}
@ -454,7 +568,9 @@ async fn main_inner() -> Result<(), NgcliError> {
},
}
}
if third_round.is_empty() {
return Ok(());
}
let blocks: Vec<Block> = Broker::ext(
Box::new(ConnectionWebSocket {}),
peer_privk,
@ -463,15 +579,18 @@ async fn main_inner() -> Result<(), NgcliError> {
broker_server.get_ws_url(&None).await.unwrap().0, // for now we are only connecting to NextGraph SaaS cloud (nextgraph.eu) so it is safe.
ExtObjectGetV0 {
overlay: overlay_id,
ids: third_round.iter().map(|o| o.id).collect::<Vec<ObjectId>>(),
ids: third_round
.iter()
.map(|(o, _)| o.id)
.collect::<Vec<ObjectId>>(),
include_files: true,
},
)
.await?;
blocks.into_iter().for_each(|b| _ = store.put(&b));
for third_ref in third_round.into_iter() {
match Object::load(third_ref.id, Some(third_ref.key), &store) {
let store = Arc::new(store);
for (third_ref, filename) in third_round.into_iter() {
match Object::load(third_ref.id, Some(third_ref.key.clone()), &store) {
Err(e) => println!("Error: {:?}", e),
Ok(o) => match o.content_v0() {
Err(e) => println!("Error: {:?}", e),
@ -479,6 +598,15 @@ async fn main_inner() -> Result<(), NgcliError> {
Err(e) => println!("Error: {:?}", e),
Ok(s) => println!("Here is the snapshot data :\n\r{}", s),
},
Ok(ObjectContentV0::RandomAccessFileMeta(file_meta)) => {
get_random_access_file(
file_meta,
third_ref,
filename,
sub_matches,
&store,
)?;
}
Ok(ObjectContentV0::CommitBody(CommitBody::V0(
CommitBodyV0::Repository(repo),
))) => {

Loading…
Cancel
Save