diff --git a/Cargo.lock b/Cargo.lock index 74b35d70..43e57ca3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3212,7 +3212,7 @@ dependencies = [ [[package]] name = "kinode" -version = "0.8.2" +version = "0.8.3" dependencies = [ "aes-gcm", "alloy", @@ -3269,7 +3269,7 @@ dependencies = [ [[package]] name = "kinode_lib" -version = "0.8.2" +version = "0.8.3" dependencies = [ "lib", ] @@ -3391,7 +3391,7 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" [[package]] name = "lib" -version = "0.8.2" +version = "0.8.3" dependencies = [ "alloy", "kit", diff --git a/Cargo.toml b/Cargo.toml index 260f7042..15c40cb9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "kinode_lib" authors = ["KinodeDAO"] -version = "0.8.2" +version = "0.8.3" edition = "2021" description = "A general-purpose sovereign cloud computing platform" homepage = "https://kinode.org" diff --git a/kinode/Cargo.toml b/kinode/Cargo.toml index 7ea062d0..3158632f 100644 --- a/kinode/Cargo.toml +++ b/kinode/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "kinode" authors = ["KinodeDAO"] -version = "0.8.2" +version = "0.8.3" edition = "2021" description = "A general-purpose sovereign cloud computing platform" homepage = "https://kinode.org" diff --git a/kinode/packages/kino_updates/widget/src/lib.rs b/kinode/packages/kino_updates/widget/src/lib.rs index b1209389..4e22ccb6 100644 --- a/kinode/packages/kino_updates/widget/src/lib.rs +++ b/kinode/packages/kino_updates/widget/src/lib.rs @@ -149,8 +149,13 @@ fn fetch_most_recent_blog_posts(n: usize) -> Vec { 60, vec![], ) { - Ok(response) => serde_json::from_slice::>(response.body()) - .expect("Invalid UTF-8 from kinode.org"), + Ok(response) => match serde_json::from_slice::>(response.body()) { + Ok(posts) => posts, + Err(e) => { + println!("Failed to parse blog posts: {e:?}"); + vec![] + } + }, Err(e) => { println!("Failed to fetch blog posts: {e:?}"); vec![] diff --git a/kinode/src/http/login.html b/kinode/src/http/login.html index aedca716..dce382e6 100644 --- a/kinode/src/http/login.html +++ b/kinode/src/http/login.html @@ -1138,6 +1138,35 @@ Constrain images and videos to the parent width and preserve their intrinsic asp --tw-grayscale: grayscale(100%); filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); } + + .top-left-button { + position: absolute; + top: 2rem; + left: 2rem; + width: 3rem; + height: 3rem; + border-radius: 50%; + background-color: transparent; + display: flex; + align-items: center; + justify-content: center; + transition: background-color 0.3s; + border: 1px solid white; + } + + .top-left-button img { + width: 2.5rem; + height: 2.5rem; + border-radius: 50%; + } + + .top-left-button:hover { + background-color: black; + } + + .hidden { + display: none; + } @@ -1145,6 +1174,11 @@ Constrain images and videos to the parent width and preserve their intrinsic asp style="background-color: #22211f; background-image: linear-gradient(-55deg, #f75a2977 0%, transparent 72.05%),linear-gradient(-11deg, #86000172 3%, transparent 57.05%);"> +
c.match(/[a-zA-Z0-9]/) ? c : '-') + .join(""); + return subdomain; + } + + function isHosted(host) { + const parts = host.split('.'); + if (parts.length < 3) { + return false; + } + const [thirdLast, secondLast, last] = parts.slice(-3); + if (thirdLast === 'hosting' && secondLast === 'kinode' && last === 'net') { + return true; + } + return false; + } + document.addEventListener("DOMContentLoaded", () => { const form = document.getElementById("signup-form"); form.addEventListener("submit", (e) => { @@ -1238,4 +1305,4 @@ Constrain images and videos to the parent width and preserve their intrinsic asp - \ No newline at end of file + diff --git a/kinode/src/http/server.rs b/kinode/src/http/server.rs index 0ff82806..004fbfc0 100644 --- a/kinode/src/http/server.rs +++ b/kinode/src/http/server.rs @@ -546,12 +546,22 @@ async fn http_handler( .into_response()); } if request_subdomain != subdomain { + let query_string = if !query_params.is_empty() { + let params: Vec = query_params + .iter() + .map(|(key, value)| format!("{}={}", key, value)) + .collect(); + format!("?{}", params.join("&")) + } else { + String::new() + }; + return Ok(warp::http::Response::builder() .status(StatusCode::TEMPORARY_REDIRECT) .header( "Location", format!( - "{}://{}.{}{}", + "{}://{}.{}{}{}", match headers.get("X-Forwarded-Proto") { Some(proto) => proto.to_str().unwrap_or("http"), None => "http", @@ -559,6 +569,7 @@ async fn http_handler( subdomain, host, original_path, + query_string, ), ) .body(vec![]) @@ -584,28 +595,10 @@ async fn http_handler( &jwt_secret_bytes, ) { // redirect to login page so they can get an auth token - if original_path == "" { - return Ok(warp::http::Response::builder() - .status(StatusCode::OK) - .body(login_html.to_string()) - .into_response()); - } else { - return Ok(warp::http::Response::builder() - .status(StatusCode::TEMPORARY_REDIRECT) - .header( - "Location", - format!( - "{}://{}", - match headers.get("X-Forwarded-Proto") { - Some(proto) => proto.to_str().unwrap_or("http"), - None => "http", - }, - host, - ), - ) - .body(vec![]) - .into_response()); - } + return Ok(warp::http::Response::builder() + .status(StatusCode::OK) + .body(login_html.to_string()) + .into_response()); } } } diff --git a/kinode/src/register.rs b/kinode/src/register.rs index 21cb42b0..954c99c7 100644 --- a/kinode/src/register.rs +++ b/kinode/src/register.rs @@ -707,7 +707,6 @@ pub async fn assign_routing( let namehash = FixedBytes::<32>::from_slice(&keygen::namehash(&our.name)); let ip_call = ipCall { _0: namehash }.abi_encode(); let key_call = keyCall { _0: namehash }.abi_encode(); - let router_call = routersCall { _0: namehash }.abi_encode(); let tx_input = TransactionInput::new(Bytes::from(ip_call)); let tx = TransactionRequest::default() .to(kns_address) @@ -737,18 +736,6 @@ pub async fn assign_routing( )); } - let router_tx_input = TransactionInput::new(Bytes::from(router_call)); - let router_tx = TransactionRequest::default() - .to(kns_address) - .input(router_tx_input); - - let Ok(routers) = provider.call(&router_tx).await else { - return Err(anyhow::anyhow!("Failed to fetch node routers from PKI")); - }; - let Ok(routers) = >>::abi_decode(&routers, false) else { - return Err(anyhow::anyhow!("Failed to decode node routers from PKI")); - }; - let node_ip = format!( "{}.{}.{}.{}", (ip >> 24) & 0xFF, @@ -757,7 +744,7 @@ pub async fn assign_routing( ip & 0xFF ); - if !routers.is_empty() { + if !our.is_direct() { // indirect node return Ok(()); } diff --git a/kinode/src/terminal/mod.rs b/kinode/src/terminal/mod.rs index 8b8326e9..05eae5e6 100644 --- a/kinode/src/terminal/mod.rs +++ b/kinode/src/terminal/mod.rs @@ -444,9 +444,9 @@ pub async fn terminal( )?; }, // - // BACKSPACE or DELETE: delete a single character at cursor + // BACKSPACE: delete a single character at cursor // - KeyCode::Backspace | KeyCode::Delete => { + KeyCode::Backspace => { if line_col == prompt_len { continue; } @@ -477,6 +477,35 @@ pub async fn terminal( )?; }, // + // DELETE: delete a single character at right of cursor + // + KeyCode::Delete => { + if line_col == current_line.len() { + continue; + } + current_line.remove(line_col); + if search_mode { + utils::execute_search( + &our, + &mut stdout, + ¤t_line, + prompt_len, + (win_cols, win_rows), + (line_col, cursor_col), + &mut command_history, + search_depth, + )?; + continue; + } + execute!( + stdout, + cursor::MoveTo(0, win_rows), + terminal::Clear(ClearType::CurrentLine), + Print(utils::truncate_in_place(¤t_line, prompt_len, win_cols, (line_col, cursor_col))), + cursor::MoveTo(cursor_col, win_rows), + )?; + } + // // LEFT: move cursor one spot left // KeyCode::Left => { @@ -587,7 +616,7 @@ pub async fn terminal( _ = sigalrm.recv() => return Err(anyhow::anyhow!("exiting due to SIGALRM")), _ = sighup.recv() => return Err(anyhow::anyhow!("exiting due to SIGHUP")), _ = sigint.recv() => return Err(anyhow::anyhow!("exiting due to SIGINT")), - _ = sigpipe.recv() => return Err(anyhow::anyhow!("exiting due to SIGPIPE")), + _ = sigpipe.recv() => continue, // IGNORE SIGPIPE! _ = sigquit.recv() => return Err(anyhow::anyhow!("exiting due to SIGQUIT")), _ = sigterm.recv() => return Err(anyhow::anyhow!("exiting due to SIGTERM")), _ = sigusr1.recv() => return Err(anyhow::anyhow!("exiting due to SIGUSR1")), diff --git a/lib/Cargo.toml b/lib/Cargo.toml index 80fa82f8..9d8147d0 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "lib" authors = ["KinodeDAO"] -version = "0.8.2" +version = "0.8.3" edition = "2021" description = "A general-purpose sovereign cloud computing platform" homepage = "https://kinode.org" diff --git a/lib/src/core.rs b/lib/src/core.rs index 35316e22..2e460700 100644 --- a/lib/src/core.rs +++ b/lib/src/core.rs @@ -3,6 +3,7 @@ use ring::signature; use rusqlite::types::{FromSql, FromSqlError, ToSql, ValueRef}; use serde::{Deserialize, Serialize}; use std::collections::{BTreeMap, HashMap, HashSet}; +use std::hash::{Hash, Hasher}; use thiserror::Error; lazy_static::lazy_static! { @@ -470,7 +471,7 @@ pub enum Message { Response((Response, Option)), } -#[derive(Clone, Debug, Hash, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct Capability { pub issuer: Address, pub params: String, @@ -488,6 +489,14 @@ impl PartialEq for Capability { } } +impl Hash for Capability { + fn hash(&self, state: &mut H) { + self.issuer.hash(state); + let params: serde_json::Value = serde_json::from_str(&self.params).unwrap_or_default(); + params.hash(state); + } +} + impl Capability { pub fn new(issuer: T, params: U) -> Self where