mirror of
https://github.com/uqbar-dao/nectar.git
synced 2024-12-23 00:21:38 +03:00
commit
1183cdff3a
101
Cargo.lock
generated
101
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.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.1",
|
||||
"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.1",
|
||||
"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.1",
|
||||
"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.1",
|
||||
"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.1",
|
||||
"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.1",
|
||||
"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.1",
|
||||
"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.1",
|
||||
"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.1",
|
||||
"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.1",
|
||||
"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.1",
|
||||
"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.1",
|
||||
"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.1",
|
||||
"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.1",
|
||||
"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.1",
|
||||
"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.1",
|
||||
"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.1",
|
||||
"kinode_process_lib 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"wit-bindgen",
|
||||
@ -3706,9 +3717,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",
|
||||
@ -3729,8 +3739,31 @@ 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.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c257733fdc158b8223e43d92baeac02fe3d6a06b62953dbaea36e989f861b138"
|
||||
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 = "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",
|
||||
@ -3794,7 +3827,7 @@ dependencies = [
|
||||
"alloy-sol-types",
|
||||
"anyhow",
|
||||
"hex",
|
||||
"kinode_process_lib 0.9.1",
|
||||
"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.1",
|
||||
"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.1",
|
||||
"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.1",
|
||||
"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.1",
|
||||
"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.1",
|
||||
"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.1",
|
||||
"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.1",
|
||||
"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.1",
|
||||
"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.1",
|
||||
"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.1",
|
||||
"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`
|
||||
|
4
kinode/packages/app_store/Cargo.lock
generated
4
kinode/packages/app_store/Cargo.lock
generated
@ -1617,9 +1617,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kinode_process_lib"
|
||||
version = "0.9.1"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76c5b69ac1fc0cb457c7714ceb8c0a5bdbee4ee00b837f9f16ea711e902bdfe8"
|
||||
checksum = "c257733fdc158b8223e43d92baeac02fe3d6a06b62953dbaea36e989f861b138"
|
||||
dependencies = [
|
||||
"alloy",
|
||||
"alloy-primitives",
|
||||
|
@ -11,7 +11,7 @@ alloy-primitives = "0.7.6"
|
||||
alloy-sol-types = "0.7.6"
|
||||
anyhow = "1.0"
|
||||
bincode = "1.3.3"
|
||||
kinode_process_lib = "0.9.1"
|
||||
kinode_process_lib = "0.9.4"
|
||||
process_macros = { git = "https://github.com/kinode-dao/process_macros", rev = "626e501" }
|
||||
rand = "0.8"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
|
@ -9,7 +9,6 @@ use crate::{
|
||||
},
|
||||
state::{MirrorCheck, PackageState, State},
|
||||
};
|
||||
|
||||
use kinode_process_lib::{
|
||||
http::{self, server, Method, StatusCode},
|
||||
println, Address, LazyLoadBlob, PackageId, Request, SendError, SendErrorKind,
|
||||
@ -22,7 +21,7 @@ const ICON: &str = include_str!("icon");
|
||||
/// Bind static and dynamic HTTP paths for the app store,
|
||||
/// bind to our WS updates path, and add icon and widget to homepage.
|
||||
pub fn init_frontend(our: &Address, http_server: &mut server::HttpServer) {
|
||||
let config = server::HttpBindingConfig::default();
|
||||
let config = server::HttpBindingConfig::default().secure_subdomain(true);
|
||||
|
||||
for path in [
|
||||
"/apps", // all on-chain apps
|
||||
@ -44,12 +43,19 @@ pub fn init_frontend(our: &Address, http_server: &mut server::HttpServer) {
|
||||
.bind_http_path(path, config.clone())
|
||||
.expect("failed to bind http path");
|
||||
}
|
||||
|
||||
// bind /apps path at base domain, in addition to secure subdomain,
|
||||
// so that widget can access it
|
||||
http_server
|
||||
.bind_http_path("/apps-public", config.clone().secure_subdomain(false))
|
||||
.expect("failed to bind http path");
|
||||
|
||||
http_server
|
||||
.serve_ui(&our, "ui", vec!["/"], config.clone())
|
||||
.expect("failed to serve static UI");
|
||||
|
||||
http_server
|
||||
.bind_ws_path("/", server::WsBindingConfig::default())
|
||||
.secure_bind_ws_path("/")
|
||||
.expect("failed to bind ws path");
|
||||
|
||||
// add ourselves to the homepage
|
||||
@ -136,7 +142,7 @@ fn make_widget() -> String {
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
function fetchApps() {
|
||||
fetch('/main:app_store:sys/apps', { credentials: 'include' })
|
||||
fetch('/main:app_store:sys/apps-public', { credentials: 'include' })
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
const container = document.getElementById('latest-apps');
|
||||
@ -255,7 +261,7 @@ fn serve_paths(
|
||||
|
||||
match bound_path {
|
||||
// GET all apps
|
||||
"/apps" => {
|
||||
"/apps" | "/apps-public" => {
|
||||
let resp = Request::to(("our", "chain", "app_store", "sys"))
|
||||
.body(serde_json::to_vec(&ChainRequests::GetApps)?)
|
||||
.send_and_await_response(5)??;
|
||||
|
@ -11,7 +11,7 @@ alloy-primitives = "0.7.6"
|
||||
alloy-sol-types = "0.7.6"
|
||||
anyhow = "1.0"
|
||||
bincode = "1.3.3"
|
||||
kinode_process_lib = "0.9.1"
|
||||
kinode_process_lib = "0.9.4"
|
||||
process_macros = { git = "https://github.com/kinode-dao/process_macros", rev = "626e501" }
|
||||
rand = "0.8"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
|
@ -8,7 +8,7 @@ simulation-mode = []
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
kinode_process_lib = "0.9.1"
|
||||
kinode_process_lib = "0.9.4"
|
||||
process_macros = { git = "https://github.com/kinode-dao/process_macros", rev = "626e501" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
|
@ -8,7 +8,7 @@ simulation-mode = []
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
kinode_process_lib = "0.9.1"
|
||||
kinode_process_lib = "0.9.4"
|
||||
process_macros = { git = "https://github.com/kinode-dao/process_macros", rev = "626e501" }
|
||||
rand = "0.8"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
|
@ -9,7 +9,7 @@ simulation-mode = []
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
bincode = "1.3.3"
|
||||
kinode_process_lib = "0.9.1"
|
||||
kinode_process_lib = "0.9.4"
|
||||
process_macros = { git = "https://github.com/kinode-dao/process_macros", rev = "626e501" }
|
||||
rand = "0.8"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
|
@ -8,7 +8,7 @@ simulation-mode = []
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
kinode_process_lib = "0.9.1"
|
||||
kinode_process_lib = "0.9.4"
|
||||
process_macros = { git = "https://github.com/kinode-dao/process_macros", rev = "626e501" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
|
@ -2,13 +2,13 @@ import React from "react";
|
||||
import { BrowserRouter as Router, Route, Routes } from "react-router-dom";
|
||||
|
||||
import Header from "./components/Header";
|
||||
import { APP_DETAILS_PATH, DOWNLOAD_PATH, MY_DOWNLOADS_PATH, PUBLISH_PATH, STORE_PATH } from "./constants/path";
|
||||
import { APP_DETAILS_PATH, DOWNLOAD_PATH, MY_APPS_PATH, PUBLISH_PATH, STORE_PATH } from "./constants/path";
|
||||
|
||||
import StorePage from "./pages/StorePage";
|
||||
import AppPage from "./pages/AppPage";
|
||||
import DownloadPage from "./pages/DownloadPage";
|
||||
import PublishPage from "./pages/PublishPage";
|
||||
import MyDownloadsPage from "./pages/MyDownloadsPage";
|
||||
import MyAppsPage from "./pages/MyAppsPage";
|
||||
|
||||
|
||||
const BASE_URL = import.meta.env.BASE_URL;
|
||||
@ -22,7 +22,7 @@ function App() {
|
||||
<Header />
|
||||
<Routes>
|
||||
<Route path={STORE_PATH} element={<StorePage />} />
|
||||
<Route path={MY_DOWNLOADS_PATH} element={<MyDownloadsPage />} />
|
||||
<Route path={MY_APPS_PATH} element={<MyAppsPage />} />
|
||||
<Route path={`${APP_DETAILS_PATH}/:id`} element={<AppPage />} />
|
||||
<Route path={PUBLISH_PATH} element={<PublishPage />} />
|
||||
<Route path={`${DOWNLOAD_PATH}/:id`} element={<DownloadPage />} />
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { STORE_PATH, PUBLISH_PATH, MY_DOWNLOADS_PATH } from '../constants/path';
|
||||
import { STORE_PATH, PUBLISH_PATH, MY_APPS_PATH } from '../constants/path';
|
||||
import { ConnectButton } from '@rainbow-me/rainbowkit';
|
||||
import { FaHome } from "react-icons/fa";
|
||||
|
||||
@ -9,12 +9,12 @@ const Header: React.FC = () => {
|
||||
<header className="app-header">
|
||||
<div className="header-left">
|
||||
<nav>
|
||||
<button onClick={() => window.location.href = '/'}>
|
||||
<button onClick={() => window.location.href = '/'} className="home-button">
|
||||
<FaHome />
|
||||
</button>
|
||||
<Link to={STORE_PATH} className={location.pathname === STORE_PATH ? 'active' : ''}>Apps</Link>
|
||||
<Link to={PUBLISH_PATH} className={location.pathname === PUBLISH_PATH ? 'active' : ''}>Publish</Link>
|
||||
<Link to={MY_DOWNLOADS_PATH} className={location.pathname === MY_DOWNLOADS_PATH ? 'active' : ''}>My Downloads</Link>
|
||||
<Link to={MY_APPS_PATH} className={location.pathname === MY_APPS_PATH ? 'active' : ''}>My Apps</Link>
|
||||
</nav>
|
||||
</div>
|
||||
<div className="header-right">
|
||||
|
@ -2,4 +2,4 @@ export const STORE_PATH = '/';
|
||||
export const PUBLISH_PATH = '/publish';
|
||||
export const APP_DETAILS_PATH = '/app';
|
||||
export const DOWNLOAD_PATH = '/download';
|
||||
export const MY_DOWNLOADS_PATH = '/my-downloads';
|
||||
export const MY_APPS_PATH = '/my-apps';
|
||||
|
@ -136,6 +136,8 @@ td {
|
||||
.app-icon {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
min-width: 64px;
|
||||
min-height: 64px;
|
||||
object-fit: cover;
|
||||
border-radius: var(--border-radius);
|
||||
}
|
||||
@ -348,6 +350,13 @@ td {
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
|
||||
.home-button {
|
||||
min-width: 48px;
|
||||
min-height: 48px;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
.app-screenshot {
|
||||
max-width: 200px;
|
||||
height: auto;
|
||||
|
@ -3,15 +3,41 @@ import { FaFolder, FaFile, FaChevronLeft, FaSync, FaRocket, FaSpinner, FaCheck,
|
||||
import useAppsStore from "../store";
|
||||
import { DownloadItem, PackageManifest, PackageState } from "../types/Apps";
|
||||
|
||||
export default function MyDownloadsPage() {
|
||||
const { fetchDownloads, fetchDownloadsForApp, startMirroring, stopMirroring, installApp, removeDownload, fetchInstalled, installed } = useAppsStore();
|
||||
// Core packages that cannot be uninstalled
|
||||
const CORE_PACKAGES = [
|
||||
"app_store:sys",
|
||||
"contacts:sys",
|
||||
"kino_updates:sys",
|
||||
"terminal:sys",
|
||||
"chess:sys",
|
||||
"kns_indexer:sys",
|
||||
"settings:sys",
|
||||
"homepage:sys"
|
||||
];
|
||||
|
||||
export default function MyAppsPage() {
|
||||
const {
|
||||
fetchDownloads,
|
||||
fetchDownloadsForApp,
|
||||
startMirroring,
|
||||
stopMirroring,
|
||||
installApp,
|
||||
removeDownload,
|
||||
fetchInstalled,
|
||||
installed,
|
||||
uninstallApp
|
||||
} = useAppsStore();
|
||||
|
||||
const [currentPath, setCurrentPath] = useState<string[]>([]);
|
||||
const [items, setItems] = useState<DownloadItem[]>([]);
|
||||
const [isInstalling, setIsInstalling] = useState(false);
|
||||
const [isUninstalling, setIsUninstalling] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [showCapApproval, setShowCapApproval] = useState(false);
|
||||
const [manifest, setManifest] = useState<PackageManifest | null>(null);
|
||||
const [selectedItem, setSelectedItem] = useState<DownloadItem | null>(null);
|
||||
const [showUninstallConfirm, setShowUninstallConfirm] = useState(false);
|
||||
const [appToUninstall, setAppToUninstall] = useState<any>(null);
|
||||
|
||||
useEffect(() => {
|
||||
loadItems();
|
||||
@ -33,6 +59,35 @@ export default function MyDownloadsPage() {
|
||||
}
|
||||
};
|
||||
|
||||
const initiateUninstall = (app: any) => {
|
||||
const packageId = `${app.package_id.package_name}:${app.package_id.publisher_node}`;
|
||||
if (CORE_PACKAGES.includes(packageId)) {
|
||||
setError("Cannot uninstall core system packages");
|
||||
return;
|
||||
}
|
||||
setAppToUninstall(app);
|
||||
setShowUninstallConfirm(true);
|
||||
};
|
||||
|
||||
const handleUninstall = async () => {
|
||||
if (!appToUninstall) return;
|
||||
setIsUninstalling(true);
|
||||
const packageId = `${appToUninstall.package_id.package_name}:${appToUninstall.package_id.publisher_node}`;
|
||||
try {
|
||||
await uninstallApp(packageId);
|
||||
await fetchInstalled();
|
||||
await loadItems();
|
||||
setShowUninstallConfirm(false);
|
||||
setAppToUninstall(null);
|
||||
} catch (error) {
|
||||
console.error('Uninstallation failed:', error);
|
||||
setError(`Uninstallation failed: ${error instanceof Error ? error.message : String(error)}`);
|
||||
} finally {
|
||||
setIsUninstalling(false);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const navigateToItem = (item: DownloadItem) => {
|
||||
if (item.Dir) {
|
||||
setCurrentPath([...currentPath, item.Dir.name]);
|
||||
@ -85,7 +140,6 @@ export default function MyDownloadsPage() {
|
||||
|
||||
if (!versionHash) throw new Error('Invalid file name format');
|
||||
|
||||
// Construct packageId by combining currentPath and remaining parts of the filename
|
||||
const packageId = [...currentPath, ...parts].join(':');
|
||||
|
||||
await installApp(packageId, versionHash);
|
||||
@ -121,8 +175,48 @@ export default function MyDownloadsPage() {
|
||||
|
||||
return (
|
||||
<div className="downloads-page">
|
||||
<h2>Downloads</h2>
|
||||
<h2>My Apps</h2>
|
||||
|
||||
{/* Installed Apps Section */}
|
||||
<div className="file-explorer">
|
||||
<h3>Installed Apps</h3>
|
||||
<table className="downloads-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Package ID</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{Object.values(installed).map((app) => {
|
||||
const packageId = `${app.package_id.package_name}:${app.package_id.publisher_node}`;
|
||||
const isCore = CORE_PACKAGES.includes(packageId);
|
||||
return (
|
||||
<tr key={packageId}>
|
||||
<td>{packageId}</td>
|
||||
<td>
|
||||
{isCore ? (
|
||||
<span className="core-package">Core Package</span>
|
||||
) : (
|
||||
<button
|
||||
onClick={() => initiateUninstall(app)}
|
||||
disabled={isUninstalling}
|
||||
>
|
||||
{isUninstalling ? <FaSpinner className="fa-spin" /> : <FaTrash />}
|
||||
Uninstall
|
||||
</button>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{/* Downloads Section */}
|
||||
<div className="file-explorer">
|
||||
<h3>Downloads</h3>
|
||||
<div className="path-navigation">
|
||||
{currentPath.length > 0 && (
|
||||
<button onClick={navigateUp} className="navigate-up">
|
||||
@ -172,7 +266,8 @@ export default function MyDownloadsPage() {
|
||||
)}
|
||||
{isFile && isInstalled && (
|
||||
<FaCheck className="installed" />
|
||||
)} </td>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
@ -186,6 +281,45 @@ export default function MyDownloadsPage() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Uninstall Confirmation Modal */}
|
||||
{showUninstallConfirm && appToUninstall && (
|
||||
<div className="cap-approval-popup">
|
||||
<div className="cap-approval-content">
|
||||
<h3>Confirm Uninstall</h3>
|
||||
<div className="warning-message">
|
||||
Are you sure you want to uninstall this app?
|
||||
</div>
|
||||
<div className="package-info">
|
||||
<strong>Package ID:</strong> {`${appToUninstall.package_id.package_name}:${appToUninstall.package_id.publisher_node}`}
|
||||
</div>
|
||||
{appToUninstall.metadata?.name && (
|
||||
<div className="package-info">
|
||||
<strong>Name:</strong> {appToUninstall.metadata.name}
|
||||
</div>
|
||||
)}
|
||||
<div className="approval-buttons">
|
||||
<button
|
||||
onClick={() => {
|
||||
setShowUninstallConfirm(false);
|
||||
setAppToUninstall(null);
|
||||
}}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
onClick={handleUninstall}
|
||||
disabled={isUninstalling}
|
||||
className="danger"
|
||||
>
|
||||
{isUninstalling ? <FaSpinner className="fa-spin" /> : 'Confirm Uninstall'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
|
||||
{showCapApproval && manifest && (
|
||||
<div className="cap-approval-popup">
|
||||
<div className="cap-approval-content">
|
@ -17,7 +17,7 @@ The format is "/" + "process_name:package_name:publisher_node"
|
||||
const BASE_URL = `/main:app_store:sys`;
|
||||
|
||||
// This is the proxy URL, it must match the node you are developing against
|
||||
const PROXY_URL = (process.env.VITE_NODE_URL || 'http://127.0.0.1:8080').replace('localhost', '127.0.0.1');
|
||||
const PROXY_URL = (process.env.VITE_NODE_URL || 'http://127.0.0.1:8080');
|
||||
|
||||
console.log('process.env.VITE_NODE_URL', process.env.VITE_NODE_URL, PROXY_URL);
|
||||
|
||||
|
@ -8,7 +8,7 @@ simulation-mode = []
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
kinode_process_lib = "0.9.1"
|
||||
kinode_process_lib = "0.9.4"
|
||||
process_macros = { git = "https://github.com/kinode-dao/process_macros", rev = "626e501" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
|
4
kinode/packages/chess/Cargo.lock
generated
4
kinode/packages/chess/Cargo.lock
generated
@ -1520,9 +1520,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kinode_process_lib"
|
||||
version = "0.9.1"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76c5b69ac1fc0cb457c7714ceb8c0a5bdbee4ee00b837f9f16ea711e902bdfe8"
|
||||
checksum = "c257733fdc158b8223e43d92baeac02fe3d6a06b62953dbaea36e989f861b138"
|
||||
dependencies = [
|
||||
"alloy",
|
||||
"alloy-primitives",
|
||||
|
@ -9,7 +9,7 @@ simulation-mode = []
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
bincode = "1.3.3"
|
||||
kinode_process_lib = "0.9.1"
|
||||
kinode_process_lib = "0.9.4"
|
||||
pleco = "0.5"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
|
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);
|
||||
};
|
||||
|
4
kinode/packages/homepage/Cargo.lock
generated
4
kinode/packages/homepage/Cargo.lock
generated
@ -1464,9 +1464,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kinode_process_lib"
|
||||
version = "0.9.1"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76c5b69ac1fc0cb457c7714ceb8c0a5bdbee4ee00b837f9f16ea711e902bdfe8"
|
||||
checksum = "c257733fdc158b8223e43d92baeac02fe3d6a06b62953dbaea36e989f861b138"
|
||||
dependencies = [
|
||||
"alloy",
|
||||
"alloy-primitives",
|
||||
|
@ -9,7 +9,7 @@ simulation-mode = []
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
bincode = "1.3.3"
|
||||
kinode_process_lib = "0.9.1"
|
||||
kinode_process_lib = "0.9.4"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
wit-bindgen = "0.24.0"
|
||||
|
4
kinode/packages/kino_updates/Cargo.lock
generated
4
kinode/packages/kino_updates/Cargo.lock
generated
@ -1476,9 +1476,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kinode_process_lib"
|
||||
version = "0.9.1"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76c5b69ac1fc0cb457c7714ceb8c0a5bdbee4ee00b837f9f16ea711e902bdfe8"
|
||||
checksum = "c257733fdc158b8223e43d92baeac02fe3d6a06b62953dbaea36e989f861b138"
|
||||
dependencies = [
|
||||
"alloy",
|
||||
"alloy-primitives",
|
||||
|
@ -9,7 +9,7 @@ simulation-mode = []
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
bincode = "1.3.3"
|
||||
kinode_process_lib = "0.9.1"
|
||||
kinode_process_lib = "0.9.4"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
url = "2.5.0"
|
||||
|
@ -7,7 +7,7 @@ edition = "2021"
|
||||
simulation-mode = []
|
||||
|
||||
[dependencies]
|
||||
kinode_process_lib = "0.9.1"
|
||||
kinode_process_lib = "0.9.4"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
url = "2.5.0"
|
||||
|
4
kinode/packages/kns_indexer/Cargo.lock
generated
4
kinode/packages/kns_indexer/Cargo.lock
generated
@ -1462,9 +1462,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kinode_process_lib"
|
||||
version = "0.9.1"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76c5b69ac1fc0cb457c7714ceb8c0a5bdbee4ee00b837f9f16ea711e902bdfe8"
|
||||
checksum = "c257733fdc158b8223e43d92baeac02fe3d6a06b62953dbaea36e989f861b138"
|
||||
dependencies = [
|
||||
"alloy",
|
||||
"alloy-primitives",
|
||||
|
@ -7,7 +7,7 @@ edition = "2021"
|
||||
simulation-mode = []
|
||||
|
||||
[dependencies]
|
||||
kinode_process_lib = "0.9.1"
|
||||
kinode_process_lib = "0.9.4"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
wit-bindgen = "0.24.0"
|
||||
|
@ -11,7 +11,7 @@ anyhow = "1.0"
|
||||
alloy-primitives = "0.7.0"
|
||||
alloy-sol-types = "0.7.0"
|
||||
hex = "0.4.3"
|
||||
kinode_process_lib = "0.9.1"
|
||||
kinode_process_lib = "0.9.4"
|
||||
rmp-serde = "1.1.2"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
|
@ -7,7 +7,7 @@ edition = "2021"
|
||||
simulation-mode = []
|
||||
|
||||
[dependencies]
|
||||
kinode_process_lib = "0.9.1"
|
||||
kinode_process_lib = "0.9.4"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
wit-bindgen = "0.24.0"
|
||||
|
4
kinode/packages/settings/Cargo.lock
generated
4
kinode/packages/settings/Cargo.lock
generated
@ -1452,9 +1452,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kinode_process_lib"
|
||||
version = "0.9.1"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76c5b69ac1fc0cb457c7714ceb8c0a5bdbee4ee00b837f9f16ea711e902bdfe8"
|
||||
checksum = "c257733fdc158b8223e43d92baeac02fe3d6a06b62953dbaea36e989f861b138"
|
||||
dependencies = [
|
||||
"alloy",
|
||||
"alloy-primitives",
|
||||
|
@ -10,7 +10,7 @@ simulation-mode = []
|
||||
anyhow = "1.0"
|
||||
base64 = "0.22.0"
|
||||
bincode = "1.3.3"
|
||||
kinode_process_lib = "0.9.1"
|
||||
kinode_process_lib = "0.9.4"
|
||||
rmp-serde = "1.2.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
|
4
kinode/packages/terminal/Cargo.lock
generated
4
kinode/packages/terminal/Cargo.lock
generated
@ -1620,9 +1620,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kinode_process_lib"
|
||||
version = "0.9.1"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76c5b69ac1fc0cb457c7714ceb8c0a5bdbee4ee00b837f9f16ea711e902bdfe8"
|
||||
checksum = "c257733fdc158b8223e43d92baeac02fe3d6a06b62953dbaea36e989f861b138"
|
||||
dependencies = [
|
||||
"alloy",
|
||||
"alloy-primitives",
|
||||
|
@ -8,7 +8,7 @@ simulation-mode = []
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
kinode_process_lib = "0.9.1"
|
||||
kinode_process_lib = "0.9.4"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
wit-bindgen = "0.24.0"
|
||||
|
@ -8,7 +8,7 @@ simulation-mode = []
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
kinode_process_lib = "0.9.1"
|
||||
kinode_process_lib = "0.9.4"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
wit-bindgen = "0.24.0"
|
||||
|
@ -7,7 +7,7 @@ edition = "2021"
|
||||
simulation-mode = []
|
||||
|
||||
[dependencies]
|
||||
kinode_process_lib = "0.9.1"
|
||||
kinode_process_lib = "0.9.4"
|
||||
wit-bindgen = "0.24.0"
|
||||
|
||||
[lib]
|
||||
|
@ -7,7 +7,7 @@ edition = "2021"
|
||||
simulation-mode = []
|
||||
|
||||
[dependencies]
|
||||
kinode_process_lib = "0.9.1"
|
||||
kinode_process_lib = "0.9.4"
|
||||
wit-bindgen = "0.24.0"
|
||||
|
||||
[lib]
|
||||
|
@ -7,7 +7,7 @@ edition = "2021"
|
||||
simulation-mode = []
|
||||
|
||||
[dependencies]
|
||||
kinode_process_lib = "0.9.1"
|
||||
kinode_process_lib = "0.9.4"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
wit-bindgen = "0.24.0"
|
||||
|
@ -8,7 +8,7 @@ simulation-mode = []
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
kinode_process_lib = "0.9.1"
|
||||
kinode_process_lib = "0.9.4"
|
||||
rmp-serde = "1.1.2"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
|
@ -8,7 +8,7 @@ simulation-mode = []
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
kinode_process_lib = "0.9.1"
|
||||
kinode_process_lib = "0.9.4"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
wit-bindgen = "0.24.0"
|
||||
|
@ -9,7 +9,7 @@ simulation-mode = []
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
clap = "4.4"
|
||||
kinode_process_lib = "0.9.1"
|
||||
kinode_process_lib = "0.9.4"
|
||||
regex = "1.10.3"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
|
@ -7,7 +7,7 @@ edition = "2021"
|
||||
simulation-mode = []
|
||||
|
||||
[dependencies]
|
||||
kinode_process_lib = "0.9.1"
|
||||
kinode_process_lib = "0.9.4"
|
||||
rmp-serde = "1.1.2"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
wit-bindgen = "0.24.0"
|
||||
|
@ -7,7 +7,7 @@ edition = "2021"
|
||||
simulation-mode = []
|
||||
|
||||
[dependencies]
|
||||
kinode_process_lib = "0.9.1"
|
||||
kinode_process_lib = "0.9.4"
|
||||
rmp-serde = "1.1.2"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
wit-bindgen = "0.24.0"
|
||||
|
@ -7,7 +7,7 @@ edition = "2021"
|
||||
simulation-mode = []
|
||||
|
||||
[dependencies]
|
||||
kinode_process_lib = "0.9.1"
|
||||
kinode_process_lib = "0.9.4"
|
||||
rmp-serde = "1.1.2"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
wit-bindgen = "0.24.0"
|
||||
|
@ -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",
|
||||
{
|
||||
|
@ -9,7 +9,7 @@ simulation-mode = []
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
bincode = "1.3.3"
|
||||
kinode_process_lib = "0.9.1"
|
||||
kinode_process_lib = "0.9.4"
|
||||
rand = "0.8"
|
||||
regex = "1.10.3"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
|
@ -9,7 +9,7 @@ simulation-mode = []
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
clap = "4.4"
|
||||
kinode_process_lib = "0.9.1"
|
||||
kinode_process_lib = "0.9.4"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
wit-bindgen = "0.24.0"
|
||||
|
4
kinode/packages/tester/Cargo.lock
generated
4
kinode/packages/tester/Cargo.lock
generated
@ -1452,9 +1452,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kinode_process_lib"
|
||||
version = "0.9.1"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76c5b69ac1fc0cb457c7714ceb8c0a5bdbee4ee00b837f9f16ea711e902bdfe8"
|
||||
checksum = "c257733fdc158b8223e43d92baeac02fe3d6a06b62953dbaea36e989f861b138"
|
||||
dependencies = [
|
||||
"alloy",
|
||||
"alloy-primitives",
|
||||
|
@ -9,7 +9,7 @@ simulation-mode = []
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
bincode = "1.3.3"
|
||||
kinode_process_lib = "0.9.1"
|
||||
kinode_process_lib = "0.9.4"
|
||||
process_macros = { git = "https://github.com/kinode-dao/process_macros", rev = "626e501" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
|
@ -231,6 +231,17 @@
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Check for redirect parameter on page load
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const redirectPath = urlParams.get('redirect');
|
||||
if (redirectPath) {
|
||||
// Ensure the redirect path starts with a slash
|
||||
const path = redirectPath.startsWith('/') ? redirectPath : '/' + redirectPath;
|
||||
window.location.href = path;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
|
@ -798,7 +798,7 @@ pub async fn kernel(
|
||||
// networking capabilities.
|
||||
let Some(persisted) = process_map.get(&kernel_message.target.process) else {
|
||||
t::Printout::new(
|
||||
0,
|
||||
2,
|
||||
format!(
|
||||
"event loop: got {} from network for {}, but process does not exist{}",
|
||||
match kernel_message.message {
|
||||
|
@ -21,7 +21,6 @@ function App() {
|
||||
const [reset, setReset] = useState<boolean>(false);
|
||||
const [direct, setDirect] = useState<boolean>(false);
|
||||
const [knsName, setKnsName] = useState<string>('');
|
||||
const [appSizeOnLoad, setAppSizeOnLoad] = useState<number>(0);
|
||||
const [networkingKey, setNetworkingKey] = useState<string>('');
|
||||
const [ipAddress, setIpAddress] = useState<number>(0);
|
||||
const [ws_port, setWsPort] = useState<number>(0);
|
||||
@ -36,10 +35,6 @@ function App() {
|
||||
const openConnect = () => setConnectOpen(true)
|
||||
const closeConnect = () => setConnectOpen(false)
|
||||
|
||||
useEffect(() => setAppSizeOnLoad(
|
||||
(window.performance.getEntriesByType('navigation') as any)[0].transferSize
|
||||
), []);
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
try {
|
||||
@ -84,7 +79,7 @@ function App() {
|
||||
// todo, most of these can be removed...
|
||||
const props = {
|
||||
direct, setDirect,
|
||||
key, appSizeOnLoad,
|
||||
key,
|
||||
keyFileName, setKeyFileName,
|
||||
reset, setReset,
|
||||
pw, setPw,
|
||||
|
@ -21,7 +21,6 @@ export interface PageProps {
|
||||
setReset: React.Dispatch<React.SetStateAction<boolean>>,
|
||||
pw: string,
|
||||
setPw: React.Dispatch<React.SetStateAction<string>>,
|
||||
appSizeOnLoad: number,
|
||||
nodeChainId: string,
|
||||
}
|
||||
|
||||
|
@ -7,13 +7,13 @@ import {
|
||||
import { PageProps } from "../lib/types";
|
||||
import Loader from "../components/Loader";
|
||||
import { sha256, toBytes } from "viem";
|
||||
import { redirectToHomepage } from "../utils/redirect-to-homepage";
|
||||
|
||||
interface ImportKeyfileProps extends PageProps { }
|
||||
|
||||
function ImportKeyfile({
|
||||
pw,
|
||||
setPw,
|
||||
appSizeOnLoad,
|
||||
}: ImportKeyfileProps) {
|
||||
|
||||
const [localKey, setLocalKey] = useState<Uint8Array | null>(null);
|
||||
@ -70,24 +70,15 @@ function ImportKeyfile({
|
||||
if (result.status > 399) {
|
||||
throw new Error("Incorrect password");
|
||||
}
|
||||
redirectToHomepage();
|
||||
|
||||
const interval = setInterval(async () => {
|
||||
const res = await fetch("/", { credentials: 'include' });
|
||||
if (
|
||||
res.status < 300 &&
|
||||
Number(res.headers.get("content-length")) !== appSizeOnLoad
|
||||
) {
|
||||
clearInterval(interval);
|
||||
window.location.replace("/");
|
||||
}
|
||||
}, 2000);
|
||||
}
|
||||
} catch {
|
||||
window.alert("An error occurred, please try again.");
|
||||
setLoading(false);
|
||||
}
|
||||
},
|
||||
[localKey, pw, keyErrs, appSizeOnLoad]
|
||||
[localKey, pw, keyErrs]
|
||||
);
|
||||
|
||||
return (
|
||||
|
@ -4,13 +4,13 @@ import Loader from "../components/Loader";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { sha256, toBytes } from "viem";
|
||||
import { Tooltip } from "../components/Tooltip";
|
||||
import { redirectToHomepage } from "../utils/redirect-to-homepage";
|
||||
|
||||
interface LoginProps extends PageProps { }
|
||||
|
||||
function Login({
|
||||
pw,
|
||||
setPw,
|
||||
appSizeOnLoad,
|
||||
routers,
|
||||
setRouters,
|
||||
knsName,
|
||||
@ -57,23 +57,14 @@ function Login({
|
||||
if (result.status > 399) {
|
||||
throw new Error(await result.text());
|
||||
}
|
||||
redirectToHomepage();
|
||||
|
||||
const interval = setInterval(async () => {
|
||||
const res = await fetch("/", { credentials: 'include' });
|
||||
if (
|
||||
res.status < 300 &&
|
||||
Number(res.headers.get("content-length")) !== appSizeOnLoad
|
||||
) {
|
||||
clearInterval(interval);
|
||||
window.location.replace("/");
|
||||
}
|
||||
}, 2000);
|
||||
} catch (err: any) {
|
||||
setKeyErrs([String(err)]);
|
||||
setLoading("");
|
||||
}
|
||||
},
|
||||
[pw, appSizeOnLoad]
|
||||
[pw]
|
||||
);
|
||||
|
||||
const isDirect = Boolean(routers?.length === 0);
|
||||
|
@ -5,6 +5,7 @@ import { Tooltip } from "../components/Tooltip";
|
||||
import { sha256, toBytes } from "viem";
|
||||
import { useSignTypedData, useAccount, useChainId } from 'wagmi'
|
||||
import { KIMAP } from "../abis";
|
||||
import { redirectToHomepage } from "../utils/redirect-to-homepage";
|
||||
|
||||
type SetPasswordProps = {
|
||||
direct: boolean;
|
||||
@ -12,7 +13,6 @@ type SetPasswordProps = {
|
||||
reset: boolean;
|
||||
knsName: string;
|
||||
setPw: React.Dispatch<React.SetStateAction<string>>;
|
||||
appSizeOnLoad: number;
|
||||
nodeChainId: string;
|
||||
closeConnect: () => void;
|
||||
};
|
||||
@ -23,7 +23,6 @@ function SetPassword({
|
||||
pw,
|
||||
reset,
|
||||
setPw,
|
||||
appSizeOnLoad,
|
||||
}: SetPasswordProps) {
|
||||
const [pw2, setPw2] = useState("");
|
||||
const [error, setError] = useState("");
|
||||
@ -103,25 +102,15 @@ function SetPassword({
|
||||
const base64String = await result.json();
|
||||
|
||||
downloadKeyfile(knsName, base64String);
|
||||
redirectToHomepage();
|
||||
|
||||
const interval = setInterval(async () => {
|
||||
const res = await fetch("/", { credentials: 'include' });
|
||||
|
||||
if (
|
||||
res.status < 300 &&
|
||||
Number(res.headers.get("content-length")) !== appSizeOnLoad
|
||||
) {
|
||||
clearInterval(interval);
|
||||
window.location.replace("/");
|
||||
}
|
||||
}, 2000);
|
||||
} catch {
|
||||
alert("There was an error setting your password, please try again.");
|
||||
setLoading(false);
|
||||
}
|
||||
}, 500);
|
||||
},
|
||||
[appSizeOnLoad, direct, pw, pw2, reset, knsName]
|
||||
[direct, pw, pw2, reset, knsName]
|
||||
);
|
||||
|
||||
return (
|
||||
|
9
kinode/src/register-ui/src/utils/redirect-to-homepage.ts
Normal file
9
kinode/src/register-ui/src/utils/redirect-to-homepage.ts
Normal file
@ -0,0 +1,9 @@
|
||||
export const redirectToHomepage = () => {
|
||||
const interval = setInterval(async () => {
|
||||
const res = await fetch("/version", { credentials: 'include' });
|
||||
if (res.status == 200) {
|
||||
clearInterval(interval);
|
||||
window.location.replace("/");
|
||||
}
|
||||
}, 500);
|
||||
};
|
Loading…
Reference in New Issue
Block a user