mirror of
https://github.com/uqbar-dao/nectar.git
synced 2024-12-22 08:01:47 +03:00
real mvp
This commit is contained in:
parent
4521d82093
commit
5486100e1d
70
Cargo.lock
generated
70
Cargo.lock
generated
@ -78,7 +78,7 @@ name = "alias"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"kinode_process_lib 0.9.1",
|
||||
"kinode_process_lib 0.9.3",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"wit-bindgen",
|
||||
@ -1012,7 +1012,7 @@ dependencies = [
|
||||
"alloy-sol-types",
|
||||
"anyhow",
|
||||
"bincode",
|
||||
"kinode_process_lib 0.9.1",
|
||||
"kinode_process_lib 0.9.3",
|
||||
"process_macros",
|
||||
"rand 0.8.5",
|
||||
"serde",
|
||||
@ -1383,7 +1383,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
"kinode_process_lib 0.9.1",
|
||||
"kinode_process_lib 0.9.3",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"url",
|
||||
@ -1592,7 +1592,7 @@ name = "cat"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"kinode_process_lib 0.9.1",
|
||||
"kinode_process_lib 0.9.3",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"wit-bindgen",
|
||||
@ -1656,7 +1656,7 @@ dependencies = [
|
||||
"alloy-sol-types",
|
||||
"anyhow",
|
||||
"bincode",
|
||||
"kinode_process_lib 0.9.1",
|
||||
"kinode_process_lib 0.9.3",
|
||||
"process_macros",
|
||||
"rand 0.8.5",
|
||||
"serde",
|
||||
@ -1675,7 +1675,7 @@ version = "0.2.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
"kinode_process_lib 0.9.1",
|
||||
"kinode_process_lib 0.9.3",
|
||||
"pleco",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@ -1867,7 +1867,7 @@ checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2"
|
||||
name = "contacts"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"kinode_process_lib 0.9.1",
|
||||
"kinode_process_lib 0.9.3",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"wit-bindgen",
|
||||
@ -2443,7 +2443,7 @@ name = "download"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"kinode_process_lib 0.9.1",
|
||||
"kinode_process_lib 0.9.3",
|
||||
"process_macros",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@ -2455,7 +2455,7 @@ name = "downloads"
|
||||
version = "0.5.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"kinode_process_lib 0.9.1",
|
||||
"kinode_process_lib 0.9.3",
|
||||
"process_macros",
|
||||
"rand 0.8.5",
|
||||
"serde",
|
||||
@ -2492,7 +2492,7 @@ dependencies = [
|
||||
name = "echo"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"kinode_process_lib 0.9.1",
|
||||
"kinode_process_lib 0.9.3",
|
||||
"wit-bindgen",
|
||||
]
|
||||
|
||||
@ -2731,7 +2731,7 @@ version = "0.2.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
"kinode_process_lib 0.9.1",
|
||||
"kinode_process_lib 0.9.3",
|
||||
"process_macros",
|
||||
"rand 0.8.5",
|
||||
"serde",
|
||||
@ -2885,7 +2885,7 @@ dependencies = [
|
||||
name = "get_block"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"kinode_process_lib 0.9.1",
|
||||
"kinode_process_lib 0.9.3",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"wit-bindgen",
|
||||
@ -2950,7 +2950,7 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
|
||||
name = "globe"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"kinode_process_lib 0.9.1",
|
||||
"kinode_process_lib 0.9.3",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"url",
|
||||
@ -3077,7 +3077,7 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
name = "help"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"kinode_process_lib 0.9.1",
|
||||
"kinode_process_lib 0.9.3",
|
||||
"wit-bindgen",
|
||||
]
|
||||
|
||||
@ -3106,7 +3106,7 @@ checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46"
|
||||
name = "hi"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"kinode_process_lib 0.9.1",
|
||||
"kinode_process_lib 0.9.3",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"wit-bindgen",
|
||||
@ -3139,7 +3139,7 @@ version = "0.1.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
"kinode_process_lib 0.9.1",
|
||||
"kinode_process_lib 0.9.3",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"wit-bindgen",
|
||||
@ -3454,7 +3454,7 @@ name = "install"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"kinode_process_lib 0.9.1",
|
||||
"kinode_process_lib 0.9.3",
|
||||
"process_macros",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@ -3631,7 +3631,7 @@ name = "kfetch"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"kinode_process_lib 0.9.1",
|
||||
"kinode_process_lib 0.9.3",
|
||||
"rmp-serde",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@ -3643,7 +3643,7 @@ name = "kill"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"kinode_process_lib 0.9.1",
|
||||
"kinode_process_lib 0.9.3",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"wit-bindgen",
|
||||
@ -3715,9 +3715,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kinode_process_lib"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76c5b69ac1fc0cb457c7714ceb8c0a5bdbee4ee00b837f9f16ea711e902bdfe8"
|
||||
version = "0.9.2"
|
||||
source = "git+https://github.com/kinode-dao/process_lib.git?rev=9ac9e51#9ac9e513c0228f2dcfe8999ed4ca2c38246ee3db"
|
||||
dependencies = [
|
||||
"alloy 0.1.4",
|
||||
"alloy-primitives",
|
||||
@ -3738,8 +3737,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kinode_process_lib"
|
||||
version = "0.9.2"
|
||||
source = "git+https://github.com/kinode-dao/process_lib.git?rev=9ac9e51#9ac9e513c0228f2dcfe8999ed4ca2c38246ee3db"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7722aef4bff0625445fafda89a02f82ce0e16c7def6024e1317ae55a632ad331"
|
||||
dependencies = [
|
||||
"alloy 0.1.4",
|
||||
"alloy-primitives",
|
||||
@ -3840,7 +3840,7 @@ dependencies = [
|
||||
"alloy-sol-types",
|
||||
"anyhow",
|
||||
"hex",
|
||||
"kinode_process_lib 0.9.1",
|
||||
"kinode_process_lib 0.9.3",
|
||||
"rmp-serde",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@ -4067,7 +4067,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
"kinode_process_lib 0.9.1",
|
||||
"kinode_process_lib 0.9.3",
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@ -4237,7 +4237,7 @@ dependencies = [
|
||||
name = "net_diagnostics"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"kinode_process_lib 0.9.1",
|
||||
"kinode_process_lib 0.9.3",
|
||||
"rmp-serde",
|
||||
"serde",
|
||||
"wit-bindgen",
|
||||
@ -4563,7 +4563,7 @@ dependencies = [
|
||||
name = "peer"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"kinode_process_lib 0.9.1",
|
||||
"kinode_process_lib 0.9.3",
|
||||
"rmp-serde",
|
||||
"serde",
|
||||
"wit-bindgen",
|
||||
@ -4573,7 +4573,7 @@ dependencies = [
|
||||
name = "peers"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"kinode_process_lib 0.9.1",
|
||||
"kinode_process_lib 0.9.3",
|
||||
"rmp-serde",
|
||||
"serde",
|
||||
"wit-bindgen",
|
||||
@ -5614,7 +5614,7 @@ dependencies = [
|
||||
"anyhow",
|
||||
"base64 0.22.1",
|
||||
"bincode",
|
||||
"kinode_process_lib 0.9.1",
|
||||
"kinode_process_lib 0.9.3",
|
||||
"rmp-serde",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@ -5832,7 +5832,7 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
||||
name = "state"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"kinode_process_lib 0.9.1",
|
||||
"kinode_process_lib 0.9.3",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"wit-bindgen",
|
||||
@ -6009,7 +6009,7 @@ version = "0.1.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
"kinode_process_lib 0.9.1",
|
||||
"kinode_process_lib 0.9.3",
|
||||
"rand 0.8.5",
|
||||
"regex",
|
||||
"serde",
|
||||
@ -6023,7 +6023,7 @@ version = "0.1.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
"kinode_process_lib 0.9.1",
|
||||
"kinode_process_lib 0.9.3",
|
||||
"process_macros",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@ -6280,7 +6280,7 @@ version = "0.2.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
"kinode_process_lib 0.9.1",
|
||||
"kinode_process_lib 0.9.3",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"wit-bindgen",
|
||||
@ -6611,7 +6611,7 @@ name = "uninstall"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"kinode_process_lib 0.9.1",
|
||||
"kinode_process_lib 0.9.3",
|
||||
"process_macros",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
35
kinode/packages/contacts/api/contacts:sys-v0.wit
Normal file
35
kinode/packages/contacts/api/contacts:sys-v0.wit
Normal file
@ -0,0 +1,35 @@
|
||||
interface contacts {
|
||||
enum capabilities {
|
||||
read-name-only,
|
||||
read,
|
||||
add,
|
||||
remove,
|
||||
}
|
||||
|
||||
variant contacts-request {
|
||||
get-names, // requires read-names-only
|
||||
get-all-contacts, // requires read
|
||||
get-contact(string), // requires read
|
||||
add-contact(string), // requires add
|
||||
// tuple<node, field, value>
|
||||
add-field(tuple<string, string, string>), // requires add
|
||||
remove-contact(string), // requires remove
|
||||
// tuple<node, field>
|
||||
remove-field(tuple<string, string>), // requires remove
|
||||
}
|
||||
|
||||
variant contacts-response {
|
||||
get-names(list<string>),
|
||||
get-all-contacts, // JSON all-contacts dict in blob
|
||||
get-contact, // JSON contact dict in blob
|
||||
add-contact,
|
||||
add-field,
|
||||
remove-contact,
|
||||
remove-field,
|
||||
}
|
||||
}
|
||||
|
||||
world contacts-sys-v0 {
|
||||
import contacts;
|
||||
include process-v0;
|
||||
}
|
@ -7,7 +7,7 @@ edition = "2021"
|
||||
simulation-mode = []
|
||||
|
||||
[dependencies]
|
||||
kinode_process_lib = "0.9.1"
|
||||
kinode_process_lib = "0.9.3"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
wit-bindgen = "0.24.0"
|
||||
|
@ -1,40 +1,85 @@
|
||||
use crate::kinode::process::contacts::{ContactsRequest, ContactsResponse};
|
||||
use kinode_process_lib::{
|
||||
await_message, call_init, eth, get_blob, homepage, http, kernel_types, net, println, Address,
|
||||
LazyLoadBlob, Message, NodeId, ProcessId, Request, Response, SendError, SendErrorKind,
|
||||
await_message, call_init, get_blob, get_typed_state, homepage, http, println, set_state,
|
||||
Address, LazyLoadBlob, Message, NodeId, Response,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
|
||||
const ICON: &str = include_str!("icon");
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
struct Contact(HashMap<String, serde_json::Value>);
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
struct Contacts(HashMap<NodeId, Contact>);
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
struct ContactsState {
|
||||
our: Address,
|
||||
contacts: Contacts,
|
||||
}
|
||||
|
||||
impl ContactsState {
|
||||
fn new(our: Address) -> Self {
|
||||
Self { our }
|
||||
get_typed_state(|bytes| serde_json::from_slice(bytes)).unwrap_or(Self {
|
||||
our,
|
||||
contacts: Contacts(HashMap::new()),
|
||||
})
|
||||
}
|
||||
|
||||
fn save(&self) {
|
||||
set_state(&serde_json::to_vec(&self).expect("Failed to serialize contacts state!"));
|
||||
}
|
||||
|
||||
fn contacts(&self) -> &Contacts {
|
||||
&self.contacts
|
||||
}
|
||||
|
||||
fn get_contact(&self, node: NodeId) -> Option<&Contact> {
|
||||
self.contacts.0.get(&node)
|
||||
}
|
||||
|
||||
fn add_contact(&mut self, node: NodeId) {
|
||||
self.contacts.0.insert(node, Contact(HashMap::new()));
|
||||
}
|
||||
|
||||
fn remove_contact(&mut self, node: NodeId) {
|
||||
self.contacts.0.remove(&node);
|
||||
}
|
||||
|
||||
fn add_field(&mut self, node: NodeId, field: String, value: serde_json::Value) {
|
||||
self.contacts
|
||||
.0
|
||||
.entry(node)
|
||||
.or_insert_with(|| Contact(HashMap::new()))
|
||||
.0
|
||||
.insert(field, value);
|
||||
}
|
||||
|
||||
fn remove_field(&mut self, node: NodeId, field: String) {
|
||||
if let Some(contact) = self.contacts.0.get_mut(&node) {
|
||||
contact.0.remove(&field);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wit_bindgen::generate!({
|
||||
path: "target/wit",
|
||||
world: "process-v0",
|
||||
world: "contacts-sys-v0",
|
||||
generate_unused_types: true,
|
||||
additional_derives: [PartialEq, serde::Deserialize, serde::Serialize],
|
||||
});
|
||||
|
||||
call_init!(initialize);
|
||||
fn initialize(our: Address) {
|
||||
// add ourselves to the homepage
|
||||
homepage::add_to_homepage("Contacts", Some(ICON), Some("/"), None);
|
||||
|
||||
// Grab our state, then enter the main event loop.
|
||||
let mut state: ContactsState = ContactsState::new(our);
|
||||
|
||||
let mut http_server = http::server::HttpServer::new(5);
|
||||
|
||||
// Serve the index.html and other UI files found in pkg/ui at the root path.
|
||||
// Serving securely at `settings-sys` subdomain
|
||||
// serve the frontend on a secure subdomain
|
||||
http_server
|
||||
.serve_ui(
|
||||
&state.our,
|
||||
@ -52,8 +97,8 @@ fn initialize(our: Address) {
|
||||
fn main_loop(state: &mut ContactsState, http_server: &mut http::server::HttpServer) {
|
||||
loop {
|
||||
match await_message() {
|
||||
Err(send_error) => {
|
||||
println!("got send error: {send_error:?}");
|
||||
Err(_send_error) => {
|
||||
// ignore send errors, local-only process
|
||||
continue;
|
||||
}
|
||||
Ok(Message::Request {
|
||||
@ -84,44 +129,25 @@ fn handle_request(
|
||||
body: &[u8],
|
||||
state: &mut ContactsState,
|
||||
http_server: &mut http::server::HttpServer,
|
||||
) -> SettingsResponse {
|
||||
) -> Option<ContactsResponse> {
|
||||
// source node is ALWAYS ourselves since networking is disabled
|
||||
if source.process == "http_server:distro:sys" {
|
||||
// receive HTTP requests and websocket connection messages from our server
|
||||
let server_request = http_server
|
||||
.parse_request(body)
|
||||
.map_err(|_| SettingsError::MalformedRequest)?;
|
||||
let server_request = http_server.parse_request(body).unwrap();
|
||||
|
||||
http_server.handle_request(
|
||||
server_request,
|
||||
|req| {
|
||||
let result = handle_http_request(state, &req);
|
||||
match result {
|
||||
Ok((resp, blob)) => (resp, blob),
|
||||
Err(e) => {
|
||||
println!("error handling HTTP request: {e}");
|
||||
(
|
||||
http::server::HttpResponse {
|
||||
status: 500,
|
||||
headers: HashMap::new(),
|
||||
},
|
||||
Some(LazyLoadBlob {
|
||||
mime: Some("application/text".to_string()),
|
||||
bytes: e.to_string().as_bytes().to_vec(),
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
|req| handle_http_request(state, &req),
|
||||
|_channel_id, _message_type, _blob| {
|
||||
// we don't expect websocket messages
|
||||
},
|
||||
);
|
||||
Ok(None)
|
||||
None
|
||||
} else {
|
||||
let settings_request = serde_json::from_slice::<SettingsRequest>(body)
|
||||
.map_err(|_| SettingsError::MalformedRequest)?;
|
||||
handle_settings_request(state, settings_request)
|
||||
// let settings_request = serde_json::from_slice::<SettingsRequest>(body)
|
||||
// .map_err(|_| SettingsError::MalformedRequest)?;
|
||||
// handle_settings_request(state, settings_request)
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
@ -129,163 +155,62 @@ fn handle_request(
|
||||
fn handle_http_request(
|
||||
state: &mut ContactsState,
|
||||
http_request: &http::server::IncomingHttpRequest,
|
||||
) -> anyhow::Result<(http::server::HttpResponse, Option<LazyLoadBlob>)> {
|
||||
match http_request.method()?.as_str() {
|
||||
) -> (http::server::HttpResponse, Option<LazyLoadBlob>) {
|
||||
match http_request.method().unwrap().as_str() {
|
||||
"GET" => {
|
||||
state.fetch()?;
|
||||
Ok((
|
||||
// state.fetch().unwrap();
|
||||
(
|
||||
http::server::HttpResponse::new(http::StatusCode::OK)
|
||||
.header("Content-Type", "application/json"),
|
||||
Some(LazyLoadBlob::new(
|
||||
Some("application/json"),
|
||||
serde_json::to_vec(&state)?,
|
||||
serde_json::to_vec(&state).unwrap(),
|
||||
)),
|
||||
))
|
||||
)
|
||||
}
|
||||
"POST" => {
|
||||
let Some(blob) = get_blob() else {
|
||||
return Err(anyhow::anyhow!("malformed request"));
|
||||
};
|
||||
let request = serde_json::from_slice::<SettingsRequest>(&blob.bytes)?;
|
||||
let response = handle_settings_request(state, request);
|
||||
Ok((
|
||||
let blob = get_blob().unwrap();
|
||||
let request = serde_json::from_slice::<ContactsRequest>(&blob.bytes).unwrap();
|
||||
let response = handle_contacts_request(state, request);
|
||||
let response: Option<String> = Some("ok".to_string());
|
||||
(
|
||||
http::server::HttpResponse::new(http::StatusCode::OK)
|
||||
.header("Content-Type", "application/json"),
|
||||
match response {
|
||||
Ok(Some(data)) => Some(LazyLoadBlob::new(
|
||||
Some(data) => Some(LazyLoadBlob::new(
|
||||
Some("application/json"),
|
||||
serde_json::to_vec(&data)?,
|
||||
)),
|
||||
Ok(None) => None,
|
||||
Err(e) => Some(LazyLoadBlob::new(
|
||||
Some("application/json"),
|
||||
serde_json::to_vec(&e)?,
|
||||
serde_json::to_vec(&data).unwrap(),
|
||||
)),
|
||||
None => None,
|
||||
},
|
||||
))
|
||||
)
|
||||
}
|
||||
// Any other method will be rejected.
|
||||
_ => Ok((
|
||||
_ => (
|
||||
http::server::HttpResponse::new(http::StatusCode::METHOD_NOT_ALLOWED),
|
||||
None,
|
||||
)),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_settings_request(
|
||||
state: &mut SettingsState,
|
||||
request: SettingsRequest,
|
||||
) -> SettingsResponse {
|
||||
fn handle_contacts_request(
|
||||
state: &mut ContactsState,
|
||||
request: ContactsRequest,
|
||||
) -> ContactsResponse {
|
||||
match request {
|
||||
SettingsRequest::Hi {
|
||||
node,
|
||||
content,
|
||||
timeout,
|
||||
} => {
|
||||
if let Err(SendError { kind, .. }) = Request::to((&node, "net", "distro", "sys"))
|
||||
.body(content.into_bytes())
|
||||
.send_and_await_response(timeout)
|
||||
.unwrap()
|
||||
{
|
||||
match kind {
|
||||
SendErrorKind::Timeout => {
|
||||
println!("message to {node} timed out");
|
||||
return Err(SettingsError::HiTimeout);
|
||||
}
|
||||
SendErrorKind::Offline => {
|
||||
println!("{node} is offline or does not exist");
|
||||
return Err(SettingsError::HiOffline);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
SettingsRequest::PeerId(node) => {
|
||||
// get peer info
|
||||
match Request::to(("our", "net", "distro", "sys"))
|
||||
.body(rmp_serde::to_vec(&net::NetAction::GetPeer(node)).unwrap())
|
||||
.send_and_await_response(30)
|
||||
.unwrap()
|
||||
{
|
||||
Ok(msg) => match rmp_serde::from_slice::<net::NetResponse>(msg.body()) {
|
||||
Ok(net::NetResponse::Peer(Some(peer))) => {
|
||||
println!("got peer info: {peer:?}");
|
||||
return Ok(Some(SettingsData::PeerId(peer)));
|
||||
}
|
||||
Ok(net::NetResponse::Peer(None)) => {
|
||||
println!("peer not found");
|
||||
return Ok(None);
|
||||
}
|
||||
_ => {
|
||||
return Err(SettingsError::KernelNonresponsive);
|
||||
}
|
||||
},
|
||||
Err(_) => {
|
||||
return Err(SettingsError::KernelNonresponsive);
|
||||
}
|
||||
}
|
||||
}
|
||||
SettingsRequest::EthConfig(action) => {
|
||||
match Request::to(("our", "eth", "distro", "sys"))
|
||||
.body(serde_json::to_vec(&action).unwrap())
|
||||
.send_and_await_response(30)
|
||||
.unwrap()
|
||||
{
|
||||
Ok(msg) => match serde_json::from_slice::<eth::EthConfigResponse>(msg.body()) {
|
||||
Ok(eth::EthConfigResponse::PermissionDenied) => {
|
||||
return Err(SettingsError::KernelNonresponsive);
|
||||
}
|
||||
Ok(other) => {
|
||||
println!("eth config action succeeded: {other:?}");
|
||||
}
|
||||
Err(_) => {
|
||||
return Err(SettingsError::KernelNonresponsive);
|
||||
}
|
||||
},
|
||||
Err(_) => {
|
||||
return Err(SettingsError::KernelNonresponsive);
|
||||
}
|
||||
}
|
||||
}
|
||||
SettingsRequest::Shutdown => {
|
||||
// shutdown the node IMMEDIATELY!
|
||||
Request::to(("our", "kernel", "distro", "sys"))
|
||||
.body(serde_json::to_vec(&kernel_types::KernelCommand::Shutdown).unwrap())
|
||||
.send()
|
||||
.unwrap();
|
||||
}
|
||||
SettingsRequest::KillProcess(pid) => {
|
||||
// kill a process
|
||||
if let Err(_) = Request::to(("our", "kernel", "distro", "sys"))
|
||||
.body(serde_json::to_vec(&kernel_types::KernelCommand::KillProcess(pid)).unwrap())
|
||||
.send_and_await_response(30)
|
||||
.unwrap()
|
||||
{
|
||||
return SettingsResponse::Err(SettingsError::KernelNonresponsive);
|
||||
}
|
||||
}
|
||||
SettingsRequest::SetStylesheet(stylesheet) => {
|
||||
let Ok(()) = kinode_process_lib::vfs::File {
|
||||
path: "/homepage:sys/pkg/kinode.css".to_string(),
|
||||
timeout: 5,
|
||||
}
|
||||
.write(stylesheet.as_bytes()) else {
|
||||
return SettingsResponse::Err(SettingsError::KernelNonresponsive);
|
||||
};
|
||||
Request::to(("our", "homepage", "homepage", "sys"))
|
||||
.body(
|
||||
serde_json::json!({ "SetStylesheet": stylesheet })
|
||||
.to_string()
|
||||
.as_bytes(),
|
||||
)
|
||||
.send()
|
||||
.unwrap();
|
||||
state.stylesheet = Some(stylesheet);
|
||||
return SettingsResponse::Ok(None);
|
||||
}
|
||||
ContactsRequest::GetNames => ContactsResponse::GetNames(
|
||||
state
|
||||
.contacts()
|
||||
.0
|
||||
.keys()
|
||||
.map(|node| node.to_string())
|
||||
.collect(),
|
||||
),
|
||||
ContactsRequest::GetAllContacts => ContactsResponse::GetAllContacts,
|
||||
ContactsRequest::GetContact(node) => ContactsResponse::GetContact,
|
||||
ContactsRequest::AddContact(node) => ContactsResponse::AddContact,
|
||||
ContactsRequest::AddField((node, field, value)) => ContactsResponse::AddField,
|
||||
ContactsRequest::RemoveContact(node) => ContactsResponse::RemoveContact,
|
||||
ContactsRequest::RemoveField((node, field)) => ContactsResponse::RemoveField,
|
||||
}
|
||||
|
||||
state.fetch().map_err(|_| SettingsError::StateFetchFailed)?;
|
||||
SettingsResponse::Ok(None)
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Contacts",
|
||||
"description": "Store and manage your contacts.",
|
||||
"description": "Save and manage your Kinode OS contacts.",
|
||||
"image": "",
|
||||
"properties": {
|
||||
"package_name": "contacts",
|
||||
|
@ -6,13 +6,10 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<link rel="stylesheet" href="/kinode.css">
|
||||
<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>
|
||||
document.title = window.our.node + " - settings";
|
||||
document.title = window.our.node + " - contacts";
|
||||
</script>
|
||||
<style>
|
||||
h1,
|
||||
@ -40,195 +37,17 @@
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
gap: 20px 20px;
|
||||
grid-auto-flow: row;
|
||||
grid-template-areas:
|
||||
"diagnostics diagnostics diagnostics"
|
||||
"node-info pings pings"
|
||||
"eth-rpc-providers eth-rpc-providers eth-rpc-settings"
|
||||
"kernel kernel kernel"
|
||||
"kinode-css kinode-css kinode-css";
|
||||
padding: 20px;
|
||||
max-width: 960px;
|
||||
min-width: 300px;
|
||||
}
|
||||
|
||||
article#net-diagnostics {
|
||||
grid-area: diagnostics;
|
||||
}
|
||||
|
||||
p#diagnostics,
|
||||
p#peer-pki-response,
|
||||
p#peer-ping-response {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
article#node-info {
|
||||
grid-area: node-info;
|
||||
word-wrap: break-word;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
#shutdown {
|
||||
background-color: var(--ansi-red)
|
||||
}
|
||||
|
||||
#shutdown:hover {
|
||||
background-color: var(--maroon);
|
||||
}
|
||||
|
||||
article#pings {
|
||||
grid-area: pings;
|
||||
}
|
||||
|
||||
article#eth-rpc-providers {
|
||||
grid-area: eth-rpc-providers;
|
||||
}
|
||||
|
||||
article#eth-rpc-settings {
|
||||
grid-area: eth-rpc-settings;
|
||||
}
|
||||
|
||||
article#kernel {
|
||||
grid-area: kernel;
|
||||
}
|
||||
|
||||
article#kinode-css {
|
||||
grid-area: kinode-css;
|
||||
}
|
||||
|
||||
textarea#stylesheet-editor {
|
||||
width: 100%;
|
||||
min-width: 300px;
|
||||
min-height: 400px;
|
||||
}
|
||||
|
||||
div#provider-edits {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 20px 20px;
|
||||
grid-auto-flow: row;
|
||||
}
|
||||
|
||||
article {
|
||||
border: 1px solid #444;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||
max-height: 600px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
li {
|
||||
padding: 8px;
|
||||
margin-bottom: 6px;
|
||||
border-radius: 4px;
|
||||
word-wrap: break-word;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
#process-map li p:first-child {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#process-map li ul {
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
#process-map li ul li {
|
||||
margin-bottom: 1px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
button.kill-process {
|
||||
padding: 3px 6px;
|
||||
margin: 10px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>system diagnostics & settings</h1>
|
||||
<h1>contacts</h1>
|
||||
<main>
|
||||
<article id="net-diagnostics">
|
||||
<h2>networking diagnostics</h2>
|
||||
<p id="diagnostics"></p>
|
||||
</article>
|
||||
|
||||
<article id="node-info">
|
||||
<h2>node info</h2>
|
||||
<p id="node-name"></p>
|
||||
<p id="net-key"></p>
|
||||
<p id="ip-ports"></p>
|
||||
<p id="routers"></p>
|
||||
<button id="shutdown">shut down node(!)</button>
|
||||
</article>
|
||||
|
||||
<article id="pings">
|
||||
<h2>fetch PKI data</h2>
|
||||
<form id="get-peer-pki">
|
||||
<input type="text" name="peer" placeholder="peer-name.os">
|
||||
<button type="submit">get peer info</button>
|
||||
</form>
|
||||
<p id="peer-pki-response"></p>
|
||||
<h2>ping a node</h2>
|
||||
<form id="ping-peer">
|
||||
<input type="text" name="peer" placeholder="peer-name.os">
|
||||
<input type="text" name="content" placeholder="message">
|
||||
<input type="number" name="timeout" placeholder="timeout (seconds)">
|
||||
<button type="submit">ping</button>
|
||||
</form>
|
||||
<p id="peer-ping-response"></p>
|
||||
</article>
|
||||
|
||||
<article id="eth-rpc-providers">
|
||||
<h2>ETH RPC providers</h2>
|
||||
<div id="provider-edits">
|
||||
<form id="add-eth-provider">
|
||||
<input type="number" name="chain-id" placeholder="1">
|
||||
<input type="text" name="rpc-url" placeholder="wss://rpc-url.com">
|
||||
<button type="submit">add provider</button>
|
||||
</form>
|
||||
<form id="remove-eth-provider">
|
||||
<input type="number" name="chain-id" placeholder="1">
|
||||
<input type="text" name="rpc-url" placeholder="wss://rpc-url.com">
|
||||
<button type="submit">remove provider</button>
|
||||
</form>
|
||||
</div>
|
||||
<ul id="providers"></ul>
|
||||
</article>
|
||||
|
||||
<article id="eth-rpc-settings">
|
||||
<h2>ETH RPC settings</h2>
|
||||
<p id="public"></p>
|
||||
<div>
|
||||
<p>nodes allowed to connect:</p>
|
||||
<ul id="allowed-nodes"></ul>
|
||||
</div>
|
||||
<div>
|
||||
<p>nodes banned from connecting:</p>
|
||||
<ul id="denied-nodes"></ul>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article id="kernel">
|
||||
<h2>running processes</h2>
|
||||
<ul id="process-map"></ul>
|
||||
</article>
|
||||
|
||||
<article id="kinode-css">
|
||||
<h2>stylesheet editor</h2>
|
||||
<textarea id="stylesheet-editor"></textarea>
|
||||
<button id="save-stylesheet">update kinode.css</button>
|
||||
</article>
|
||||
|
||||
|
||||
<script src="/settings:settings:sys/script.js"></script>
|
||||
<script src="/contacts:contacts:sys/script.js"></script>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
|
@ -1,16 +1,16 @@
|
||||
const APP_PATH = '/settings:settings:sys/ask';
|
||||
const APP_PATH = '/contacts:contacts:sys/';
|
||||
|
||||
// Fetch initial data and populate the UI
|
||||
function init() {
|
||||
fetch(APP_PATH)
|
||||
fetch(APP_PATH + 'get')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
populate(data);
|
||||
});
|
||||
}
|
||||
|
||||
function api_call(body) {
|
||||
fetch(APP_PATH, {
|
||||
function api_call(path, body) {
|
||||
fetch(APP_PATH + path, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
@ -19,302 +19,19 @@ function api_call(body) {
|
||||
});
|
||||
}
|
||||
|
||||
function shutdown() {
|
||||
api_call("Shutdown");
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
function populate(data) {
|
||||
populate_node_info(data.identity);
|
||||
populate_net_diagnostics(data.diagnostics);
|
||||
populate_eth_rpc_providers(data.eth_rpc_providers);
|
||||
populate_eth_rpc_settings(data.eth_rpc_access_settings);
|
||||
populate_process_map(data.process_map);
|
||||
populate_stylesheet_editor(data.stylesheet);
|
||||
}
|
||||
|
||||
function populate_node_info(identity) {
|
||||
document.getElementById('node-name').innerText = identity.name;
|
||||
document.getElementById('net-key').innerText = identity.networking_key;
|
||||
if (identity.ws_routing) {
|
||||
document.getElementById('ip-ports').innerText = identity.ws_routing;
|
||||
} else {
|
||||
document.getElementById('ip-ports').style.display = 'none';
|
||||
}
|
||||
if (identity.routers) {
|
||||
document.getElementById('routers').innerText = identity.routers;
|
||||
} else {
|
||||
document.getElementById('routers').style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
function populate_net_diagnostics(diagnostics) {
|
||||
document.getElementById('diagnostics').innerText = diagnostics;
|
||||
}
|
||||
|
||||
function populate_eth_rpc_providers(providers) {
|
||||
const ul = document.getElementById('providers');
|
||||
ul.innerHTML = '';
|
||||
providers.forEach(provider => {
|
||||
const li = document.createElement('li');
|
||||
li.innerHTML = `${JSON.stringify(provider, undefined, 2)}`;
|
||||
ul.appendChild(li);
|
||||
});
|
||||
}
|
||||
|
||||
function populate_eth_rpc_settings(settings) {
|
||||
if (settings.public) {
|
||||
document.getElementById('public').innerText = 'status: public';
|
||||
document.getElementById('allowed-nodes').style.display = 'none';
|
||||
} else {
|
||||
document.getElementById('public').innerText = 'status: private';
|
||||
const ul = document.getElementById('allowed-nodes');
|
||||
ul.innerHTML = '';
|
||||
if (settings.allow.length === 0) {
|
||||
const li = document.createElement('li');
|
||||
li.innerHTML = `<li>(none)</li>`;
|
||||
ul.appendChild(li);
|
||||
} else {
|
||||
settings.allow.forEach(allowed_node => {
|
||||
const li = document.createElement('li');
|
||||
li.innerHTML = `<li>${allowed_node}</li>`;
|
||||
ul.appendChild(li);
|
||||
});
|
||||
}
|
||||
}
|
||||
const ul = document.getElementById('denied-nodes');
|
||||
ul.innerHTML = '';
|
||||
if (settings.deny.length === 0) {
|
||||
const li = document.createElement('li');
|
||||
li.innerHTML = `<li>(none)</li>`;
|
||||
ul.appendChild(li);
|
||||
} else {
|
||||
settings.deny.forEach(denied_node => {
|
||||
const li = document.createElement('li');
|
||||
li.innerHTML = `<li>${denied_node}</li>`;
|
||||
ul.appendChild(li);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function populate_process_map(process_map) {
|
||||
// apps we don't want user to kill, also runtime modules that cannot be killed
|
||||
const do_not_kill = [
|
||||
'settings:setting:sys',
|
||||
'main:app_store:sys',
|
||||
'net:distro:sys',
|
||||
'kernel:distro:sys',
|
||||
'kv:distro:sys',
|
||||
'sqlite:distro:sys',
|
||||
'eth:distro:sys',
|
||||
'vfs:distro:sys',
|
||||
'state:distro:sys',
|
||||
'kns_indexer:kns_indexer:sys',
|
||||
'http_client:distro:sys',
|
||||
'http_server:distro:sys',
|
||||
'terminal:terminal:sys',
|
||||
'timer:distro:sys',
|
||||
];
|
||||
|
||||
const ul = document.getElementById('process-map');
|
||||
ul.innerHTML = '';
|
||||
Object.entries(process_map).forEach(([id, process]) => {
|
||||
const li = document.createElement('li');
|
||||
|
||||
const toggleButton = document.createElement('button');
|
||||
toggleButton.textContent = `${id}`;
|
||||
toggleButton.onclick = function () {
|
||||
const details = this.nextElementSibling;
|
||||
details.style.display = details.style.display === 'none' ? 'block' : 'none';
|
||||
};
|
||||
li.appendChild(toggleButton);
|
||||
|
||||
const detailsDiv = document.createElement('div');
|
||||
detailsDiv.style.display = 'none';
|
||||
|
||||
if (!do_not_kill.includes(id)) {
|
||||
const killButton = document.createElement('button');
|
||||
killButton.className = 'kill-process';
|
||||
killButton.setAttribute('data-id', id);
|
||||
killButton.textContent = 'kill';
|
||||
detailsDiv.appendChild(killButton);
|
||||
}
|
||||
|
||||
const publicInfo = document.createElement('p');
|
||||
publicInfo.textContent = `public: ${process.public}`;
|
||||
detailsDiv.appendChild(publicInfo);
|
||||
|
||||
const onExit = document.createElement('p');
|
||||
onExit.textContent = `on_exit: ${process.on_exit}`;
|
||||
detailsDiv.appendChild(onExit);
|
||||
|
||||
if (process.wit_version) {
|
||||
const witVersion = document.createElement('p');
|
||||
witVersion.textContent = `wit_version: ${process.wit_version}`;
|
||||
detailsDiv.appendChild(witVersion);
|
||||
}
|
||||
|
||||
if (process.wasm_bytes_handle) {
|
||||
const wasmBytesHandle = document.createElement('p');
|
||||
wasmBytesHandle.textContent = `wasm_bytes_handle: ${process.wasm_bytes_handle}`;
|
||||
detailsDiv.appendChild(wasmBytesHandle);
|
||||
}
|
||||
|
||||
const capsList = document.createElement('ul');
|
||||
process.capabilities.forEach(cap => {
|
||||
const capLi = document.createElement('li');
|
||||
capLi.textContent = `${cap.issuer}(${JSON.stringify(JSON.parse(cap.params), null, 2)})`;
|
||||
capsList.appendChild(capLi);
|
||||
});
|
||||
detailsDiv.appendChild(capsList);
|
||||
|
||||
li.appendChild(detailsDiv);
|
||||
ul.appendChild(li);
|
||||
});
|
||||
document.querySelectorAll('.kill-process').forEach(button => {
|
||||
button.addEventListener('click', () => {
|
||||
api_call({ "KillProcess": button.getAttribute('data-id') });
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function populate_stylesheet_editor(stylesheet) {
|
||||
document.getElementById('stylesheet-editor').value = stylesheet;
|
||||
}
|
||||
|
||||
function save_stylesheet() {
|
||||
const stylesheet = document.getElementById('stylesheet-editor').value;
|
||||
api_call({ "SetStylesheet": stylesheet });
|
||||
console.log(data);
|
||||
}
|
||||
|
||||
// Call init to start the application
|
||||
init();
|
||||
|
||||
// Setup event listeners
|
||||
document.getElementById('shutdown').addEventListener('click', shutdown);
|
||||
|
||||
document.getElementById('save-stylesheet').addEventListener('click', save_stylesheet);
|
||||
|
||||
document.getElementById('get-peer-pki').addEventListener('submit', (e) => {
|
||||
e.preventDefault();
|
||||
const data = new FormData(e.target);
|
||||
const body = {
|
||||
"PeerId": data.get('peer'),
|
||||
};
|
||||
fetch(APP_PATH, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
}).then(response => response.json())
|
||||
.then(data => {
|
||||
if (data === null) {
|
||||
document.getElementById('peer-pki-response').innerText = "no pki data for peer";
|
||||
} else {
|
||||
e.target.reset();
|
||||
document.getElementById('peer-pki-response').innerText = JSON.stringify(data, undefined, 2);
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
document.getElementById('ping-peer').addEventListener('submit', (e) => {
|
||||
e.preventDefault();
|
||||
const data = new FormData(e.target);
|
||||
const body = {
|
||||
"Hi": {
|
||||
node: data.get('peer'),
|
||||
content: data.get('content'),
|
||||
timeout: Number(data.get('timeout')),
|
||||
}
|
||||
};
|
||||
fetch(APP_PATH, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
}).then(response => response.json())
|
||||
.then(data => {
|
||||
if (data === null) {
|
||||
e.target.reset();
|
||||
document.getElementById('peer-ping-response').innerText = "ping successful!";
|
||||
} else if (data === "HiTimeout") {
|
||||
document.getElementById('peer-ping-response').innerText = "node timed out";
|
||||
} else if (data === "HiOffline") {
|
||||
document.getElementById('peer-ping-response').innerText = "node is offline";
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
document.getElementById('add-eth-provider').addEventListener('submit', (e) => {
|
||||
e.preventDefault();
|
||||
const data = new FormData(e.target);
|
||||
const rpc_url = data.get('rpc-url');
|
||||
// validate rpc url
|
||||
if (!rpc_url.startsWith('wss://') && !rpc_url.startsWith('ws://')) {
|
||||
alert('Invalid RPC URL');
|
||||
return;
|
||||
}
|
||||
const body = {
|
||||
"EthConfig": {
|
||||
"AddProvider": {
|
||||
chain_id: Number(data.get('chain-id')),
|
||||
trusted: false,
|
||||
provider: { "RpcUrl": rpc_url },
|
||||
}
|
||||
}
|
||||
};
|
||||
fetch(APP_PATH, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
}).then(response => response.json())
|
||||
.then(data => {
|
||||
if (data === null) {
|
||||
e.target.reset();
|
||||
return;
|
||||
} else {
|
||||
alert(data);
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
document.getElementById('remove-eth-provider').addEventListener('submit', (e) => {
|
||||
e.preventDefault();
|
||||
const data = new FormData(e.target);
|
||||
const body = {
|
||||
"EthConfig": {
|
||||
"RemoveProvider": [Number(data.get('chain-id')), data.get('rpc-url')]
|
||||
}
|
||||
};
|
||||
fetch(APP_PATH, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
}).then(response => response.json())
|
||||
.then(data => {
|
||||
if (data === null) {
|
||||
e.target.reset();
|
||||
return;
|
||||
} else {
|
||||
alert(data);
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
// Setup WebSocket connection
|
||||
const wsProtocol = location.protocol === 'https:' ? 'wss://' : 'ws://';
|
||||
const ws = new WebSocket(wsProtocol + location.host + "/settings:settings:sys/");
|
||||
ws.onmessage = event => {
|
||||
const data = JSON.parse(event.data);
|
||||
console.log(data);
|
||||
populate(data);
|
||||
};
|
||||
// const wsProtocol = location.protocol === 'https:' ? 'wss://' : 'ws://';
|
||||
// const ws = new WebSocket(wsProtocol + location.host + "/settings:settings:sys/");
|
||||
// ws.onmessage = event => {
|
||||
// const data = JSON.parse(event.data);
|
||||
// console.log(data);
|
||||
// populate(data);
|
||||
// };
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user