parent
495340dabe
commit
17e1eb95e3
@ -0,0 +1,152 @@ |
||||
<!-- |
||||
// Copyright (c) 2022-2024 Niko Bonnieure, Par le Peuple, NextGraph.org developers |
||||
// All rights reserved. |
||||
// Licensed under the Apache License, Version 2.0 |
||||
// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0> |
||||
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>, |
||||
// at your option. All files in the project carrying such |
||||
// notice may not be copied, modified, or distributed except |
||||
// according to those terms. |
||||
--> |
||||
|
||||
<script lang="ts"> |
||||
import { |
||||
branch_subscribe, |
||||
active_session, |
||||
toast_error, |
||||
toast_success, |
||||
display_error, |
||||
online, |
||||
} from "../../store"; |
||||
import { |
||||
cur_tab, |
||||
show_doc_popup |
||||
} from "../../tab"; |
||||
import { get } from "svelte/store"; |
||||
import { onMount, onDestroy, tick } from "svelte"; |
||||
import ng from "../../api"; |
||||
import { t } from "svelte-i18n"; |
||||
import { |
||||
ShieldExclamation, |
||||
ShieldCheck, |
||||
Camera |
||||
} from "svelte-heros-v2"; |
||||
import { |
||||
Toggle, |
||||
Button |
||||
} from "flowbite-svelte"; |
||||
let is_tauri = import.meta.env.TAURI_PLATFORM; |
||||
|
||||
let heads = []; |
||||
onMount(async ()=>{ |
||||
heads = await ng.signature_status($active_session.session_id, "did:ng:"+$cur_tab.branch.nuri+":"+$cur_tab.store.overlay); |
||||
}); |
||||
let snapshot = false; |
||||
let force_snapshot = false; |
||||
let can_sign = false; |
||||
let has_signatures = false; |
||||
let hide_snapshot = false; |
||||
$: force_snapshot = heads.every(h => h[1]) && heads.length && !heads[0][2]; |
||||
$: can_sign = force_snapshot || !heads[0]?.[2] ; |
||||
$: has_signatures = heads.some(h => h[1]); |
||||
let cur_link; |
||||
|
||||
function signed_commit_link(head) { |
||||
return `did:ng:${$cur_tab.branch.nuri}:${$cur_tab.store.overlay}:${head[1]}:${$cur_tab.store.has_outer}` |
||||
} |
||||
|
||||
async function sign() { |
||||
if (snapshot) await sign_snapshot(); |
||||
else { |
||||
try { |
||||
let immediate = await ng.signature_request($active_session.session_id, "did:ng:"+$cur_tab.branch.nuri+":"+$cur_tab.store.overlay); |
||||
if (immediate) { |
||||
heads = await ng.signature_status($active_session.session_id, "did:ng:"+$cur_tab.branch.nuri+":"+$cur_tab.store.overlay); |
||||
cur_link=signed_commit_link(heads[0]); |
||||
hide_snapshot = true; |
||||
toast_success($t("doc.signature_is_ready")); |
||||
} else { |
||||
$show_doc_popup = false; |
||||
toast_success($t("doc.signature_is_on_its_way")); |
||||
} |
||||
} catch (e) { |
||||
toast_error(display_error(e)); |
||||
} |
||||
} |
||||
} |
||||
async function sign_snapshot() { |
||||
try { |
||||
let immediate = await ng.signed_snapshot_request($active_session.session_id, "did:ng:"+$cur_tab.branch.nuri+":"+$cur_tab.store.overlay); |
||||
if (immediate) { |
||||
heads = await ng.signature_status($active_session.session_id, "did:ng:"+$cur_tab.branch.nuri+":"+$cur_tab.store.overlay); |
||||
} else { |
||||
$show_doc_popup = false; |
||||
toast_success($t("doc.signed_snapshot_is_on_its_way")); |
||||
} |
||||
} catch (e) { |
||||
toast_error(display_error(e)); |
||||
} |
||||
} |
||||
|
||||
</script> |
||||
|
||||
<div class="flex flex-col"> |
||||
<span class="font-bold text-xl">Signature</span> |
||||
|
||||
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" |
||||
on:click={()=>cur_link=signed_commit_link(head)} on:keypress={()=>cur_link=signed_commit_link(head)} tabindex="0" role="button"> |
||||
<ShieldCheck tabindex="-1" class="w-5 h-5 mr-2" /> |
||||
{head[0].substring(0,7)} |
||||
</div> |
||||
{:else} |
||||
<div style="font-family: monospace; font: Courier; font-size:16px;" class="flex my-2"> |
||||
<ShieldExclamation tabindex="-1" class="w-5 h-5 mr-2" /> |
||||
{head[0].substring(0,7)} |
||||
</div> |
||||
{/if} |
||||
{/each} |
||||
{#if !hide_snapshot} |
||||
{#if force_snapshot} |
||||
<Button |
||||
disabled={!$online && !is_tauri} |
||||
on:click|once={sign_snapshot} |
||||
on:keypress|once={sign_snapshot} |
||||
class="select-none mt-2 mb-2 text-white bg-primary-700 hover:bg-primary-700/90 focus:ring-4 focus:ring-primary-500/50 rounded-lg text-base p-2 text-center inline-flex items-center dark:focus:ring-primary-700/55" |
||||
> |
||||
<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> |
||||
|
||||
{:else if can_sign} |
||||
<button |
||||
on:click|once={sign} |
||||
on:keypress|once={sign} |
||||
class="shrink select-none mt-2 mb-3 text-white bg-primary-700 hover:bg-primary-700/90 focus:ring-4 focus:ring-primary-500/50 rounded-lg text-base p-2 text-center inline-flex items-center dark:focus:ring-primary-700/55" |
||||
> |
||||
<ShieldCheck tabindex="-1" class="mr-2 focus:outline-none" /> |
||||
{$t("doc.sign_heads")} |
||||
</button> |
||||
<Toggle |
||||
disabled={!$online && !is_tauri} |
||||
class="clickable mb-3" |
||||
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} |
||||
{: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> |
||||
<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 style="font-family: monospace; font: Courier; font-size:16px;" class="break-all">ngcli get {cur_link}</span> |
||||
{/if} |
||||
</div> |
||||
|
||||
|
@ -0,0 +1,121 @@ |
||||
// Copyright (c) 2022-2024 Niko Bonnieure, Par le Peuple, NextGraph.org developers
|
||||
// All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0
|
||||
// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0>
|
||||
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
|
||||
// at your option. All files in the project carrying such
|
||||
// notice may not be copied, modified, or distributed except
|
||||
// according to those terms.
|
||||
|
||||
use crate::verifier::Verifier; |
||||
use ng_net::app_protocol::NuriTargetV0; |
||||
use ng_oxigraph::oxigraph::sparql::{Query, QueryResults}; |
||||
use ng_repo::errors::{StorageError, VerifierError}; |
||||
use ng_repo::types::*; |
||||
use serde_json::json; |
||||
use yrs::types::ToJson; |
||||
use yrs::updates::decoder::Decode; |
||||
use yrs::{GetString, Transact}; |
||||
|
||||
impl Verifier { |
||||
pub(crate) fn take_snapshot( |
||||
&self, |
||||
crdt: &BranchCrdt, |
||||
branch_id: &BranchId, |
||||
target: &NuriTargetV0, |
||||
) -> Result<String, VerifierError> { |
||||
let state = match self |
||||
.user_storage |
||||
.as_ref() |
||||
.unwrap() |
||||
.branch_get_discrete_state(branch_id) |
||||
{ |
||||
Ok(s) => Ok(s), |
||||
Err(StorageError::NoDiscreteState) => Ok(vec![]), |
||||
Err(e) => Err(e), |
||||
}?; |
||||
|
||||
let discrete = if state.is_empty() { |
||||
serde_json::Value::Null |
||||
} else { |
||||
match crdt { |
||||
BranchCrdt::Automerge(_) => { |
||||
let doc = automerge::Automerge::load(&state) |
||||
.map_err(|e| VerifierError::AutomergeError(e.to_string()))?; |
||||
|
||||
serde_json::json!(automerge::AutoSerde::from(&doc)) |
||||
} |
||||
BranchCrdt::YText(_) => { |
||||
let doc = yrs::Doc::new(); |
||||
let text = doc.get_or_insert_text("ng"); |
||||
let mut txn = doc.transact_mut(); |
||||
let update = yrs::Update::decode_v1(&state) |
||||
.map_err(|e| VerifierError::YrsError(e.to_string()))?; |
||||
txn.apply_update(update); |
||||
serde_json::Value::from(text.get_string(&txn)) |
||||
} |
||||
BranchCrdt::YArray(_) => { |
||||
let doc = yrs::Doc::new(); |
||||
let array = doc.get_or_insert_array("ng"); |
||||
let mut txn = doc.transact_mut(); |
||||
let update = yrs::Update::decode_v1(&state) |
||||
.map_err(|e| VerifierError::YrsError(e.to_string()))?; |
||||
txn.apply_update(update); |
||||
let mut json = String::new(); |
||||
array.to_json(&txn).to_json(&mut json); |
||||
|
||||
serde_json::from_str(&json).map_err(|_| VerifierError::InvalidJson)? |
||||
} |
||||
BranchCrdt::YMap(_) => { |
||||
let doc = yrs::Doc::new(); |
||||
let map = doc.get_or_insert_map("ng"); |
||||
let mut txn = doc.transact_mut(); |
||||
let update = yrs::Update::decode_v1(&state) |
||||
.map_err(|e| VerifierError::YrsError(e.to_string()))?; |
||||
txn.apply_update(update); |
||||
let mut json = String::new(); |
||||
map.to_json(&txn).to_json(&mut json); |
||||
serde_json::from_str(&json).map_err(|_| VerifierError::InvalidJson)? |
||||
} |
||||
BranchCrdt::YXml(_) => { |
||||
// TODO: if it is markdown, output the markdown instead of XML
|
||||
let doc = yrs::Doc::new(); |
||||
let xml = doc.get_or_insert_xml_fragment("prosemirror"); |
||||
let mut txn = doc.transact_mut(); |
||||
let update = yrs::Update::decode_v1(&state) |
||||
.map_err(|e| VerifierError::YrsError(e.to_string()))?; |
||||
txn.apply_update(update); |
||||
serde_json::json!({"xml":xml.get_string(&txn)}) |
||||
} |
||||
_ => return Err(VerifierError::InvalidBranch), |
||||
} |
||||
}; |
||||
|
||||
let store = self.graph_dataset.as_ref().unwrap(); |
||||
let parsed = Query::parse("CONSTRUCT { ?s ?p ?o } WHERE { ?s ?p ?o }", None).unwrap(); |
||||
let results = store |
||||
.query(parsed, self.resolve_target_for_sparql(target, true)?) |
||||
.map_err(|e| VerifierError::OxigraphError(e.to_string()))?; |
||||
let results = if let QueryResults::Graph(quads) = results { |
||||
let mut results = Vec::with_capacity(quads.size_hint().0); |
||||
for quad in quads { |
||||
match quad { |
||||
Err(e) => return Err(VerifierError::OxigraphError(e.to_string())), |
||||
Ok(triple) => results.push(triple.to_string()), |
||||
} |
||||
} |
||||
results |
||||
} else { |
||||
return Err(VerifierError::OxigraphError( |
||||
"Invalid Oxigraph query result".to_string(), |
||||
)); |
||||
}; |
||||
|
||||
let res = json!({ |
||||
"discrete": discrete, |
||||
"graph": results, |
||||
}); |
||||
|
||||
Ok(serde_json::to_string(&res).unwrap()) |
||||
} |
||||
} |
Loading…
Reference in new issue