get homepage/register mixup sorted out; serve app data on homepage api route

This commit is contained in:
Tobias Merkle 2024-03-25 16:51:27 -04:00
parent adbd492231
commit 7d4fd2d664
65 changed files with 167 additions and 95 deletions

View File

@ -2,8 +2,8 @@
use kinode_process_lib::{
await_message, call_init, get_blob,
http::{
bind_http_path, bind_http_static_path, get_mime_type, serve_index_html, serve_ui,
HttpServerError,
bind_http_path, bind_http_static_path, get_mime_type, send_response, serve_index_html,
serve_ui, HttpServerError, HttpServerRequest, IncomingHttpRequest, StatusCode,
},
println,
vfs::{FileType, VfsAction, VfsRequest, VfsResponse},
@ -27,6 +27,14 @@ enum HomepageRequest {
Remove,
}
#[derive(Serialize, Deserialize)]
struct HomepageApp {
package_name: String,
path: String,
label: String,
base64_icon: String,
}
wit_bindgen::generate!({
path: "wit",
world: "process",
@ -35,7 +43,7 @@ wit_bindgen::generate!({
},
});
const HOME_PAGE: &str = include_str!("../../pkg/ui/index.html");
const HOME_PAGE: &str = include_str!("index.html");
const APP_TEMPLATE: &str = r#"
<a class="app-link" id="${package_name}" href="/${path}">
@ -46,95 +54,121 @@ const APP_TEMPLATE: &str = r#"
call_init!(init);
// Copied in from process_lib serve_ui. see https://github.com/kinode-dao/process_lib/blob/main/src/http.rs
fn static_serve_dir(
our: &Address,
directory: &str,
authenticated: bool,
local_only: bool,
paths: Vec<&str>,
) -> anyhow::Result<()> {
serve_index_html(our, directory, authenticated, local_only, paths)?;
let initial_path = format!("{}/pkg/{}", our.package_id(), directory);
println!("initial path: {}", initial_path);
let mut queue = VecDeque::new();
queue.push_back(initial_path.clone());
while let Some(path) = queue.pop_front() {
let Ok(directory_response) = KiRequest::to(("our", "vfs", "distro", "sys"))
.body(serde_json::to_vec(&VfsRequest {
path,
action: VfsAction::ReadDir,
})?)
.send_and_await_response(5)?
else {
return Err(anyhow::anyhow!(
"serve_ui: no response for path: {}",
initial_path
));
};
let directory_body = serde_json::from_slice::<VfsResponse>(directory_response.body())?;
// Determine if it's a file or a directory and handle appropriately
match directory_body {
VfsResponse::ReadDir(directory_info) => {
for entry in directory_info {
match entry.file_type {
// If it's a file, serve it statically
FileType::File => {
KiRequest::to(("our", "vfs", "distro", "sys"))
.body(serde_json::to_vec(&VfsRequest {
path: entry.path.clone(),
action: VfsAction::Read,
})?)
.send_and_await_response(5)??;
let Some(blob) = get_blob() else {
return Err(anyhow::anyhow!(
"serve_ui: no blob for {}",
entry.path
));
};
let content_type = get_mime_type(&entry.path);
println!("binding {}", entry.path.replace(&initial_path, ""));
bind_http_static_path(
entry.path.replace(&initial_path, ""),
authenticated, // Must be authenticated
local_only, // Is not local-only
Some(content_type),
blob.bytes,
)?;
}
FileType::Directory => {
// Push the directory onto the queue
queue.push_back(entry.path);
}
_ => {}
}
}
}
_ => {
return Err(anyhow::anyhow!(
"serve_ui: unexpected response for path: {:?}",
directory_body
))
}
};
}
Ok(())
/// bind to root path on http_server (we have special dispensation to do so!)
fn bind_index(our: &str, apps: &HashMap<ProcessId, String>) {
bind_http_static_path(
"/",
true,
false,
Some("text/html".to_string()),
HOME_PAGE
.replace("${our}", our)
.replace(
"${apps}",
&apps
.values()
.map(String::as_str)
.collect::<Vec<&str>>()
.join("\n"),
)
.to_string()
.as_bytes()
.to_vec(),
)
.expect("failed to bind to /");
}
// // Copied in from process_lib serve_ui. see https://github.com/kinode-dao/process_lib/blob/main/src/http.rs
// fn static_serve_dir(
// our: &Address,
// directory: &str,
// authenticated: bool,
// local_only: bool,
// paths: Vec<&str>,
// ) -> anyhow::Result<()> {
// serve_index_html(our, directory, authenticated, local_only, paths)?;
// let initial_path = format!("{}/pkg/{}", our.package_id(), directory);
// println!("initial path: {}", initial_path);
// let mut queue = VecDeque::new();
// queue.push_back(initial_path.clone());
// while let Some(path) = queue.pop_front() {
// let Ok(directory_response) = KiRequest::to(("our", "vfs", "distro", "sys"))
// .body(serde_json::to_vec(&VfsRequest {
// path,
// action: VfsAction::ReadDir,
// })?)
// .send_and_await_response(5)?
// else {
// return Err(anyhow::anyhow!(
// "serve_ui: no response for path: {}",
// initial_path
// ));
// };
// let directory_body = serde_json::from_slice::<VfsResponse>(directory_response.body())?;
// // Determine if it's a file or a directory and handle appropriately
// match directory_body {
// VfsResponse::ReadDir(directory_info) => {
// for entry in directory_info {
// match entry.file_type {
// // If it's a file, serve it statically
// FileType::File => {
// KiRequest::to(("our", "vfs", "distro", "sys"))
// .body(serde_json::to_vec(&VfsRequest {
// path: entry.path.clone(),
// action: VfsAction::Read,
// })?)
// .send_and_await_response(5)??;
// let Some(blob) = get_blob() else {
// return Err(anyhow::anyhow!(
// "serve_ui: no blob for {}",
// entry.path
// ));
// };
// let content_type = get_mime_type(&entry.path);
// println!("binding {}", entry.path.replace(&initial_path, ""));
// bind_http_static_path(
// entry.path.replace(&initial_path, ""),
// authenticated, // Must be authenticated
// local_only, // Is not local-only
// Some(content_type),
// blob.bytes,
// )?;
// }
// FileType::Directory => {
// // Push the directory onto the queue
// queue.push_back(entry.path);
// }
// _ => {}
// }
// }
// }
// _ => {
// return Err(anyhow::anyhow!(
// "serve_ui: unexpected response for path: {:?}",
// directory_body
// ))
// }
// };
// }
// Ok(())
// }
fn init(our: Address) {
let mut apps: HashMap<ProcessId, String> = HashMap::new();
let mut app_data: HashMap<ProcessId, HomepageApp> = HashMap::new();
static_serve_dir(&our, "ui", true, false, vec!["/", "/login"]);
// static_serve_dir(&our, "index.html", true, false, vec!["/"]);
bind_index(&our.node, &apps);
bind_http_static_path(
"/our",
@ -156,6 +190,8 @@ fn init(our: Address) {
)
.expect("failed to bind to /our.js");
bind_http_path("/apps", true, true).expect("failed to bind /apps");
loop {
let Ok(ref message) = await_message() else {
// we never send requests, so this will never happen
@ -175,6 +211,15 @@ fn init(our: Address) {
if let Ok(request) = serde_json::from_slice::<HomepageRequest>(message.body()) {
match request {
HomepageRequest::Add { label, icon, path } => {
app_data.insert(
message.source().process.clone(),
HomepageApp {
package_name: message.source().clone().package().to_string(),
path: path.clone(),
label: label.clone(),
base64_icon: icon.clone(),
},
);
apps.insert(
message.source().process.clone(),
APP_TEMPLATE
@ -197,13 +242,43 @@ fn init(our: Address) {
.replace("${label}", &label)
.replace("${base64_icon}", &icon),
);
// bind_index(&our.node, &apps);
bind_index(&our.node, &apps);
}
HomepageRequest::Remove => {
apps.remove(&message.source().process);
// bind_index(&our.node, &apps);
bind_index(&our.node, &apps);
}
}
} else if let Ok(request) = serde_json::from_slice::<HttpServerRequest>(message.body())
{
match request {
HttpServerRequest::Http(incoming) => {
let path = incoming.bound_path(None);
println!("on path: {}", path);
if path == "/apps" {
send_response(
StatusCode::OK,
Some(HashMap::from([(
"Content-Type".to_string(),
"application/json".to_string(),
)])),
app_data
.values()
.map(|app| serde_json::to_string(app).unwrap())
.collect::<Vec<String>>()
.join("\n")
.as_bytes()
.to_vec(),
);
}
send_response(
StatusCode::OK,
Some(HashMap::new()),
"hello".as_bytes().to_vec(),
);
}
_ => {}
}
}
}
}

View File

@ -5,12 +5,10 @@
"on_exit": "Restart",
"request_networking": false,
"request_capabilities": [
"http_server:distro:sys",
"vfs:distro:sys"
"http_server:distro:sys"
],
"grant_capabilities": [
"http_server:distro:sys",
"vfs:distro:sys"
"http_server:distro:sys"
],
"public": false
}

View File

@ -38,8 +38,7 @@
"scripts": {
"start": "react-scripts start",
"build": "GENERATE_SOURCEMAP=false react-scripts build",
"build:copy": "npm run build && npm run copy",
"copy": "mkdir -p ../../../src/register-ui/build && rm -rf ../../../src/register-ui/build/* && cp -r build/* ../../../src/register-ui/build/",
"build:copy": "npm run build",
"inline": "node ./add-inline-tags.js && cd build && inline-source ./index.html > ./inline-index.html && cd ..",
"build-inline": "npm run build && npm run inline",
"test": "react-scripts test",

View File

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

Before

Width:  |  Height:  |  Size: 431 B

After

Width:  |  Height:  |  Size: 431 B

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB