mirror of
https://github.com/uqbar-dao/nectar.git
synced 2025-01-08 18:52:49 +03:00
More app store stuff
This commit is contained in:
parent
59b1f6a8dc
commit
251709f297
7
modules/app_store/app_store/Cargo.lock
generated
7
modules/app_store/app_store/Cargo.lock
generated
@ -19,6 +19,7 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sha2",
|
"sha2",
|
||||||
|
"urlencoding",
|
||||||
"wit-bindgen",
|
"wit-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -473,6 +474,12 @@ dependencies = [
|
|||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "urlencoding"
|
||||||
|
version = "2.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "version_check"
|
name = "version_check"
|
||||||
version = "0.9.4"
|
version = "0.9.4"
|
||||||
|
@ -13,6 +13,7 @@ anyhow = "1.0"
|
|||||||
bincode = "1.3.3"
|
bincode = "1.3.3"
|
||||||
kinode_process_lib = { git = "https://github.com/uqbar-dao/process_lib.git", tag = "v0.5.5-alpha" }
|
kinode_process_lib = { git = "https://github.com/uqbar-dao/process_lib.git", tag = "v0.5.5-alpha" }
|
||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
|
urlencoding = "2.1.0"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
sha2 = "0.10.8"
|
sha2 = "0.10.8"
|
||||||
|
229
modules/app_store/app_store/src/http_api.rs
Normal file
229
modules/app_store/app_store/src/http_api.rs
Normal file
@ -0,0 +1,229 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use kinode_process_lib::{http::{send_response, IncomingHttpRequest, StatusCode}, Address};
|
||||||
|
|
||||||
|
use crate::{PackageListing, State};
|
||||||
|
|
||||||
|
pub fn handle_http_request(
|
||||||
|
our: &Address,
|
||||||
|
state: &mut State,
|
||||||
|
req: IncomingHttpRequest,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
let path = req.path()?;
|
||||||
|
let method = req.method()?;
|
||||||
|
|
||||||
|
let (status_code, headers, body) = match path.as_str() {
|
||||||
|
"/apps" => {
|
||||||
|
match method.as_str() {
|
||||||
|
"GET" => {
|
||||||
|
// TODO: Return a list of the user's apps
|
||||||
|
(
|
||||||
|
StatusCode::OK,
|
||||||
|
None,
|
||||||
|
serde_json::to_vec(&vec![
|
||||||
|
PackageListing {
|
||||||
|
owner: our.node.clone(),
|
||||||
|
publisher: our.node.clone(),
|
||||||
|
name: "Chess".to_string(),
|
||||||
|
icon: "".to_string(),
|
||||||
|
package_name: "chess".to_string(),
|
||||||
|
description: Some("A test app".to_string()),
|
||||||
|
website: Some("https://example.com".to_string()),
|
||||||
|
rating: 3.0,
|
||||||
|
versions: HashMap::new(),
|
||||||
|
mirrors: vec![],
|
||||||
|
},
|
||||||
|
PackageListing {
|
||||||
|
owner: our.node.clone(),
|
||||||
|
publisher: our.node.clone(),
|
||||||
|
name: "File Transfer".to_string(),
|
||||||
|
icon: "".to_string(),
|
||||||
|
package_name: "file_transfer".to_string(),
|
||||||
|
description: Some("A test app".to_string()),
|
||||||
|
website: Some("https://example.com".to_string()),
|
||||||
|
rating: 3.0,
|
||||||
|
versions: HashMap::new(),
|
||||||
|
mirrors: vec![],
|
||||||
|
},
|
||||||
|
])?,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
"POST" => {
|
||||||
|
// Add an app
|
||||||
|
(StatusCode::CREATED, None, format!("Installed").into_bytes())
|
||||||
|
}
|
||||||
|
_ => (
|
||||||
|
StatusCode::METHOD_NOT_ALLOWED,
|
||||||
|
None,
|
||||||
|
format!("Invalid method {} for {}", method, path).into_bytes(),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"/apps/:id" => {
|
||||||
|
let Some(app_id) = path.split("/").last() else {
|
||||||
|
return Err(anyhow::anyhow!("No app ID"));
|
||||||
|
};
|
||||||
|
|
||||||
|
match method.as_str() {
|
||||||
|
"PUT" => {
|
||||||
|
// Update an app
|
||||||
|
(
|
||||||
|
StatusCode::NO_CONTENT,
|
||||||
|
None,
|
||||||
|
format!("Updated").into_bytes(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
"DELETE" => {
|
||||||
|
// Uninstall an app
|
||||||
|
(
|
||||||
|
StatusCode::NO_CONTENT,
|
||||||
|
None,
|
||||||
|
format!("Uninstalled").into_bytes(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
_ => (
|
||||||
|
StatusCode::METHOD_NOT_ALLOWED,
|
||||||
|
None,
|
||||||
|
format!("Invalid method {} for {}", method, path).into_bytes(),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"/apps/latest" => {
|
||||||
|
match method.as_str() {
|
||||||
|
"GET" => {
|
||||||
|
// Return a list of latest apps
|
||||||
|
// The first 2 will show up in "featured"
|
||||||
|
(
|
||||||
|
StatusCode::OK,
|
||||||
|
None,
|
||||||
|
serde_json::to_vec(&vec![
|
||||||
|
PackageListing {
|
||||||
|
owner: our.node.clone(),
|
||||||
|
publisher: our.node.clone(),
|
||||||
|
name: "Remote".to_string(),
|
||||||
|
icon: "".to_string(),
|
||||||
|
package_name: "remote".to_string(),
|
||||||
|
description: Some("A test app".to_string()),
|
||||||
|
website: Some("https://example.com".to_string()),
|
||||||
|
rating: 3.0,
|
||||||
|
versions: HashMap::new(),
|
||||||
|
mirrors: vec![],
|
||||||
|
},
|
||||||
|
PackageListing {
|
||||||
|
owner: our.node.clone(),
|
||||||
|
publisher: our.node.clone(),
|
||||||
|
name: "Happy Path".to_string(),
|
||||||
|
icon: "".to_string(),
|
||||||
|
package_name: "happy_path".to_string(),
|
||||||
|
description: Some("A test app".to_string()),
|
||||||
|
website: Some("https://example.com".to_string()),
|
||||||
|
rating: 3.0,
|
||||||
|
versions: HashMap::new(),
|
||||||
|
mirrors: vec![],
|
||||||
|
},
|
||||||
|
PackageListing {
|
||||||
|
owner: our.node.clone(),
|
||||||
|
publisher: our.node.clone(),
|
||||||
|
name: "Meme Deck".to_string(),
|
||||||
|
icon: "".to_string(),
|
||||||
|
package_name: "meme_deck".to_string(),
|
||||||
|
description: Some("A test app".to_string()),
|
||||||
|
website: Some("https://example.com".to_string()),
|
||||||
|
rating: 3.0,
|
||||||
|
versions: HashMap::new(),
|
||||||
|
mirrors: vec![],
|
||||||
|
},
|
||||||
|
PackageListing {
|
||||||
|
owner: our.node.clone(),
|
||||||
|
publisher: our.node.clone(),
|
||||||
|
name: "Sheep Simulator".to_string(),
|
||||||
|
icon: "".to_string(),
|
||||||
|
package_name: "sheep_simulator".to_string(),
|
||||||
|
description: Some("A test app".to_string()),
|
||||||
|
website: Some("https://example.com".to_string()),
|
||||||
|
rating: 3.0,
|
||||||
|
versions: HashMap::new(),
|
||||||
|
mirrors: vec![],
|
||||||
|
},
|
||||||
|
])?,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
_ => (
|
||||||
|
StatusCode::METHOD_NOT_ALLOWED,
|
||||||
|
None,
|
||||||
|
format!("Invalid method {} for {}", method, path).into_bytes(),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"/apps/search/:query" => {
|
||||||
|
match method.as_str() {
|
||||||
|
"GET" => {
|
||||||
|
let Some(encoded_query) = path.split("/").last() else {
|
||||||
|
return Err(anyhow::anyhow!("No query"));
|
||||||
|
};
|
||||||
|
let query = urlencoding::decode(encoded_query).expect("UTF-8");
|
||||||
|
|
||||||
|
// Return a list of apps matching the query
|
||||||
|
// Query by name, publisher, package_name, description, website
|
||||||
|
(
|
||||||
|
StatusCode::OK,
|
||||||
|
None,
|
||||||
|
serde_json::to_vec(&vec![
|
||||||
|
PackageListing {
|
||||||
|
owner: our.node.clone(),
|
||||||
|
publisher: our.node.clone(),
|
||||||
|
name: "Winch".to_string(),
|
||||||
|
icon: "".to_string(),
|
||||||
|
package_name: "winch".to_string(),
|
||||||
|
description: Some("A test app".to_string()),
|
||||||
|
website: Some("https://example.com".to_string()),
|
||||||
|
rating: 3.0,
|
||||||
|
versions: HashMap::new(),
|
||||||
|
mirrors: vec![],
|
||||||
|
},
|
||||||
|
PackageListing {
|
||||||
|
owner: our.node.clone(),
|
||||||
|
publisher: our.node.clone(),
|
||||||
|
name: "Bucket".to_string(),
|
||||||
|
icon: "".to_string(),
|
||||||
|
package_name: "bucket".to_string(),
|
||||||
|
description: Some("A test app".to_string()),
|
||||||
|
website: Some("https://example.com".to_string()),
|
||||||
|
rating: 3.0,
|
||||||
|
versions: HashMap::new(),
|
||||||
|
mirrors: vec![],
|
||||||
|
},
|
||||||
|
])?,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
_ => (
|
||||||
|
StatusCode::METHOD_NOT_ALLOWED,
|
||||||
|
None,
|
||||||
|
format!("Invalid method {} for {}", method, path).into_bytes(),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"/apps/publish" => {
|
||||||
|
match method.as_str() {
|
||||||
|
"POST" => {
|
||||||
|
// Publish an app
|
||||||
|
(StatusCode::OK, None, format!("Success").into_bytes())
|
||||||
|
}
|
||||||
|
_ => (
|
||||||
|
StatusCode::METHOD_NOT_ALLOWED,
|
||||||
|
None,
|
||||||
|
format!("Invalid method {} for {}", method, path).into_bytes(),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => (
|
||||||
|
StatusCode::NOT_FOUND,
|
||||||
|
None,
|
||||||
|
format!("Path not found: {}", path).into_bytes(),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
send_response(status_code, headers, body)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
use kinode_process_lib::http::{
|
use kinode_process_lib::http::{
|
||||||
bind_http_path, send_response, serve_ui, HttpServerRequest, IncomingHttpRequest, StatusCode,
|
bind_http_path, serve_ui, HttpServerRequest
|
||||||
};
|
};
|
||||||
use kinode_process_lib::kernel_types as kt;
|
use kinode_process_lib::kernel_types as kt;
|
||||||
use kinode_process_lib::*;
|
use kinode_process_lib::*;
|
||||||
@ -21,6 +21,8 @@ mod ft_worker_lib;
|
|||||||
use ft_worker_lib::{
|
use ft_worker_lib::{
|
||||||
spawn_receive_transfer, spawn_transfer, FTWorkerCommand, FTWorkerResult, FileTransferContext,
|
spawn_receive_transfer, spawn_transfer, FTWorkerCommand, FTWorkerResult, FileTransferContext,
|
||||||
};
|
};
|
||||||
|
mod http_api;
|
||||||
|
use http_api::handle_http_request;
|
||||||
|
|
||||||
/// App Store:
|
/// App Store:
|
||||||
/// acts as both a local package manager and a protocol to share packages across the network.
|
/// acts as both a local package manager and a protocol to share packages across the network.
|
||||||
@ -214,203 +216,6 @@ fn init(our: Address) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_http_request(
|
|
||||||
our: &Address,
|
|
||||||
state: &mut State,
|
|
||||||
req: IncomingHttpRequest,
|
|
||||||
) -> anyhow::Result<()> {
|
|
||||||
let path = req.path()?;
|
|
||||||
let method = req.method()?;
|
|
||||||
|
|
||||||
let (status_code, headers, body) = match path.as_str() {
|
|
||||||
"/apps" => {
|
|
||||||
match method.as_str() {
|
|
||||||
"GET" => {
|
|
||||||
// TODO: Return a list of the user's apps
|
|
||||||
(
|
|
||||||
StatusCode::OK,
|
|
||||||
None,
|
|
||||||
serde_json::to_vec(&vec![
|
|
||||||
PackageListing {
|
|
||||||
owner: our.node.clone(),
|
|
||||||
publisher: our.node.clone(),
|
|
||||||
name: "Chess".to_string(),
|
|
||||||
icon: "".to_string(),
|
|
||||||
package_name: "chess".to_string(),
|
|
||||||
description: Some("A test app".to_string()),
|
|
||||||
website: Some("https://example.com".to_string()),
|
|
||||||
rating: 3.0,
|
|
||||||
versions: HashMap::new(),
|
|
||||||
mirrors: vec![],
|
|
||||||
},
|
|
||||||
PackageListing {
|
|
||||||
owner: our.node.clone(),
|
|
||||||
publisher: our.node.clone(),
|
|
||||||
name: "File Transfer".to_string(),
|
|
||||||
icon: "".to_string(),
|
|
||||||
package_name: "file_transfer".to_string(),
|
|
||||||
description: Some("A test app".to_string()),
|
|
||||||
website: Some("https://example.com".to_string()),
|
|
||||||
rating: 3.0,
|
|
||||||
versions: HashMap::new(),
|
|
||||||
mirrors: vec![],
|
|
||||||
},
|
|
||||||
])?,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
"POST" => {
|
|
||||||
// Add an app
|
|
||||||
(StatusCode::CREATED, None, format!("Installed").into_bytes())
|
|
||||||
}
|
|
||||||
_ => (
|
|
||||||
StatusCode::METHOD_NOT_ALLOWED,
|
|
||||||
None,
|
|
||||||
format!("Invalid method {} for {}", method, path).into_bytes(),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"/apps/:id" => {
|
|
||||||
let Some(app_id) = path.split("/").last() else {
|
|
||||||
return Err(anyhow::anyhow!("No app ID"));
|
|
||||||
};
|
|
||||||
|
|
||||||
match method.as_str() {
|
|
||||||
"PUT" => {
|
|
||||||
// Update an app
|
|
||||||
(
|
|
||||||
StatusCode::NO_CONTENT,
|
|
||||||
None,
|
|
||||||
format!("Updated").into_bytes(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
"DELETE" => {
|
|
||||||
// Uninstall an app
|
|
||||||
(
|
|
||||||
StatusCode::NO_CONTENT,
|
|
||||||
None,
|
|
||||||
format!("Uninstalled").into_bytes(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
_ => (
|
|
||||||
StatusCode::METHOD_NOT_ALLOWED,
|
|
||||||
None,
|
|
||||||
format!("Invalid method {} for {}", method, path).into_bytes(),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"/apps/latest" => {
|
|
||||||
match method.as_str() {
|
|
||||||
"GET" => {
|
|
||||||
// Return a list of latest apps
|
|
||||||
(
|
|
||||||
StatusCode::OK,
|
|
||||||
None,
|
|
||||||
serde_json::to_vec(&vec![
|
|
||||||
PackageListing {
|
|
||||||
owner: our.node.clone(),
|
|
||||||
publisher: our.node.clone(),
|
|
||||||
name: "Remote".to_string(),
|
|
||||||
icon: "".to_string(),
|
|
||||||
package_name: "remote".to_string(),
|
|
||||||
description: Some("A test app".to_string()),
|
|
||||||
website: Some("https://example.com".to_string()),
|
|
||||||
rating: 3.0,
|
|
||||||
versions: HashMap::new(),
|
|
||||||
mirrors: vec![],
|
|
||||||
},
|
|
||||||
PackageListing {
|
|
||||||
owner: our.node.clone(),
|
|
||||||
publisher: our.node.clone(),
|
|
||||||
name: "Happy Path".to_string(),
|
|
||||||
icon: "".to_string(),
|
|
||||||
package_name: "happy_path".to_string(),
|
|
||||||
description: Some("A test app".to_string()),
|
|
||||||
website: Some("https://example.com".to_string()),
|
|
||||||
rating: 3.0,
|
|
||||||
versions: HashMap::new(),
|
|
||||||
mirrors: vec![],
|
|
||||||
},
|
|
||||||
])?,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
_ => (
|
|
||||||
StatusCode::METHOD_NOT_ALLOWED,
|
|
||||||
None,
|
|
||||||
format!("Invalid method {} for {}", method, path).into_bytes(),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"/apps/search/:query" => {
|
|
||||||
match method.as_str() {
|
|
||||||
"GET" => {
|
|
||||||
let Some(query) = path.split("/").last() else {
|
|
||||||
return Err(anyhow::anyhow!("No query"));
|
|
||||||
};
|
|
||||||
// Return a list of apps matching the query
|
|
||||||
// Query by name, publisher, package_name, description, website
|
|
||||||
(
|
|
||||||
StatusCode::OK,
|
|
||||||
None,
|
|
||||||
serde_json::to_vec(&vec![
|
|
||||||
PackageListing {
|
|
||||||
owner: our.node.clone(),
|
|
||||||
publisher: our.node.clone(),
|
|
||||||
name: "Winch".to_string(),
|
|
||||||
icon: "".to_string(),
|
|
||||||
package_name: "winch".to_string(),
|
|
||||||
description: Some("A test app".to_string()),
|
|
||||||
website: Some("https://example.com".to_string()),
|
|
||||||
rating: 3.0,
|
|
||||||
versions: HashMap::new(),
|
|
||||||
mirrors: vec![],
|
|
||||||
},
|
|
||||||
PackageListing {
|
|
||||||
owner: our.node.clone(),
|
|
||||||
publisher: our.node.clone(),
|
|
||||||
name: "Bucket".to_string(),
|
|
||||||
icon: "".to_string(),
|
|
||||||
package_name: "bucket".to_string(),
|
|
||||||
description: Some("A test app".to_string()),
|
|
||||||
website: Some("https://example.com".to_string()),
|
|
||||||
rating: 3.0,
|
|
||||||
versions: HashMap::new(),
|
|
||||||
mirrors: vec![],
|
|
||||||
},
|
|
||||||
])?,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
_ => (
|
|
||||||
StatusCode::METHOD_NOT_ALLOWED,
|
|
||||||
None,
|
|
||||||
format!("Invalid method {} for {}", method, path).into_bytes(),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"/apps/publish" => {
|
|
||||||
match method.as_str() {
|
|
||||||
"POST" => {
|
|
||||||
// Publish an app
|
|
||||||
(StatusCode::OK, None, format!("Success").into_bytes())
|
|
||||||
}
|
|
||||||
_ => (
|
|
||||||
StatusCode::METHOD_NOT_ALLOWED,
|
|
||||||
None,
|
|
||||||
format!("Invalid method {} for {}", method, path).into_bytes(),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => (
|
|
||||||
StatusCode::NOT_FOUND,
|
|
||||||
None,
|
|
||||||
format!("Path not found: {}", path).into_bytes(),
|
|
||||||
),
|
|
||||||
};
|
|
||||||
|
|
||||||
send_response(status_code, headers, body)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_message(our: &Address, mut state: &mut State, message: &Message) -> anyhow::Result<()> {
|
fn handle_message(our: &Address, mut state: &mut State, message: &Message) -> anyhow::Result<()> {
|
||||||
match message {
|
match message {
|
||||||
Message::Request {
|
Message::Request {
|
||||||
@ -631,13 +436,13 @@ fn handle_new_package(
|
|||||||
let metadata = String::from_utf8(blob.bytes)?;
|
let metadata = String::from_utf8(blob.bytes)?;
|
||||||
let metadata = serde_json::from_str::<kt::PackageMetadata>(&metadata)?;
|
let metadata = serde_json::from_str::<kt::PackageMetadata>(&metadata)?;
|
||||||
|
|
||||||
let versions = HashMap::new();
|
let mut versions = HashMap::new();
|
||||||
versions.insert(metadata.version, version_hash);
|
versions.insert(metadata.version, version_hash);
|
||||||
|
|
||||||
let listing_data = PackageListing {
|
let listing_data = PackageListing {
|
||||||
owner: our.node.clone(),
|
owner: our.node.clone(),
|
||||||
publisher: our.node.clone(),
|
publisher: our.node.clone(),
|
||||||
name: metadata.package,
|
name: metadata.package.clone(),
|
||||||
icon: "".to_string(),
|
icon: "".to_string(),
|
||||||
package_name: metadata.package,
|
package_name: metadata.package,
|
||||||
description: metadata.description,
|
description: metadata.description,
|
||||||
|
Loading…
Reference in New Issue
Block a user