mirror of
https://github.com/uqbar-dao/nectar.git
synced 2024-12-23 00:21:38 +03:00
contacts: make kimap work in sim-mode, improve FE
This commit is contained in:
parent
5ef0771abd
commit
c24d2b51f9
@ -5,9 +5,22 @@ use kinode_process_lib::{
|
|||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
const ICON: &str = include_str!("icon");
|
const ICON: &str = include_str!("icon");
|
||||||
|
|
||||||
|
#[cfg(not(feature = "simulation-mode"))]
|
||||||
|
const CHAIN_ID: u64 = kimap::KIMAP_CHAIN_ID;
|
||||||
|
#[cfg(feature = "simulation-mode")]
|
||||||
|
const CHAIN_ID: u64 = 31337; // local
|
||||||
|
|
||||||
|
const CHAIN_TIMEOUT: u64 = 60; // 60s
|
||||||
|
|
||||||
|
#[cfg(not(feature = "simulation-mode"))]
|
||||||
|
const KIMAP_ADDRESS: &'static str = kimap::KIMAP_ADDRESS; // optimism
|
||||||
|
#[cfg(feature = "simulation-mode")]
|
||||||
|
const KIMAP_ADDRESS: &str = "0xEce71a05B36CA55B895427cD9a440eEF7Cf3669D";
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
struct Contact(HashMap<String, serde_json::Value>);
|
struct Contact(HashMap<String, serde_json::Value>);
|
||||||
|
|
||||||
@ -17,7 +30,6 @@ struct Contacts(HashMap<NodeId, Contact>);
|
|||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
struct ContactsState {
|
struct ContactsState {
|
||||||
our: Address,
|
our: Address,
|
||||||
kimap: kimap::Kimap,
|
|
||||||
contacts: Contacts,
|
contacts: Contacts,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -25,7 +37,6 @@ impl ContactsState {
|
|||||||
fn new(our: Address) -> Self {
|
fn new(our: Address) -> Self {
|
||||||
get_typed_state(|bytes| serde_json::from_slice(bytes)).unwrap_or(Self {
|
get_typed_state(|bytes| serde_json::from_slice(bytes)).unwrap_or(Self {
|
||||||
our,
|
our,
|
||||||
kimap: kimap::Kimap::default(30),
|
|
||||||
contacts: Contacts(HashMap::new()),
|
contacts: Contacts(HashMap::new()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -96,6 +107,11 @@ fn initialize(our: Address) {
|
|||||||
|
|
||||||
let mut state: ContactsState = ContactsState::new(our);
|
let mut state: ContactsState = ContactsState::new(our);
|
||||||
|
|
||||||
|
let kimap = kimap::Kimap::new(
|
||||||
|
eth::Provider::new(CHAIN_ID, CHAIN_TIMEOUT),
|
||||||
|
eth::Address::from_str(KIMAP_ADDRESS).unwrap(),
|
||||||
|
);
|
||||||
|
|
||||||
let mut http_server = http::server::HttpServer::new(5);
|
let mut http_server = http::server::HttpServer::new(5);
|
||||||
|
|
||||||
// serve the frontend on a secure subdomain
|
// serve the frontend on a secure subdomain
|
||||||
@ -110,10 +126,14 @@ fn initialize(our: Address) {
|
|||||||
http_server.secure_bind_http_path("/ask").unwrap();
|
http_server.secure_bind_http_path("/ask").unwrap();
|
||||||
http_server.secure_bind_ws_path("/").unwrap();
|
http_server.secure_bind_ws_path("/").unwrap();
|
||||||
|
|
||||||
main_loop(&mut state, &mut http_server);
|
main_loop(&mut state, &kimap, &mut http_server);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main_loop(state: &mut ContactsState, http_server: &mut http::server::HttpServer) {
|
fn main_loop(
|
||||||
|
state: &mut ContactsState,
|
||||||
|
kimap: &kimap::Kimap,
|
||||||
|
http_server: &mut http::server::HttpServer,
|
||||||
|
) {
|
||||||
loop {
|
loop {
|
||||||
match await_message() {
|
match await_message() {
|
||||||
Err(_send_error) => {
|
Err(_send_error) => {
|
||||||
@ -131,7 +151,7 @@ fn main_loop(state: &mut ContactsState, http_server: &mut http::server::HttpServ
|
|||||||
if source.node() != state.our.node {
|
if source.node() != state.our.node {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
handle_request(&source, &body, capabilities, state, http_server);
|
handle_request(&source, &body, capabilities, state, kimap, http_server);
|
||||||
}
|
}
|
||||||
_ => continue, // ignore responses
|
_ => continue, // ignore responses
|
||||||
}
|
}
|
||||||
@ -143,6 +163,7 @@ fn handle_request(
|
|||||||
body: &[u8],
|
body: &[u8],
|
||||||
capabilities: Vec<Capability>,
|
capabilities: Vec<Capability>,
|
||||||
state: &mut ContactsState,
|
state: &mut ContactsState,
|
||||||
|
kimap: &kimap::Kimap,
|
||||||
http_server: &mut http::server::HttpServer,
|
http_server: &mut http::server::HttpServer,
|
||||||
) {
|
) {
|
||||||
// source node is ALWAYS ourselves since networking is disabled
|
// source node is ALWAYS ourselves since networking is disabled
|
||||||
@ -152,14 +173,14 @@ fn handle_request(
|
|||||||
|
|
||||||
http_server.handle_request(
|
http_server.handle_request(
|
||||||
server_request,
|
server_request,
|
||||||
|req| handle_http_request(state, &req),
|
|req| handle_http_request(state, kimap, &req),
|
||||||
|_channel_id, _message_type, _blob| {
|
|_channel_id, _message_type, _blob| {
|
||||||
// we don't expect websocket messages
|
// we don't expect websocket messages
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
// if request is not from frontend, check that it has the required capabilities
|
// if request is not from frontend, check that it has the required capabilities
|
||||||
let (response, blob) = handle_contacts_request(state, body, Some(capabilities));
|
let (response, blob) = handle_contacts_request(state, kimap, body, Some(capabilities));
|
||||||
let mut response = Response::new().body(serde_json::to_vec(&response).unwrap());
|
let mut response = Response::new().body(serde_json::to_vec(&response).unwrap());
|
||||||
if let Some(blob) = blob {
|
if let Some(blob) = blob {
|
||||||
response = response.blob(blob);
|
response = response.blob(blob);
|
||||||
@ -172,6 +193,7 @@ fn handle_request(
|
|||||||
/// Handle HTTP requests from our own frontend.
|
/// Handle HTTP requests from our own frontend.
|
||||||
fn handle_http_request(
|
fn handle_http_request(
|
||||||
state: &mut ContactsState,
|
state: &mut ContactsState,
|
||||||
|
kimap: &kimap::Kimap,
|
||||||
http_request: &http::server::IncomingHttpRequest,
|
http_request: &http::server::IncomingHttpRequest,
|
||||||
) -> (http::server::HttpResponse, Option<LazyLoadBlob>) {
|
) -> (http::server::HttpResponse, Option<LazyLoadBlob>) {
|
||||||
match http_request.method().unwrap().as_str() {
|
match http_request.method().unwrap().as_str() {
|
||||||
@ -185,7 +207,7 @@ fn handle_http_request(
|
|||||||
),
|
),
|
||||||
"POST" => {
|
"POST" => {
|
||||||
let blob = get_blob().unwrap();
|
let blob = get_blob().unwrap();
|
||||||
let (response, blob) = handle_contacts_request(state, blob.bytes(), None);
|
let (response, blob) = handle_contacts_request(state, kimap, blob.bytes(), None);
|
||||||
if let contacts::Response::Error(e) = response {
|
if let contacts::Response::Error(e) = response {
|
||||||
return (
|
return (
|
||||||
http::server::HttpResponse::new(http::StatusCode::BAD_REQUEST)
|
http::server::HttpResponse::new(http::StatusCode::BAD_REQUEST)
|
||||||
@ -218,6 +240,7 @@ fn handle_http_request(
|
|||||||
|
|
||||||
fn handle_contacts_request(
|
fn handle_contacts_request(
|
||||||
state: &mut ContactsState,
|
state: &mut ContactsState,
|
||||||
|
kimap: &kimap::Kimap,
|
||||||
request_bytes: &[u8],
|
request_bytes: &[u8],
|
||||||
capabilities: Option<Vec<Capability>>,
|
capabilities: Option<Vec<Capability>>,
|
||||||
) -> (contacts::Response, Option<LazyLoadBlob>) {
|
) -> (contacts::Response, Option<LazyLoadBlob>) {
|
||||||
@ -281,14 +304,14 @@ fn handle_contacts_request(
|
|||||||
)),
|
)),
|
||||||
),
|
),
|
||||||
contacts::Request::AddContact(node) => {
|
contacts::Request::AddContact(node) => {
|
||||||
if let Some((response, blob)) = invalid_node(state, &node) {
|
if let Some((response, blob)) = invalid_node(kimap, &node) {
|
||||||
return (response, blob);
|
return (response, blob);
|
||||||
}
|
}
|
||||||
state.add_contact(node);
|
state.add_contact(node);
|
||||||
(contacts::Response::AddContact, None)
|
(contacts::Response::AddContact, None)
|
||||||
}
|
}
|
||||||
contacts::Request::AddField((node, field, value)) => {
|
contacts::Request::AddField((node, field, value)) => {
|
||||||
if let Some((response, blob)) = invalid_node(state, &node) {
|
if let Some((response, blob)) = invalid_node(kimap, &node) {
|
||||||
return (response, blob);
|
return (response, blob);
|
||||||
}
|
}
|
||||||
let Ok(value) = serde_json::from_str::<serde_json::Value>(&value) else {
|
let Ok(value) = serde_json::from_str::<serde_json::Value>(&value) else {
|
||||||
@ -312,11 +335,10 @@ fn handle_contacts_request(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn invalid_node(
|
fn invalid_node(
|
||||||
state: &ContactsState,
|
kimap: &kimap::Kimap,
|
||||||
node: &str,
|
node: &str,
|
||||||
) -> Option<(contacts::Response, Option<LazyLoadBlob>)> {
|
) -> Option<(contacts::Response, Option<LazyLoadBlob>)> {
|
||||||
if state
|
if kimap
|
||||||
.kimap
|
|
||||||
.get(&node)
|
.get(&node)
|
||||||
.map(|(tba, _, _)| tba != eth::Address::ZERO)
|
.map(|(tba, _, _)| tba != eth::Address::ZERO)
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
<link href="https://api.fontshare.com/v2/css?f[]=clash-display@400,700,500,600,300&display=swap" rel="stylesheet" />
|
<link href="https://api.fontshare.com/v2/css?f[]=clash-display@400,700,500,600,300&display=swap" rel="stylesheet" />
|
||||||
<script src="/our.js"></script>
|
<script src="/our.js"></script>
|
||||||
<script>
|
<script>
|
||||||
document.title = window.our.node + " - contacts";
|
document.title = "contacts - " + window.our.node;
|
||||||
</script>
|
</script>
|
||||||
<style>
|
<style>
|
||||||
h1,
|
h1,
|
||||||
@ -33,14 +33,58 @@
|
|||||||
|
|
||||||
main {
|
main {
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 1fr 1fr 1fr;
|
|
||||||
gap: 20px 20px;
|
|
||||||
grid-auto-flow: row;
|
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
max-width: 960px;
|
max-width: 960px;
|
||||||
min-width: 300px;
|
min-width: 300px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
form.add-contact {
|
||||||
|
max-width: 400px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#contacts {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact:first-of-type {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact {
|
||||||
|
padding: 10px;
|
||||||
|
border: 1px solid var(--tasteful-dark);
|
||||||
|
border-radius: 5px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
display: grid;
|
||||||
|
gap: 10px;
|
||||||
|
grid-auto-flow: row;
|
||||||
|
grid-template-areas:
|
||||||
|
"name name delete"
|
||||||
|
"fields fields fields"
|
||||||
|
"add-field add-field add-field";
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact h3 {
|
||||||
|
grid-area: name;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact ul {
|
||||||
|
grid-area: fields;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact ul li {
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
form.delete-contact {
|
||||||
|
grid-area: delete;
|
||||||
|
}
|
||||||
|
|
||||||
|
form.add-field {
|
||||||
|
grid-area: add-field;
|
||||||
|
max-width: 400px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
@ -48,7 +92,6 @@
|
|||||||
<h1>contacts</h1>
|
<h1>contacts</h1>
|
||||||
<main>
|
<main>
|
||||||
<article id="edit">
|
<article id="edit">
|
||||||
<h2>Contacts</h2>
|
|
||||||
<form id="add-contact">
|
<form id="add-contact">
|
||||||
<input type="text" name="node">
|
<input type="text" name="node">
|
||||||
<button type="submit">add contact</button>
|
<button type="submit">add contact</button>
|
||||||
|
@ -20,9 +20,45 @@ function populate_contacts(contacts) {
|
|||||||
ul.innerHTML = '';
|
ul.innerHTML = '';
|
||||||
Object.entries(contacts).forEach(([node, contact]) => {
|
Object.entries(contacts).forEach(([node, contact]) => {
|
||||||
const li = document.createElement('li');
|
const li = document.createElement('li');
|
||||||
li.innerHTML = `${JSON.stringify(node, undefined, 2)}`;
|
const div = document.createElement('div');
|
||||||
|
div.classList.add('contact');
|
||||||
|
div.innerHTML = `<h3>${node}</h3>
|
||||||
|
<ul>
|
||||||
|
${Object.entries(contact).map(([field, value]) => `<li>${field}: ${value}</li>`).join('')}
|
||||||
|
</ul>
|
||||||
|
<form class="delete-contact" id="${node}">
|
||||||
|
<button type="submit">delete</button>
|
||||||
|
</form>
|
||||||
|
<form class="add-field" id="${node}">
|
||||||
|
<input type="text" name="field" placeholder="Field">
|
||||||
|
<input type="text" name="value" placeholder="Value">
|
||||||
|
<button type="submit">add</button>
|
||||||
|
</form>
|
||||||
|
`;
|
||||||
|
li.appendChild(div);
|
||||||
ul.appendChild(li);
|
ul.appendChild(li);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ul.querySelectorAll('.delete-contact').forEach(form => {
|
||||||
|
form.addEventListener('submit', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
const node = this.getAttribute('id');
|
||||||
|
api_call({
|
||||||
|
"RemoveContact": node
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
ul.querySelectorAll('.add-field').forEach(form => {
|
||||||
|
form.addEventListener('submit', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
const node = this.getAttribute('id');
|
||||||
|
const data = new FormData(e.target);
|
||||||
|
api_call({
|
||||||
|
"AddField": [node, data.get('field'), data.get('value')]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
document.getElementById('add-contact').addEventListener('submit', (e) => {
|
document.getElementById('add-contact').addEventListener('submit', (e) => {
|
||||||
@ -39,6 +75,7 @@ document.getElementById('add-contact').addEventListener('submit', (e) => {
|
|||||||
},
|
},
|
||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body),
|
||||||
}).then(response => {
|
}).then(response => {
|
||||||
|
e.target.reset();
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
return null;
|
return null;
|
||||||
} else {
|
} else {
|
||||||
@ -46,7 +83,6 @@ document.getElementById('add-contact').addEventListener('submit', (e) => {
|
|||||||
}
|
}
|
||||||
}).then(data => {
|
}).then(data => {
|
||||||
if (data === null) {
|
if (data === null) {
|
||||||
e.target.reset();
|
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
alert(JSON.stringify(data));
|
alert(JSON.stringify(data));
|
||||||
|
Loading…
Reference in New Issue
Block a user