mirror of
https://github.com/uqbar-dao/nectar.git
synced 2024-12-23 00:21:38 +03:00
Merge branch 'develop' into dr/move-app-store-to-ssd
This commit is contained in:
commit
7b59e50e33
91
Cargo.lock
generated
91
Cargo.lock
generated
@ -78,7 +78,7 @@ name = "alias"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"kinode_process_lib 0.9.4",
|
||||
"kinode_process_lib 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"wit-bindgen",
|
||||
@ -1012,7 +1012,7 @@ dependencies = [
|
||||
"alloy-sol-types",
|
||||
"anyhow",
|
||||
"bincode",
|
||||
"kinode_process_lib 0.9.4",
|
||||
"kinode_process_lib 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"process_macros",
|
||||
"rand 0.8.5",
|
||||
"serde",
|
||||
@ -1383,7 +1383,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
"kinode_process_lib 0.9.4",
|
||||
"kinode_process_lib 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"url",
|
||||
@ -1592,7 +1592,7 @@ name = "cat"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"kinode_process_lib 0.9.4",
|
||||
"kinode_process_lib 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"wit-bindgen",
|
||||
@ -1656,7 +1656,7 @@ dependencies = [
|
||||
"alloy-sol-types",
|
||||
"anyhow",
|
||||
"bincode",
|
||||
"kinode_process_lib 0.9.4",
|
||||
"kinode_process_lib 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"process_macros",
|
||||
"rand 0.8.5",
|
||||
"serde",
|
||||
@ -1675,7 +1675,7 @@ version = "0.2.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
"kinode_process_lib 0.9.4",
|
||||
"kinode_process_lib 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pleco",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@ -1863,6 +1863,17 @@ version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2"
|
||||
|
||||
[[package]]
|
||||
name = "contacts"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"kinode_process_lib 0.9.4 (git+https://github.com/kinode-dao/process_lib?rev=088a549)",
|
||||
"process_macros",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"wit-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "convert_case"
|
||||
version = "0.4.0"
|
||||
@ -2433,7 +2444,7 @@ name = "download"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"kinode_process_lib 0.9.4",
|
||||
"kinode_process_lib 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"process_macros",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@ -2445,7 +2456,7 @@ name = "downloads"
|
||||
version = "0.5.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"kinode_process_lib 0.9.4",
|
||||
"kinode_process_lib 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"process_macros",
|
||||
"rand 0.8.5",
|
||||
"serde",
|
||||
@ -2482,7 +2493,7 @@ dependencies = [
|
||||
name = "echo"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"kinode_process_lib 0.9.4",
|
||||
"kinode_process_lib 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wit-bindgen",
|
||||
]
|
||||
|
||||
@ -2721,7 +2732,7 @@ version = "0.2.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
"kinode_process_lib 0.9.4",
|
||||
"kinode_process_lib 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"process_macros",
|
||||
"rand 0.8.5",
|
||||
"serde",
|
||||
@ -2875,7 +2886,7 @@ dependencies = [
|
||||
name = "get_block"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"kinode_process_lib 0.9.4",
|
||||
"kinode_process_lib 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"wit-bindgen",
|
||||
@ -2940,7 +2951,7 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
|
||||
name = "globe"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"kinode_process_lib 0.9.4",
|
||||
"kinode_process_lib 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"url",
|
||||
@ -3067,7 +3078,7 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
name = "help"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"kinode_process_lib 0.9.4",
|
||||
"kinode_process_lib 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wit-bindgen",
|
||||
]
|
||||
|
||||
@ -3096,7 +3107,7 @@ checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46"
|
||||
name = "hi"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"kinode_process_lib 0.9.4",
|
||||
"kinode_process_lib 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"wit-bindgen",
|
||||
@ -3129,7 +3140,7 @@ version = "0.1.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
"kinode_process_lib 0.9.4",
|
||||
"kinode_process_lib 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"wit-bindgen",
|
||||
@ -3444,7 +3455,7 @@ name = "install"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"kinode_process_lib 0.9.4",
|
||||
"kinode_process_lib 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"process_macros",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@ -3621,7 +3632,7 @@ name = "kfetch"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"kinode_process_lib 0.9.4",
|
||||
"kinode_process_lib 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rmp-serde",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@ -3633,7 +3644,7 @@ name = "kill"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"kinode_process_lib 0.9.4",
|
||||
"kinode_process_lib 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"wit-bindgen",
|
||||
@ -3749,6 +3760,28 @@ dependencies = [
|
||||
"wit-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kinode_process_lib"
|
||||
version = "0.9.4"
|
||||
source = "git+https://github.com/kinode-dao/process_lib?rev=088a549#088a5497257eada697e0869d6a8d7e9ef5e620f6"
|
||||
dependencies = [
|
||||
"alloy 0.1.4",
|
||||
"alloy-primitives",
|
||||
"alloy-sol-macro",
|
||||
"alloy-sol-types",
|
||||
"anyhow",
|
||||
"bincode",
|
||||
"http 1.1.0",
|
||||
"mime_guess",
|
||||
"rand 0.8.5",
|
||||
"rmp-serde",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"url",
|
||||
"wit-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kit"
|
||||
version = "0.7.7"
|
||||
@ -3794,7 +3827,7 @@ dependencies = [
|
||||
"alloy-sol-types",
|
||||
"anyhow",
|
||||
"hex",
|
||||
"kinode_process_lib 0.9.4",
|
||||
"kinode_process_lib 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rmp-serde",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@ -4023,7 +4056,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
"kinode_process_lib 0.9.4",
|
||||
"kinode_process_lib 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@ -4193,7 +4226,7 @@ dependencies = [
|
||||
name = "net_diagnostics"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"kinode_process_lib 0.9.4",
|
||||
"kinode_process_lib 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rmp-serde",
|
||||
"serde",
|
||||
"wit-bindgen",
|
||||
@ -4519,7 +4552,7 @@ dependencies = [
|
||||
name = "peer"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"kinode_process_lib 0.9.4",
|
||||
"kinode_process_lib 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rmp-serde",
|
||||
"serde",
|
||||
"wit-bindgen",
|
||||
@ -4529,7 +4562,7 @@ dependencies = [
|
||||
name = "peers"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"kinode_process_lib 0.9.4",
|
||||
"kinode_process_lib 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rmp-serde",
|
||||
"serde",
|
||||
"wit-bindgen",
|
||||
@ -5570,7 +5603,7 @@ dependencies = [
|
||||
"anyhow",
|
||||
"base64 0.22.1",
|
||||
"bincode",
|
||||
"kinode_process_lib 0.9.4",
|
||||
"kinode_process_lib 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rmp-serde",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@ -5788,7 +5821,7 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
||||
name = "state"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"kinode_process_lib 0.9.4",
|
||||
"kinode_process_lib 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"wit-bindgen",
|
||||
@ -5965,7 +5998,7 @@ version = "0.1.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
"kinode_process_lib 0.9.4",
|
||||
"kinode_process_lib 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.8.5",
|
||||
"regex",
|
||||
"serde",
|
||||
@ -5979,7 +6012,7 @@ version = "0.1.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
"kinode_process_lib 0.9.4",
|
||||
"kinode_process_lib 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"process_macros",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@ -6236,7 +6269,7 @@ version = "0.2.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
"kinode_process_lib 0.9.4",
|
||||
"kinode_process_lib 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"wit-bindgen",
|
||||
@ -6567,7 +6600,7 @@ name = "uninstall"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"kinode_process_lib 0.9.4",
|
||||
"kinode_process_lib 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"process_macros",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
@ -17,6 +17,7 @@ members = [
|
||||
"kinode/packages/app_store/app_store", "kinode/packages/app_store/ft_worker",
|
||||
"kinode/packages/app_store/download", "kinode/packages/app_store/install", "kinode/packages/app_store/uninstall", "kinode/packages/app_store/downloads", "kinode/packages/app_store/chain",
|
||||
"kinode/packages/chess/chess",
|
||||
"kinode/packages/contacts/contacts",
|
||||
"kinode/packages/homepage/homepage",
|
||||
"kinode/packages/kino_updates/blog", "kinode/packages/kino_updates/globe",
|
||||
"kinode/packages/kns_indexer/kns_indexer", "kinode/packages/kns_indexer/get_block", "kinode/packages/kns_indexer/state",
|
||||
|
@ -129,6 +129,7 @@ The distro userspace packages are:
|
||||
|
||||
- `app_store:sys`
|
||||
- `chess:sys`
|
||||
- `contacts:sys`
|
||||
- `homepage:sys`
|
||||
- `kino_updates:sys`
|
||||
- `kns_indexer:sys`
|
||||
|
3218
kinode/packages/contacts/Cargo.lock
generated
Normal file
3218
kinode/packages/contacts/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
11
kinode/packages/contacts/Cargo.toml
Normal file
11
kinode/packages/contacts/Cargo.toml
Normal file
@ -0,0 +1,11 @@
|
||||
[workspace]
|
||||
resolver = "2"
|
||||
members = [
|
||||
"contacts",
|
||||
"get_names",
|
||||
]
|
||||
|
||||
[profile.release]
|
||||
panic = "abort"
|
||||
opt-level = "s"
|
||||
lto = true
|
36
kinode/packages/contacts/api/contacts:sys-v0.wit
Normal file
36
kinode/packages/contacts/api/contacts:sys-v0.wit
Normal file
@ -0,0 +1,36 @@
|
||||
interface contacts {
|
||||
enum capability {
|
||||
read-name-only,
|
||||
read,
|
||||
add,
|
||||
remove,
|
||||
}
|
||||
|
||||
variant 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 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,
|
||||
err(string), // any failed request will receive this response
|
||||
}
|
||||
}
|
||||
|
||||
world contacts-sys-v0 {
|
||||
import contacts;
|
||||
include process-v0;
|
||||
}
|
20
kinode/packages/contacts/contacts/Cargo.toml
Normal file
20
kinode/packages/contacts/contacts/Cargo.toml
Normal file
@ -0,0 +1,20 @@
|
||||
[package]
|
||||
name = "contacts"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[features]
|
||||
simulation-mode = []
|
||||
|
||||
[dependencies]
|
||||
kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "088a549" }
|
||||
process_macros = { git = "https://github.com/kinode-dao/process_macros", rev = "626e501" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
wit-bindgen = "0.24.0"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[package.metadata.component]
|
||||
package = "kinode:process"
|
1
kinode/packages/contacts/contacts/src/icon
Normal file
1
kinode/packages/contacts/contacts/src/icon
Normal file
@ -0,0 +1 @@
|
||||
data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAwMCIgaGVpZ2h0PSIxMDAwIiB2aWV3Qm94PSIwIDAgMTAwMCAxMDAwIiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgo8cmVjdCB4PSIyMCIgeT0iMjAiIHdpZHRoPSI5NjAiIGhlaWdodD0iOTYwIiByeD0iNDgwIiBmaWxsPSJ1cmwoI3BhaW50MF9saW5lYXJfMjA5MV8xMDI4MikiIGZpbGwtb3BhY2l0eT0iMC40Ii8+CjxyZWN0IHg9IjIwIiB5PSIyMCIgd2lkdGg9Ijk2MCIgaGVpZ2h0PSI5NjAiIHJ4PSI0ODAiIHN0cm9rZT0idXJsKCNwYWludDFfbGluZWFyXzIwOTFfMTAyODIpIiBzdHJva2Utd2lkdGg9IjQwIi8+CjxwYXRoIGQ9Ik01ODEuMTE2IDU4NS43ODNMNjY0LjUzOCA2NjkuMTJDNjE3LjY5OSA3MTUuNzU4IDU1Ni42MjUgNzM5IDQ5NS4yNDUgNzM5QzQzMy44NjUgNzM5IDM3Mi42MzggNzE1LjYwNSAzMjYuMTA1IDY2OS4xMkMyODAuNzk3IDYyMy44NTggMjU2IDU2My45MTcgMjU2IDUwMEMyNTYgNDM2LjA4MyAyODAuOTUgMzc2LjE0MiAzMjYuMTA1IDMzMC44OEMzNzEuMjYgMjg1LjYxOSA0MzEuMjYyIDI2MSA0OTUuMjQ1IDI2MUM1NTkuMjI3IDI2MSA2MTkuMjMgMjg1LjkyNCA2NjQuNTM4IDMzMC44OEw1ODAuOTYzIDQxNC41MjNDNTQ1LjYwNCAzODMuOTQgNTA1LjE5NCAzNzQuNzY2IDQ2MS4yNjQgMzkxLjEyN0MzOTQuOTg1IDQxNS44OTkgMzY2LjgyMSA0ODkuMjk2IDM5Ny40MzUgNTUyLjc1NEMzOTguNjU5IDU1NS4yMDEgMzk4Ljk2NSA1NTguNzE4IDM5OC4zNTMgNTYxLjMxN0MzOTUuNzUxIDU3Mi40OCAzOTIuNjg5IDU4My40OSAzODkuNzgxIDU5NC4zNDZDMzg2LjEwNyA2MDcuOTU1IDM5My45MTQgNjE1Ljc1NCA0MDcuNTM3IDYxMi4yMzdDNDE4LjU1OCA2MDkuMzMxIDQyOS41NzkgNjA2LjEyIDQ0MC42IDYwMy42NzRDNDQzLjM1NSA2MDIuOTA5IDQ0Ny4wMjkgNjAzLjIxNSA0NDkuNjMxIDYwNC40MzhDNDkwLjUgNjIzLjM5OSA1MjkuOTkxIDYyMC44IDU2Ny43OTkgNTk2LjE4MUM1NzIuNTQ0IDU5My4xMjMgNTc2LjY3NyA1ODkuNDUzIDU4MS4xMTYgNTg1Ljc4M1oiIGZpbGw9InVybCgjcGFpbnQyX2xpbmVhcl8yMDkxXzEwMjgyKSIvPgo8ZGVmcz4KPGxpbmVhckdyYWRpZW50IGlkPSJwYWludDBfbGluZWFyXzIwOTFfMTAyODIiIHgxPSI1MDAiIHkxPSIwIiB4Mj0iNTAwIiB5Mj0iMTAwMCIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPgo8c3RvcCBzdG9wLWNvbG9yPSIjRjM1NDIyIi8+CjxzdG9wIG9mZnNldD0iMSIgc3RvcC1vcGFjaXR5PSIwIi8+CjwvbGluZWFyR3JhZGllbnQ+CjxsaW5lYXJHcmFkaWVudCBpZD0icGFpbnQxX2xpbmVhcl8yMDkxXzEwMjgyIiB4MT0iNzgyLjUiIHkxPSI3My41IiB4Mj0iMTg1LjUiIHkyPSI4OTQuNSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPgo8c3RvcCBzdG9wLWNvbG9yPSIjRjM1NDIyIi8+CjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iI0E1MzEwQyIvPgo8L2xpbmVhckdyYWRpZW50Pgo8bGluZWFyR3JhZGllbnQgaWQ9InBhaW50Ml9saW5lYXJfMjA5MV8xMDI4MiIgeDE9Ijc1NCIgeTE9Ijg5LjUiIHgyPSIyNTYiIHkyPSIxMDE1LjUiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj4KPHN0b3Agc3RvcC1jb2xvcj0iI0YzNTQyMiIvPgo8c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNBNzMyMEQiLz4KPC9saW5lYXJHcmFkaWVudD4KPC9kZWZzPgo8L3N2Zz4K
|
350
kinode/packages/contacts/contacts/src/lib.rs
Normal file
350
kinode/packages/contacts/contacts/src/lib.rs
Normal file
@ -0,0 +1,350 @@
|
||||
use crate::kinode::process::contacts;
|
||||
use kinode_process_lib::{
|
||||
await_message, call_init, eth, get_blob, get_typed_state, homepage, http, kimap, kiprintln,
|
||||
set_state, Address, Capability, LazyLoadBlob, Message, NodeId, Response,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::str::FromStr;
|
||||
|
||||
wit_bindgen::generate!({
|
||||
path: "target/wit",
|
||||
world: "contacts-sys-v0",
|
||||
generate_unused_types: true,
|
||||
additional_derives: [PartialEq, serde::Deserialize, serde::Serialize, process_macros::SerdeJsonInto],
|
||||
});
|
||||
|
||||
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)]
|
||||
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 {
|
||||
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()));
|
||||
self.save();
|
||||
}
|
||||
|
||||
fn remove_contact(&mut self, node: NodeId) {
|
||||
self.contacts.0.remove(&node);
|
||||
self.save();
|
||||
}
|
||||
|
||||
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);
|
||||
self.save();
|
||||
}
|
||||
|
||||
fn remove_field(&mut self, node: NodeId, field: String) {
|
||||
if let Some(contact) = self.contacts.0.get_mut(&node) {
|
||||
contact.0.remove(&field);
|
||||
}
|
||||
self.save();
|
||||
}
|
||||
|
||||
fn ws_update(&self, http_server: &mut http::server::HttpServer) {
|
||||
http_server.ws_push_all_channels(
|
||||
"/",
|
||||
http::server::WsMessageType::Text,
|
||||
LazyLoadBlob::new(
|
||||
Some("application/json"),
|
||||
serde_json::to_vec(self.contacts()).unwrap(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
call_init!(initialize);
|
||||
fn initialize(our: Address) {
|
||||
kiprintln!("started");
|
||||
|
||||
homepage::add_to_homepage("Contacts", Some(ICON), Some("/"), None);
|
||||
|
||||
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);
|
||||
|
||||
// serve the frontend on a secure subdomain
|
||||
http_server
|
||||
.serve_ui(
|
||||
&state.our,
|
||||
"ui",
|
||||
vec!["/"],
|
||||
http::server::HttpBindingConfig::default().secure_subdomain(true),
|
||||
)
|
||||
.unwrap();
|
||||
http_server.secure_bind_http_path("/ask").unwrap();
|
||||
http_server.secure_bind_ws_path("/").unwrap();
|
||||
|
||||
main_loop(&mut state, &kimap, &mut http_server);
|
||||
}
|
||||
|
||||
fn main_loop(
|
||||
state: &mut ContactsState,
|
||||
kimap: &kimap::Kimap,
|
||||
http_server: &mut http::server::HttpServer,
|
||||
) {
|
||||
loop {
|
||||
match await_message() {
|
||||
Err(_send_error) => {
|
||||
// ignore send errors, local-only process
|
||||
continue;
|
||||
}
|
||||
Ok(Message::Request {
|
||||
source,
|
||||
body,
|
||||
capabilities,
|
||||
..
|
||||
}) => {
|
||||
// ignore messages from other nodes -- technically superfluous check
|
||||
// since manifest does not acquire networking capability
|
||||
if source.node() != state.our.node {
|
||||
continue;
|
||||
}
|
||||
handle_request(&source, &body, capabilities, state, kimap, http_server);
|
||||
}
|
||||
_ => continue, // ignore responses
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_request(
|
||||
source: &Address,
|
||||
body: &[u8],
|
||||
capabilities: Vec<Capability>,
|
||||
state: &mut ContactsState,
|
||||
kimap: &kimap::Kimap,
|
||||
http_server: &mut http::server::HttpServer,
|
||||
) {
|
||||
// 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).unwrap();
|
||||
|
||||
http_server.handle_request(
|
||||
server_request,
|
||||
|req| handle_http_request(state, kimap, &req),
|
||||
|_channel_id, _message_type, _blob| {
|
||||
// we don't expect websocket messages
|
||||
},
|
||||
);
|
||||
} else {
|
||||
// if request is not from frontend, check that it has the required capabilities
|
||||
let (response, blob) = handle_contacts_request(state, kimap, body, Some(capabilities));
|
||||
let mut response = Response::new().body(response);
|
||||
if let Some(blob) = blob {
|
||||
response = response.blob(blob);
|
||||
}
|
||||
response.send().unwrap();
|
||||
}
|
||||
state.ws_update(http_server);
|
||||
}
|
||||
|
||||
/// Handle HTTP requests from our own frontend.
|
||||
fn handle_http_request(
|
||||
state: &mut ContactsState,
|
||||
kimap: &kimap::Kimap,
|
||||
http_request: &http::server::IncomingHttpRequest,
|
||||
) -> (http::server::HttpResponse, Option<LazyLoadBlob>) {
|
||||
match http_request.method().unwrap().as_str() {
|
||||
"GET" => (
|
||||
http::server::HttpResponse::new(http::StatusCode::OK)
|
||||
.header("Content-Type", "application/json"),
|
||||
Some(LazyLoadBlob::new(
|
||||
Some("application/json"),
|
||||
serde_json::to_vec(state.contacts()).unwrap(),
|
||||
)),
|
||||
),
|
||||
"POST" => {
|
||||
let blob = get_blob().unwrap();
|
||||
let (response, blob) = handle_contacts_request(state, kimap, blob.bytes(), None);
|
||||
if let contacts::Response::Err(e) = response {
|
||||
return (
|
||||
http::server::HttpResponse::new(http::StatusCode::BAD_REQUEST)
|
||||
.header("Content-Type", "application/json"),
|
||||
Some(LazyLoadBlob::new(
|
||||
Some("application/json"),
|
||||
serde_json::to_vec(&e).unwrap(),
|
||||
)),
|
||||
);
|
||||
}
|
||||
(
|
||||
http::server::HttpResponse::new(http::StatusCode::OK)
|
||||
.header("Content-Type", "application/json"),
|
||||
match blob {
|
||||
Some(blob) => Some(LazyLoadBlob::new(
|
||||
Some("application/json"),
|
||||
serde_json::to_vec(&blob.bytes).unwrap(),
|
||||
)),
|
||||
None => None,
|
||||
},
|
||||
)
|
||||
}
|
||||
// Any other method will be rejected.
|
||||
_ => (
|
||||
http::server::HttpResponse::new(http::StatusCode::METHOD_NOT_ALLOWED),
|
||||
None,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_contacts_request(
|
||||
state: &mut ContactsState,
|
||||
kimap: &kimap::Kimap,
|
||||
request_bytes: &[u8],
|
||||
capabilities: Option<Vec<Capability>>,
|
||||
) -> (contacts::Response, Option<LazyLoadBlob>) {
|
||||
let Ok(request) = serde_json::from_slice::<contacts::Request>(request_bytes) else {
|
||||
return (
|
||||
contacts::Response::Err("Malformed request".to_string()),
|
||||
None,
|
||||
);
|
||||
};
|
||||
// if request is not from frontend, check capabilities:
|
||||
// each request requires one of read-name-only, read, add, or remove
|
||||
if let Some(capabilities) = capabilities {
|
||||
let required_capability = Capability::new(
|
||||
&state.our,
|
||||
serde_json::to_string(&match request {
|
||||
contacts::Request::GetNames => contacts::Capability::ReadNameOnly,
|
||||
contacts::Request::GetAllContacts | contacts::Request::GetContact(_) => {
|
||||
contacts::Capability::Read
|
||||
}
|
||||
contacts::Request::AddContact(_) | contacts::Request::AddField(_) => {
|
||||
contacts::Capability::Add
|
||||
}
|
||||
contacts::Request::RemoveContact(_) | contacts::Request::RemoveField(_) => {
|
||||
contacts::Capability::Remove
|
||||
}
|
||||
})
|
||||
.unwrap(),
|
||||
);
|
||||
if !capabilities.contains(&required_capability) {
|
||||
return (
|
||||
contacts::Response::Err("Missing capability".to_string()),
|
||||
None,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
match request {
|
||||
contacts::Request::GetNames => (
|
||||
contacts::Response::GetNames(
|
||||
state
|
||||
.contacts()
|
||||
.0
|
||||
.keys()
|
||||
.map(|node| node.to_string())
|
||||
.collect(),
|
||||
),
|
||||
None,
|
||||
),
|
||||
contacts::Request::GetAllContacts => (
|
||||
contacts::Response::GetAllContacts,
|
||||
Some(LazyLoadBlob::new(
|
||||
Some("application/json"),
|
||||
serde_json::to_vec(state.contacts()).unwrap(),
|
||||
)),
|
||||
),
|
||||
contacts::Request::GetContact(node) => (
|
||||
contacts::Response::GetContact,
|
||||
Some(LazyLoadBlob::new(
|
||||
Some("application/json"),
|
||||
serde_json::to_vec(&state.get_contact(node)).unwrap(),
|
||||
)),
|
||||
),
|
||||
contacts::Request::AddContact(node) => {
|
||||
if let Some((response, blob)) = invalid_node(kimap, &node) {
|
||||
return (response, blob);
|
||||
}
|
||||
state.add_contact(node);
|
||||
(contacts::Response::AddContact, None)
|
||||
}
|
||||
contacts::Request::AddField((node, field, value)) => {
|
||||
if let Some((response, blob)) = invalid_node(kimap, &node) {
|
||||
return (response, blob);
|
||||
}
|
||||
let Ok(value) = serde_json::from_str::<serde_json::Value>(&value) else {
|
||||
return (contacts::Response::Err("Malformed value".to_string()), None);
|
||||
};
|
||||
state.add_field(node, field, value);
|
||||
(contacts::Response::AddField, None)
|
||||
}
|
||||
contacts::Request::RemoveContact(node) => {
|
||||
state.remove_contact(node);
|
||||
(contacts::Response::RemoveContact, None)
|
||||
}
|
||||
contacts::Request::RemoveField((node, field)) => {
|
||||
state.remove_field(node, field);
|
||||
(contacts::Response::RemoveField, None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn invalid_node(
|
||||
kimap: &kimap::Kimap,
|
||||
node: &str,
|
||||
) -> Option<(contacts::Response, Option<LazyLoadBlob>)> {
|
||||
if kimap
|
||||
.get(&node)
|
||||
.map(|(tba, _, _)| tba != eth::Address::ZERO)
|
||||
.unwrap_or(false)
|
||||
{
|
||||
None
|
||||
} else {
|
||||
Some((
|
||||
contacts::Response::Err("Node name invalid or does not exist".to_string()),
|
||||
None,
|
||||
))
|
||||
}
|
||||
}
|
19
kinode/packages/contacts/get_names/Cargo.toml
Normal file
19
kinode/packages/contacts/get_names/Cargo.toml
Normal file
@ -0,0 +1,19 @@
|
||||
[package]
|
||||
name = "get-names"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
kinode_process_lib = "0.9.2"
|
||||
process_macros = { git = "https://github.com/kinode-dao/process_macros", rev = "626e501" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
wit-bindgen = "0.24.0"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[package.metadata.component]
|
||||
package = "kinode:process"
|
35
kinode/packages/contacts/get_names/src/lib.rs
Normal file
35
kinode/packages/contacts/get_names/src/lib.rs
Normal file
@ -0,0 +1,35 @@
|
||||
use crate::kinode::process::contacts;
|
||||
use kinode_process_lib::{call_init, println, Address, Capability, Request};
|
||||
|
||||
wit_bindgen::generate!({
|
||||
path: "target/wit",
|
||||
world: "contacts-sys-v0",
|
||||
generate_unused_types: true,
|
||||
additional_derives: [serde::Deserialize, serde::Serialize, process_macros::SerdeJsonInto],
|
||||
});
|
||||
|
||||
call_init!(init);
|
||||
fn init(our: Address) {
|
||||
let contacts_process = Address::from((our.node(), ("contacts", "contacts", "sys")));
|
||||
|
||||
let read_names_cap = Capability::new(
|
||||
&contacts_process,
|
||||
serde_json::to_string(&contacts::Capability::ReadNameOnly).unwrap(),
|
||||
);
|
||||
|
||||
let Ok(Ok(response)) = Request::to(&contacts_process)
|
||||
.body(contacts::Request::GetNames)
|
||||
.capabilities(vec![read_names_cap])
|
||||
.send_and_await_response(5)
|
||||
else {
|
||||
println!("did not receive expected response from contacts:contacts:sys");
|
||||
return;
|
||||
};
|
||||
|
||||
let Ok(contacts::Response::GetNames(names)) = response.body().try_into() else {
|
||||
println!("did not receive GetNames response from contacts:contacts:sys");
|
||||
return;
|
||||
};
|
||||
|
||||
println!("{names:?}");
|
||||
}
|
18
kinode/packages/contacts/metadata.json
Normal file
18
kinode/packages/contacts/metadata.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "Contacts",
|
||||
"description": "Save and manage your Kinode OS contacts.",
|
||||
"image": "",
|
||||
"properties": {
|
||||
"package_name": "contacts",
|
||||
"current_version": "0.1.0",
|
||||
"publisher": "sys",
|
||||
"mirrors": [],
|
||||
"code_hashes": {
|
||||
"0.1.0": ""
|
||||
},
|
||||
"wit_version": 0,
|
||||
"dependencies": []
|
||||
},
|
||||
"external_url": "https://kinode.org",
|
||||
"animation_url": ""
|
||||
}
|
20
kinode/packages/contacts/pkg/manifest.json
Normal file
20
kinode/packages/contacts/pkg/manifest.json
Normal file
@ -0,0 +1,20 @@
|
||||
[
|
||||
{
|
||||
"process_name": "contacts",
|
||||
"process_wasm_path": "/contacts.wasm",
|
||||
"on_exit": "Restart",
|
||||
"request_networking": false,
|
||||
"request_capabilities": [
|
||||
"eth:distro:sys",
|
||||
"homepage:homepage:sys",
|
||||
"http_server:distro:sys",
|
||||
"vfs:distro:sys"
|
||||
],
|
||||
"grant_capabilities": [
|
||||
"eth:distro:sys",
|
||||
"http_server:distro:sys",
|
||||
"vfs:distro:sys"
|
||||
],
|
||||
"public": false
|
||||
}
|
||||
]
|
18
kinode/packages/contacts/pkg/scripts.json
Normal file
18
kinode/packages/contacts/pkg/scripts.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"get_names.wasm": {
|
||||
"root": false,
|
||||
"public": false,
|
||||
"request_networking": false,
|
||||
"request_capabilities": [
|
||||
"contacts:contacts:sys",
|
||||
{
|
||||
"process": "contacts:contacts:sys",
|
||||
"params": "ReadNameOnly"
|
||||
}
|
||||
],
|
||||
"grant_capabilities": [
|
||||
"contacts:contacts:sys"
|
||||
],
|
||||
"wit_version": 0
|
||||
}
|
||||
}
|
146
kinode/packages/contacts/pkg/ui/index.html
Normal file
146
kinode/packages/contacts/pkg/ui/index.html
Normal file
@ -0,0 +1,146 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<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" />
|
||||
<script src="/our.js"></script>
|
||||
<script>
|
||||
document.title = "contacts - " + window.our.node;
|
||||
</script>
|
||||
<style>
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6,
|
||||
p,
|
||||
a,
|
||||
li {
|
||||
font-family: 'Kode Mono', monospace;
|
||||
}
|
||||
|
||||
#title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
max-width: 720px;
|
||||
min-width: 300px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
#title h1 {
|
||||
margin: 0;
|
||||
flex-grow: 1;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
#title button {
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
#edit {
|
||||
max-width: 720px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
#contacts-article {
|
||||
margin: 20px;
|
||||
}
|
||||
|
||||
main {
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
max-width: 960px;
|
||||
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;
|
||||
max-width: 100%;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.contact ul li {
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
form.delete-contact {
|
||||
grid-area: delete;
|
||||
}
|
||||
|
||||
form.add-field {
|
||||
grid-area: add-field;
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
.remove-field {
|
||||
background-color: var(--tasteful-red);
|
||||
font-size: 0.8em;
|
||||
padding: 3px 10px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<span id="title">
|
||||
<button id="back-button"><svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 576 512"
|
||||
height="1em" width="1em" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M280.37 148.26L96 300.11V464a16 16 0 0 0 16 16l112.06-.29a16 16 0 0 0 15.92-16V368a16 16 0 0 1 16-16h64a16 16 0 0 1 16 16v95.64a16 16 0 0 0 16 16.05L464 480a16 16 0 0 0 16-16V300L295.67 148.26a12.19 12.19 0 0 0-15.3 0zM571.6 251.47L488 182.56V44.05a12 12 0 0 0-12-12h-56a12 12 0 0 0-12 12v72.61L318.47 43a48 48 0 0 0-61 0L4.34 251.47a12 12 0 0 0-1.6 16.9l25.5 31A12 12 0 0 0 45.15 301l235.22-193.74a12.19 12.19 0 0 1 15.3 0L530.9 301a12 12 0 0 0 16.9-1.6l25.5-31a12 12 0 0 0-1.7-16.93z">
|
||||
</path>
|
||||
</svg></button>
|
||||
<h1>contacts</h1>
|
||||
</span>
|
||||
<main>
|
||||
<article id="edit">
|
||||
<form id="add-contact">
|
||||
<input type="text" name="node" placeholder="node name (e.g. my-friend.os)">
|
||||
<button type="submit">add new contact</button>
|
||||
</form>
|
||||
</article>
|
||||
|
||||
<article id="contacts-article">
|
||||
<ul id="contacts"></ul>
|
||||
</article>
|
||||
|
||||
<script src="/contacts:contacts:sys/script.js"></script>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
132
kinode/packages/contacts/pkg/ui/script.js
Normal file
132
kinode/packages/contacts/pkg/ui/script.js
Normal file
@ -0,0 +1,132 @@
|
||||
const APP_PATH = '/contacts:contacts:sys/ask';
|
||||
|
||||
function api_call(body) {
|
||||
fetch(APP_PATH, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
}
|
||||
|
||||
function populate(data) {
|
||||
console.log(data);
|
||||
populate_contacts(data);
|
||||
}
|
||||
|
||||
function populate_contacts(contacts) {
|
||||
const ul = document.getElementById('contacts');
|
||||
ul.innerHTML = '';
|
||||
// sort contacts alphabetically by node
|
||||
Object.entries(contacts).sort((a, b) => a[0].localeCompare(b[0])).forEach(([node, contact]) => {
|
||||
const li = document.createElement('li');
|
||||
const div = document.createElement('div');
|
||||
div.classList.add('contact');
|
||||
div.innerHTML = `<h3>${node}</h3>
|
||||
<ul>
|
||||
${Object.entries(contact).sort((a, b) => a[0].localeCompare(b[0])).map(([field, value]) => `
|
||||
<li>
|
||||
${field}: ${JSON.stringify(value)}
|
||||
<button class="remove-field" onclick="removeField('${node}', '${field}')">X</button>
|
||||
</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 (e.g. name)">
|
||||
<input type="text" name="value" placeholder="value (e.g. John Doe)" title="Enter any valid JSON value (e.g. "John Doe", 42, true, [1,2,3], {"key":"value"})">
|
||||
<button type="submit">add</button>
|
||||
</form>
|
||||
`;
|
||||
li.appendChild(div);
|
||||
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);
|
||||
let value = data.get('value');
|
||||
// if value is not valid JSON, wrap it in quotes
|
||||
try {
|
||||
JSON.parse(value);
|
||||
} catch (e) {
|
||||
// If parsing fails, assume it's a string and wrap it in quotes
|
||||
value = `"${value}"`;
|
||||
}
|
||||
api_call({
|
||||
"AddField": [node, data.get('field'), value]
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
document.getElementById('back-button').addEventListener('click', () => {
|
||||
// set page to `/` while also removing the subdomain
|
||||
const url = new URL(window.location.href);
|
||||
if (url.hostname.split('.')[0] === 'contacts-sys') {
|
||||
url.hostname = url.hostname.split('.').slice(1).join('.');
|
||||
}
|
||||
url.pathname = '/';
|
||||
window.location.href = url.toString();
|
||||
});
|
||||
|
||||
document.getElementById('add-contact').addEventListener('submit', (e) => {
|
||||
e.preventDefault();
|
||||
const data = new FormData(e.target);
|
||||
const node = data.get('node');
|
||||
const body = {
|
||||
"AddContact": node
|
||||
};
|
||||
fetch(APP_PATH, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
}).then(response => {
|
||||
e.target.reset();
|
||||
if (response.status === 200) {
|
||||
return null;
|
||||
} else {
|
||||
return response.json();
|
||||
}
|
||||
}).then(data => {
|
||||
if (data === null) {
|
||||
return;
|
||||
} else {
|
||||
alert(JSON.stringify(data));
|
||||
}
|
||||
}).catch(error => {
|
||||
console.error('Error:', error);
|
||||
});
|
||||
})
|
||||
|
||||
function removeField(node, field) {
|
||||
api_call({
|
||||
"RemoveField": [node, field]
|
||||
});
|
||||
}
|
||||
|
||||
// Setup WebSocket connection
|
||||
const wsProtocol = location.protocol === 'https:' ? 'wss://' : 'ws://';
|
||||
const ws = new WebSocket(wsProtocol + location.host + "/contacts:contacts:sys/");
|
||||
ws.onmessage = event => {
|
||||
const data = JSON.parse(event.data);
|
||||
populate(data);
|
||||
};
|
||||
|
@ -5,6 +5,8 @@
|
||||
"on_exit": "Restart",
|
||||
"request_networking": true,
|
||||
"request_capabilities": [
|
||||
"app_store:app_store:sys",
|
||||
"contacts:contacts:sys",
|
||||
"chess:chess:sys",
|
||||
"eth:distro:sys",
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user