forked from NextGraph/nextgraph-rs
parent
0022643342
commit
ef93afe4cb
@ -1,5 +1,5 @@ |
|||||||
NG_ACCOUNT_DOMAIN= |
export NG_ACCOUNT_DOMAIN=test.com |
||||||
NG_ACCOUNT_ADMIN= |
export NG_ACCOUNT_ADMIN=vC1HC5YZQppsHUkerBe2oXoNjAlleuvkpXpdUIWnRO8A |
||||||
NG_ACCOUNT_LOCAL_PEER_KEY= |
export NG_ACCOUNT_LOCAL_PEER_KEY=5Q2FBa_VrUSGgJtsKc9Zstrtr0PyZYEgmeGxgtjrIo4A |
||||||
NG_ACCOUNT_SERVER=127.0.0.1,14400,[the broker's peer ID] |
export NG_ACCOUNT_SERVER=127.0.0.1,14400,95h47CSgbyHyHU4NAiaBd3hE76VONaQHlG9Mx0aJOLUA |
||||||
RUST_LOG= |
export RUST_LOG=debug |
||||||
|
@ -0,0 +1,97 @@ |
|||||||
|
<!-- |
||||||
|
// Copyright (c) 2022-2025 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 { onMount, tick, onDestroy } from "svelte"; |
||||||
|
import { |
||||||
|
sparql_update, |
||||||
|
toast_error, |
||||||
|
toast_success, |
||||||
|
active_session |
||||||
|
} from "../store"; |
||||||
|
import ng from "../api"; |
||||||
|
import { |
||||||
|
in_memory_discrete, open_viewer, set_viewer, set_editor, set_view_or_edit, cur_tab_doc_can_edit, cur_tab |
||||||
|
} from "../tab"; |
||||||
|
import{ PencilSquare, Lifebuoy } from "svelte-heros-v2"; |
||||||
|
import { t } from "svelte-i18n"; |
||||||
|
import { Button, Progressbar, Spinner, Alert } from "flowbite-svelte"; |
||||||
|
|
||||||
|
import Highlight, { LineNumbers } from "svelte-highlight"; |
||||||
|
import hljs from "highlight.js"; |
||||||
|
import { definer } from "../turtle"; |
||||||
|
import "svelte-highlight/styles/github.css"; |
||||||
|
const language = { |
||||||
|
name: "turtle", |
||||||
|
register: (hljs) => { |
||||||
|
return definer(hljs); |
||||||
|
}, |
||||||
|
}; |
||||||
|
|
||||||
|
export let commits = {graph:[]}; |
||||||
|
let source = ""; |
||||||
|
$: source = commits.graph.join(" .\r\n") + (commits.graph.length ? " .":""); |
||||||
|
|
||||||
|
const openQuery = async () => { |
||||||
|
|
||||||
|
await sparql_update("INSERT DATA { <> <did:ng:x:ng#social_query_sparql> \"CONSTRUCT { ?s ?p ?o } WHERE { ?s ?p ?o }\".}"); |
||||||
|
let commit_id = commits.heads[0]; |
||||||
|
let commit_key = commits.head_keys[0]; |
||||||
|
let session = $active_session; |
||||||
|
if (!session) return; |
||||||
|
let request_nuri = "did:ng:"+$cur_tab.doc.nuri+":c:"+commit_id+":k:"+commit_key; |
||||||
|
await ng.social_query_start( |
||||||
|
session.session_id, |
||||||
|
"did:ng:a", |
||||||
|
request_nuri, |
||||||
|
"did:ng:d:c", |
||||||
|
2, |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
onMount(()=>{ |
||||||
|
console.log($active_session); |
||||||
|
}); |
||||||
|
|
||||||
|
const info = () => { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
</script> |
||||||
|
<div class="flex-col"> |
||||||
|
<h1> Social Query</h1> |
||||||
|
{#if !source} |
||||||
|
<p class="p-3">{$t("doc.no_triples")}</p> |
||||||
|
{/if} |
||||||
|
<button |
||||||
|
on:click={info} |
||||||
|
on:keypress={info} |
||||||
|
class="select-none ml-2 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" |
||||||
|
> |
||||||
|
info |
||||||
|
</button> |
||||||
|
<button |
||||||
|
on:click={openQuery} |
||||||
|
on:keypress={openQuery} |
||||||
|
class="select-none ml-2 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" |
||||||
|
> |
||||||
|
<Lifebuoy tabindex="-1" class="mr-2 focus:outline-none" /> |
||||||
|
Start query |
||||||
|
</button> |
||||||
|
|
||||||
|
{#if source} |
||||||
|
<Highlight {language} code={source} class="mb-10" let:highlighted > |
||||||
|
<LineNumbers {highlighted} wrapLines hideBorder /> |
||||||
|
</Highlight> |
||||||
|
{/if} |
||||||
|
|
||||||
|
</div> |
||||||
|
|
@ -0,0 +1,95 @@ |
|||||||
|
// Copyright (c) 2022-2025 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.
|
||||||
|
|
||||||
|
//! Account Storage (Object Key/Col/Value Mapping)
|
||||||
|
|
||||||
|
use std::collections::HashSet; |
||||||
|
use std::hash::{DefaultHasher, Hash, Hasher}; |
||||||
|
|
||||||
|
use ng_net::types::InboxMsg; |
||||||
|
use ng_repo::utils::now_precise_timestamp; |
||||||
|
use serde_bare::to_vec; |
||||||
|
|
||||||
|
use ng_repo::errors::StorageError; |
||||||
|
use ng_repo::kcv_storage::*; |
||||||
|
use ng_repo::types::*; |
||||||
|
|
||||||
|
pub struct AccountStorage<'a> { |
||||||
|
key: Vec<u8>, |
||||||
|
storage: &'a dyn KCVStorage, |
||||||
|
} |
||||||
|
|
||||||
|
impl<'a> IModel for AccountStorage<'a> { |
||||||
|
fn key(&self) -> &Vec<u8> { |
||||||
|
&self.key |
||||||
|
} |
||||||
|
fn storage(&self) -> &dyn KCVStorage { |
||||||
|
self.storage |
||||||
|
} |
||||||
|
fn class(&self) -> &Class { |
||||||
|
&Self::CLASS |
||||||
|
} |
||||||
|
fn existential(&mut self) -> Option<&mut dyn IExistentialValue> { |
||||||
|
None |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl<'a> AccountStorage<'a> { |
||||||
|
// User <-> Inboxes : list of inboxes a user has registered as reader.
|
||||||
|
// FIXME: this should be in accounts storage, but because it doesn't implement the ORM yet, it is quicker to implement it here.
|
||||||
|
pub const INBOXES: MultiValueColumn<Self, (PubKey, OverlayId)> = MultiValueColumn::new(b'k'); |
||||||
|
|
||||||
|
pub const CLASS: Class<'a> = Class::new( |
||||||
|
"Account", |
||||||
|
None, |
||||||
|
None, |
||||||
|
&[], |
||||||
|
&[&Self::INBOXES as &dyn IMultiValueColumn], |
||||||
|
); |
||||||
|
|
||||||
|
pub fn load_inboxes( |
||||||
|
user: &UserId, |
||||||
|
storage: &'a dyn KCVStorage, |
||||||
|
) -> Result<HashSet<(PubKey, OverlayId)>, StorageError> { |
||||||
|
let mut opening = Self::new(user, storage); |
||||||
|
Self::INBOXES.get_all(&mut opening) |
||||||
|
} |
||||||
|
|
||||||
|
pub fn new(user: &UserId, storage: &'a dyn KCVStorage) -> Self { |
||||||
|
let mut key: Vec<u8> = Vec::with_capacity(33); |
||||||
|
key.append(&mut to_vec(user).unwrap()); |
||||||
|
Self { key, storage } |
||||||
|
} |
||||||
|
|
||||||
|
pub fn open( |
||||||
|
user: &UserId, |
||||||
|
storage: &'a dyn KCVStorage, |
||||||
|
) -> Result<AccountStorage<'a>, StorageError> { |
||||||
|
let opening = Self::new(user, storage); |
||||||
|
Ok(opening) |
||||||
|
} |
||||||
|
|
||||||
|
pub fn add_inbox( |
||||||
|
user: &UserId, |
||||||
|
inbox: PubKey, |
||||||
|
overlay: OverlayId, |
||||||
|
storage: &'a dyn KCVStorage, |
||||||
|
) -> Result<(), StorageError> { |
||||||
|
let mut opening = Self::new(user, storage); |
||||||
|
Self::INBOXES.add(&mut opening, &(inbox,overlay)) |
||||||
|
} |
||||||
|
|
||||||
|
pub fn create( |
||||||
|
user: &UserId, |
||||||
|
storage: &'a dyn KCVStorage, |
||||||
|
) -> Result<AccountStorage<'a>, StorageError> { |
||||||
|
let creating = Self::new(user, storage); |
||||||
|
Ok(creating) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,120 @@ |
|||||||
|
// Copyright (c) 2022-2025 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.
|
||||||
|
|
||||||
|
//! Inbox Storage (Object Key/Col/Value Mapping)
|
||||||
|
|
||||||
|
use std::collections::HashSet; |
||||||
|
use std::hash::{DefaultHasher, Hash, Hasher}; |
||||||
|
|
||||||
|
use ng_net::types::InboxMsg; |
||||||
|
use ng_repo::utils::now_precise_timestamp; |
||||||
|
use serde_bare::to_vec; |
||||||
|
|
||||||
|
use ng_repo::errors::StorageError; |
||||||
|
use ng_repo::kcv_storage::*; |
||||||
|
use ng_repo::types::*; |
||||||
|
|
||||||
|
pub struct InboxStorage<'a> { |
||||||
|
key: Vec<u8>, |
||||||
|
storage: &'a dyn KCVStorage, |
||||||
|
} |
||||||
|
|
||||||
|
impl<'a> IModel for InboxStorage<'a> { |
||||||
|
fn key(&self) -> &Vec<u8> { |
||||||
|
&self.key |
||||||
|
} |
||||||
|
fn storage(&self) -> &dyn KCVStorage { |
||||||
|
self.storage |
||||||
|
} |
||||||
|
fn class(&self) -> &Class { |
||||||
|
&Self::CLASS |
||||||
|
} |
||||||
|
fn existential(&mut self) -> Option<&mut dyn IExistentialValue> { |
||||||
|
None |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// seconds, nanosecs, hash of InboxMsgBody
|
||||||
|
type MsgKeySuffix = (u64, u32, u64); |
||||||
|
|
||||||
|
impl<'a> InboxStorage<'a> { |
||||||
|
// Inbox <-> Msg : list of incoming messages that will be delivered once a user is online
|
||||||
|
pub const MSGS: MultiMapColumn<Self, MsgKeySuffix, InboxMsg> = MultiMapColumn::new(b'm'); |
||||||
|
// Inbox <-> User : list of users who registered as readers of an inbox
|
||||||
|
pub const READERS: MultiValueColumn<Self, UserId> = MultiValueColumn::new(b'i'); |
||||||
|
|
||||||
|
pub const CLASS: Class<'a> = Class::new( |
||||||
|
"Inbox", |
||||||
|
None, |
||||||
|
None, |
||||||
|
&[], |
||||||
|
&[&Self::MSGS as &dyn IMultiValueColumn, &Self::READERS], |
||||||
|
); |
||||||
|
|
||||||
|
pub fn take_first_msg( |
||||||
|
inbox: &PubKey, |
||||||
|
overlay: &OverlayId, |
||||||
|
storage: &'a dyn KCVStorage, |
||||||
|
) -> Result<InboxMsg, StorageError> { |
||||||
|
let mut opening = Self::new(inbox, overlay, storage); |
||||||
|
Self::MSGS.take_first_value(&mut opening) |
||||||
|
} |
||||||
|
|
||||||
|
pub fn load_readers( |
||||||
|
inbox: &PubKey, |
||||||
|
overlay: &OverlayId, |
||||||
|
storage: &'a dyn KCVStorage, |
||||||
|
) -> Result<HashSet<UserId>, StorageError> { |
||||||
|
let mut opening = Self::new(inbox, overlay, storage); |
||||||
|
Self::READERS.get_all(&mut opening) |
||||||
|
} |
||||||
|
|
||||||
|
pub fn new(inbox: &PubKey, overlay: &OverlayId, storage: &'a dyn KCVStorage) -> Self { |
||||||
|
let mut key: Vec<u8> = Vec::with_capacity(33 + 33); |
||||||
|
key.append(&mut to_vec(overlay).unwrap()); |
||||||
|
key.append(&mut to_vec(inbox).unwrap()); |
||||||
|
Self { key, storage } |
||||||
|
} |
||||||
|
|
||||||
|
pub fn open( |
||||||
|
inbox: &PubKey, |
||||||
|
overlay: &OverlayId, |
||||||
|
storage: &'a dyn KCVStorage, |
||||||
|
) -> Result<InboxStorage<'a>, StorageError> { |
||||||
|
let opening = Self::new(inbox, overlay, storage); |
||||||
|
Ok(opening) |
||||||
|
} |
||||||
|
|
||||||
|
pub fn register_reader( |
||||||
|
inbox: &PubKey, |
||||||
|
overlay: &OverlayId, |
||||||
|
reader: &UserId, |
||||||
|
storage: &'a dyn KCVStorage, |
||||||
|
) -> Result<(), StorageError> { |
||||||
|
let mut opening = Self::new(inbox, overlay, storage); |
||||||
|
Self::READERS.add(&mut opening, reader) |
||||||
|
} |
||||||
|
|
||||||
|
pub fn enqueue_msg(&mut self, msg: &InboxMsg) -> Result<(), StorageError> { |
||||||
|
let (sec,nano) = now_precise_timestamp(); |
||||||
|
let mut hasher = DefaultHasher::new(); |
||||||
|
msg.body.hash(&mut hasher); |
||||||
|
let key = (sec,nano, hasher.finish()); |
||||||
|
Self::MSGS.add(self, &key,msg) |
||||||
|
} |
||||||
|
|
||||||
|
pub fn create( |
||||||
|
inbox: &PubKey, |
||||||
|
overlay: &OverlayId, |
||||||
|
storage: &'a dyn KCVStorage, |
||||||
|
) -> Result<InboxStorage<'a>, StorageError> { |
||||||
|
let creating = Self::new(inbox, overlay, storage); |
||||||
|
Ok(creating) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,94 @@ |
|||||||
|
/* |
||||||
|
* Copyright (c) 2022-2025 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 std::sync::Arc; |
||||||
|
|
||||||
|
use async_std::sync::Mutex; |
||||||
|
|
||||||
|
use ng_repo::errors::*; |
||||||
|
use ng_repo::log::*; |
||||||
|
use ng_repo::types::OverlayId; |
||||||
|
|
||||||
|
use crate::broker::BROKER; |
||||||
|
use crate::connection::NoiseFSM; |
||||||
|
use crate::types::*; |
||||||
|
use crate::{actor::*, types::ProtocolMessage}; |
||||||
|
|
||||||
|
impl ClientEvent { |
||||||
|
pub fn get_actor(&self, id: i64) -> Box<dyn EActor> { |
||||||
|
Actor::<ClientEvent, ()>::new_responder(id) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl TryFrom<ProtocolMessage> for ClientEvent { |
||||||
|
type Error = ProtocolError; |
||||||
|
fn try_from(msg: ProtocolMessage) -> Result<Self, Self::Error> { |
||||||
|
|
||||||
|
if let ProtocolMessage::ClientMessage(ClientMessage::V0(ClientMessageV0 { |
||||||
|
content: ClientMessageContentV0::ClientEvent(e), |
||||||
|
.. |
||||||
|
})) = msg |
||||||
|
{ |
||||||
|
Ok(e) |
||||||
|
} else { |
||||||
|
log_debug!("INVALID {:?}", msg); |
||||||
|
Err(ProtocolError::InvalidValue) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl From<ClientEvent> for ProtocolMessage { |
||||||
|
fn from(e: ClientEvent) -> ProtocolMessage { |
||||||
|
ProtocolMessage::ClientMessage(ClientMessage::V0(ClientMessageV0 { |
||||||
|
content: ClientMessageContentV0::ClientEvent(e), |
||||||
|
overlay: OverlayId::nil(), |
||||||
|
padding: vec![] |
||||||
|
})) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl Actor<'_, ClientEvent, ()> {} |
||||||
|
|
||||||
|
#[async_trait::async_trait] |
||||||
|
impl EActor for Actor<'_, ClientEvent, ()> { |
||||||
|
async fn respond( |
||||||
|
&mut self, |
||||||
|
msg: ProtocolMessage, |
||||||
|
fsm: Arc<Mutex<NoiseFSM>>, |
||||||
|
) -> Result<(), ProtocolError> { |
||||||
|
let req = ClientEvent::try_from(msg)?; |
||||||
|
match req { |
||||||
|
ClientEvent::InboxPopRequest => { |
||||||
|
let sb = { BROKER.read().await.get_server_broker()? }; |
||||||
|
let user = {fsm.lock().await.user_id()?}; |
||||||
|
let res: Result<InboxMsg, ServerError> = { |
||||||
|
sb.read().await.inbox_pop_for_user(user).await |
||||||
|
}; |
||||||
|
|
||||||
|
if let Ok(msg) = res { |
||||||
|
let _ = fsm |
||||||
|
.lock() |
||||||
|
.await |
||||||
|
.send(ProtocolMessage::ClientMessage(ClientMessage::V0( |
||||||
|
ClientMessageV0 { |
||||||
|
overlay: msg.body.to_overlay.clone(), |
||||||
|
padding: vec![], |
||||||
|
content: ClientMessageContentV0::InboxReceive{msg, from_queue: true}, |
||||||
|
}, |
||||||
|
))) |
||||||
|
.await; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,74 @@ |
|||||||
|
/* |
||||||
|
* Copyright (c) 2022-2025 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 std::sync::Arc; |
||||||
|
|
||||||
|
use async_std::sync::Mutex; |
||||||
|
|
||||||
|
use ng_repo::errors::*; |
||||||
|
use ng_repo::log::*; |
||||||
|
use ng_repo::types::OverlayId; |
||||||
|
|
||||||
|
use crate::broker::BROKER; |
||||||
|
use crate::connection::NoiseFSM; |
||||||
|
use crate::types::*; |
||||||
|
use crate::{actor::*, types::ProtocolMessage}; |
||||||
|
|
||||||
|
impl InboxPost { |
||||||
|
pub fn get_actor(&self, id: i64) -> Box<dyn EActor> { |
||||||
|
Actor::<InboxPost, ()>::new_responder(id) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl TryFrom<ProtocolMessage> for InboxPost { |
||||||
|
type Error = ProtocolError; |
||||||
|
fn try_from(msg: ProtocolMessage) -> Result<Self, Self::Error> { |
||||||
|
let req: ClientRequestContentV0 = msg.try_into()?; |
||||||
|
if let ClientRequestContentV0::InboxPost(a) = req { |
||||||
|
Ok(a) |
||||||
|
} else { |
||||||
|
log_debug!("INVALID {:?}", req); |
||||||
|
Err(ProtocolError::InvalidValue) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl From<InboxPost> for ProtocolMessage { |
||||||
|
fn from(msg: InboxPost) -> ProtocolMessage { |
||||||
|
ProtocolMessage::from_client_request_v0( |
||||||
|
ClientRequestContentV0::InboxPost(msg), |
||||||
|
OverlayId::nil(), |
||||||
|
) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl Actor<'_, InboxPost, ()> {} |
||||||
|
|
||||||
|
#[async_trait::async_trait] |
||||||
|
impl EActor for Actor<'_, InboxPost, ()> { |
||||||
|
async fn respond( |
||||||
|
&mut self, |
||||||
|
msg: ProtocolMessage, |
||||||
|
fsm: Arc<Mutex<NoiseFSM>>, |
||||||
|
) -> Result<(), ProtocolError> { |
||||||
|
let req = InboxPost::try_from(msg)?; |
||||||
|
let sb = { BROKER.read().await.get_server_broker()? }; |
||||||
|
let res: Result<(), ServerError> = sb |
||||||
|
.read() |
||||||
|
.await.inbox_post(req).await; |
||||||
|
|
||||||
|
fsm.lock() |
||||||
|
.await |
||||||
|
.send_in_reply_to(res.into(), self.id()) |
||||||
|
.await?; |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,91 @@ |
|||||||
|
/* |
||||||
|
* Copyright (c) 2022-2025 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 std::sync::Arc; |
||||||
|
|
||||||
|
use async_std::sync::Mutex; |
||||||
|
|
||||||
|
use ng_repo::errors::*; |
||||||
|
use ng_repo::log::*; |
||||||
|
use ng_repo::types::OverlayId; |
||||||
|
use ng_repo::utils::verify; |
||||||
|
|
||||||
|
use crate::broker::BROKER; |
||||||
|
use crate::connection::NoiseFSM; |
||||||
|
use crate::types::*; |
||||||
|
use crate::{actor::*, types::ProtocolMessage}; |
||||||
|
|
||||||
|
impl InboxRegister { |
||||||
|
pub fn get_actor(&self, id: i64) -> Box<dyn EActor> { |
||||||
|
Actor::<InboxRegister, ()>::new_responder(id) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl TryFrom<ProtocolMessage> for InboxRegister { |
||||||
|
type Error = ProtocolError; |
||||||
|
fn try_from(msg: ProtocolMessage) -> Result<Self, Self::Error> { |
||||||
|
let req: ClientRequestContentV0 = msg.try_into()?; |
||||||
|
if let ClientRequestContentV0::InboxRegister(a) = req { |
||||||
|
Ok(a) |
||||||
|
} else { |
||||||
|
log_debug!("INVALID {:?}", req); |
||||||
|
Err(ProtocolError::InvalidValue) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl From<InboxRegister> for ProtocolMessage { |
||||||
|
fn from(msg: InboxRegister) -> ProtocolMessage { |
||||||
|
ProtocolMessage::from_client_request_v0( |
||||||
|
ClientRequestContentV0::InboxRegister(msg), |
||||||
|
OverlayId::nil(), |
||||||
|
) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl Actor<'_, InboxRegister, ()> {} |
||||||
|
|
||||||
|
#[async_trait::async_trait] |
||||||
|
impl EActor for Actor<'_, InboxRegister, ()> { |
||||||
|
async fn respond( |
||||||
|
&mut self, |
||||||
|
msg: ProtocolMessage, |
||||||
|
fsm: Arc<Mutex<NoiseFSM>>, |
||||||
|
) -> Result<(), ProtocolError> { |
||||||
|
let req = InboxRegister::try_from(msg)?; |
||||||
|
|
||||||
|
// verify registration
|
||||||
|
if verify(&req.challenge, req.sig, req.inbox_id).is_err() { |
||||||
|
fsm.lock() |
||||||
|
.await |
||||||
|
.send_in_reply_to(Result::<(), _>::Err(ServerError::InvalidSignature).into(), self.id()) |
||||||
|
.await?; |
||||||
|
return Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
let sb = { BROKER.read().await.get_server_broker()? }; |
||||||
|
|
||||||
|
let user_id = { |
||||||
|
let fsm = fsm.lock().await; |
||||||
|
fsm.user_id()? |
||||||
|
}; |
||||||
|
|
||||||
|
let res: Result<(), ServerError> = sb |
||||||
|
.read() |
||||||
|
.await.inbox_register(user_id, req); |
||||||
|
|
||||||
|
fsm.lock() |
||||||
|
.await |
||||||
|
.send_in_reply_to(res.into(), self.id()) |
||||||
|
.await?; |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,589 @@ |
|||||||
|
// Copyright (c) 2022-2025 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.
|
||||||
|
|
||||||
|
//! Processor for each type of InboxMsgContent
|
||||||
|
|
||||||
|
use ng_net::actor::SoS; |
||||||
|
use ng_oxigraph::oxigraph::sparql::QueryResults; |
||||||
|
use ng_oxigraph::oxrdf::{NamedNode, Term, Triple}; |
||||||
|
use ng_oxigraph::oxsdatatypes::DateTime; |
||||||
|
use ng_repo::types::{Block, ObjectRef, OverlayId, PrivKey, RepoId, StoreRepo, StoreRepoV0}; |
||||||
|
use ng_repo::{errors::*, store::Store, types::Commit}; |
||||||
|
use ng_repo::log::*; |
||||||
|
|
||||||
|
use ng_net::types::{InboxMsg, InboxMsgContent, InboxPost, SocialQuery, SocialQueryResponse, SocialQueryResponseContent}; |
||||||
|
use ng_net::app_protocol::*; |
||||||
|
|
||||||
|
use crate::verifier::*; |
||||||
|
|
||||||
|
impl Verifier { |
||||||
|
|
||||||
|
async fn post_to_inbox(&self, post: InboxPost) -> Result<(), VerifierError> { |
||||||
|
match self.client_request::<_,()>(post).await |
||||||
|
{ |
||||||
|
Err(e) => Err(VerifierError::InboxError(e.to_string())), |
||||||
|
Ok(SoS::Stream(_)) => Err(VerifierError::InboxError(NgError::InvalidResponse.to_string())), |
||||||
|
Ok(SoS::Single(_)) => Ok(()), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
pub(crate) async fn create_social_query_forwarder( |
||||||
|
&mut self,
|
||||||
|
social_query_doc_nuri_string: &String, |
||||||
|
from_forwarder_nuri_string: &String, |
||||||
|
from_profile_nuri_string: &String, |
||||||
|
from_inbox_nuri_string: &String, |
||||||
|
) -> Result<(String,NuriV0), VerifierError> { |
||||||
|
// creating the ForwardedSocialQuery in the private store
|
||||||
|
let forwarder = self.doc_create_with_store_repo( |
||||||
|
"Graph".to_string(), "social:query:forwarded".to_string(), |
||||||
|
"store".to_string(), None // meaning in private store
|
||||||
|
).await?; |
||||||
|
let forwarder_nuri = NuriV0::new_from_repo_graph(&forwarder)?; |
||||||
|
let forwarder_id = forwarder_nuri.target.repo_id().clone(); |
||||||
|
let forwarder_nuri_string = NuriV0::repo_id(&forwarder_id); |
||||||
|
|
||||||
|
// adding triples in forwarder doc : ng:social_query_id
|
||||||
|
let sparql_update = format!(" PREFIX ng: <did:ng:x:ng#> |
||||||
|
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
|
||||||
|
INSERT DATA {{ <> ng:social_query_id <{social_query_doc_nuri_string}>. |
||||||
|
<> ng:social_query_forwarder <{from_forwarder_nuri_string}>. |
||||||
|
<> ng:social_query_from_inbox <{from_inbox_nuri_string}>. |
||||||
|
<> ng:social_query_from_profile <{from_profile_nuri_string}>. |
||||||
|
<> ng:social_query_started \"{}\"^^xsd:dateTime . }}",DateTime::now()); |
||||||
|
let ret = self |
||||||
|
.process_sparql_update(&forwarder_nuri, &sparql_update, &Some(forwarder_nuri_string.clone()), vec![]) |
||||||
|
.await; |
||||||
|
if let Err(e) = ret { |
||||||
|
return Err(VerifierError::SparqlError(e)); |
||||||
|
} |
||||||
|
Ok((forwarder_nuri_string,forwarder_nuri)) |
||||||
|
} |
||||||
|
|
||||||
|
pub(crate) async fn mark_social_query_forwarder(&mut self, forwarder_nuri_string: &String, forwarder_nuri: &NuriV0, predicate: String) -> Result<(), VerifierError> { |
||||||
|
|
||||||
|
// adding triples in forwarder doc : ng:social_query_id
|
||||||
|
let sparql_update = format!("INSERT DATA {{ <{forwarder_nuri_string}> <did:ng:x:ng#{predicate}> \"{}\"^^<http://www.w3.org/2001/XMLSchema#dateTime> . }}",DateTime::now()); |
||||||
|
let ret = self |
||||||
|
.process_sparql_update(forwarder_nuri, &sparql_update, &None, vec![]) |
||||||
|
.await; |
||||||
|
if let Err(e) = ret { |
||||||
|
return Err(VerifierError::SparqlError(e)); |
||||||
|
} |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
fn get_privkey_of_inbox(&self, this_overlay: &OverlayId) -> Result<PrivKey, VerifierError> { |
||||||
|
let store = self.get_store_by_overlay_id(this_overlay)?; |
||||||
|
let repo = self.repos.get(&store.id()).ok_or(NgError::RepoNotFound)?; |
||||||
|
let from_inbox = repo.inbox.to_owned().ok_or(NgError::InboxNotFound)?; |
||||||
|
Ok(from_inbox) |
||||||
|
} |
||||||
|
|
||||||
|
pub(crate) fn get_profile_replying_to(&self, forwarded_from_profile: &String) -> Result<
|
||||||
|
(OverlayId, PrivKey) ,NgError> { |
||||||
|
|
||||||
|
let from_profile_id = if forwarded_from_profile.starts_with("did:ng:b") { |
||||||
|
self.config.protected_store_id.unwrap() |
||||||
|
} else { |
||||||
|
self.config.public_store_id.unwrap() |
||||||
|
}; |
||||||
|
|
||||||
|
let repo = self.repos.get(&from_profile_id).ok_or(NgError::RepoNotFound)?; |
||||||
|
let inbox = repo.inbox.to_owned().ok_or(NgError::InboxNotFound)?; |
||||||
|
let overlay = repo.store.get_store_repo().outer_overlay(); |
||||||
|
|
||||||
|
Ok( (overlay, inbox.clone()) ) |
||||||
|
} |
||||||
|
|
||||||
|
pub(crate) fn get_2_profiles(&self) -> Result<(
|
||||||
|
(StoreRepo, PrivKey), // public
|
||||||
|
(StoreRepo, PrivKey) // protected
|
||||||
|
) ,NgError> { |
||||||
|
|
||||||
|
let protected_store_id = self.config.protected_store_id.unwrap(); |
||||||
|
let protected_repo = self.repos.get(&protected_store_id).ok_or(NgError::RepoNotFound)?; |
||||||
|
let protected_inbox = protected_repo.inbox.to_owned().ok_or(NgError::InboxNotFound)?; |
||||||
|
let protected_store_repo = protected_repo.store.get_store_repo(); |
||||||
|
|
||||||
|
let public_store_id = self.config.public_store_id.unwrap(); |
||||||
|
let public_repo = self.repos.get(&public_store_id).ok_or(NgError::RepoNotFound)?; |
||||||
|
let public_inbox = public_repo.inbox.to_owned().ok_or(NgError::InboxNotFound)?; |
||||||
|
let public_store_repo = public_repo.store.get_store_repo(); |
||||||
|
|
||||||
|
Ok(( |
||||||
|
(*public_store_repo, public_inbox.clone()), |
||||||
|
(*protected_store_repo, protected_inbox.clone()) |
||||||
|
)) |
||||||
|
} |
||||||
|
|
||||||
|
pub(crate) async fn social_query_dispatch( |
||||||
|
&mut self, |
||||||
|
to_profile_nuri: &String, |
||||||
|
to_inbox_nuri: &String, |
||||||
|
forwarder_nuri: &NuriV0, |
||||||
|
forwarder_id: &RepoId, |
||||||
|
from_profiles: &(
|
||||||
|
(StoreRepo, PrivKey), // public
|
||||||
|
(StoreRepo, PrivKey) // protected
|
||||||
|
), |
||||||
|
query_id: &RepoId, |
||||||
|
definition_commit_body_ref: &ObjectRef, |
||||||
|
blocks: &Vec<Block>, |
||||||
|
degree: u16, |
||||||
|
) -> Result<(), VerifierError> { |
||||||
|
|
||||||
|
// first add an entry in the local forwarded social query, to monitor progress
|
||||||
|
let sparql_update = format!(" |
||||||
|
PREFIX ng: <did:ng:x:ng#> |
||||||
|
INSERT DATA {{
|
||||||
|
<did:ng:_> ng:social_query_forwarded_to_profile <{to_profile_nuri}> . |
||||||
|
<did:ng:_> ng:social_query_forwarded_to_inbox <{to_inbox_nuri}> . |
||||||
|
}}"); |
||||||
|
let ret = self |
||||||
|
.process_sparql_update(&forwarder_nuri, &sparql_update, &None, vec![]) |
||||||
|
.await; |
||||||
|
if let Err(e) = ret { |
||||||
|
return Err(VerifierError::SparqlError(e)); |
||||||
|
} |
||||||
|
// then send InboxPost message.
|
||||||
|
|
||||||
|
let from_profile = if to_profile_nuri.starts_with("did:ng:b") { |
||||||
|
&from_profiles.1 |
||||||
|
} else { |
||||||
|
&from_profiles.0 |
||||||
|
}; |
||||||
|
|
||||||
|
self.post_to_inbox(InboxPost::new_social_query_request( |
||||||
|
from_profile.0,
|
||||||
|
from_profile.1.clone(), |
||||||
|
*forwarder_id, |
||||||
|
to_profile_nuri.clone(), |
||||||
|
to_inbox_nuri.clone(), |
||||||
|
None, |
||||||
|
*query_id, |
||||||
|
definition_commit_body_ref.clone(), |
||||||
|
blocks.to_vec(), |
||||||
|
degree, |
||||||
|
)?).await?; |
||||||
|
|
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
pub(crate) async fn process_inbox( |
||||||
|
&mut self, |
||||||
|
msg: InboxMsg, |
||||||
|
content: InboxMsgContent, |
||||||
|
) -> Result<(), VerifierError> { |
||||||
|
|
||||||
|
match content { |
||||||
|
InboxMsgContent::SocialQuery(SocialQuery::Request(req)) => { |
||||||
|
|
||||||
|
let profile_id_nuri = NuriV0::from_store_repo_string(&req.from_profile_store_repo); |
||||||
|
|
||||||
|
//TODO: check that msg.body.from_overlay matches with req.from_profile_store_repo
|
||||||
|
|
||||||
|
//TODO: check that this contact is mutual req.from_profile_store_repo must be in our contact list
|
||||||
|
|
||||||
|
// getting the privkey of the inbox because we will need it here below to send responses.
|
||||||
|
let reply_with_inbox = self.get_privkey_of_inbox(&msg.body.to_overlay)?; |
||||||
|
|
||||||
|
let social_query_doc_nuri_string: String = NuriV0::repo_id(&req.query_id); |
||||||
|
|
||||||
|
// checking that we didn't process this query ID yet. if we did, return a SocialQueryResponseContent::AlreadyRequested
|
||||||
|
match self.sparql_query( |
||||||
|
&NuriV0::new_entire_user_site(), |
||||||
|
format!("ASK {{ ?s <did:ng:x:ng#social_query_id> <{social_query_doc_nuri_string}> }}"), None).await?
|
||||||
|
{ |
||||||
|
QueryResults::Boolean(true) => { |
||||||
|
let post = InboxPost::new_social_query_response_replying_to( |
||||||
|
&msg.body, |
||||||
|
&req, |
||||||
|
SocialQueryResponseContent::AlreadyRequested, |
||||||
|
reply_with_inbox.clone() |
||||||
|
)?; |
||||||
|
self.post_to_inbox(post).await?; |
||||||
|
return Ok(()); |
||||||
|
} |
||||||
|
_ => {} |
||||||
|
} |
||||||
|
|
||||||
|
// otherwise, create the forwarder
|
||||||
|
let (forwarder_nuri_string, forwarder_nuri) = self.create_social_query_forwarder( |
||||||
|
&social_query_doc_nuri_string, |
||||||
|
&NuriV0::repo_id(&req.forwarder_id), |
||||||
|
&NuriV0::from_store_repo_string(&req.from_profile_store_repo), |
||||||
|
&NuriV0::inbox(&msg.body.from_inbox.unwrap()) |
||||||
|
).await?; |
||||||
|
|
||||||
|
let temp_mini_block_storage = Store::new_temp_in_mem(); |
||||||
|
for block in msg.blocks.iter() { |
||||||
|
let _id = temp_mini_block_storage.put(block)?; |
||||||
|
} |
||||||
|
let commit = Commit::load(req.definition_commit_body_ref.clone(), |
||||||
|
&temp_mini_block_storage, true) |
||||||
|
.map_err(|e| { |
||||||
|
//log_err!("err : {:?}", e);
|
||||||
|
e |
||||||
|
})?; |
||||||
|
|
||||||
|
let triples = Verifier::get_triples_from_transaction(commit.body().unwrap())?; |
||||||
|
|
||||||
|
let mut sparql: Option<String> = None; |
||||||
|
for triple in triples { |
||||||
|
if triple.predicate.as_str() == "did:ng:x:ng#social_query_sparql" { |
||||||
|
sparql = Some( |
||||||
|
match triple.object { |
||||||
|
Term::Literal(l) => l.value().into(), |
||||||
|
_ => return Err(VerifierError::InvalidSocialQuery) |
||||||
|
}); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
//TODO: in case of errors here below, mark the forwarder as ng:social_query_error
|
||||||
|
if sparql.is_none() { return Err(VerifierError::InvalidSocialQuery); } |
||||||
|
|
||||||
|
log_info!("{}",sparql.as_ref().unwrap()); |
||||||
|
|
||||||
|
let res = self.sparql_query(&NuriV0::new_entire_user_site(), sparql.unwrap(), None).await?; |
||||||
|
|
||||||
|
let results = match res { |
||||||
|
QueryResults::Boolean(_) | QueryResults::Solutions(_) => return Err(VerifierError::NotImplemented), |
||||||
|
QueryResults::Graph(triples) => { |
||||||
|
let mut results = vec![]; |
||||||
|
for t in triples { |
||||||
|
match t { |
||||||
|
Err(e) => { log_err!("{}",e.to_string()); return Err(VerifierError::SparqlError(e.to_string()))}, |
||||||
|
Ok(triple) => results.push(triple), |
||||||
|
} |
||||||
|
} |
||||||
|
results |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
log_info!("{:?}",results); |
||||||
|
|
||||||
|
// Do we have local results matching the request's query? If yes, we send them back to the forwarder right away
|
||||||
|
if !results.is_empty() { |
||||||
|
let content = SocialQueryResponseContent::Graph(serde_bare::to_vec(&results).unwrap()); |
||||||
|
let post = InboxPost::new_social_query_response_replying_to( |
||||||
|
&msg.body, |
||||||
|
&req, |
||||||
|
content, |
||||||
|
reply_with_inbox.clone() |
||||||
|
)?; |
||||||
|
self.post_to_inbox(post).await?; |
||||||
|
} |
||||||
|
|
||||||
|
// only fan out if we have contacts (that match the grant selected by current user)
|
||||||
|
// and if degree is > to 1 or equal to zero
|
||||||
|
if req.degree == 1 { |
||||||
|
|
||||||
|
// ending here.
|
||||||
|
self.mark_social_query_forwarder(&forwarder_nuri_string, &forwarder_nuri, "social_query_ended".to_string()).await?; |
||||||
|
let post = InboxPost::new_social_query_response_replying_to( |
||||||
|
&msg.body, |
||||||
|
&req, |
||||||
|
SocialQueryResponseContent::EndOfReplies, |
||||||
|
reply_with_inbox.clone() |
||||||
|
)?; |
||||||
|
self.post_to_inbox(post).await?; |
||||||
|
|
||||||
|
return Ok(()) |
||||||
|
} |
||||||
|
// fan out forwarded social queries to all contacts (except the one we received it from)
|
||||||
|
|
||||||
|
// getting the contacts to forward to
|
||||||
|
let sparql = format!("PREFIX ng: <did:ng:x:ng#> |
||||||
|
SELECT ?profile_id ?inbox_id WHERE
|
||||||
|
{{ ?c ng:c \"social:contact\" . |
||||||
|
OPTIONAL {{ ?c ng:site ?profile_id . ?c ng:site_inbox ?inbox_id }} |
||||||
|
OPTIONAL {{ ?c ng:protected ?profile_id . ?c ng:protected_inbox ?inbox_id }} |
||||||
|
FILTER ( bound(?profile_id) && NOT EXISTS {{ ?c ng:site <{profile_id_nuri}> }} && NOT EXISTS {{ ?c ng:protected <{profile_id_nuri}> }} ) |
||||||
|
}}"); |
||||||
|
log_info!("{sparql}"); |
||||||
|
let sols = match self.sparql_query( |
||||||
|
&NuriV0::new_entire_user_site(), |
||||||
|
sparql, None).await?
|
||||||
|
{ |
||||||
|
QueryResults::Solutions(sols) => { sols } |
||||||
|
_ => return Err(VerifierError::SparqlError(NgError::InvalidResponse.to_string())), |
||||||
|
}; |
||||||
|
|
||||||
|
let degree = if req.degree == 0 { 0 } else { req.degree - 1 }; |
||||||
|
log_info!("new degree {degree}"); |
||||||
|
let mut found_contact = false; |
||||||
|
let forwarder_id = forwarder_nuri.target.repo_id().clone(); |
||||||
|
|
||||||
|
let from_profiles = self.get_2_profiles()?; |
||||||
|
|
||||||
|
for sol in sols { |
||||||
|
match sol { |
||||||
|
Err(e) => return Err(VerifierError::SparqlError(e.to_string())), |
||||||
|
Ok(s) => { |
||||||
|
if let Some(Term::NamedNode(profile_id)) = s.get("profile_id") { |
||||||
|
let to_profile_nuri = profile_id.as_string(); |
||||||
|
if let Some(Term::NamedNode(inbox_id)) = s.get("inbox_id") { |
||||||
|
let to_inbox_nuri = inbox_id.as_string(); |
||||||
|
|
||||||
|
found_contact = true; |
||||||
|
|
||||||
|
self.social_query_dispatch( |
||||||
|
to_profile_nuri,
|
||||||
|
to_inbox_nuri,
|
||||||
|
&forwarder_nuri,
|
||||||
|
&forwarder_id,
|
||||||
|
&from_profiles, |
||||||
|
&req.query_id,
|
||||||
|
&req.definition_commit_body_ref,
|
||||||
|
&msg.blocks,
|
||||||
|
degree |
||||||
|
).await?; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
// if not found any contact, we stop here
|
||||||
|
log_info!("found contact {found_contact}"); |
||||||
|
if !found_contact { |
||||||
|
self.mark_social_query_forwarder(&forwarder_nuri_string, &forwarder_nuri, "social_query_ended".to_string()).await?; |
||||||
|
let post = InboxPost::new_social_query_response_replying_to( |
||||||
|
&msg.body, |
||||||
|
&req, |
||||||
|
SocialQueryResponseContent::EndOfReplies, |
||||||
|
reply_with_inbox |
||||||
|
)?; |
||||||
|
self.post_to_inbox(post).await?; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
InboxMsgContent::SocialQuery(SocialQuery::Response(response)) => { |
||||||
|
|
||||||
|
if msg.body.from_inbox.is_none() { |
||||||
|
// TODO log error
|
||||||
|
// we do nothing as this is invalid msg. it must have a from.
|
||||||
|
return Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
// TODO: first we open the response.forwarder_id (because in webapp, it might not be loaded yet)
|
||||||
|
|
||||||
|
let forwarder_nuri = NuriV0::new_repo_target_from_id(&response.forwarder_id); |
||||||
|
let forwarder_nuri_string = NuriV0::repo_id(&response.forwarder_id); |
||||||
|
// checking that we do have a running ForwardedSocialQuery, and that it didnt end, otherwise it must be spam.
|
||||||
|
match self.sparql_query( &forwarder_nuri, format!("ASK {{ <> <did:ng:x:ng#social_query_id> <{}> }} ", |
||||||
|
NuriV0::repo_id(&response.query_id)),Some(forwarder_nuri_string.clone())).await? { |
||||||
|
QueryResults::Boolean(true) => {} |
||||||
|
_ => { return Err(VerifierError::InvalidSocialQuery) } |
||||||
|
} |
||||||
|
let (forwarded_from_profile, forwarded_from_inbox, from_forwarder) = match self.sparql_query( |
||||||
|
&forwarder_nuri, |
||||||
|
"PREFIX ng: <did:ng:x:ng#> |
||||||
|
SELECT ?from_profile ?from_inbox ?from_forwarder ?ended WHERE
|
||||||
|
{{ <> ng:social_query_from_profile ?from_profile . |
||||||
|
<> ng:social_query_from_inbox ?from_inbox . |
||||||
|
<> ng:social_query_forwarder ?from_forwarder . |
||||||
|
<> ng:social_query_ended ?ended .
|
||||||
|
}}".to_string(),
|
||||||
|
Some(forwarder_nuri_string)).await?
|
||||||
|
{ |
||||||
|
QueryResults::Solutions(mut sols) => { |
||||||
|
match sols.next() { |
||||||
|
None => { |
||||||
|
log_info!("at origin"); |
||||||
|
(None, None, None) |
||||||
|
} |
||||||
|
Some(Err(e)) => { |
||||||
|
// TODO log error
|
||||||
|
// we do nothing as we couldn't find the ForwardedSocialQuery
|
||||||
|
return Err(VerifierError::SparqlError(e.to_string())); |
||||||
|
} |
||||||
|
Some(Ok(sol)) => { |
||||||
|
if let Some(Term::NamedNode(_)) = sol.get("ended") { |
||||||
|
// TODO log error : someone is giving back some results while the forwarder is ended
|
||||||
|
return Ok(()) |
||||||
|
}; |
||||||
|
let from_profile = if let Some(Term::NamedNode(nuri)) = sol.get("from_profile") { |
||||||
|
Some(nuri.as_string().clone()) |
||||||
|
} else { |
||||||
|
None |
||||||
|
}; |
||||||
|
let from_inbox = if let Some(Term::NamedNode(nuri)) = sol.get("from_inbox") { |
||||||
|
Some(nuri.as_string().clone()) |
||||||
|
} else { |
||||||
|
None |
||||||
|
}; |
||||||
|
let from_forwarder = if let Some(Term::NamedNode(nuri)) = sol.get("from_forwarder") { |
||||||
|
Some(nuri.as_string().clone()) |
||||||
|
} else { |
||||||
|
None |
||||||
|
}; |
||||||
|
|
||||||
|
(from_profile, from_inbox, from_forwarder) |
||||||
|
} |
||||||
|
}
|
||||||
|
} |
||||||
|
_ => return Err(VerifierError::SparqlError(NgError::InvalidResponse.to_string())), |
||||||
|
}; |
||||||
|
|
||||||
|
// searching for the tokenized commit that added this forwarding.
|
||||||
|
let spar = format!("PREFIX ng: <did:ng:x:ng#> |
||||||
|
SELECT ?token WHERE
|
||||||
|
{{ ?token ng:social_query_forwarded_to_inbox <{}> . |
||||||
|
MINUS {{ ?token ng:social_query_ended ?t . }} . |
||||||
|
}}", |
||||||
|
NuriV0::inbox(&msg.body.from_inbox.unwrap()) |
||||||
|
); |
||||||
|
log_info!("{spar}"); |
||||||
|
let token = match self.sparql_query( |
||||||
|
&forwarder_nuri, |
||||||
|
//<> ng:social_query_id <{}> NuriV0::inbox(&msg.body.from_inbox.unwrap()),
|
||||||
|
spar, |
||||||
|
Some(NuriV0::repo_id(&response.forwarder_id))).await?
|
||||||
|
{ |
||||||
|
QueryResults::Solutions(mut sols) => { |
||||||
|
match sols.next() { |
||||||
|
None => { return Err(VerifierError::SparqlError("Token not found".to_string())); } |
||||||
|
Some(Err(e)) => { |
||||||
|
// TODO log error
|
||||||
|
// we do nothing as we couldn't find the token
|
||||||
|
return Err(VerifierError::SparqlError(e.to_string())); |
||||||
|
} |
||||||
|
Some(Ok(sol)) => { |
||||||
|
if let Some(Term::NamedNode(token)) = sol.get("token") { |
||||||
|
token.as_string().clone() |
||||||
|
} else { |
||||||
|
// TODO log error
|
||||||
|
// we do nothing as we couldn't find the token
|
||||||
|
return Err(VerifierError::SparqlError(NgError::InvalidResponse.to_string())); |
||||||
|
} |
||||||
|
} |
||||||
|
}
|
||||||
|
} |
||||||
|
_ => return Err(VerifierError::SparqlError(NgError::InvalidResponse.to_string())), |
||||||
|
}; |
||||||
|
log_info!("token = {token}"); |
||||||
|
|
||||||
|
let at_origin = forwarded_from_profile.is_none() || forwarded_from_inbox.is_none() || from_forwarder.is_none(); |
||||||
|
|
||||||
|
match response.content { |
||||||
|
SocialQueryResponseContent::AlreadyRequested
|
||||||
|
| SocialQueryResponseContent::EndOfReplies
|
||||||
|
| SocialQueryResponseContent::Error(_) => { |
||||||
|
// ending here this forwarding.
|
||||||
|
self.mark_social_query_forwarder(&token, &forwarder_nuri, "social_query_ended".to_string()).await?; |
||||||
|
// TODO record error
|
||||||
|
|
||||||
|
// if we are at the end of the whole ForwardedSocialQuery (no more pending responses)
|
||||||
|
// we send EndOfReplies upstream, and mark as ended.
|
||||||
|
|
||||||
|
let the_end = match self.sparql_query( |
||||||
|
&forwarder_nuri, |
||||||
|
format!("PREFIX ng: <did:ng:x:ng#> |
||||||
|
SELECT ?token WHERE
|
||||||
|
{{ ?token ng:social_query_forwarded_to_profile ?p . |
||||||
|
MINUS {{ ?token ng:social_query_ended ?t . }} |
||||||
|
}}"), |
||||||
|
None).await? |
||||||
|
{ |
||||||
|
QueryResults::Solutions(mut sols) => { |
||||||
|
match sols.next() { |
||||||
|
None => true, |
||||||
|
_ => false, |
||||||
|
} |
||||||
|
} |
||||||
|
_ => { |
||||||
|
// TODO: log error
|
||||||
|
false |
||||||
|
} |
||||||
|
}; |
||||||
|
if the_end { |
||||||
|
// marking the end
|
||||||
|
self.mark_social_query_forwarder(&NuriV0::repo_id(&response.forwarder_id), &forwarder_nuri, "social_query_ended".to_string()).await?; |
||||||
|
|
||||||
|
if !at_origin { |
||||||
|
// getting the privkey of the inbox because we will need it here below to send responses.
|
||||||
|
let from = self.get_profile_replying_to(forwarded_from_profile.as_ref().unwrap())?; |
||||||
|
|
||||||
|
// sending EndOfReplies upstream
|
||||||
|
let to_overlay = NuriV0::from_profile_into_overlay_id(forwarded_from_profile.as_ref().unwrap())?; |
||||||
|
let to_inbox_id = NuriV0::from_inbox_into_id(forwarded_from_inbox.as_ref().unwrap())?; |
||||||
|
let from_forwarder = NuriV0::from_repo_nuri_to_id(from_forwarder.as_ref().unwrap())?; |
||||||
|
let post = InboxPost::new_social_query_response( |
||||||
|
to_overlay, |
||||||
|
to_inbox_id, |
||||||
|
Some(from), |
||||||
|
response.query_id, |
||||||
|
from_forwarder, |
||||||
|
SocialQueryResponseContent::EndOfReplies |
||||||
|
)?; |
||||||
|
self.post_to_inbox(post).await?; |
||||||
|
} |
||||||
|
}
|
||||||
|
} |
||||||
|
SocialQueryResponseContent::Graph(graph) => { |
||||||
|
|
||||||
|
if at_origin { |
||||||
|
|
||||||
|
// insert the triples in the query document
|
||||||
|
let triples: Vec<Triple> = serde_bare::from_slice(&graph)?; |
||||||
|
|
||||||
|
if triples.is_empty() { |
||||||
|
return Err(VerifierError::InvalidResponse); |
||||||
|
} |
||||||
|
|
||||||
|
let overlay_id = self.repos.get(&response.query_id).ok_or(VerifierError::RepoNotFound)?.store.outer_overlay(); |
||||||
|
let nuri_ov = NuriV0::repo_graph_name(&response.query_id, &overlay_id); |
||||||
|
let graph_name = NamedNode::new_unchecked(&nuri_ov); |
||||||
|
|
||||||
|
let quads = triples.into_iter().map(|t| t.in_graph(graph_name.clone()) ).collect(); |
||||||
|
|
||||||
|
// let quad = Quad {
|
||||||
|
// subject: NamedNode::new_unchecked(&nuri).into(),
|
||||||
|
// predicate: NG_ONTOLOGY_CLASS_NAME.clone().into(),
|
||||||
|
// object: Literal::new_simple_literal(primary_class).into(),
|
||||||
|
// graph_name: NamedNode::new_unchecked(&header_branch_nuri).into(),
|
||||||
|
// };
|
||||||
|
let commits = self.prepare_sparql_update(quads, vec![], vec![]).await?; |
||||||
|
|
||||||
|
|
||||||
|
} else { |
||||||
|
|
||||||
|
// we forward upstream
|
||||||
|
|
||||||
|
// getting the privkey of the inbox because we will need it here below to send responses.
|
||||||
|
let from = self.get_profile_replying_to(forwarded_from_profile.as_ref().unwrap())?; |
||||||
|
|
||||||
|
let to_overlay = NuriV0::from_profile_into_overlay_id(forwarded_from_profile.as_ref().unwrap())?; |
||||||
|
let to_inbox_id = NuriV0::from_inbox_into_id(forwarded_from_inbox.as_ref().unwrap())?; |
||||||
|
let from_forwarder = NuriV0::from_repo_nuri_to_id(from_forwarder.as_ref().unwrap())?; |
||||||
|
let post = InboxPost::new_social_query_response( |
||||||
|
to_overlay, |
||||||
|
to_inbox_id, |
||||||
|
Some(from), |
||||||
|
response.query_id, |
||||||
|
from_forwarder, |
||||||
|
SocialQueryResponseContent::Graph(graph) |
||||||
|
)?; |
||||||
|
self.post_to_inbox(post).await?; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
SocialQueryResponseContent::QueryResult(_) | SocialQueryResponseContent::False | SocialQueryResponseContent::True => { |
||||||
|
// not implemented yet
|
||||||
|
unimplemented!(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
_ => unimplemented!() |
||||||
|
} |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue